1 //===-- xray_profiling.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 // This is the implementation of a profiling handler.
14 //===----------------------------------------------------------------------===//
18 #include "sanitizer_common/sanitizer_atomic.h"
19 #include "sanitizer_common/sanitizer_flags.h"
20 #include "xray/xray_interface.h"
21 #include "xray/xray_log_interface.h"
23 #include "xray_flags.h"
24 #include "xray_profile_collector.h"
25 #include "xray_profiling_flags.h"
26 #include "xray_recursion_guard.h"
28 #include "xray_utils.h"
35 constexpr uptr XRayProfilingVersion = 0x20180424;
37 struct XRayProfilingFileHeader {
38 const u64 MagicBytes = 0x7872617970726f66; // Identifier for XRay profiling
39 // files 'xrayprof' in hex.
40 const uptr Version = XRayProfilingVersion;
41 uptr Timestamp = 0; // System time in nanoseconds.
42 uptr PID = 0; // Process ID.
45 atomic_sint32_t ProfilerLogFlushStatus = {
46 XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
48 atomic_sint32_t ProfilerLogStatus = {XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};
50 SpinMutex ProfilerOptionsMutex;
52 struct alignas(64) ProfilingData {
53 FunctionCallTrie::Allocators *Allocators = nullptr;
54 FunctionCallTrie *FCT = nullptr;
57 static pthread_key_t ProfilingKey;
59 thread_local std::aligned_storage<sizeof(ProfilingData)>::type ThreadStorage{};
60 static ProfilingData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
61 thread_local auto ThreadOnce = [] {
62 new (&ThreadStorage) ProfilingData{};
63 pthread_setspecific(ProfilingKey, &ThreadStorage);
68 auto &TLD = *reinterpret_cast<ProfilingData *>(&ThreadStorage);
70 // We need to check whether the global flag to finalizing/finalized has been
71 // switched. If it is, then we ought to not actually initialise the data.
72 auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
73 if (Status == XRayLogInitStatus::XRAY_LOG_FINALIZING ||
74 Status == XRayLogInitStatus::XRAY_LOG_FINALIZED)
77 // If we're live, then we re-initialize TLD if the pointers are not null.
78 if (UNLIKELY(TLD.Allocators == nullptr && TLD.FCT == nullptr)) {
79 TLD.Allocators = reinterpret_cast<FunctionCallTrie::Allocators *>(
80 InternalAlloc(sizeof(FunctionCallTrie::Allocators)));
81 new (TLD.Allocators) FunctionCallTrie::Allocators();
82 *TLD.Allocators = FunctionCallTrie::InitAllocators();
83 TLD.FCT = reinterpret_cast<FunctionCallTrie *>(
84 InternalAlloc(sizeof(FunctionCallTrie)));
85 new (TLD.FCT) FunctionCallTrie(*TLD.Allocators);
91 static void cleanupTLD() XRAY_NEVER_INSTRUMENT {
92 auto &TLD = *reinterpret_cast<ProfilingData *>(&ThreadStorage);
93 if (TLD.Allocators != nullptr && TLD.FCT != nullptr) {
94 TLD.FCT->~FunctionCallTrie();
95 TLD.Allocators->~Allocators();
96 InternalFree(TLD.FCT);
97 InternalFree(TLD.Allocators);
99 TLD.Allocators = nullptr;
105 const char *profilingCompilerDefinedFlags() XRAY_NEVER_INSTRUMENT {
106 #ifdef XRAY_PROFILER_DEFAULT_OPTIONS
107 return SANITIZER_STRINGIFY(XRAY_PROFILER_DEFAULT_OPTIONS);
113 atomic_sint32_t ProfileFlushStatus = {
114 XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
116 XRayLogFlushStatus profilingFlush() XRAY_NEVER_INSTRUMENT {
117 if (atomic_load(&ProfilerLogStatus, memory_order_acquire) !=
118 XRayLogInitStatus::XRAY_LOG_FINALIZED) {
120 Report("Not flushing profiles, profiling not been finalized.\n");
121 return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
124 s32 Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
125 if (!atomic_compare_exchange_strong(&ProfilerLogFlushStatus, &Result,
126 XRayLogFlushStatus::XRAY_LOG_FLUSHING,
127 memory_order_acq_rel)) {
129 Report("Not flushing profiles, implementation still finalizing.\n");
132 // At this point, we'll create the file that will contain the profile, but
133 // only if the options say so.
134 if (!profilingFlags()->no_flush) {
135 // First check whether we have data in the profile collector service
136 // before we try and write anything down.
137 XRayBuffer B = profileCollectorService::nextBuffer({nullptr, 0});
138 if (B.Data == nullptr) {
140 Report("profiling: No data to flush.\n");
145 Report("profiling: Failed to flush to file, dropping data.\n");
147 XRayProfilingFileHeader Header;
148 Header.Timestamp = NanoTime();
149 Header.PID = internal_getpid();
150 retryingWriteAll(Fd, reinterpret_cast<const char *>(&Header),
151 reinterpret_cast<const char *>(&Header) +
154 // Now for each of the threads, write out the profile data as we would
155 // see it in memory, verbatim.
156 while (B.Data != nullptr && B.Size != 0) {
157 retryingWriteAll(Fd, reinterpret_cast<const char *>(B.Data),
158 reinterpret_cast<const char *>(B.Data) + B.Size);
159 B = profileCollectorService::nextBuffer(B);
161 // Then we close out the file.
167 profileCollectorService::reset();
169 // Flush the current thread's local data structures as well.
172 atomic_store(&ProfilerLogStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED,
173 memory_order_release);
175 return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
180 thread_local atomic_uint8_t ReentranceGuard{0};
182 static void postCurrentThreadFCT(ProfilingData &TLD) {
183 if (TLD.Allocators == nullptr || TLD.FCT == nullptr)
186 profileCollectorService::post(*TLD.FCT, GetTid());
192 void profilingHandleArg0(int32_t FuncId,
193 XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {
195 auto TSC = readTSC(CPU);
196 RecursionGuard G(ReentranceGuard);
200 auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
201 auto &TLD = getThreadLocalData();
202 if (UNLIKELY(Status == XRayLogInitStatus::XRAY_LOG_FINALIZED ||
203 Status == XRayLogInitStatus::XRAY_LOG_FINALIZING)) {
204 postCurrentThreadFCT(TLD);
209 case XRayEntryType::ENTRY:
210 case XRayEntryType::LOG_ARGS_ENTRY:
211 TLD.FCT->enterFunction(FuncId, TSC);
213 case XRayEntryType::EXIT:
214 case XRayEntryType::TAIL:
215 TLD.FCT->exitFunction(FuncId, TSC);
218 // FIXME: Handle bugs.
223 void profilingHandleArg1(int32_t FuncId, XRayEntryType Entry,
224 uint64_t) XRAY_NEVER_INSTRUMENT {
225 return profilingHandleArg0(FuncId, Entry);
228 XRayLogInitStatus profilingFinalize() XRAY_NEVER_INSTRUMENT {
229 s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED;
230 if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
231 XRayLogInitStatus::XRAY_LOG_FINALIZING,
232 memory_order_release)) {
234 Report("Cannot finalize profile, the profiling is not initialized.\n");
235 return static_cast<XRayLogInitStatus>(CurrentStatus);
238 // Wait a grace period to allow threads to see that we're finalizing.
239 SleepForMillis(profilingFlags()->grace_period_ms);
241 // We also want to make sure that the current thread's data is cleaned up,
243 auto &TLD = getThreadLocalData();
244 postCurrentThreadFCT(TLD);
246 // Then we force serialize the log data.
247 profileCollectorService::serialize();
249 atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_FINALIZED,
250 memory_order_release);
251 return XRayLogInitStatus::XRAY_LOG_FINALIZED;
255 profilingLoggingInit(size_t BufferSize, size_t BufferMax, void *Options,
256 size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
257 if (BufferSize != 0 || BufferMax != 0) {
259 Report("__xray_log_init() being used, and is unsupported. Use "
260 "__xray_log_init_mode(...) instead. Bailing out.");
261 return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
264 s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
265 if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
266 XRayLogInitStatus::XRAY_LOG_INITIALIZING,
267 memory_order_release)) {
269 Report("Cannot initialize already initialised profiling "
270 "implementation.\n");
271 return static_cast<XRayLogInitStatus>(CurrentStatus);
275 SpinMutexLock Lock(&ProfilerOptionsMutex);
276 FlagParser ConfigParser;
279 registerProfilerFlags(&ConfigParser, &Flags);
280 ConfigParser.ParseString(profilingCompilerDefinedFlags());
281 const char *Env = GetEnv("XRAY_PROFILING_OPTIONS");
284 ConfigParser.ParseString(Env);
286 // Then parse the configuration string provided.
287 ConfigParser.ParseString(static_cast<const char *>(Options));
289 ReportUnrecognizedFlags();
290 *profilingFlags() = Flags;
293 // We need to reset the profile data collection implementation now.
294 profileCollectorService::reset();
296 // We need to set up the exit handlers.
297 static pthread_once_t Once = PTHREAD_ONCE_INIT;
298 pthread_once(&Once, +[] {
299 pthread_key_create(&ProfilingKey, +[](void *P) {
300 // This is the thread-exit handler.
301 auto &TLD = *reinterpret_cast<ProfilingData *>(P);
302 if (TLD.Allocators == nullptr && TLD.FCT == nullptr)
305 postCurrentThreadFCT(TLD);
308 // We also need to set up an exit handler, so that we can get the profile
309 // information at exit time. We use the C API to do this, to not rely on C++
310 // ABI functions for registering exit handlers.
312 // Finalize and flush.
313 if (profilingFinalize() != XRAY_LOG_FINALIZED) {
317 if (profilingFlush() != XRAY_LOG_FLUSHED) {
322 Report("XRay Profile flushed at exit.");
326 __xray_log_set_buffer_iterator(profileCollectorService::nextBuffer);
327 __xray_set_handler(profilingHandleArg0);
328 __xray_set_handler_arg1(profilingHandleArg1);
330 atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZED,
331 memory_order_release);
333 Report("XRay Profiling init successful.\n");
335 return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
338 bool profilingDynamicInitializer() XRAY_NEVER_INSTRUMENT {
339 // Set up the flag defaults from the static defaults and the
340 // compiler-provided defaults.
342 SpinMutexLock Lock(&ProfilerOptionsMutex);
343 auto *F = profilingFlags();
345 FlagParser ProfilingParser;
346 registerProfilerFlags(&ProfilingParser, F);
347 ProfilingParser.ParseString(profilingCompilerDefinedFlags());
351 profilingLoggingInit,
356 auto RegistrationResult = __xray_log_register_mode("xray-profiling", Impl);
357 if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK) {
359 Report("Cannot register XRay Profiling mode to 'xray-profiling'; error = "
365 if (!internal_strcmp(flags()->xray_mode, "xray-profiling"))
366 __xray_log_select_mode("xray_profiling");
370 } // namespace __xray
372 static auto UNUSED Unused = __xray::profilingDynamicInitializer();