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