Path: blob/main/contrib/llvm-project/compiler-rt/lib/xray/xray_buffer_queue.h
35265 views
//===-- xray_buffer_queue.h ------------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file is a part of XRay, a dynamic runtime instrumentation system.9//10// Defines the interface for a buffer queue implementation.11//12//===----------------------------------------------------------------------===//13#ifndef XRAY_BUFFER_QUEUE_H14#define XRAY_BUFFER_QUEUE_H1516#include "sanitizer_common/sanitizer_atomic.h"17#include "sanitizer_common/sanitizer_common.h"18#include "sanitizer_common/sanitizer_mutex.h"19#include "xray_defs.h"20#include <cstddef>21#include <cstdint>2223namespace __xray {2425/// BufferQueue implements a circular queue of fixed sized buffers (much like a26/// freelist) but is concerned with making it quick to initialise, finalise, and27/// get from or return buffers to the queue. This is one key component of the28/// "flight data recorder" (FDR) mode to support ongoing XRay function call29/// trace collection.30class BufferQueue {31public:32/// ControlBlock represents the memory layout of how we interpret the backing33/// store for all buffers and extents managed by a BufferQueue instance. The34/// ControlBlock has the reference count as the first member, sized according35/// to platform-specific cache-line size. We never use the Buffer member of36/// the union, which is only there for compiler-supported alignment and37/// sizing.38///39/// This ensures that the `Data` member will be placed at least kCacheLineSize40/// bytes from the beginning of the structure.41struct ControlBlock {42union {43atomic_uint64_t RefCount;44char Buffer[kCacheLineSize];45};4647/// We need to make this size 1, to conform to the C++ rules for array data48/// members. Typically, we want to subtract this 1 byte for sizing49/// information.50char Data[1];51};5253struct Buffer {54atomic_uint64_t *Extents = nullptr;55uint64_t Generation{0};56void *Data = nullptr;57size_t Size = 0;5859private:60friend class BufferQueue;61ControlBlock *BackingStore = nullptr;62ControlBlock *ExtentsBackingStore = nullptr;63size_t Count = 0;64};6566struct BufferRep {67// The managed buffer.68Buffer Buff;6970// This is true if the buffer has been returned to the available queue, and71// is considered "used" by another thread.72bool Used = false;73};7475private:76// This models a ForwardIterator. |T| Must be either a `Buffer` or `const77// Buffer`. Note that we only advance to the "used" buffers, when78// incrementing, so that at dereference we're always at a valid point.79template <class T> class Iterator {80public:81BufferRep *Buffers = nullptr;82size_t Offset = 0;83size_t Max = 0;8485Iterator &operator++() {86DCHECK_NE(Offset, Max);87do {88++Offset;89} while (Offset != Max && !Buffers[Offset].Used);90return *this;91}9293Iterator operator++(int) {94Iterator C = *this;95++(*this);96return C;97}9899T &operator*() const { return Buffers[Offset].Buff; }100101T *operator->() const { return &(Buffers[Offset].Buff); }102103Iterator(BufferRep *Root, size_t O, size_t M) XRAY_NEVER_INSTRUMENT104: Buffers(Root),105Offset(O),106Max(M) {107// We want to advance to the first Offset where the 'Used' property is108// true, or to the end of the list/queue.109while (Offset != Max && !Buffers[Offset].Used) {110++Offset;111}112}113114Iterator() = default;115Iterator(const Iterator &) = default;116Iterator(Iterator &&) = default;117Iterator &operator=(const Iterator &) = default;118Iterator &operator=(Iterator &&) = default;119~Iterator() = default;120121template <class V>122friend bool operator==(const Iterator &L, const Iterator<V> &R) {123DCHECK_EQ(L.Max, R.Max);124return L.Buffers == R.Buffers && L.Offset == R.Offset;125}126127template <class V>128friend bool operator!=(const Iterator &L, const Iterator<V> &R) {129return !(L == R);130}131};132133// Size of each individual Buffer.134size_t BufferSize;135136// Amount of pre-allocated buffers.137size_t BufferCount;138139SpinMutex Mutex;140atomic_uint8_t Finalizing;141142// The collocated ControlBlock and buffer storage.143ControlBlock *BackingStore;144145// The collocated ControlBlock and extents storage.146ControlBlock *ExtentsBackingStore;147148// A dynamically allocated array of BufferRep instances.149BufferRep *Buffers;150151// Pointer to the next buffer to be handed out.152BufferRep *Next;153154// Pointer to the entry in the array where the next released buffer will be155// placed.156BufferRep *First;157158// Count of buffers that have been handed out through 'getBuffer'.159size_t LiveBuffers;160161// We use a generation number to identify buffers and which generation they're162// associated with.163atomic_uint64_t Generation;164165/// Releases references to the buffers backed by the current buffer queue.166void cleanupBuffers();167168public:169enum class ErrorCode : unsigned {170Ok,171NotEnoughMemory,172QueueFinalizing,173UnrecognizedBuffer,174AlreadyFinalized,175AlreadyInitialized,176};177178static const char *getErrorString(ErrorCode E) {179switch (E) {180case ErrorCode::Ok:181return "(none)";182case ErrorCode::NotEnoughMemory:183return "no available buffers in the queue";184case ErrorCode::QueueFinalizing:185return "queue already finalizing";186case ErrorCode::UnrecognizedBuffer:187return "buffer being returned not owned by buffer queue";188case ErrorCode::AlreadyFinalized:189return "queue already finalized";190case ErrorCode::AlreadyInitialized:191return "queue already initialized";192}193return "unknown error";194}195196/// Initialise a queue of size |N| with buffers of size |B|. We report success197/// through |Success|.198BufferQueue(size_t B, size_t N, bool &Success);199200/// Updates |Buf| to contain the pointer to an appropriate buffer. Returns an201/// error in case there are no available buffers to return when we will run202/// over the upper bound for the total buffers.203///204/// Requirements:205/// - BufferQueue is not finalising.206///207/// Returns:208/// - ErrorCode::NotEnoughMemory on exceeding MaxSize.209/// - ErrorCode::Ok when we find a Buffer.210/// - ErrorCode::QueueFinalizing or ErrorCode::AlreadyFinalized on211/// a finalizing/finalized BufferQueue.212ErrorCode getBuffer(Buffer &Buf);213214/// Updates |Buf| to point to nullptr, with size 0.215///216/// Returns:217/// - ErrorCode::Ok when we successfully release the buffer.218/// - ErrorCode::UnrecognizedBuffer for when this BufferQueue does not own219/// the buffer being released.220ErrorCode releaseBuffer(Buffer &Buf);221222/// Initializes the buffer queue, starting a new generation. We can re-set the223/// size of buffers with |BS| along with the buffer count with |BC|.224///225/// Returns:226/// - ErrorCode::Ok when we successfully initialize the buffer. This227/// requires that the buffer queue is previously finalized.228/// - ErrorCode::AlreadyInitialized when the buffer queue is not finalized.229ErrorCode init(size_t BS, size_t BC);230231bool finalizing() const {232return atomic_load(&Finalizing, memory_order_acquire);233}234235uint64_t generation() const {236return atomic_load(&Generation, memory_order_acquire);237}238239/// Returns the configured size of the buffers in the buffer queue.240size_t ConfiguredBufferSize() const { return BufferSize; }241242/// Sets the state of the BufferQueue to finalizing, which ensures that:243///244/// - All subsequent attempts to retrieve a Buffer will fail.245/// - All releaseBuffer operations will not fail.246///247/// After a call to finalize succeeds, all subsequent calls to finalize will248/// fail with ErrorCode::QueueFinalizing.249ErrorCode finalize();250251/// Applies the provided function F to each Buffer in the queue, only if the252/// Buffer is marked 'used' (i.e. has been the result of getBuffer(...) and a253/// releaseBuffer(...) operation).254template <class F> void apply(F Fn) XRAY_NEVER_INSTRUMENT {255SpinMutexLock G(&Mutex);256for (auto I = begin(), E = end(); I != E; ++I)257Fn(*I);258}259260using const_iterator = Iterator<const Buffer>;261using iterator = Iterator<Buffer>;262263/// Provides iterator access to the raw Buffer instances.264iterator begin() const { return iterator(Buffers, 0, BufferCount); }265const_iterator cbegin() const {266return const_iterator(Buffers, 0, BufferCount);267}268iterator end() const { return iterator(Buffers, BufferCount, BufferCount); }269const_iterator cend() const {270return const_iterator(Buffers, BufferCount, BufferCount);271}272273// Cleans up allocated buffers.274~BufferQueue();275};276277} // namespace __xray278279#endif // XRAY_BUFFER_QUEUE_H280281282