10b57cec5SDimitry Andric //===-- xray_allocator.h ---------------------------------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file is a part of XRay, a dynamic runtime instrumentation system.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric // Defines the allocator interface for an arena allocator, used primarily for
120b57cec5SDimitry Andric // the profiling runtime.
130b57cec5SDimitry Andric //
140b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
150b57cec5SDimitry Andric #ifndef XRAY_ALLOCATOR_H
160b57cec5SDimitry Andric #define XRAY_ALLOCATOR_H
170b57cec5SDimitry Andric
180b57cec5SDimitry Andric #include "sanitizer_common/sanitizer_common.h"
190b57cec5SDimitry Andric #include "sanitizer_common/sanitizer_internal_defs.h"
200b57cec5SDimitry Andric #include "sanitizer_common/sanitizer_mutex.h"
210b57cec5SDimitry Andric #if SANITIZER_FUCHSIA
220b57cec5SDimitry Andric #include <zircon/process.h>
230b57cec5SDimitry Andric #include <zircon/status.h>
240b57cec5SDimitry Andric #include <zircon/syscalls.h>
250b57cec5SDimitry Andric #else
260b57cec5SDimitry Andric #include "sanitizer_common/sanitizer_posix.h"
270b57cec5SDimitry Andric #endif
280b57cec5SDimitry Andric #include "xray_defs.h"
290b57cec5SDimitry Andric #include "xray_utils.h"
300b57cec5SDimitry Andric #include <cstddef>
310b57cec5SDimitry Andric #include <cstdint>
320b57cec5SDimitry Andric #include <sys/mman.h>
330b57cec5SDimitry Andric
340b57cec5SDimitry Andric namespace __xray {
350b57cec5SDimitry Andric
360b57cec5SDimitry Andric // We implement our own memory allocation routine which will bypass the
370b57cec5SDimitry Andric // internal allocator. This allows us to manage the memory directly, using
380b57cec5SDimitry Andric // mmap'ed memory to back the allocators.
allocate()390b57cec5SDimitry Andric template <class T> T *allocate() XRAY_NEVER_INSTRUMENT {
400b57cec5SDimitry Andric uptr RoundedSize = RoundUpTo(sizeof(T), GetPageSizeCached());
410b57cec5SDimitry Andric #if SANITIZER_FUCHSIA
420b57cec5SDimitry Andric zx_handle_t Vmo;
430b57cec5SDimitry Andric zx_status_t Status = _zx_vmo_create(RoundedSize, 0, &Vmo);
440b57cec5SDimitry Andric if (Status != ZX_OK) {
450b57cec5SDimitry Andric if (Verbosity())
460b57cec5SDimitry Andric Report("XRay Profiling: Failed to create VMO of size %zu: %s\n",
470b57cec5SDimitry Andric sizeof(T), _zx_status_get_string(Status));
480b57cec5SDimitry Andric return nullptr;
490b57cec5SDimitry Andric }
500b57cec5SDimitry Andric uintptr_t B;
510b57cec5SDimitry Andric Status =
520b57cec5SDimitry Andric _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
530b57cec5SDimitry Andric Vmo, 0, sizeof(T), &B);
540b57cec5SDimitry Andric _zx_handle_close(Vmo);
550b57cec5SDimitry Andric if (Status != ZX_OK) {
560b57cec5SDimitry Andric if (Verbosity())
570b57cec5SDimitry Andric Report("XRay Profiling: Failed to map VMAR of size %zu: %s\n", sizeof(T),
580b57cec5SDimitry Andric _zx_status_get_string(Status));
590b57cec5SDimitry Andric return nullptr;
600b57cec5SDimitry Andric }
610b57cec5SDimitry Andric return reinterpret_cast<T *>(B);
620b57cec5SDimitry Andric #else
630b57cec5SDimitry Andric uptr B = internal_mmap(NULL, RoundedSize, PROT_READ | PROT_WRITE,
640b57cec5SDimitry Andric MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
650b57cec5SDimitry Andric int ErrNo = 0;
660b57cec5SDimitry Andric if (UNLIKELY(internal_iserror(B, &ErrNo))) {
670b57cec5SDimitry Andric if (Verbosity())
68*0eae32dcSDimitry Andric Report("XRay Profiling: Failed to allocate memory of size %zu; Error = "
69*0eae32dcSDimitry Andric "%zu\n",
700b57cec5SDimitry Andric RoundedSize, B);
710b57cec5SDimitry Andric return nullptr;
720b57cec5SDimitry Andric }
730b57cec5SDimitry Andric #endif
740b57cec5SDimitry Andric return reinterpret_cast<T *>(B);
750b57cec5SDimitry Andric }
760b57cec5SDimitry Andric
deallocate(T * B)770b57cec5SDimitry Andric template <class T> void deallocate(T *B) XRAY_NEVER_INSTRUMENT {
780b57cec5SDimitry Andric if (B == nullptr)
790b57cec5SDimitry Andric return;
800b57cec5SDimitry Andric uptr RoundedSize = RoundUpTo(sizeof(T), GetPageSizeCached());
810b57cec5SDimitry Andric #if SANITIZER_FUCHSIA
820b57cec5SDimitry Andric _zx_vmar_unmap(_zx_vmar_root_self(), reinterpret_cast<uintptr_t>(B),
830b57cec5SDimitry Andric RoundedSize);
840b57cec5SDimitry Andric #else
850b57cec5SDimitry Andric internal_munmap(B, RoundedSize);
860b57cec5SDimitry Andric #endif
870b57cec5SDimitry Andric }
880b57cec5SDimitry Andric
890b57cec5SDimitry Andric template <class T = unsigned char>
allocateBuffer(size_t S)900b57cec5SDimitry Andric T *allocateBuffer(size_t S) XRAY_NEVER_INSTRUMENT {
910b57cec5SDimitry Andric uptr RoundedSize = RoundUpTo(S * sizeof(T), GetPageSizeCached());
920b57cec5SDimitry Andric #if SANITIZER_FUCHSIA
930b57cec5SDimitry Andric zx_handle_t Vmo;
940b57cec5SDimitry Andric zx_status_t Status = _zx_vmo_create(RoundedSize, 0, &Vmo);
950b57cec5SDimitry Andric if (Status != ZX_OK) {
960b57cec5SDimitry Andric if (Verbosity())
970b57cec5SDimitry Andric Report("XRay Profiling: Failed to create VMO of size %zu: %s\n", S,
980b57cec5SDimitry Andric _zx_status_get_string(Status));
990b57cec5SDimitry Andric return nullptr;
1000b57cec5SDimitry Andric }
1010b57cec5SDimitry Andric uintptr_t B;
1020b57cec5SDimitry Andric Status = _zx_vmar_map(_zx_vmar_root_self(),
1030b57cec5SDimitry Andric ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, Vmo, 0, S, &B);
1040b57cec5SDimitry Andric _zx_handle_close(Vmo);
1050b57cec5SDimitry Andric if (Status != ZX_OK) {
1060b57cec5SDimitry Andric if (Verbosity())
1070b57cec5SDimitry Andric Report("XRay Profiling: Failed to map VMAR of size %zu: %s\n", S,
1080b57cec5SDimitry Andric _zx_status_get_string(Status));
1090b57cec5SDimitry Andric return nullptr;
1100b57cec5SDimitry Andric }
1110b57cec5SDimitry Andric #else
1120b57cec5SDimitry Andric uptr B = internal_mmap(NULL, RoundedSize, PROT_READ | PROT_WRITE,
1130b57cec5SDimitry Andric MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
1140b57cec5SDimitry Andric int ErrNo = 0;
1150b57cec5SDimitry Andric if (UNLIKELY(internal_iserror(B, &ErrNo))) {
1160b57cec5SDimitry Andric if (Verbosity())
117*0eae32dcSDimitry Andric Report("XRay Profiling: Failed to allocate memory of size %zu; Error = "
118*0eae32dcSDimitry Andric "%zu\n",
1190b57cec5SDimitry Andric RoundedSize, B);
1200b57cec5SDimitry Andric return nullptr;
1210b57cec5SDimitry Andric }
1220b57cec5SDimitry Andric #endif
1230b57cec5SDimitry Andric return reinterpret_cast<T *>(B);
1240b57cec5SDimitry Andric }
1250b57cec5SDimitry Andric
deallocateBuffer(T * B,size_t S)1260b57cec5SDimitry Andric template <class T> void deallocateBuffer(T *B, size_t S) XRAY_NEVER_INSTRUMENT {
1270b57cec5SDimitry Andric if (B == nullptr)
1280b57cec5SDimitry Andric return;
1290b57cec5SDimitry Andric uptr RoundedSize = RoundUpTo(S * sizeof(T), GetPageSizeCached());
1300b57cec5SDimitry Andric #if SANITIZER_FUCHSIA
1310b57cec5SDimitry Andric _zx_vmar_unmap(_zx_vmar_root_self(), reinterpret_cast<uintptr_t>(B),
1320b57cec5SDimitry Andric RoundedSize);
1330b57cec5SDimitry Andric #else
1340b57cec5SDimitry Andric internal_munmap(B, RoundedSize);
1350b57cec5SDimitry Andric #endif
1360b57cec5SDimitry Andric }
1370b57cec5SDimitry Andric
1380b57cec5SDimitry Andric template <class T, class... U>
initArray(size_t N,U &&...Us)1390b57cec5SDimitry Andric T *initArray(size_t N, U &&... Us) XRAY_NEVER_INSTRUMENT {
1400b57cec5SDimitry Andric auto A = allocateBuffer<T>(N);
1410b57cec5SDimitry Andric if (A != nullptr)
1420b57cec5SDimitry Andric while (N > 0)
1430b57cec5SDimitry Andric new (A + (--N)) T(std::forward<U>(Us)...);
1440b57cec5SDimitry Andric return A;
1450b57cec5SDimitry Andric }
1460b57cec5SDimitry Andric
1470b57cec5SDimitry Andric /// The Allocator type hands out fixed-sized chunks of memory that are
1480b57cec5SDimitry Andric /// cache-line aligned and sized. This is useful for placement of
1490b57cec5SDimitry Andric /// performance-sensitive data in memory that's frequently accessed. The
1500b57cec5SDimitry Andric /// allocator also self-limits the peak memory usage to a dynamically defined
1510b57cec5SDimitry Andric /// maximum.
1520b57cec5SDimitry Andric ///
1530b57cec5SDimitry Andric /// N is the lower-bound size of the block of memory to return from the
1540b57cec5SDimitry Andric /// allocation function. N is used to compute the size of a block, which is
1550b57cec5SDimitry Andric /// cache-line-size multiples worth of memory. We compute the size of a block by
1560b57cec5SDimitry Andric /// determining how many cache lines worth of memory is required to subsume N.
1570b57cec5SDimitry Andric ///
1580b57cec5SDimitry Andric /// The Allocator instance will manage its own memory acquired through mmap.
1590b57cec5SDimitry Andric /// This severely constrains the platforms on which this can be used to POSIX
1600b57cec5SDimitry Andric /// systems where mmap semantics are well-defined.
1610b57cec5SDimitry Andric ///
1620b57cec5SDimitry Andric /// FIXME: Isolate the lower-level memory management to a different abstraction
1630b57cec5SDimitry Andric /// that can be platform-specific.
1640b57cec5SDimitry Andric template <size_t N> struct Allocator {
1650b57cec5SDimitry Andric // The Allocator returns memory as Block instances.
1660b57cec5SDimitry Andric struct Block {
1670b57cec5SDimitry Andric /// Compute the minimum cache-line size multiple that is >= N.
1680b57cec5SDimitry Andric static constexpr auto Size = nearest_boundary(N, kCacheLineSize);
1690b57cec5SDimitry Andric void *Data;
1700b57cec5SDimitry Andric };
1710b57cec5SDimitry Andric
1720b57cec5SDimitry Andric private:
1730b57cec5SDimitry Andric size_t MaxMemory{0};
1740b57cec5SDimitry Andric unsigned char *BackingStore = nullptr;
1750b57cec5SDimitry Andric unsigned char *AlignedNextBlock = nullptr;
1760b57cec5SDimitry Andric size_t AllocatedBlocks = 0;
1770b57cec5SDimitry Andric bool Owned;
1780b57cec5SDimitry Andric SpinMutex Mutex{};
1790b57cec5SDimitry Andric
AllocAllocator1800b57cec5SDimitry Andric void *Alloc() XRAY_NEVER_INSTRUMENT {
1810b57cec5SDimitry Andric SpinMutexLock Lock(&Mutex);
1820b57cec5SDimitry Andric if (UNLIKELY(BackingStore == nullptr)) {
1830b57cec5SDimitry Andric BackingStore = allocateBuffer(MaxMemory);
1840b57cec5SDimitry Andric if (BackingStore == nullptr) {
1850b57cec5SDimitry Andric if (Verbosity())
186*0eae32dcSDimitry Andric Report("XRay Profiling: Failed to allocate memory for allocator\n");
1870b57cec5SDimitry Andric return nullptr;
1880b57cec5SDimitry Andric }
1890b57cec5SDimitry Andric
1900b57cec5SDimitry Andric AlignedNextBlock = BackingStore;
1910b57cec5SDimitry Andric
1920b57cec5SDimitry Andric // Ensure that NextBlock is aligned appropriately.
1930b57cec5SDimitry Andric auto BackingStoreNum = reinterpret_cast<uintptr_t>(BackingStore);
1940b57cec5SDimitry Andric auto AlignedNextBlockNum = nearest_boundary(
1950b57cec5SDimitry Andric reinterpret_cast<uintptr_t>(AlignedNextBlock), kCacheLineSize);
1960b57cec5SDimitry Andric if (diff(AlignedNextBlockNum, BackingStoreNum) > ptrdiff_t(MaxMemory)) {
1970b57cec5SDimitry Andric deallocateBuffer(BackingStore, MaxMemory);
1980b57cec5SDimitry Andric AlignedNextBlock = BackingStore = nullptr;
1990b57cec5SDimitry Andric if (Verbosity())
2000b57cec5SDimitry Andric Report("XRay Profiling: Cannot obtain enough memory from "
201*0eae32dcSDimitry Andric "preallocated region\n");
2020b57cec5SDimitry Andric return nullptr;
2030b57cec5SDimitry Andric }
2040b57cec5SDimitry Andric
2050b57cec5SDimitry Andric AlignedNextBlock = reinterpret_cast<unsigned char *>(AlignedNextBlockNum);
2060b57cec5SDimitry Andric
2070b57cec5SDimitry Andric // Assert that AlignedNextBlock is cache-line aligned.
2080b57cec5SDimitry Andric DCHECK_EQ(reinterpret_cast<uintptr_t>(AlignedNextBlock) % kCacheLineSize,
2090b57cec5SDimitry Andric 0);
2100b57cec5SDimitry Andric }
2110b57cec5SDimitry Andric
2120b57cec5SDimitry Andric if (((AllocatedBlocks + 1) * Block::Size) > MaxMemory)
2130b57cec5SDimitry Andric return nullptr;
2140b57cec5SDimitry Andric
2150b57cec5SDimitry Andric // Align the pointer we'd like to return to an appropriate alignment, then
2160b57cec5SDimitry Andric // advance the pointer from where to start allocations.
2170b57cec5SDimitry Andric void *Result = AlignedNextBlock;
2180b57cec5SDimitry Andric AlignedNextBlock =
2190b57cec5SDimitry Andric reinterpret_cast<unsigned char *>(AlignedNextBlock) + Block::Size;
2200b57cec5SDimitry Andric ++AllocatedBlocks;
2210b57cec5SDimitry Andric return Result;
2220b57cec5SDimitry Andric }
2230b57cec5SDimitry Andric
2240b57cec5SDimitry Andric public:
AllocatorAllocator2250b57cec5SDimitry Andric explicit Allocator(size_t M) XRAY_NEVER_INSTRUMENT
2260b57cec5SDimitry Andric : MaxMemory(RoundUpTo(M, kCacheLineSize)),
2270b57cec5SDimitry Andric BackingStore(nullptr),
2280b57cec5SDimitry Andric AlignedNextBlock(nullptr),
2290b57cec5SDimitry Andric AllocatedBlocks(0),
2300b57cec5SDimitry Andric Owned(true),
2310b57cec5SDimitry Andric Mutex() {}
2320b57cec5SDimitry Andric
AllocatorAllocator2330b57cec5SDimitry Andric explicit Allocator(void *P, size_t M) XRAY_NEVER_INSTRUMENT
2340b57cec5SDimitry Andric : MaxMemory(M),
2350b57cec5SDimitry Andric BackingStore(reinterpret_cast<unsigned char *>(P)),
2360b57cec5SDimitry Andric AlignedNextBlock(reinterpret_cast<unsigned char *>(P)),
2370b57cec5SDimitry Andric AllocatedBlocks(0),
2380b57cec5SDimitry Andric Owned(false),
2390b57cec5SDimitry Andric Mutex() {}
2400b57cec5SDimitry Andric
2410b57cec5SDimitry Andric Allocator(const Allocator &) = delete;
2420b57cec5SDimitry Andric Allocator &operator=(const Allocator &) = delete;
2430b57cec5SDimitry Andric
AllocatorAllocator2440b57cec5SDimitry Andric Allocator(Allocator &&O) XRAY_NEVER_INSTRUMENT {
2450b57cec5SDimitry Andric SpinMutexLock L0(&Mutex);
2460b57cec5SDimitry Andric SpinMutexLock L1(&O.Mutex);
2470b57cec5SDimitry Andric MaxMemory = O.MaxMemory;
2480b57cec5SDimitry Andric O.MaxMemory = 0;
2490b57cec5SDimitry Andric BackingStore = O.BackingStore;
2500b57cec5SDimitry Andric O.BackingStore = nullptr;
2510b57cec5SDimitry Andric AlignedNextBlock = O.AlignedNextBlock;
2520b57cec5SDimitry Andric O.AlignedNextBlock = nullptr;
2530b57cec5SDimitry Andric AllocatedBlocks = O.AllocatedBlocks;
2540b57cec5SDimitry Andric O.AllocatedBlocks = 0;
2550b57cec5SDimitry Andric Owned = O.Owned;
2560b57cec5SDimitry Andric O.Owned = false;
2570b57cec5SDimitry Andric }
2580b57cec5SDimitry Andric
2590b57cec5SDimitry Andric Allocator &operator=(Allocator &&O) XRAY_NEVER_INSTRUMENT {
2600b57cec5SDimitry Andric SpinMutexLock L0(&Mutex);
2610b57cec5SDimitry Andric SpinMutexLock L1(&O.Mutex);
2620b57cec5SDimitry Andric MaxMemory = O.MaxMemory;
2630b57cec5SDimitry Andric O.MaxMemory = 0;
2640b57cec5SDimitry Andric if (BackingStore != nullptr)
2650b57cec5SDimitry Andric deallocateBuffer(BackingStore, MaxMemory);
2660b57cec5SDimitry Andric BackingStore = O.BackingStore;
2670b57cec5SDimitry Andric O.BackingStore = nullptr;
2680b57cec5SDimitry Andric AlignedNextBlock = O.AlignedNextBlock;
2690b57cec5SDimitry Andric O.AlignedNextBlock = nullptr;
2700b57cec5SDimitry Andric AllocatedBlocks = O.AllocatedBlocks;
2710b57cec5SDimitry Andric O.AllocatedBlocks = 0;
2720b57cec5SDimitry Andric Owned = O.Owned;
2730b57cec5SDimitry Andric O.Owned = false;
2740b57cec5SDimitry Andric return *this;
2750b57cec5SDimitry Andric }
2760b57cec5SDimitry Andric
AllocateAllocator2770b57cec5SDimitry Andric Block Allocate() XRAY_NEVER_INSTRUMENT { return {Alloc()}; }
2780b57cec5SDimitry Andric
~AllocatorAllocator2790b57cec5SDimitry Andric ~Allocator() NOEXCEPT XRAY_NEVER_INSTRUMENT {
2800b57cec5SDimitry Andric if (Owned && BackingStore != nullptr) {
2810b57cec5SDimitry Andric deallocateBuffer(BackingStore, MaxMemory);
2820b57cec5SDimitry Andric }
2830b57cec5SDimitry Andric }
2840b57cec5SDimitry Andric };
2850b57cec5SDimitry Andric
2860b57cec5SDimitry Andric } // namespace __xray
2870b57cec5SDimitry Andric
2880b57cec5SDimitry Andric #endif // XRAY_ALLOCATOR_H
289