1*68d75effSDimitry Andric //===-- xray_profiling.cpp --------------------------------------*- C++ -*-===// 2*68d75effSDimitry Andric // 3*68d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*68d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*68d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*68d75effSDimitry Andric // 7*68d75effSDimitry Andric //===----------------------------------------------------------------------===// 8*68d75effSDimitry Andric // 9*68d75effSDimitry Andric // This file is a part of XRay, a dynamic runtime instrumentation system. 10*68d75effSDimitry Andric // 11*68d75effSDimitry Andric // This is the implementation of a profiling handler. 12*68d75effSDimitry Andric // 13*68d75effSDimitry Andric //===----------------------------------------------------------------------===// 14*68d75effSDimitry Andric #include <memory> 15*68d75effSDimitry Andric #include <time.h> 16*68d75effSDimitry Andric 17*68d75effSDimitry Andric #include "sanitizer_common/sanitizer_atomic.h" 18*68d75effSDimitry Andric #include "sanitizer_common/sanitizer_flags.h" 19*68d75effSDimitry Andric #include "xray/xray_interface.h" 20*68d75effSDimitry Andric #include "xray/xray_log_interface.h" 21*68d75effSDimitry Andric #include "xray_buffer_queue.h" 22*68d75effSDimitry Andric #include "xray_flags.h" 23*68d75effSDimitry Andric #include "xray_profile_collector.h" 24*68d75effSDimitry Andric #include "xray_profiling_flags.h" 25*68d75effSDimitry Andric #include "xray_recursion_guard.h" 26*68d75effSDimitry Andric #include "xray_tsc.h" 27*68d75effSDimitry Andric #include "xray_utils.h" 28*68d75effSDimitry Andric #include <pthread.h> 29*68d75effSDimitry Andric 30*68d75effSDimitry Andric namespace __xray { 31*68d75effSDimitry Andric 32*68d75effSDimitry Andric namespace { 33*68d75effSDimitry Andric 34*68d75effSDimitry Andric static atomic_sint32_t ProfilerLogFlushStatus = { 35*68d75effSDimitry Andric XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING}; 36*68d75effSDimitry Andric 37*68d75effSDimitry Andric static atomic_sint32_t ProfilerLogStatus = { 38*68d75effSDimitry Andric XRayLogInitStatus::XRAY_LOG_UNINITIALIZED}; 39*68d75effSDimitry Andric 40*68d75effSDimitry Andric static SpinMutex ProfilerOptionsMutex; 41*68d75effSDimitry Andric 42*68d75effSDimitry Andric struct ProfilingData { 43*68d75effSDimitry Andric atomic_uintptr_t Allocators; 44*68d75effSDimitry Andric atomic_uintptr_t FCT; 45*68d75effSDimitry Andric }; 46*68d75effSDimitry Andric 47*68d75effSDimitry Andric static pthread_key_t ProfilingKey; 48*68d75effSDimitry Andric 49*68d75effSDimitry Andric // We use a global buffer queue, which gets initialized once at initialisation 50*68d75effSDimitry Andric // time, and gets reset when profiling is "done". 51*68d75effSDimitry Andric static std::aligned_storage<sizeof(BufferQueue), alignof(BufferQueue)>::type 52*68d75effSDimitry Andric BufferQueueStorage; 53*68d75effSDimitry Andric static BufferQueue *BQ = nullptr; 54*68d75effSDimitry Andric 55*68d75effSDimitry Andric thread_local FunctionCallTrie::Allocators::Buffers ThreadBuffers; 56*68d75effSDimitry Andric thread_local std::aligned_storage<sizeof(FunctionCallTrie::Allocators), 57*68d75effSDimitry Andric alignof(FunctionCallTrie::Allocators)>::type 58*68d75effSDimitry Andric AllocatorsStorage; 59*68d75effSDimitry Andric thread_local std::aligned_storage<sizeof(FunctionCallTrie), 60*68d75effSDimitry Andric alignof(FunctionCallTrie)>::type 61*68d75effSDimitry Andric FunctionCallTrieStorage; 62*68d75effSDimitry Andric thread_local ProfilingData TLD{{0}, {0}}; 63*68d75effSDimitry Andric thread_local atomic_uint8_t ReentranceGuard{0}; 64*68d75effSDimitry Andric 65*68d75effSDimitry Andric // We use a separate guard for ensuring that for this thread, if we're already 66*68d75effSDimitry Andric // cleaning up, that any signal handlers don't attempt to cleanup nor 67*68d75effSDimitry Andric // initialise. 68*68d75effSDimitry Andric thread_local atomic_uint8_t TLDInitGuard{0}; 69*68d75effSDimitry Andric 70*68d75effSDimitry Andric // We also use a separate latch to signal that the thread is exiting, and 71*68d75effSDimitry Andric // non-essential work should be ignored (things like recording events, etc.). 72*68d75effSDimitry Andric thread_local atomic_uint8_t ThreadExitingLatch{0}; 73*68d75effSDimitry Andric 74*68d75effSDimitry Andric static ProfilingData *getThreadLocalData() XRAY_NEVER_INSTRUMENT { 75*68d75effSDimitry Andric thread_local auto ThreadOnce = []() XRAY_NEVER_INSTRUMENT { 76*68d75effSDimitry Andric pthread_setspecific(ProfilingKey, &TLD); 77*68d75effSDimitry Andric return false; 78*68d75effSDimitry Andric }(); 79*68d75effSDimitry Andric (void)ThreadOnce; 80*68d75effSDimitry Andric 81*68d75effSDimitry Andric RecursionGuard TLDInit(TLDInitGuard); 82*68d75effSDimitry Andric if (!TLDInit) 83*68d75effSDimitry Andric return nullptr; 84*68d75effSDimitry Andric 85*68d75effSDimitry Andric if (atomic_load_relaxed(&ThreadExitingLatch)) 86*68d75effSDimitry Andric return nullptr; 87*68d75effSDimitry Andric 88*68d75effSDimitry Andric uptr Allocators = 0; 89*68d75effSDimitry Andric if (atomic_compare_exchange_strong(&TLD.Allocators, &Allocators, 1, 90*68d75effSDimitry Andric memory_order_acq_rel)) { 91*68d75effSDimitry Andric bool Success = false; 92*68d75effSDimitry Andric auto AllocatorsUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT { 93*68d75effSDimitry Andric if (!Success) 94*68d75effSDimitry Andric atomic_store(&TLD.Allocators, 0, memory_order_release); 95*68d75effSDimitry Andric }); 96*68d75effSDimitry Andric 97*68d75effSDimitry Andric // Acquire a set of buffers for this thread. 98*68d75effSDimitry Andric if (BQ == nullptr) 99*68d75effSDimitry Andric return nullptr; 100*68d75effSDimitry Andric 101*68d75effSDimitry Andric if (BQ->getBuffer(ThreadBuffers.NodeBuffer) != BufferQueue::ErrorCode::Ok) 102*68d75effSDimitry Andric return nullptr; 103*68d75effSDimitry Andric auto NodeBufferUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT { 104*68d75effSDimitry Andric if (!Success) 105*68d75effSDimitry Andric BQ->releaseBuffer(ThreadBuffers.NodeBuffer); 106*68d75effSDimitry Andric }); 107*68d75effSDimitry Andric 108*68d75effSDimitry Andric if (BQ->getBuffer(ThreadBuffers.RootsBuffer) != BufferQueue::ErrorCode::Ok) 109*68d75effSDimitry Andric return nullptr; 110*68d75effSDimitry Andric auto RootsBufferUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT { 111*68d75effSDimitry Andric if (!Success) 112*68d75effSDimitry Andric BQ->releaseBuffer(ThreadBuffers.RootsBuffer); 113*68d75effSDimitry Andric }); 114*68d75effSDimitry Andric 115*68d75effSDimitry Andric if (BQ->getBuffer(ThreadBuffers.ShadowStackBuffer) != 116*68d75effSDimitry Andric BufferQueue::ErrorCode::Ok) 117*68d75effSDimitry Andric return nullptr; 118*68d75effSDimitry Andric auto ShadowStackBufferUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT { 119*68d75effSDimitry Andric if (!Success) 120*68d75effSDimitry Andric BQ->releaseBuffer(ThreadBuffers.ShadowStackBuffer); 121*68d75effSDimitry Andric }); 122*68d75effSDimitry Andric 123*68d75effSDimitry Andric if (BQ->getBuffer(ThreadBuffers.NodeIdPairBuffer) != 124*68d75effSDimitry Andric BufferQueue::ErrorCode::Ok) 125*68d75effSDimitry Andric return nullptr; 126*68d75effSDimitry Andric 127*68d75effSDimitry Andric Success = true; 128*68d75effSDimitry Andric new (&AllocatorsStorage) FunctionCallTrie::Allocators( 129*68d75effSDimitry Andric FunctionCallTrie::InitAllocatorsFromBuffers(ThreadBuffers)); 130*68d75effSDimitry Andric Allocators = reinterpret_cast<uptr>( 131*68d75effSDimitry Andric reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage)); 132*68d75effSDimitry Andric atomic_store(&TLD.Allocators, Allocators, memory_order_release); 133*68d75effSDimitry Andric } 134*68d75effSDimitry Andric 135*68d75effSDimitry Andric if (Allocators == 1) 136*68d75effSDimitry Andric return nullptr; 137*68d75effSDimitry Andric 138*68d75effSDimitry Andric uptr FCT = 0; 139*68d75effSDimitry Andric if (atomic_compare_exchange_strong(&TLD.FCT, &FCT, 1, memory_order_acq_rel)) { 140*68d75effSDimitry Andric new (&FunctionCallTrieStorage) 141*68d75effSDimitry Andric FunctionCallTrie(*reinterpret_cast<FunctionCallTrie::Allocators *>( 142*68d75effSDimitry Andric atomic_load_relaxed(&TLD.Allocators))); 143*68d75effSDimitry Andric FCT = reinterpret_cast<uptr>( 144*68d75effSDimitry Andric reinterpret_cast<FunctionCallTrie *>(&FunctionCallTrieStorage)); 145*68d75effSDimitry Andric atomic_store(&TLD.FCT, FCT, memory_order_release); 146*68d75effSDimitry Andric } 147*68d75effSDimitry Andric 148*68d75effSDimitry Andric if (FCT == 1) 149*68d75effSDimitry Andric return nullptr; 150*68d75effSDimitry Andric 151*68d75effSDimitry Andric return &TLD; 152*68d75effSDimitry Andric } 153*68d75effSDimitry Andric 154*68d75effSDimitry Andric static void cleanupTLD() XRAY_NEVER_INSTRUMENT { 155*68d75effSDimitry Andric auto FCT = atomic_exchange(&TLD.FCT, 0, memory_order_acq_rel); 156*68d75effSDimitry Andric if (FCT == reinterpret_cast<uptr>(reinterpret_cast<FunctionCallTrie *>( 157*68d75effSDimitry Andric &FunctionCallTrieStorage))) 158*68d75effSDimitry Andric reinterpret_cast<FunctionCallTrie *>(FCT)->~FunctionCallTrie(); 159*68d75effSDimitry Andric 160*68d75effSDimitry Andric auto Allocators = atomic_exchange(&TLD.Allocators, 0, memory_order_acq_rel); 161*68d75effSDimitry Andric if (Allocators == 162*68d75effSDimitry Andric reinterpret_cast<uptr>( 163*68d75effSDimitry Andric reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage))) 164*68d75effSDimitry Andric reinterpret_cast<FunctionCallTrie::Allocators *>(Allocators)->~Allocators(); 165*68d75effSDimitry Andric } 166*68d75effSDimitry Andric 167*68d75effSDimitry Andric static void postCurrentThreadFCT(ProfilingData &T) XRAY_NEVER_INSTRUMENT { 168*68d75effSDimitry Andric RecursionGuard TLDInit(TLDInitGuard); 169*68d75effSDimitry Andric if (!TLDInit) 170*68d75effSDimitry Andric return; 171*68d75effSDimitry Andric 172*68d75effSDimitry Andric uptr P = atomic_exchange(&T.FCT, 0, memory_order_acq_rel); 173*68d75effSDimitry Andric if (P != reinterpret_cast<uptr>( 174*68d75effSDimitry Andric reinterpret_cast<FunctionCallTrie *>(&FunctionCallTrieStorage))) 175*68d75effSDimitry Andric return; 176*68d75effSDimitry Andric 177*68d75effSDimitry Andric auto FCT = reinterpret_cast<FunctionCallTrie *>(P); 178*68d75effSDimitry Andric DCHECK_NE(FCT, nullptr); 179*68d75effSDimitry Andric 180*68d75effSDimitry Andric uptr A = atomic_exchange(&T.Allocators, 0, memory_order_acq_rel); 181*68d75effSDimitry Andric if (A != 182*68d75effSDimitry Andric reinterpret_cast<uptr>( 183*68d75effSDimitry Andric reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage))) 184*68d75effSDimitry Andric return; 185*68d75effSDimitry Andric 186*68d75effSDimitry Andric auto Allocators = reinterpret_cast<FunctionCallTrie::Allocators *>(A); 187*68d75effSDimitry Andric DCHECK_NE(Allocators, nullptr); 188*68d75effSDimitry Andric 189*68d75effSDimitry Andric // Always move the data into the profile collector. 190*68d75effSDimitry Andric profileCollectorService::post(BQ, std::move(*FCT), std::move(*Allocators), 191*68d75effSDimitry Andric std::move(ThreadBuffers), GetTid()); 192*68d75effSDimitry Andric 193*68d75effSDimitry Andric // Re-initialize the ThreadBuffers object to a known "default" state. 194*68d75effSDimitry Andric ThreadBuffers = FunctionCallTrie::Allocators::Buffers{}; 195*68d75effSDimitry Andric } 196*68d75effSDimitry Andric 197*68d75effSDimitry Andric } // namespace 198*68d75effSDimitry Andric 199*68d75effSDimitry Andric const char *profilingCompilerDefinedFlags() XRAY_NEVER_INSTRUMENT { 200*68d75effSDimitry Andric #ifdef XRAY_PROFILER_DEFAULT_OPTIONS 201*68d75effSDimitry Andric return SANITIZER_STRINGIFY(XRAY_PROFILER_DEFAULT_OPTIONS); 202*68d75effSDimitry Andric #else 203*68d75effSDimitry Andric return ""; 204*68d75effSDimitry Andric #endif 205*68d75effSDimitry Andric } 206*68d75effSDimitry Andric 207*68d75effSDimitry Andric XRayLogFlushStatus profilingFlush() XRAY_NEVER_INSTRUMENT { 208*68d75effSDimitry Andric if (atomic_load(&ProfilerLogStatus, memory_order_acquire) != 209*68d75effSDimitry Andric XRayLogInitStatus::XRAY_LOG_FINALIZED) { 210*68d75effSDimitry Andric if (Verbosity()) 211*68d75effSDimitry Andric Report("Not flushing profiles, profiling not been finalized.\n"); 212*68d75effSDimitry Andric return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING; 213*68d75effSDimitry Andric } 214*68d75effSDimitry Andric 215*68d75effSDimitry Andric RecursionGuard SignalGuard(ReentranceGuard); 216*68d75effSDimitry Andric if (!SignalGuard) { 217*68d75effSDimitry Andric if (Verbosity()) 218*68d75effSDimitry Andric Report("Cannot finalize properly inside a signal handler!\n"); 219*68d75effSDimitry Andric atomic_store(&ProfilerLogFlushStatus, 220*68d75effSDimitry Andric XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING, 221*68d75effSDimitry Andric memory_order_release); 222*68d75effSDimitry Andric return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING; 223*68d75effSDimitry Andric } 224*68d75effSDimitry Andric 225*68d75effSDimitry Andric s32 Previous = atomic_exchange(&ProfilerLogFlushStatus, 226*68d75effSDimitry Andric XRayLogFlushStatus::XRAY_LOG_FLUSHING, 227*68d75effSDimitry Andric memory_order_acq_rel); 228*68d75effSDimitry Andric if (Previous == XRayLogFlushStatus::XRAY_LOG_FLUSHING) { 229*68d75effSDimitry Andric if (Verbosity()) 230*68d75effSDimitry Andric Report("Not flushing profiles, implementation still flushing.\n"); 231*68d75effSDimitry Andric return XRayLogFlushStatus::XRAY_LOG_FLUSHING; 232*68d75effSDimitry Andric } 233*68d75effSDimitry Andric 234*68d75effSDimitry Andric // At this point, we'll create the file that will contain the profile, but 235*68d75effSDimitry Andric // only if the options say so. 236*68d75effSDimitry Andric if (!profilingFlags()->no_flush) { 237*68d75effSDimitry Andric // First check whether we have data in the profile collector service 238*68d75effSDimitry Andric // before we try and write anything down. 239*68d75effSDimitry Andric XRayBuffer B = profileCollectorService::nextBuffer({nullptr, 0}); 240*68d75effSDimitry Andric if (B.Data == nullptr) { 241*68d75effSDimitry Andric if (Verbosity()) 242*68d75effSDimitry Andric Report("profiling: No data to flush.\n"); 243*68d75effSDimitry Andric } else { 244*68d75effSDimitry Andric LogWriter *LW = LogWriter::Open(); 245*68d75effSDimitry Andric if (LW == nullptr) { 246*68d75effSDimitry Andric if (Verbosity()) 247*68d75effSDimitry Andric Report("profiling: Failed to flush to file, dropping data.\n"); 248*68d75effSDimitry Andric } else { 249*68d75effSDimitry Andric // Now for each of the buffers, write out the profile data as we would 250*68d75effSDimitry Andric // see it in memory, verbatim. 251*68d75effSDimitry Andric while (B.Data != nullptr && B.Size != 0) { 252*68d75effSDimitry Andric LW->WriteAll(reinterpret_cast<const char *>(B.Data), 253*68d75effSDimitry Andric reinterpret_cast<const char *>(B.Data) + B.Size); 254*68d75effSDimitry Andric B = profileCollectorService::nextBuffer(B); 255*68d75effSDimitry Andric } 256*68d75effSDimitry Andric } 257*68d75effSDimitry Andric LogWriter::Close(LW); 258*68d75effSDimitry Andric } 259*68d75effSDimitry Andric } 260*68d75effSDimitry Andric 261*68d75effSDimitry Andric profileCollectorService::reset(); 262*68d75effSDimitry Andric 263*68d75effSDimitry Andric atomic_store(&ProfilerLogFlushStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED, 264*68d75effSDimitry Andric memory_order_release); 265*68d75effSDimitry Andric atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_UNINITIALIZED, 266*68d75effSDimitry Andric memory_order_release); 267*68d75effSDimitry Andric 268*68d75effSDimitry Andric return XRayLogFlushStatus::XRAY_LOG_FLUSHED; 269*68d75effSDimitry Andric } 270*68d75effSDimitry Andric 271*68d75effSDimitry Andric void profilingHandleArg0(int32_t FuncId, 272*68d75effSDimitry Andric XRayEntryType Entry) XRAY_NEVER_INSTRUMENT { 273*68d75effSDimitry Andric unsigned char CPU; 274*68d75effSDimitry Andric auto TSC = readTSC(CPU); 275*68d75effSDimitry Andric RecursionGuard G(ReentranceGuard); 276*68d75effSDimitry Andric if (!G) 277*68d75effSDimitry Andric return; 278*68d75effSDimitry Andric 279*68d75effSDimitry Andric auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire); 280*68d75effSDimitry Andric if (UNLIKELY(Status == XRayLogInitStatus::XRAY_LOG_UNINITIALIZED || 281*68d75effSDimitry Andric Status == XRayLogInitStatus::XRAY_LOG_INITIALIZING)) 282*68d75effSDimitry Andric return; 283*68d75effSDimitry Andric 284*68d75effSDimitry Andric if (UNLIKELY(Status == XRayLogInitStatus::XRAY_LOG_FINALIZED || 285*68d75effSDimitry Andric Status == XRayLogInitStatus::XRAY_LOG_FINALIZING)) { 286*68d75effSDimitry Andric postCurrentThreadFCT(TLD); 287*68d75effSDimitry Andric return; 288*68d75effSDimitry Andric } 289*68d75effSDimitry Andric 290*68d75effSDimitry Andric auto T = getThreadLocalData(); 291*68d75effSDimitry Andric if (T == nullptr) 292*68d75effSDimitry Andric return; 293*68d75effSDimitry Andric 294*68d75effSDimitry Andric auto FCT = reinterpret_cast<FunctionCallTrie *>(atomic_load_relaxed(&T->FCT)); 295*68d75effSDimitry Andric switch (Entry) { 296*68d75effSDimitry Andric case XRayEntryType::ENTRY: 297*68d75effSDimitry Andric case XRayEntryType::LOG_ARGS_ENTRY: 298*68d75effSDimitry Andric FCT->enterFunction(FuncId, TSC, CPU); 299*68d75effSDimitry Andric break; 300*68d75effSDimitry Andric case XRayEntryType::EXIT: 301*68d75effSDimitry Andric case XRayEntryType::TAIL: 302*68d75effSDimitry Andric FCT->exitFunction(FuncId, TSC, CPU); 303*68d75effSDimitry Andric break; 304*68d75effSDimitry Andric default: 305*68d75effSDimitry Andric // FIXME: Handle bugs. 306*68d75effSDimitry Andric break; 307*68d75effSDimitry Andric } 308*68d75effSDimitry Andric } 309*68d75effSDimitry Andric 310*68d75effSDimitry Andric void profilingHandleArg1(int32_t FuncId, XRayEntryType Entry, 311*68d75effSDimitry Andric uint64_t) XRAY_NEVER_INSTRUMENT { 312*68d75effSDimitry Andric return profilingHandleArg0(FuncId, Entry); 313*68d75effSDimitry Andric } 314*68d75effSDimitry Andric 315*68d75effSDimitry Andric XRayLogInitStatus profilingFinalize() XRAY_NEVER_INSTRUMENT { 316*68d75effSDimitry Andric s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED; 317*68d75effSDimitry Andric if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus, 318*68d75effSDimitry Andric XRayLogInitStatus::XRAY_LOG_FINALIZING, 319*68d75effSDimitry Andric memory_order_release)) { 320*68d75effSDimitry Andric if (Verbosity()) 321*68d75effSDimitry Andric Report("Cannot finalize profile, the profiling is not initialized.\n"); 322*68d75effSDimitry Andric return static_cast<XRayLogInitStatus>(CurrentStatus); 323*68d75effSDimitry Andric } 324*68d75effSDimitry Andric 325*68d75effSDimitry Andric // Mark then finalize the current generation of buffers. This allows us to let 326*68d75effSDimitry Andric // the threads currently holding onto new buffers still use them, but let the 327*68d75effSDimitry Andric // last reference do the memory cleanup. 328*68d75effSDimitry Andric DCHECK_NE(BQ, nullptr); 329*68d75effSDimitry Andric BQ->finalize(); 330*68d75effSDimitry Andric 331*68d75effSDimitry Andric // Wait a grace period to allow threads to see that we're finalizing. 332*68d75effSDimitry Andric SleepForMillis(profilingFlags()->grace_period_ms); 333*68d75effSDimitry Andric 334*68d75effSDimitry Andric // If we for some reason are entering this function from an instrumented 335*68d75effSDimitry Andric // handler, we bail out. 336*68d75effSDimitry Andric RecursionGuard G(ReentranceGuard); 337*68d75effSDimitry Andric if (!G) 338*68d75effSDimitry Andric return static_cast<XRayLogInitStatus>(CurrentStatus); 339*68d75effSDimitry Andric 340*68d75effSDimitry Andric // Post the current thread's data if we have any. 341*68d75effSDimitry Andric postCurrentThreadFCT(TLD); 342*68d75effSDimitry Andric 343*68d75effSDimitry Andric // Then we force serialize the log data. 344*68d75effSDimitry Andric profileCollectorService::serialize(); 345*68d75effSDimitry Andric 346*68d75effSDimitry Andric atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_FINALIZED, 347*68d75effSDimitry Andric memory_order_release); 348*68d75effSDimitry Andric return XRayLogInitStatus::XRAY_LOG_FINALIZED; 349*68d75effSDimitry Andric } 350*68d75effSDimitry Andric 351*68d75effSDimitry Andric XRayLogInitStatus 352*68d75effSDimitry Andric profilingLoggingInit(size_t, size_t, void *Options, 353*68d75effSDimitry Andric size_t OptionsSize) XRAY_NEVER_INSTRUMENT { 354*68d75effSDimitry Andric RecursionGuard G(ReentranceGuard); 355*68d75effSDimitry Andric if (!G) 356*68d75effSDimitry Andric return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; 357*68d75effSDimitry Andric 358*68d75effSDimitry Andric s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; 359*68d75effSDimitry Andric if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus, 360*68d75effSDimitry Andric XRayLogInitStatus::XRAY_LOG_INITIALIZING, 361*68d75effSDimitry Andric memory_order_acq_rel)) { 362*68d75effSDimitry Andric if (Verbosity()) 363*68d75effSDimitry Andric Report("Cannot initialize already initialised profiling " 364*68d75effSDimitry Andric "implementation.\n"); 365*68d75effSDimitry Andric return static_cast<XRayLogInitStatus>(CurrentStatus); 366*68d75effSDimitry Andric } 367*68d75effSDimitry Andric 368*68d75effSDimitry Andric { 369*68d75effSDimitry Andric SpinMutexLock Lock(&ProfilerOptionsMutex); 370*68d75effSDimitry Andric FlagParser ConfigParser; 371*68d75effSDimitry Andric ProfilerFlags Flags; 372*68d75effSDimitry Andric Flags.setDefaults(); 373*68d75effSDimitry Andric registerProfilerFlags(&ConfigParser, &Flags); 374*68d75effSDimitry Andric ConfigParser.ParseString(profilingCompilerDefinedFlags()); 375*68d75effSDimitry Andric const char *Env = GetEnv("XRAY_PROFILING_OPTIONS"); 376*68d75effSDimitry Andric if (Env == nullptr) 377*68d75effSDimitry Andric Env = ""; 378*68d75effSDimitry Andric ConfigParser.ParseString(Env); 379*68d75effSDimitry Andric 380*68d75effSDimitry Andric // Then parse the configuration string provided. 381*68d75effSDimitry Andric ConfigParser.ParseString(static_cast<const char *>(Options)); 382*68d75effSDimitry Andric if (Verbosity()) 383*68d75effSDimitry Andric ReportUnrecognizedFlags(); 384*68d75effSDimitry Andric *profilingFlags() = Flags; 385*68d75effSDimitry Andric } 386*68d75effSDimitry Andric 387*68d75effSDimitry Andric // We need to reset the profile data collection implementation now. 388*68d75effSDimitry Andric profileCollectorService::reset(); 389*68d75effSDimitry Andric 390*68d75effSDimitry Andric // Then also reset the buffer queue implementation. 391*68d75effSDimitry Andric if (BQ == nullptr) { 392*68d75effSDimitry Andric bool Success = false; 393*68d75effSDimitry Andric new (&BufferQueueStorage) 394*68d75effSDimitry Andric BufferQueue(profilingFlags()->per_thread_allocator_max, 395*68d75effSDimitry Andric profilingFlags()->buffers_max, Success); 396*68d75effSDimitry Andric if (!Success) { 397*68d75effSDimitry Andric if (Verbosity()) 398*68d75effSDimitry Andric Report("Failed to initialize preallocated memory buffers!"); 399*68d75effSDimitry Andric atomic_store(&ProfilerLogStatus, 400*68d75effSDimitry Andric XRayLogInitStatus::XRAY_LOG_UNINITIALIZED, 401*68d75effSDimitry Andric memory_order_release); 402*68d75effSDimitry Andric return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; 403*68d75effSDimitry Andric } 404*68d75effSDimitry Andric 405*68d75effSDimitry Andric // If we've succeded, set the global pointer to the initialised storage. 406*68d75effSDimitry Andric BQ = reinterpret_cast<BufferQueue *>(&BufferQueueStorage); 407*68d75effSDimitry Andric } else { 408*68d75effSDimitry Andric BQ->finalize(); 409*68d75effSDimitry Andric auto InitStatus = BQ->init(profilingFlags()->per_thread_allocator_max, 410*68d75effSDimitry Andric profilingFlags()->buffers_max); 411*68d75effSDimitry Andric 412*68d75effSDimitry Andric if (InitStatus != BufferQueue::ErrorCode::Ok) { 413*68d75effSDimitry Andric if (Verbosity()) 414*68d75effSDimitry Andric Report("Failed to initialize preallocated memory buffers; error: %s", 415*68d75effSDimitry Andric BufferQueue::getErrorString(InitStatus)); 416*68d75effSDimitry Andric atomic_store(&ProfilerLogStatus, 417*68d75effSDimitry Andric XRayLogInitStatus::XRAY_LOG_UNINITIALIZED, 418*68d75effSDimitry Andric memory_order_release); 419*68d75effSDimitry Andric return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; 420*68d75effSDimitry Andric } 421*68d75effSDimitry Andric 422*68d75effSDimitry Andric DCHECK(!BQ->finalizing()); 423*68d75effSDimitry Andric } 424*68d75effSDimitry Andric 425*68d75effSDimitry Andric // We need to set up the exit handlers. 426*68d75effSDimitry Andric static pthread_once_t Once = PTHREAD_ONCE_INIT; 427*68d75effSDimitry Andric pthread_once( 428*68d75effSDimitry Andric &Once, +[] { 429*68d75effSDimitry Andric pthread_key_create( 430*68d75effSDimitry Andric &ProfilingKey, +[](void *P) XRAY_NEVER_INSTRUMENT { 431*68d75effSDimitry Andric if (atomic_exchange(&ThreadExitingLatch, 1, memory_order_acq_rel)) 432*68d75effSDimitry Andric return; 433*68d75effSDimitry Andric 434*68d75effSDimitry Andric if (P == nullptr) 435*68d75effSDimitry Andric return; 436*68d75effSDimitry Andric 437*68d75effSDimitry Andric auto T = reinterpret_cast<ProfilingData *>(P); 438*68d75effSDimitry Andric if (atomic_load_relaxed(&T->Allocators) == 0) 439*68d75effSDimitry Andric return; 440*68d75effSDimitry Andric 441*68d75effSDimitry Andric { 442*68d75effSDimitry Andric // If we're somehow executing this while inside a 443*68d75effSDimitry Andric // non-reentrant-friendly context, we skip attempting to post 444*68d75effSDimitry Andric // the current thread's data. 445*68d75effSDimitry Andric RecursionGuard G(ReentranceGuard); 446*68d75effSDimitry Andric if (!G) 447*68d75effSDimitry Andric return; 448*68d75effSDimitry Andric 449*68d75effSDimitry Andric postCurrentThreadFCT(*T); 450*68d75effSDimitry Andric } 451*68d75effSDimitry Andric }); 452*68d75effSDimitry Andric 453*68d75effSDimitry Andric // We also need to set up an exit handler, so that we can get the 454*68d75effSDimitry Andric // profile information at exit time. We use the C API to do this, to not 455*68d75effSDimitry Andric // rely on C++ ABI functions for registering exit handlers. 456*68d75effSDimitry Andric Atexit(+[]() XRAY_NEVER_INSTRUMENT { 457*68d75effSDimitry Andric if (atomic_exchange(&ThreadExitingLatch, 1, memory_order_acq_rel)) 458*68d75effSDimitry Andric return; 459*68d75effSDimitry Andric 460*68d75effSDimitry Andric auto Cleanup = 461*68d75effSDimitry Andric at_scope_exit([]() XRAY_NEVER_INSTRUMENT { cleanupTLD(); }); 462*68d75effSDimitry Andric 463*68d75effSDimitry Andric // Finalize and flush. 464*68d75effSDimitry Andric if (profilingFinalize() != XRAY_LOG_FINALIZED || 465*68d75effSDimitry Andric profilingFlush() != XRAY_LOG_FLUSHED) 466*68d75effSDimitry Andric return; 467*68d75effSDimitry Andric 468*68d75effSDimitry Andric if (Verbosity()) 469*68d75effSDimitry Andric Report("XRay Profile flushed at exit."); 470*68d75effSDimitry Andric }); 471*68d75effSDimitry Andric }); 472*68d75effSDimitry Andric 473*68d75effSDimitry Andric __xray_log_set_buffer_iterator(profileCollectorService::nextBuffer); 474*68d75effSDimitry Andric __xray_set_handler(profilingHandleArg0); 475*68d75effSDimitry Andric __xray_set_handler_arg1(profilingHandleArg1); 476*68d75effSDimitry Andric 477*68d75effSDimitry Andric atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZED, 478*68d75effSDimitry Andric memory_order_release); 479*68d75effSDimitry Andric if (Verbosity()) 480*68d75effSDimitry Andric Report("XRay Profiling init successful.\n"); 481*68d75effSDimitry Andric 482*68d75effSDimitry Andric return XRayLogInitStatus::XRAY_LOG_INITIALIZED; 483*68d75effSDimitry Andric } 484*68d75effSDimitry Andric 485*68d75effSDimitry Andric bool profilingDynamicInitializer() XRAY_NEVER_INSTRUMENT { 486*68d75effSDimitry Andric // Set up the flag defaults from the static defaults and the 487*68d75effSDimitry Andric // compiler-provided defaults. 488*68d75effSDimitry Andric { 489*68d75effSDimitry Andric SpinMutexLock Lock(&ProfilerOptionsMutex); 490*68d75effSDimitry Andric auto *F = profilingFlags(); 491*68d75effSDimitry Andric F->setDefaults(); 492*68d75effSDimitry Andric FlagParser ProfilingParser; 493*68d75effSDimitry Andric registerProfilerFlags(&ProfilingParser, F); 494*68d75effSDimitry Andric ProfilingParser.ParseString(profilingCompilerDefinedFlags()); 495*68d75effSDimitry Andric } 496*68d75effSDimitry Andric 497*68d75effSDimitry Andric XRayLogImpl Impl{ 498*68d75effSDimitry Andric profilingLoggingInit, 499*68d75effSDimitry Andric profilingFinalize, 500*68d75effSDimitry Andric profilingHandleArg0, 501*68d75effSDimitry Andric profilingFlush, 502*68d75effSDimitry Andric }; 503*68d75effSDimitry Andric auto RegistrationResult = __xray_log_register_mode("xray-profiling", Impl); 504*68d75effSDimitry Andric if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK) { 505*68d75effSDimitry Andric if (Verbosity()) 506*68d75effSDimitry Andric Report("Cannot register XRay Profiling mode to 'xray-profiling'; error = " 507*68d75effSDimitry Andric "%d\n", 508*68d75effSDimitry Andric RegistrationResult); 509*68d75effSDimitry Andric return false; 510*68d75effSDimitry Andric } 511*68d75effSDimitry Andric 512*68d75effSDimitry Andric if (!internal_strcmp(flags()->xray_mode, "xray-profiling")) 513*68d75effSDimitry Andric __xray_log_select_mode("xray_profiling"); 514*68d75effSDimitry Andric return true; 515*68d75effSDimitry Andric } 516*68d75effSDimitry Andric 517*68d75effSDimitry Andric } // namespace __xray 518*68d75effSDimitry Andric 519*68d75effSDimitry Andric static auto UNUSED Unused = __xray::profilingDynamicInitializer(); 520