xref: /openbsd-src/gnu/llvm/compiler-rt/lib/xray/xray_profiling.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- xray_profiling.cpp --------------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of XRay, a dynamic runtime instrumentation system.
103cab2bb3Spatrick //
113cab2bb3Spatrick // This is the implementation of a profiling handler.
123cab2bb3Spatrick //
133cab2bb3Spatrick //===----------------------------------------------------------------------===//
143cab2bb3Spatrick #include <memory>
153cab2bb3Spatrick #include <time.h>
163cab2bb3Spatrick 
173cab2bb3Spatrick #include "sanitizer_common/sanitizer_atomic.h"
183cab2bb3Spatrick #include "sanitizer_common/sanitizer_flags.h"
193cab2bb3Spatrick #include "xray/xray_interface.h"
203cab2bb3Spatrick #include "xray/xray_log_interface.h"
213cab2bb3Spatrick #include "xray_buffer_queue.h"
223cab2bb3Spatrick #include "xray_flags.h"
233cab2bb3Spatrick #include "xray_profile_collector.h"
243cab2bb3Spatrick #include "xray_profiling_flags.h"
253cab2bb3Spatrick #include "xray_recursion_guard.h"
263cab2bb3Spatrick #include "xray_tsc.h"
273cab2bb3Spatrick #include "xray_utils.h"
283cab2bb3Spatrick #include <pthread.h>
293cab2bb3Spatrick 
303cab2bb3Spatrick namespace __xray {
313cab2bb3Spatrick 
323cab2bb3Spatrick namespace {
333cab2bb3Spatrick 
343cab2bb3Spatrick static atomic_sint32_t ProfilerLogFlushStatus = {
353cab2bb3Spatrick     XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
363cab2bb3Spatrick 
373cab2bb3Spatrick static atomic_sint32_t ProfilerLogStatus = {
383cab2bb3Spatrick     XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};
393cab2bb3Spatrick 
403cab2bb3Spatrick static SpinMutex ProfilerOptionsMutex;
413cab2bb3Spatrick 
423cab2bb3Spatrick struct ProfilingData {
433cab2bb3Spatrick   atomic_uintptr_t Allocators;
443cab2bb3Spatrick   atomic_uintptr_t FCT;
453cab2bb3Spatrick };
463cab2bb3Spatrick 
473cab2bb3Spatrick static pthread_key_t ProfilingKey;
483cab2bb3Spatrick 
493cab2bb3Spatrick // We use a global buffer queue, which gets initialized once at initialisation
503cab2bb3Spatrick // time, and gets reset when profiling is "done".
513cab2bb3Spatrick static std::aligned_storage<sizeof(BufferQueue), alignof(BufferQueue)>::type
523cab2bb3Spatrick     BufferQueueStorage;
533cab2bb3Spatrick static BufferQueue *BQ = nullptr;
543cab2bb3Spatrick 
553cab2bb3Spatrick thread_local FunctionCallTrie::Allocators::Buffers ThreadBuffers;
563cab2bb3Spatrick thread_local std::aligned_storage<sizeof(FunctionCallTrie::Allocators),
573cab2bb3Spatrick                                   alignof(FunctionCallTrie::Allocators)>::type
583cab2bb3Spatrick     AllocatorsStorage;
593cab2bb3Spatrick thread_local std::aligned_storage<sizeof(FunctionCallTrie),
603cab2bb3Spatrick                                   alignof(FunctionCallTrie)>::type
613cab2bb3Spatrick     FunctionCallTrieStorage;
623cab2bb3Spatrick thread_local ProfilingData TLD{{0}, {0}};
633cab2bb3Spatrick thread_local atomic_uint8_t ReentranceGuard{0};
643cab2bb3Spatrick 
653cab2bb3Spatrick // We use a separate guard for ensuring that for this thread, if we're already
663cab2bb3Spatrick // cleaning up, that any signal handlers don't attempt to cleanup nor
673cab2bb3Spatrick // initialise.
683cab2bb3Spatrick thread_local atomic_uint8_t TLDInitGuard{0};
693cab2bb3Spatrick 
703cab2bb3Spatrick // We also use a separate latch to signal that the thread is exiting, and
713cab2bb3Spatrick // non-essential work should be ignored (things like recording events, etc.).
723cab2bb3Spatrick thread_local atomic_uint8_t ThreadExitingLatch{0};
733cab2bb3Spatrick 
getThreadLocalData()743cab2bb3Spatrick static ProfilingData *getThreadLocalData() XRAY_NEVER_INSTRUMENT {
753cab2bb3Spatrick   thread_local auto ThreadOnce = []() XRAY_NEVER_INSTRUMENT {
763cab2bb3Spatrick     pthread_setspecific(ProfilingKey, &TLD);
773cab2bb3Spatrick     return false;
783cab2bb3Spatrick   }();
793cab2bb3Spatrick   (void)ThreadOnce;
803cab2bb3Spatrick 
813cab2bb3Spatrick   RecursionGuard TLDInit(TLDInitGuard);
823cab2bb3Spatrick   if (!TLDInit)
833cab2bb3Spatrick     return nullptr;
843cab2bb3Spatrick 
853cab2bb3Spatrick   if (atomic_load_relaxed(&ThreadExitingLatch))
863cab2bb3Spatrick     return nullptr;
873cab2bb3Spatrick 
883cab2bb3Spatrick   uptr Allocators = 0;
893cab2bb3Spatrick   if (atomic_compare_exchange_strong(&TLD.Allocators, &Allocators, 1,
903cab2bb3Spatrick                                      memory_order_acq_rel)) {
913cab2bb3Spatrick     bool Success = false;
923cab2bb3Spatrick     auto AllocatorsUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT {
933cab2bb3Spatrick       if (!Success)
943cab2bb3Spatrick         atomic_store(&TLD.Allocators, 0, memory_order_release);
953cab2bb3Spatrick     });
963cab2bb3Spatrick 
973cab2bb3Spatrick     // Acquire a set of buffers for this thread.
983cab2bb3Spatrick     if (BQ == nullptr)
993cab2bb3Spatrick       return nullptr;
1003cab2bb3Spatrick 
1013cab2bb3Spatrick     if (BQ->getBuffer(ThreadBuffers.NodeBuffer) != BufferQueue::ErrorCode::Ok)
1023cab2bb3Spatrick       return nullptr;
1033cab2bb3Spatrick     auto NodeBufferUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT {
1043cab2bb3Spatrick       if (!Success)
1053cab2bb3Spatrick         BQ->releaseBuffer(ThreadBuffers.NodeBuffer);
1063cab2bb3Spatrick     });
1073cab2bb3Spatrick 
1083cab2bb3Spatrick     if (BQ->getBuffer(ThreadBuffers.RootsBuffer) != BufferQueue::ErrorCode::Ok)
1093cab2bb3Spatrick       return nullptr;
1103cab2bb3Spatrick     auto RootsBufferUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT {
1113cab2bb3Spatrick       if (!Success)
1123cab2bb3Spatrick         BQ->releaseBuffer(ThreadBuffers.RootsBuffer);
1133cab2bb3Spatrick     });
1143cab2bb3Spatrick 
1153cab2bb3Spatrick     if (BQ->getBuffer(ThreadBuffers.ShadowStackBuffer) !=
1163cab2bb3Spatrick         BufferQueue::ErrorCode::Ok)
1173cab2bb3Spatrick       return nullptr;
1183cab2bb3Spatrick     auto ShadowStackBufferUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT {
1193cab2bb3Spatrick       if (!Success)
1203cab2bb3Spatrick         BQ->releaseBuffer(ThreadBuffers.ShadowStackBuffer);
1213cab2bb3Spatrick     });
1223cab2bb3Spatrick 
1233cab2bb3Spatrick     if (BQ->getBuffer(ThreadBuffers.NodeIdPairBuffer) !=
1243cab2bb3Spatrick         BufferQueue::ErrorCode::Ok)
1253cab2bb3Spatrick       return nullptr;
1263cab2bb3Spatrick 
1273cab2bb3Spatrick     Success = true;
1283cab2bb3Spatrick     new (&AllocatorsStorage) FunctionCallTrie::Allocators(
1293cab2bb3Spatrick         FunctionCallTrie::InitAllocatorsFromBuffers(ThreadBuffers));
1303cab2bb3Spatrick     Allocators = reinterpret_cast<uptr>(
1313cab2bb3Spatrick         reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage));
1323cab2bb3Spatrick     atomic_store(&TLD.Allocators, Allocators, memory_order_release);
1333cab2bb3Spatrick   }
1343cab2bb3Spatrick 
1353cab2bb3Spatrick   if (Allocators == 1)
1363cab2bb3Spatrick     return nullptr;
1373cab2bb3Spatrick 
1383cab2bb3Spatrick   uptr FCT = 0;
1393cab2bb3Spatrick   if (atomic_compare_exchange_strong(&TLD.FCT, &FCT, 1, memory_order_acq_rel)) {
1403cab2bb3Spatrick     new (&FunctionCallTrieStorage)
1413cab2bb3Spatrick         FunctionCallTrie(*reinterpret_cast<FunctionCallTrie::Allocators *>(
1423cab2bb3Spatrick             atomic_load_relaxed(&TLD.Allocators)));
1433cab2bb3Spatrick     FCT = reinterpret_cast<uptr>(
1443cab2bb3Spatrick         reinterpret_cast<FunctionCallTrie *>(&FunctionCallTrieStorage));
1453cab2bb3Spatrick     atomic_store(&TLD.FCT, FCT, memory_order_release);
1463cab2bb3Spatrick   }
1473cab2bb3Spatrick 
1483cab2bb3Spatrick   if (FCT == 1)
1493cab2bb3Spatrick     return nullptr;
1503cab2bb3Spatrick 
1513cab2bb3Spatrick   return &TLD;
1523cab2bb3Spatrick }
1533cab2bb3Spatrick 
cleanupTLD()1543cab2bb3Spatrick static void cleanupTLD() XRAY_NEVER_INSTRUMENT {
1553cab2bb3Spatrick   auto FCT = atomic_exchange(&TLD.FCT, 0, memory_order_acq_rel);
1563cab2bb3Spatrick   if (FCT == reinterpret_cast<uptr>(reinterpret_cast<FunctionCallTrie *>(
1573cab2bb3Spatrick                  &FunctionCallTrieStorage)))
1583cab2bb3Spatrick     reinterpret_cast<FunctionCallTrie *>(FCT)->~FunctionCallTrie();
1593cab2bb3Spatrick 
1603cab2bb3Spatrick   auto Allocators = atomic_exchange(&TLD.Allocators, 0, memory_order_acq_rel);
1613cab2bb3Spatrick   if (Allocators ==
1623cab2bb3Spatrick       reinterpret_cast<uptr>(
1633cab2bb3Spatrick           reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage)))
1643cab2bb3Spatrick     reinterpret_cast<FunctionCallTrie::Allocators *>(Allocators)->~Allocators();
1653cab2bb3Spatrick }
1663cab2bb3Spatrick 
postCurrentThreadFCT(ProfilingData & T)1673cab2bb3Spatrick static void postCurrentThreadFCT(ProfilingData &T) XRAY_NEVER_INSTRUMENT {
1683cab2bb3Spatrick   RecursionGuard TLDInit(TLDInitGuard);
1693cab2bb3Spatrick   if (!TLDInit)
1703cab2bb3Spatrick     return;
1713cab2bb3Spatrick 
1723cab2bb3Spatrick   uptr P = atomic_exchange(&T.FCT, 0, memory_order_acq_rel);
1733cab2bb3Spatrick   if (P != reinterpret_cast<uptr>(
1743cab2bb3Spatrick                reinterpret_cast<FunctionCallTrie *>(&FunctionCallTrieStorage)))
1753cab2bb3Spatrick     return;
1763cab2bb3Spatrick 
1773cab2bb3Spatrick   auto FCT = reinterpret_cast<FunctionCallTrie *>(P);
1783cab2bb3Spatrick   DCHECK_NE(FCT, nullptr);
1793cab2bb3Spatrick 
1803cab2bb3Spatrick   uptr A = atomic_exchange(&T.Allocators, 0, memory_order_acq_rel);
1813cab2bb3Spatrick   if (A !=
1823cab2bb3Spatrick       reinterpret_cast<uptr>(
1833cab2bb3Spatrick           reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage)))
1843cab2bb3Spatrick     return;
1853cab2bb3Spatrick 
1863cab2bb3Spatrick   auto Allocators = reinterpret_cast<FunctionCallTrie::Allocators *>(A);
1873cab2bb3Spatrick   DCHECK_NE(Allocators, nullptr);
1883cab2bb3Spatrick 
1893cab2bb3Spatrick   // Always move the data into the profile collector.
1903cab2bb3Spatrick   profileCollectorService::post(BQ, std::move(*FCT), std::move(*Allocators),
1913cab2bb3Spatrick                                 std::move(ThreadBuffers), GetTid());
1923cab2bb3Spatrick 
1933cab2bb3Spatrick   // Re-initialize the ThreadBuffers object to a known "default" state.
1943cab2bb3Spatrick   ThreadBuffers = FunctionCallTrie::Allocators::Buffers{};
1953cab2bb3Spatrick }
1963cab2bb3Spatrick 
1973cab2bb3Spatrick } // namespace
1983cab2bb3Spatrick 
profilingCompilerDefinedFlags()1993cab2bb3Spatrick const char *profilingCompilerDefinedFlags() XRAY_NEVER_INSTRUMENT {
2003cab2bb3Spatrick #ifdef XRAY_PROFILER_DEFAULT_OPTIONS
2013cab2bb3Spatrick   return SANITIZER_STRINGIFY(XRAY_PROFILER_DEFAULT_OPTIONS);
2023cab2bb3Spatrick #else
2033cab2bb3Spatrick   return "";
2043cab2bb3Spatrick #endif
2053cab2bb3Spatrick }
2063cab2bb3Spatrick 
profilingFlush()2073cab2bb3Spatrick XRayLogFlushStatus profilingFlush() XRAY_NEVER_INSTRUMENT {
2083cab2bb3Spatrick   if (atomic_load(&ProfilerLogStatus, memory_order_acquire) !=
2093cab2bb3Spatrick       XRayLogInitStatus::XRAY_LOG_FINALIZED) {
2103cab2bb3Spatrick     if (Verbosity())
2113cab2bb3Spatrick       Report("Not flushing profiles, profiling not been finalized.\n");
2123cab2bb3Spatrick     return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
2133cab2bb3Spatrick   }
2143cab2bb3Spatrick 
2153cab2bb3Spatrick   RecursionGuard SignalGuard(ReentranceGuard);
2163cab2bb3Spatrick   if (!SignalGuard) {
2173cab2bb3Spatrick     if (Verbosity())
2183cab2bb3Spatrick       Report("Cannot finalize properly inside a signal handler!\n");
2193cab2bb3Spatrick     atomic_store(&ProfilerLogFlushStatus,
2203cab2bb3Spatrick                  XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING,
2213cab2bb3Spatrick                  memory_order_release);
2223cab2bb3Spatrick     return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
2233cab2bb3Spatrick   }
2243cab2bb3Spatrick 
2253cab2bb3Spatrick   s32 Previous = atomic_exchange(&ProfilerLogFlushStatus,
2263cab2bb3Spatrick                                  XRayLogFlushStatus::XRAY_LOG_FLUSHING,
2273cab2bb3Spatrick                                  memory_order_acq_rel);
2283cab2bb3Spatrick   if (Previous == XRayLogFlushStatus::XRAY_LOG_FLUSHING) {
2293cab2bb3Spatrick     if (Verbosity())
2303cab2bb3Spatrick       Report("Not flushing profiles, implementation still flushing.\n");
2313cab2bb3Spatrick     return XRayLogFlushStatus::XRAY_LOG_FLUSHING;
2323cab2bb3Spatrick   }
2333cab2bb3Spatrick 
2343cab2bb3Spatrick   // At this point, we'll create the file that will contain the profile, but
2353cab2bb3Spatrick   // only if the options say so.
2363cab2bb3Spatrick   if (!profilingFlags()->no_flush) {
2373cab2bb3Spatrick     // First check whether we have data in the profile collector service
2383cab2bb3Spatrick     // before we try and write anything down.
2393cab2bb3Spatrick     XRayBuffer B = profileCollectorService::nextBuffer({nullptr, 0});
2403cab2bb3Spatrick     if (B.Data == nullptr) {
2413cab2bb3Spatrick       if (Verbosity())
2423cab2bb3Spatrick         Report("profiling: No data to flush.\n");
2433cab2bb3Spatrick     } else {
2443cab2bb3Spatrick       LogWriter *LW = LogWriter::Open();
2453cab2bb3Spatrick       if (LW == nullptr) {
2463cab2bb3Spatrick         if (Verbosity())
2473cab2bb3Spatrick           Report("profiling: Failed to flush to file, dropping data.\n");
2483cab2bb3Spatrick       } else {
2493cab2bb3Spatrick         // Now for each of the buffers, write out the profile data as we would
2503cab2bb3Spatrick         // see it in memory, verbatim.
2513cab2bb3Spatrick         while (B.Data != nullptr && B.Size != 0) {
2523cab2bb3Spatrick           LW->WriteAll(reinterpret_cast<const char *>(B.Data),
2533cab2bb3Spatrick                        reinterpret_cast<const char *>(B.Data) + B.Size);
2543cab2bb3Spatrick           B = profileCollectorService::nextBuffer(B);
2553cab2bb3Spatrick         }
2563cab2bb3Spatrick       }
2573cab2bb3Spatrick       LogWriter::Close(LW);
2583cab2bb3Spatrick     }
2593cab2bb3Spatrick   }
2603cab2bb3Spatrick 
2613cab2bb3Spatrick   profileCollectorService::reset();
2623cab2bb3Spatrick 
2633cab2bb3Spatrick   atomic_store(&ProfilerLogFlushStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED,
2643cab2bb3Spatrick                memory_order_release);
2653cab2bb3Spatrick   atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_UNINITIALIZED,
2663cab2bb3Spatrick                memory_order_release);
2673cab2bb3Spatrick 
2683cab2bb3Spatrick   return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
2693cab2bb3Spatrick }
2703cab2bb3Spatrick 
profilingHandleArg0(int32_t FuncId,XRayEntryType Entry)2713cab2bb3Spatrick void profilingHandleArg0(int32_t FuncId,
2723cab2bb3Spatrick                          XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {
2733cab2bb3Spatrick   unsigned char CPU;
2743cab2bb3Spatrick   auto TSC = readTSC(CPU);
2753cab2bb3Spatrick   RecursionGuard G(ReentranceGuard);
2763cab2bb3Spatrick   if (!G)
2773cab2bb3Spatrick     return;
2783cab2bb3Spatrick 
2793cab2bb3Spatrick   auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
2803cab2bb3Spatrick   if (UNLIKELY(Status == XRayLogInitStatus::XRAY_LOG_UNINITIALIZED ||
2813cab2bb3Spatrick                Status == XRayLogInitStatus::XRAY_LOG_INITIALIZING))
2823cab2bb3Spatrick     return;
2833cab2bb3Spatrick 
2843cab2bb3Spatrick   if (UNLIKELY(Status == XRayLogInitStatus::XRAY_LOG_FINALIZED ||
2853cab2bb3Spatrick                Status == XRayLogInitStatus::XRAY_LOG_FINALIZING)) {
2863cab2bb3Spatrick     postCurrentThreadFCT(TLD);
2873cab2bb3Spatrick     return;
2883cab2bb3Spatrick   }
2893cab2bb3Spatrick 
2903cab2bb3Spatrick   auto T = getThreadLocalData();
2913cab2bb3Spatrick   if (T == nullptr)
2923cab2bb3Spatrick     return;
2933cab2bb3Spatrick 
2943cab2bb3Spatrick   auto FCT = reinterpret_cast<FunctionCallTrie *>(atomic_load_relaxed(&T->FCT));
2953cab2bb3Spatrick   switch (Entry) {
2963cab2bb3Spatrick   case XRayEntryType::ENTRY:
2973cab2bb3Spatrick   case XRayEntryType::LOG_ARGS_ENTRY:
2983cab2bb3Spatrick     FCT->enterFunction(FuncId, TSC, CPU);
2993cab2bb3Spatrick     break;
3003cab2bb3Spatrick   case XRayEntryType::EXIT:
3013cab2bb3Spatrick   case XRayEntryType::TAIL:
3023cab2bb3Spatrick     FCT->exitFunction(FuncId, TSC, CPU);
3033cab2bb3Spatrick     break;
3043cab2bb3Spatrick   default:
3053cab2bb3Spatrick     // FIXME: Handle bugs.
3063cab2bb3Spatrick     break;
3073cab2bb3Spatrick   }
3083cab2bb3Spatrick }
3093cab2bb3Spatrick 
profilingHandleArg1(int32_t FuncId,XRayEntryType Entry,uint64_t)3103cab2bb3Spatrick void profilingHandleArg1(int32_t FuncId, XRayEntryType Entry,
3113cab2bb3Spatrick                          uint64_t) XRAY_NEVER_INSTRUMENT {
3123cab2bb3Spatrick   return profilingHandleArg0(FuncId, Entry);
3133cab2bb3Spatrick }
3143cab2bb3Spatrick 
profilingFinalize()3153cab2bb3Spatrick XRayLogInitStatus profilingFinalize() XRAY_NEVER_INSTRUMENT {
3163cab2bb3Spatrick   s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED;
3173cab2bb3Spatrick   if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
3183cab2bb3Spatrick                                       XRayLogInitStatus::XRAY_LOG_FINALIZING,
3193cab2bb3Spatrick                                       memory_order_release)) {
3203cab2bb3Spatrick     if (Verbosity())
3213cab2bb3Spatrick       Report("Cannot finalize profile, the profiling is not initialized.\n");
3223cab2bb3Spatrick     return static_cast<XRayLogInitStatus>(CurrentStatus);
3233cab2bb3Spatrick   }
3243cab2bb3Spatrick 
3253cab2bb3Spatrick   // Mark then finalize the current generation of buffers. This allows us to let
3263cab2bb3Spatrick   // the threads currently holding onto new buffers still use them, but let the
3273cab2bb3Spatrick   // last reference do the memory cleanup.
3283cab2bb3Spatrick   DCHECK_NE(BQ, nullptr);
3293cab2bb3Spatrick   BQ->finalize();
3303cab2bb3Spatrick 
3313cab2bb3Spatrick   // Wait a grace period to allow threads to see that we're finalizing.
3323cab2bb3Spatrick   SleepForMillis(profilingFlags()->grace_period_ms);
3333cab2bb3Spatrick 
3343cab2bb3Spatrick   // If we for some reason are entering this function from an instrumented
3353cab2bb3Spatrick   // handler, we bail out.
3363cab2bb3Spatrick   RecursionGuard G(ReentranceGuard);
3373cab2bb3Spatrick   if (!G)
3383cab2bb3Spatrick     return static_cast<XRayLogInitStatus>(CurrentStatus);
3393cab2bb3Spatrick 
3403cab2bb3Spatrick   // Post the current thread's data if we have any.
3413cab2bb3Spatrick   postCurrentThreadFCT(TLD);
3423cab2bb3Spatrick 
3433cab2bb3Spatrick   // Then we force serialize the log data.
3443cab2bb3Spatrick   profileCollectorService::serialize();
3453cab2bb3Spatrick 
3463cab2bb3Spatrick   atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_FINALIZED,
3473cab2bb3Spatrick                memory_order_release);
3483cab2bb3Spatrick   return XRayLogInitStatus::XRAY_LOG_FINALIZED;
3493cab2bb3Spatrick }
3503cab2bb3Spatrick 
3513cab2bb3Spatrick XRayLogInitStatus
profilingLoggingInit(size_t,size_t,void * Options,size_t OptionsSize)3523cab2bb3Spatrick profilingLoggingInit(size_t, size_t, void *Options,
3533cab2bb3Spatrick                      size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
3543cab2bb3Spatrick   RecursionGuard G(ReentranceGuard);
3553cab2bb3Spatrick   if (!G)
3563cab2bb3Spatrick     return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
3573cab2bb3Spatrick 
3583cab2bb3Spatrick   s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
3593cab2bb3Spatrick   if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
3603cab2bb3Spatrick                                       XRayLogInitStatus::XRAY_LOG_INITIALIZING,
3613cab2bb3Spatrick                                       memory_order_acq_rel)) {
3623cab2bb3Spatrick     if (Verbosity())
3633cab2bb3Spatrick       Report("Cannot initialize already initialised profiling "
3643cab2bb3Spatrick              "implementation.\n");
3653cab2bb3Spatrick     return static_cast<XRayLogInitStatus>(CurrentStatus);
3663cab2bb3Spatrick   }
3673cab2bb3Spatrick 
3683cab2bb3Spatrick   {
3693cab2bb3Spatrick     SpinMutexLock Lock(&ProfilerOptionsMutex);
3703cab2bb3Spatrick     FlagParser ConfigParser;
3713cab2bb3Spatrick     ProfilerFlags Flags;
3723cab2bb3Spatrick     Flags.setDefaults();
3733cab2bb3Spatrick     registerProfilerFlags(&ConfigParser, &Flags);
3743cab2bb3Spatrick     ConfigParser.ParseString(profilingCompilerDefinedFlags());
3753cab2bb3Spatrick     const char *Env = GetEnv("XRAY_PROFILING_OPTIONS");
3763cab2bb3Spatrick     if (Env == nullptr)
3773cab2bb3Spatrick       Env = "";
3783cab2bb3Spatrick     ConfigParser.ParseString(Env);
3793cab2bb3Spatrick 
3803cab2bb3Spatrick     // Then parse the configuration string provided.
3813cab2bb3Spatrick     ConfigParser.ParseString(static_cast<const char *>(Options));
3823cab2bb3Spatrick     if (Verbosity())
3833cab2bb3Spatrick       ReportUnrecognizedFlags();
3843cab2bb3Spatrick     *profilingFlags() = Flags;
3853cab2bb3Spatrick   }
3863cab2bb3Spatrick 
3873cab2bb3Spatrick   // We need to reset the profile data collection implementation now.
3883cab2bb3Spatrick   profileCollectorService::reset();
3893cab2bb3Spatrick 
3903cab2bb3Spatrick   // Then also reset the buffer queue implementation.
3913cab2bb3Spatrick   if (BQ == nullptr) {
3923cab2bb3Spatrick     bool Success = false;
3933cab2bb3Spatrick     new (&BufferQueueStorage)
3943cab2bb3Spatrick         BufferQueue(profilingFlags()->per_thread_allocator_max,
3953cab2bb3Spatrick                     profilingFlags()->buffers_max, Success);
3963cab2bb3Spatrick     if (!Success) {
3973cab2bb3Spatrick       if (Verbosity())
3983cab2bb3Spatrick         Report("Failed to initialize preallocated memory buffers!");
3993cab2bb3Spatrick       atomic_store(&ProfilerLogStatus,
4003cab2bb3Spatrick                    XRayLogInitStatus::XRAY_LOG_UNINITIALIZED,
4013cab2bb3Spatrick                    memory_order_release);
4023cab2bb3Spatrick       return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
4033cab2bb3Spatrick     }
4043cab2bb3Spatrick 
405*810390e3Srobert     // If we've succeeded, set the global pointer to the initialised storage.
4063cab2bb3Spatrick     BQ = reinterpret_cast<BufferQueue *>(&BufferQueueStorage);
4073cab2bb3Spatrick   } else {
4083cab2bb3Spatrick     BQ->finalize();
4093cab2bb3Spatrick     auto InitStatus = BQ->init(profilingFlags()->per_thread_allocator_max,
4103cab2bb3Spatrick                                profilingFlags()->buffers_max);
4113cab2bb3Spatrick 
4123cab2bb3Spatrick     if (InitStatus != BufferQueue::ErrorCode::Ok) {
4133cab2bb3Spatrick       if (Verbosity())
4143cab2bb3Spatrick         Report("Failed to initialize preallocated memory buffers; error: %s",
4153cab2bb3Spatrick                BufferQueue::getErrorString(InitStatus));
4163cab2bb3Spatrick       atomic_store(&ProfilerLogStatus,
4173cab2bb3Spatrick                    XRayLogInitStatus::XRAY_LOG_UNINITIALIZED,
4183cab2bb3Spatrick                    memory_order_release);
4193cab2bb3Spatrick       return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
4203cab2bb3Spatrick     }
4213cab2bb3Spatrick 
4223cab2bb3Spatrick     DCHECK(!BQ->finalizing());
4233cab2bb3Spatrick   }
4243cab2bb3Spatrick 
4253cab2bb3Spatrick   // We need to set up the exit handlers.
4263cab2bb3Spatrick   static pthread_once_t Once = PTHREAD_ONCE_INIT;
4273cab2bb3Spatrick   pthread_once(
4283cab2bb3Spatrick       &Once, +[] {
4293cab2bb3Spatrick         pthread_key_create(
4303cab2bb3Spatrick             &ProfilingKey, +[](void *P) XRAY_NEVER_INSTRUMENT {
4313cab2bb3Spatrick               if (atomic_exchange(&ThreadExitingLatch, 1, memory_order_acq_rel))
4323cab2bb3Spatrick                 return;
4333cab2bb3Spatrick 
4343cab2bb3Spatrick               if (P == nullptr)
4353cab2bb3Spatrick                 return;
4363cab2bb3Spatrick 
4373cab2bb3Spatrick               auto T = reinterpret_cast<ProfilingData *>(P);
4383cab2bb3Spatrick               if (atomic_load_relaxed(&T->Allocators) == 0)
4393cab2bb3Spatrick                 return;
4403cab2bb3Spatrick 
4413cab2bb3Spatrick               {
4423cab2bb3Spatrick                 // If we're somehow executing this while inside a
4433cab2bb3Spatrick                 // non-reentrant-friendly context, we skip attempting to post
4443cab2bb3Spatrick                 // the current thread's data.
4453cab2bb3Spatrick                 RecursionGuard G(ReentranceGuard);
4463cab2bb3Spatrick                 if (!G)
4473cab2bb3Spatrick                   return;
4483cab2bb3Spatrick 
4493cab2bb3Spatrick                 postCurrentThreadFCT(*T);
4503cab2bb3Spatrick               }
4513cab2bb3Spatrick             });
4523cab2bb3Spatrick 
4533cab2bb3Spatrick         // We also need to set up an exit handler, so that we can get the
4543cab2bb3Spatrick         // profile information at exit time. We use the C API to do this, to not
4553cab2bb3Spatrick         // rely on C++ ABI functions for registering exit handlers.
4563cab2bb3Spatrick         Atexit(+[]() XRAY_NEVER_INSTRUMENT {
4573cab2bb3Spatrick           if (atomic_exchange(&ThreadExitingLatch, 1, memory_order_acq_rel))
4583cab2bb3Spatrick             return;
4593cab2bb3Spatrick 
4603cab2bb3Spatrick           auto Cleanup =
4613cab2bb3Spatrick               at_scope_exit([]() XRAY_NEVER_INSTRUMENT { cleanupTLD(); });
4623cab2bb3Spatrick 
4633cab2bb3Spatrick           // Finalize and flush.
4643cab2bb3Spatrick           if (profilingFinalize() != XRAY_LOG_FINALIZED ||
4653cab2bb3Spatrick               profilingFlush() != XRAY_LOG_FLUSHED)
4663cab2bb3Spatrick             return;
4673cab2bb3Spatrick 
4683cab2bb3Spatrick           if (Verbosity())
4693cab2bb3Spatrick             Report("XRay Profile flushed at exit.");
4703cab2bb3Spatrick         });
4713cab2bb3Spatrick       });
4723cab2bb3Spatrick 
4733cab2bb3Spatrick   __xray_log_set_buffer_iterator(profileCollectorService::nextBuffer);
4743cab2bb3Spatrick   __xray_set_handler(profilingHandleArg0);
4753cab2bb3Spatrick   __xray_set_handler_arg1(profilingHandleArg1);
4763cab2bb3Spatrick 
4773cab2bb3Spatrick   atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZED,
4783cab2bb3Spatrick                memory_order_release);
4793cab2bb3Spatrick   if (Verbosity())
4803cab2bb3Spatrick     Report("XRay Profiling init successful.\n");
4813cab2bb3Spatrick 
4823cab2bb3Spatrick   return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
4833cab2bb3Spatrick }
4843cab2bb3Spatrick 
profilingDynamicInitializer()4853cab2bb3Spatrick bool profilingDynamicInitializer() XRAY_NEVER_INSTRUMENT {
4863cab2bb3Spatrick   // Set up the flag defaults from the static defaults and the
4873cab2bb3Spatrick   // compiler-provided defaults.
4883cab2bb3Spatrick   {
4893cab2bb3Spatrick     SpinMutexLock Lock(&ProfilerOptionsMutex);
4903cab2bb3Spatrick     auto *F = profilingFlags();
4913cab2bb3Spatrick     F->setDefaults();
4923cab2bb3Spatrick     FlagParser ProfilingParser;
4933cab2bb3Spatrick     registerProfilerFlags(&ProfilingParser, F);
4943cab2bb3Spatrick     ProfilingParser.ParseString(profilingCompilerDefinedFlags());
4953cab2bb3Spatrick   }
4963cab2bb3Spatrick 
4973cab2bb3Spatrick   XRayLogImpl Impl{
4983cab2bb3Spatrick       profilingLoggingInit,
4993cab2bb3Spatrick       profilingFinalize,
5003cab2bb3Spatrick       profilingHandleArg0,
5013cab2bb3Spatrick       profilingFlush,
5023cab2bb3Spatrick   };
5033cab2bb3Spatrick   auto RegistrationResult = __xray_log_register_mode("xray-profiling", Impl);
5043cab2bb3Spatrick   if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK) {
5053cab2bb3Spatrick     if (Verbosity())
5063cab2bb3Spatrick       Report("Cannot register XRay Profiling mode to 'xray-profiling'; error = "
5073cab2bb3Spatrick              "%d\n",
5083cab2bb3Spatrick              RegistrationResult);
5093cab2bb3Spatrick     return false;
5103cab2bb3Spatrick   }
5113cab2bb3Spatrick 
5123cab2bb3Spatrick   if (!internal_strcmp(flags()->xray_mode, "xray-profiling"))
5133cab2bb3Spatrick     __xray_log_select_mode("xray_profiling");
5143cab2bb3Spatrick   return true;
5153cab2bb3Spatrick }
5163cab2bb3Spatrick 
5173cab2bb3Spatrick } // namespace __xray
5183cab2bb3Spatrick 
5193cab2bb3Spatrick static auto UNUSED Unused = __xray::profilingDynamicInitializer();
520