xref: /openbsd-src/gnu/llvm/compiler-rt/lib/xray/xray_allocator.h (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- xray_allocator.h ---------------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of XRay, a dynamic runtime instrumentation system.
103cab2bb3Spatrick //
113cab2bb3Spatrick // Defines the allocator interface for an arena allocator, used primarily for
123cab2bb3Spatrick // the profiling runtime.
133cab2bb3Spatrick //
143cab2bb3Spatrick //===----------------------------------------------------------------------===//
153cab2bb3Spatrick #ifndef XRAY_ALLOCATOR_H
163cab2bb3Spatrick #define XRAY_ALLOCATOR_H
173cab2bb3Spatrick 
183cab2bb3Spatrick #include "sanitizer_common/sanitizer_common.h"
193cab2bb3Spatrick #include "sanitizer_common/sanitizer_internal_defs.h"
203cab2bb3Spatrick #include "sanitizer_common/sanitizer_mutex.h"
213cab2bb3Spatrick #if SANITIZER_FUCHSIA
223cab2bb3Spatrick #include <zircon/process.h>
233cab2bb3Spatrick #include <zircon/status.h>
243cab2bb3Spatrick #include <zircon/syscalls.h>
253cab2bb3Spatrick #else
263cab2bb3Spatrick #include "sanitizer_common/sanitizer_posix.h"
273cab2bb3Spatrick #endif
283cab2bb3Spatrick #include "xray_defs.h"
293cab2bb3Spatrick #include "xray_utils.h"
303cab2bb3Spatrick #include <cstddef>
313cab2bb3Spatrick #include <cstdint>
323cab2bb3Spatrick #include <sys/mman.h>
333cab2bb3Spatrick 
343cab2bb3Spatrick namespace __xray {
353cab2bb3Spatrick 
363cab2bb3Spatrick // We implement our own memory allocation routine which will bypass the
373cab2bb3Spatrick // internal allocator. This allows us to manage the memory directly, using
383cab2bb3Spatrick // mmap'ed memory to back the allocators.
allocate()393cab2bb3Spatrick template <class T> T *allocate() XRAY_NEVER_INSTRUMENT {
403cab2bb3Spatrick   uptr RoundedSize = RoundUpTo(sizeof(T), GetPageSizeCached());
413cab2bb3Spatrick #if SANITIZER_FUCHSIA
423cab2bb3Spatrick   zx_handle_t Vmo;
433cab2bb3Spatrick   zx_status_t Status = _zx_vmo_create(RoundedSize, 0, &Vmo);
443cab2bb3Spatrick   if (Status != ZX_OK) {
453cab2bb3Spatrick     if (Verbosity())
463cab2bb3Spatrick       Report("XRay Profiling: Failed to create VMO of size %zu: %s\n",
473cab2bb3Spatrick              sizeof(T), _zx_status_get_string(Status));
483cab2bb3Spatrick     return nullptr;
493cab2bb3Spatrick   }
503cab2bb3Spatrick   uintptr_t B;
513cab2bb3Spatrick   Status =
523cab2bb3Spatrick       _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
533cab2bb3Spatrick                    Vmo, 0, sizeof(T), &B);
543cab2bb3Spatrick   _zx_handle_close(Vmo);
553cab2bb3Spatrick   if (Status != ZX_OK) {
563cab2bb3Spatrick     if (Verbosity())
573cab2bb3Spatrick       Report("XRay Profiling: Failed to map VMAR of size %zu: %s\n", sizeof(T),
583cab2bb3Spatrick              _zx_status_get_string(Status));
593cab2bb3Spatrick     return nullptr;
603cab2bb3Spatrick   }
613cab2bb3Spatrick   return reinterpret_cast<T *>(B);
623cab2bb3Spatrick #else
633cab2bb3Spatrick   uptr B = internal_mmap(NULL, RoundedSize, PROT_READ | PROT_WRITE,
643cab2bb3Spatrick                          MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
653cab2bb3Spatrick   int ErrNo = 0;
663cab2bb3Spatrick   if (UNLIKELY(internal_iserror(B, &ErrNo))) {
673cab2bb3Spatrick     if (Verbosity())
68*810390e3Srobert       Report("XRay Profiling: Failed to allocate memory of size %zu; Error = "
69*810390e3Srobert              "%zu\n",
703cab2bb3Spatrick              RoundedSize, B);
713cab2bb3Spatrick     return nullptr;
723cab2bb3Spatrick   }
733cab2bb3Spatrick #endif
743cab2bb3Spatrick   return reinterpret_cast<T *>(B);
753cab2bb3Spatrick }
763cab2bb3Spatrick 
deallocate(T * B)773cab2bb3Spatrick template <class T> void deallocate(T *B) XRAY_NEVER_INSTRUMENT {
783cab2bb3Spatrick   if (B == nullptr)
793cab2bb3Spatrick     return;
803cab2bb3Spatrick   uptr RoundedSize = RoundUpTo(sizeof(T), GetPageSizeCached());
813cab2bb3Spatrick #if SANITIZER_FUCHSIA
823cab2bb3Spatrick   _zx_vmar_unmap(_zx_vmar_root_self(), reinterpret_cast<uintptr_t>(B),
833cab2bb3Spatrick                  RoundedSize);
843cab2bb3Spatrick #else
853cab2bb3Spatrick   internal_munmap(B, RoundedSize);
863cab2bb3Spatrick #endif
873cab2bb3Spatrick }
883cab2bb3Spatrick 
893cab2bb3Spatrick template <class T = unsigned char>
allocateBuffer(size_t S)903cab2bb3Spatrick T *allocateBuffer(size_t S) XRAY_NEVER_INSTRUMENT {
913cab2bb3Spatrick   uptr RoundedSize = RoundUpTo(S * sizeof(T), GetPageSizeCached());
923cab2bb3Spatrick #if SANITIZER_FUCHSIA
933cab2bb3Spatrick   zx_handle_t Vmo;
943cab2bb3Spatrick   zx_status_t Status = _zx_vmo_create(RoundedSize, 0, &Vmo);
953cab2bb3Spatrick   if (Status != ZX_OK) {
963cab2bb3Spatrick     if (Verbosity())
973cab2bb3Spatrick       Report("XRay Profiling: Failed to create VMO of size %zu: %s\n", S,
983cab2bb3Spatrick              _zx_status_get_string(Status));
993cab2bb3Spatrick     return nullptr;
1003cab2bb3Spatrick   }
1013cab2bb3Spatrick   uintptr_t B;
1023cab2bb3Spatrick   Status = _zx_vmar_map(_zx_vmar_root_self(),
1033cab2bb3Spatrick                         ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, Vmo, 0, S, &B);
1043cab2bb3Spatrick   _zx_handle_close(Vmo);
1053cab2bb3Spatrick   if (Status != ZX_OK) {
1063cab2bb3Spatrick     if (Verbosity())
1073cab2bb3Spatrick       Report("XRay Profiling: Failed to map VMAR of size %zu: %s\n", S,
1083cab2bb3Spatrick              _zx_status_get_string(Status));
1093cab2bb3Spatrick     return nullptr;
1103cab2bb3Spatrick   }
1113cab2bb3Spatrick #else
1123cab2bb3Spatrick   uptr B = internal_mmap(NULL, RoundedSize, PROT_READ | PROT_WRITE,
1133cab2bb3Spatrick                          MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1143cab2bb3Spatrick   int ErrNo = 0;
1153cab2bb3Spatrick   if (UNLIKELY(internal_iserror(B, &ErrNo))) {
1163cab2bb3Spatrick     if (Verbosity())
117*810390e3Srobert       Report("XRay Profiling: Failed to allocate memory of size %zu; Error = "
118*810390e3Srobert              "%zu\n",
1193cab2bb3Spatrick              RoundedSize, B);
1203cab2bb3Spatrick     return nullptr;
1213cab2bb3Spatrick   }
1223cab2bb3Spatrick #endif
1233cab2bb3Spatrick   return reinterpret_cast<T *>(B);
1243cab2bb3Spatrick }
1253cab2bb3Spatrick 
deallocateBuffer(T * B,size_t S)1263cab2bb3Spatrick template <class T> void deallocateBuffer(T *B, size_t S) XRAY_NEVER_INSTRUMENT {
1273cab2bb3Spatrick   if (B == nullptr)
1283cab2bb3Spatrick     return;
1293cab2bb3Spatrick   uptr RoundedSize = RoundUpTo(S * sizeof(T), GetPageSizeCached());
1303cab2bb3Spatrick #if SANITIZER_FUCHSIA
1313cab2bb3Spatrick   _zx_vmar_unmap(_zx_vmar_root_self(), reinterpret_cast<uintptr_t>(B),
1323cab2bb3Spatrick                  RoundedSize);
1333cab2bb3Spatrick #else
1343cab2bb3Spatrick   internal_munmap(B, RoundedSize);
1353cab2bb3Spatrick #endif
1363cab2bb3Spatrick }
1373cab2bb3Spatrick 
1383cab2bb3Spatrick template <class T, class... U>
initArray(size_t N,U &&...Us)1393cab2bb3Spatrick T *initArray(size_t N, U &&... Us) XRAY_NEVER_INSTRUMENT {
1403cab2bb3Spatrick   auto A = allocateBuffer<T>(N);
1413cab2bb3Spatrick   if (A != nullptr)
1423cab2bb3Spatrick     while (N > 0)
1433cab2bb3Spatrick       new (A + (--N)) T(std::forward<U>(Us)...);
1443cab2bb3Spatrick   return A;
1453cab2bb3Spatrick }
1463cab2bb3Spatrick 
1473cab2bb3Spatrick /// The Allocator type hands out fixed-sized chunks of memory that are
1483cab2bb3Spatrick /// cache-line aligned and sized. This is useful for placement of
1493cab2bb3Spatrick /// performance-sensitive data in memory that's frequently accessed. The
1503cab2bb3Spatrick /// allocator also self-limits the peak memory usage to a dynamically defined
1513cab2bb3Spatrick /// maximum.
1523cab2bb3Spatrick ///
1533cab2bb3Spatrick /// N is the lower-bound size of the block of memory to return from the
1543cab2bb3Spatrick /// allocation function. N is used to compute the size of a block, which is
1553cab2bb3Spatrick /// cache-line-size multiples worth of memory. We compute the size of a block by
1563cab2bb3Spatrick /// determining how many cache lines worth of memory is required to subsume N.
1573cab2bb3Spatrick ///
1583cab2bb3Spatrick /// The Allocator instance will manage its own memory acquired through mmap.
1593cab2bb3Spatrick /// This severely constrains the platforms on which this can be used to POSIX
1603cab2bb3Spatrick /// systems where mmap semantics are well-defined.
1613cab2bb3Spatrick ///
1623cab2bb3Spatrick /// FIXME: Isolate the lower-level memory management to a different abstraction
1633cab2bb3Spatrick /// that can be platform-specific.
1643cab2bb3Spatrick template <size_t N> struct Allocator {
1653cab2bb3Spatrick   // The Allocator returns memory as Block instances.
1663cab2bb3Spatrick   struct Block {
1673cab2bb3Spatrick     /// Compute the minimum cache-line size multiple that is >= N.
1683cab2bb3Spatrick     static constexpr auto Size = nearest_boundary(N, kCacheLineSize);
1693cab2bb3Spatrick     void *Data;
1703cab2bb3Spatrick   };
1713cab2bb3Spatrick 
1723cab2bb3Spatrick private:
1733cab2bb3Spatrick   size_t MaxMemory{0};
1743cab2bb3Spatrick   unsigned char *BackingStore = nullptr;
1753cab2bb3Spatrick   unsigned char *AlignedNextBlock = nullptr;
1763cab2bb3Spatrick   size_t AllocatedBlocks = 0;
1773cab2bb3Spatrick   bool Owned;
1783cab2bb3Spatrick   SpinMutex Mutex{};
1793cab2bb3Spatrick 
AllocAllocator1803cab2bb3Spatrick   void *Alloc() XRAY_NEVER_INSTRUMENT {
1813cab2bb3Spatrick     SpinMutexLock Lock(&Mutex);
1823cab2bb3Spatrick     if (UNLIKELY(BackingStore == nullptr)) {
1833cab2bb3Spatrick       BackingStore = allocateBuffer(MaxMemory);
1843cab2bb3Spatrick       if (BackingStore == nullptr) {
1853cab2bb3Spatrick         if (Verbosity())
186*810390e3Srobert           Report("XRay Profiling: Failed to allocate memory for allocator\n");
1873cab2bb3Spatrick         return nullptr;
1883cab2bb3Spatrick       }
1893cab2bb3Spatrick 
1903cab2bb3Spatrick       AlignedNextBlock = BackingStore;
1913cab2bb3Spatrick 
1923cab2bb3Spatrick       // Ensure that NextBlock is aligned appropriately.
1933cab2bb3Spatrick       auto BackingStoreNum = reinterpret_cast<uintptr_t>(BackingStore);
1943cab2bb3Spatrick       auto AlignedNextBlockNum = nearest_boundary(
1953cab2bb3Spatrick           reinterpret_cast<uintptr_t>(AlignedNextBlock), kCacheLineSize);
1963cab2bb3Spatrick       if (diff(AlignedNextBlockNum, BackingStoreNum) > ptrdiff_t(MaxMemory)) {
1973cab2bb3Spatrick         deallocateBuffer(BackingStore, MaxMemory);
1983cab2bb3Spatrick         AlignedNextBlock = BackingStore = nullptr;
1993cab2bb3Spatrick         if (Verbosity())
2003cab2bb3Spatrick           Report("XRay Profiling: Cannot obtain enough memory from "
201*810390e3Srobert                  "preallocated region\n");
2023cab2bb3Spatrick         return nullptr;
2033cab2bb3Spatrick       }
2043cab2bb3Spatrick 
2053cab2bb3Spatrick       AlignedNextBlock = reinterpret_cast<unsigned char *>(AlignedNextBlockNum);
2063cab2bb3Spatrick 
2073cab2bb3Spatrick       // Assert that AlignedNextBlock is cache-line aligned.
2083cab2bb3Spatrick       DCHECK_EQ(reinterpret_cast<uintptr_t>(AlignedNextBlock) % kCacheLineSize,
2093cab2bb3Spatrick                 0);
2103cab2bb3Spatrick     }
2113cab2bb3Spatrick 
2123cab2bb3Spatrick     if (((AllocatedBlocks + 1) * Block::Size) > MaxMemory)
2133cab2bb3Spatrick       return nullptr;
2143cab2bb3Spatrick 
2153cab2bb3Spatrick     // Align the pointer we'd like to return to an appropriate alignment, then
2163cab2bb3Spatrick     // advance the pointer from where to start allocations.
2173cab2bb3Spatrick     void *Result = AlignedNextBlock;
2183cab2bb3Spatrick     AlignedNextBlock =
2193cab2bb3Spatrick         reinterpret_cast<unsigned char *>(AlignedNextBlock) + Block::Size;
2203cab2bb3Spatrick     ++AllocatedBlocks;
2213cab2bb3Spatrick     return Result;
2223cab2bb3Spatrick   }
2233cab2bb3Spatrick 
2243cab2bb3Spatrick public:
AllocatorAllocator2253cab2bb3Spatrick   explicit Allocator(size_t M) XRAY_NEVER_INSTRUMENT
2263cab2bb3Spatrick       : MaxMemory(RoundUpTo(M, kCacheLineSize)),
2273cab2bb3Spatrick         BackingStore(nullptr),
2283cab2bb3Spatrick         AlignedNextBlock(nullptr),
2293cab2bb3Spatrick         AllocatedBlocks(0),
2303cab2bb3Spatrick         Owned(true),
2313cab2bb3Spatrick         Mutex() {}
2323cab2bb3Spatrick 
AllocatorAllocator2333cab2bb3Spatrick   explicit Allocator(void *P, size_t M) XRAY_NEVER_INSTRUMENT
2343cab2bb3Spatrick       : MaxMemory(M),
2353cab2bb3Spatrick         BackingStore(reinterpret_cast<unsigned char *>(P)),
2363cab2bb3Spatrick         AlignedNextBlock(reinterpret_cast<unsigned char *>(P)),
2373cab2bb3Spatrick         AllocatedBlocks(0),
2383cab2bb3Spatrick         Owned(false),
2393cab2bb3Spatrick         Mutex() {}
2403cab2bb3Spatrick 
2413cab2bb3Spatrick   Allocator(const Allocator &) = delete;
2423cab2bb3Spatrick   Allocator &operator=(const Allocator &) = delete;
2433cab2bb3Spatrick 
AllocatorAllocator2443cab2bb3Spatrick   Allocator(Allocator &&O) XRAY_NEVER_INSTRUMENT {
2453cab2bb3Spatrick     SpinMutexLock L0(&Mutex);
2463cab2bb3Spatrick     SpinMutexLock L1(&O.Mutex);
2473cab2bb3Spatrick     MaxMemory = O.MaxMemory;
2483cab2bb3Spatrick     O.MaxMemory = 0;
2493cab2bb3Spatrick     BackingStore = O.BackingStore;
2503cab2bb3Spatrick     O.BackingStore = nullptr;
2513cab2bb3Spatrick     AlignedNextBlock = O.AlignedNextBlock;
2523cab2bb3Spatrick     O.AlignedNextBlock = nullptr;
2533cab2bb3Spatrick     AllocatedBlocks = O.AllocatedBlocks;
2543cab2bb3Spatrick     O.AllocatedBlocks = 0;
2553cab2bb3Spatrick     Owned = O.Owned;
2563cab2bb3Spatrick     O.Owned = false;
2573cab2bb3Spatrick   }
2583cab2bb3Spatrick 
2593cab2bb3Spatrick   Allocator &operator=(Allocator &&O) XRAY_NEVER_INSTRUMENT {
2603cab2bb3Spatrick     SpinMutexLock L0(&Mutex);
2613cab2bb3Spatrick     SpinMutexLock L1(&O.Mutex);
2623cab2bb3Spatrick     MaxMemory = O.MaxMemory;
2633cab2bb3Spatrick     O.MaxMemory = 0;
2643cab2bb3Spatrick     if (BackingStore != nullptr)
2653cab2bb3Spatrick       deallocateBuffer(BackingStore, MaxMemory);
2663cab2bb3Spatrick     BackingStore = O.BackingStore;
2673cab2bb3Spatrick     O.BackingStore = nullptr;
2683cab2bb3Spatrick     AlignedNextBlock = O.AlignedNextBlock;
2693cab2bb3Spatrick     O.AlignedNextBlock = nullptr;
2703cab2bb3Spatrick     AllocatedBlocks = O.AllocatedBlocks;
2713cab2bb3Spatrick     O.AllocatedBlocks = 0;
2723cab2bb3Spatrick     Owned = O.Owned;
2733cab2bb3Spatrick     O.Owned = false;
2743cab2bb3Spatrick     return *this;
2753cab2bb3Spatrick   }
2763cab2bb3Spatrick 
AllocateAllocator2773cab2bb3Spatrick   Block Allocate() XRAY_NEVER_INSTRUMENT { return {Alloc()}; }
2783cab2bb3Spatrick 
~AllocatorAllocator2793cab2bb3Spatrick   ~Allocator() NOEXCEPT XRAY_NEVER_INSTRUMENT {
2803cab2bb3Spatrick     if (Owned && BackingStore != nullptr) {
2813cab2bb3Spatrick       deallocateBuffer(BackingStore, MaxMemory);
2823cab2bb3Spatrick     }
2833cab2bb3Spatrick   }
2843cab2bb3Spatrick };
2853cab2bb3Spatrick 
2863cab2bb3Spatrick } // namespace __xray
2873cab2bb3Spatrick 
2883cab2bb3Spatrick #endif // XRAY_ALLOCATOR_H
289