168d75effSDimitry Andric //===-- xray_profiling.cpp --------------------------------------*- C++ -*-===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric // 968d75effSDimitry Andric // This file is a part of XRay, a dynamic runtime instrumentation system. 1068d75effSDimitry Andric // 1168d75effSDimitry Andric // This is the implementation of a profiling handler. 1268d75effSDimitry Andric // 1368d75effSDimitry Andric //===----------------------------------------------------------------------===// 1468d75effSDimitry Andric #include <memory> 1568d75effSDimitry Andric #include <time.h> 1668d75effSDimitry Andric 1768d75effSDimitry Andric #include "sanitizer_common/sanitizer_atomic.h" 1868d75effSDimitry Andric #include "sanitizer_common/sanitizer_flags.h" 1968d75effSDimitry Andric #include "xray/xray_interface.h" 2068d75effSDimitry Andric #include "xray/xray_log_interface.h" 2168d75effSDimitry Andric #include "xray_buffer_queue.h" 2268d75effSDimitry Andric #include "xray_flags.h" 2368d75effSDimitry Andric #include "xray_profile_collector.h" 2468d75effSDimitry Andric #include "xray_profiling_flags.h" 2568d75effSDimitry Andric #include "xray_recursion_guard.h" 2668d75effSDimitry Andric #include "xray_tsc.h" 2768d75effSDimitry Andric #include "xray_utils.h" 2868d75effSDimitry Andric #include <pthread.h> 2968d75effSDimitry Andric 3068d75effSDimitry Andric namespace __xray { 3168d75effSDimitry Andric 3268d75effSDimitry Andric namespace { 3368d75effSDimitry Andric 3468d75effSDimitry Andric static atomic_sint32_t ProfilerLogFlushStatus = { 3568d75effSDimitry Andric XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING}; 3668d75effSDimitry Andric 3768d75effSDimitry Andric static atomic_sint32_t ProfilerLogStatus = { 3868d75effSDimitry Andric XRayLogInitStatus::XRAY_LOG_UNINITIALIZED}; 3968d75effSDimitry Andric 4068d75effSDimitry Andric static SpinMutex ProfilerOptionsMutex; 4168d75effSDimitry Andric 4268d75effSDimitry Andric struct ProfilingData { 4368d75effSDimitry Andric atomic_uintptr_t Allocators; 4468d75effSDimitry Andric atomic_uintptr_t FCT; 4568d75effSDimitry Andric }; 4668d75effSDimitry Andric 4768d75effSDimitry Andric static pthread_key_t ProfilingKey; 4868d75effSDimitry Andric 4968d75effSDimitry Andric // We use a global buffer queue, which gets initialized once at initialisation 5068d75effSDimitry Andric // time, and gets reset when profiling is "done". 5168d75effSDimitry Andric static std::aligned_storage<sizeof(BufferQueue), alignof(BufferQueue)>::type 5268d75effSDimitry Andric BufferQueueStorage; 5368d75effSDimitry Andric static BufferQueue *BQ = nullptr; 5468d75effSDimitry Andric 5568d75effSDimitry Andric thread_local FunctionCallTrie::Allocators::Buffers ThreadBuffers; 5668d75effSDimitry Andric thread_local std::aligned_storage<sizeof(FunctionCallTrie::Allocators), 5768d75effSDimitry Andric alignof(FunctionCallTrie::Allocators)>::type 5868d75effSDimitry Andric AllocatorsStorage; 5968d75effSDimitry Andric thread_local std::aligned_storage<sizeof(FunctionCallTrie), 6068d75effSDimitry Andric alignof(FunctionCallTrie)>::type 6168d75effSDimitry Andric FunctionCallTrieStorage; 6268d75effSDimitry Andric thread_local ProfilingData TLD{{0}, {0}}; 6368d75effSDimitry Andric thread_local atomic_uint8_t ReentranceGuard{0}; 6468d75effSDimitry Andric 6568d75effSDimitry Andric // We use a separate guard for ensuring that for this thread, if we're already 6668d75effSDimitry Andric // cleaning up, that any signal handlers don't attempt to cleanup nor 6768d75effSDimitry Andric // initialise. 6868d75effSDimitry Andric thread_local atomic_uint8_t TLDInitGuard{0}; 6968d75effSDimitry Andric 7068d75effSDimitry Andric // We also use a separate latch to signal that the thread is exiting, and 7168d75effSDimitry Andric // non-essential work should be ignored (things like recording events, etc.). 7268d75effSDimitry Andric thread_local atomic_uint8_t ThreadExitingLatch{0}; 7368d75effSDimitry Andric 7468d75effSDimitry Andric static ProfilingData *getThreadLocalData() XRAY_NEVER_INSTRUMENT { 7568d75effSDimitry Andric thread_local auto ThreadOnce = []() XRAY_NEVER_INSTRUMENT { 7668d75effSDimitry Andric pthread_setspecific(ProfilingKey, &TLD); 7768d75effSDimitry Andric return false; 7868d75effSDimitry Andric }(); 7968d75effSDimitry Andric (void)ThreadOnce; 8068d75effSDimitry Andric 8168d75effSDimitry Andric RecursionGuard TLDInit(TLDInitGuard); 8268d75effSDimitry Andric if (!TLDInit) 8368d75effSDimitry Andric return nullptr; 8468d75effSDimitry Andric 8568d75effSDimitry Andric if (atomic_load_relaxed(&ThreadExitingLatch)) 8668d75effSDimitry Andric return nullptr; 8768d75effSDimitry Andric 8868d75effSDimitry Andric uptr Allocators = 0; 8968d75effSDimitry Andric if (atomic_compare_exchange_strong(&TLD.Allocators, &Allocators, 1, 9068d75effSDimitry Andric memory_order_acq_rel)) { 9168d75effSDimitry Andric bool Success = false; 9268d75effSDimitry Andric auto AllocatorsUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT { 9368d75effSDimitry Andric if (!Success) 9468d75effSDimitry Andric atomic_store(&TLD.Allocators, 0, memory_order_release); 9568d75effSDimitry Andric }); 9668d75effSDimitry Andric 9768d75effSDimitry Andric // Acquire a set of buffers for this thread. 9868d75effSDimitry Andric if (BQ == nullptr) 9968d75effSDimitry Andric return nullptr; 10068d75effSDimitry Andric 10168d75effSDimitry Andric if (BQ->getBuffer(ThreadBuffers.NodeBuffer) != BufferQueue::ErrorCode::Ok) 10268d75effSDimitry Andric return nullptr; 10368d75effSDimitry Andric auto NodeBufferUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT { 10468d75effSDimitry Andric if (!Success) 10568d75effSDimitry Andric BQ->releaseBuffer(ThreadBuffers.NodeBuffer); 10668d75effSDimitry Andric }); 10768d75effSDimitry Andric 10868d75effSDimitry Andric if (BQ->getBuffer(ThreadBuffers.RootsBuffer) != BufferQueue::ErrorCode::Ok) 10968d75effSDimitry Andric return nullptr; 11068d75effSDimitry Andric auto RootsBufferUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT { 11168d75effSDimitry Andric if (!Success) 11268d75effSDimitry Andric BQ->releaseBuffer(ThreadBuffers.RootsBuffer); 11368d75effSDimitry Andric }); 11468d75effSDimitry Andric 11568d75effSDimitry Andric if (BQ->getBuffer(ThreadBuffers.ShadowStackBuffer) != 11668d75effSDimitry Andric BufferQueue::ErrorCode::Ok) 11768d75effSDimitry Andric return nullptr; 11868d75effSDimitry Andric auto ShadowStackBufferUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT { 11968d75effSDimitry Andric if (!Success) 12068d75effSDimitry Andric BQ->releaseBuffer(ThreadBuffers.ShadowStackBuffer); 12168d75effSDimitry Andric }); 12268d75effSDimitry Andric 12368d75effSDimitry Andric if (BQ->getBuffer(ThreadBuffers.NodeIdPairBuffer) != 12468d75effSDimitry Andric BufferQueue::ErrorCode::Ok) 12568d75effSDimitry Andric return nullptr; 12668d75effSDimitry Andric 12768d75effSDimitry Andric Success = true; 12868d75effSDimitry Andric new (&AllocatorsStorage) FunctionCallTrie::Allocators( 12968d75effSDimitry Andric FunctionCallTrie::InitAllocatorsFromBuffers(ThreadBuffers)); 13068d75effSDimitry Andric Allocators = reinterpret_cast<uptr>( 13168d75effSDimitry Andric reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage)); 13268d75effSDimitry Andric atomic_store(&TLD.Allocators, Allocators, memory_order_release); 13368d75effSDimitry Andric } 13468d75effSDimitry Andric 13568d75effSDimitry Andric if (Allocators == 1) 13668d75effSDimitry Andric return nullptr; 13768d75effSDimitry Andric 13868d75effSDimitry Andric uptr FCT = 0; 13968d75effSDimitry Andric if (atomic_compare_exchange_strong(&TLD.FCT, &FCT, 1, memory_order_acq_rel)) { 14068d75effSDimitry Andric new (&FunctionCallTrieStorage) 14168d75effSDimitry Andric FunctionCallTrie(*reinterpret_cast<FunctionCallTrie::Allocators *>( 14268d75effSDimitry Andric atomic_load_relaxed(&TLD.Allocators))); 14368d75effSDimitry Andric FCT = reinterpret_cast<uptr>( 14468d75effSDimitry Andric reinterpret_cast<FunctionCallTrie *>(&FunctionCallTrieStorage)); 14568d75effSDimitry Andric atomic_store(&TLD.FCT, FCT, memory_order_release); 14668d75effSDimitry Andric } 14768d75effSDimitry Andric 14868d75effSDimitry Andric if (FCT == 1) 14968d75effSDimitry Andric return nullptr; 15068d75effSDimitry Andric 15168d75effSDimitry Andric return &TLD; 15268d75effSDimitry Andric } 15368d75effSDimitry Andric 15468d75effSDimitry Andric static void cleanupTLD() XRAY_NEVER_INSTRUMENT { 15568d75effSDimitry Andric auto FCT = atomic_exchange(&TLD.FCT, 0, memory_order_acq_rel); 15668d75effSDimitry Andric if (FCT == reinterpret_cast<uptr>(reinterpret_cast<FunctionCallTrie *>( 15768d75effSDimitry Andric &FunctionCallTrieStorage))) 15868d75effSDimitry Andric reinterpret_cast<FunctionCallTrie *>(FCT)->~FunctionCallTrie(); 15968d75effSDimitry Andric 16068d75effSDimitry Andric auto Allocators = atomic_exchange(&TLD.Allocators, 0, memory_order_acq_rel); 16168d75effSDimitry Andric if (Allocators == 16268d75effSDimitry Andric reinterpret_cast<uptr>( 16368d75effSDimitry Andric reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage))) 16468d75effSDimitry Andric reinterpret_cast<FunctionCallTrie::Allocators *>(Allocators)->~Allocators(); 16568d75effSDimitry Andric } 16668d75effSDimitry Andric 16768d75effSDimitry Andric static void postCurrentThreadFCT(ProfilingData &T) XRAY_NEVER_INSTRUMENT { 16868d75effSDimitry Andric RecursionGuard TLDInit(TLDInitGuard); 16968d75effSDimitry Andric if (!TLDInit) 17068d75effSDimitry Andric return; 17168d75effSDimitry Andric 17268d75effSDimitry Andric uptr P = atomic_exchange(&T.FCT, 0, memory_order_acq_rel); 17368d75effSDimitry Andric if (P != reinterpret_cast<uptr>( 17468d75effSDimitry Andric reinterpret_cast<FunctionCallTrie *>(&FunctionCallTrieStorage))) 17568d75effSDimitry Andric return; 17668d75effSDimitry Andric 17768d75effSDimitry Andric auto FCT = reinterpret_cast<FunctionCallTrie *>(P); 17868d75effSDimitry Andric DCHECK_NE(FCT, nullptr); 17968d75effSDimitry Andric 18068d75effSDimitry Andric uptr A = atomic_exchange(&T.Allocators, 0, memory_order_acq_rel); 18168d75effSDimitry Andric if (A != 18268d75effSDimitry Andric reinterpret_cast<uptr>( 18368d75effSDimitry Andric reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage))) 18468d75effSDimitry Andric return; 18568d75effSDimitry Andric 18668d75effSDimitry Andric auto Allocators = reinterpret_cast<FunctionCallTrie::Allocators *>(A); 18768d75effSDimitry Andric DCHECK_NE(Allocators, nullptr); 18868d75effSDimitry Andric 18968d75effSDimitry Andric // Always move the data into the profile collector. 19068d75effSDimitry Andric profileCollectorService::post(BQ, std::move(*FCT), std::move(*Allocators), 19168d75effSDimitry Andric std::move(ThreadBuffers), GetTid()); 19268d75effSDimitry Andric 19368d75effSDimitry Andric // Re-initialize the ThreadBuffers object to a known "default" state. 19468d75effSDimitry Andric ThreadBuffers = FunctionCallTrie::Allocators::Buffers{}; 19568d75effSDimitry Andric } 19668d75effSDimitry Andric 19768d75effSDimitry Andric } // namespace 19868d75effSDimitry Andric 19968d75effSDimitry Andric const char *profilingCompilerDefinedFlags() XRAY_NEVER_INSTRUMENT { 20068d75effSDimitry Andric #ifdef XRAY_PROFILER_DEFAULT_OPTIONS 20168d75effSDimitry Andric return SANITIZER_STRINGIFY(XRAY_PROFILER_DEFAULT_OPTIONS); 20268d75effSDimitry Andric #else 20368d75effSDimitry Andric return ""; 20468d75effSDimitry Andric #endif 20568d75effSDimitry Andric } 20668d75effSDimitry Andric 20768d75effSDimitry Andric XRayLogFlushStatus profilingFlush() XRAY_NEVER_INSTRUMENT { 20868d75effSDimitry Andric if (atomic_load(&ProfilerLogStatus, memory_order_acquire) != 20968d75effSDimitry Andric XRayLogInitStatus::XRAY_LOG_FINALIZED) { 21068d75effSDimitry Andric if (Verbosity()) 21168d75effSDimitry Andric Report("Not flushing profiles, profiling not been finalized.\n"); 21268d75effSDimitry Andric return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING; 21368d75effSDimitry Andric } 21468d75effSDimitry Andric 21568d75effSDimitry Andric RecursionGuard SignalGuard(ReentranceGuard); 21668d75effSDimitry Andric if (!SignalGuard) { 21768d75effSDimitry Andric if (Verbosity()) 21868d75effSDimitry Andric Report("Cannot finalize properly inside a signal handler!\n"); 21968d75effSDimitry Andric atomic_store(&ProfilerLogFlushStatus, 22068d75effSDimitry Andric XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING, 22168d75effSDimitry Andric memory_order_release); 22268d75effSDimitry Andric return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING; 22368d75effSDimitry Andric } 22468d75effSDimitry Andric 22568d75effSDimitry Andric s32 Previous = atomic_exchange(&ProfilerLogFlushStatus, 22668d75effSDimitry Andric XRayLogFlushStatus::XRAY_LOG_FLUSHING, 22768d75effSDimitry Andric memory_order_acq_rel); 22868d75effSDimitry Andric if (Previous == XRayLogFlushStatus::XRAY_LOG_FLUSHING) { 22968d75effSDimitry Andric if (Verbosity()) 23068d75effSDimitry Andric Report("Not flushing profiles, implementation still flushing.\n"); 23168d75effSDimitry Andric return XRayLogFlushStatus::XRAY_LOG_FLUSHING; 23268d75effSDimitry Andric } 23368d75effSDimitry Andric 23468d75effSDimitry Andric // At this point, we'll create the file that will contain the profile, but 23568d75effSDimitry Andric // only if the options say so. 23668d75effSDimitry Andric if (!profilingFlags()->no_flush) { 23768d75effSDimitry Andric // First check whether we have data in the profile collector service 23868d75effSDimitry Andric // before we try and write anything down. 23968d75effSDimitry Andric XRayBuffer B = profileCollectorService::nextBuffer({nullptr, 0}); 24068d75effSDimitry Andric if (B.Data == nullptr) { 24168d75effSDimitry Andric if (Verbosity()) 24268d75effSDimitry Andric Report("profiling: No data to flush.\n"); 24368d75effSDimitry Andric } else { 24468d75effSDimitry Andric LogWriter *LW = LogWriter::Open(); 24568d75effSDimitry Andric if (LW == nullptr) { 24668d75effSDimitry Andric if (Verbosity()) 24768d75effSDimitry Andric Report("profiling: Failed to flush to file, dropping data.\n"); 24868d75effSDimitry Andric } else { 24968d75effSDimitry Andric // Now for each of the buffers, write out the profile data as we would 25068d75effSDimitry Andric // see it in memory, verbatim. 25168d75effSDimitry Andric while (B.Data != nullptr && B.Size != 0) { 25268d75effSDimitry Andric LW->WriteAll(reinterpret_cast<const char *>(B.Data), 25368d75effSDimitry Andric reinterpret_cast<const char *>(B.Data) + B.Size); 25468d75effSDimitry Andric B = profileCollectorService::nextBuffer(B); 25568d75effSDimitry Andric } 25668d75effSDimitry Andric } 25768d75effSDimitry Andric LogWriter::Close(LW); 25868d75effSDimitry Andric } 25968d75effSDimitry Andric } 26068d75effSDimitry Andric 26168d75effSDimitry Andric profileCollectorService::reset(); 26268d75effSDimitry Andric 26368d75effSDimitry Andric atomic_store(&ProfilerLogFlushStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED, 26468d75effSDimitry Andric memory_order_release); 26568d75effSDimitry Andric atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_UNINITIALIZED, 26668d75effSDimitry Andric memory_order_release); 26768d75effSDimitry Andric 26868d75effSDimitry Andric return XRayLogFlushStatus::XRAY_LOG_FLUSHED; 26968d75effSDimitry Andric } 27068d75effSDimitry Andric 27168d75effSDimitry Andric void profilingHandleArg0(int32_t FuncId, 27268d75effSDimitry Andric XRayEntryType Entry) XRAY_NEVER_INSTRUMENT { 27368d75effSDimitry Andric unsigned char CPU; 27468d75effSDimitry Andric auto TSC = readTSC(CPU); 27568d75effSDimitry Andric RecursionGuard G(ReentranceGuard); 27668d75effSDimitry Andric if (!G) 27768d75effSDimitry Andric return; 27868d75effSDimitry Andric 27968d75effSDimitry Andric auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire); 28068d75effSDimitry Andric if (UNLIKELY(Status == XRayLogInitStatus::XRAY_LOG_UNINITIALIZED || 28168d75effSDimitry Andric Status == XRayLogInitStatus::XRAY_LOG_INITIALIZING)) 28268d75effSDimitry Andric return; 28368d75effSDimitry Andric 28468d75effSDimitry Andric if (UNLIKELY(Status == XRayLogInitStatus::XRAY_LOG_FINALIZED || 28568d75effSDimitry Andric Status == XRayLogInitStatus::XRAY_LOG_FINALIZING)) { 28668d75effSDimitry Andric postCurrentThreadFCT(TLD); 28768d75effSDimitry Andric return; 28868d75effSDimitry Andric } 28968d75effSDimitry Andric 29068d75effSDimitry Andric auto T = getThreadLocalData(); 29168d75effSDimitry Andric if (T == nullptr) 29268d75effSDimitry Andric return; 29368d75effSDimitry Andric 29468d75effSDimitry Andric auto FCT = reinterpret_cast<FunctionCallTrie *>(atomic_load_relaxed(&T->FCT)); 29568d75effSDimitry Andric switch (Entry) { 29668d75effSDimitry Andric case XRayEntryType::ENTRY: 29768d75effSDimitry Andric case XRayEntryType::LOG_ARGS_ENTRY: 29868d75effSDimitry Andric FCT->enterFunction(FuncId, TSC, CPU); 29968d75effSDimitry Andric break; 30068d75effSDimitry Andric case XRayEntryType::EXIT: 30168d75effSDimitry Andric case XRayEntryType::TAIL: 30268d75effSDimitry Andric FCT->exitFunction(FuncId, TSC, CPU); 30368d75effSDimitry Andric break; 30468d75effSDimitry Andric default: 30568d75effSDimitry Andric // FIXME: Handle bugs. 30668d75effSDimitry Andric break; 30768d75effSDimitry Andric } 30868d75effSDimitry Andric } 30968d75effSDimitry Andric 31068d75effSDimitry Andric void profilingHandleArg1(int32_t FuncId, XRayEntryType Entry, 31168d75effSDimitry Andric uint64_t) XRAY_NEVER_INSTRUMENT { 31268d75effSDimitry Andric return profilingHandleArg0(FuncId, Entry); 31368d75effSDimitry Andric } 31468d75effSDimitry Andric 31568d75effSDimitry Andric XRayLogInitStatus profilingFinalize() XRAY_NEVER_INSTRUMENT { 31668d75effSDimitry Andric s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED; 31768d75effSDimitry Andric if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus, 31868d75effSDimitry Andric XRayLogInitStatus::XRAY_LOG_FINALIZING, 31968d75effSDimitry Andric memory_order_release)) { 32068d75effSDimitry Andric if (Verbosity()) 32168d75effSDimitry Andric Report("Cannot finalize profile, the profiling is not initialized.\n"); 32268d75effSDimitry Andric return static_cast<XRayLogInitStatus>(CurrentStatus); 32368d75effSDimitry Andric } 32468d75effSDimitry Andric 32568d75effSDimitry Andric // Mark then finalize the current generation of buffers. This allows us to let 32668d75effSDimitry Andric // the threads currently holding onto new buffers still use them, but let the 32768d75effSDimitry Andric // last reference do the memory cleanup. 32868d75effSDimitry Andric DCHECK_NE(BQ, nullptr); 32968d75effSDimitry Andric BQ->finalize(); 33068d75effSDimitry Andric 33168d75effSDimitry Andric // Wait a grace period to allow threads to see that we're finalizing. 33268d75effSDimitry Andric SleepForMillis(profilingFlags()->grace_period_ms); 33368d75effSDimitry Andric 33468d75effSDimitry Andric // If we for some reason are entering this function from an instrumented 33568d75effSDimitry Andric // handler, we bail out. 33668d75effSDimitry Andric RecursionGuard G(ReentranceGuard); 33768d75effSDimitry Andric if (!G) 33868d75effSDimitry Andric return static_cast<XRayLogInitStatus>(CurrentStatus); 33968d75effSDimitry Andric 34068d75effSDimitry Andric // Post the current thread's data if we have any. 34168d75effSDimitry Andric postCurrentThreadFCT(TLD); 34268d75effSDimitry Andric 34368d75effSDimitry Andric // Then we force serialize the log data. 34468d75effSDimitry Andric profileCollectorService::serialize(); 34568d75effSDimitry Andric 34668d75effSDimitry Andric atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_FINALIZED, 34768d75effSDimitry Andric memory_order_release); 34868d75effSDimitry Andric return XRayLogInitStatus::XRAY_LOG_FINALIZED; 34968d75effSDimitry Andric } 35068d75effSDimitry Andric 35168d75effSDimitry Andric XRayLogInitStatus 35268d75effSDimitry Andric profilingLoggingInit(size_t, size_t, void *Options, 35368d75effSDimitry Andric size_t OptionsSize) XRAY_NEVER_INSTRUMENT { 35468d75effSDimitry Andric RecursionGuard G(ReentranceGuard); 35568d75effSDimitry Andric if (!G) 35668d75effSDimitry Andric return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; 35768d75effSDimitry Andric 35868d75effSDimitry Andric s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; 35968d75effSDimitry Andric if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus, 36068d75effSDimitry Andric XRayLogInitStatus::XRAY_LOG_INITIALIZING, 36168d75effSDimitry Andric memory_order_acq_rel)) { 36268d75effSDimitry Andric if (Verbosity()) 36368d75effSDimitry Andric Report("Cannot initialize already initialised profiling " 36468d75effSDimitry Andric "implementation.\n"); 36568d75effSDimitry Andric return static_cast<XRayLogInitStatus>(CurrentStatus); 36668d75effSDimitry Andric } 36768d75effSDimitry Andric 36868d75effSDimitry Andric { 36968d75effSDimitry Andric SpinMutexLock Lock(&ProfilerOptionsMutex); 37068d75effSDimitry Andric FlagParser ConfigParser; 37168d75effSDimitry Andric ProfilerFlags Flags; 37268d75effSDimitry Andric Flags.setDefaults(); 37368d75effSDimitry Andric registerProfilerFlags(&ConfigParser, &Flags); 37468d75effSDimitry Andric ConfigParser.ParseString(profilingCompilerDefinedFlags()); 37568d75effSDimitry Andric const char *Env = GetEnv("XRAY_PROFILING_OPTIONS"); 37668d75effSDimitry Andric if (Env == nullptr) 37768d75effSDimitry Andric Env = ""; 37868d75effSDimitry Andric ConfigParser.ParseString(Env); 37968d75effSDimitry Andric 38068d75effSDimitry Andric // Then parse the configuration string provided. 38168d75effSDimitry Andric ConfigParser.ParseString(static_cast<const char *>(Options)); 38268d75effSDimitry Andric if (Verbosity()) 38368d75effSDimitry Andric ReportUnrecognizedFlags(); 38468d75effSDimitry Andric *profilingFlags() = Flags; 38568d75effSDimitry Andric } 38668d75effSDimitry Andric 38768d75effSDimitry Andric // We need to reset the profile data collection implementation now. 38868d75effSDimitry Andric profileCollectorService::reset(); 38968d75effSDimitry Andric 39068d75effSDimitry Andric // Then also reset the buffer queue implementation. 39168d75effSDimitry Andric if (BQ == nullptr) { 39268d75effSDimitry Andric bool Success = false; 39368d75effSDimitry Andric new (&BufferQueueStorage) 39468d75effSDimitry Andric BufferQueue(profilingFlags()->per_thread_allocator_max, 39568d75effSDimitry Andric profilingFlags()->buffers_max, Success); 39668d75effSDimitry Andric if (!Success) { 39768d75effSDimitry Andric if (Verbosity()) 39868d75effSDimitry Andric Report("Failed to initialize preallocated memory buffers!"); 39968d75effSDimitry Andric atomic_store(&ProfilerLogStatus, 40068d75effSDimitry Andric XRayLogInitStatus::XRAY_LOG_UNINITIALIZED, 40168d75effSDimitry Andric memory_order_release); 40268d75effSDimitry Andric return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; 40368d75effSDimitry Andric } 40468d75effSDimitry Andric 405*349cc55cSDimitry Andric // If we've succeeded, set the global pointer to the initialised storage. 40668d75effSDimitry Andric BQ = reinterpret_cast<BufferQueue *>(&BufferQueueStorage); 40768d75effSDimitry Andric } else { 40868d75effSDimitry Andric BQ->finalize(); 40968d75effSDimitry Andric auto InitStatus = BQ->init(profilingFlags()->per_thread_allocator_max, 41068d75effSDimitry Andric profilingFlags()->buffers_max); 41168d75effSDimitry Andric 41268d75effSDimitry Andric if (InitStatus != BufferQueue::ErrorCode::Ok) { 41368d75effSDimitry Andric if (Verbosity()) 41468d75effSDimitry Andric Report("Failed to initialize preallocated memory buffers; error: %s", 41568d75effSDimitry Andric BufferQueue::getErrorString(InitStatus)); 41668d75effSDimitry Andric atomic_store(&ProfilerLogStatus, 41768d75effSDimitry Andric XRayLogInitStatus::XRAY_LOG_UNINITIALIZED, 41868d75effSDimitry Andric memory_order_release); 41968d75effSDimitry Andric return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED; 42068d75effSDimitry Andric } 42168d75effSDimitry Andric 42268d75effSDimitry Andric DCHECK(!BQ->finalizing()); 42368d75effSDimitry Andric } 42468d75effSDimitry Andric 42568d75effSDimitry Andric // We need to set up the exit handlers. 42668d75effSDimitry Andric static pthread_once_t Once = PTHREAD_ONCE_INIT; 42768d75effSDimitry Andric pthread_once( 42868d75effSDimitry Andric &Once, +[] { 42968d75effSDimitry Andric pthread_key_create( 43068d75effSDimitry Andric &ProfilingKey, +[](void *P) XRAY_NEVER_INSTRUMENT { 43168d75effSDimitry Andric if (atomic_exchange(&ThreadExitingLatch, 1, memory_order_acq_rel)) 43268d75effSDimitry Andric return; 43368d75effSDimitry Andric 43468d75effSDimitry Andric if (P == nullptr) 43568d75effSDimitry Andric return; 43668d75effSDimitry Andric 43768d75effSDimitry Andric auto T = reinterpret_cast<ProfilingData *>(P); 43868d75effSDimitry Andric if (atomic_load_relaxed(&T->Allocators) == 0) 43968d75effSDimitry Andric return; 44068d75effSDimitry Andric 44168d75effSDimitry Andric { 44268d75effSDimitry Andric // If we're somehow executing this while inside a 44368d75effSDimitry Andric // non-reentrant-friendly context, we skip attempting to post 44468d75effSDimitry Andric // the current thread's data. 44568d75effSDimitry Andric RecursionGuard G(ReentranceGuard); 44668d75effSDimitry Andric if (!G) 44768d75effSDimitry Andric return; 44868d75effSDimitry Andric 44968d75effSDimitry Andric postCurrentThreadFCT(*T); 45068d75effSDimitry Andric } 45168d75effSDimitry Andric }); 45268d75effSDimitry Andric 45368d75effSDimitry Andric // We also need to set up an exit handler, so that we can get the 45468d75effSDimitry Andric // profile information at exit time. We use the C API to do this, to not 45568d75effSDimitry Andric // rely on C++ ABI functions for registering exit handlers. 45668d75effSDimitry Andric Atexit(+[]() XRAY_NEVER_INSTRUMENT { 45768d75effSDimitry Andric if (atomic_exchange(&ThreadExitingLatch, 1, memory_order_acq_rel)) 45868d75effSDimitry Andric return; 45968d75effSDimitry Andric 46068d75effSDimitry Andric auto Cleanup = 46168d75effSDimitry Andric at_scope_exit([]() XRAY_NEVER_INSTRUMENT { cleanupTLD(); }); 46268d75effSDimitry Andric 46368d75effSDimitry Andric // Finalize and flush. 46468d75effSDimitry Andric if (profilingFinalize() != XRAY_LOG_FINALIZED || 46568d75effSDimitry Andric profilingFlush() != XRAY_LOG_FLUSHED) 46668d75effSDimitry Andric return; 46768d75effSDimitry Andric 46868d75effSDimitry Andric if (Verbosity()) 46968d75effSDimitry Andric Report("XRay Profile flushed at exit."); 47068d75effSDimitry Andric }); 47168d75effSDimitry Andric }); 47268d75effSDimitry Andric 47368d75effSDimitry Andric __xray_log_set_buffer_iterator(profileCollectorService::nextBuffer); 47468d75effSDimitry Andric __xray_set_handler(profilingHandleArg0); 47568d75effSDimitry Andric __xray_set_handler_arg1(profilingHandleArg1); 47668d75effSDimitry Andric 47768d75effSDimitry Andric atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZED, 47868d75effSDimitry Andric memory_order_release); 47968d75effSDimitry Andric if (Verbosity()) 48068d75effSDimitry Andric Report("XRay Profiling init successful.\n"); 48168d75effSDimitry Andric 48268d75effSDimitry Andric return XRayLogInitStatus::XRAY_LOG_INITIALIZED; 48368d75effSDimitry Andric } 48468d75effSDimitry Andric 48568d75effSDimitry Andric bool profilingDynamicInitializer() XRAY_NEVER_INSTRUMENT { 48668d75effSDimitry Andric // Set up the flag defaults from the static defaults and the 48768d75effSDimitry Andric // compiler-provided defaults. 48868d75effSDimitry Andric { 48968d75effSDimitry Andric SpinMutexLock Lock(&ProfilerOptionsMutex); 49068d75effSDimitry Andric auto *F = profilingFlags(); 49168d75effSDimitry Andric F->setDefaults(); 49268d75effSDimitry Andric FlagParser ProfilingParser; 49368d75effSDimitry Andric registerProfilerFlags(&ProfilingParser, F); 49468d75effSDimitry Andric ProfilingParser.ParseString(profilingCompilerDefinedFlags()); 49568d75effSDimitry Andric } 49668d75effSDimitry Andric 49768d75effSDimitry Andric XRayLogImpl Impl{ 49868d75effSDimitry Andric profilingLoggingInit, 49968d75effSDimitry Andric profilingFinalize, 50068d75effSDimitry Andric profilingHandleArg0, 50168d75effSDimitry Andric profilingFlush, 50268d75effSDimitry Andric }; 50368d75effSDimitry Andric auto RegistrationResult = __xray_log_register_mode("xray-profiling", Impl); 50468d75effSDimitry Andric if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK) { 50568d75effSDimitry Andric if (Verbosity()) 50668d75effSDimitry Andric Report("Cannot register XRay Profiling mode to 'xray-profiling'; error = " 50768d75effSDimitry Andric "%d\n", 50868d75effSDimitry Andric RegistrationResult); 50968d75effSDimitry Andric return false; 51068d75effSDimitry Andric } 51168d75effSDimitry Andric 51268d75effSDimitry Andric if (!internal_strcmp(flags()->xray_mode, "xray-profiling")) 51368d75effSDimitry Andric __xray_log_select_mode("xray_profiling"); 51468d75effSDimitry Andric return true; 51568d75effSDimitry Andric } 51668d75effSDimitry Andric 51768d75effSDimitry Andric } // namespace __xray 51868d75effSDimitry Andric 51968d75effSDimitry Andric static auto UNUSED Unused = __xray::profilingDynamicInitializer(); 520