xref: /llvm-project/compiler-rt/lib/xray/xray_profile_collector.cpp (revision cac7821438f625d6c8a36dd9363f9acd3a3d93de)
1b3018603SNico Weber //===-- xray_profile_collector.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 //
9b3018603SNico Weber // This file is a part of XRay, a dynamic runtime instrumentation system.
10b3018603SNico Weber //
11b3018603SNico Weber // This implements the interface for the profileCollectorService.
12b3018603SNico Weber //
13b3018603SNico Weber //===----------------------------------------------------------------------===//
14b3018603SNico Weber #include "xray_profile_collector.h"
15b3018603SNico Weber #include "sanitizer_common/sanitizer_common.h"
16b3018603SNico Weber #include "xray_allocator.h"
17b3018603SNico Weber #include "xray_defs.h"
18b3018603SNico Weber #include "xray_profiling_flags.h"
19b3018603SNico Weber #include "xray_segmented_array.h"
20b3018603SNico Weber #include <memory>
21b3018603SNico Weber #include <pthread.h>
22b3018603SNico Weber #include <utility>
23b3018603SNico Weber 
24b3018603SNico Weber namespace __xray {
25b3018603SNico Weber namespace profileCollectorService {
26b3018603SNico Weber 
27b3018603SNico Weber namespace {
28b3018603SNico Weber 
29b3018603SNico Weber SpinMutex GlobalMutex;
30b3018603SNico Weber struct ThreadTrie {
31b3018603SNico Weber   tid_t TId;
32*cac78214SMarc Auberer   alignas(FunctionCallTrie) std::byte TrieStorage[sizeof(FunctionCallTrie)];
33b3018603SNico Weber };
34b3018603SNico Weber 
35b3018603SNico Weber struct ProfileBuffer {
36b3018603SNico Weber   void *Data;
37b3018603SNico Weber   size_t Size;
38b3018603SNico Weber };
39b3018603SNico Weber 
40b3018603SNico Weber // Current version of the profile format.
41b3018603SNico Weber constexpr u64 XRayProfilingVersion = 0x20180424;
42b3018603SNico Weber 
43b3018603SNico Weber // Identifier for XRay profiling files 'xrayprof' in hex.
44b3018603SNico Weber constexpr u64 XRayMagicBytes = 0x7872617970726f66;
45b3018603SNico Weber 
46b3018603SNico Weber struct XRayProfilingFileHeader {
47b3018603SNico Weber   const u64 MagicBytes = XRayMagicBytes;
48b3018603SNico Weber   const u64 Version = XRayProfilingVersion;
49b3018603SNico Weber   u64 Timestamp = 0; // System time in nanoseconds.
50b3018603SNico Weber   u64 PID = 0;       // Process ID.
51b3018603SNico Weber };
52b3018603SNico Weber 
53b3018603SNico Weber struct BlockHeader {
54b3018603SNico Weber   u32 BlockSize;
55b3018603SNico Weber   u32 BlockNum;
56b3018603SNico Weber   u64 ThreadId;
57b3018603SNico Weber };
58b3018603SNico Weber 
59b3018603SNico Weber struct ThreadData {
60b3018603SNico Weber   BufferQueue *BQ;
61b3018603SNico Weber   FunctionCallTrie::Allocators::Buffers Buffers;
62b3018603SNico Weber   FunctionCallTrie::Allocators Allocators;
63b3018603SNico Weber   FunctionCallTrie FCT;
64b3018603SNico Weber   tid_t TId;
65b3018603SNico Weber };
66b3018603SNico Weber 
67b3018603SNico Weber using ThreadDataArray = Array<ThreadData>;
68b3018603SNico Weber using ThreadDataAllocator = ThreadDataArray::AllocatorType;
69b3018603SNico Weber 
70b3018603SNico Weber // We use a separate buffer queue for the backing store for the allocator used
71b3018603SNico Weber // by the ThreadData array. This lets us host the buffers, allocators, and tries
72b3018603SNico Weber // associated with a thread by moving the data into the array instead of
73b3018603SNico Weber // attempting to copy the data to a separately backed set of tries.
74*cac78214SMarc Auberer alignas(BufferQueue) static std::byte BufferQueueStorage[sizeof(BufferQueue)];
75b3018603SNico Weber static BufferQueue *BQ = nullptr;
76b3018603SNico Weber static BufferQueue::Buffer Buffer;
77*cac78214SMarc Auberer alignas(ThreadDataAllocator) static std::byte
78*cac78214SMarc Auberer     ThreadDataAllocatorStorage[sizeof(ThreadDataAllocator)];
79*cac78214SMarc Auberer alignas(ThreadDataArray) static std::byte
80*cac78214SMarc Auberer     ThreadDataArrayStorage[sizeof(ThreadDataArray)];
81b3018603SNico Weber 
82b3018603SNico Weber static ThreadDataAllocator *TDAllocator = nullptr;
83b3018603SNico Weber static ThreadDataArray *TDArray = nullptr;
84b3018603SNico Weber 
85b3018603SNico Weber using ProfileBufferArray = Array<ProfileBuffer>;
86b3018603SNico Weber using ProfileBufferArrayAllocator = typename ProfileBufferArray::AllocatorType;
87b3018603SNico Weber 
88b3018603SNico Weber // These need to be global aligned storage to avoid dynamic initialization. We
89b3018603SNico Weber // need these to be aligned to allow us to placement new objects into the
90b3018603SNico Weber // storage, and have pointers to those objects be appropriately aligned.
91*cac78214SMarc Auberer alignas(ProfileBufferArray) static std::byte
92*cac78214SMarc Auberer     ProfileBuffersStorage[sizeof(ProfileBufferArray)];
93*cac78214SMarc Auberer alignas(ProfileBufferArrayAllocator) static std::byte
94*cac78214SMarc Auberer     ProfileBufferArrayAllocatorStorage[sizeof(ProfileBufferArrayAllocator)];
95b3018603SNico Weber 
96b3018603SNico Weber static ProfileBufferArrayAllocator *ProfileBuffersAllocator = nullptr;
97b3018603SNico Weber static ProfileBufferArray *ProfileBuffers = nullptr;
98b3018603SNico Weber 
99b3018603SNico Weber // Use a global flag to determine whether the collector implementation has been
100b3018603SNico Weber // initialized.
101b3018603SNico Weber static atomic_uint8_t CollectorInitialized{0};
102b3018603SNico Weber 
103b3018603SNico Weber } // namespace
104b3018603SNico Weber 
post(BufferQueue * Q,FunctionCallTrie && T,FunctionCallTrie::Allocators && A,FunctionCallTrie::Allocators::Buffers && B,tid_t TId)105b3018603SNico Weber void post(BufferQueue *Q, FunctionCallTrie &&T,
106b3018603SNico Weber           FunctionCallTrie::Allocators &&A,
107b3018603SNico Weber           FunctionCallTrie::Allocators::Buffers &&B,
108b3018603SNico Weber           tid_t TId) XRAY_NEVER_INSTRUMENT {
109b3018603SNico Weber   DCHECK_NE(Q, nullptr);
110b3018603SNico Weber 
111b3018603SNico Weber   // Bail out early if the collector has not been initialized.
112b3018603SNico Weber   if (!atomic_load(&CollectorInitialized, memory_order_acquire)) {
113b3018603SNico Weber     T.~FunctionCallTrie();
114b3018603SNico Weber     A.~Allocators();
115b3018603SNico Weber     Q->releaseBuffer(B.NodeBuffer);
116b3018603SNico Weber     Q->releaseBuffer(B.RootsBuffer);
117b3018603SNico Weber     Q->releaseBuffer(B.ShadowStackBuffer);
118b3018603SNico Weber     Q->releaseBuffer(B.NodeIdPairBuffer);
119b3018603SNico Weber     B.~Buffers();
120b3018603SNico Weber     return;
121b3018603SNico Weber   }
122b3018603SNico Weber 
123b3018603SNico Weber   {
124b3018603SNico Weber     SpinMutexLock Lock(&GlobalMutex);
125b3018603SNico Weber     DCHECK_NE(TDAllocator, nullptr);
126b3018603SNico Weber     DCHECK_NE(TDArray, nullptr);
127b3018603SNico Weber 
128b3018603SNico Weber     if (TDArray->AppendEmplace(Q, std::move(B), std::move(A), std::move(T),
129b3018603SNico Weber                                TId) == nullptr) {
130b3018603SNico Weber       // If we fail to add the data to the array, we should destroy the objects
131b3018603SNico Weber       // handed us.
132b3018603SNico Weber       T.~FunctionCallTrie();
133b3018603SNico Weber       A.~Allocators();
134b3018603SNico Weber       Q->releaseBuffer(B.NodeBuffer);
135b3018603SNico Weber       Q->releaseBuffer(B.RootsBuffer);
136b3018603SNico Weber       Q->releaseBuffer(B.ShadowStackBuffer);
137b3018603SNico Weber       Q->releaseBuffer(B.NodeIdPairBuffer);
138b3018603SNico Weber       B.~Buffers();
139b3018603SNico Weber     }
140b3018603SNico Weber   }
141b3018603SNico Weber }
142b3018603SNico Weber 
143b3018603SNico Weber // A PathArray represents the function id's representing a stack trace. In this
144b3018603SNico Weber // context a path is almost always represented from the leaf function in a call
145b3018603SNico Weber // stack to a root of the call trie.
146b3018603SNico Weber using PathArray = Array<int32_t>;
147b3018603SNico Weber 
148b3018603SNico Weber struct ProfileRecord {
149b3018603SNico Weber   using PathAllocator = typename PathArray::AllocatorType;
150b3018603SNico Weber 
151b3018603SNico Weber   // The Path in this record is the function id's from the leaf to the root of
152b3018603SNico Weber   // the function call stack as represented from a FunctionCallTrie.
153b3018603SNico Weber   PathArray Path;
154b3018603SNico Weber   const FunctionCallTrie::Node *Node;
155b3018603SNico Weber };
156b3018603SNico Weber 
157b3018603SNico Weber namespace {
158b3018603SNico Weber 
159b3018603SNico Weber using ProfileRecordArray = Array<ProfileRecord>;
160b3018603SNico Weber 
161b3018603SNico Weber // Walk a depth-first traversal of each root of the FunctionCallTrie to generate
162b3018603SNico Weber // the path(s) and the data associated with the path.
163b3018603SNico Weber static void
populateRecords(ProfileRecordArray & PRs,ProfileRecord::PathAllocator & PA,const FunctionCallTrie & Trie)164b3018603SNico Weber populateRecords(ProfileRecordArray &PRs, ProfileRecord::PathAllocator &PA,
165b3018603SNico Weber                 const FunctionCallTrie &Trie) XRAY_NEVER_INSTRUMENT {
166b3018603SNico Weber   using StackArray = Array<const FunctionCallTrie::Node *>;
167b3018603SNico Weber   using StackAllocator = typename StackArray::AllocatorType;
168b3018603SNico Weber   StackAllocator StackAlloc(profilingFlags()->stack_allocator_max);
169b3018603SNico Weber   StackArray DFSStack(StackAlloc);
170b3018603SNico Weber   for (const auto *R : Trie.getRoots()) {
171b3018603SNico Weber     DFSStack.Append(R);
172b3018603SNico Weber     while (!DFSStack.empty()) {
173b3018603SNico Weber       auto *Node = DFSStack.back();
174b3018603SNico Weber       DFSStack.trim(1);
175b3018603SNico Weber       if (Node == nullptr)
176b3018603SNico Weber         continue;
177b3018603SNico Weber       auto Record = PRs.AppendEmplace(PathArray{PA}, Node);
178b3018603SNico Weber       if (Record == nullptr)
179b3018603SNico Weber         return;
180b3018603SNico Weber       DCHECK_NE(Record, nullptr);
181b3018603SNico Weber 
182b3018603SNico Weber       // Traverse the Node's parents and as we're doing so, get the FIds in
183b3018603SNico Weber       // the order they appear.
184b3018603SNico Weber       for (auto N = Node; N != nullptr; N = N->Parent)
185b3018603SNico Weber         Record->Path.Append(N->FId);
186b3018603SNico Weber       DCHECK(!Record->Path.empty());
187b3018603SNico Weber 
188b3018603SNico Weber       for (const auto C : Node->Callees)
189b3018603SNico Weber         DFSStack.Append(C.NodePtr);
190b3018603SNico Weber     }
191b3018603SNico Weber   }
192b3018603SNico Weber }
193b3018603SNico Weber 
serializeRecords(ProfileBuffer * Buffer,const BlockHeader & Header,const ProfileRecordArray & ProfileRecords)194b3018603SNico Weber static void serializeRecords(ProfileBuffer *Buffer, const BlockHeader &Header,
195b3018603SNico Weber                              const ProfileRecordArray &ProfileRecords)
196b3018603SNico Weber     XRAY_NEVER_INSTRUMENT {
197b3018603SNico Weber   auto NextPtr = static_cast<uint8_t *>(
198b3018603SNico Weber                      internal_memcpy(Buffer->Data, &Header, sizeof(Header))) +
199b3018603SNico Weber                  sizeof(Header);
200b3018603SNico Weber   for (const auto &Record : ProfileRecords) {
201b3018603SNico Weber     // List of IDs follow:
202b3018603SNico Weber     for (const auto FId : Record.Path)
203b3018603SNico Weber       NextPtr =
204b3018603SNico Weber           static_cast<uint8_t *>(internal_memcpy(NextPtr, &FId, sizeof(FId))) +
205b3018603SNico Weber           sizeof(FId);
206b3018603SNico Weber 
207b3018603SNico Weber     // Add the sentinel here.
208b3018603SNico Weber     constexpr int32_t SentinelFId = 0;
209b3018603SNico Weber     NextPtr = static_cast<uint8_t *>(
210b3018603SNico Weber                   internal_memset(NextPtr, SentinelFId, sizeof(SentinelFId))) +
211b3018603SNico Weber               sizeof(SentinelFId);
212b3018603SNico Weber 
213b3018603SNico Weber     // Add the node data here.
214b3018603SNico Weber     NextPtr =
215b3018603SNico Weber         static_cast<uint8_t *>(internal_memcpy(
216b3018603SNico Weber             NextPtr, &Record.Node->CallCount, sizeof(Record.Node->CallCount))) +
217b3018603SNico Weber         sizeof(Record.Node->CallCount);
218b3018603SNico Weber     NextPtr = static_cast<uint8_t *>(
219b3018603SNico Weber                   internal_memcpy(NextPtr, &Record.Node->CumulativeLocalTime,
220b3018603SNico Weber                                   sizeof(Record.Node->CumulativeLocalTime))) +
221b3018603SNico Weber               sizeof(Record.Node->CumulativeLocalTime);
222b3018603SNico Weber   }
223b3018603SNico Weber 
224b3018603SNico Weber   DCHECK_EQ(NextPtr - static_cast<uint8_t *>(Buffer->Data), Buffer->Size);
225b3018603SNico Weber }
226b3018603SNico Weber 
227b3018603SNico Weber } // namespace
228b3018603SNico Weber 
serialize()229b3018603SNico Weber void serialize() XRAY_NEVER_INSTRUMENT {
230b3018603SNico Weber   if (!atomic_load(&CollectorInitialized, memory_order_acquire))
231b3018603SNico Weber     return;
232b3018603SNico Weber 
233b3018603SNico Weber   SpinMutexLock Lock(&GlobalMutex);
234b3018603SNico Weber 
235b3018603SNico Weber   // Clear out the global ProfileBuffers, if it's not empty.
236b3018603SNico Weber   for (auto &B : *ProfileBuffers)
237b3018603SNico Weber     deallocateBuffer(reinterpret_cast<unsigned char *>(B.Data), B.Size);
238b3018603SNico Weber   ProfileBuffers->trim(ProfileBuffers->size());
239b3018603SNico Weber 
240b3018603SNico Weber   DCHECK_NE(TDArray, nullptr);
241b3018603SNico Weber   if (TDArray->empty())
242b3018603SNico Weber     return;
243b3018603SNico Weber 
244b3018603SNico Weber   // Then repopulate the global ProfileBuffers.
245b3018603SNico Weber   u32 I = 0;
246b3018603SNico Weber   auto MaxSize = profilingFlags()->global_allocator_max;
247b3018603SNico Weber   auto ProfileArena = allocateBuffer(MaxSize);
248b3018603SNico Weber   if (ProfileArena == nullptr)
249b3018603SNico Weber     return;
250b3018603SNico Weber 
251b3018603SNico Weber   auto ProfileArenaCleanup = at_scope_exit(
252b3018603SNico Weber       [&]() XRAY_NEVER_INSTRUMENT { deallocateBuffer(ProfileArena, MaxSize); });
253b3018603SNico Weber 
254b3018603SNico Weber   auto PathArena = allocateBuffer(profilingFlags()->global_allocator_max);
255b3018603SNico Weber   if (PathArena == nullptr)
256b3018603SNico Weber     return;
257b3018603SNico Weber 
258b3018603SNico Weber   auto PathArenaCleanup = at_scope_exit(
259b3018603SNico Weber       [&]() XRAY_NEVER_INSTRUMENT { deallocateBuffer(PathArena, MaxSize); });
260b3018603SNico Weber 
261b3018603SNico Weber   for (const auto &ThreadTrie : *TDArray) {
262b3018603SNico Weber     using ProfileRecordAllocator = typename ProfileRecordArray::AllocatorType;
263b3018603SNico Weber     ProfileRecordAllocator PRAlloc(ProfileArena,
264b3018603SNico Weber                                    profilingFlags()->global_allocator_max);
265b3018603SNico Weber     ProfileRecord::PathAllocator PathAlloc(
266b3018603SNico Weber         PathArena, profilingFlags()->global_allocator_max);
267b3018603SNico Weber     ProfileRecordArray ProfileRecords(PRAlloc);
268b3018603SNico Weber 
269b3018603SNico Weber     // First, we want to compute the amount of space we're going to need. We'll
270b3018603SNico Weber     // use a local allocator and an __xray::Array<...> to store the intermediary
271b3018603SNico Weber     // data, then compute the size as we're going along. Then we'll allocate the
272b3018603SNico Weber     // contiguous space to contain the thread buffer data.
273b3018603SNico Weber     if (ThreadTrie.FCT.getRoots().empty())
274b3018603SNico Weber       continue;
275b3018603SNico Weber 
276b3018603SNico Weber     populateRecords(ProfileRecords, PathAlloc, ThreadTrie.FCT);
277b3018603SNico Weber     DCHECK(!ThreadTrie.FCT.getRoots().empty());
278b3018603SNico Weber     DCHECK(!ProfileRecords.empty());
279b3018603SNico Weber 
280b3018603SNico Weber     // Go through each record, to compute the sizes.
281b3018603SNico Weber     //
282b3018603SNico Weber     // header size = block size (4 bytes)
283b3018603SNico Weber     //   + block number (4 bytes)
284b3018603SNico Weber     //   + thread id (8 bytes)
285b3018603SNico Weber     // record size = path ids (4 bytes * number of ids + sentinel 4 bytes)
286b3018603SNico Weber     //   + call count (8 bytes)
287b3018603SNico Weber     //   + local time (8 bytes)
288b3018603SNico Weber     //   + end of record (8 bytes)
289b3018603SNico Weber     u32 CumulativeSizes = 0;
290b3018603SNico Weber     for (const auto &Record : ProfileRecords)
291b3018603SNico Weber       CumulativeSizes += 20 + (4 * Record.Path.size());
292b3018603SNico Weber 
293b3018603SNico Weber     BlockHeader Header{16 + CumulativeSizes, I++, ThreadTrie.TId};
294b3018603SNico Weber     auto B = ProfileBuffers->Append({});
295b3018603SNico Weber     B->Size = sizeof(Header) + CumulativeSizes;
296b3018603SNico Weber     B->Data = allocateBuffer(B->Size);
297b3018603SNico Weber     DCHECK_NE(B->Data, nullptr);
298b3018603SNico Weber     serializeRecords(B, Header, ProfileRecords);
299b3018603SNico Weber   }
300b3018603SNico Weber }
301b3018603SNico Weber 
reset()302b3018603SNico Weber void reset() XRAY_NEVER_INSTRUMENT {
303b3018603SNico Weber   atomic_store(&CollectorInitialized, 0, memory_order_release);
304b3018603SNico Weber   SpinMutexLock Lock(&GlobalMutex);
305b3018603SNico Weber 
306b3018603SNico Weber   if (ProfileBuffers != nullptr) {
307b3018603SNico Weber     // Clear out the profile buffers that have been serialized.
308b3018603SNico Weber     for (auto &B : *ProfileBuffers)
309b3018603SNico Weber       deallocateBuffer(reinterpret_cast<uint8_t *>(B.Data), B.Size);
310b3018603SNico Weber     ProfileBuffers->trim(ProfileBuffers->size());
311b3018603SNico Weber     ProfileBuffers = nullptr;
312b3018603SNico Weber   }
313b3018603SNico Weber 
314b3018603SNico Weber   if (TDArray != nullptr) {
315b3018603SNico Weber     // Release the resources as required.
316b3018603SNico Weber     for (auto &TD : *TDArray) {
317b3018603SNico Weber       TD.BQ->releaseBuffer(TD.Buffers.NodeBuffer);
318b3018603SNico Weber       TD.BQ->releaseBuffer(TD.Buffers.RootsBuffer);
319b3018603SNico Weber       TD.BQ->releaseBuffer(TD.Buffers.ShadowStackBuffer);
320b3018603SNico Weber       TD.BQ->releaseBuffer(TD.Buffers.NodeIdPairBuffer);
321b3018603SNico Weber     }
322b3018603SNico Weber     // We don't bother destroying the array here because we've already
323b3018603SNico Weber     // potentially freed the backing store for the array. Instead we're going to
324b3018603SNico Weber     // reset the pointer to nullptr, and re-use the storage later instead
325b3018603SNico Weber     // (placement-new'ing into the storage as-is).
326b3018603SNico Weber     TDArray = nullptr;
327b3018603SNico Weber   }
328b3018603SNico Weber 
329b3018603SNico Weber   if (TDAllocator != nullptr) {
330b3018603SNico Weber     TDAllocator->~Allocator();
331b3018603SNico Weber     TDAllocator = nullptr;
332b3018603SNico Weber   }
333b3018603SNico Weber 
334b3018603SNico Weber   if (Buffer.Data != nullptr) {
335b3018603SNico Weber     BQ->releaseBuffer(Buffer);
336b3018603SNico Weber   }
337b3018603SNico Weber 
338b3018603SNico Weber   if (BQ == nullptr) {
339b3018603SNico Weber     bool Success = false;
340b3018603SNico Weber     new (&BufferQueueStorage)
341b3018603SNico Weber         BufferQueue(profilingFlags()->global_allocator_max, 1, Success);
342b3018603SNico Weber     if (!Success)
343b3018603SNico Weber       return;
344b3018603SNico Weber     BQ = reinterpret_cast<BufferQueue *>(&BufferQueueStorage);
345b3018603SNico Weber   } else {
346b3018603SNico Weber     BQ->finalize();
347b3018603SNico Weber 
348b3018603SNico Weber     if (BQ->init(profilingFlags()->global_allocator_max, 1) !=
349b3018603SNico Weber         BufferQueue::ErrorCode::Ok)
350b3018603SNico Weber       return;
351b3018603SNico Weber   }
352b3018603SNico Weber 
353b3018603SNico Weber   if (BQ->getBuffer(Buffer) != BufferQueue::ErrorCode::Ok)
354b3018603SNico Weber     return;
355b3018603SNico Weber 
356b3018603SNico Weber   new (&ProfileBufferArrayAllocatorStorage)
357b3018603SNico Weber       ProfileBufferArrayAllocator(profilingFlags()->global_allocator_max);
358b3018603SNico Weber   ProfileBuffersAllocator = reinterpret_cast<ProfileBufferArrayAllocator *>(
359b3018603SNico Weber       &ProfileBufferArrayAllocatorStorage);
360b3018603SNico Weber 
361b3018603SNico Weber   new (&ProfileBuffersStorage) ProfileBufferArray(*ProfileBuffersAllocator);
362b3018603SNico Weber   ProfileBuffers =
363b3018603SNico Weber       reinterpret_cast<ProfileBufferArray *>(&ProfileBuffersStorage);
364b3018603SNico Weber 
365b3018603SNico Weber   new (&ThreadDataAllocatorStorage)
366b3018603SNico Weber       ThreadDataAllocator(Buffer.Data, Buffer.Size);
367b3018603SNico Weber   TDAllocator =
368b3018603SNico Weber       reinterpret_cast<ThreadDataAllocator *>(&ThreadDataAllocatorStorage);
369b3018603SNico Weber   new (&ThreadDataArrayStorage) ThreadDataArray(*TDAllocator);
370b3018603SNico Weber   TDArray = reinterpret_cast<ThreadDataArray *>(&ThreadDataArrayStorage);
371b3018603SNico Weber 
372b3018603SNico Weber   atomic_store(&CollectorInitialized, 1, memory_order_release);
373b3018603SNico Weber }
374b3018603SNico Weber 
nextBuffer(XRayBuffer B)375b3018603SNico Weber XRayBuffer nextBuffer(XRayBuffer B) XRAY_NEVER_INSTRUMENT {
376b3018603SNico Weber   SpinMutexLock Lock(&GlobalMutex);
377b3018603SNico Weber 
378b3018603SNico Weber   if (ProfileBuffers == nullptr || ProfileBuffers->size() == 0)
379b3018603SNico Weber     return {nullptr, 0};
380b3018603SNico Weber 
381b3018603SNico Weber   static pthread_once_t Once = PTHREAD_ONCE_INIT;
382*cac78214SMarc Auberer   alignas(XRayProfilingFileHeader) static std::byte
383*cac78214SMarc Auberer       FileHeaderStorage[sizeof(XRayProfilingFileHeader)];
384b3018603SNico Weber   pthread_once(
385b3018603SNico Weber       &Once, +[]() XRAY_NEVER_INSTRUMENT {
386b3018603SNico Weber         new (&FileHeaderStorage) XRayProfilingFileHeader{};
387b3018603SNico Weber       });
388b3018603SNico Weber 
389b3018603SNico Weber   if (UNLIKELY(B.Data == nullptr)) {
390b3018603SNico Weber     // The first buffer should always contain the file header information.
391b3018603SNico Weber     auto &FileHeader =
392b3018603SNico Weber         *reinterpret_cast<XRayProfilingFileHeader *>(&FileHeaderStorage);
393b3018603SNico Weber     FileHeader.Timestamp = NanoTime();
394b3018603SNico Weber     FileHeader.PID = internal_getpid();
395b3018603SNico Weber     return {&FileHeaderStorage, sizeof(XRayProfilingFileHeader)};
396b3018603SNico Weber   }
397b3018603SNico Weber 
398b3018603SNico Weber   if (UNLIKELY(B.Data == &FileHeaderStorage))
399b3018603SNico Weber     return {(*ProfileBuffers)[0].Data, (*ProfileBuffers)[0].Size};
400b3018603SNico Weber 
401b3018603SNico Weber   BlockHeader Header;
402b3018603SNico Weber   internal_memcpy(&Header, B.Data, sizeof(BlockHeader));
403b3018603SNico Weber   auto NextBlock = Header.BlockNum + 1;
404b3018603SNico Weber   if (NextBlock < ProfileBuffers->size())
405b3018603SNico Weber     return {(*ProfileBuffers)[NextBlock].Data,
406b3018603SNico Weber             (*ProfileBuffers)[NextBlock].Size};
407b3018603SNico Weber   return {nullptr, 0};
408b3018603SNico Weber }
409b3018603SNico Weber 
410b3018603SNico Weber } // namespace profileCollectorService
411b3018603SNico Weber } // namespace __xray
412