xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/xray/xray_fdr_logging.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
168d75effSDimitry Andric //===-- xray_fdr_logging.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 // Here we implement the Flight Data Recorder mode for XRay, where we use
1268d75effSDimitry Andric // compact structures to store records in memory as well as when writing out the
1368d75effSDimitry Andric // data to files.
1468d75effSDimitry Andric //
1568d75effSDimitry Andric //===----------------------------------------------------------------------===//
1668d75effSDimitry Andric #include "xray_fdr_logging.h"
1768d75effSDimitry Andric #include <cassert>
1806c3fb27SDimitry Andric #include <cstddef>
1968d75effSDimitry Andric #include <errno.h>
2068d75effSDimitry Andric #include <limits>
2168d75effSDimitry Andric #include <memory>
2268d75effSDimitry Andric #include <pthread.h>
2368d75effSDimitry Andric #include <sys/time.h>
2468d75effSDimitry Andric #include <time.h>
2568d75effSDimitry Andric #include <unistd.h>
2668d75effSDimitry Andric 
2768d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator_internal.h"
2868d75effSDimitry Andric #include "sanitizer_common/sanitizer_atomic.h"
2968d75effSDimitry Andric #include "sanitizer_common/sanitizer_common.h"
3068d75effSDimitry Andric #include "xray/xray_interface.h"
3168d75effSDimitry Andric #include "xray/xray_records.h"
3268d75effSDimitry Andric #include "xray_allocator.h"
3368d75effSDimitry Andric #include "xray_buffer_queue.h"
3468d75effSDimitry Andric #include "xray_defs.h"
3568d75effSDimitry Andric #include "xray_fdr_controller.h"
3668d75effSDimitry Andric #include "xray_fdr_flags.h"
3768d75effSDimitry Andric #include "xray_fdr_log_writer.h"
3868d75effSDimitry Andric #include "xray_flags.h"
3968d75effSDimitry Andric #include "xray_recursion_guard.h"
4068d75effSDimitry Andric #include "xray_tsc.h"
4168d75effSDimitry Andric #include "xray_utils.h"
4268d75effSDimitry Andric 
4368d75effSDimitry Andric namespace __xray {
4468d75effSDimitry Andric 
4568d75effSDimitry Andric static atomic_sint32_t LoggingStatus = {
4668d75effSDimitry Andric     XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};
4768d75effSDimitry Andric 
4868d75effSDimitry Andric namespace {
4968d75effSDimitry Andric 
5068d75effSDimitry Andric // Group together thread-local-data in a struct, then hide it behind a function
5168d75effSDimitry Andric // call so that it can be initialized on first use instead of as a global. We
5268d75effSDimitry Andric // force the alignment to 64-bytes for x86 cache line alignment, as this
5368d75effSDimitry Andric // structure is used in the hot path of implementation.
5468d75effSDimitry Andric struct XRAY_TLS_ALIGNAS(64) ThreadLocalData {
5568d75effSDimitry Andric   BufferQueue::Buffer Buffer{};
5668d75effSDimitry Andric   BufferQueue *BQ = nullptr;
5768d75effSDimitry Andric 
58*0fca6ea1SDimitry Andric   using LogWriterStorage = std::byte[sizeof(FDRLogWriter)];
59*0fca6ea1SDimitry Andric   alignas(FDRLogWriter) LogWriterStorage LWStorage;
6068d75effSDimitry Andric   FDRLogWriter *Writer = nullptr;
6168d75effSDimitry Andric 
62*0fca6ea1SDimitry Andric   using ControllerStorage = std::byte[sizeof(FDRController<>)];
63*0fca6ea1SDimitry Andric   alignas(FDRController<>) ControllerStorage CStorage;
6468d75effSDimitry Andric   FDRController<> *Controller = nullptr;
6568d75effSDimitry Andric };
6668d75effSDimitry Andric 
6768d75effSDimitry Andric } // namespace
6868d75effSDimitry Andric 
6968d75effSDimitry Andric static_assert(std::is_trivially_destructible<ThreadLocalData>::value,
7068d75effSDimitry Andric               "ThreadLocalData must be trivially destructible");
7168d75effSDimitry Andric 
7268d75effSDimitry Andric // Use a global pthread key to identify thread-local data for logging.
7368d75effSDimitry Andric static pthread_key_t Key;
7468d75effSDimitry Andric 
7568d75effSDimitry Andric // Global BufferQueue.
76*0fca6ea1SDimitry Andric static std::byte BufferQueueStorage[sizeof(BufferQueue)];
7768d75effSDimitry Andric static BufferQueue *BQ = nullptr;
7868d75effSDimitry Andric 
7968d75effSDimitry Andric // Global thresholds for function durations.
8068d75effSDimitry Andric static atomic_uint64_t ThresholdTicks{0};
8168d75effSDimitry Andric 
8268d75effSDimitry Andric // Global for ticks per second.
8368d75effSDimitry Andric static atomic_uint64_t TicksPerSec{0};
8468d75effSDimitry Andric 
8568d75effSDimitry Andric static atomic_sint32_t LogFlushStatus = {
8668d75effSDimitry Andric     XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
8768d75effSDimitry Andric 
8868d75effSDimitry Andric // This function will initialize the thread-local data structure used by the FDR
8968d75effSDimitry Andric // logging implementation and return a reference to it. The implementation
9068d75effSDimitry Andric // details require a bit of care to maintain.
9168d75effSDimitry Andric //
9268d75effSDimitry Andric // First, some requirements on the implementation in general:
9368d75effSDimitry Andric //
9468d75effSDimitry Andric //   - XRay handlers should not call any memory allocation routines that may
9568d75effSDimitry Andric //     delegate to an instrumented implementation. This means functions like
9668d75effSDimitry Andric //     malloc() and free() should not be called while instrumenting.
9768d75effSDimitry Andric //
9868d75effSDimitry Andric //   - We would like to use some thread-local data initialized on first-use of
9968d75effSDimitry Andric //     the XRay instrumentation. These allow us to implement unsynchronized
10068d75effSDimitry Andric //     routines that access resources associated with the thread.
10168d75effSDimitry Andric //
10268d75effSDimitry Andric // The implementation here uses a few mechanisms that allow us to provide both
10368d75effSDimitry Andric // the requirements listed above. We do this by:
10468d75effSDimitry Andric //
10568d75effSDimitry Andric //   1. Using a thread-local aligned storage buffer for representing the
10668d75effSDimitry Andric //      ThreadLocalData struct. This data will be uninitialized memory by
10768d75effSDimitry Andric //      design.
10868d75effSDimitry Andric //
10968d75effSDimitry Andric //   2. Not requiring a thread exit handler/implementation, keeping the
11068d75effSDimitry Andric //      thread-local as purely a collection of references/data that do not
11168d75effSDimitry Andric //      require cleanup.
11268d75effSDimitry Andric //
11368d75effSDimitry Andric // We're doing this to avoid using a `thread_local` object that has a
11468d75effSDimitry Andric // non-trivial destructor, because the C++ runtime might call std::malloc(...)
11568d75effSDimitry Andric // to register calls to destructors. Deadlocks may arise when, for example, an
11668d75effSDimitry Andric // externally provided malloc implementation is XRay instrumented, and
11768d75effSDimitry Andric // initializing the thread-locals involves calling into malloc. A malloc
11868d75effSDimitry Andric // implementation that does global synchronization might be holding a lock for a
11968d75effSDimitry Andric // critical section, calling a function that might be XRay instrumented (and
12068d75effSDimitry Andric // thus in turn calling into malloc by virtue of registration of the
12168d75effSDimitry Andric // thread_local's destructor).
12268d75effSDimitry Andric #if XRAY_HAS_TLS_ALIGNAS
12368d75effSDimitry Andric static_assert(alignof(ThreadLocalData) >= 64,
12468d75effSDimitry Andric               "ThreadLocalData must be cache line aligned.");
12568d75effSDimitry Andric #endif
12668d75effSDimitry Andric static ThreadLocalData &getThreadLocalData() {
127*0fca6ea1SDimitry Andric   alignas(ThreadLocalData) thread_local std::byte
128*0fca6ea1SDimitry Andric       TLDStorage[sizeof(ThreadLocalData)];
12968d75effSDimitry Andric 
13068d75effSDimitry Andric   if (pthread_getspecific(Key) == NULL) {
13168d75effSDimitry Andric     new (reinterpret_cast<ThreadLocalData *>(&TLDStorage)) ThreadLocalData{};
13268d75effSDimitry Andric     pthread_setspecific(Key, &TLDStorage);
13368d75effSDimitry Andric   }
13468d75effSDimitry Andric 
13568d75effSDimitry Andric   return *reinterpret_cast<ThreadLocalData *>(&TLDStorage);
13668d75effSDimitry Andric }
13768d75effSDimitry Andric 
13868d75effSDimitry Andric static XRayFileHeader &fdrCommonHeaderInfo() {
13906c3fb27SDimitry Andric   alignas(XRayFileHeader) static std::byte HStorage[sizeof(XRayFileHeader)];
14068d75effSDimitry Andric   static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
14168d75effSDimitry Andric   static bool TSCSupported = true;
14268d75effSDimitry Andric   static uint64_t CycleFrequency = NanosecondsPerSecond;
14368d75effSDimitry Andric   pthread_once(
14468d75effSDimitry Andric       &OnceInit, +[] {
14568d75effSDimitry Andric         XRayFileHeader &H = reinterpret_cast<XRayFileHeader &>(HStorage);
14668d75effSDimitry Andric         // Version 2 of the log writes the extents of the buffer, instead of
14768d75effSDimitry Andric         // relying on an end-of-buffer record.
14868d75effSDimitry Andric         // Version 3 includes PID metadata record.
14968d75effSDimitry Andric         // Version 4 includes CPU data in the custom event records.
15068d75effSDimitry Andric         // Version 5 uses relative deltas for custom and typed event records,
15168d75effSDimitry Andric         // and removes the CPU data in custom event records (similar to how
15268d75effSDimitry Andric         // function records use deltas instead of full TSCs and rely on other
15368d75effSDimitry Andric         // metadata records for TSC wraparound and CPU migration).
15468d75effSDimitry Andric         H.Version = 5;
15568d75effSDimitry Andric         H.Type = FileTypes::FDR_LOG;
15668d75effSDimitry Andric 
15768d75effSDimitry Andric         // Test for required CPU features and cache the cycle frequency
15868d75effSDimitry Andric         TSCSupported = probeRequiredCPUFeatures();
15968d75effSDimitry Andric         if (TSCSupported)
16068d75effSDimitry Andric           CycleFrequency = getTSCFrequency();
16168d75effSDimitry Andric         H.CycleFrequency = CycleFrequency;
16268d75effSDimitry Andric 
16368d75effSDimitry Andric         // FIXME: Actually check whether we have 'constant_tsc' and
16468d75effSDimitry Andric         // 'nonstop_tsc' before setting the values in the header.
16568d75effSDimitry Andric         H.ConstantTSC = 1;
16668d75effSDimitry Andric         H.NonstopTSC = 1;
16768d75effSDimitry Andric       });
16868d75effSDimitry Andric   return reinterpret_cast<XRayFileHeader &>(HStorage);
16968d75effSDimitry Andric }
17068d75effSDimitry Andric 
17168d75effSDimitry Andric // This is the iterator implementation, which knows how to handle FDR-mode
17268d75effSDimitry Andric // specific buffers. This is used as an implementation of the iterator function
17368d75effSDimitry Andric // needed by __xray_set_buffer_iterator(...). It maintains a global state of the
17468d75effSDimitry Andric // buffer iteration for the currently installed FDR mode buffers. In particular:
17568d75effSDimitry Andric //
17668d75effSDimitry Andric //   - If the argument represents the initial state of XRayBuffer ({nullptr, 0})
17768d75effSDimitry Andric //     then the iterator returns the header information.
17868d75effSDimitry Andric //   - If the argument represents the header information ({address of header
17968d75effSDimitry Andric //     info, size of the header info}) then it returns the first FDR buffer's
18068d75effSDimitry Andric //     address and extents.
18168d75effSDimitry Andric //   - It will keep returning the next buffer and extents as there are more
18268d75effSDimitry Andric //     buffers to process. When the input represents the last buffer, it will
18368d75effSDimitry Andric //     return the initial state to signal completion ({nullptr, 0}).
18468d75effSDimitry Andric //
18568d75effSDimitry Andric // See xray/xray_log_interface.h for more details on the requirements for the
18668d75effSDimitry Andric // implementations of __xray_set_buffer_iterator(...) and
18768d75effSDimitry Andric // __xray_log_process_buffers(...).
18868d75effSDimitry Andric XRayBuffer fdrIterator(const XRayBuffer B) {
18968d75effSDimitry Andric   DCHECK(internal_strcmp(__xray_log_get_current_mode(), "xray-fdr") == 0);
19068d75effSDimitry Andric   DCHECK(BQ->finalizing());
19168d75effSDimitry Andric 
19268d75effSDimitry Andric   if (BQ == nullptr || !BQ->finalizing()) {
19368d75effSDimitry Andric     if (Verbosity())
19468d75effSDimitry Andric       Report(
19568d75effSDimitry Andric           "XRay FDR: Failed global buffer queue is null or not finalizing!\n");
19668d75effSDimitry Andric     return {nullptr, 0};
19768d75effSDimitry Andric   }
19868d75effSDimitry Andric 
19968d75effSDimitry Andric   // We use a global scratch-pad for the header information, which only gets
20068d75effSDimitry Andric   // initialized the first time this function is called. We'll update one part
20168d75effSDimitry Andric   // of this information with some relevant data (in particular the number of
20268d75effSDimitry Andric   // buffers to expect).
20306c3fb27SDimitry Andric   alignas(
20406c3fb27SDimitry Andric       XRayFileHeader) static std::byte HeaderStorage[sizeof(XRayFileHeader)];
20568d75effSDimitry Andric   static pthread_once_t HeaderOnce = PTHREAD_ONCE_INIT;
20668d75effSDimitry Andric   pthread_once(
20768d75effSDimitry Andric       &HeaderOnce, +[] {
20868d75effSDimitry Andric         reinterpret_cast<XRayFileHeader &>(HeaderStorage) =
20968d75effSDimitry Andric             fdrCommonHeaderInfo();
21068d75effSDimitry Andric       });
21168d75effSDimitry Andric 
21268d75effSDimitry Andric   // We use a convenience alias for code referring to Header from here on out.
21368d75effSDimitry Andric   auto &Header = reinterpret_cast<XRayFileHeader &>(HeaderStorage);
21468d75effSDimitry Andric   if (B.Data == nullptr && B.Size == 0) {
21568d75effSDimitry Andric     Header.FdrData = FdrAdditionalHeaderData{BQ->ConfiguredBufferSize()};
21668d75effSDimitry Andric     return XRayBuffer{static_cast<void *>(&Header), sizeof(Header)};
21768d75effSDimitry Andric   }
21868d75effSDimitry Andric 
21968d75effSDimitry Andric   static BufferQueue::const_iterator It{};
22068d75effSDimitry Andric   static BufferQueue::const_iterator End{};
22168d75effSDimitry Andric   static uint8_t *CurrentBuffer{nullptr};
22268d75effSDimitry Andric   static size_t SerializedBufferSize = 0;
22368d75effSDimitry Andric   if (B.Data == static_cast<void *>(&Header) && B.Size == sizeof(Header)) {
22468d75effSDimitry Andric     // From this point on, we provide raw access to the raw buffer we're getting
22568d75effSDimitry Andric     // from the BufferQueue. We're relying on the iterators from the current
22668d75effSDimitry Andric     // Buffer queue.
22768d75effSDimitry Andric     It = BQ->cbegin();
22868d75effSDimitry Andric     End = BQ->cend();
22968d75effSDimitry Andric   }
23068d75effSDimitry Andric 
23168d75effSDimitry Andric   if (CurrentBuffer != nullptr) {
23268d75effSDimitry Andric     deallocateBuffer(CurrentBuffer, SerializedBufferSize);
23368d75effSDimitry Andric     CurrentBuffer = nullptr;
23468d75effSDimitry Andric   }
23568d75effSDimitry Andric 
23668d75effSDimitry Andric   if (It == End)
23768d75effSDimitry Andric     return {nullptr, 0};
23868d75effSDimitry Andric 
23968d75effSDimitry Andric   // Set up the current buffer to contain the extents like we would when writing
24068d75effSDimitry Andric   // out to disk. The difference here would be that we still write "empty"
24168d75effSDimitry Andric   // buffers, or at least go through the iterators faithfully to let the
24268d75effSDimitry Andric   // handlers see the empty buffers in the queue.
24368d75effSDimitry Andric   //
24468d75effSDimitry Andric   // We need this atomic fence here to ensure that writes happening to the
24568d75effSDimitry Andric   // buffer have been committed before we load the extents atomically. Because
24668d75effSDimitry Andric   // the buffer is not explicitly synchronised across threads, we rely on the
24768d75effSDimitry Andric   // fence ordering to ensure that writes we expect to have been completed
24868d75effSDimitry Andric   // before the fence are fully committed before we read the extents.
24968d75effSDimitry Andric   atomic_thread_fence(memory_order_acquire);
25068d75effSDimitry Andric   auto BufferSize = atomic_load(It->Extents, memory_order_acquire);
25168d75effSDimitry Andric   SerializedBufferSize = BufferSize + sizeof(MetadataRecord);
25268d75effSDimitry Andric   CurrentBuffer = allocateBuffer(SerializedBufferSize);
25368d75effSDimitry Andric   if (CurrentBuffer == nullptr)
25468d75effSDimitry Andric     return {nullptr, 0};
25568d75effSDimitry Andric 
25668d75effSDimitry Andric   // Write out the extents as a Metadata Record into the CurrentBuffer.
25768d75effSDimitry Andric   MetadataRecord ExtentsRecord;
25868d75effSDimitry Andric   ExtentsRecord.Type = uint8_t(RecordType::Metadata);
25968d75effSDimitry Andric   ExtentsRecord.RecordKind =
26068d75effSDimitry Andric       uint8_t(MetadataRecord::RecordKinds::BufferExtents);
26168d75effSDimitry Andric   internal_memcpy(ExtentsRecord.Data, &BufferSize, sizeof(BufferSize));
26268d75effSDimitry Andric   auto AfterExtents =
26368d75effSDimitry Andric       static_cast<char *>(internal_memcpy(CurrentBuffer, &ExtentsRecord,
26468d75effSDimitry Andric                                           sizeof(MetadataRecord))) +
26568d75effSDimitry Andric       sizeof(MetadataRecord);
26668d75effSDimitry Andric   internal_memcpy(AfterExtents, It->Data, BufferSize);
26768d75effSDimitry Andric 
26868d75effSDimitry Andric   XRayBuffer Result;
26968d75effSDimitry Andric   Result.Data = CurrentBuffer;
27068d75effSDimitry Andric   Result.Size = SerializedBufferSize;
27168d75effSDimitry Andric   ++It;
27268d75effSDimitry Andric   return Result;
27368d75effSDimitry Andric }
27468d75effSDimitry Andric 
27568d75effSDimitry Andric // Must finalize before flushing.
27668d75effSDimitry Andric XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
27768d75effSDimitry Andric   if (atomic_load(&LoggingStatus, memory_order_acquire) !=
27868d75effSDimitry Andric       XRayLogInitStatus::XRAY_LOG_FINALIZED) {
27968d75effSDimitry Andric     if (Verbosity())
28068d75effSDimitry Andric       Report("Not flushing log, implementation is not finalized.\n");
28168d75effSDimitry Andric     return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
28268d75effSDimitry Andric   }
28368d75effSDimitry Andric 
284fe6060f1SDimitry Andric   if (atomic_exchange(&LogFlushStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHING,
285fe6060f1SDimitry Andric                       memory_order_release) ==
286fe6060f1SDimitry Andric       XRayLogFlushStatus::XRAY_LOG_FLUSHING) {
28768d75effSDimitry Andric     if (Verbosity())
288fe6060f1SDimitry Andric       Report("Not flushing log, implementation is still flushing.\n");
289fe6060f1SDimitry Andric     return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
29068d75effSDimitry Andric   }
29168d75effSDimitry Andric 
29268d75effSDimitry Andric   if (BQ == nullptr) {
29368d75effSDimitry Andric     if (Verbosity())
29468d75effSDimitry Andric       Report("Cannot flush when global buffer queue is null.\n");
29568d75effSDimitry Andric     return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
29668d75effSDimitry Andric   }
29768d75effSDimitry Andric 
29868d75effSDimitry Andric   // We wait a number of milliseconds to allow threads to see that we've
29968d75effSDimitry Andric   // finalised before attempting to flush the log.
30068d75effSDimitry Andric   SleepForMillis(fdrFlags()->grace_period_ms);
30168d75effSDimitry Andric 
30268d75effSDimitry Andric   // At this point, we're going to uninstall the iterator implementation, before
30368d75effSDimitry Andric   // we decide to do anything further with the global buffer queue.
30468d75effSDimitry Andric   __xray_log_remove_buffer_iterator();
30568d75effSDimitry Andric 
30668d75effSDimitry Andric   // Once flushed, we should set the global status of the logging implementation
30768d75effSDimitry Andric   // to "uninitialized" to allow for FDR-logging multiple runs.
30868d75effSDimitry Andric   auto ResetToUnitialized = at_scope_exit([] {
30968d75effSDimitry Andric     atomic_store(&LoggingStatus, XRayLogInitStatus::XRAY_LOG_UNINITIALIZED,
31068d75effSDimitry Andric                  memory_order_release);
31168d75effSDimitry Andric   });
31268d75effSDimitry Andric 
31368d75effSDimitry Andric   auto CleanupBuffers = at_scope_exit([] {
31468d75effSDimitry Andric     auto &TLD = getThreadLocalData();
31568d75effSDimitry Andric     if (TLD.Controller != nullptr)
31668d75effSDimitry Andric       TLD.Controller->flush();
31768d75effSDimitry Andric   });
31868d75effSDimitry Andric 
31968d75effSDimitry Andric   if (fdrFlags()->no_file_flush) {
32068d75effSDimitry Andric     if (Verbosity())
32168d75effSDimitry Andric       Report("XRay FDR: Not flushing to file, 'no_file_flush=true'.\n");
32268d75effSDimitry Andric 
32368d75effSDimitry Andric     atomic_store(&LogFlushStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED,
32468d75effSDimitry Andric                  memory_order_release);
32568d75effSDimitry Andric     return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
32668d75effSDimitry Andric   }
32768d75effSDimitry Andric 
32868d75effSDimitry Andric   // We write out the file in the following format:
32968d75effSDimitry Andric   //
33068d75effSDimitry Andric   //   1) We write down the XRay file header with version 1, type FDR_LOG.
33168d75effSDimitry Andric   //   2) Then we use the 'apply' member of the BufferQueue that's live, to
33268d75effSDimitry Andric   //      ensure that at this point in time we write down the buffers that have
33368d75effSDimitry Andric   //      been released (and marked "used") -- we dump the full buffer for now
33468d75effSDimitry Andric   //      (fixed-sized) and let the tools reading the buffers deal with the data
33568d75effSDimitry Andric   //      afterwards.
33668d75effSDimitry Andric   //
33768d75effSDimitry Andric   LogWriter *LW = LogWriter::Open();
33868d75effSDimitry Andric   if (LW == nullptr) {
33968d75effSDimitry Andric     auto Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
34068d75effSDimitry Andric     atomic_store(&LogFlushStatus, Result, memory_order_release);
34168d75effSDimitry Andric     return Result;
34268d75effSDimitry Andric   }
34368d75effSDimitry Andric 
34468d75effSDimitry Andric   XRayFileHeader Header = fdrCommonHeaderInfo();
34568d75effSDimitry Andric   Header.FdrData = FdrAdditionalHeaderData{BQ->ConfiguredBufferSize()};
34668d75effSDimitry Andric   LW->WriteAll(reinterpret_cast<char *>(&Header),
34768d75effSDimitry Andric                reinterpret_cast<char *>(&Header) + sizeof(Header));
34868d75effSDimitry Andric 
34968d75effSDimitry Andric   // Release the current thread's buffer before we attempt to write out all the
35068d75effSDimitry Andric   // buffers. This ensures that in case we had only a single thread going, that
35168d75effSDimitry Andric   // we are able to capture the data nonetheless.
35268d75effSDimitry Andric   auto &TLD = getThreadLocalData();
35368d75effSDimitry Andric   if (TLD.Controller != nullptr)
35468d75effSDimitry Andric     TLD.Controller->flush();
35568d75effSDimitry Andric 
35668d75effSDimitry Andric   BQ->apply([&](const BufferQueue::Buffer &B) {
35768d75effSDimitry Andric     // Starting at version 2 of the FDR logging implementation, we only write
35868d75effSDimitry Andric     // the records identified by the extents of the buffer. We use the Extents
35968d75effSDimitry Andric     // from the Buffer and write that out as the first record in the buffer.  We
36068d75effSDimitry Andric     // still use a Metadata record, but fill in the extents instead for the
36168d75effSDimitry Andric     // data.
36268d75effSDimitry Andric     MetadataRecord ExtentsRecord;
36368d75effSDimitry Andric     auto BufferExtents = atomic_load(B.Extents, memory_order_acquire);
36468d75effSDimitry Andric     DCHECK(BufferExtents <= B.Size);
36568d75effSDimitry Andric     ExtentsRecord.Type = uint8_t(RecordType::Metadata);
36668d75effSDimitry Andric     ExtentsRecord.RecordKind =
36768d75effSDimitry Andric         uint8_t(MetadataRecord::RecordKinds::BufferExtents);
36868d75effSDimitry Andric     internal_memcpy(ExtentsRecord.Data, &BufferExtents, sizeof(BufferExtents));
36968d75effSDimitry Andric     if (BufferExtents > 0) {
37068d75effSDimitry Andric       LW->WriteAll(reinterpret_cast<char *>(&ExtentsRecord),
37168d75effSDimitry Andric                    reinterpret_cast<char *>(&ExtentsRecord) +
37268d75effSDimitry Andric                        sizeof(MetadataRecord));
37368d75effSDimitry Andric       LW->WriteAll(reinterpret_cast<char *>(B.Data),
37468d75effSDimitry Andric                    reinterpret_cast<char *>(B.Data) + BufferExtents);
37568d75effSDimitry Andric     }
37668d75effSDimitry Andric   });
37768d75effSDimitry Andric 
37868d75effSDimitry Andric   atomic_store(&LogFlushStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED,
37968d75effSDimitry Andric                memory_order_release);
38068d75effSDimitry Andric   return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
38168d75effSDimitry Andric }
38268d75effSDimitry Andric 
38368d75effSDimitry Andric XRayLogInitStatus fdrLoggingFinalize() XRAY_NEVER_INSTRUMENT {
38468d75effSDimitry Andric   s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED;
38568d75effSDimitry Andric   if (!atomic_compare_exchange_strong(&LoggingStatus, &CurrentStatus,
38668d75effSDimitry Andric                                       XRayLogInitStatus::XRAY_LOG_FINALIZING,
38768d75effSDimitry Andric                                       memory_order_release)) {
38868d75effSDimitry Andric     if (Verbosity())
38968d75effSDimitry Andric       Report("Cannot finalize log, implementation not initialized.\n");
39068d75effSDimitry Andric     return static_cast<XRayLogInitStatus>(CurrentStatus);
39168d75effSDimitry Andric   }
39268d75effSDimitry Andric 
39368d75effSDimitry Andric   // Do special things to make the log finalize itself, and not allow any more
39468d75effSDimitry Andric   // operations to be performed until re-initialized.
39568d75effSDimitry Andric   if (BQ == nullptr) {
39668d75effSDimitry Andric     if (Verbosity())
39768d75effSDimitry Andric       Report("Attempting to finalize an uninitialized global buffer!\n");
39868d75effSDimitry Andric   } else {
39968d75effSDimitry Andric     BQ->finalize();
40068d75effSDimitry Andric   }
40168d75effSDimitry Andric 
40268d75effSDimitry Andric   atomic_store(&LoggingStatus, XRayLogInitStatus::XRAY_LOG_FINALIZED,
40368d75effSDimitry Andric                memory_order_release);
40468d75effSDimitry Andric   return XRayLogInitStatus::XRAY_LOG_FINALIZED;
40568d75effSDimitry Andric }
40668d75effSDimitry Andric 
40768d75effSDimitry Andric struct TSCAndCPU {
40868d75effSDimitry Andric   uint64_t TSC = 0;
40968d75effSDimitry Andric   unsigned char CPU = 0;
41068d75effSDimitry Andric };
41168d75effSDimitry Andric 
41268d75effSDimitry Andric static TSCAndCPU getTimestamp() XRAY_NEVER_INSTRUMENT {
41368d75effSDimitry Andric   // We want to get the TSC as early as possible, so that we can check whether
41468d75effSDimitry Andric   // we've seen this CPU before. We also do it before we load anything else,
41568d75effSDimitry Andric   // to allow for forward progress with the scheduling.
41668d75effSDimitry Andric   TSCAndCPU Result;
41768d75effSDimitry Andric 
41868d75effSDimitry Andric   // Test once for required CPU features
41968d75effSDimitry Andric   static pthread_once_t OnceProbe = PTHREAD_ONCE_INIT;
42068d75effSDimitry Andric   static bool TSCSupported = true;
42168d75effSDimitry Andric   pthread_once(
42268d75effSDimitry Andric       &OnceProbe, +[] { TSCSupported = probeRequiredCPUFeatures(); });
42368d75effSDimitry Andric 
42468d75effSDimitry Andric   if (TSCSupported) {
42568d75effSDimitry Andric     Result.TSC = __xray::readTSC(Result.CPU);
42668d75effSDimitry Andric   } else {
42768d75effSDimitry Andric     // FIXME: This code needs refactoring as it appears in multiple locations
42868d75effSDimitry Andric     timespec TS;
42968d75effSDimitry Andric     int result = clock_gettime(CLOCK_REALTIME, &TS);
43068d75effSDimitry Andric     if (result != 0) {
43168d75effSDimitry Andric       Report("clock_gettime(2) return %d, errno=%d", result, int(errno));
43268d75effSDimitry Andric       TS = {0, 0};
43368d75effSDimitry Andric     }
43468d75effSDimitry Andric     Result.CPU = 0;
43568d75effSDimitry Andric     Result.TSC = TS.tv_sec * __xray::NanosecondsPerSecond + TS.tv_nsec;
43668d75effSDimitry Andric   }
43768d75effSDimitry Andric   return Result;
43868d75effSDimitry Andric }
43968d75effSDimitry Andric 
44068d75effSDimitry Andric thread_local atomic_uint8_t Running{0};
44168d75effSDimitry Andric 
44268d75effSDimitry Andric static bool setupTLD(ThreadLocalData &TLD) XRAY_NEVER_INSTRUMENT {
44368d75effSDimitry Andric   // Check if we're finalizing, before proceeding.
44468d75effSDimitry Andric   {
44568d75effSDimitry Andric     auto Status = atomic_load(&LoggingStatus, memory_order_acquire);
44668d75effSDimitry Andric     if (Status == XRayLogInitStatus::XRAY_LOG_FINALIZING ||
44768d75effSDimitry Andric         Status == XRayLogInitStatus::XRAY_LOG_FINALIZED) {
44868d75effSDimitry Andric       if (TLD.Controller != nullptr) {
44968d75effSDimitry Andric         TLD.Controller->flush();
45068d75effSDimitry Andric         TLD.Controller = nullptr;
45168d75effSDimitry Andric       }
45268d75effSDimitry Andric       return false;
45368d75effSDimitry Andric     }
45468d75effSDimitry Andric   }
45568d75effSDimitry Andric 
45668d75effSDimitry Andric   if (UNLIKELY(TLD.Controller == nullptr)) {
45768d75effSDimitry Andric     // Set up the TLD buffer queue.
45868d75effSDimitry Andric     if (UNLIKELY(BQ == nullptr))
45968d75effSDimitry Andric       return false;
46068d75effSDimitry Andric     TLD.BQ = BQ;
46168d75effSDimitry Andric 
46268d75effSDimitry Andric     // Check that we have a valid buffer.
46368d75effSDimitry Andric     if (TLD.Buffer.Generation != BQ->generation() &&
46468d75effSDimitry Andric         TLD.BQ->releaseBuffer(TLD.Buffer) != BufferQueue::ErrorCode::Ok)
46568d75effSDimitry Andric       return false;
46668d75effSDimitry Andric 
46768d75effSDimitry Andric     // Set up a buffer, before setting up the log writer. Bail out on failure.
46868d75effSDimitry Andric     if (TLD.BQ->getBuffer(TLD.Buffer) != BufferQueue::ErrorCode::Ok)
46968d75effSDimitry Andric       return false;
47068d75effSDimitry Andric 
47168d75effSDimitry Andric     // Set up the Log Writer for this thread.
47268d75effSDimitry Andric     if (UNLIKELY(TLD.Writer == nullptr)) {
47368d75effSDimitry Andric       auto *LWStorage = reinterpret_cast<FDRLogWriter *>(&TLD.LWStorage);
47468d75effSDimitry Andric       new (LWStorage) FDRLogWriter(TLD.Buffer);
47568d75effSDimitry Andric       TLD.Writer = LWStorage;
47668d75effSDimitry Andric     } else {
47768d75effSDimitry Andric       TLD.Writer->resetRecord();
47868d75effSDimitry Andric     }
47968d75effSDimitry Andric 
48068d75effSDimitry Andric     auto *CStorage = reinterpret_cast<FDRController<> *>(&TLD.CStorage);
48168d75effSDimitry Andric     new (CStorage)
48268d75effSDimitry Andric         FDRController<>(TLD.BQ, TLD.Buffer, *TLD.Writer, clock_gettime,
48368d75effSDimitry Andric                         atomic_load_relaxed(&ThresholdTicks));
48468d75effSDimitry Andric     TLD.Controller = CStorage;
48568d75effSDimitry Andric   }
48668d75effSDimitry Andric 
48768d75effSDimitry Andric   DCHECK_NE(TLD.Controller, nullptr);
48868d75effSDimitry Andric   return true;
48968d75effSDimitry Andric }
49068d75effSDimitry Andric 
49168d75effSDimitry Andric void fdrLoggingHandleArg0(int32_t FuncId,
49268d75effSDimitry Andric                           XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {
49368d75effSDimitry Andric   auto TC = getTimestamp();
49468d75effSDimitry Andric   auto &TSC = TC.TSC;
49568d75effSDimitry Andric   auto &CPU = TC.CPU;
49668d75effSDimitry Andric   RecursionGuard Guard{Running};
49768d75effSDimitry Andric   if (!Guard)
49868d75effSDimitry Andric     return;
49968d75effSDimitry Andric 
50068d75effSDimitry Andric   auto &TLD = getThreadLocalData();
50168d75effSDimitry Andric   if (!setupTLD(TLD))
50268d75effSDimitry Andric     return;
50368d75effSDimitry Andric 
50468d75effSDimitry Andric   switch (Entry) {
50568d75effSDimitry Andric   case XRayEntryType::ENTRY:
50668d75effSDimitry Andric   case XRayEntryType::LOG_ARGS_ENTRY:
50768d75effSDimitry Andric     TLD.Controller->functionEnter(FuncId, TSC, CPU);
50868d75effSDimitry Andric     return;
50968d75effSDimitry Andric   case XRayEntryType::EXIT:
51068d75effSDimitry Andric     TLD.Controller->functionExit(FuncId, TSC, CPU);
51168d75effSDimitry Andric     return;
51268d75effSDimitry Andric   case XRayEntryType::TAIL:
51368d75effSDimitry Andric     TLD.Controller->functionTailExit(FuncId, TSC, CPU);
51468d75effSDimitry Andric     return;
51568d75effSDimitry Andric   case XRayEntryType::CUSTOM_EVENT:
51668d75effSDimitry Andric   case XRayEntryType::TYPED_EVENT:
51768d75effSDimitry Andric     break;
51868d75effSDimitry Andric   }
51968d75effSDimitry Andric }
52068d75effSDimitry Andric 
52168d75effSDimitry Andric void fdrLoggingHandleArg1(int32_t FuncId, XRayEntryType Entry,
52268d75effSDimitry Andric                           uint64_t Arg) XRAY_NEVER_INSTRUMENT {
52368d75effSDimitry Andric   auto TC = getTimestamp();
52468d75effSDimitry Andric   auto &TSC = TC.TSC;
52568d75effSDimitry Andric   auto &CPU = TC.CPU;
52668d75effSDimitry Andric   RecursionGuard Guard{Running};
52768d75effSDimitry Andric   if (!Guard)
52868d75effSDimitry Andric     return;
52968d75effSDimitry Andric 
53068d75effSDimitry Andric   auto &TLD = getThreadLocalData();
53168d75effSDimitry Andric   if (!setupTLD(TLD))
53268d75effSDimitry Andric     return;
53368d75effSDimitry Andric 
53468d75effSDimitry Andric   switch (Entry) {
53568d75effSDimitry Andric   case XRayEntryType::ENTRY:
53668d75effSDimitry Andric   case XRayEntryType::LOG_ARGS_ENTRY:
53768d75effSDimitry Andric     TLD.Controller->functionEnterArg(FuncId, TSC, CPU, Arg);
53868d75effSDimitry Andric     return;
53968d75effSDimitry Andric   case XRayEntryType::EXIT:
54068d75effSDimitry Andric     TLD.Controller->functionExit(FuncId, TSC, CPU);
54168d75effSDimitry Andric     return;
54268d75effSDimitry Andric   case XRayEntryType::TAIL:
54368d75effSDimitry Andric     TLD.Controller->functionTailExit(FuncId, TSC, CPU);
54468d75effSDimitry Andric     return;
54568d75effSDimitry Andric   case XRayEntryType::CUSTOM_EVENT:
54668d75effSDimitry Andric   case XRayEntryType::TYPED_EVENT:
54768d75effSDimitry Andric     break;
54868d75effSDimitry Andric   }
54968d75effSDimitry Andric }
55068d75effSDimitry Andric 
55168d75effSDimitry Andric void fdrLoggingHandleCustomEvent(void *Event,
55268d75effSDimitry Andric                                  std::size_t EventSize) XRAY_NEVER_INSTRUMENT {
55368d75effSDimitry Andric   auto TC = getTimestamp();
55468d75effSDimitry Andric   auto &TSC = TC.TSC;
55568d75effSDimitry Andric   auto &CPU = TC.CPU;
55668d75effSDimitry Andric   RecursionGuard Guard{Running};
55768d75effSDimitry Andric   if (!Guard)
55868d75effSDimitry Andric     return;
55968d75effSDimitry Andric 
56068d75effSDimitry Andric   // Complain when we ever get at least one custom event that's larger than what
56168d75effSDimitry Andric   // we can possibly support.
56268d75effSDimitry Andric   if (EventSize >
56368d75effSDimitry Andric       static_cast<std::size_t>(std::numeric_limits<int32_t>::max())) {
56468d75effSDimitry Andric     static pthread_once_t Once = PTHREAD_ONCE_INIT;
56568d75effSDimitry Andric     pthread_once(
56668d75effSDimitry Andric         &Once, +[] {
56768d75effSDimitry Andric           Report("Custom event size too large; truncating to %d.\n",
56868d75effSDimitry Andric                  std::numeric_limits<int32_t>::max());
56968d75effSDimitry Andric         });
57068d75effSDimitry Andric   }
57168d75effSDimitry Andric 
57268d75effSDimitry Andric   auto &TLD = getThreadLocalData();
57368d75effSDimitry Andric   if (!setupTLD(TLD))
57468d75effSDimitry Andric     return;
57568d75effSDimitry Andric 
57668d75effSDimitry Andric   int32_t ReducedEventSize = static_cast<int32_t>(EventSize);
57768d75effSDimitry Andric   TLD.Controller->customEvent(TSC, CPU, Event, ReducedEventSize);
57868d75effSDimitry Andric }
57968d75effSDimitry Andric 
58006c3fb27SDimitry Andric void fdrLoggingHandleTypedEvent(size_t EventType, const void *Event,
58106c3fb27SDimitry Andric                                 size_t EventSize) noexcept
58206c3fb27SDimitry Andric     XRAY_NEVER_INSTRUMENT {
58368d75effSDimitry Andric   auto TC = getTimestamp();
58468d75effSDimitry Andric   auto &TSC = TC.TSC;
58568d75effSDimitry Andric   auto &CPU = TC.CPU;
58668d75effSDimitry Andric   RecursionGuard Guard{Running};
58768d75effSDimitry Andric   if (!Guard)
58868d75effSDimitry Andric     return;
58968d75effSDimitry Andric 
59068d75effSDimitry Andric   // Complain when we ever get at least one typed event that's larger than what
59168d75effSDimitry Andric   // we can possibly support.
59268d75effSDimitry Andric   if (EventSize >
59368d75effSDimitry Andric       static_cast<std::size_t>(std::numeric_limits<int32_t>::max())) {
59468d75effSDimitry Andric     static pthread_once_t Once = PTHREAD_ONCE_INIT;
59568d75effSDimitry Andric     pthread_once(
59668d75effSDimitry Andric         &Once, +[] {
59768d75effSDimitry Andric           Report("Typed event size too large; truncating to %d.\n",
59868d75effSDimitry Andric                  std::numeric_limits<int32_t>::max());
59968d75effSDimitry Andric         });
60068d75effSDimitry Andric   }
60168d75effSDimitry Andric 
60268d75effSDimitry Andric   auto &TLD = getThreadLocalData();
60368d75effSDimitry Andric   if (!setupTLD(TLD))
60468d75effSDimitry Andric     return;
60568d75effSDimitry Andric 
60668d75effSDimitry Andric   int32_t ReducedEventSize = static_cast<int32_t>(EventSize);
60706c3fb27SDimitry Andric   TLD.Controller->typedEvent(TSC, CPU, static_cast<uint16_t>(EventType), Event,
60806c3fb27SDimitry Andric                              ReducedEventSize);
60968d75effSDimitry Andric }
61068d75effSDimitry Andric 
61168d75effSDimitry Andric XRayLogInitStatus fdrLoggingInit(size_t, size_t, void *Options,
61268d75effSDimitry Andric                                  size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
61368d75effSDimitry Andric   if (Options == nullptr)
61468d75effSDimitry Andric     return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
61568d75effSDimitry Andric 
61668d75effSDimitry Andric   s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
61768d75effSDimitry Andric   if (!atomic_compare_exchange_strong(&LoggingStatus, &CurrentStatus,
61868d75effSDimitry Andric                                       XRayLogInitStatus::XRAY_LOG_INITIALIZING,
61968d75effSDimitry Andric                                       memory_order_release)) {
62068d75effSDimitry Andric     if (Verbosity())
62168d75effSDimitry Andric       Report("Cannot initialize already initialized implementation.\n");
62268d75effSDimitry Andric     return static_cast<XRayLogInitStatus>(CurrentStatus);
62368d75effSDimitry Andric   }
62468d75effSDimitry Andric 
62568d75effSDimitry Andric   if (Verbosity())
62668d75effSDimitry Andric     Report("Initializing FDR mode with options: %s\n",
62768d75effSDimitry Andric            static_cast<const char *>(Options));
62868d75effSDimitry Andric 
62968d75effSDimitry Andric   // TODO: Factor out the flags specific to the FDR mode implementation. For
63068d75effSDimitry Andric   // now, use the global/single definition of the flags, since the FDR mode
63168d75effSDimitry Andric   // flags are already defined there.
63268d75effSDimitry Andric   FlagParser FDRParser;
63368d75effSDimitry Andric   FDRFlags FDRFlags;
63468d75effSDimitry Andric   registerXRayFDRFlags(&FDRParser, &FDRFlags);
63568d75effSDimitry Andric   FDRFlags.setDefaults();
63668d75effSDimitry Andric 
63768d75effSDimitry Andric   // Override first from the general XRAY_DEFAULT_OPTIONS compiler-provided
63868d75effSDimitry Andric   // options until we migrate everyone to use the XRAY_FDR_OPTIONS
63968d75effSDimitry Andric   // compiler-provided options.
64068d75effSDimitry Andric   FDRParser.ParseString(useCompilerDefinedFlags());
64168d75effSDimitry Andric   FDRParser.ParseString(useCompilerDefinedFDRFlags());
64268d75effSDimitry Andric   auto *EnvOpts = GetEnv("XRAY_FDR_OPTIONS");
64368d75effSDimitry Andric   if (EnvOpts == nullptr)
64468d75effSDimitry Andric     EnvOpts = "";
64568d75effSDimitry Andric   FDRParser.ParseString(EnvOpts);
64668d75effSDimitry Andric 
64768d75effSDimitry Andric   // FIXME: Remove this when we fully remove the deprecated flags.
64868d75effSDimitry Andric   if (internal_strlen(EnvOpts) == 0) {
64968d75effSDimitry Andric     FDRFlags.func_duration_threshold_us =
65068d75effSDimitry Andric         flags()->xray_fdr_log_func_duration_threshold_us;
65168d75effSDimitry Andric     FDRFlags.grace_period_ms = flags()->xray_fdr_log_grace_period_ms;
65268d75effSDimitry Andric   }
65368d75effSDimitry Andric 
65468d75effSDimitry Andric   // The provided options should always override the compiler-provided and
65568d75effSDimitry Andric   // environment-variable defined options.
65668d75effSDimitry Andric   FDRParser.ParseString(static_cast<const char *>(Options));
65768d75effSDimitry Andric   *fdrFlags() = FDRFlags;
65868d75effSDimitry Andric   auto BufferSize = FDRFlags.buffer_size;
65968d75effSDimitry Andric   auto BufferMax = FDRFlags.buffer_max;
66068d75effSDimitry Andric 
66168d75effSDimitry Andric   if (BQ == nullptr) {
66268d75effSDimitry Andric     bool Success = false;
66368d75effSDimitry Andric     BQ = reinterpret_cast<BufferQueue *>(&BufferQueueStorage);
66468d75effSDimitry Andric     new (BQ) BufferQueue(BufferSize, BufferMax, Success);
66568d75effSDimitry Andric     if (!Success) {
66668d75effSDimitry Andric       Report("BufferQueue init failed.\n");
66768d75effSDimitry Andric       return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
66868d75effSDimitry Andric     }
66968d75effSDimitry Andric   } else {
67068d75effSDimitry Andric     if (BQ->init(BufferSize, BufferMax) != BufferQueue::ErrorCode::Ok) {
67168d75effSDimitry Andric       if (Verbosity())
67268d75effSDimitry Andric         Report("Failed to re-initialize global buffer queue. Init failed.\n");
67368d75effSDimitry Andric       return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
67468d75effSDimitry Andric     }
67568d75effSDimitry Andric   }
67668d75effSDimitry Andric 
67768d75effSDimitry Andric   static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
67868d75effSDimitry Andric   pthread_once(
67968d75effSDimitry Andric       &OnceInit, +[] {
68068d75effSDimitry Andric         atomic_store(&TicksPerSec,
68168d75effSDimitry Andric                      probeRequiredCPUFeatures() ? getTSCFrequency()
68268d75effSDimitry Andric                                                 : __xray::NanosecondsPerSecond,
68368d75effSDimitry Andric                      memory_order_release);
68468d75effSDimitry Andric         pthread_key_create(
68568d75effSDimitry Andric             &Key, +[](void *TLDPtr) {
68668d75effSDimitry Andric               if (TLDPtr == nullptr)
68768d75effSDimitry Andric                 return;
68868d75effSDimitry Andric               auto &TLD = *reinterpret_cast<ThreadLocalData *>(TLDPtr);
68968d75effSDimitry Andric               if (TLD.BQ == nullptr)
69068d75effSDimitry Andric                 return;
69168d75effSDimitry Andric               if (TLD.Buffer.Data == nullptr)
69268d75effSDimitry Andric                 return;
69368d75effSDimitry Andric               auto EC = TLD.BQ->releaseBuffer(TLD.Buffer);
69468d75effSDimitry Andric               if (EC != BufferQueue::ErrorCode::Ok)
69568d75effSDimitry Andric                 Report("At thread exit, failed to release buffer at %p; "
69668d75effSDimitry Andric                        "error=%s\n",
69768d75effSDimitry Andric                        TLD.Buffer.Data, BufferQueue::getErrorString(EC));
69868d75effSDimitry Andric             });
69968d75effSDimitry Andric       });
70068d75effSDimitry Andric 
70168d75effSDimitry Andric   atomic_store(&ThresholdTicks,
70268d75effSDimitry Andric                atomic_load_relaxed(&TicksPerSec) *
70368d75effSDimitry Andric                    fdrFlags()->func_duration_threshold_us / 1000000,
70468d75effSDimitry Andric                memory_order_release);
70568d75effSDimitry Andric   // Arg1 handler should go in first to avoid concurrent code accidentally
70668d75effSDimitry Andric   // falling back to arg0 when it should have ran arg1.
70768d75effSDimitry Andric   __xray_set_handler_arg1(fdrLoggingHandleArg1);
70868d75effSDimitry Andric   // Install the actual handleArg0 handler after initialising the buffers.
70968d75effSDimitry Andric   __xray_set_handler(fdrLoggingHandleArg0);
71068d75effSDimitry Andric   __xray_set_customevent_handler(fdrLoggingHandleCustomEvent);
71168d75effSDimitry Andric   __xray_set_typedevent_handler(fdrLoggingHandleTypedEvent);
71268d75effSDimitry Andric 
71368d75effSDimitry Andric   // Install the buffer iterator implementation.
71468d75effSDimitry Andric   __xray_log_set_buffer_iterator(fdrIterator);
71568d75effSDimitry Andric 
71668d75effSDimitry Andric   atomic_store(&LoggingStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZED,
71768d75effSDimitry Andric                memory_order_release);
71868d75effSDimitry Andric 
71968d75effSDimitry Andric   if (Verbosity())
72068d75effSDimitry Andric     Report("XRay FDR init successful.\n");
72168d75effSDimitry Andric   return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
72268d75effSDimitry Andric }
72368d75effSDimitry Andric 
72468d75effSDimitry Andric bool fdrLogDynamicInitializer() XRAY_NEVER_INSTRUMENT {
72568d75effSDimitry Andric   XRayLogImpl Impl{
72668d75effSDimitry Andric       fdrLoggingInit,
72768d75effSDimitry Andric       fdrLoggingFinalize,
72868d75effSDimitry Andric       fdrLoggingHandleArg0,
72968d75effSDimitry Andric       fdrLoggingFlush,
73068d75effSDimitry Andric   };
73168d75effSDimitry Andric   auto RegistrationResult = __xray_log_register_mode("xray-fdr", Impl);
73268d75effSDimitry Andric   if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK &&
73368d75effSDimitry Andric       Verbosity()) {
73468d75effSDimitry Andric     Report("Cannot register XRay FDR mode to 'xray-fdr'; error = %d\n",
73568d75effSDimitry Andric            RegistrationResult);
73668d75effSDimitry Andric     return false;
73768d75effSDimitry Andric   }
73868d75effSDimitry Andric 
73968d75effSDimitry Andric   if (flags()->xray_fdr_log ||
74068d75effSDimitry Andric       !internal_strcmp(flags()->xray_mode, "xray-fdr")) {
74168d75effSDimitry Andric     auto SelectResult = __xray_log_select_mode("xray-fdr");
74268d75effSDimitry Andric     if (SelectResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK &&
74368d75effSDimitry Andric         Verbosity()) {
74468d75effSDimitry Andric       Report("Cannot select XRay FDR mode as 'xray-fdr'; error = %d\n",
74568d75effSDimitry Andric              SelectResult);
74668d75effSDimitry Andric       return false;
74768d75effSDimitry Andric     }
74868d75effSDimitry Andric   }
74968d75effSDimitry Andric   return true;
75068d75effSDimitry Andric }
75168d75effSDimitry Andric 
75268d75effSDimitry Andric } // namespace __xray
75368d75effSDimitry Andric 
75468d75effSDimitry Andric static auto UNUSED Unused = __xray::fdrLogDynamicInitializer();
755