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