xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/xray/xray_buffer_queue.cpp (revision 349cc55c9796c4596a5b9904cd3281af295f878f)
168d75effSDimitry Andric //===-- xray_buffer_queue.cpp ----------------------------------*- C++ -*-===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
9*349cc55cSDimitry Andric // This file is a part of XRay, a dynamic runtime instrumentation system.
1068d75effSDimitry Andric //
1168d75effSDimitry Andric // Defines the interface for a buffer queue implementation.
1268d75effSDimitry Andric //
1368d75effSDimitry Andric //===----------------------------------------------------------------------===//
1468d75effSDimitry Andric #include "xray_buffer_queue.h"
1568d75effSDimitry Andric #include "sanitizer_common/sanitizer_atomic.h"
1668d75effSDimitry Andric #include "sanitizer_common/sanitizer_common.h"
1768d75effSDimitry Andric #include "sanitizer_common/sanitizer_libc.h"
1868d75effSDimitry Andric #if !SANITIZER_FUCHSIA
1968d75effSDimitry Andric #include "sanitizer_common/sanitizer_posix.h"
2068d75effSDimitry Andric #endif
2168d75effSDimitry Andric #include "xray_allocator.h"
2268d75effSDimitry Andric #include "xray_defs.h"
2368d75effSDimitry Andric #include <memory>
2468d75effSDimitry Andric #include <sys/mman.h>
2568d75effSDimitry Andric 
2668d75effSDimitry Andric using namespace __xray;
2768d75effSDimitry Andric 
2868d75effSDimitry Andric namespace {
2968d75effSDimitry Andric 
allocControlBlock(size_t Size,size_t Count)3068d75effSDimitry Andric BufferQueue::ControlBlock *allocControlBlock(size_t Size, size_t Count) {
3168d75effSDimitry Andric   auto B =
3268d75effSDimitry Andric       allocateBuffer((sizeof(BufferQueue::ControlBlock) - 1) + (Size * Count));
3368d75effSDimitry Andric   return B == nullptr ? nullptr
3468d75effSDimitry Andric                       : reinterpret_cast<BufferQueue::ControlBlock *>(B);
3568d75effSDimitry Andric }
3668d75effSDimitry Andric 
deallocControlBlock(BufferQueue::ControlBlock * C,size_t Size,size_t Count)3768d75effSDimitry Andric void deallocControlBlock(BufferQueue::ControlBlock *C, size_t Size,
3868d75effSDimitry Andric                          size_t Count) {
3968d75effSDimitry Andric   deallocateBuffer(reinterpret_cast<unsigned char *>(C),
4068d75effSDimitry Andric                    (sizeof(BufferQueue::ControlBlock) - 1) + (Size * Count));
4168d75effSDimitry Andric }
4268d75effSDimitry Andric 
decRefCount(BufferQueue::ControlBlock * C,size_t Size,size_t Count)4368d75effSDimitry Andric void decRefCount(BufferQueue::ControlBlock *C, size_t Size, size_t Count) {
4468d75effSDimitry Andric   if (C == nullptr)
4568d75effSDimitry Andric     return;
4668d75effSDimitry Andric   if (atomic_fetch_sub(&C->RefCount, 1, memory_order_acq_rel) == 1)
4768d75effSDimitry Andric     deallocControlBlock(C, Size, Count);
4868d75effSDimitry Andric }
4968d75effSDimitry Andric 
incRefCount(BufferQueue::ControlBlock * C)5068d75effSDimitry Andric void incRefCount(BufferQueue::ControlBlock *C) {
5168d75effSDimitry Andric   if (C == nullptr)
5268d75effSDimitry Andric     return;
5368d75effSDimitry Andric   atomic_fetch_add(&C->RefCount, 1, memory_order_acq_rel);
5468d75effSDimitry Andric }
5568d75effSDimitry Andric 
5668d75effSDimitry Andric // We use a struct to ensure that we are allocating one atomic_uint64_t per
5768d75effSDimitry Andric // cache line. This allows us to not worry about false-sharing among atomic
5868d75effSDimitry Andric // objects being updated (constantly) by different threads.
5968d75effSDimitry Andric struct ExtentsPadded {
6068d75effSDimitry Andric   union {
6168d75effSDimitry Andric     atomic_uint64_t Extents;
6268d75effSDimitry Andric     unsigned char Storage[kCacheLineSize];
6368d75effSDimitry Andric   };
6468d75effSDimitry Andric };
6568d75effSDimitry Andric 
6668d75effSDimitry Andric constexpr size_t kExtentsSize = sizeof(ExtentsPadded);
6768d75effSDimitry Andric 
6868d75effSDimitry Andric } // namespace
6968d75effSDimitry Andric 
init(size_t BS,size_t BC)7068d75effSDimitry Andric BufferQueue::ErrorCode BufferQueue::init(size_t BS, size_t BC) {
7168d75effSDimitry Andric   SpinMutexLock Guard(&Mutex);
7268d75effSDimitry Andric 
7368d75effSDimitry Andric   if (!finalizing())
7468d75effSDimitry Andric     return BufferQueue::ErrorCode::AlreadyInitialized;
7568d75effSDimitry Andric 
7668d75effSDimitry Andric   cleanupBuffers();
7768d75effSDimitry Andric 
7868d75effSDimitry Andric   bool Success = false;
7968d75effSDimitry Andric   BufferSize = BS;
8068d75effSDimitry Andric   BufferCount = BC;
8168d75effSDimitry Andric 
8268d75effSDimitry Andric   BackingStore = allocControlBlock(BufferSize, BufferCount);
8368d75effSDimitry Andric   if (BackingStore == nullptr)
8468d75effSDimitry Andric     return BufferQueue::ErrorCode::NotEnoughMemory;
8568d75effSDimitry Andric 
8668d75effSDimitry Andric   auto CleanupBackingStore = at_scope_exit([&, this] {
8768d75effSDimitry Andric     if (Success)
8868d75effSDimitry Andric       return;
8968d75effSDimitry Andric     deallocControlBlock(BackingStore, BufferSize, BufferCount);
9068d75effSDimitry Andric     BackingStore = nullptr;
9168d75effSDimitry Andric   });
9268d75effSDimitry Andric 
9368d75effSDimitry Andric   // Initialize enough atomic_uint64_t instances, each
9468d75effSDimitry Andric   ExtentsBackingStore = allocControlBlock(kExtentsSize, BufferCount);
9568d75effSDimitry Andric   if (ExtentsBackingStore == nullptr)
9668d75effSDimitry Andric     return BufferQueue::ErrorCode::NotEnoughMemory;
9768d75effSDimitry Andric 
9868d75effSDimitry Andric   auto CleanupExtentsBackingStore = at_scope_exit([&, this] {
9968d75effSDimitry Andric     if (Success)
10068d75effSDimitry Andric       return;
10168d75effSDimitry Andric     deallocControlBlock(ExtentsBackingStore, kExtentsSize, BufferCount);
10268d75effSDimitry Andric     ExtentsBackingStore = nullptr;
10368d75effSDimitry Andric   });
10468d75effSDimitry Andric 
10568d75effSDimitry Andric   Buffers = initArray<BufferRep>(BufferCount);
10668d75effSDimitry Andric   if (Buffers == nullptr)
10768d75effSDimitry Andric     return BufferQueue::ErrorCode::NotEnoughMemory;
10868d75effSDimitry Andric 
10968d75effSDimitry Andric   // At this point we increment the generation number to associate the buffers
11068d75effSDimitry Andric   // to the new generation.
11168d75effSDimitry Andric   atomic_fetch_add(&Generation, 1, memory_order_acq_rel);
11268d75effSDimitry Andric 
11368d75effSDimitry Andric   // First, we initialize the refcount in the ControlBlock, which we treat as
11468d75effSDimitry Andric   // being at the start of the BackingStore pointer.
11568d75effSDimitry Andric   atomic_store(&BackingStore->RefCount, 1, memory_order_release);
11668d75effSDimitry Andric   atomic_store(&ExtentsBackingStore->RefCount, 1, memory_order_release);
11768d75effSDimitry Andric 
11868d75effSDimitry Andric   // Then we initialise the individual buffers that sub-divide the whole backing
11968d75effSDimitry Andric   // store. Each buffer will start at the `Data` member of the ControlBlock, and
12068d75effSDimitry Andric   // will be offsets from these locations.
12168d75effSDimitry Andric   for (size_t i = 0; i < BufferCount; ++i) {
12268d75effSDimitry Andric     auto &T = Buffers[i];
12368d75effSDimitry Andric     auto &Buf = T.Buff;
12468d75effSDimitry Andric     auto *E = reinterpret_cast<ExtentsPadded *>(&ExtentsBackingStore->Data +
12568d75effSDimitry Andric                                                 (kExtentsSize * i));
12668d75effSDimitry Andric     Buf.Extents = &E->Extents;
12768d75effSDimitry Andric     atomic_store(Buf.Extents, 0, memory_order_release);
12868d75effSDimitry Andric     Buf.Generation = generation();
12968d75effSDimitry Andric     Buf.Data = &BackingStore->Data + (BufferSize * i);
13068d75effSDimitry Andric     Buf.Size = BufferSize;
13168d75effSDimitry Andric     Buf.BackingStore = BackingStore;
13268d75effSDimitry Andric     Buf.ExtentsBackingStore = ExtentsBackingStore;
13368d75effSDimitry Andric     Buf.Count = BufferCount;
13468d75effSDimitry Andric     T.Used = false;
13568d75effSDimitry Andric   }
13668d75effSDimitry Andric 
13768d75effSDimitry Andric   Next = Buffers;
13868d75effSDimitry Andric   First = Buffers;
13968d75effSDimitry Andric   LiveBuffers = 0;
14068d75effSDimitry Andric   atomic_store(&Finalizing, 0, memory_order_release);
14168d75effSDimitry Andric   Success = true;
14268d75effSDimitry Andric   return BufferQueue::ErrorCode::Ok;
14368d75effSDimitry Andric }
14468d75effSDimitry Andric 
BufferQueue(size_t B,size_t N,bool & Success)14568d75effSDimitry Andric BufferQueue::BufferQueue(size_t B, size_t N,
14668d75effSDimitry Andric                          bool &Success) XRAY_NEVER_INSTRUMENT
14768d75effSDimitry Andric     : BufferSize(B),
14868d75effSDimitry Andric       BufferCount(N),
14968d75effSDimitry Andric       Mutex(),
15068d75effSDimitry Andric       Finalizing{1},
15168d75effSDimitry Andric       BackingStore(nullptr),
15268d75effSDimitry Andric       ExtentsBackingStore(nullptr),
15368d75effSDimitry Andric       Buffers(nullptr),
15468d75effSDimitry Andric       Next(Buffers),
15568d75effSDimitry Andric       First(Buffers),
15668d75effSDimitry Andric       LiveBuffers(0),
15768d75effSDimitry Andric       Generation{0} {
15868d75effSDimitry Andric   Success = init(B, N) == BufferQueue::ErrorCode::Ok;
15968d75effSDimitry Andric }
16068d75effSDimitry Andric 
getBuffer(Buffer & Buf)16168d75effSDimitry Andric BufferQueue::ErrorCode BufferQueue::getBuffer(Buffer &Buf) {
16268d75effSDimitry Andric   if (atomic_load(&Finalizing, memory_order_acquire))
16368d75effSDimitry Andric     return ErrorCode::QueueFinalizing;
16468d75effSDimitry Andric 
16568d75effSDimitry Andric   BufferRep *B = nullptr;
16668d75effSDimitry Andric   {
16768d75effSDimitry Andric     SpinMutexLock Guard(&Mutex);
16868d75effSDimitry Andric     if (LiveBuffers == BufferCount)
16968d75effSDimitry Andric       return ErrorCode::NotEnoughMemory;
17068d75effSDimitry Andric     B = Next++;
17168d75effSDimitry Andric     if (Next == (Buffers + BufferCount))
17268d75effSDimitry Andric       Next = Buffers;
17368d75effSDimitry Andric     ++LiveBuffers;
17468d75effSDimitry Andric   }
17568d75effSDimitry Andric 
17668d75effSDimitry Andric   incRefCount(BackingStore);
17768d75effSDimitry Andric   incRefCount(ExtentsBackingStore);
17868d75effSDimitry Andric   Buf = B->Buff;
17968d75effSDimitry Andric   Buf.Generation = generation();
18068d75effSDimitry Andric   B->Used = true;
18168d75effSDimitry Andric   return ErrorCode::Ok;
18268d75effSDimitry Andric }
18368d75effSDimitry Andric 
releaseBuffer(Buffer & Buf)18468d75effSDimitry Andric BufferQueue::ErrorCode BufferQueue::releaseBuffer(Buffer &Buf) {
18568d75effSDimitry Andric   // Check whether the buffer being referred to is within the bounds of the
18668d75effSDimitry Andric   // backing store's range.
18768d75effSDimitry Andric   BufferRep *B = nullptr;
18868d75effSDimitry Andric   {
18968d75effSDimitry Andric     SpinMutexLock Guard(&Mutex);
19068d75effSDimitry Andric     if (Buf.Generation != generation() || LiveBuffers == 0) {
19168d75effSDimitry Andric       Buf = {};
19268d75effSDimitry Andric       decRefCount(Buf.BackingStore, Buf.Size, Buf.Count);
19368d75effSDimitry Andric       decRefCount(Buf.ExtentsBackingStore, kExtentsSize, Buf.Count);
19468d75effSDimitry Andric       return BufferQueue::ErrorCode::Ok;
19568d75effSDimitry Andric     }
19668d75effSDimitry Andric 
19768d75effSDimitry Andric     if (Buf.Data < &BackingStore->Data ||
19868d75effSDimitry Andric         Buf.Data > &BackingStore->Data + (BufferCount * BufferSize))
19968d75effSDimitry Andric       return BufferQueue::ErrorCode::UnrecognizedBuffer;
20068d75effSDimitry Andric 
20168d75effSDimitry Andric     --LiveBuffers;
20268d75effSDimitry Andric     B = First++;
20368d75effSDimitry Andric     if (First == (Buffers + BufferCount))
20468d75effSDimitry Andric       First = Buffers;
20568d75effSDimitry Andric   }
20668d75effSDimitry Andric 
20768d75effSDimitry Andric   // Now that the buffer has been released, we mark it as "used".
20868d75effSDimitry Andric   B->Buff = Buf;
20968d75effSDimitry Andric   B->Used = true;
21068d75effSDimitry Andric   decRefCount(Buf.BackingStore, Buf.Size, Buf.Count);
21168d75effSDimitry Andric   decRefCount(Buf.ExtentsBackingStore, kExtentsSize, Buf.Count);
21268d75effSDimitry Andric   atomic_store(B->Buff.Extents, atomic_load(Buf.Extents, memory_order_acquire),
21368d75effSDimitry Andric                memory_order_release);
21468d75effSDimitry Andric   Buf = {};
21568d75effSDimitry Andric   return ErrorCode::Ok;
21668d75effSDimitry Andric }
21768d75effSDimitry Andric 
finalize()21868d75effSDimitry Andric BufferQueue::ErrorCode BufferQueue::finalize() {
21968d75effSDimitry Andric   if (atomic_exchange(&Finalizing, 1, memory_order_acq_rel))
22068d75effSDimitry Andric     return ErrorCode::QueueFinalizing;
22168d75effSDimitry Andric   return ErrorCode::Ok;
22268d75effSDimitry Andric }
22368d75effSDimitry Andric 
cleanupBuffers()22468d75effSDimitry Andric void BufferQueue::cleanupBuffers() {
22568d75effSDimitry Andric   for (auto B = Buffers, E = Buffers + BufferCount; B != E; ++B)
22668d75effSDimitry Andric     B->~BufferRep();
22768d75effSDimitry Andric   deallocateBuffer(Buffers, BufferCount);
22868d75effSDimitry Andric   decRefCount(BackingStore, BufferSize, BufferCount);
22968d75effSDimitry Andric   decRefCount(ExtentsBackingStore, kExtentsSize, BufferCount);
23068d75effSDimitry Andric   BackingStore = nullptr;
23168d75effSDimitry Andric   ExtentsBackingStore = nullptr;
23268d75effSDimitry Andric   Buffers = nullptr;
23368d75effSDimitry Andric   BufferCount = 0;
23468d75effSDimitry Andric   BufferSize = 0;
23568d75effSDimitry Andric }
23668d75effSDimitry Andric 
~BufferQueue()23768d75effSDimitry Andric BufferQueue::~BufferQueue() { cleanupBuffers(); }
238