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
getThreadLocalData()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
fdrCommonHeaderInfo()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(...).
fdrIterator(const XRayBuffer B)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.
fdrLoggingFlush()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
fdrLoggingFinalize()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
getTimestamp()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
setupTLD(ThreadLocalData & TLD)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
fdrLoggingHandleArg0(int32_t FuncId,XRayEntryType Entry)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
fdrLoggingHandleArg1(int32_t FuncId,XRayEntryType Entry,uint64_t Arg)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
fdrLoggingHandleCustomEvent(void * Event,std::size_t EventSize)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
fdrLoggingHandleTypedEvent(size_t EventType,const void * Event,size_t EventSize)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
fdrLoggingInit(size_t,size_t,void * Options,size_t OptionsSize)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
fdrLogDynamicInitializer()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