xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/xray/xray_profile_collector.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
168d75effSDimitry Andric //===-- xray_profile_collector.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 //
968d75effSDimitry Andric // This file is a part of XRay, a dynamic runtime instrumentation system.
1068d75effSDimitry Andric //
1168d75effSDimitry Andric // This implements the interface for the profileCollectorService.
1268d75effSDimitry Andric //
1368d75effSDimitry Andric //===----------------------------------------------------------------------===//
1468d75effSDimitry Andric #include "xray_profile_collector.h"
1568d75effSDimitry Andric #include "sanitizer_common/sanitizer_common.h"
1668d75effSDimitry Andric #include "xray_allocator.h"
1768d75effSDimitry Andric #include "xray_defs.h"
1868d75effSDimitry Andric #include "xray_profiling_flags.h"
1968d75effSDimitry Andric #include "xray_segmented_array.h"
2068d75effSDimitry Andric #include <memory>
2168d75effSDimitry Andric #include <pthread.h>
2268d75effSDimitry Andric #include <utility>
2368d75effSDimitry Andric 
2468d75effSDimitry Andric namespace __xray {
2568d75effSDimitry Andric namespace profileCollectorService {
2668d75effSDimitry Andric 
2768d75effSDimitry Andric namespace {
2868d75effSDimitry Andric 
2968d75effSDimitry Andric SpinMutex GlobalMutex;
3068d75effSDimitry Andric struct ThreadTrie {
3168d75effSDimitry Andric   tid_t TId;
32*0fca6ea1SDimitry Andric   alignas(FunctionCallTrie) std::byte TrieStorage[sizeof(FunctionCallTrie)];
3368d75effSDimitry Andric };
3468d75effSDimitry Andric 
3568d75effSDimitry Andric struct ProfileBuffer {
3668d75effSDimitry Andric   void *Data;
3768d75effSDimitry Andric   size_t Size;
3868d75effSDimitry Andric };
3968d75effSDimitry Andric 
4068d75effSDimitry Andric // Current version of the profile format.
4168d75effSDimitry Andric constexpr u64 XRayProfilingVersion = 0x20180424;
4268d75effSDimitry Andric 
4368d75effSDimitry Andric // Identifier for XRay profiling files 'xrayprof' in hex.
4468d75effSDimitry Andric constexpr u64 XRayMagicBytes = 0x7872617970726f66;
4568d75effSDimitry Andric 
4668d75effSDimitry Andric struct XRayProfilingFileHeader {
4768d75effSDimitry Andric   const u64 MagicBytes = XRayMagicBytes;
4868d75effSDimitry Andric   const u64 Version = XRayProfilingVersion;
4968d75effSDimitry Andric   u64 Timestamp = 0; // System time in nanoseconds.
5068d75effSDimitry Andric   u64 PID = 0;       // Process ID.
5168d75effSDimitry Andric };
5268d75effSDimitry Andric 
5368d75effSDimitry Andric struct BlockHeader {
5468d75effSDimitry Andric   u32 BlockSize;
5568d75effSDimitry Andric   u32 BlockNum;
5668d75effSDimitry Andric   u64 ThreadId;
5768d75effSDimitry Andric };
5868d75effSDimitry Andric 
5968d75effSDimitry Andric struct ThreadData {
6068d75effSDimitry Andric   BufferQueue *BQ;
6168d75effSDimitry Andric   FunctionCallTrie::Allocators::Buffers Buffers;
6268d75effSDimitry Andric   FunctionCallTrie::Allocators Allocators;
6368d75effSDimitry Andric   FunctionCallTrie FCT;
6468d75effSDimitry Andric   tid_t TId;
6568d75effSDimitry Andric };
6668d75effSDimitry Andric 
6768d75effSDimitry Andric using ThreadDataArray = Array<ThreadData>;
6868d75effSDimitry Andric using ThreadDataAllocator = ThreadDataArray::AllocatorType;
6968d75effSDimitry Andric 
7068d75effSDimitry Andric // We use a separate buffer queue for the backing store for the allocator used
7168d75effSDimitry Andric // by the ThreadData array. This lets us host the buffers, allocators, and tries
7268d75effSDimitry Andric // associated with a thread by moving the data into the array instead of
7368d75effSDimitry Andric // attempting to copy the data to a separately backed set of tries.
74*0fca6ea1SDimitry Andric alignas(BufferQueue) static std::byte BufferQueueStorage[sizeof(BufferQueue)];
7568d75effSDimitry Andric static BufferQueue *BQ = nullptr;
7668d75effSDimitry Andric static BufferQueue::Buffer Buffer;
77*0fca6ea1SDimitry Andric alignas(ThreadDataAllocator) static std::byte
78*0fca6ea1SDimitry Andric     ThreadDataAllocatorStorage[sizeof(ThreadDataAllocator)];
79*0fca6ea1SDimitry Andric alignas(ThreadDataArray) static std::byte
80*0fca6ea1SDimitry Andric     ThreadDataArrayStorage[sizeof(ThreadDataArray)];
8168d75effSDimitry Andric 
8268d75effSDimitry Andric static ThreadDataAllocator *TDAllocator = nullptr;
8368d75effSDimitry Andric static ThreadDataArray *TDArray = nullptr;
8468d75effSDimitry Andric 
8568d75effSDimitry Andric using ProfileBufferArray = Array<ProfileBuffer>;
8668d75effSDimitry Andric using ProfileBufferArrayAllocator = typename ProfileBufferArray::AllocatorType;
8768d75effSDimitry Andric 
8868d75effSDimitry Andric // These need to be global aligned storage to avoid dynamic initialization. We
8968d75effSDimitry Andric // need these to be aligned to allow us to placement new objects into the
9068d75effSDimitry Andric // storage, and have pointers to those objects be appropriately aligned.
91*0fca6ea1SDimitry Andric alignas(ProfileBufferArray) static std::byte
92*0fca6ea1SDimitry Andric     ProfileBuffersStorage[sizeof(ProfileBufferArray)];
93*0fca6ea1SDimitry Andric alignas(ProfileBufferArrayAllocator) static std::byte
94*0fca6ea1SDimitry Andric     ProfileBufferArrayAllocatorStorage[sizeof(ProfileBufferArrayAllocator)];
9568d75effSDimitry Andric 
9668d75effSDimitry Andric static ProfileBufferArrayAllocator *ProfileBuffersAllocator = nullptr;
9768d75effSDimitry Andric static ProfileBufferArray *ProfileBuffers = nullptr;
9868d75effSDimitry Andric 
9968d75effSDimitry Andric // Use a global flag to determine whether the collector implementation has been
10068d75effSDimitry Andric // initialized.
10168d75effSDimitry Andric static atomic_uint8_t CollectorInitialized{0};
10268d75effSDimitry Andric 
10368d75effSDimitry Andric } // namespace
10468d75effSDimitry Andric 
10568d75effSDimitry Andric void post(BufferQueue *Q, FunctionCallTrie &&T,
10668d75effSDimitry Andric           FunctionCallTrie::Allocators &&A,
10768d75effSDimitry Andric           FunctionCallTrie::Allocators::Buffers &&B,
10868d75effSDimitry Andric           tid_t TId) XRAY_NEVER_INSTRUMENT {
10968d75effSDimitry Andric   DCHECK_NE(Q, nullptr);
11068d75effSDimitry Andric 
11168d75effSDimitry Andric   // Bail out early if the collector has not been initialized.
11268d75effSDimitry Andric   if (!atomic_load(&CollectorInitialized, memory_order_acquire)) {
11368d75effSDimitry Andric     T.~FunctionCallTrie();
11468d75effSDimitry Andric     A.~Allocators();
11568d75effSDimitry Andric     Q->releaseBuffer(B.NodeBuffer);
11668d75effSDimitry Andric     Q->releaseBuffer(B.RootsBuffer);
11768d75effSDimitry Andric     Q->releaseBuffer(B.ShadowStackBuffer);
11868d75effSDimitry Andric     Q->releaseBuffer(B.NodeIdPairBuffer);
11968d75effSDimitry Andric     B.~Buffers();
12068d75effSDimitry Andric     return;
12168d75effSDimitry Andric   }
12268d75effSDimitry Andric 
12368d75effSDimitry Andric   {
12468d75effSDimitry Andric     SpinMutexLock Lock(&GlobalMutex);
12568d75effSDimitry Andric     DCHECK_NE(TDAllocator, nullptr);
12668d75effSDimitry Andric     DCHECK_NE(TDArray, nullptr);
12768d75effSDimitry Andric 
12868d75effSDimitry Andric     if (TDArray->AppendEmplace(Q, std::move(B), std::move(A), std::move(T),
12968d75effSDimitry Andric                                TId) == nullptr) {
13068d75effSDimitry Andric       // If we fail to add the data to the array, we should destroy the objects
13168d75effSDimitry Andric       // handed us.
13268d75effSDimitry Andric       T.~FunctionCallTrie();
13368d75effSDimitry Andric       A.~Allocators();
13468d75effSDimitry Andric       Q->releaseBuffer(B.NodeBuffer);
13568d75effSDimitry Andric       Q->releaseBuffer(B.RootsBuffer);
13668d75effSDimitry Andric       Q->releaseBuffer(B.ShadowStackBuffer);
13768d75effSDimitry Andric       Q->releaseBuffer(B.NodeIdPairBuffer);
13868d75effSDimitry Andric       B.~Buffers();
13968d75effSDimitry Andric     }
14068d75effSDimitry Andric   }
14168d75effSDimitry Andric }
14268d75effSDimitry Andric 
14368d75effSDimitry Andric // A PathArray represents the function id's representing a stack trace. In this
14468d75effSDimitry Andric // context a path is almost always represented from the leaf function in a call
14568d75effSDimitry Andric // stack to a root of the call trie.
14668d75effSDimitry Andric using PathArray = Array<int32_t>;
14768d75effSDimitry Andric 
14868d75effSDimitry Andric struct ProfileRecord {
14968d75effSDimitry Andric   using PathAllocator = typename PathArray::AllocatorType;
15068d75effSDimitry Andric 
15168d75effSDimitry Andric   // The Path in this record is the function id's from the leaf to the root of
15268d75effSDimitry Andric   // the function call stack as represented from a FunctionCallTrie.
15368d75effSDimitry Andric   PathArray Path;
15468d75effSDimitry Andric   const FunctionCallTrie::Node *Node;
15568d75effSDimitry Andric };
15668d75effSDimitry Andric 
15768d75effSDimitry Andric namespace {
15868d75effSDimitry Andric 
15968d75effSDimitry Andric using ProfileRecordArray = Array<ProfileRecord>;
16068d75effSDimitry Andric 
16168d75effSDimitry Andric // Walk a depth-first traversal of each root of the FunctionCallTrie to generate
16268d75effSDimitry Andric // the path(s) and the data associated with the path.
16368d75effSDimitry Andric static void
16468d75effSDimitry Andric populateRecords(ProfileRecordArray &PRs, ProfileRecord::PathAllocator &PA,
16568d75effSDimitry Andric                 const FunctionCallTrie &Trie) XRAY_NEVER_INSTRUMENT {
16668d75effSDimitry Andric   using StackArray = Array<const FunctionCallTrie::Node *>;
16768d75effSDimitry Andric   using StackAllocator = typename StackArray::AllocatorType;
16868d75effSDimitry Andric   StackAllocator StackAlloc(profilingFlags()->stack_allocator_max);
16968d75effSDimitry Andric   StackArray DFSStack(StackAlloc);
17068d75effSDimitry Andric   for (const auto *R : Trie.getRoots()) {
17168d75effSDimitry Andric     DFSStack.Append(R);
17268d75effSDimitry Andric     while (!DFSStack.empty()) {
17368d75effSDimitry Andric       auto *Node = DFSStack.back();
17468d75effSDimitry Andric       DFSStack.trim(1);
17568d75effSDimitry Andric       if (Node == nullptr)
17668d75effSDimitry Andric         continue;
17768d75effSDimitry Andric       auto Record = PRs.AppendEmplace(PathArray{PA}, Node);
17868d75effSDimitry Andric       if (Record == nullptr)
17968d75effSDimitry Andric         return;
18068d75effSDimitry Andric       DCHECK_NE(Record, nullptr);
18168d75effSDimitry Andric 
18268d75effSDimitry Andric       // Traverse the Node's parents and as we're doing so, get the FIds in
18368d75effSDimitry Andric       // the order they appear.
18468d75effSDimitry Andric       for (auto N = Node; N != nullptr; N = N->Parent)
18568d75effSDimitry Andric         Record->Path.Append(N->FId);
18668d75effSDimitry Andric       DCHECK(!Record->Path.empty());
18768d75effSDimitry Andric 
18868d75effSDimitry Andric       for (const auto C : Node->Callees)
18968d75effSDimitry Andric         DFSStack.Append(C.NodePtr);
19068d75effSDimitry Andric     }
19168d75effSDimitry Andric   }
19268d75effSDimitry Andric }
19368d75effSDimitry Andric 
19468d75effSDimitry Andric static void serializeRecords(ProfileBuffer *Buffer, const BlockHeader &Header,
19568d75effSDimitry Andric                              const ProfileRecordArray &ProfileRecords)
19668d75effSDimitry Andric     XRAY_NEVER_INSTRUMENT {
19768d75effSDimitry Andric   auto NextPtr = static_cast<uint8_t *>(
19868d75effSDimitry Andric                      internal_memcpy(Buffer->Data, &Header, sizeof(Header))) +
19968d75effSDimitry Andric                  sizeof(Header);
20068d75effSDimitry Andric   for (const auto &Record : ProfileRecords) {
20168d75effSDimitry Andric     // List of IDs follow:
20268d75effSDimitry Andric     for (const auto FId : Record.Path)
20368d75effSDimitry Andric       NextPtr =
20468d75effSDimitry Andric           static_cast<uint8_t *>(internal_memcpy(NextPtr, &FId, sizeof(FId))) +
20568d75effSDimitry Andric           sizeof(FId);
20668d75effSDimitry Andric 
20768d75effSDimitry Andric     // Add the sentinel here.
20868d75effSDimitry Andric     constexpr int32_t SentinelFId = 0;
20968d75effSDimitry Andric     NextPtr = static_cast<uint8_t *>(
21068d75effSDimitry Andric                   internal_memset(NextPtr, SentinelFId, sizeof(SentinelFId))) +
21168d75effSDimitry Andric               sizeof(SentinelFId);
21268d75effSDimitry Andric 
21368d75effSDimitry Andric     // Add the node data here.
21468d75effSDimitry Andric     NextPtr =
21568d75effSDimitry Andric         static_cast<uint8_t *>(internal_memcpy(
21668d75effSDimitry Andric             NextPtr, &Record.Node->CallCount, sizeof(Record.Node->CallCount))) +
21768d75effSDimitry Andric         sizeof(Record.Node->CallCount);
21868d75effSDimitry Andric     NextPtr = static_cast<uint8_t *>(
21968d75effSDimitry Andric                   internal_memcpy(NextPtr, &Record.Node->CumulativeLocalTime,
22068d75effSDimitry Andric                                   sizeof(Record.Node->CumulativeLocalTime))) +
22168d75effSDimitry Andric               sizeof(Record.Node->CumulativeLocalTime);
22268d75effSDimitry Andric   }
22368d75effSDimitry Andric 
22468d75effSDimitry Andric   DCHECK_EQ(NextPtr - static_cast<uint8_t *>(Buffer->Data), Buffer->Size);
22568d75effSDimitry Andric }
22668d75effSDimitry Andric 
22768d75effSDimitry Andric } // namespace
22868d75effSDimitry Andric 
22968d75effSDimitry Andric void serialize() XRAY_NEVER_INSTRUMENT {
23068d75effSDimitry Andric   if (!atomic_load(&CollectorInitialized, memory_order_acquire))
23168d75effSDimitry Andric     return;
23268d75effSDimitry Andric 
23368d75effSDimitry Andric   SpinMutexLock Lock(&GlobalMutex);
23468d75effSDimitry Andric 
23568d75effSDimitry Andric   // Clear out the global ProfileBuffers, if it's not empty.
23668d75effSDimitry Andric   for (auto &B : *ProfileBuffers)
23768d75effSDimitry Andric     deallocateBuffer(reinterpret_cast<unsigned char *>(B.Data), B.Size);
23868d75effSDimitry Andric   ProfileBuffers->trim(ProfileBuffers->size());
23968d75effSDimitry Andric 
24068d75effSDimitry Andric   DCHECK_NE(TDArray, nullptr);
24168d75effSDimitry Andric   if (TDArray->empty())
24268d75effSDimitry Andric     return;
24368d75effSDimitry Andric 
24468d75effSDimitry Andric   // Then repopulate the global ProfileBuffers.
24568d75effSDimitry Andric   u32 I = 0;
24668d75effSDimitry Andric   auto MaxSize = profilingFlags()->global_allocator_max;
24768d75effSDimitry Andric   auto ProfileArena = allocateBuffer(MaxSize);
24868d75effSDimitry Andric   if (ProfileArena == nullptr)
24968d75effSDimitry Andric     return;
25068d75effSDimitry Andric 
25168d75effSDimitry Andric   auto ProfileArenaCleanup = at_scope_exit(
25268d75effSDimitry Andric       [&]() XRAY_NEVER_INSTRUMENT { deallocateBuffer(ProfileArena, MaxSize); });
25368d75effSDimitry Andric 
25468d75effSDimitry Andric   auto PathArena = allocateBuffer(profilingFlags()->global_allocator_max);
25568d75effSDimitry Andric   if (PathArena == nullptr)
25668d75effSDimitry Andric     return;
25768d75effSDimitry Andric 
25868d75effSDimitry Andric   auto PathArenaCleanup = at_scope_exit(
25968d75effSDimitry Andric       [&]() XRAY_NEVER_INSTRUMENT { deallocateBuffer(PathArena, MaxSize); });
26068d75effSDimitry Andric 
26168d75effSDimitry Andric   for (const auto &ThreadTrie : *TDArray) {
26268d75effSDimitry Andric     using ProfileRecordAllocator = typename ProfileRecordArray::AllocatorType;
26368d75effSDimitry Andric     ProfileRecordAllocator PRAlloc(ProfileArena,
26468d75effSDimitry Andric                                    profilingFlags()->global_allocator_max);
26568d75effSDimitry Andric     ProfileRecord::PathAllocator PathAlloc(
26668d75effSDimitry Andric         PathArena, profilingFlags()->global_allocator_max);
26768d75effSDimitry Andric     ProfileRecordArray ProfileRecords(PRAlloc);
26868d75effSDimitry Andric 
26968d75effSDimitry Andric     // First, we want to compute the amount of space we're going to need. We'll
27068d75effSDimitry Andric     // use a local allocator and an __xray::Array<...> to store the intermediary
27168d75effSDimitry Andric     // data, then compute the size as we're going along. Then we'll allocate the
27268d75effSDimitry Andric     // contiguous space to contain the thread buffer data.
27368d75effSDimitry Andric     if (ThreadTrie.FCT.getRoots().empty())
27468d75effSDimitry Andric       continue;
27568d75effSDimitry Andric 
27668d75effSDimitry Andric     populateRecords(ProfileRecords, PathAlloc, ThreadTrie.FCT);
27768d75effSDimitry Andric     DCHECK(!ThreadTrie.FCT.getRoots().empty());
27868d75effSDimitry Andric     DCHECK(!ProfileRecords.empty());
27968d75effSDimitry Andric 
28068d75effSDimitry Andric     // Go through each record, to compute the sizes.
28168d75effSDimitry Andric     //
28268d75effSDimitry Andric     // header size = block size (4 bytes)
28368d75effSDimitry Andric     //   + block number (4 bytes)
28468d75effSDimitry Andric     //   + thread id (8 bytes)
28568d75effSDimitry Andric     // record size = path ids (4 bytes * number of ids + sentinel 4 bytes)
28668d75effSDimitry Andric     //   + call count (8 bytes)
28768d75effSDimitry Andric     //   + local time (8 bytes)
28868d75effSDimitry Andric     //   + end of record (8 bytes)
28968d75effSDimitry Andric     u32 CumulativeSizes = 0;
29068d75effSDimitry Andric     for (const auto &Record : ProfileRecords)
29168d75effSDimitry Andric       CumulativeSizes += 20 + (4 * Record.Path.size());
29268d75effSDimitry Andric 
29368d75effSDimitry Andric     BlockHeader Header{16 + CumulativeSizes, I++, ThreadTrie.TId};
29468d75effSDimitry Andric     auto B = ProfileBuffers->Append({});
29568d75effSDimitry Andric     B->Size = sizeof(Header) + CumulativeSizes;
29668d75effSDimitry Andric     B->Data = allocateBuffer(B->Size);
29768d75effSDimitry Andric     DCHECK_NE(B->Data, nullptr);
29868d75effSDimitry Andric     serializeRecords(B, Header, ProfileRecords);
29968d75effSDimitry Andric   }
30068d75effSDimitry Andric }
30168d75effSDimitry Andric 
30268d75effSDimitry Andric void reset() XRAY_NEVER_INSTRUMENT {
30368d75effSDimitry Andric   atomic_store(&CollectorInitialized, 0, memory_order_release);
30468d75effSDimitry Andric   SpinMutexLock Lock(&GlobalMutex);
30568d75effSDimitry Andric 
30668d75effSDimitry Andric   if (ProfileBuffers != nullptr) {
30768d75effSDimitry Andric     // Clear out the profile buffers that have been serialized.
30868d75effSDimitry Andric     for (auto &B : *ProfileBuffers)
30968d75effSDimitry Andric       deallocateBuffer(reinterpret_cast<uint8_t *>(B.Data), B.Size);
31068d75effSDimitry Andric     ProfileBuffers->trim(ProfileBuffers->size());
31168d75effSDimitry Andric     ProfileBuffers = nullptr;
31268d75effSDimitry Andric   }
31368d75effSDimitry Andric 
31468d75effSDimitry Andric   if (TDArray != nullptr) {
31568d75effSDimitry Andric     // Release the resources as required.
31668d75effSDimitry Andric     for (auto &TD : *TDArray) {
31768d75effSDimitry Andric       TD.BQ->releaseBuffer(TD.Buffers.NodeBuffer);
31868d75effSDimitry Andric       TD.BQ->releaseBuffer(TD.Buffers.RootsBuffer);
31968d75effSDimitry Andric       TD.BQ->releaseBuffer(TD.Buffers.ShadowStackBuffer);
32068d75effSDimitry Andric       TD.BQ->releaseBuffer(TD.Buffers.NodeIdPairBuffer);
32168d75effSDimitry Andric     }
32268d75effSDimitry Andric     // We don't bother destroying the array here because we've already
32368d75effSDimitry Andric     // potentially freed the backing store for the array. Instead we're going to
32468d75effSDimitry Andric     // reset the pointer to nullptr, and re-use the storage later instead
32568d75effSDimitry Andric     // (placement-new'ing into the storage as-is).
32668d75effSDimitry Andric     TDArray = nullptr;
32768d75effSDimitry Andric   }
32868d75effSDimitry Andric 
32968d75effSDimitry Andric   if (TDAllocator != nullptr) {
33068d75effSDimitry Andric     TDAllocator->~Allocator();
33168d75effSDimitry Andric     TDAllocator = nullptr;
33268d75effSDimitry Andric   }
33368d75effSDimitry Andric 
33468d75effSDimitry Andric   if (Buffer.Data != nullptr) {
33568d75effSDimitry Andric     BQ->releaseBuffer(Buffer);
33668d75effSDimitry Andric   }
33768d75effSDimitry Andric 
33868d75effSDimitry Andric   if (BQ == nullptr) {
33968d75effSDimitry Andric     bool Success = false;
34068d75effSDimitry Andric     new (&BufferQueueStorage)
34168d75effSDimitry Andric         BufferQueue(profilingFlags()->global_allocator_max, 1, Success);
34268d75effSDimitry Andric     if (!Success)
34368d75effSDimitry Andric       return;
34468d75effSDimitry Andric     BQ = reinterpret_cast<BufferQueue *>(&BufferQueueStorage);
34568d75effSDimitry Andric   } else {
34668d75effSDimitry Andric     BQ->finalize();
34768d75effSDimitry Andric 
34868d75effSDimitry Andric     if (BQ->init(profilingFlags()->global_allocator_max, 1) !=
34968d75effSDimitry Andric         BufferQueue::ErrorCode::Ok)
35068d75effSDimitry Andric       return;
35168d75effSDimitry Andric   }
35268d75effSDimitry Andric 
35368d75effSDimitry Andric   if (BQ->getBuffer(Buffer) != BufferQueue::ErrorCode::Ok)
35468d75effSDimitry Andric     return;
35568d75effSDimitry Andric 
35668d75effSDimitry Andric   new (&ProfileBufferArrayAllocatorStorage)
35768d75effSDimitry Andric       ProfileBufferArrayAllocator(profilingFlags()->global_allocator_max);
35868d75effSDimitry Andric   ProfileBuffersAllocator = reinterpret_cast<ProfileBufferArrayAllocator *>(
35968d75effSDimitry Andric       &ProfileBufferArrayAllocatorStorage);
36068d75effSDimitry Andric 
36168d75effSDimitry Andric   new (&ProfileBuffersStorage) ProfileBufferArray(*ProfileBuffersAllocator);
36268d75effSDimitry Andric   ProfileBuffers =
36368d75effSDimitry Andric       reinterpret_cast<ProfileBufferArray *>(&ProfileBuffersStorage);
36468d75effSDimitry Andric 
36568d75effSDimitry Andric   new (&ThreadDataAllocatorStorage)
36668d75effSDimitry Andric       ThreadDataAllocator(Buffer.Data, Buffer.Size);
36768d75effSDimitry Andric   TDAllocator =
36868d75effSDimitry Andric       reinterpret_cast<ThreadDataAllocator *>(&ThreadDataAllocatorStorage);
36968d75effSDimitry Andric   new (&ThreadDataArrayStorage) ThreadDataArray(*TDAllocator);
37068d75effSDimitry Andric   TDArray = reinterpret_cast<ThreadDataArray *>(&ThreadDataArrayStorage);
37168d75effSDimitry Andric 
37268d75effSDimitry Andric   atomic_store(&CollectorInitialized, 1, memory_order_release);
37368d75effSDimitry Andric }
37468d75effSDimitry Andric 
37568d75effSDimitry Andric XRayBuffer nextBuffer(XRayBuffer B) XRAY_NEVER_INSTRUMENT {
37668d75effSDimitry Andric   SpinMutexLock Lock(&GlobalMutex);
37768d75effSDimitry Andric 
37868d75effSDimitry Andric   if (ProfileBuffers == nullptr || ProfileBuffers->size() == 0)
37968d75effSDimitry Andric     return {nullptr, 0};
38068d75effSDimitry Andric 
38168d75effSDimitry Andric   static pthread_once_t Once = PTHREAD_ONCE_INIT;
382*0fca6ea1SDimitry Andric   alignas(XRayProfilingFileHeader) static std::byte
383*0fca6ea1SDimitry Andric       FileHeaderStorage[sizeof(XRayProfilingFileHeader)];
38468d75effSDimitry Andric   pthread_once(
38568d75effSDimitry Andric       &Once, +[]() XRAY_NEVER_INSTRUMENT {
38668d75effSDimitry Andric         new (&FileHeaderStorage) XRayProfilingFileHeader{};
38768d75effSDimitry Andric       });
38868d75effSDimitry Andric 
38968d75effSDimitry Andric   if (UNLIKELY(B.Data == nullptr)) {
39068d75effSDimitry Andric     // The first buffer should always contain the file header information.
39168d75effSDimitry Andric     auto &FileHeader =
39268d75effSDimitry Andric         *reinterpret_cast<XRayProfilingFileHeader *>(&FileHeaderStorage);
39368d75effSDimitry Andric     FileHeader.Timestamp = NanoTime();
39468d75effSDimitry Andric     FileHeader.PID = internal_getpid();
39568d75effSDimitry Andric     return {&FileHeaderStorage, sizeof(XRayProfilingFileHeader)};
39668d75effSDimitry Andric   }
39768d75effSDimitry Andric 
39868d75effSDimitry Andric   if (UNLIKELY(B.Data == &FileHeaderStorage))
39968d75effSDimitry Andric     return {(*ProfileBuffers)[0].Data, (*ProfileBuffers)[0].Size};
40068d75effSDimitry Andric 
40168d75effSDimitry Andric   BlockHeader Header;
40268d75effSDimitry Andric   internal_memcpy(&Header, B.Data, sizeof(BlockHeader));
40368d75effSDimitry Andric   auto NextBlock = Header.BlockNum + 1;
40468d75effSDimitry Andric   if (NextBlock < ProfileBuffers->size())
40568d75effSDimitry Andric     return {(*ProfileBuffers)[NextBlock].Data,
40668d75effSDimitry Andric             (*ProfileBuffers)[NextBlock].Size};
40768d75effSDimitry Andric   return {nullptr, 0};
40868d75effSDimitry Andric }
40968d75effSDimitry Andric 
41068d75effSDimitry Andric } // namespace profileCollectorService
41168d75effSDimitry Andric } // namespace __xray
412