xref: /openbsd-src/gnu/llvm/compiler-rt/lib/xray/xray_fdr_logging.cpp (revision d89ec533011f513df1010f142a111086a0785f09)
13cab2bb3Spatrick //===-- xray_fdr_logging.cpp -----------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of XRay, a dynamic runtime instrumentation system.
103cab2bb3Spatrick //
113cab2bb3Spatrick // Here we implement the Flight Data Recorder mode for XRay, where we use
123cab2bb3Spatrick // compact structures to store records in memory as well as when writing out the
133cab2bb3Spatrick // data to files.
143cab2bb3Spatrick //
153cab2bb3Spatrick //===----------------------------------------------------------------------===//
163cab2bb3Spatrick #include "xray_fdr_logging.h"
173cab2bb3Spatrick #include <cassert>
183cab2bb3Spatrick #include <errno.h>
193cab2bb3Spatrick #include <limits>
203cab2bb3Spatrick #include <memory>
213cab2bb3Spatrick #include <pthread.h>
223cab2bb3Spatrick #include <sys/time.h>
233cab2bb3Spatrick #include <time.h>
243cab2bb3Spatrick #include <unistd.h>
253cab2bb3Spatrick 
263cab2bb3Spatrick #include "sanitizer_common/sanitizer_allocator_internal.h"
273cab2bb3Spatrick #include "sanitizer_common/sanitizer_atomic.h"
283cab2bb3Spatrick #include "sanitizer_common/sanitizer_common.h"
293cab2bb3Spatrick #include "xray/xray_interface.h"
303cab2bb3Spatrick #include "xray/xray_records.h"
313cab2bb3Spatrick #include "xray_allocator.h"
323cab2bb3Spatrick #include "xray_buffer_queue.h"
333cab2bb3Spatrick #include "xray_defs.h"
343cab2bb3Spatrick #include "xray_fdr_controller.h"
353cab2bb3Spatrick #include "xray_fdr_flags.h"
363cab2bb3Spatrick #include "xray_fdr_log_writer.h"
373cab2bb3Spatrick #include "xray_flags.h"
383cab2bb3Spatrick #include "xray_recursion_guard.h"
393cab2bb3Spatrick #include "xray_tsc.h"
403cab2bb3Spatrick #include "xray_utils.h"
413cab2bb3Spatrick 
423cab2bb3Spatrick namespace __xray {
433cab2bb3Spatrick 
443cab2bb3Spatrick static atomic_sint32_t LoggingStatus = {
453cab2bb3Spatrick     XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};
463cab2bb3Spatrick 
473cab2bb3Spatrick namespace {
483cab2bb3Spatrick 
493cab2bb3Spatrick // Group together thread-local-data in a struct, then hide it behind a function
503cab2bb3Spatrick // call so that it can be initialized on first use instead of as a global. We
513cab2bb3Spatrick // force the alignment to 64-bytes for x86 cache line alignment, as this
523cab2bb3Spatrick // structure is used in the hot path of implementation.
533cab2bb3Spatrick struct XRAY_TLS_ALIGNAS(64) ThreadLocalData {
543cab2bb3Spatrick   BufferQueue::Buffer Buffer{};
553cab2bb3Spatrick   BufferQueue *BQ = nullptr;
563cab2bb3Spatrick 
573cab2bb3Spatrick   using LogWriterStorage =
583cab2bb3Spatrick       typename std::aligned_storage<sizeof(FDRLogWriter),
593cab2bb3Spatrick                                     alignof(FDRLogWriter)>::type;
603cab2bb3Spatrick 
613cab2bb3Spatrick   LogWriterStorage LWStorage;
623cab2bb3Spatrick   FDRLogWriter *Writer = nullptr;
633cab2bb3Spatrick 
643cab2bb3Spatrick   using ControllerStorage =
653cab2bb3Spatrick       typename std::aligned_storage<sizeof(FDRController<>),
663cab2bb3Spatrick                                     alignof(FDRController<>)>::type;
673cab2bb3Spatrick   ControllerStorage CStorage;
683cab2bb3Spatrick   FDRController<> *Controller = nullptr;
693cab2bb3Spatrick };
703cab2bb3Spatrick 
713cab2bb3Spatrick } // namespace
723cab2bb3Spatrick 
733cab2bb3Spatrick static_assert(std::is_trivially_destructible<ThreadLocalData>::value,
743cab2bb3Spatrick               "ThreadLocalData must be trivially destructible");
753cab2bb3Spatrick 
763cab2bb3Spatrick // Use a global pthread key to identify thread-local data for logging.
773cab2bb3Spatrick static pthread_key_t Key;
783cab2bb3Spatrick 
793cab2bb3Spatrick // Global BufferQueue.
803cab2bb3Spatrick static std::aligned_storage<sizeof(BufferQueue)>::type BufferQueueStorage;
813cab2bb3Spatrick static BufferQueue *BQ = nullptr;
823cab2bb3Spatrick 
833cab2bb3Spatrick // Global thresholds for function durations.
843cab2bb3Spatrick static atomic_uint64_t ThresholdTicks{0};
853cab2bb3Spatrick 
863cab2bb3Spatrick // Global for ticks per second.
873cab2bb3Spatrick static atomic_uint64_t TicksPerSec{0};
883cab2bb3Spatrick 
893cab2bb3Spatrick static atomic_sint32_t LogFlushStatus = {
903cab2bb3Spatrick     XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
913cab2bb3Spatrick 
923cab2bb3Spatrick // This function will initialize the thread-local data structure used by the FDR
933cab2bb3Spatrick // logging implementation and return a reference to it. The implementation
943cab2bb3Spatrick // details require a bit of care to maintain.
953cab2bb3Spatrick //
963cab2bb3Spatrick // First, some requirements on the implementation in general:
973cab2bb3Spatrick //
983cab2bb3Spatrick //   - XRay handlers should not call any memory allocation routines that may
993cab2bb3Spatrick //     delegate to an instrumented implementation. This means functions like
1003cab2bb3Spatrick //     malloc() and free() should not be called while instrumenting.
1013cab2bb3Spatrick //
1023cab2bb3Spatrick //   - We would like to use some thread-local data initialized on first-use of
1033cab2bb3Spatrick //     the XRay instrumentation. These allow us to implement unsynchronized
1043cab2bb3Spatrick //     routines that access resources associated with the thread.
1053cab2bb3Spatrick //
1063cab2bb3Spatrick // The implementation here uses a few mechanisms that allow us to provide both
1073cab2bb3Spatrick // the requirements listed above. We do this by:
1083cab2bb3Spatrick //
1093cab2bb3Spatrick //   1. Using a thread-local aligned storage buffer for representing the
1103cab2bb3Spatrick //      ThreadLocalData struct. This data will be uninitialized memory by
1113cab2bb3Spatrick //      design.
1123cab2bb3Spatrick //
1133cab2bb3Spatrick //   2. Not requiring a thread exit handler/implementation, keeping the
1143cab2bb3Spatrick //      thread-local as purely a collection of references/data that do not
1153cab2bb3Spatrick //      require cleanup.
1163cab2bb3Spatrick //
1173cab2bb3Spatrick // We're doing this to avoid using a `thread_local` object that has a
1183cab2bb3Spatrick // non-trivial destructor, because the C++ runtime might call std::malloc(...)
1193cab2bb3Spatrick // to register calls to destructors. Deadlocks may arise when, for example, an
1203cab2bb3Spatrick // externally provided malloc implementation is XRay instrumented, and
1213cab2bb3Spatrick // initializing the thread-locals involves calling into malloc. A malloc
1223cab2bb3Spatrick // implementation that does global synchronization might be holding a lock for a
1233cab2bb3Spatrick // critical section, calling a function that might be XRay instrumented (and
1243cab2bb3Spatrick // thus in turn calling into malloc by virtue of registration of the
1253cab2bb3Spatrick // thread_local's destructor).
1263cab2bb3Spatrick #if XRAY_HAS_TLS_ALIGNAS
1273cab2bb3Spatrick static_assert(alignof(ThreadLocalData) >= 64,
1283cab2bb3Spatrick               "ThreadLocalData must be cache line aligned.");
1293cab2bb3Spatrick #endif
getThreadLocalData()1303cab2bb3Spatrick static ThreadLocalData &getThreadLocalData() {
1313cab2bb3Spatrick   thread_local typename std::aligned_storage<
1323cab2bb3Spatrick       sizeof(ThreadLocalData), alignof(ThreadLocalData)>::type TLDStorage{};
1333cab2bb3Spatrick 
1343cab2bb3Spatrick   if (pthread_getspecific(Key) == NULL) {
1353cab2bb3Spatrick     new (reinterpret_cast<ThreadLocalData *>(&TLDStorage)) ThreadLocalData{};
1363cab2bb3Spatrick     pthread_setspecific(Key, &TLDStorage);
1373cab2bb3Spatrick   }
1383cab2bb3Spatrick 
1393cab2bb3Spatrick   return *reinterpret_cast<ThreadLocalData *>(&TLDStorage);
1403cab2bb3Spatrick }
1413cab2bb3Spatrick 
fdrCommonHeaderInfo()1423cab2bb3Spatrick static XRayFileHeader &fdrCommonHeaderInfo() {
1433cab2bb3Spatrick   static std::aligned_storage<sizeof(XRayFileHeader)>::type HStorage;
1443cab2bb3Spatrick   static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
1453cab2bb3Spatrick   static bool TSCSupported = true;
1463cab2bb3Spatrick   static uint64_t CycleFrequency = NanosecondsPerSecond;
1473cab2bb3Spatrick   pthread_once(
1483cab2bb3Spatrick       &OnceInit, +[] {
1493cab2bb3Spatrick         XRayFileHeader &H = reinterpret_cast<XRayFileHeader &>(HStorage);
1503cab2bb3Spatrick         // Version 2 of the log writes the extents of the buffer, instead of
1513cab2bb3Spatrick         // relying on an end-of-buffer record.
1523cab2bb3Spatrick         // Version 3 includes PID metadata record.
1533cab2bb3Spatrick         // Version 4 includes CPU data in the custom event records.
1543cab2bb3Spatrick         // Version 5 uses relative deltas for custom and typed event records,
1553cab2bb3Spatrick         // and removes the CPU data in custom event records (similar to how
1563cab2bb3Spatrick         // function records use deltas instead of full TSCs and rely on other
1573cab2bb3Spatrick         // metadata records for TSC wraparound and CPU migration).
1583cab2bb3Spatrick         H.Version = 5;
1593cab2bb3Spatrick         H.Type = FileTypes::FDR_LOG;
1603cab2bb3Spatrick 
1613cab2bb3Spatrick         // Test for required CPU features and cache the cycle frequency
1623cab2bb3Spatrick         TSCSupported = probeRequiredCPUFeatures();
1633cab2bb3Spatrick         if (TSCSupported)
1643cab2bb3Spatrick           CycleFrequency = getTSCFrequency();
1653cab2bb3Spatrick         H.CycleFrequency = CycleFrequency;
1663cab2bb3Spatrick 
1673cab2bb3Spatrick         // FIXME: Actually check whether we have 'constant_tsc' and
1683cab2bb3Spatrick         // 'nonstop_tsc' before setting the values in the header.
1693cab2bb3Spatrick         H.ConstantTSC = 1;
1703cab2bb3Spatrick         H.NonstopTSC = 1;
1713cab2bb3Spatrick       });
1723cab2bb3Spatrick   return reinterpret_cast<XRayFileHeader &>(HStorage);
1733cab2bb3Spatrick }
1743cab2bb3Spatrick 
1753cab2bb3Spatrick // This is the iterator implementation, which knows how to handle FDR-mode
1763cab2bb3Spatrick // specific buffers. This is used as an implementation of the iterator function
1773cab2bb3Spatrick // needed by __xray_set_buffer_iterator(...). It maintains a global state of the
1783cab2bb3Spatrick // buffer iteration for the currently installed FDR mode buffers. In particular:
1793cab2bb3Spatrick //
1803cab2bb3Spatrick //   - If the argument represents the initial state of XRayBuffer ({nullptr, 0})
1813cab2bb3Spatrick //     then the iterator returns the header information.
1823cab2bb3Spatrick //   - If the argument represents the header information ({address of header
1833cab2bb3Spatrick //     info, size of the header info}) then it returns the first FDR buffer's
1843cab2bb3Spatrick //     address and extents.
1853cab2bb3Spatrick //   - It will keep returning the next buffer and extents as there are more
1863cab2bb3Spatrick //     buffers to process. When the input represents the last buffer, it will
1873cab2bb3Spatrick //     return the initial state to signal completion ({nullptr, 0}).
1883cab2bb3Spatrick //
1893cab2bb3Spatrick // See xray/xray_log_interface.h for more details on the requirements for the
1903cab2bb3Spatrick // implementations of __xray_set_buffer_iterator(...) and
1913cab2bb3Spatrick // __xray_log_process_buffers(...).
fdrIterator(const XRayBuffer B)1923cab2bb3Spatrick XRayBuffer fdrIterator(const XRayBuffer B) {
1933cab2bb3Spatrick   DCHECK(internal_strcmp(__xray_log_get_current_mode(), "xray-fdr") == 0);
1943cab2bb3Spatrick   DCHECK(BQ->finalizing());
1953cab2bb3Spatrick 
1963cab2bb3Spatrick   if (BQ == nullptr || !BQ->finalizing()) {
1973cab2bb3Spatrick     if (Verbosity())
1983cab2bb3Spatrick       Report(
1993cab2bb3Spatrick           "XRay FDR: Failed global buffer queue is null or not finalizing!\n");
2003cab2bb3Spatrick     return {nullptr, 0};
2013cab2bb3Spatrick   }
2023cab2bb3Spatrick 
2033cab2bb3Spatrick   // We use a global scratch-pad for the header information, which only gets
2043cab2bb3Spatrick   // initialized the first time this function is called. We'll update one part
2053cab2bb3Spatrick   // of this information with some relevant data (in particular the number of
2063cab2bb3Spatrick   // buffers to expect).
2073cab2bb3Spatrick   static std::aligned_storage<sizeof(XRayFileHeader)>::type HeaderStorage;
2083cab2bb3Spatrick   static pthread_once_t HeaderOnce = PTHREAD_ONCE_INIT;
2093cab2bb3Spatrick   pthread_once(
2103cab2bb3Spatrick       &HeaderOnce, +[] {
2113cab2bb3Spatrick         reinterpret_cast<XRayFileHeader &>(HeaderStorage) =
2123cab2bb3Spatrick             fdrCommonHeaderInfo();
2133cab2bb3Spatrick       });
2143cab2bb3Spatrick 
2153cab2bb3Spatrick   // We use a convenience alias for code referring to Header from here on out.
2163cab2bb3Spatrick   auto &Header = reinterpret_cast<XRayFileHeader &>(HeaderStorage);
2173cab2bb3Spatrick   if (B.Data == nullptr && B.Size == 0) {
2183cab2bb3Spatrick     Header.FdrData = FdrAdditionalHeaderData{BQ->ConfiguredBufferSize()};
2193cab2bb3Spatrick     return XRayBuffer{static_cast<void *>(&Header), sizeof(Header)};
2203cab2bb3Spatrick   }
2213cab2bb3Spatrick 
2223cab2bb3Spatrick   static BufferQueue::const_iterator It{};
2233cab2bb3Spatrick   static BufferQueue::const_iterator End{};
2243cab2bb3Spatrick   static uint8_t *CurrentBuffer{nullptr};
2253cab2bb3Spatrick   static size_t SerializedBufferSize = 0;
2263cab2bb3Spatrick   if (B.Data == static_cast<void *>(&Header) && B.Size == sizeof(Header)) {
2273cab2bb3Spatrick     // From this point on, we provide raw access to the raw buffer we're getting
2283cab2bb3Spatrick     // from the BufferQueue. We're relying on the iterators from the current
2293cab2bb3Spatrick     // Buffer queue.
2303cab2bb3Spatrick     It = BQ->cbegin();
2313cab2bb3Spatrick     End = BQ->cend();
2323cab2bb3Spatrick   }
2333cab2bb3Spatrick 
2343cab2bb3Spatrick   if (CurrentBuffer != nullptr) {
2353cab2bb3Spatrick     deallocateBuffer(CurrentBuffer, SerializedBufferSize);
2363cab2bb3Spatrick     CurrentBuffer = nullptr;
2373cab2bb3Spatrick   }
2383cab2bb3Spatrick 
2393cab2bb3Spatrick   if (It == End)
2403cab2bb3Spatrick     return {nullptr, 0};
2413cab2bb3Spatrick 
2423cab2bb3Spatrick   // Set up the current buffer to contain the extents like we would when writing
2433cab2bb3Spatrick   // out to disk. The difference here would be that we still write "empty"
2443cab2bb3Spatrick   // buffers, or at least go through the iterators faithfully to let the
2453cab2bb3Spatrick   // handlers see the empty buffers in the queue.
2463cab2bb3Spatrick   //
2473cab2bb3Spatrick   // We need this atomic fence here to ensure that writes happening to the
2483cab2bb3Spatrick   // buffer have been committed before we load the extents atomically. Because
2493cab2bb3Spatrick   // the buffer is not explicitly synchronised across threads, we rely on the
2503cab2bb3Spatrick   // fence ordering to ensure that writes we expect to have been completed
2513cab2bb3Spatrick   // before the fence are fully committed before we read the extents.
2523cab2bb3Spatrick   atomic_thread_fence(memory_order_acquire);
2533cab2bb3Spatrick   auto BufferSize = atomic_load(It->Extents, memory_order_acquire);
2543cab2bb3Spatrick   SerializedBufferSize = BufferSize + sizeof(MetadataRecord);
2553cab2bb3Spatrick   CurrentBuffer = allocateBuffer(SerializedBufferSize);
2563cab2bb3Spatrick   if (CurrentBuffer == nullptr)
2573cab2bb3Spatrick     return {nullptr, 0};
2583cab2bb3Spatrick 
2593cab2bb3Spatrick   // Write out the extents as a Metadata Record into the CurrentBuffer.
2603cab2bb3Spatrick   MetadataRecord ExtentsRecord;
2613cab2bb3Spatrick   ExtentsRecord.Type = uint8_t(RecordType::Metadata);
2623cab2bb3Spatrick   ExtentsRecord.RecordKind =
2633cab2bb3Spatrick       uint8_t(MetadataRecord::RecordKinds::BufferExtents);
2643cab2bb3Spatrick   internal_memcpy(ExtentsRecord.Data, &BufferSize, sizeof(BufferSize));
2653cab2bb3Spatrick   auto AfterExtents =
2663cab2bb3Spatrick       static_cast<char *>(internal_memcpy(CurrentBuffer, &ExtentsRecord,
2673cab2bb3Spatrick                                           sizeof(MetadataRecord))) +
2683cab2bb3Spatrick       sizeof(MetadataRecord);
2693cab2bb3Spatrick   internal_memcpy(AfterExtents, It->Data, BufferSize);
2703cab2bb3Spatrick 
2713cab2bb3Spatrick   XRayBuffer Result;
2723cab2bb3Spatrick   Result.Data = CurrentBuffer;
2733cab2bb3Spatrick   Result.Size = SerializedBufferSize;
2743cab2bb3Spatrick   ++It;
2753cab2bb3Spatrick   return Result;
2763cab2bb3Spatrick }
2773cab2bb3Spatrick 
2783cab2bb3Spatrick // Must finalize before flushing.
fdrLoggingFlush()2793cab2bb3Spatrick XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
2803cab2bb3Spatrick   if (atomic_load(&LoggingStatus, memory_order_acquire) !=
2813cab2bb3Spatrick       XRayLogInitStatus::XRAY_LOG_FINALIZED) {
2823cab2bb3Spatrick     if (Verbosity())
2833cab2bb3Spatrick       Report("Not flushing log, implementation is not finalized.\n");
2843cab2bb3Spatrick     return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
2853cab2bb3Spatrick   }
2863cab2bb3Spatrick 
287*d89ec533Spatrick   if (atomic_exchange(&LogFlushStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHING,
288*d89ec533Spatrick                       memory_order_release) ==
289*d89ec533Spatrick       XRayLogFlushStatus::XRAY_LOG_FLUSHING) {
2903cab2bb3Spatrick     if (Verbosity())
291*d89ec533Spatrick       Report("Not flushing log, implementation is still flushing.\n");
292*d89ec533Spatrick     return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
2933cab2bb3Spatrick   }
2943cab2bb3Spatrick 
2953cab2bb3Spatrick   if (BQ == nullptr) {
2963cab2bb3Spatrick     if (Verbosity())
2973cab2bb3Spatrick       Report("Cannot flush when global buffer queue is null.\n");
2983cab2bb3Spatrick     return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
2993cab2bb3Spatrick   }
3003cab2bb3Spatrick 
3013cab2bb3Spatrick   // We wait a number of milliseconds to allow threads to see that we've
3023cab2bb3Spatrick   // finalised before attempting to flush the log.
3033cab2bb3Spatrick   SleepForMillis(fdrFlags()->grace_period_ms);
3043cab2bb3Spatrick 
3053cab2bb3Spatrick   // At this point, we're going to uninstall the iterator implementation, before
3063cab2bb3Spatrick   // we decide to do anything further with the global buffer queue.
3073cab2bb3Spatrick   __xray_log_remove_buffer_iterator();
3083cab2bb3Spatrick 
3093cab2bb3Spatrick   // Once flushed, we should set the global status of the logging implementation
3103cab2bb3Spatrick   // to "uninitialized" to allow for FDR-logging multiple runs.
3113cab2bb3Spatrick   auto ResetToUnitialized = at_scope_exit([] {
3123cab2bb3Spatrick     atomic_store(&LoggingStatus, XRayLogInitStatus::XRAY_LOG_UNINITIALIZED,
3133cab2bb3Spatrick                  memory_order_release);
3143cab2bb3Spatrick   });
3153cab2bb3Spatrick 
3163cab2bb3Spatrick   auto CleanupBuffers = at_scope_exit([] {
3173cab2bb3Spatrick     auto &TLD = getThreadLocalData();
3183cab2bb3Spatrick     if (TLD.Controller != nullptr)
3193cab2bb3Spatrick       TLD.Controller->flush();
3203cab2bb3Spatrick   });
3213cab2bb3Spatrick 
3223cab2bb3Spatrick   if (fdrFlags()->no_file_flush) {
3233cab2bb3Spatrick     if (Verbosity())
3243cab2bb3Spatrick       Report("XRay FDR: Not flushing to file, 'no_file_flush=true'.\n");
3253cab2bb3Spatrick 
3263cab2bb3Spatrick     atomic_store(&LogFlushStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED,
3273cab2bb3Spatrick                  memory_order_release);
3283cab2bb3Spatrick     return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
3293cab2bb3Spatrick   }
3303cab2bb3Spatrick 
3313cab2bb3Spatrick   // We write out the file in the following format:
3323cab2bb3Spatrick   //
3333cab2bb3Spatrick   //   1) We write down the XRay file header with version 1, type FDR_LOG.
3343cab2bb3Spatrick   //   2) Then we use the 'apply' member of the BufferQueue that's live, to
3353cab2bb3Spatrick   //      ensure that at this point in time we write down the buffers that have
3363cab2bb3Spatrick   //      been released (and marked "used") -- we dump the full buffer for now
3373cab2bb3Spatrick   //      (fixed-sized) and let the tools reading the buffers deal with the data
3383cab2bb3Spatrick   //      afterwards.
3393cab2bb3Spatrick   //
3403cab2bb3Spatrick   LogWriter *LW = LogWriter::Open();
3413cab2bb3Spatrick   if (LW == nullptr) {
3423cab2bb3Spatrick     auto Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
3433cab2bb3Spatrick     atomic_store(&LogFlushStatus, Result, memory_order_release);
3443cab2bb3Spatrick     return Result;
3453cab2bb3Spatrick   }
3463cab2bb3Spatrick 
3473cab2bb3Spatrick   XRayFileHeader Header = fdrCommonHeaderInfo();
3483cab2bb3Spatrick   Header.FdrData = FdrAdditionalHeaderData{BQ->ConfiguredBufferSize()};
3493cab2bb3Spatrick   LW->WriteAll(reinterpret_cast<char *>(&Header),
3503cab2bb3Spatrick                reinterpret_cast<char *>(&Header) + sizeof(Header));
3513cab2bb3Spatrick 
3523cab2bb3Spatrick   // Release the current thread's buffer before we attempt to write out all the
3533cab2bb3Spatrick   // buffers. This ensures that in case we had only a single thread going, that
3543cab2bb3Spatrick   // we are able to capture the data nonetheless.
3553cab2bb3Spatrick   auto &TLD = getThreadLocalData();
3563cab2bb3Spatrick   if (TLD.Controller != nullptr)
3573cab2bb3Spatrick     TLD.Controller->flush();
3583cab2bb3Spatrick 
3593cab2bb3Spatrick   BQ->apply([&](const BufferQueue::Buffer &B) {
3603cab2bb3Spatrick     // Starting at version 2 of the FDR logging implementation, we only write
3613cab2bb3Spatrick     // the records identified by the extents of the buffer. We use the Extents
3623cab2bb3Spatrick     // from the Buffer and write that out as the first record in the buffer.  We
3633cab2bb3Spatrick     // still use a Metadata record, but fill in the extents instead for the
3643cab2bb3Spatrick     // data.
3653cab2bb3Spatrick     MetadataRecord ExtentsRecord;
3663cab2bb3Spatrick     auto BufferExtents = atomic_load(B.Extents, memory_order_acquire);
3673cab2bb3Spatrick     DCHECK(BufferExtents <= B.Size);
3683cab2bb3Spatrick     ExtentsRecord.Type = uint8_t(RecordType::Metadata);
3693cab2bb3Spatrick     ExtentsRecord.RecordKind =
3703cab2bb3Spatrick         uint8_t(MetadataRecord::RecordKinds::BufferExtents);
3713cab2bb3Spatrick     internal_memcpy(ExtentsRecord.Data, &BufferExtents, sizeof(BufferExtents));
3723cab2bb3Spatrick     if (BufferExtents > 0) {
3733cab2bb3Spatrick       LW->WriteAll(reinterpret_cast<char *>(&ExtentsRecord),
3743cab2bb3Spatrick                    reinterpret_cast<char *>(&ExtentsRecord) +
3753cab2bb3Spatrick                        sizeof(MetadataRecord));
3763cab2bb3Spatrick       LW->WriteAll(reinterpret_cast<char *>(B.Data),
3773cab2bb3Spatrick                    reinterpret_cast<char *>(B.Data) + BufferExtents);
3783cab2bb3Spatrick     }
3793cab2bb3Spatrick   });
3803cab2bb3Spatrick 
3813cab2bb3Spatrick   atomic_store(&LogFlushStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED,
3823cab2bb3Spatrick                memory_order_release);
3833cab2bb3Spatrick   return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
3843cab2bb3Spatrick }
3853cab2bb3Spatrick 
fdrLoggingFinalize()3863cab2bb3Spatrick XRayLogInitStatus fdrLoggingFinalize() XRAY_NEVER_INSTRUMENT {
3873cab2bb3Spatrick   s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED;
3883cab2bb3Spatrick   if (!atomic_compare_exchange_strong(&LoggingStatus, &CurrentStatus,
3893cab2bb3Spatrick                                       XRayLogInitStatus::XRAY_LOG_FINALIZING,
3903cab2bb3Spatrick                                       memory_order_release)) {
3913cab2bb3Spatrick     if (Verbosity())
3923cab2bb3Spatrick       Report("Cannot finalize log, implementation not initialized.\n");
3933cab2bb3Spatrick     return static_cast<XRayLogInitStatus>(CurrentStatus);
3943cab2bb3Spatrick   }
3953cab2bb3Spatrick 
3963cab2bb3Spatrick   // Do special things to make the log finalize itself, and not allow any more
3973cab2bb3Spatrick   // operations to be performed until re-initialized.
3983cab2bb3Spatrick   if (BQ == nullptr) {
3993cab2bb3Spatrick     if (Verbosity())
4003cab2bb3Spatrick       Report("Attempting to finalize an uninitialized global buffer!\n");
4013cab2bb3Spatrick   } else {
4023cab2bb3Spatrick     BQ->finalize();
4033cab2bb3Spatrick   }
4043cab2bb3Spatrick 
4053cab2bb3Spatrick   atomic_store(&LoggingStatus, XRayLogInitStatus::XRAY_LOG_FINALIZED,
4063cab2bb3Spatrick                memory_order_release);
4073cab2bb3Spatrick   return XRayLogInitStatus::XRAY_LOG_FINALIZED;
4083cab2bb3Spatrick }
4093cab2bb3Spatrick 
4103cab2bb3Spatrick struct TSCAndCPU {
4113cab2bb3Spatrick   uint64_t TSC = 0;
4123cab2bb3Spatrick   unsigned char CPU = 0;
4133cab2bb3Spatrick };
4143cab2bb3Spatrick 
getTimestamp()4153cab2bb3Spatrick static TSCAndCPU getTimestamp() XRAY_NEVER_INSTRUMENT {
4163cab2bb3Spatrick   // We want to get the TSC as early as possible, so that we can check whether
4173cab2bb3Spatrick   // we've seen this CPU before. We also do it before we load anything else,
4183cab2bb3Spatrick   // to allow for forward progress with the scheduling.
4193cab2bb3Spatrick   TSCAndCPU Result;
4203cab2bb3Spatrick 
4213cab2bb3Spatrick   // Test once for required CPU features
4223cab2bb3Spatrick   static pthread_once_t OnceProbe = PTHREAD_ONCE_INIT;
4233cab2bb3Spatrick   static bool TSCSupported = true;
4243cab2bb3Spatrick   pthread_once(
4253cab2bb3Spatrick       &OnceProbe, +[] { TSCSupported = probeRequiredCPUFeatures(); });
4263cab2bb3Spatrick 
4273cab2bb3Spatrick   if (TSCSupported) {
4283cab2bb3Spatrick     Result.TSC = __xray::readTSC(Result.CPU);
4293cab2bb3Spatrick   } else {
4303cab2bb3Spatrick     // FIXME: This code needs refactoring as it appears in multiple locations
4313cab2bb3Spatrick     timespec TS;
4323cab2bb3Spatrick     int result = clock_gettime(CLOCK_REALTIME, &TS);
4333cab2bb3Spatrick     if (result != 0) {
4343cab2bb3Spatrick       Report("clock_gettime(2) return %d, errno=%d", result, int(errno));
4353cab2bb3Spatrick       TS = {0, 0};
4363cab2bb3Spatrick     }
4373cab2bb3Spatrick     Result.CPU = 0;
4383cab2bb3Spatrick     Result.TSC = TS.tv_sec * __xray::NanosecondsPerSecond + TS.tv_nsec;
4393cab2bb3Spatrick   }
4403cab2bb3Spatrick   return Result;
4413cab2bb3Spatrick }
4423cab2bb3Spatrick 
4433cab2bb3Spatrick thread_local atomic_uint8_t Running{0};
4443cab2bb3Spatrick 
setupTLD(ThreadLocalData & TLD)4453cab2bb3Spatrick static bool setupTLD(ThreadLocalData &TLD) XRAY_NEVER_INSTRUMENT {
4463cab2bb3Spatrick   // Check if we're finalizing, before proceeding.
4473cab2bb3Spatrick   {
4483cab2bb3Spatrick     auto Status = atomic_load(&LoggingStatus, memory_order_acquire);
4493cab2bb3Spatrick     if (Status == XRayLogInitStatus::XRAY_LOG_FINALIZING ||
4503cab2bb3Spatrick         Status == XRayLogInitStatus::XRAY_LOG_FINALIZED) {
4513cab2bb3Spatrick       if (TLD.Controller != nullptr) {
4523cab2bb3Spatrick         TLD.Controller->flush();
4533cab2bb3Spatrick         TLD.Controller = nullptr;
4543cab2bb3Spatrick       }
4553cab2bb3Spatrick       return false;
4563cab2bb3Spatrick     }
4573cab2bb3Spatrick   }
4583cab2bb3Spatrick 
4593cab2bb3Spatrick   if (UNLIKELY(TLD.Controller == nullptr)) {
4603cab2bb3Spatrick     // Set up the TLD buffer queue.
4613cab2bb3Spatrick     if (UNLIKELY(BQ == nullptr))
4623cab2bb3Spatrick       return false;
4633cab2bb3Spatrick     TLD.BQ = BQ;
4643cab2bb3Spatrick 
4653cab2bb3Spatrick     // Check that we have a valid buffer.
4663cab2bb3Spatrick     if (TLD.Buffer.Generation != BQ->generation() &&
4673cab2bb3Spatrick         TLD.BQ->releaseBuffer(TLD.Buffer) != BufferQueue::ErrorCode::Ok)
4683cab2bb3Spatrick       return false;
4693cab2bb3Spatrick 
4703cab2bb3Spatrick     // Set up a buffer, before setting up the log writer. Bail out on failure.
4713cab2bb3Spatrick     if (TLD.BQ->getBuffer(TLD.Buffer) != BufferQueue::ErrorCode::Ok)
4723cab2bb3Spatrick       return false;
4733cab2bb3Spatrick 
4743cab2bb3Spatrick     // Set up the Log Writer for this thread.
4753cab2bb3Spatrick     if (UNLIKELY(TLD.Writer == nullptr)) {
4763cab2bb3Spatrick       auto *LWStorage = reinterpret_cast<FDRLogWriter *>(&TLD.LWStorage);
4773cab2bb3Spatrick       new (LWStorage) FDRLogWriter(TLD.Buffer);
4783cab2bb3Spatrick       TLD.Writer = LWStorage;
4793cab2bb3Spatrick     } else {
4803cab2bb3Spatrick       TLD.Writer->resetRecord();
4813cab2bb3Spatrick     }
4823cab2bb3Spatrick 
4833cab2bb3Spatrick     auto *CStorage = reinterpret_cast<FDRController<> *>(&TLD.CStorage);
4843cab2bb3Spatrick     new (CStorage)
4853cab2bb3Spatrick         FDRController<>(TLD.BQ, TLD.Buffer, *TLD.Writer, clock_gettime,
4863cab2bb3Spatrick                         atomic_load_relaxed(&ThresholdTicks));
4873cab2bb3Spatrick     TLD.Controller = CStorage;
4883cab2bb3Spatrick   }
4893cab2bb3Spatrick 
4903cab2bb3Spatrick   DCHECK_NE(TLD.Controller, nullptr);
4913cab2bb3Spatrick   return true;
4923cab2bb3Spatrick }
4933cab2bb3Spatrick 
fdrLoggingHandleArg0(int32_t FuncId,XRayEntryType Entry)4943cab2bb3Spatrick void fdrLoggingHandleArg0(int32_t FuncId,
4953cab2bb3Spatrick                           XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {
4963cab2bb3Spatrick   auto TC = getTimestamp();
4973cab2bb3Spatrick   auto &TSC = TC.TSC;
4983cab2bb3Spatrick   auto &CPU = TC.CPU;
4993cab2bb3Spatrick   RecursionGuard Guard{Running};
5003cab2bb3Spatrick   if (!Guard)
5013cab2bb3Spatrick     return;
5023cab2bb3Spatrick 
5033cab2bb3Spatrick   auto &TLD = getThreadLocalData();
5043cab2bb3Spatrick   if (!setupTLD(TLD))
5053cab2bb3Spatrick     return;
5063cab2bb3Spatrick 
5073cab2bb3Spatrick   switch (Entry) {
5083cab2bb3Spatrick   case XRayEntryType::ENTRY:
5093cab2bb3Spatrick   case XRayEntryType::LOG_ARGS_ENTRY:
5103cab2bb3Spatrick     TLD.Controller->functionEnter(FuncId, TSC, CPU);
5113cab2bb3Spatrick     return;
5123cab2bb3Spatrick   case XRayEntryType::EXIT:
5133cab2bb3Spatrick     TLD.Controller->functionExit(FuncId, TSC, CPU);
5143cab2bb3Spatrick     return;
5153cab2bb3Spatrick   case XRayEntryType::TAIL:
5163cab2bb3Spatrick     TLD.Controller->functionTailExit(FuncId, TSC, CPU);
5173cab2bb3Spatrick     return;
5183cab2bb3Spatrick   case XRayEntryType::CUSTOM_EVENT:
5193cab2bb3Spatrick   case XRayEntryType::TYPED_EVENT:
5203cab2bb3Spatrick     break;
5213cab2bb3Spatrick   }
5223cab2bb3Spatrick }
5233cab2bb3Spatrick 
fdrLoggingHandleArg1(int32_t FuncId,XRayEntryType Entry,uint64_t Arg)5243cab2bb3Spatrick void fdrLoggingHandleArg1(int32_t FuncId, XRayEntryType Entry,
5253cab2bb3Spatrick                           uint64_t Arg) XRAY_NEVER_INSTRUMENT {
5263cab2bb3Spatrick   auto TC = getTimestamp();
5273cab2bb3Spatrick   auto &TSC = TC.TSC;
5283cab2bb3Spatrick   auto &CPU = TC.CPU;
5293cab2bb3Spatrick   RecursionGuard Guard{Running};
5303cab2bb3Spatrick   if (!Guard)
5313cab2bb3Spatrick     return;
5323cab2bb3Spatrick 
5333cab2bb3Spatrick   auto &TLD = getThreadLocalData();
5343cab2bb3Spatrick   if (!setupTLD(TLD))
5353cab2bb3Spatrick     return;
5363cab2bb3Spatrick 
5373cab2bb3Spatrick   switch (Entry) {
5383cab2bb3Spatrick   case XRayEntryType::ENTRY:
5393cab2bb3Spatrick   case XRayEntryType::LOG_ARGS_ENTRY:
5403cab2bb3Spatrick     TLD.Controller->functionEnterArg(FuncId, TSC, CPU, Arg);
5413cab2bb3Spatrick     return;
5423cab2bb3Spatrick   case XRayEntryType::EXIT:
5433cab2bb3Spatrick     TLD.Controller->functionExit(FuncId, TSC, CPU);
5443cab2bb3Spatrick     return;
5453cab2bb3Spatrick   case XRayEntryType::TAIL:
5463cab2bb3Spatrick     TLD.Controller->functionTailExit(FuncId, TSC, CPU);
5473cab2bb3Spatrick     return;
5483cab2bb3Spatrick   case XRayEntryType::CUSTOM_EVENT:
5493cab2bb3Spatrick   case XRayEntryType::TYPED_EVENT:
5503cab2bb3Spatrick     break;
5513cab2bb3Spatrick   }
5523cab2bb3Spatrick }
5533cab2bb3Spatrick 
fdrLoggingHandleCustomEvent(void * Event,std::size_t EventSize)5543cab2bb3Spatrick void fdrLoggingHandleCustomEvent(void *Event,
5553cab2bb3Spatrick                                  std::size_t EventSize) XRAY_NEVER_INSTRUMENT {
5563cab2bb3Spatrick   auto TC = getTimestamp();
5573cab2bb3Spatrick   auto &TSC = TC.TSC;
5583cab2bb3Spatrick   auto &CPU = TC.CPU;
5593cab2bb3Spatrick   RecursionGuard Guard{Running};
5603cab2bb3Spatrick   if (!Guard)
5613cab2bb3Spatrick     return;
5623cab2bb3Spatrick 
5633cab2bb3Spatrick   // Complain when we ever get at least one custom event that's larger than what
5643cab2bb3Spatrick   // we can possibly support.
5653cab2bb3Spatrick   if (EventSize >
5663cab2bb3Spatrick       static_cast<std::size_t>(std::numeric_limits<int32_t>::max())) {
5673cab2bb3Spatrick     static pthread_once_t Once = PTHREAD_ONCE_INIT;
5683cab2bb3Spatrick     pthread_once(
5693cab2bb3Spatrick         &Once, +[] {
5703cab2bb3Spatrick           Report("Custom event size too large; truncating to %d.\n",
5713cab2bb3Spatrick                  std::numeric_limits<int32_t>::max());
5723cab2bb3Spatrick         });
5733cab2bb3Spatrick   }
5743cab2bb3Spatrick 
5753cab2bb3Spatrick   auto &TLD = getThreadLocalData();
5763cab2bb3Spatrick   if (!setupTLD(TLD))
5773cab2bb3Spatrick     return;
5783cab2bb3Spatrick 
5793cab2bb3Spatrick   int32_t ReducedEventSize = static_cast<int32_t>(EventSize);
5803cab2bb3Spatrick   TLD.Controller->customEvent(TSC, CPU, Event, ReducedEventSize);
5813cab2bb3Spatrick }
5823cab2bb3Spatrick 
fdrLoggingHandleTypedEvent(uint16_t EventType,const void * Event,std::size_t EventSize)5833cab2bb3Spatrick void fdrLoggingHandleTypedEvent(
5843cab2bb3Spatrick     uint16_t EventType, const void *Event,
5853cab2bb3Spatrick     std::size_t EventSize) noexcept XRAY_NEVER_INSTRUMENT {
5863cab2bb3Spatrick   auto TC = getTimestamp();
5873cab2bb3Spatrick   auto &TSC = TC.TSC;
5883cab2bb3Spatrick   auto &CPU = TC.CPU;
5893cab2bb3Spatrick   RecursionGuard Guard{Running};
5903cab2bb3Spatrick   if (!Guard)
5913cab2bb3Spatrick     return;
5923cab2bb3Spatrick 
5933cab2bb3Spatrick   // Complain when we ever get at least one typed event that's larger than what
5943cab2bb3Spatrick   // we can possibly support.
5953cab2bb3Spatrick   if (EventSize >
5963cab2bb3Spatrick       static_cast<std::size_t>(std::numeric_limits<int32_t>::max())) {
5973cab2bb3Spatrick     static pthread_once_t Once = PTHREAD_ONCE_INIT;
5983cab2bb3Spatrick     pthread_once(
5993cab2bb3Spatrick         &Once, +[] {
6003cab2bb3Spatrick           Report("Typed event size too large; truncating to %d.\n",
6013cab2bb3Spatrick                  std::numeric_limits<int32_t>::max());
6023cab2bb3Spatrick         });
6033cab2bb3Spatrick   }
6043cab2bb3Spatrick 
6053cab2bb3Spatrick   auto &TLD = getThreadLocalData();
6063cab2bb3Spatrick   if (!setupTLD(TLD))
6073cab2bb3Spatrick     return;
6083cab2bb3Spatrick 
6093cab2bb3Spatrick   int32_t ReducedEventSize = static_cast<int32_t>(EventSize);
6103cab2bb3Spatrick   TLD.Controller->typedEvent(TSC, CPU, EventType, Event, ReducedEventSize);
6113cab2bb3Spatrick }
6123cab2bb3Spatrick 
fdrLoggingInit(size_t,size_t,void * Options,size_t OptionsSize)6133cab2bb3Spatrick XRayLogInitStatus fdrLoggingInit(size_t, size_t, void *Options,
6143cab2bb3Spatrick                                  size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
6153cab2bb3Spatrick   if (Options == nullptr)
6163cab2bb3Spatrick     return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
6173cab2bb3Spatrick 
6183cab2bb3Spatrick   s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
6193cab2bb3Spatrick   if (!atomic_compare_exchange_strong(&LoggingStatus, &CurrentStatus,
6203cab2bb3Spatrick                                       XRayLogInitStatus::XRAY_LOG_INITIALIZING,
6213cab2bb3Spatrick                                       memory_order_release)) {
6223cab2bb3Spatrick     if (Verbosity())
6233cab2bb3Spatrick       Report("Cannot initialize already initialized implementation.\n");
6243cab2bb3Spatrick     return static_cast<XRayLogInitStatus>(CurrentStatus);
6253cab2bb3Spatrick   }
6263cab2bb3Spatrick 
6273cab2bb3Spatrick   if (Verbosity())
6283cab2bb3Spatrick     Report("Initializing FDR mode with options: %s\n",
6293cab2bb3Spatrick            static_cast<const char *>(Options));
6303cab2bb3Spatrick 
6313cab2bb3Spatrick   // TODO: Factor out the flags specific to the FDR mode implementation. For
6323cab2bb3Spatrick   // now, use the global/single definition of the flags, since the FDR mode
6333cab2bb3Spatrick   // flags are already defined there.
6343cab2bb3Spatrick   FlagParser FDRParser;
6353cab2bb3Spatrick   FDRFlags FDRFlags;
6363cab2bb3Spatrick   registerXRayFDRFlags(&FDRParser, &FDRFlags);
6373cab2bb3Spatrick   FDRFlags.setDefaults();
6383cab2bb3Spatrick 
6393cab2bb3Spatrick   // Override first from the general XRAY_DEFAULT_OPTIONS compiler-provided
6403cab2bb3Spatrick   // options until we migrate everyone to use the XRAY_FDR_OPTIONS
6413cab2bb3Spatrick   // compiler-provided options.
6423cab2bb3Spatrick   FDRParser.ParseString(useCompilerDefinedFlags());
6433cab2bb3Spatrick   FDRParser.ParseString(useCompilerDefinedFDRFlags());
6443cab2bb3Spatrick   auto *EnvOpts = GetEnv("XRAY_FDR_OPTIONS");
6453cab2bb3Spatrick   if (EnvOpts == nullptr)
6463cab2bb3Spatrick     EnvOpts = "";
6473cab2bb3Spatrick   FDRParser.ParseString(EnvOpts);
6483cab2bb3Spatrick 
6493cab2bb3Spatrick   // FIXME: Remove this when we fully remove the deprecated flags.
6503cab2bb3Spatrick   if (internal_strlen(EnvOpts) == 0) {
6513cab2bb3Spatrick     FDRFlags.func_duration_threshold_us =
6523cab2bb3Spatrick         flags()->xray_fdr_log_func_duration_threshold_us;
6533cab2bb3Spatrick     FDRFlags.grace_period_ms = flags()->xray_fdr_log_grace_period_ms;
6543cab2bb3Spatrick   }
6553cab2bb3Spatrick 
6563cab2bb3Spatrick   // The provided options should always override the compiler-provided and
6573cab2bb3Spatrick   // environment-variable defined options.
6583cab2bb3Spatrick   FDRParser.ParseString(static_cast<const char *>(Options));
6593cab2bb3Spatrick   *fdrFlags() = FDRFlags;
6603cab2bb3Spatrick   auto BufferSize = FDRFlags.buffer_size;
6613cab2bb3Spatrick   auto BufferMax = FDRFlags.buffer_max;
6623cab2bb3Spatrick 
6633cab2bb3Spatrick   if (BQ == nullptr) {
6643cab2bb3Spatrick     bool Success = false;
6653cab2bb3Spatrick     BQ = reinterpret_cast<BufferQueue *>(&BufferQueueStorage);
6663cab2bb3Spatrick     new (BQ) BufferQueue(BufferSize, BufferMax, Success);
6673cab2bb3Spatrick     if (!Success) {
6683cab2bb3Spatrick       Report("BufferQueue init failed.\n");
6693cab2bb3Spatrick       return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
6703cab2bb3Spatrick     }
6713cab2bb3Spatrick   } else {
6723cab2bb3Spatrick     if (BQ->init(BufferSize, BufferMax) != BufferQueue::ErrorCode::Ok) {
6733cab2bb3Spatrick       if (Verbosity())
6743cab2bb3Spatrick         Report("Failed to re-initialize global buffer queue. Init failed.\n");
6753cab2bb3Spatrick       return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
6763cab2bb3Spatrick     }
6773cab2bb3Spatrick   }
6783cab2bb3Spatrick 
6793cab2bb3Spatrick   static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
6803cab2bb3Spatrick   pthread_once(
6813cab2bb3Spatrick       &OnceInit, +[] {
6823cab2bb3Spatrick         atomic_store(&TicksPerSec,
6833cab2bb3Spatrick                      probeRequiredCPUFeatures() ? getTSCFrequency()
6843cab2bb3Spatrick                                                 : __xray::NanosecondsPerSecond,
6853cab2bb3Spatrick                      memory_order_release);
6863cab2bb3Spatrick         pthread_key_create(
6873cab2bb3Spatrick             &Key, +[](void *TLDPtr) {
6883cab2bb3Spatrick               if (TLDPtr == nullptr)
6893cab2bb3Spatrick                 return;
6903cab2bb3Spatrick               auto &TLD = *reinterpret_cast<ThreadLocalData *>(TLDPtr);
6913cab2bb3Spatrick               if (TLD.BQ == nullptr)
6923cab2bb3Spatrick                 return;
6933cab2bb3Spatrick               if (TLD.Buffer.Data == nullptr)
6943cab2bb3Spatrick                 return;
6953cab2bb3Spatrick               auto EC = TLD.BQ->releaseBuffer(TLD.Buffer);
6963cab2bb3Spatrick               if (EC != BufferQueue::ErrorCode::Ok)
6973cab2bb3Spatrick                 Report("At thread exit, failed to release buffer at %p; "
6983cab2bb3Spatrick                        "error=%s\n",
6993cab2bb3Spatrick                        TLD.Buffer.Data, BufferQueue::getErrorString(EC));
7003cab2bb3Spatrick             });
7013cab2bb3Spatrick       });
7023cab2bb3Spatrick 
7033cab2bb3Spatrick   atomic_store(&ThresholdTicks,
7043cab2bb3Spatrick                atomic_load_relaxed(&TicksPerSec) *
7053cab2bb3Spatrick                    fdrFlags()->func_duration_threshold_us / 1000000,
7063cab2bb3Spatrick                memory_order_release);
7073cab2bb3Spatrick   // Arg1 handler should go in first to avoid concurrent code accidentally
7083cab2bb3Spatrick   // falling back to arg0 when it should have ran arg1.
7093cab2bb3Spatrick   __xray_set_handler_arg1(fdrLoggingHandleArg1);
7103cab2bb3Spatrick   // Install the actual handleArg0 handler after initialising the buffers.
7113cab2bb3Spatrick   __xray_set_handler(fdrLoggingHandleArg0);
7123cab2bb3Spatrick   __xray_set_customevent_handler(fdrLoggingHandleCustomEvent);
7133cab2bb3Spatrick   __xray_set_typedevent_handler(fdrLoggingHandleTypedEvent);
7143cab2bb3Spatrick 
7153cab2bb3Spatrick   // Install the buffer iterator implementation.
7163cab2bb3Spatrick   __xray_log_set_buffer_iterator(fdrIterator);
7173cab2bb3Spatrick 
7183cab2bb3Spatrick   atomic_store(&LoggingStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZED,
7193cab2bb3Spatrick                memory_order_release);
7203cab2bb3Spatrick 
7213cab2bb3Spatrick   if (Verbosity())
7223cab2bb3Spatrick     Report("XRay FDR init successful.\n");
7233cab2bb3Spatrick   return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
7243cab2bb3Spatrick }
7253cab2bb3Spatrick 
fdrLogDynamicInitializer()7263cab2bb3Spatrick bool fdrLogDynamicInitializer() XRAY_NEVER_INSTRUMENT {
7273cab2bb3Spatrick   XRayLogImpl Impl{
7283cab2bb3Spatrick       fdrLoggingInit,
7293cab2bb3Spatrick       fdrLoggingFinalize,
7303cab2bb3Spatrick       fdrLoggingHandleArg0,
7313cab2bb3Spatrick       fdrLoggingFlush,
7323cab2bb3Spatrick   };
7333cab2bb3Spatrick   auto RegistrationResult = __xray_log_register_mode("xray-fdr", Impl);
7343cab2bb3Spatrick   if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK &&
7353cab2bb3Spatrick       Verbosity()) {
7363cab2bb3Spatrick     Report("Cannot register XRay FDR mode to 'xray-fdr'; error = %d\n",
7373cab2bb3Spatrick            RegistrationResult);
7383cab2bb3Spatrick     return false;
7393cab2bb3Spatrick   }
7403cab2bb3Spatrick 
7413cab2bb3Spatrick   if (flags()->xray_fdr_log ||
7423cab2bb3Spatrick       !internal_strcmp(flags()->xray_mode, "xray-fdr")) {
7433cab2bb3Spatrick     auto SelectResult = __xray_log_select_mode("xray-fdr");
7443cab2bb3Spatrick     if (SelectResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK &&
7453cab2bb3Spatrick         Verbosity()) {
7463cab2bb3Spatrick       Report("Cannot select XRay FDR mode as 'xray-fdr'; error = %d\n",
7473cab2bb3Spatrick              SelectResult);
7483cab2bb3Spatrick       return false;
7493cab2bb3Spatrick     }
7503cab2bb3Spatrick   }
7513cab2bb3Spatrick   return true;
7523cab2bb3Spatrick }
7533cab2bb3Spatrick 
7543cab2bb3Spatrick } // namespace __xray
7553cab2bb3Spatrick 
7563cab2bb3Spatrick static auto UNUSED Unused = __xray::fdrLogDynamicInitializer();
757