]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/scudo/standalone/secondary.cc
Merge compiler-rt trunk r366426, resolve conflicts, and add
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / scudo / standalone / secondary.cc
1 //===-- secondary.cc --------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "secondary.h"
10
11 #include "string_utils.h"
12
13 namespace scudo {
14
15 // As with the Primary, the size passed to this function includes any desired
16 // alignment, so that the frontend can align the user allocation. The hint
17 // parameter allows us to unmap spurious memory when dealing with larger
18 // (greater than a page) alignments on 32-bit platforms.
19 // Due to the sparsity of address space available on those platforms, requesting
20 // an allocation from the Secondary with a large alignment would end up wasting
21 // VA space (even though we are not committing the whole thing), hence the need
22 // to trim off some of the reserved space.
23 // For allocations requested with an alignment greater than or equal to a page,
24 // the committed memory will amount to something close to Size - AlignmentHint
25 // (pending rounding and headers).
26 void *MapAllocator::allocate(uptr Size, uptr AlignmentHint, uptr *BlockEnd) {
27   DCHECK_GT(Size, AlignmentHint);
28   const uptr PageSize = getPageSizeCached();
29   const uptr MapSize =
30       roundUpTo(Size + LargeBlock::getHeaderSize(), PageSize) + 2 * PageSize;
31   MapPlatformData Data = {};
32   uptr MapBase =
33       reinterpret_cast<uptr>(map(nullptr, MapSize, "scudo:secondary",
34                                  MAP_NOACCESS | MAP_ALLOWNOMEM, &Data));
35   if (!MapBase)
36     return nullptr;
37   uptr CommitBase = MapBase + PageSize;
38   uptr MapEnd = MapBase + MapSize;
39
40   // In the unlikely event of alignments larger than a page, adjust the amount
41   // of memory we want to commit, and trim the extra memory.
42   if (AlignmentHint >= PageSize) {
43     // For alignments greater than or equal to a page, the user pointer (eg: the
44     // pointer that is returned by the C or C++ allocation APIs) ends up on a
45     // page boundary , and our headers will live in the preceding page.
46     CommitBase = roundUpTo(MapBase + PageSize + 1, AlignmentHint) - PageSize;
47     const uptr NewMapBase = CommitBase - PageSize;
48     DCHECK_GE(NewMapBase, MapBase);
49     // We only trim the extra memory on 32-bit platforms: 64-bit platforms
50     // are less constrained memory wise, and that saves us two syscalls.
51     if (SCUDO_WORDSIZE == 32U && NewMapBase != MapBase) {
52       unmap(reinterpret_cast<void *>(MapBase), NewMapBase - MapBase, 0, &Data);
53       MapBase = NewMapBase;
54     }
55     const uptr NewMapEnd = CommitBase + PageSize +
56                            roundUpTo((Size - AlignmentHint), PageSize) +
57                            PageSize;
58     DCHECK_LE(NewMapEnd, MapEnd);
59     if (SCUDO_WORDSIZE == 32U && NewMapEnd != MapEnd) {
60       unmap(reinterpret_cast<void *>(NewMapEnd), MapEnd - NewMapEnd, 0, &Data);
61       MapEnd = NewMapEnd;
62     }
63   }
64
65   const uptr CommitSize = MapEnd - PageSize - CommitBase;
66   const uptr Ptr =
67       reinterpret_cast<uptr>(map(reinterpret_cast<void *>(CommitBase),
68                                  CommitSize, "scudo:secondary", 0, &Data));
69   LargeBlock::Header *H = reinterpret_cast<LargeBlock::Header *>(Ptr);
70   H->MapBase = MapBase;
71   H->MapSize = MapEnd - MapBase;
72   H->BlockEnd = CommitBase + CommitSize;
73   H->Data = Data;
74   {
75     ScopedLock L(Mutex);
76     if (!Tail) {
77       Tail = H;
78     } else {
79       Tail->Next = H;
80       H->Prev = Tail;
81       Tail = H;
82     }
83     AllocatedBytes += CommitSize;
84     if (LargestSize < CommitSize)
85       LargestSize = CommitSize;
86     NumberOfAllocs++;
87     Stats.add(StatAllocated, CommitSize);
88     Stats.add(StatMapped, H->MapSize);
89   }
90   if (BlockEnd)
91     *BlockEnd = CommitBase + CommitSize;
92   return reinterpret_cast<void *>(Ptr + LargeBlock::getHeaderSize());
93 }
94
95 void MapAllocator::deallocate(void *Ptr) {
96   LargeBlock::Header *H = LargeBlock::getHeader(Ptr);
97   {
98     ScopedLock L(Mutex);
99     LargeBlock::Header *Prev = H->Prev;
100     LargeBlock::Header *Next = H->Next;
101     if (Prev) {
102       CHECK_EQ(Prev->Next, H);
103       Prev->Next = Next;
104     }
105     if (Next) {
106       CHECK_EQ(Next->Prev, H);
107       Next->Prev = Prev;
108     }
109     if (Tail == H) {
110       CHECK(!Next);
111       Tail = Prev;
112     } else {
113       CHECK(Next);
114     }
115     const uptr CommitSize = H->BlockEnd - reinterpret_cast<uptr>(H);
116     FreedBytes += CommitSize;
117     NumberOfFrees++;
118     Stats.sub(StatAllocated, CommitSize);
119     Stats.sub(StatMapped, H->MapSize);
120   }
121   void *Addr = reinterpret_cast<void *>(H->MapBase);
122   const uptr Size = H->MapSize;
123   MapPlatformData Data;
124   Data = H->Data;
125   unmap(Addr, Size, UNMAP_ALL, &Data);
126 }
127
128 void MapAllocator::printStats() const {
129   Printf("Stats: MapAllocator: allocated %zd times (%zdK), freed %zd times "
130          "(%zdK), remains %zd (%zdK) max %zdM\n",
131          NumberOfAllocs, AllocatedBytes >> 10, NumberOfFrees, FreedBytes >> 10,
132          NumberOfAllocs - NumberOfFrees, (AllocatedBytes - FreedBytes) >> 10,
133          LargestSize >> 20);
134 }
135
136 } // namespace scudo