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