diff options
author | nkozlovskiy <nmk@ydb.tech> | 2023-10-11 19:11:46 +0300 |
---|---|---|
committer | nkozlovskiy <nmk@ydb.tech> | 2023-10-11 19:33:28 +0300 |
commit | 61b3971447e473726d6cdb23fc298e457b4d973c (patch) | |
tree | e2a2a864bb7717f7ae6138f6a3194a254dd2c7bb /contrib/libs/clang14-rt/lib/scudo/scudo_allocator_secondary.h | |
parent | a674dc57d88d43c2e8e90a6084d5d2c988e0402c (diff) | |
download | ydb-61b3971447e473726d6cdb23fc298e457b4d973c.tar.gz |
add sanitizers dependencies
Diffstat (limited to 'contrib/libs/clang14-rt/lib/scudo/scudo_allocator_secondary.h')
-rw-r--r-- | contrib/libs/clang14-rt/lib/scudo/scudo_allocator_secondary.h | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/contrib/libs/clang14-rt/lib/scudo/scudo_allocator_secondary.h b/contrib/libs/clang14-rt/lib/scudo/scudo_allocator_secondary.h new file mode 100644 index 0000000000..80198c4aeb --- /dev/null +++ b/contrib/libs/clang14-rt/lib/scudo/scudo_allocator_secondary.h @@ -0,0 +1,192 @@ +//===-- scudo_allocator_secondary.h -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// Scudo Secondary Allocator. +/// This services allocation that are too large to be serviced by the Primary +/// Allocator. It is directly backed by the memory mapping functions of the +/// operating system. +/// +//===----------------------------------------------------------------------===// + +#ifndef SCUDO_ALLOCATOR_SECONDARY_H_ +#define SCUDO_ALLOCATOR_SECONDARY_H_ + +#ifndef SCUDO_ALLOCATOR_H_ +# error "This file must be included inside scudo_allocator.h." +#endif + +// Secondary backed allocations are standalone chunks that contain extra +// information stored in a LargeChunk::Header prior to the frontend's header. +// +// The secondary takes care of alignment requirements (so that it can release +// unnecessary pages in the rare event of larger alignments), and as such must +// know about the frontend's header size. +// +// Since Windows doesn't support partial releasing of a reserved memory region, +// we have to keep track of both the reserved and the committed memory. +// +// The resulting chunk resembles the following: +// +// +--------------------+ +// | Guard page(s) | +// +--------------------+ +// | Unused space* | +// +--------------------+ +// | LargeChunk::Header | +// +--------------------+ +// | {Unp,P}ackedHeader | +// +--------------------+ +// | Data (aligned) | +// +--------------------+ +// | Unused space** | +// +--------------------+ +// | Guard page(s) | +// +--------------------+ + +namespace LargeChunk { +struct Header { + ReservedAddressRange StoredRange; + uptr CommittedSize; + uptr Size; +}; +constexpr uptr getHeaderSize() { + return RoundUpTo(sizeof(Header), MinAlignment); +} +static Header *getHeader(uptr Ptr) { + return reinterpret_cast<Header *>(Ptr - getHeaderSize()); +} +static Header *getHeader(const void *Ptr) { + return getHeader(reinterpret_cast<uptr>(Ptr)); +} +} // namespace LargeChunk + +class LargeMmapAllocator { + public: + void Init() { + internal_memset(this, 0, sizeof(*this)); + } + + void *Allocate(AllocatorStats *Stats, uptr Size, uptr Alignment) { + const uptr UserSize = Size - Chunk::getHeaderSize(); + // The Scudo frontend prevents us from allocating more than + // MaxAllowedMallocSize, so integer overflow checks would be superfluous. + uptr ReservedSize = Size + LargeChunk::getHeaderSize(); + if (UNLIKELY(Alignment > MinAlignment)) + ReservedSize += Alignment; + const uptr PageSize = GetPageSizeCached(); + ReservedSize = RoundUpTo(ReservedSize, PageSize); + // Account for 2 guard pages, one before and one after the chunk. + ReservedSize += 2 * PageSize; + + ReservedAddressRange AddressRange; + uptr ReservedBeg = AddressRange.Init(ReservedSize, SecondaryAllocatorName); + if (UNLIKELY(ReservedBeg == ~static_cast<uptr>(0))) + return nullptr; + // A page-aligned pointer is assumed after that, so check it now. + DCHECK(IsAligned(ReservedBeg, PageSize)); + uptr ReservedEnd = ReservedBeg + ReservedSize; + // The beginning of the user area for that allocation comes after the + // initial guard page, and both headers. This is the pointer that has to + // abide by alignment requirements. + uptr CommittedBeg = ReservedBeg + PageSize; + uptr UserBeg = CommittedBeg + HeadersSize; + uptr UserEnd = UserBeg + UserSize; + uptr CommittedEnd = RoundUpTo(UserEnd, PageSize); + + // In the rare event of larger alignments, we will attempt to fit the mmap + // area better and unmap extraneous memory. This will also ensure that the + // offset and unused bytes field of the header stay small. + if (UNLIKELY(Alignment > MinAlignment)) { + if (!IsAligned(UserBeg, Alignment)) { + UserBeg = RoundUpTo(UserBeg, Alignment); + CommittedBeg = RoundDownTo(UserBeg - HeadersSize, PageSize); + const uptr NewReservedBeg = CommittedBeg - PageSize; + DCHECK_GE(NewReservedBeg, ReservedBeg); + if (!SANITIZER_WINDOWS && NewReservedBeg != ReservedBeg) { + AddressRange.Unmap(ReservedBeg, NewReservedBeg - ReservedBeg); + ReservedBeg = NewReservedBeg; + } + UserEnd = UserBeg + UserSize; + CommittedEnd = RoundUpTo(UserEnd, PageSize); + } + const uptr NewReservedEnd = CommittedEnd + PageSize; + DCHECK_LE(NewReservedEnd, ReservedEnd); + if (!SANITIZER_WINDOWS && NewReservedEnd != ReservedEnd) { + AddressRange.Unmap(NewReservedEnd, ReservedEnd - NewReservedEnd); + ReservedEnd = NewReservedEnd; + } + } + + DCHECK_LE(UserEnd, CommittedEnd); + const uptr CommittedSize = CommittedEnd - CommittedBeg; + // Actually mmap the memory, preserving the guard pages on either sides. + CHECK_EQ(CommittedBeg, AddressRange.Map(CommittedBeg, CommittedSize)); + const uptr Ptr = UserBeg - Chunk::getHeaderSize(); + LargeChunk::Header *H = LargeChunk::getHeader(Ptr); + H->StoredRange = AddressRange; + H->Size = CommittedEnd - Ptr; + H->CommittedSize = CommittedSize; + + // The primary adds the whole class size to the stats when allocating a + // chunk, so we will do something similar here. But we will not account for + // the guard pages. + { + SpinMutexLock l(&StatsMutex); + Stats->Add(AllocatorStatAllocated, CommittedSize); + Stats->Add(AllocatorStatMapped, CommittedSize); + AllocatedBytes += CommittedSize; + if (LargestSize < CommittedSize) + LargestSize = CommittedSize; + NumberOfAllocs++; + } + + return reinterpret_cast<void *>(Ptr); + } + + void Deallocate(AllocatorStats *Stats, void *Ptr) { + LargeChunk::Header *H = LargeChunk::getHeader(Ptr); + // Since we're unmapping the entirety of where the ReservedAddressRange + // actually is, copy onto the stack. + ReservedAddressRange AddressRange = H->StoredRange; + const uptr Size = H->CommittedSize; + { + SpinMutexLock l(&StatsMutex); + Stats->Sub(AllocatorStatAllocated, Size); + Stats->Sub(AllocatorStatMapped, Size); + FreedBytes += Size; + NumberOfFrees++; + } + AddressRange.Unmap(reinterpret_cast<uptr>(AddressRange.base()), + AddressRange.size()); + } + + static uptr GetActuallyAllocatedSize(void *Ptr) { + return LargeChunk::getHeader(Ptr)->Size; + } + + void PrintStats() { + Printf("Stats: LargeMmapAllocator: allocated %zd times (%zd K), " + "freed %zd times (%zd K), remains %zd (%zd K) max %zd M\n", + NumberOfAllocs, AllocatedBytes >> 10, NumberOfFrees, + FreedBytes >> 10, NumberOfAllocs - NumberOfFrees, + (AllocatedBytes - FreedBytes) >> 10, LargestSize >> 20); + } + + private: + static constexpr uptr HeadersSize = + LargeChunk::getHeaderSize() + Chunk::getHeaderSize(); + + StaticSpinMutex StatsMutex; + u32 NumberOfAllocs; + u32 NumberOfFrees; + uptr AllocatedBytes; + uptr FreedBytes; + uptr LargestSize; +}; + +#endif // SCUDO_ALLOCATOR_SECONDARY_H_ |