]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/xray/xray_allocator.h
Merge compiler-rt trunk r351319, and resolve conflicts.
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / xray / xray_allocator.h
1 //===-- xray_allocator.h ---------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file is a part of XRay, a dynamic runtime instrumentation system.
11 //
12 // Defines the allocator interface for an arena allocator, used primarily for
13 // the profiling runtime.
14 //
15 //===----------------------------------------------------------------------===//
16 #ifndef XRAY_ALLOCATOR_H
17 #define XRAY_ALLOCATOR_H
18
19 #include "sanitizer_common/sanitizer_common.h"
20 #include "sanitizer_common/sanitizer_internal_defs.h"
21 #include "sanitizer_common/sanitizer_mutex.h"
22 #if SANITIZER_FUCHSIA
23 #include <zircon/process.h>
24 #include <zircon/status.h>
25 #include <zircon/syscalls.h>
26 #else
27 #include "sanitizer_common/sanitizer_posix.h"
28 #endif
29 #include "xray_defs.h"
30 #include "xray_utils.h"
31 #include <cstddef>
32 #include <cstdint>
33 #include <sys/mman.h>
34
35 namespace __xray {
36
37 // We implement our own memory allocation routine which will bypass the
38 // internal allocator. This allows us to manage the memory directly, using
39 // mmap'ed memory to back the allocators.
40 template <class T> T *allocate() XRAY_NEVER_INSTRUMENT {
41   uptr RoundedSize = RoundUpTo(sizeof(T), GetPageSizeCached());
42 #if SANITIZER_FUCHSIA
43   zx_handle_t Vmo;
44   zx_status_t Status = _zx_vmo_create(RoundedSize, 0, &Vmo);
45   if (Status != ZX_OK) {
46     if (Verbosity())
47       Report("XRay Profiling: Failed to create VMO of size %zu: %s\n",
48              sizeof(T), _zx_status_get_string(Status));
49     return nullptr;
50   }
51   uintptr_t B;
52   Status =
53       _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
54                    Vmo, 0, sizeof(T), &B);
55   _zx_handle_close(Vmo);
56   if (Status != ZX_OK) {
57     if (Verbosity())
58       Report("XRay Profiling: Failed to map VMAR of size %zu: %s\n", sizeof(T),
59              _zx_status_get_string(Status));
60     return nullptr;
61   }
62   return reinterpret_cast<T *>(B);
63 #else
64   uptr B = internal_mmap(NULL, RoundedSize, PROT_READ | PROT_WRITE,
65                          MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
66   int ErrNo = 0;
67   if (UNLIKELY(internal_iserror(B, &ErrNo))) {
68     if (Verbosity())
69       Report(
70           "XRay Profiling: Failed to allocate memory of size %d; Error = %d.\n",
71           RoundedSize, B);
72     return nullptr;
73   }
74 #endif
75   return reinterpret_cast<T *>(B);
76 }
77
78 template <class T> void deallocate(T *B) XRAY_NEVER_INSTRUMENT {
79   if (B == nullptr)
80     return;
81   uptr RoundedSize = RoundUpTo(sizeof(T), GetPageSizeCached());
82 #if SANITIZER_FUCHSIA
83   _zx_vmar_unmap(_zx_vmar_root_self(), reinterpret_cast<uintptr_t>(B),
84                  RoundedSize);
85 #else
86   internal_munmap(B, RoundedSize);
87 #endif
88 }
89
90 template <class T = unsigned char>
91 T *allocateBuffer(size_t S) XRAY_NEVER_INSTRUMENT {
92   uptr RoundedSize = RoundUpTo(S * sizeof(T), GetPageSizeCached());
93 #if SANITIZER_FUCHSIA
94   zx_handle_t Vmo;
95   zx_status_t Status = _zx_vmo_create(RoundedSize, 0, &Vmo);
96   if (Status != ZX_OK) {
97     if (Verbosity())
98       Report("XRay Profiling: Failed to create VMO of size %zu: %s\n", S,
99              _zx_status_get_string(Status));
100     return nullptr;
101   }
102   uintptr_t B;
103   Status = _zx_vmar_map(_zx_vmar_root_self(),
104                         ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, Vmo, 0, S, &B);
105   _zx_handle_close(Vmo);
106   if (Status != ZX_OK) {
107     if (Verbosity())
108       Report("XRay Profiling: Failed to map VMAR of size %zu: %s\n", S,
109              _zx_status_get_string(Status));
110     return nullptr;
111   }
112 #else
113   uptr B = internal_mmap(NULL, RoundedSize, PROT_READ | PROT_WRITE,
114                          MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
115   int ErrNo = 0;
116   if (UNLIKELY(internal_iserror(B, &ErrNo))) {
117     if (Verbosity())
118       Report(
119           "XRay Profiling: Failed to allocate memory of size %d; Error = %d.\n",
120           RoundedSize, B);
121     return nullptr;
122   }
123 #endif
124   return reinterpret_cast<T *>(B);
125 }
126
127 template <class T> void deallocateBuffer(T *B, size_t S) XRAY_NEVER_INSTRUMENT {
128   if (B == nullptr)
129     return;
130   uptr RoundedSize = RoundUpTo(S * sizeof(T), GetPageSizeCached());
131 #if SANITIZER_FUCHSIA
132   _zx_vmar_unmap(_zx_vmar_root_self(), reinterpret_cast<uintptr_t>(B),
133                  RoundedSize);
134 #else
135   internal_munmap(B, RoundedSize);
136 #endif
137 }
138
139 template <class T, class... U>
140 T *initArray(size_t N, U &&... Us) XRAY_NEVER_INSTRUMENT {
141   auto A = allocateBuffer<T>(N);
142   if (A != nullptr)
143     while (N > 0)
144       new (A + (--N)) T(std::forward<U>(Us)...);
145   return A;
146 }
147
148 /// The Allocator type hands out fixed-sized chunks of memory that are
149 /// cache-line aligned and sized. This is useful for placement of
150 /// performance-sensitive data in memory that's frequently accessed. The
151 /// allocator also self-limits the peak memory usage to a dynamically defined
152 /// maximum.
153 ///
154 /// N is the lower-bound size of the block of memory to return from the
155 /// allocation function. N is used to compute the size of a block, which is
156 /// cache-line-size multiples worth of memory. We compute the size of a block by
157 /// determining how many cache lines worth of memory is required to subsume N.
158 ///
159 /// The Allocator instance will manage its own memory acquired through mmap.
160 /// This severely constrains the platforms on which this can be used to POSIX
161 /// systems where mmap semantics are well-defined.
162 ///
163 /// FIXME: Isolate the lower-level memory management to a different abstraction
164 /// that can be platform-specific.
165 template <size_t N> struct Allocator {
166   // The Allocator returns memory as Block instances.
167   struct Block {
168     /// Compute the minimum cache-line size multiple that is >= N.
169     static constexpr auto Size = nearest_boundary(N, kCacheLineSize);
170     void *Data;
171   };
172
173 private:
174   size_t MaxMemory{0};
175   unsigned char *BackingStore = nullptr;
176   unsigned char *AlignedNextBlock = nullptr;
177   size_t AllocatedBlocks = 0;
178   bool Owned;
179   SpinMutex Mutex{};
180
181   void *Alloc() XRAY_NEVER_INSTRUMENT {
182     SpinMutexLock Lock(&Mutex);
183     if (UNLIKELY(BackingStore == nullptr)) {
184       BackingStore = allocateBuffer(MaxMemory);
185       if (BackingStore == nullptr) {
186         if (Verbosity())
187           Report("XRay Profiling: Failed to allocate memory for allocator.\n");
188         return nullptr;
189       }
190
191       AlignedNextBlock = BackingStore;
192
193       // Ensure that NextBlock is aligned appropriately.
194       auto BackingStoreNum = reinterpret_cast<uintptr_t>(BackingStore);
195       auto AlignedNextBlockNum = nearest_boundary(
196           reinterpret_cast<uintptr_t>(AlignedNextBlock), kCacheLineSize);
197       if (diff(AlignedNextBlockNum, BackingStoreNum) > ptrdiff_t(MaxMemory)) {
198         deallocateBuffer(BackingStore, MaxMemory);
199         AlignedNextBlock = BackingStore = nullptr;
200         if (Verbosity())
201           Report("XRay Profiling: Cannot obtain enough memory from "
202                  "preallocated region.\n");
203         return nullptr;
204       }
205
206       AlignedNextBlock = reinterpret_cast<unsigned char *>(AlignedNextBlockNum);
207
208       // Assert that AlignedNextBlock is cache-line aligned.
209       DCHECK_EQ(reinterpret_cast<uintptr_t>(AlignedNextBlock) % kCacheLineSize,
210                 0);
211     }
212
213     if (((AllocatedBlocks + 1) * Block::Size) > MaxMemory)
214       return nullptr;
215
216     // Align the pointer we'd like to return to an appropriate alignment, then
217     // advance the pointer from where to start allocations.
218     void *Result = AlignedNextBlock;
219     AlignedNextBlock =
220         reinterpret_cast<unsigned char *>(AlignedNextBlock) + Block::Size;
221     ++AllocatedBlocks;
222     return Result;
223   }
224
225 public:
226   explicit Allocator(size_t M) XRAY_NEVER_INSTRUMENT
227       : MaxMemory(RoundUpTo(M, kCacheLineSize)),
228         BackingStore(nullptr),
229         AlignedNextBlock(nullptr),
230         AllocatedBlocks(0),
231         Owned(true),
232         Mutex() {}
233
234   explicit Allocator(void *P, size_t M) XRAY_NEVER_INSTRUMENT
235       : MaxMemory(M),
236         BackingStore(reinterpret_cast<unsigned char *>(P)),
237         AlignedNextBlock(reinterpret_cast<unsigned char *>(P)),
238         AllocatedBlocks(0),
239         Owned(false),
240         Mutex() {}
241
242   Allocator(const Allocator &) = delete;
243   Allocator &operator=(const Allocator &) = delete;
244
245   Allocator(Allocator &&O) XRAY_NEVER_INSTRUMENT {
246     SpinMutexLock L0(&Mutex);
247     SpinMutexLock L1(&O.Mutex);
248     MaxMemory = O.MaxMemory;
249     O.MaxMemory = 0;
250     BackingStore = O.BackingStore;
251     O.BackingStore = nullptr;
252     AlignedNextBlock = O.AlignedNextBlock;
253     O.AlignedNextBlock = nullptr;
254     AllocatedBlocks = O.AllocatedBlocks;
255     O.AllocatedBlocks = 0;
256     Owned = O.Owned;
257     O.Owned = false;
258   }
259
260   Allocator &operator=(Allocator &&O) XRAY_NEVER_INSTRUMENT {
261     SpinMutexLock L0(&Mutex);
262     SpinMutexLock L1(&O.Mutex);
263     MaxMemory = O.MaxMemory;
264     O.MaxMemory = 0;
265     if (BackingStore != nullptr)
266       deallocateBuffer(BackingStore, MaxMemory);
267     BackingStore = O.BackingStore;
268     O.BackingStore = nullptr;
269     AlignedNextBlock = O.AlignedNextBlock;
270     O.AlignedNextBlock = nullptr;
271     AllocatedBlocks = O.AllocatedBlocks;
272     O.AllocatedBlocks = 0;
273     Owned = O.Owned;
274     O.Owned = false;
275     return *this;
276   }
277
278   Block Allocate() XRAY_NEVER_INSTRUMENT { return {Alloc()}; }
279
280   ~Allocator() NOEXCEPT XRAY_NEVER_INSTRUMENT {
281     if (Owned && BackingStore != nullptr) {
282       deallocateBuffer(BackingStore, MaxMemory);
283     }
284   }
285 };
286
287 } // namespace __xray
288
289 #endif // XRAY_ALLOCATOR_H