xref: /llvm-project/compiler-rt/lib/xray/xray_buffer_queue.cpp (revision a1e7e401d2af002af26e1512a31e09eb2d0cf1dc)
1b3018603SNico Weber //===-- xray_buffer_queue.cpp ----------------------------------*- C++ -*-===//
2b3018603SNico Weber //
3b3018603SNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4b3018603SNico Weber // See https://llvm.org/LICENSE.txt for license information.
5b3018603SNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b3018603SNico Weber //
7b3018603SNico Weber //===----------------------------------------------------------------------===//
8b3018603SNico Weber //
9*a1e7e401SKazuaki Ishizaki // This file is a part of XRay, a dynamic runtime instrumentation system.
10b3018603SNico Weber //
11b3018603SNico Weber // Defines the interface for a buffer queue implementation.
12b3018603SNico Weber //
13b3018603SNico Weber //===----------------------------------------------------------------------===//
14b3018603SNico Weber #include "xray_buffer_queue.h"
15b3018603SNico Weber #include "sanitizer_common/sanitizer_atomic.h"
16b3018603SNico Weber #include "sanitizer_common/sanitizer_common.h"
17b3018603SNico Weber #include "sanitizer_common/sanitizer_libc.h"
18b3018603SNico Weber #if !SANITIZER_FUCHSIA
19b3018603SNico Weber #include "sanitizer_common/sanitizer_posix.h"
20b3018603SNico Weber #endif
21b3018603SNico Weber #include "xray_allocator.h"
22b3018603SNico Weber #include "xray_defs.h"
23b3018603SNico Weber #include <memory>
24b3018603SNico Weber #include <sys/mman.h>
25b3018603SNico Weber 
26b3018603SNico Weber using namespace __xray;
27b3018603SNico Weber 
28b3018603SNico Weber namespace {
29b3018603SNico Weber 
allocControlBlock(size_t Size,size_t Count)30b3018603SNico Weber BufferQueue::ControlBlock *allocControlBlock(size_t Size, size_t Count) {
31b3018603SNico Weber   auto B =
32b3018603SNico Weber       allocateBuffer((sizeof(BufferQueue::ControlBlock) - 1) + (Size * Count));
33b3018603SNico Weber   return B == nullptr ? nullptr
34b3018603SNico Weber                       : reinterpret_cast<BufferQueue::ControlBlock *>(B);
35b3018603SNico Weber }
36b3018603SNico Weber 
deallocControlBlock(BufferQueue::ControlBlock * C,size_t Size,size_t Count)37b3018603SNico Weber void deallocControlBlock(BufferQueue::ControlBlock *C, size_t Size,
38b3018603SNico Weber                          size_t Count) {
39b3018603SNico Weber   deallocateBuffer(reinterpret_cast<unsigned char *>(C),
40b3018603SNico Weber                    (sizeof(BufferQueue::ControlBlock) - 1) + (Size * Count));
41b3018603SNico Weber }
42b3018603SNico Weber 
decRefCount(BufferQueue::ControlBlock * C,size_t Size,size_t Count)43b3018603SNico Weber void decRefCount(BufferQueue::ControlBlock *C, size_t Size, size_t Count) {
44b3018603SNico Weber   if (C == nullptr)
45b3018603SNico Weber     return;
46b3018603SNico Weber   if (atomic_fetch_sub(&C->RefCount, 1, memory_order_acq_rel) == 1)
47b3018603SNico Weber     deallocControlBlock(C, Size, Count);
48b3018603SNico Weber }
49b3018603SNico Weber 
incRefCount(BufferQueue::ControlBlock * C)50b3018603SNico Weber void incRefCount(BufferQueue::ControlBlock *C) {
51b3018603SNico Weber   if (C == nullptr)
52b3018603SNico Weber     return;
53b3018603SNico Weber   atomic_fetch_add(&C->RefCount, 1, memory_order_acq_rel);
54b3018603SNico Weber }
55b3018603SNico Weber 
56b3018603SNico Weber // We use a struct to ensure that we are allocating one atomic_uint64_t per
57b3018603SNico Weber // cache line. This allows us to not worry about false-sharing among atomic
58b3018603SNico Weber // objects being updated (constantly) by different threads.
59b3018603SNico Weber struct ExtentsPadded {
60b3018603SNico Weber   union {
61b3018603SNico Weber     atomic_uint64_t Extents;
62b3018603SNico Weber     unsigned char Storage[kCacheLineSize];
63b3018603SNico Weber   };
64b3018603SNico Weber };
65b3018603SNico Weber 
66b3018603SNico Weber constexpr size_t kExtentsSize = sizeof(ExtentsPadded);
67b3018603SNico Weber 
68b3018603SNico Weber } // namespace
69b3018603SNico Weber 
init(size_t BS,size_t BC)70b3018603SNico Weber BufferQueue::ErrorCode BufferQueue::init(size_t BS, size_t BC) {
71b3018603SNico Weber   SpinMutexLock Guard(&Mutex);
72b3018603SNico Weber 
73b3018603SNico Weber   if (!finalizing())
74b3018603SNico Weber     return BufferQueue::ErrorCode::AlreadyInitialized;
75b3018603SNico Weber 
76b3018603SNico Weber   cleanupBuffers();
77b3018603SNico Weber 
78b3018603SNico Weber   bool Success = false;
79b3018603SNico Weber   BufferSize = BS;
80b3018603SNico Weber   BufferCount = BC;
81b3018603SNico Weber 
82b3018603SNico Weber   BackingStore = allocControlBlock(BufferSize, BufferCount);
83b3018603SNico Weber   if (BackingStore == nullptr)
84b3018603SNico Weber     return BufferQueue::ErrorCode::NotEnoughMemory;
85b3018603SNico Weber 
86b3018603SNico Weber   auto CleanupBackingStore = at_scope_exit([&, this] {
87b3018603SNico Weber     if (Success)
88b3018603SNico Weber       return;
89b3018603SNico Weber     deallocControlBlock(BackingStore, BufferSize, BufferCount);
90b3018603SNico Weber     BackingStore = nullptr;
91b3018603SNico Weber   });
92b3018603SNico Weber 
93b3018603SNico Weber   // Initialize enough atomic_uint64_t instances, each
94b3018603SNico Weber   ExtentsBackingStore = allocControlBlock(kExtentsSize, BufferCount);
95b3018603SNico Weber   if (ExtentsBackingStore == nullptr)
96b3018603SNico Weber     return BufferQueue::ErrorCode::NotEnoughMemory;
97b3018603SNico Weber 
98b3018603SNico Weber   auto CleanupExtentsBackingStore = at_scope_exit([&, this] {
99b3018603SNico Weber     if (Success)
100b3018603SNico Weber       return;
101b3018603SNico Weber     deallocControlBlock(ExtentsBackingStore, kExtentsSize, BufferCount);
102b3018603SNico Weber     ExtentsBackingStore = nullptr;
103b3018603SNico Weber   });
104b3018603SNico Weber 
105b3018603SNico Weber   Buffers = initArray<BufferRep>(BufferCount);
106b3018603SNico Weber   if (Buffers == nullptr)
107b3018603SNico Weber     return BufferQueue::ErrorCode::NotEnoughMemory;
108b3018603SNico Weber 
109b3018603SNico Weber   // At this point we increment the generation number to associate the buffers
110b3018603SNico Weber   // to the new generation.
111b3018603SNico Weber   atomic_fetch_add(&Generation, 1, memory_order_acq_rel);
112b3018603SNico Weber 
113b3018603SNico Weber   // First, we initialize the refcount in the ControlBlock, which we treat as
114b3018603SNico Weber   // being at the start of the BackingStore pointer.
115b3018603SNico Weber   atomic_store(&BackingStore->RefCount, 1, memory_order_release);
116b3018603SNico Weber   atomic_store(&ExtentsBackingStore->RefCount, 1, memory_order_release);
117b3018603SNico Weber 
118b3018603SNico Weber   // Then we initialise the individual buffers that sub-divide the whole backing
119b3018603SNico Weber   // store. Each buffer will start at the `Data` member of the ControlBlock, and
120b3018603SNico Weber   // will be offsets from these locations.
121b3018603SNico Weber   for (size_t i = 0; i < BufferCount; ++i) {
122b3018603SNico Weber     auto &T = Buffers[i];
123b3018603SNico Weber     auto &Buf = T.Buff;
124b3018603SNico Weber     auto *E = reinterpret_cast<ExtentsPadded *>(&ExtentsBackingStore->Data +
125b3018603SNico Weber                                                 (kExtentsSize * i));
126b3018603SNico Weber     Buf.Extents = &E->Extents;
127b3018603SNico Weber     atomic_store(Buf.Extents, 0, memory_order_release);
128b3018603SNico Weber     Buf.Generation = generation();
129b3018603SNico Weber     Buf.Data = &BackingStore->Data + (BufferSize * i);
130b3018603SNico Weber     Buf.Size = BufferSize;
131b3018603SNico Weber     Buf.BackingStore = BackingStore;
132b3018603SNico Weber     Buf.ExtentsBackingStore = ExtentsBackingStore;
133b3018603SNico Weber     Buf.Count = BufferCount;
134b3018603SNico Weber     T.Used = false;
135b3018603SNico Weber   }
136b3018603SNico Weber 
137b3018603SNico Weber   Next = Buffers;
138b3018603SNico Weber   First = Buffers;
139b3018603SNico Weber   LiveBuffers = 0;
140b3018603SNico Weber   atomic_store(&Finalizing, 0, memory_order_release);
141b3018603SNico Weber   Success = true;
142b3018603SNico Weber   return BufferQueue::ErrorCode::Ok;
143b3018603SNico Weber }
144b3018603SNico Weber 
BufferQueue(size_t B,size_t N,bool & Success)145b3018603SNico Weber BufferQueue::BufferQueue(size_t B, size_t N,
146b3018603SNico Weber                          bool &Success) XRAY_NEVER_INSTRUMENT
147b3018603SNico Weber     : BufferSize(B),
148b3018603SNico Weber       BufferCount(N),
149b3018603SNico Weber       Mutex(),
150b3018603SNico Weber       Finalizing{1},
151b3018603SNico Weber       BackingStore(nullptr),
152b3018603SNico Weber       ExtentsBackingStore(nullptr),
153b3018603SNico Weber       Buffers(nullptr),
154b3018603SNico Weber       Next(Buffers),
155b3018603SNico Weber       First(Buffers),
156b3018603SNico Weber       LiveBuffers(0),
157b3018603SNico Weber       Generation{0} {
158b3018603SNico Weber   Success = init(B, N) == BufferQueue::ErrorCode::Ok;
159b3018603SNico Weber }
160b3018603SNico Weber 
getBuffer(Buffer & Buf)161b3018603SNico Weber BufferQueue::ErrorCode BufferQueue::getBuffer(Buffer &Buf) {
162b3018603SNico Weber   if (atomic_load(&Finalizing, memory_order_acquire))
163b3018603SNico Weber     return ErrorCode::QueueFinalizing;
164b3018603SNico Weber 
165b3018603SNico Weber   BufferRep *B = nullptr;
166b3018603SNico Weber   {
167b3018603SNico Weber     SpinMutexLock Guard(&Mutex);
168b3018603SNico Weber     if (LiveBuffers == BufferCount)
169b3018603SNico Weber       return ErrorCode::NotEnoughMemory;
170b3018603SNico Weber     B = Next++;
171b3018603SNico Weber     if (Next == (Buffers + BufferCount))
172b3018603SNico Weber       Next = Buffers;
173b3018603SNico Weber     ++LiveBuffers;
174b3018603SNico Weber   }
175b3018603SNico Weber 
176b3018603SNico Weber   incRefCount(BackingStore);
177b3018603SNico Weber   incRefCount(ExtentsBackingStore);
178b3018603SNico Weber   Buf = B->Buff;
179b3018603SNico Weber   Buf.Generation = generation();
180b3018603SNico Weber   B->Used = true;
181b3018603SNico Weber   return ErrorCode::Ok;
182b3018603SNico Weber }
183b3018603SNico Weber 
releaseBuffer(Buffer & Buf)184b3018603SNico Weber BufferQueue::ErrorCode BufferQueue::releaseBuffer(Buffer &Buf) {
185b3018603SNico Weber   // Check whether the buffer being referred to is within the bounds of the
186b3018603SNico Weber   // backing store's range.
187b3018603SNico Weber   BufferRep *B = nullptr;
188b3018603SNico Weber   {
189b3018603SNico Weber     SpinMutexLock Guard(&Mutex);
190b3018603SNico Weber     if (Buf.Generation != generation() || LiveBuffers == 0) {
191b3018603SNico Weber       Buf = {};
192b3018603SNico Weber       decRefCount(Buf.BackingStore, Buf.Size, Buf.Count);
193b3018603SNico Weber       decRefCount(Buf.ExtentsBackingStore, kExtentsSize, Buf.Count);
194b3018603SNico Weber       return BufferQueue::ErrorCode::Ok;
195b3018603SNico Weber     }
196b3018603SNico Weber 
197b3018603SNico Weber     if (Buf.Data < &BackingStore->Data ||
198b3018603SNico Weber         Buf.Data > &BackingStore->Data + (BufferCount * BufferSize))
199b3018603SNico Weber       return BufferQueue::ErrorCode::UnrecognizedBuffer;
200b3018603SNico Weber 
201b3018603SNico Weber     --LiveBuffers;
202b3018603SNico Weber     B = First++;
203b3018603SNico Weber     if (First == (Buffers + BufferCount))
204b3018603SNico Weber       First = Buffers;
205b3018603SNico Weber   }
206b3018603SNico Weber 
207b3018603SNico Weber   // Now that the buffer has been released, we mark it as "used".
208b3018603SNico Weber   B->Buff = Buf;
209b3018603SNico Weber   B->Used = true;
210b3018603SNico Weber   decRefCount(Buf.BackingStore, Buf.Size, Buf.Count);
211b3018603SNico Weber   decRefCount(Buf.ExtentsBackingStore, kExtentsSize, Buf.Count);
212b3018603SNico Weber   atomic_store(B->Buff.Extents, atomic_load(Buf.Extents, memory_order_acquire),
213b3018603SNico Weber                memory_order_release);
214b3018603SNico Weber   Buf = {};
215b3018603SNico Weber   return ErrorCode::Ok;
216b3018603SNico Weber }
217b3018603SNico Weber 
finalize()218b3018603SNico Weber BufferQueue::ErrorCode BufferQueue::finalize() {
219b3018603SNico Weber   if (atomic_exchange(&Finalizing, 1, memory_order_acq_rel))
220b3018603SNico Weber     return ErrorCode::QueueFinalizing;
221b3018603SNico Weber   return ErrorCode::Ok;
222b3018603SNico Weber }
223b3018603SNico Weber 
cleanupBuffers()224b3018603SNico Weber void BufferQueue::cleanupBuffers() {
225b3018603SNico Weber   for (auto B = Buffers, E = Buffers + BufferCount; B != E; ++B)
226b3018603SNico Weber     B->~BufferRep();
227b3018603SNico Weber   deallocateBuffer(Buffers, BufferCount);
228b3018603SNico Weber   decRefCount(BackingStore, BufferSize, BufferCount);
229b3018603SNico Weber   decRefCount(ExtentsBackingStore, kExtentsSize, BufferCount);
230b3018603SNico Weber   BackingStore = nullptr;
231b3018603SNico Weber   ExtentsBackingStore = nullptr;
232b3018603SNico Weber   Buffers = nullptr;
233b3018603SNico Weber   BufferCount = 0;
234b3018603SNico Weber   BufferSize = 0;
235b3018603SNico Weber }
236b3018603SNico Weber 
~BufferQueue()237b3018603SNico Weber BufferQueue::~BufferQueue() { cleanupBuffers(); }
238