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
post(BufferQueue * Q,FunctionCallTrie && T,FunctionCallTrie::Allocators && A,FunctionCallTrie::Allocators::Buffers && B,tid_t TId)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
populateRecords(ProfileRecordArray & PRs,ProfileRecord::PathAllocator & PA,const FunctionCallTrie & Trie)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
serializeRecords(ProfileBuffer * Buffer,const BlockHeader & Header,const ProfileRecordArray & ProfileRecords)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
serialize()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
reset()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
nextBuffer(XRayBuffer B)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