//===-- xray_fdr_logging.cpp -----------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file is a part of XRay, a dynamic runtime instrumentation system. // // Here we implement the Flight Data Recorder mode for XRay, where we use // compact structures to store records in memory as well as when writing out the // data to files. // //===----------------------------------------------------------------------===// #include "xray_fdr_logging.h" #include <cassert> #include <cstddef> #include <errno.h> #include <limits> #include <memory> #include <pthread.h> #include <sys/time.h> #include <time.h> #include <unistd.h> #include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "xray/xray_interface.h" #include "xray/xray_records.h" #include "xray_allocator.h" #include "xray_buffer_queue.h" #include "xray_defs.h" #include "xray_fdr_controller.h" #include "xray_fdr_flags.h" #include "xray_fdr_log_writer.h" #include "xray_flags.h" #include "xray_recursion_guard.h" #include "xray_tsc.h" #include "xray_utils.h" namespace __xray { static atomic_sint32_t LoggingStatus = …; namespace { // Group together thread-local-data in a struct, then hide it behind a function // call so that it can be initialized on first use instead of as a global. We // force the alignment to 64-bytes for x86 cache line alignment, as this // structure is used in the hot path of implementation. struct XRAY_TLS_ALIGNAS(64) ThreadLocalData { … }; } // namespace static_assert …; // Use a global pthread key to identify thread-local data for logging. static pthread_key_t Key; // Global BufferQueue. static std::byte BufferQueueStorage[sizeof(BufferQueue)]; static BufferQueue *BQ = …; // Global thresholds for function durations. static atomic_uint64_t ThresholdTicks{ … }; // Global for ticks per second. static atomic_uint64_t TicksPerSec{ … }; static atomic_sint32_t LogFlushStatus = …; // This function will initialize the thread-local data structure used by the FDR // logging implementation and return a reference to it. The implementation // details require a bit of care to maintain. // // First, some requirements on the implementation in general: // // - XRay handlers should not call any memory allocation routines that may // delegate to an instrumented implementation. This means functions like // malloc() and free() should not be called while instrumenting. // // - We would like to use some thread-local data initialized on first-use of // the XRay instrumentation. These allow us to implement unsynchronized // routines that access resources associated with the thread. // // The implementation here uses a few mechanisms that allow us to provide both // the requirements listed above. We do this by: // // 1. Using a thread-local aligned storage buffer for representing the // ThreadLocalData struct. This data will be uninitialized memory by // design. // // 2. Not requiring a thread exit handler/implementation, keeping the // thread-local as purely a collection of references/data that do not // require cleanup. // // We're doing this to avoid using a `thread_local` object that has a // non-trivial destructor, because the C++ runtime might call std::malloc(...) // to register calls to destructors. Deadlocks may arise when, for example, an // externally provided malloc implementation is XRay instrumented, and // initializing the thread-locals involves calling into malloc. A malloc // implementation that does global synchronization might be holding a lock for a // critical section, calling a function that might be XRay instrumented (and // thus in turn calling into malloc by virtue of registration of the // thread_local's destructor). #if XRAY_HAS_TLS_ALIGNAS static_assert …; #endif static ThreadLocalData &getThreadLocalData() { … } static XRayFileHeader &fdrCommonHeaderInfo() { … } // This is the iterator implementation, which knows how to handle FDR-mode // specific buffers. This is used as an implementation of the iterator function // needed by __xray_set_buffer_iterator(...). It maintains a global state of the // buffer iteration for the currently installed FDR mode buffers. In particular: // // - If the argument represents the initial state of XRayBuffer ({nullptr, 0}) // then the iterator returns the header information. // - If the argument represents the header information ({address of header // info, size of the header info}) then it returns the first FDR buffer's // address and extents. // - It will keep returning the next buffer and extents as there are more // buffers to process. When the input represents the last buffer, it will // return the initial state to signal completion ({nullptr, 0}). // // See xray/xray_log_interface.h for more details on the requirements for the // implementations of __xray_set_buffer_iterator(...) and // __xray_log_process_buffers(...). XRayBuffer fdrIterator(const XRayBuffer B) { … } // Must finalize before flushing. XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT { … } XRayLogInitStatus fdrLoggingFinalize() XRAY_NEVER_INSTRUMENT { … } struct TSCAndCPU { … }; static TSCAndCPU getTimestamp() XRAY_NEVER_INSTRUMENT { … } thread_local atomic_uint8_t Running{ … }; static bool setupTLD(ThreadLocalData &TLD) XRAY_NEVER_INSTRUMENT { … } void fdrLoggingHandleArg0(int32_t FuncId, XRayEntryType Entry) XRAY_NEVER_INSTRUMENT { … } void fdrLoggingHandleArg1(int32_t FuncId, XRayEntryType Entry, uint64_t Arg) XRAY_NEVER_INSTRUMENT { … } void fdrLoggingHandleCustomEvent(void *Event, std::size_t EventSize) XRAY_NEVER_INSTRUMENT { … } void fdrLoggingHandleTypedEvent(size_t EventType, const void *Event, size_t EventSize) noexcept XRAY_NEVER_INSTRUMENT { … } XRayLogInitStatus fdrLoggingInit(size_t, size_t, void *Options, size_t OptionsSize) XRAY_NEVER_INSTRUMENT { … } bool fdrLogDynamicInitializer() XRAY_NEVER_INSTRUMENT { … } } // namespace __xray static auto UNUSED Unused = …;