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