Path: blob/main/contrib/llvm-project/compiler-rt/lib/xray/xray_allocator.h
35265 views
//===-- xray_allocator.h ---------------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file is a part of XRay, a dynamic runtime instrumentation system.9//10// Defines the allocator interface for an arena allocator, used primarily for11// the profiling runtime.12//13//===----------------------------------------------------------------------===//14#ifndef XRAY_ALLOCATOR_H15#define XRAY_ALLOCATOR_H1617#include "sanitizer_common/sanitizer_common.h"18#include "sanitizer_common/sanitizer_internal_defs.h"19#include "sanitizer_common/sanitizer_mutex.h"20#if SANITIZER_FUCHSIA21#include <zircon/process.h>22#include <zircon/status.h>23#include <zircon/syscalls.h>24#else25#include "sanitizer_common/sanitizer_posix.h"26#endif27#include "xray_defs.h"28#include "xray_utils.h"29#include <cstddef>30#include <cstdint>31#include <sys/mman.h>3233namespace __xray {3435// We implement our own memory allocation routine which will bypass the36// internal allocator. This allows us to manage the memory directly, using37// mmap'ed memory to back the allocators.38template <class T> T *allocate() XRAY_NEVER_INSTRUMENT {39uptr RoundedSize = RoundUpTo(sizeof(T), GetPageSizeCached());40#if SANITIZER_FUCHSIA41zx_handle_t Vmo;42zx_status_t Status = _zx_vmo_create(RoundedSize, 0, &Vmo);43if (Status != ZX_OK) {44if (Verbosity())45Report("XRay Profiling: Failed to create VMO of size %zu: %s\n",46sizeof(T), _zx_status_get_string(Status));47return nullptr;48}49uintptr_t B;50Status =51_zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,52Vmo, 0, sizeof(T), &B);53_zx_handle_close(Vmo);54if (Status != ZX_OK) {55if (Verbosity())56Report("XRay Profiling: Failed to map VMAR of size %zu: %s\n", sizeof(T),57_zx_status_get_string(Status));58return nullptr;59}60return reinterpret_cast<T *>(B);61#else62uptr B = internal_mmap(NULL, RoundedSize, PROT_READ | PROT_WRITE,63MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);64int ErrNo = 0;65if (UNLIKELY(internal_iserror(B, &ErrNo))) {66if (Verbosity())67Report("XRay Profiling: Failed to allocate memory of size %zu; Error = "68"%zu\n",69RoundedSize, B);70return nullptr;71}72#endif73return reinterpret_cast<T *>(B);74}7576template <class T> void deallocate(T *B) XRAY_NEVER_INSTRUMENT {77if (B == nullptr)78return;79uptr RoundedSize = RoundUpTo(sizeof(T), GetPageSizeCached());80#if SANITIZER_FUCHSIA81_zx_vmar_unmap(_zx_vmar_root_self(), reinterpret_cast<uintptr_t>(B),82RoundedSize);83#else84internal_munmap(B, RoundedSize);85#endif86}8788template <class T = unsigned char>89T *allocateBuffer(size_t S) XRAY_NEVER_INSTRUMENT {90uptr RoundedSize = RoundUpTo(S * sizeof(T), GetPageSizeCached());91#if SANITIZER_FUCHSIA92zx_handle_t Vmo;93zx_status_t Status = _zx_vmo_create(RoundedSize, 0, &Vmo);94if (Status != ZX_OK) {95if (Verbosity())96Report("XRay Profiling: Failed to create VMO of size %zu: %s\n", S,97_zx_status_get_string(Status));98return nullptr;99}100uintptr_t B;101Status = _zx_vmar_map(_zx_vmar_root_self(),102ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, Vmo, 0, S, &B);103_zx_handle_close(Vmo);104if (Status != ZX_OK) {105if (Verbosity())106Report("XRay Profiling: Failed to map VMAR of size %zu: %s\n", S,107_zx_status_get_string(Status));108return nullptr;109}110#else111uptr B = internal_mmap(NULL, RoundedSize, PROT_READ | PROT_WRITE,112MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);113int ErrNo = 0;114if (UNLIKELY(internal_iserror(B, &ErrNo))) {115if (Verbosity())116Report("XRay Profiling: Failed to allocate memory of size %zu; Error = "117"%zu\n",118RoundedSize, B);119return nullptr;120}121#endif122return reinterpret_cast<T *>(B);123}124125template <class T> void deallocateBuffer(T *B, size_t S) XRAY_NEVER_INSTRUMENT {126if (B == nullptr)127return;128uptr RoundedSize = RoundUpTo(S * sizeof(T), GetPageSizeCached());129#if SANITIZER_FUCHSIA130_zx_vmar_unmap(_zx_vmar_root_self(), reinterpret_cast<uintptr_t>(B),131RoundedSize);132#else133internal_munmap(B, RoundedSize);134#endif135}136137template <class T, class... U>138T *initArray(size_t N, U &&... Us) XRAY_NEVER_INSTRUMENT {139auto A = allocateBuffer<T>(N);140if (A != nullptr)141while (N > 0)142new (A + (--N)) T(std::forward<U>(Us)...);143return A;144}145146/// The Allocator type hands out fixed-sized chunks of memory that are147/// cache-line aligned and sized. This is useful for placement of148/// performance-sensitive data in memory that's frequently accessed. The149/// allocator also self-limits the peak memory usage to a dynamically defined150/// maximum.151///152/// N is the lower-bound size of the block of memory to return from the153/// allocation function. N is used to compute the size of a block, which is154/// cache-line-size multiples worth of memory. We compute the size of a block by155/// determining how many cache lines worth of memory is required to subsume N.156///157/// The Allocator instance will manage its own memory acquired through mmap.158/// This severely constrains the platforms on which this can be used to POSIX159/// systems where mmap semantics are well-defined.160///161/// FIXME: Isolate the lower-level memory management to a different abstraction162/// that can be platform-specific.163template <size_t N> struct Allocator {164// The Allocator returns memory as Block instances.165struct Block {166/// Compute the minimum cache-line size multiple that is >= N.167static constexpr auto Size = nearest_boundary(N, kCacheLineSize);168void *Data;169};170171private:172size_t MaxMemory{0};173unsigned char *BackingStore = nullptr;174unsigned char *AlignedNextBlock = nullptr;175size_t AllocatedBlocks = 0;176bool Owned;177SpinMutex Mutex{};178179void *Alloc() XRAY_NEVER_INSTRUMENT {180SpinMutexLock Lock(&Mutex);181if (UNLIKELY(BackingStore == nullptr)) {182BackingStore = allocateBuffer(MaxMemory);183if (BackingStore == nullptr) {184if (Verbosity())185Report("XRay Profiling: Failed to allocate memory for allocator\n");186return nullptr;187}188189AlignedNextBlock = BackingStore;190191// Ensure that NextBlock is aligned appropriately.192auto BackingStoreNum = reinterpret_cast<uintptr_t>(BackingStore);193auto AlignedNextBlockNum = nearest_boundary(194reinterpret_cast<uintptr_t>(AlignedNextBlock), kCacheLineSize);195if (diff(AlignedNextBlockNum, BackingStoreNum) > ptrdiff_t(MaxMemory)) {196deallocateBuffer(BackingStore, MaxMemory);197AlignedNextBlock = BackingStore = nullptr;198if (Verbosity())199Report("XRay Profiling: Cannot obtain enough memory from "200"preallocated region\n");201return nullptr;202}203204AlignedNextBlock = reinterpret_cast<unsigned char *>(AlignedNextBlockNum);205206// Assert that AlignedNextBlock is cache-line aligned.207DCHECK_EQ(reinterpret_cast<uintptr_t>(AlignedNextBlock) % kCacheLineSize,2080);209}210211if (((AllocatedBlocks + 1) * Block::Size) > MaxMemory)212return nullptr;213214// Align the pointer we'd like to return to an appropriate alignment, then215// advance the pointer from where to start allocations.216void *Result = AlignedNextBlock;217AlignedNextBlock =218reinterpret_cast<unsigned char *>(AlignedNextBlock) + Block::Size;219++AllocatedBlocks;220return Result;221}222223public:224explicit Allocator(size_t M) XRAY_NEVER_INSTRUMENT225: MaxMemory(RoundUpTo(M, kCacheLineSize)),226BackingStore(nullptr),227AlignedNextBlock(nullptr),228AllocatedBlocks(0),229Owned(true),230Mutex() {}231232explicit Allocator(void *P, size_t M) XRAY_NEVER_INSTRUMENT233: MaxMemory(M),234BackingStore(reinterpret_cast<unsigned char *>(P)),235AlignedNextBlock(reinterpret_cast<unsigned char *>(P)),236AllocatedBlocks(0),237Owned(false),238Mutex() {}239240Allocator(const Allocator &) = delete;241Allocator &operator=(const Allocator &) = delete;242243Allocator(Allocator &&O) XRAY_NEVER_INSTRUMENT {244SpinMutexLock L0(&Mutex);245SpinMutexLock L1(&O.Mutex);246MaxMemory = O.MaxMemory;247O.MaxMemory = 0;248BackingStore = O.BackingStore;249O.BackingStore = nullptr;250AlignedNextBlock = O.AlignedNextBlock;251O.AlignedNextBlock = nullptr;252AllocatedBlocks = O.AllocatedBlocks;253O.AllocatedBlocks = 0;254Owned = O.Owned;255O.Owned = false;256}257258Allocator &operator=(Allocator &&O) XRAY_NEVER_INSTRUMENT {259SpinMutexLock L0(&Mutex);260SpinMutexLock L1(&O.Mutex);261MaxMemory = O.MaxMemory;262O.MaxMemory = 0;263if (BackingStore != nullptr)264deallocateBuffer(BackingStore, MaxMemory);265BackingStore = O.BackingStore;266O.BackingStore = nullptr;267AlignedNextBlock = O.AlignedNextBlock;268O.AlignedNextBlock = nullptr;269AllocatedBlocks = O.AllocatedBlocks;270O.AllocatedBlocks = 0;271Owned = O.Owned;272O.Owned = false;273return *this;274}275276Block Allocate() XRAY_NEVER_INSTRUMENT { return {Alloc()}; }277278~Allocator() NOEXCEPT XRAY_NEVER_INSTRUMENT {279if (Owned && BackingStore != nullptr) {280deallocateBuffer(BackingStore, MaxMemory);281}282}283};284285} // namespace __xray286287#endif // XRAY_ALLOCATOR_H288289290