Path: blob/main/contrib/llvm-project/compiler-rt/lib/xray/xray_fdr_logging.cpp
35265 views
//===-- xray_fdr_logging.cpp -----------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file is a part of XRay, a dynamic runtime instrumentation system.9//10// Here we implement the Flight Data Recorder mode for XRay, where we use11// compact structures to store records in memory as well as when writing out the12// data to files.13//14//===----------------------------------------------------------------------===//15#include "xray_fdr_logging.h"16#include <cassert>17#include <cstddef>18#include <errno.h>19#include <limits>20#include <memory>21#include <pthread.h>22#include <sys/time.h>23#include <time.h>24#include <unistd.h>2526#include "sanitizer_common/sanitizer_allocator_internal.h"27#include "sanitizer_common/sanitizer_atomic.h"28#include "sanitizer_common/sanitizer_common.h"29#include "xray/xray_interface.h"30#include "xray/xray_records.h"31#include "xray_allocator.h"32#include "xray_buffer_queue.h"33#include "xray_defs.h"34#include "xray_fdr_controller.h"35#include "xray_fdr_flags.h"36#include "xray_fdr_log_writer.h"37#include "xray_flags.h"38#include "xray_recursion_guard.h"39#include "xray_tsc.h"40#include "xray_utils.h"4142namespace __xray {4344static atomic_sint32_t LoggingStatus = {45XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};4647namespace {4849// Group together thread-local-data in a struct, then hide it behind a function50// call so that it can be initialized on first use instead of as a global. We51// force the alignment to 64-bytes for x86 cache line alignment, as this52// structure is used in the hot path of implementation.53struct XRAY_TLS_ALIGNAS(64) ThreadLocalData {54BufferQueue::Buffer Buffer{};55BufferQueue *BQ = nullptr;5657using LogWriterStorage = std::byte[sizeof(FDRLogWriter)];58alignas(FDRLogWriter) LogWriterStorage LWStorage;59FDRLogWriter *Writer = nullptr;6061using ControllerStorage = std::byte[sizeof(FDRController<>)];62alignas(FDRController<>) ControllerStorage CStorage;63FDRController<> *Controller = nullptr;64};6566} // namespace6768static_assert(std::is_trivially_destructible<ThreadLocalData>::value,69"ThreadLocalData must be trivially destructible");7071// Use a global pthread key to identify thread-local data for logging.72static pthread_key_t Key;7374// Global BufferQueue.75static std::byte BufferQueueStorage[sizeof(BufferQueue)];76static BufferQueue *BQ = nullptr;7778// Global thresholds for function durations.79static atomic_uint64_t ThresholdTicks{0};8081// Global for ticks per second.82static atomic_uint64_t TicksPerSec{0};8384static atomic_sint32_t LogFlushStatus = {85XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};8687// This function will initialize the thread-local data structure used by the FDR88// logging implementation and return a reference to it. The implementation89// details require a bit of care to maintain.90//91// First, some requirements on the implementation in general:92//93// - XRay handlers should not call any memory allocation routines that may94// delegate to an instrumented implementation. This means functions like95// malloc() and free() should not be called while instrumenting.96//97// - We would like to use some thread-local data initialized on first-use of98// the XRay instrumentation. These allow us to implement unsynchronized99// routines that access resources associated with the thread.100//101// The implementation here uses a few mechanisms that allow us to provide both102// the requirements listed above. We do this by:103//104// 1. Using a thread-local aligned storage buffer for representing the105// ThreadLocalData struct. This data will be uninitialized memory by106// design.107//108// 2. Not requiring a thread exit handler/implementation, keeping the109// thread-local as purely a collection of references/data that do not110// require cleanup.111//112// We're doing this to avoid using a `thread_local` object that has a113// non-trivial destructor, because the C++ runtime might call std::malloc(...)114// to register calls to destructors. Deadlocks may arise when, for example, an115// externally provided malloc implementation is XRay instrumented, and116// initializing the thread-locals involves calling into malloc. A malloc117// implementation that does global synchronization might be holding a lock for a118// critical section, calling a function that might be XRay instrumented (and119// thus in turn calling into malloc by virtue of registration of the120// thread_local's destructor).121#if XRAY_HAS_TLS_ALIGNAS122static_assert(alignof(ThreadLocalData) >= 64,123"ThreadLocalData must be cache line aligned.");124#endif125static ThreadLocalData &getThreadLocalData() {126alignas(ThreadLocalData) thread_local std::byte127TLDStorage[sizeof(ThreadLocalData)];128129if (pthread_getspecific(Key) == NULL) {130new (reinterpret_cast<ThreadLocalData *>(&TLDStorage)) ThreadLocalData{};131pthread_setspecific(Key, &TLDStorage);132}133134return *reinterpret_cast<ThreadLocalData *>(&TLDStorage);135}136137static XRayFileHeader &fdrCommonHeaderInfo() {138alignas(XRayFileHeader) static std::byte HStorage[sizeof(XRayFileHeader)];139static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;140static bool TSCSupported = true;141static uint64_t CycleFrequency = NanosecondsPerSecond;142pthread_once(143&OnceInit, +[] {144XRayFileHeader &H = reinterpret_cast<XRayFileHeader &>(HStorage);145// Version 2 of the log writes the extents of the buffer, instead of146// relying on an end-of-buffer record.147// Version 3 includes PID metadata record.148// Version 4 includes CPU data in the custom event records.149// Version 5 uses relative deltas for custom and typed event records,150// and removes the CPU data in custom event records (similar to how151// function records use deltas instead of full TSCs and rely on other152// metadata records for TSC wraparound and CPU migration).153H.Version = 5;154H.Type = FileTypes::FDR_LOG;155156// Test for required CPU features and cache the cycle frequency157TSCSupported = probeRequiredCPUFeatures();158if (TSCSupported)159CycleFrequency = getTSCFrequency();160H.CycleFrequency = CycleFrequency;161162// FIXME: Actually check whether we have 'constant_tsc' and163// 'nonstop_tsc' before setting the values in the header.164H.ConstantTSC = 1;165H.NonstopTSC = 1;166});167return reinterpret_cast<XRayFileHeader &>(HStorage);168}169170// This is the iterator implementation, which knows how to handle FDR-mode171// specific buffers. This is used as an implementation of the iterator function172// needed by __xray_set_buffer_iterator(...). It maintains a global state of the173// buffer iteration for the currently installed FDR mode buffers. In particular:174//175// - If the argument represents the initial state of XRayBuffer ({nullptr, 0})176// then the iterator returns the header information.177// - If the argument represents the header information ({address of header178// info, size of the header info}) then it returns the first FDR buffer's179// address and extents.180// - It will keep returning the next buffer and extents as there are more181// buffers to process. When the input represents the last buffer, it will182// return the initial state to signal completion ({nullptr, 0}).183//184// See xray/xray_log_interface.h for more details on the requirements for the185// implementations of __xray_set_buffer_iterator(...) and186// __xray_log_process_buffers(...).187XRayBuffer fdrIterator(const XRayBuffer B) {188DCHECK(internal_strcmp(__xray_log_get_current_mode(), "xray-fdr") == 0);189DCHECK(BQ->finalizing());190191if (BQ == nullptr || !BQ->finalizing()) {192if (Verbosity())193Report(194"XRay FDR: Failed global buffer queue is null or not finalizing!\n");195return {nullptr, 0};196}197198// We use a global scratch-pad for the header information, which only gets199// initialized the first time this function is called. We'll update one part200// of this information with some relevant data (in particular the number of201// buffers to expect).202alignas(203XRayFileHeader) static std::byte HeaderStorage[sizeof(XRayFileHeader)];204static pthread_once_t HeaderOnce = PTHREAD_ONCE_INIT;205pthread_once(206&HeaderOnce, +[] {207reinterpret_cast<XRayFileHeader &>(HeaderStorage) =208fdrCommonHeaderInfo();209});210211// We use a convenience alias for code referring to Header from here on out.212auto &Header = reinterpret_cast<XRayFileHeader &>(HeaderStorage);213if (B.Data == nullptr && B.Size == 0) {214Header.FdrData = FdrAdditionalHeaderData{BQ->ConfiguredBufferSize()};215return XRayBuffer{static_cast<void *>(&Header), sizeof(Header)};216}217218static BufferQueue::const_iterator It{};219static BufferQueue::const_iterator End{};220static uint8_t *CurrentBuffer{nullptr};221static size_t SerializedBufferSize = 0;222if (B.Data == static_cast<void *>(&Header) && B.Size == sizeof(Header)) {223// From this point on, we provide raw access to the raw buffer we're getting224// from the BufferQueue. We're relying on the iterators from the current225// Buffer queue.226It = BQ->cbegin();227End = BQ->cend();228}229230if (CurrentBuffer != nullptr) {231deallocateBuffer(CurrentBuffer, SerializedBufferSize);232CurrentBuffer = nullptr;233}234235if (It == End)236return {nullptr, 0};237238// Set up the current buffer to contain the extents like we would when writing239// out to disk. The difference here would be that we still write "empty"240// buffers, or at least go through the iterators faithfully to let the241// handlers see the empty buffers in the queue.242//243// We need this atomic fence here to ensure that writes happening to the244// buffer have been committed before we load the extents atomically. Because245// the buffer is not explicitly synchronised across threads, we rely on the246// fence ordering to ensure that writes we expect to have been completed247// before the fence are fully committed before we read the extents.248atomic_thread_fence(memory_order_acquire);249auto BufferSize = atomic_load(It->Extents, memory_order_acquire);250SerializedBufferSize = BufferSize + sizeof(MetadataRecord);251CurrentBuffer = allocateBuffer(SerializedBufferSize);252if (CurrentBuffer == nullptr)253return {nullptr, 0};254255// Write out the extents as a Metadata Record into the CurrentBuffer.256MetadataRecord ExtentsRecord;257ExtentsRecord.Type = uint8_t(RecordType::Metadata);258ExtentsRecord.RecordKind =259uint8_t(MetadataRecord::RecordKinds::BufferExtents);260internal_memcpy(ExtentsRecord.Data, &BufferSize, sizeof(BufferSize));261auto AfterExtents =262static_cast<char *>(internal_memcpy(CurrentBuffer, &ExtentsRecord,263sizeof(MetadataRecord))) +264sizeof(MetadataRecord);265internal_memcpy(AfterExtents, It->Data, BufferSize);266267XRayBuffer Result;268Result.Data = CurrentBuffer;269Result.Size = SerializedBufferSize;270++It;271return Result;272}273274// Must finalize before flushing.275XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {276if (atomic_load(&LoggingStatus, memory_order_acquire) !=277XRayLogInitStatus::XRAY_LOG_FINALIZED) {278if (Verbosity())279Report("Not flushing log, implementation is not finalized.\n");280return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;281}282283if (atomic_exchange(&LogFlushStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHING,284memory_order_release) ==285XRayLogFlushStatus::XRAY_LOG_FLUSHING) {286if (Verbosity())287Report("Not flushing log, implementation is still flushing.\n");288return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;289}290291if (BQ == nullptr) {292if (Verbosity())293Report("Cannot flush when global buffer queue is null.\n");294return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;295}296297// We wait a number of milliseconds to allow threads to see that we've298// finalised before attempting to flush the log.299SleepForMillis(fdrFlags()->grace_period_ms);300301// At this point, we're going to uninstall the iterator implementation, before302// we decide to do anything further with the global buffer queue.303__xray_log_remove_buffer_iterator();304305// Once flushed, we should set the global status of the logging implementation306// to "uninitialized" to allow for FDR-logging multiple runs.307auto ResetToUnitialized = at_scope_exit([] {308atomic_store(&LoggingStatus, XRayLogInitStatus::XRAY_LOG_UNINITIALIZED,309memory_order_release);310});311312auto CleanupBuffers = at_scope_exit([] {313auto &TLD = getThreadLocalData();314if (TLD.Controller != nullptr)315TLD.Controller->flush();316});317318if (fdrFlags()->no_file_flush) {319if (Verbosity())320Report("XRay FDR: Not flushing to file, 'no_file_flush=true'.\n");321322atomic_store(&LogFlushStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED,323memory_order_release);324return XRayLogFlushStatus::XRAY_LOG_FLUSHED;325}326327// We write out the file in the following format:328//329// 1) We write down the XRay file header with version 1, type FDR_LOG.330// 2) Then we use the 'apply' member of the BufferQueue that's live, to331// ensure that at this point in time we write down the buffers that have332// been released (and marked "used") -- we dump the full buffer for now333// (fixed-sized) and let the tools reading the buffers deal with the data334// afterwards.335//336LogWriter *LW = LogWriter::Open();337if (LW == nullptr) {338auto Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;339atomic_store(&LogFlushStatus, Result, memory_order_release);340return Result;341}342343XRayFileHeader Header = fdrCommonHeaderInfo();344Header.FdrData = FdrAdditionalHeaderData{BQ->ConfiguredBufferSize()};345LW->WriteAll(reinterpret_cast<char *>(&Header),346reinterpret_cast<char *>(&Header) + sizeof(Header));347348// Release the current thread's buffer before we attempt to write out all the349// buffers. This ensures that in case we had only a single thread going, that350// we are able to capture the data nonetheless.351auto &TLD = getThreadLocalData();352if (TLD.Controller != nullptr)353TLD.Controller->flush();354355BQ->apply([&](const BufferQueue::Buffer &B) {356// Starting at version 2 of the FDR logging implementation, we only write357// the records identified by the extents of the buffer. We use the Extents358// from the Buffer and write that out as the first record in the buffer. We359// still use a Metadata record, but fill in the extents instead for the360// data.361MetadataRecord ExtentsRecord;362auto BufferExtents = atomic_load(B.Extents, memory_order_acquire);363DCHECK(BufferExtents <= B.Size);364ExtentsRecord.Type = uint8_t(RecordType::Metadata);365ExtentsRecord.RecordKind =366uint8_t(MetadataRecord::RecordKinds::BufferExtents);367internal_memcpy(ExtentsRecord.Data, &BufferExtents, sizeof(BufferExtents));368if (BufferExtents > 0) {369LW->WriteAll(reinterpret_cast<char *>(&ExtentsRecord),370reinterpret_cast<char *>(&ExtentsRecord) +371sizeof(MetadataRecord));372LW->WriteAll(reinterpret_cast<char *>(B.Data),373reinterpret_cast<char *>(B.Data) + BufferExtents);374}375});376377atomic_store(&LogFlushStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED,378memory_order_release);379return XRayLogFlushStatus::XRAY_LOG_FLUSHED;380}381382XRayLogInitStatus fdrLoggingFinalize() XRAY_NEVER_INSTRUMENT {383s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED;384if (!atomic_compare_exchange_strong(&LoggingStatus, &CurrentStatus,385XRayLogInitStatus::XRAY_LOG_FINALIZING,386memory_order_release)) {387if (Verbosity())388Report("Cannot finalize log, implementation not initialized.\n");389return static_cast<XRayLogInitStatus>(CurrentStatus);390}391392// Do special things to make the log finalize itself, and not allow any more393// operations to be performed until re-initialized.394if (BQ == nullptr) {395if (Verbosity())396Report("Attempting to finalize an uninitialized global buffer!\n");397} else {398BQ->finalize();399}400401atomic_store(&LoggingStatus, XRayLogInitStatus::XRAY_LOG_FINALIZED,402memory_order_release);403return XRayLogInitStatus::XRAY_LOG_FINALIZED;404}405406struct TSCAndCPU {407uint64_t TSC = 0;408unsigned char CPU = 0;409};410411static TSCAndCPU getTimestamp() XRAY_NEVER_INSTRUMENT {412// We want to get the TSC as early as possible, so that we can check whether413// we've seen this CPU before. We also do it before we load anything else,414// to allow for forward progress with the scheduling.415TSCAndCPU Result;416417// Test once for required CPU features418static pthread_once_t OnceProbe = PTHREAD_ONCE_INIT;419static bool TSCSupported = true;420pthread_once(421&OnceProbe, +[] { TSCSupported = probeRequiredCPUFeatures(); });422423if (TSCSupported) {424Result.TSC = __xray::readTSC(Result.CPU);425} else {426// FIXME: This code needs refactoring as it appears in multiple locations427timespec TS;428int result = clock_gettime(CLOCK_REALTIME, &TS);429if (result != 0) {430Report("clock_gettime(2) return %d, errno=%d", result, int(errno));431TS = {0, 0};432}433Result.CPU = 0;434Result.TSC = TS.tv_sec * __xray::NanosecondsPerSecond + TS.tv_nsec;435}436return Result;437}438439thread_local atomic_uint8_t Running{0};440441static bool setupTLD(ThreadLocalData &TLD) XRAY_NEVER_INSTRUMENT {442// Check if we're finalizing, before proceeding.443{444auto Status = atomic_load(&LoggingStatus, memory_order_acquire);445if (Status == XRayLogInitStatus::XRAY_LOG_FINALIZING ||446Status == XRayLogInitStatus::XRAY_LOG_FINALIZED) {447if (TLD.Controller != nullptr) {448TLD.Controller->flush();449TLD.Controller = nullptr;450}451return false;452}453}454455if (UNLIKELY(TLD.Controller == nullptr)) {456// Set up the TLD buffer queue.457if (UNLIKELY(BQ == nullptr))458return false;459TLD.BQ = BQ;460461// Check that we have a valid buffer.462if (TLD.Buffer.Generation != BQ->generation() &&463TLD.BQ->releaseBuffer(TLD.Buffer) != BufferQueue::ErrorCode::Ok)464return false;465466// Set up a buffer, before setting up the log writer. Bail out on failure.467if (TLD.BQ->getBuffer(TLD.Buffer) != BufferQueue::ErrorCode::Ok)468return false;469470// Set up the Log Writer for this thread.471if (UNLIKELY(TLD.Writer == nullptr)) {472auto *LWStorage = reinterpret_cast<FDRLogWriter *>(&TLD.LWStorage);473new (LWStorage) FDRLogWriter(TLD.Buffer);474TLD.Writer = LWStorage;475} else {476TLD.Writer->resetRecord();477}478479auto *CStorage = reinterpret_cast<FDRController<> *>(&TLD.CStorage);480new (CStorage)481FDRController<>(TLD.BQ, TLD.Buffer, *TLD.Writer, clock_gettime,482atomic_load_relaxed(&ThresholdTicks));483TLD.Controller = CStorage;484}485486DCHECK_NE(TLD.Controller, nullptr);487return true;488}489490void fdrLoggingHandleArg0(int32_t FuncId,491XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {492auto TC = getTimestamp();493auto &TSC = TC.TSC;494auto &CPU = TC.CPU;495RecursionGuard Guard{Running};496if (!Guard)497return;498499auto &TLD = getThreadLocalData();500if (!setupTLD(TLD))501return;502503switch (Entry) {504case XRayEntryType::ENTRY:505case XRayEntryType::LOG_ARGS_ENTRY:506TLD.Controller->functionEnter(FuncId, TSC, CPU);507return;508case XRayEntryType::EXIT:509TLD.Controller->functionExit(FuncId, TSC, CPU);510return;511case XRayEntryType::TAIL:512TLD.Controller->functionTailExit(FuncId, TSC, CPU);513return;514case XRayEntryType::CUSTOM_EVENT:515case XRayEntryType::TYPED_EVENT:516break;517}518}519520void fdrLoggingHandleArg1(int32_t FuncId, XRayEntryType Entry,521uint64_t Arg) XRAY_NEVER_INSTRUMENT {522auto TC = getTimestamp();523auto &TSC = TC.TSC;524auto &CPU = TC.CPU;525RecursionGuard Guard{Running};526if (!Guard)527return;528529auto &TLD = getThreadLocalData();530if (!setupTLD(TLD))531return;532533switch (Entry) {534case XRayEntryType::ENTRY:535case XRayEntryType::LOG_ARGS_ENTRY:536TLD.Controller->functionEnterArg(FuncId, TSC, CPU, Arg);537return;538case XRayEntryType::EXIT:539TLD.Controller->functionExit(FuncId, TSC, CPU);540return;541case XRayEntryType::TAIL:542TLD.Controller->functionTailExit(FuncId, TSC, CPU);543return;544case XRayEntryType::CUSTOM_EVENT:545case XRayEntryType::TYPED_EVENT:546break;547}548}549550void fdrLoggingHandleCustomEvent(void *Event,551std::size_t EventSize) XRAY_NEVER_INSTRUMENT {552auto TC = getTimestamp();553auto &TSC = TC.TSC;554auto &CPU = TC.CPU;555RecursionGuard Guard{Running};556if (!Guard)557return;558559// Complain when we ever get at least one custom event that's larger than what560// we can possibly support.561if (EventSize >562static_cast<std::size_t>(std::numeric_limits<int32_t>::max())) {563static pthread_once_t Once = PTHREAD_ONCE_INIT;564pthread_once(565&Once, +[] {566Report("Custom event size too large; truncating to %d.\n",567std::numeric_limits<int32_t>::max());568});569}570571auto &TLD = getThreadLocalData();572if (!setupTLD(TLD))573return;574575int32_t ReducedEventSize = static_cast<int32_t>(EventSize);576TLD.Controller->customEvent(TSC, CPU, Event, ReducedEventSize);577}578579void fdrLoggingHandleTypedEvent(size_t EventType, const void *Event,580size_t EventSize) noexcept581XRAY_NEVER_INSTRUMENT {582auto TC = getTimestamp();583auto &TSC = TC.TSC;584auto &CPU = TC.CPU;585RecursionGuard Guard{Running};586if (!Guard)587return;588589// Complain when we ever get at least one typed event that's larger than what590// we can possibly support.591if (EventSize >592static_cast<std::size_t>(std::numeric_limits<int32_t>::max())) {593static pthread_once_t Once = PTHREAD_ONCE_INIT;594pthread_once(595&Once, +[] {596Report("Typed event size too large; truncating to %d.\n",597std::numeric_limits<int32_t>::max());598});599}600601auto &TLD = getThreadLocalData();602if (!setupTLD(TLD))603return;604605int32_t ReducedEventSize = static_cast<int32_t>(EventSize);606TLD.Controller->typedEvent(TSC, CPU, static_cast<uint16_t>(EventType), Event,607ReducedEventSize);608}609610XRayLogInitStatus fdrLoggingInit(size_t, size_t, void *Options,611size_t OptionsSize) XRAY_NEVER_INSTRUMENT {612if (Options == nullptr)613return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;614615s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;616if (!atomic_compare_exchange_strong(&LoggingStatus, &CurrentStatus,617XRayLogInitStatus::XRAY_LOG_INITIALIZING,618memory_order_release)) {619if (Verbosity())620Report("Cannot initialize already initialized implementation.\n");621return static_cast<XRayLogInitStatus>(CurrentStatus);622}623624if (Verbosity())625Report("Initializing FDR mode with options: %s\n",626static_cast<const char *>(Options));627628// TODO: Factor out the flags specific to the FDR mode implementation. For629// now, use the global/single definition of the flags, since the FDR mode630// flags are already defined there.631FlagParser FDRParser;632FDRFlags FDRFlags;633registerXRayFDRFlags(&FDRParser, &FDRFlags);634FDRFlags.setDefaults();635636// Override first from the general XRAY_DEFAULT_OPTIONS compiler-provided637// options until we migrate everyone to use the XRAY_FDR_OPTIONS638// compiler-provided options.639FDRParser.ParseString(useCompilerDefinedFlags());640FDRParser.ParseString(useCompilerDefinedFDRFlags());641auto *EnvOpts = GetEnv("XRAY_FDR_OPTIONS");642if (EnvOpts == nullptr)643EnvOpts = "";644FDRParser.ParseString(EnvOpts);645646// FIXME: Remove this when we fully remove the deprecated flags.647if (internal_strlen(EnvOpts) == 0) {648FDRFlags.func_duration_threshold_us =649flags()->xray_fdr_log_func_duration_threshold_us;650FDRFlags.grace_period_ms = flags()->xray_fdr_log_grace_period_ms;651}652653// The provided options should always override the compiler-provided and654// environment-variable defined options.655FDRParser.ParseString(static_cast<const char *>(Options));656*fdrFlags() = FDRFlags;657auto BufferSize = FDRFlags.buffer_size;658auto BufferMax = FDRFlags.buffer_max;659660if (BQ == nullptr) {661bool Success = false;662BQ = reinterpret_cast<BufferQueue *>(&BufferQueueStorage);663new (BQ) BufferQueue(BufferSize, BufferMax, Success);664if (!Success) {665Report("BufferQueue init failed.\n");666return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;667}668} else {669if (BQ->init(BufferSize, BufferMax) != BufferQueue::ErrorCode::Ok) {670if (Verbosity())671Report("Failed to re-initialize global buffer queue. Init failed.\n");672return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;673}674}675676static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;677pthread_once(678&OnceInit, +[] {679atomic_store(&TicksPerSec,680probeRequiredCPUFeatures() ? getTSCFrequency()681: __xray::NanosecondsPerSecond,682memory_order_release);683pthread_key_create(684&Key, +[](void *TLDPtr) {685if (TLDPtr == nullptr)686return;687auto &TLD = *reinterpret_cast<ThreadLocalData *>(TLDPtr);688if (TLD.BQ == nullptr)689return;690if (TLD.Buffer.Data == nullptr)691return;692auto EC = TLD.BQ->releaseBuffer(TLD.Buffer);693if (EC != BufferQueue::ErrorCode::Ok)694Report("At thread exit, failed to release buffer at %p; "695"error=%s\n",696TLD.Buffer.Data, BufferQueue::getErrorString(EC));697});698});699700atomic_store(&ThresholdTicks,701atomic_load_relaxed(&TicksPerSec) *702fdrFlags()->func_duration_threshold_us / 1000000,703memory_order_release);704// Arg1 handler should go in first to avoid concurrent code accidentally705// falling back to arg0 when it should have ran arg1.706__xray_set_handler_arg1(fdrLoggingHandleArg1);707// Install the actual handleArg0 handler after initialising the buffers.708__xray_set_handler(fdrLoggingHandleArg0);709__xray_set_customevent_handler(fdrLoggingHandleCustomEvent);710__xray_set_typedevent_handler(fdrLoggingHandleTypedEvent);711712// Install the buffer iterator implementation.713__xray_log_set_buffer_iterator(fdrIterator);714715atomic_store(&LoggingStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZED,716memory_order_release);717718if (Verbosity())719Report("XRay FDR init successful.\n");720return XRayLogInitStatus::XRAY_LOG_INITIALIZED;721}722723bool fdrLogDynamicInitializer() XRAY_NEVER_INSTRUMENT {724XRayLogImpl Impl{725fdrLoggingInit,726fdrLoggingFinalize,727fdrLoggingHandleArg0,728fdrLoggingFlush,729};730auto RegistrationResult = __xray_log_register_mode("xray-fdr", Impl);731if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK &&732Verbosity()) {733Report("Cannot register XRay FDR mode to 'xray-fdr'; error = %d\n",734RegistrationResult);735return false;736}737738if (flags()->xray_fdr_log ||739!internal_strcmp(flags()->xray_mode, "xray-fdr")) {740auto SelectResult = __xray_log_select_mode("xray-fdr");741if (SelectResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK &&742Verbosity()) {743Report("Cannot select XRay FDR mode as 'xray-fdr'; error = %d\n",744SelectResult);745return false;746}747}748return true;749}750751} // namespace __xray752753static auto UNUSED Unused = __xray::fdrLogDynamicInitializer();754755756