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 atomic_sint32_t ProfilerLogFlushStatus = {
36 XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
38 atomic_sint32_t ProfilerLogStatus = {XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};
40 SpinMutex ProfilerOptionsMutex;
42 struct alignas(64) ProfilingData {
43 FunctionCallTrie::Allocators *Allocators = nullptr;
44 FunctionCallTrie *FCT = nullptr;
47 static pthread_key_t ProfilingKey;
49 thread_local std::aligned_storage<sizeof(ProfilingData)>::type ThreadStorage{};
50 static ProfilingData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
51 thread_local auto ThreadOnce = [] {
52 new (&ThreadStorage) ProfilingData{};
53 pthread_setspecific(ProfilingKey, &ThreadStorage);
58 auto &TLD = *reinterpret_cast<ProfilingData *>(&ThreadStorage);
60 // We need to check whether the global flag to finalizing/finalized has been
61 // switched. If it is, then we ought to not actually initialise the data.
62 auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
63 if (Status == XRayLogInitStatus::XRAY_LOG_FINALIZING ||
64 Status == XRayLogInitStatus::XRAY_LOG_FINALIZED)
67 // If we're live, then we re-initialize TLD if the pointers are not null.
68 if (UNLIKELY(TLD.Allocators == nullptr && TLD.FCT == nullptr)) {
69 TLD.Allocators = reinterpret_cast<FunctionCallTrie::Allocators *>(
70 InternalAlloc(sizeof(FunctionCallTrie::Allocators)));
71 new (TLD.Allocators) FunctionCallTrie::Allocators();
72 *TLD.Allocators = FunctionCallTrie::InitAllocators();
73 TLD.FCT = reinterpret_cast<FunctionCallTrie *>(
74 InternalAlloc(sizeof(FunctionCallTrie)));
75 new (TLD.FCT) FunctionCallTrie(*TLD.Allocators);
81 static void cleanupTLD() XRAY_NEVER_INSTRUMENT {
82 auto &TLD = *reinterpret_cast<ProfilingData *>(&ThreadStorage);
83 if (TLD.Allocators != nullptr && TLD.FCT != nullptr) {
84 TLD.FCT->~FunctionCallTrie();
85 TLD.Allocators->~Allocators();
86 InternalFree(TLD.FCT);
87 InternalFree(TLD.Allocators);
89 TLD.Allocators = nullptr;
95 const char *profilingCompilerDefinedFlags() XRAY_NEVER_INSTRUMENT {
96 #ifdef XRAY_PROFILER_DEFAULT_OPTIONS
97 return SANITIZER_STRINGIFY(XRAY_PROFILER_DEFAULT_OPTIONS);
103 atomic_sint32_t ProfileFlushStatus = {
104 XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
106 XRayLogFlushStatus profilingFlush() XRAY_NEVER_INSTRUMENT {
107 if (atomic_load(&ProfilerLogStatus, memory_order_acquire) !=
108 XRayLogInitStatus::XRAY_LOG_FINALIZED) {
110 Report("Not flushing profiles, profiling not been finalized.\n");
111 return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
114 s32 Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
115 if (!atomic_compare_exchange_strong(&ProfilerLogFlushStatus, &Result,
116 XRayLogFlushStatus::XRAY_LOG_FLUSHING,
117 memory_order_acq_rel)) {
119 Report("Not flushing profiles, implementation still finalizing.\n");
122 // At this point, we'll create the file that will contain the profile, but
123 // only if the options say so.
124 if (!profilingFlags()->no_flush) {
125 // First check whether we have data in the profile collector service
126 // before we try and write anything down.
127 XRayBuffer B = profileCollectorService::nextBuffer({nullptr, 0});
128 if (B.Data == nullptr) {
130 Report("profiling: No data to flush.\n");
135 Report("profiling: Failed to flush to file, dropping data.\n");
137 // Now for each of the buffers, write out the profile data as we would
138 // see it in memory, verbatim.
139 while (B.Data != nullptr && B.Size != 0) {
140 retryingWriteAll(Fd, reinterpret_cast<const char *>(B.Data),
141 reinterpret_cast<const char *>(B.Data) + B.Size);
142 B = profileCollectorService::nextBuffer(B);
144 // Then we close out the file.
150 profileCollectorService::reset();
152 // Flush the current thread's local data structures as well.
155 atomic_store(&ProfilerLogStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED,
156 memory_order_release);
158 return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
163 thread_local atomic_uint8_t ReentranceGuard{0};
165 static void postCurrentThreadFCT(ProfilingData &TLD) {
166 if (TLD.Allocators == nullptr || TLD.FCT == nullptr)
169 profileCollectorService::post(*TLD.FCT, GetTid());
175 void profilingHandleArg0(int32_t FuncId,
176 XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {
178 auto TSC = readTSC(CPU);
179 RecursionGuard G(ReentranceGuard);
183 auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
184 auto &TLD = getThreadLocalData();
185 if (UNLIKELY(Status == XRayLogInitStatus::XRAY_LOG_FINALIZED ||
186 Status == XRayLogInitStatus::XRAY_LOG_FINALIZING)) {
187 postCurrentThreadFCT(TLD);
192 case XRayEntryType::ENTRY:
193 case XRayEntryType::LOG_ARGS_ENTRY:
194 TLD.FCT->enterFunction(FuncId, TSC);
196 case XRayEntryType::EXIT:
197 case XRayEntryType::TAIL:
198 TLD.FCT->exitFunction(FuncId, TSC);
201 // FIXME: Handle bugs.
206 void profilingHandleArg1(int32_t FuncId, XRayEntryType Entry,
207 uint64_t) XRAY_NEVER_INSTRUMENT {
208 return profilingHandleArg0(FuncId, Entry);
211 XRayLogInitStatus profilingFinalize() XRAY_NEVER_INSTRUMENT {
212 s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED;
213 if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
214 XRayLogInitStatus::XRAY_LOG_FINALIZING,
215 memory_order_release)) {
217 Report("Cannot finalize profile, the profiling is not initialized.\n");
218 return static_cast<XRayLogInitStatus>(CurrentStatus);
221 // Wait a grace period to allow threads to see that we're finalizing.
222 SleepForMillis(profilingFlags()->grace_period_ms);
224 // We also want to make sure that the current thread's data is cleaned up,
226 auto &TLD = getThreadLocalData();
227 postCurrentThreadFCT(TLD);
229 // Then we force serialize the log data.
230 profileCollectorService::serialize();
232 atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_FINALIZED,
233 memory_order_release);
234 return XRayLogInitStatus::XRAY_LOG_FINALIZED;
238 profilingLoggingInit(size_t BufferSize, size_t BufferMax, void *Options,
239 size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
240 if (BufferSize != 0 || BufferMax != 0) {
242 Report("__xray_log_init() being used, and is unsupported. Use "
243 "__xray_log_init_mode(...) instead. Bailing out.");
244 return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
247 s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
248 if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
249 XRayLogInitStatus::XRAY_LOG_INITIALIZING,
250 memory_order_release)) {
252 Report("Cannot initialize already initialised profiling "
253 "implementation.\n");
254 return static_cast<XRayLogInitStatus>(CurrentStatus);
258 SpinMutexLock Lock(&ProfilerOptionsMutex);
259 FlagParser ConfigParser;
262 registerProfilerFlags(&ConfigParser, &Flags);
263 ConfigParser.ParseString(profilingCompilerDefinedFlags());
264 const char *Env = GetEnv("XRAY_PROFILING_OPTIONS");
267 ConfigParser.ParseString(Env);
269 // Then parse the configuration string provided.
270 ConfigParser.ParseString(static_cast<const char *>(Options));
272 ReportUnrecognizedFlags();
273 *profilingFlags() = Flags;
276 // We need to reset the profile data collection implementation now.
277 profileCollectorService::reset();
279 // We need to set up the exit handlers.
280 static pthread_once_t Once = PTHREAD_ONCE_INIT;
281 pthread_once(&Once, +[] {
282 pthread_key_create(&ProfilingKey, +[](void *P) {
283 // This is the thread-exit handler.
284 auto &TLD = *reinterpret_cast<ProfilingData *>(P);
285 if (TLD.Allocators == nullptr && TLD.FCT == nullptr)
288 postCurrentThreadFCT(TLD);
291 // We also need to set up an exit handler, so that we can get the profile
292 // information at exit time. We use the C API to do this, to not rely on C++
293 // ABI functions for registering exit handlers.
295 // Finalize and flush.
296 if (profilingFinalize() != XRAY_LOG_FINALIZED) {
300 if (profilingFlush() != XRAY_LOG_FLUSHED) {
305 Report("XRay Profile flushed at exit.");
309 __xray_log_set_buffer_iterator(profileCollectorService::nextBuffer);
310 __xray_set_handler(profilingHandleArg0);
311 __xray_set_handler_arg1(profilingHandleArg1);
313 atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZED,
314 memory_order_release);
316 Report("XRay Profiling init successful.\n");
318 return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
321 bool profilingDynamicInitializer() XRAY_NEVER_INSTRUMENT {
322 // Set up the flag defaults from the static defaults and the
323 // compiler-provided defaults.
325 SpinMutexLock Lock(&ProfilerOptionsMutex);
326 auto *F = profilingFlags();
328 FlagParser ProfilingParser;
329 registerProfilerFlags(&ProfilingParser, F);
330 ProfilingParser.ParseString(profilingCompilerDefinedFlags());
334 profilingLoggingInit,
339 auto RegistrationResult = __xray_log_register_mode("xray-profiling", Impl);
340 if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK) {
342 Report("Cannot register XRay Profiling mode to 'xray-profiling'; error = "
348 if (!internal_strcmp(flags()->xray_mode, "xray-profiling"))
349 __xray_log_select_mode("xray_profiling");
353 } // namespace __xray
355 static auto UNUSED Unused = __xray::profilingDynamicInitializer();