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