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