1 //===-- xray_fdr_logging.cc ------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file is a part of XRay, a dynamic runtime instrumentation system.
12 // Here we implement the Flight Data Recorder mode for XRay, where we use
13 // compact structures to store records in memory as well as when writing out the
16 //===----------------------------------------------------------------------===//
17 #include "xray_fdr_logging.h"
22 #include <sys/syscall.h>
26 #include <unordered_map>
28 #include "sanitizer_common/sanitizer_atomic.h"
29 #include "sanitizer_common/sanitizer_common.h"
30 #include "xray/xray_interface.h"
31 #include "xray/xray_records.h"
32 #include "xray_buffer_queue.h"
33 #include "xray_defs.h"
34 #include "xray_fdr_logging_impl.h"
35 #include "xray_flags.h"
37 #include "xray_utils.h"
41 // Global BufferQueue.
42 std::shared_ptr<BufferQueue> BQ;
44 __sanitizer::atomic_sint32_t LoggingStatus = {
45 XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};
47 __sanitizer::atomic_sint32_t LogFlushStatus = {
48 XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
50 std::unique_ptr<FDRLoggingOptions> FDROptions;
52 XRayLogInitStatus fdrLoggingInit(std::size_t BufferSize, std::size_t BufferMax,
54 size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
55 if (OptionsSize != sizeof(FDRLoggingOptions))
56 return static_cast<XRayLogInitStatus>(__sanitizer::atomic_load(
57 &LoggingStatus, __sanitizer::memory_order_acquire));
58 s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
59 if (!__sanitizer::atomic_compare_exchange_strong(
60 &LoggingStatus, &CurrentStatus,
61 XRayLogInitStatus::XRAY_LOG_INITIALIZING,
62 __sanitizer::memory_order_release))
63 return static_cast<XRayLogInitStatus>(CurrentStatus);
65 FDROptions.reset(new FDRLoggingOptions());
66 memcpy(FDROptions.get(), Options, OptionsSize);
68 BQ = std::make_shared<BufferQueue>(BufferSize, BufferMax, Success);
70 Report("BufferQueue init failed.\n");
71 return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
74 // Install the actual handleArg0 handler after initialising the buffers.
75 __xray_set_handler(fdrLoggingHandleArg0);
77 __sanitizer::atomic_store(&LoggingStatus,
78 XRayLogInitStatus::XRAY_LOG_INITIALIZED,
79 __sanitizer::memory_order_release);
80 Report("XRay FDR init successful.\n");
81 return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
84 // Must finalize before flushing.
85 XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
86 if (__sanitizer::atomic_load(&LoggingStatus,
87 __sanitizer::memory_order_acquire) !=
88 XRayLogInitStatus::XRAY_LOG_FINALIZED)
89 return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
91 s32 Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
92 if (!__sanitizer::atomic_compare_exchange_strong(
93 &LogFlushStatus, &Result, XRayLogFlushStatus::XRAY_LOG_FLUSHING,
94 __sanitizer::memory_order_release))
95 return static_cast<XRayLogFlushStatus>(Result);
97 // Make a copy of the BufferQueue pointer to prevent other threads that may be
98 // resetting it from blowing away the queue prematurely while we're dealing
102 // We write out the file in the following format:
104 // 1) We write down the XRay file header with version 1, type FDR_LOG.
105 // 2) Then we use the 'apply' member of the BufferQueue that's live, to
106 // ensure that at this point in time we write down the buffers that have
107 // been released (and marked "used") -- we dump the full buffer for now
108 // (fixed-sized) and let the tools reading the buffers deal with the data
111 int Fd = FDROptions->Fd;
115 auto Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
116 __sanitizer::atomic_store(&LogFlushStatus, Result,
117 __sanitizer::memory_order_release);
121 // Test for required CPU features and cache the cycle frequency
122 static bool TSCSupported = probeRequiredCPUFeatures();
123 static uint64_t CycleFrequency = TSCSupported ? getTSCFrequency()
124 : __xray::NanosecondsPerSecond;
126 XRayFileHeader Header;
128 Header.Type = FileTypes::FDR_LOG;
129 Header.CycleFrequency = CycleFrequency;
130 // FIXME: Actually check whether we have 'constant_tsc' and 'nonstop_tsc'
131 // before setting the values in the header.
132 Header.ConstantTSC = 1;
133 Header.NonstopTSC = 1;
134 Header.FdrData = FdrAdditionalHeaderData{LocalBQ->ConfiguredBufferSize()};
135 retryingWriteAll(Fd, reinterpret_cast<char *>(&Header),
136 reinterpret_cast<char *>(&Header) + sizeof(Header));
138 LocalBQ->apply([&](const BufferQueue::Buffer &B) {
139 uint64_t BufferSize = B.Size;
140 if (BufferSize > 0) {
141 retryingWriteAll(Fd, reinterpret_cast<char *>(B.Buffer),
142 reinterpret_cast<char *>(B.Buffer) + B.Size);
145 __sanitizer::atomic_store(&LogFlushStatus,
146 XRayLogFlushStatus::XRAY_LOG_FLUSHED,
147 __sanitizer::memory_order_release);
148 return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
151 XRayLogInitStatus fdrLoggingFinalize() XRAY_NEVER_INSTRUMENT {
152 s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED;
153 if (!__sanitizer::atomic_compare_exchange_strong(
154 &LoggingStatus, &CurrentStatus,
155 XRayLogInitStatus::XRAY_LOG_FINALIZING,
156 __sanitizer::memory_order_release))
157 return static_cast<XRayLogInitStatus>(CurrentStatus);
159 // Do special things to make the log finalize itself, and not allow any more
160 // operations to be performed until re-initialized.
163 __sanitizer::atomic_store(&LoggingStatus,
164 XRayLogInitStatus::XRAY_LOG_FINALIZED,
165 __sanitizer::memory_order_release);
166 return XRayLogInitStatus::XRAY_LOG_FINALIZED;
169 XRayLogInitStatus fdrLoggingReset() XRAY_NEVER_INSTRUMENT {
170 s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_FINALIZED;
171 if (__sanitizer::atomic_compare_exchange_strong(
172 &LoggingStatus, &CurrentStatus,
173 XRayLogInitStatus::XRAY_LOG_INITIALIZED,
174 __sanitizer::memory_order_release))
175 return static_cast<XRayLogInitStatus>(CurrentStatus);
177 // Release the in-memory buffer queue.
180 // Spin until the flushing status is flushed.
181 s32 CurrentFlushingStatus = XRayLogFlushStatus::XRAY_LOG_FLUSHED;
182 while (__sanitizer::atomic_compare_exchange_weak(
183 &LogFlushStatus, &CurrentFlushingStatus,
184 XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING,
185 __sanitizer::memory_order_release)) {
186 if (CurrentFlushingStatus == XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING)
188 CurrentFlushingStatus = XRayLogFlushStatus::XRAY_LOG_FLUSHED;
191 // At this point, we know that the status is flushed, and that we can assume
192 return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
195 void fdrLoggingHandleArg0(int32_t FuncId,
196 XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {
197 // We want to get the TSC as early as possible, so that we can check whether
198 // we've seen this CPU before. We also do it before we load anything else, to
199 // allow for forward progress with the scheduling.
203 // Test once for required CPU features
204 static bool TSCSupported = probeRequiredCPUFeatures();
207 TSC = __xray::readTSC(CPU);
209 // FIXME: This code needs refactoring as it appears in multiple locations
211 int result = clock_gettime(CLOCK_REALTIME, &TS);
213 Report("clock_gettime(2) return %d, errno=%d", result, int(errno));
217 TSC = TS.tv_sec * __xray::NanosecondsPerSecond + TS.tv_nsec;
220 __xray_fdr_internal::processFunctionHook(FuncId, Entry, TSC, CPU,
221 clock_gettime, LoggingStatus, BQ);
224 } // namespace __xray
226 static auto UNUSED Unused = [] {
227 using namespace __xray;
228 if (flags()->xray_fdr_log) {
230 fdrLoggingInit, fdrLoggingFinalize, fdrLoggingHandleArg0,
233 __xray_set_log_impl(Impl);