1 //===-- xray_buffer_queue.h ------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file is a part of XRay, a dynamic runtime instrumentation system.
11 //
12 // Defines the interface for a buffer queue implementation.
13 //
14 //===----------------------------------------------------------------------===//
15 #ifndef XRAY_BUFFER_QUEUE_H
16 #define XRAY_BUFFER_QUEUE_H
17 
18 #include <cstddef>
19 #include "sanitizer_common/sanitizer_atomic.h"
20 #include "sanitizer_common/sanitizer_mutex.h"
21 
22 namespace __xray {
23 
24 /// BufferQueue implements a circular queue of fixed sized buffers (much like a
25 /// freelist) but is concerned mostly with making it really quick to initialise,
26 /// finalise, and get/return buffers to the queue. This is one key component of
27 /// the "flight data recorder" (FDR) mode to support ongoing XRay function call
28 /// trace collection.
29 class BufferQueue {
30  public:
31   struct Buffer {
32     void *Buffer = nullptr;
33     size_t Size = 0;
34   };
35 
36  private:
37   struct BufferRep {
38     // The managed buffer.
39     Buffer Buff;
40 
41     // This is true if the buffer has been returned to the available queue, and
42     // is considered "used" by another thread.
43     bool Used = false;
44   };
45 
46   // Size of each individual Buffer.
47   size_t BufferSize;
48 
49   BufferRep *Buffers;
50   size_t BufferCount;
51 
52   __sanitizer::SpinMutex Mutex;
53   __sanitizer::atomic_uint8_t Finalizing;
54 
55   // Pointers to buffers managed/owned by the BufferQueue.
56   void **OwnedBuffers;
57 
58   // Pointer to the next buffer to be handed out.
59   BufferRep *Next;
60 
61   // Pointer to the entry in the array where the next released buffer will be
62   // placed.
63   BufferRep *First;
64 
65   // Count of buffers that have been handed out through 'getBuffer'.
66   size_t LiveBuffers;
67 
68  public:
69   enum class ErrorCode : unsigned {
70     Ok,
71     NotEnoughMemory,
72     QueueFinalizing,
73     UnrecognizedBuffer,
74     AlreadyFinalized,
75   };
76 
77   static const char *getErrorString(ErrorCode E) {
78     switch (E) {
79       case ErrorCode::Ok:
80         return "(none)";
81       case ErrorCode::NotEnoughMemory:
82         return "no available buffers in the queue";
83       case ErrorCode::QueueFinalizing:
84         return "queue already finalizing";
85       case ErrorCode::UnrecognizedBuffer:
86         return "buffer being returned not owned by buffer queue";
87       case ErrorCode::AlreadyFinalized:
88         return "queue already finalized";
89     }
90     return "unknown error";
91   }
92 
93   /// Initialise a queue of size |N| with buffers of size |B|. We report success
94   /// through |Success|.
95   BufferQueue(size_t B, size_t N, bool &Success);
96 
97   /// Updates |Buf| to contain the pointer to an appropriate buffer. Returns an
98   /// error in case there are no available buffers to return when we will run
99   /// over the upper bound for the total buffers.
100   ///
101   /// Requirements:
102   ///   - BufferQueue is not finalising.
103   ///
104   /// Returns:
105   ///   - ErrorCode::NotEnoughMemory on exceeding MaxSize.
106   ///   - ErrorCode::Ok when we find a Buffer.
107   ///   - ErrorCode::QueueFinalizing or ErrorCode::AlreadyFinalized on
108   ///     a finalizing/finalized BufferQueue.
109   ErrorCode getBuffer(Buffer &Buf);
110 
111   /// Updates |Buf| to point to nullptr, with size 0.
112   ///
113   /// Returns:
114   ///   - ErrorCode::Ok when we successfully release the buffer.
115   ///   - ErrorCode::UnrecognizedBuffer for when this BufferQueue does not own
116   ///     the buffer being released.
117   ErrorCode releaseBuffer(Buffer &Buf);
118 
119   bool finalizing() const {
120     return __sanitizer::atomic_load(&Finalizing,
121                                     __sanitizer::memory_order_acquire);
122   }
123 
124   /// Returns the configured size of the buffers in the buffer queue.
125   size_t ConfiguredBufferSize() const { return BufferSize; }
126 
127   /// Sets the state of the BufferQueue to finalizing, which ensures that:
128   ///
129   ///   - All subsequent attempts to retrieve a Buffer will fail.
130   ///   - All releaseBuffer operations will not fail.
131   ///
132   /// After a call to finalize succeeds, all subsequent calls to finalize will
133   /// fail with ErrorCode::QueueFinalizing.
134   ErrorCode finalize();
135 
136   /// Applies the provided function F to each Buffer in the queue, only if the
137   /// Buffer is marked 'used' (i.e. has been the result of getBuffer(...) and a
138   /// releaseBuffer(...) operation).
139   template <class F>
140   void apply(F Fn) {
141     __sanitizer::SpinMutexLock G(&Mutex);
142     for (auto I = Buffers, E = Buffers + BufferCount; I != E; ++I) {
143       const auto &T = *I;
144       if (T.Used) Fn(T.Buff);
145     }
146   }
147 
148   // Cleans up allocated buffers.
149   ~BufferQueue();
150 };
151 
152 }  // namespace __xray
153 
154 #endif  // XRAY_BUFFER_QUEUE_H
155