xref: /freebsd/contrib/llvm-project/compiler-rt/lib/xray/xray_profiling.cpp (revision 68d75eff68281c1b445e3010bb975eae07aac225)
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