xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/xray/xray_basic_logging.cpp (revision 68d75eff68281c1b445e3010bb975eae07aac225)
1*68d75effSDimitry Andric //===-- xray_basic_logging.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 // Implementation of a simple in-memory log of XRay events. This defines a
12*68d75effSDimitry Andric // logging function that's compatible with the XRay handler interface, and
13*68d75effSDimitry Andric // routines for exporting data to files.
14*68d75effSDimitry Andric //
15*68d75effSDimitry Andric //===----------------------------------------------------------------------===//
16*68d75effSDimitry Andric 
17*68d75effSDimitry Andric #include <errno.h>
18*68d75effSDimitry Andric #include <fcntl.h>
19*68d75effSDimitry Andric #include <pthread.h>
20*68d75effSDimitry Andric #include <sys/stat.h>
21*68d75effSDimitry Andric #if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD || SANITIZER_MAC
22*68d75effSDimitry Andric #include <sys/syscall.h>
23*68d75effSDimitry Andric #endif
24*68d75effSDimitry Andric #include <sys/types.h>
25*68d75effSDimitry Andric #include <time.h>
26*68d75effSDimitry Andric #include <unistd.h>
27*68d75effSDimitry Andric 
28*68d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator_internal.h"
29*68d75effSDimitry Andric #include "sanitizer_common/sanitizer_libc.h"
30*68d75effSDimitry Andric #include "xray/xray_records.h"
31*68d75effSDimitry Andric #include "xray_recursion_guard.h"
32*68d75effSDimitry Andric #include "xray_basic_flags.h"
33*68d75effSDimitry Andric #include "xray_basic_logging.h"
34*68d75effSDimitry Andric #include "xray_defs.h"
35*68d75effSDimitry Andric #include "xray_flags.h"
36*68d75effSDimitry Andric #include "xray_interface_internal.h"
37*68d75effSDimitry Andric #include "xray_tsc.h"
38*68d75effSDimitry Andric #include "xray_utils.h"
39*68d75effSDimitry Andric 
40*68d75effSDimitry Andric namespace __xray {
41*68d75effSDimitry Andric 
42*68d75effSDimitry Andric static SpinMutex LogMutex;
43*68d75effSDimitry Andric 
44*68d75effSDimitry Andric namespace {
45*68d75effSDimitry Andric // We use elements of this type to record the entry TSC of every function ID we
46*68d75effSDimitry Andric // see as we're tracing a particular thread's execution.
47*68d75effSDimitry Andric struct alignas(16) StackEntry {
48*68d75effSDimitry Andric   int32_t FuncId;
49*68d75effSDimitry Andric   uint16_t Type;
50*68d75effSDimitry Andric   uint8_t CPU;
51*68d75effSDimitry Andric   uint8_t Padding;
52*68d75effSDimitry Andric   uint64_t TSC;
53*68d75effSDimitry Andric };
54*68d75effSDimitry Andric 
55*68d75effSDimitry Andric static_assert(sizeof(StackEntry) == 16, "Wrong size for StackEntry");
56*68d75effSDimitry Andric 
57*68d75effSDimitry Andric struct XRAY_TLS_ALIGNAS(64) ThreadLocalData {
58*68d75effSDimitry Andric   void *InMemoryBuffer = nullptr;
59*68d75effSDimitry Andric   size_t BufferSize = 0;
60*68d75effSDimitry Andric   size_t BufferOffset = 0;
61*68d75effSDimitry Andric   void *ShadowStack = nullptr;
62*68d75effSDimitry Andric   size_t StackSize = 0;
63*68d75effSDimitry Andric   size_t StackEntries = 0;
64*68d75effSDimitry Andric   __xray::LogWriter *LogWriter = nullptr;
65*68d75effSDimitry Andric };
66*68d75effSDimitry Andric 
67*68d75effSDimitry Andric struct BasicLoggingOptions {
68*68d75effSDimitry Andric   int DurationFilterMicros = 0;
69*68d75effSDimitry Andric   size_t MaxStackDepth = 0;
70*68d75effSDimitry Andric   size_t ThreadBufferSize = 0;
71*68d75effSDimitry Andric };
72*68d75effSDimitry Andric } // namespace
73*68d75effSDimitry Andric 
74*68d75effSDimitry Andric static pthread_key_t PThreadKey;
75*68d75effSDimitry Andric 
76*68d75effSDimitry Andric static atomic_uint8_t BasicInitialized{0};
77*68d75effSDimitry Andric 
78*68d75effSDimitry Andric struct BasicLoggingOptions GlobalOptions;
79*68d75effSDimitry Andric 
80*68d75effSDimitry Andric thread_local atomic_uint8_t Guard{0};
81*68d75effSDimitry Andric 
82*68d75effSDimitry Andric static atomic_uint8_t UseRealTSC{0};
83*68d75effSDimitry Andric static atomic_uint64_t ThresholdTicks{0};
84*68d75effSDimitry Andric static atomic_uint64_t TicksPerSec{0};
85*68d75effSDimitry Andric static atomic_uint64_t CycleFrequency{NanosecondsPerSecond};
86*68d75effSDimitry Andric 
87*68d75effSDimitry Andric static LogWriter *getLog() XRAY_NEVER_INSTRUMENT {
88*68d75effSDimitry Andric   LogWriter* LW = LogWriter::Open();
89*68d75effSDimitry Andric   if (LW == nullptr)
90*68d75effSDimitry Andric     return LW;
91*68d75effSDimitry Andric 
92*68d75effSDimitry Andric   static pthread_once_t DetectOnce = PTHREAD_ONCE_INIT;
93*68d75effSDimitry Andric   pthread_once(&DetectOnce, +[] {
94*68d75effSDimitry Andric     if (atomic_load(&UseRealTSC, memory_order_acquire))
95*68d75effSDimitry Andric       atomic_store(&CycleFrequency, getTSCFrequency(), memory_order_release);
96*68d75effSDimitry Andric   });
97*68d75effSDimitry Andric 
98*68d75effSDimitry Andric   // Since we're here, we get to write the header. We set it up so that the
99*68d75effSDimitry Andric   // header will only be written once, at the start, and let the threads
100*68d75effSDimitry Andric   // logging do writes which just append.
101*68d75effSDimitry Andric   XRayFileHeader Header;
102*68d75effSDimitry Andric   // Version 2 includes tail exit records.
103*68d75effSDimitry Andric   // Version 3 includes pid inside records.
104*68d75effSDimitry Andric   Header.Version = 3;
105*68d75effSDimitry Andric   Header.Type = FileTypes::NAIVE_LOG;
106*68d75effSDimitry Andric   Header.CycleFrequency = atomic_load(&CycleFrequency, memory_order_acquire);
107*68d75effSDimitry Andric 
108*68d75effSDimitry Andric   // FIXME: Actually check whether we have 'constant_tsc' and 'nonstop_tsc'
109*68d75effSDimitry Andric   // before setting the values in the header.
110*68d75effSDimitry Andric   Header.ConstantTSC = 1;
111*68d75effSDimitry Andric   Header.NonstopTSC = 1;
112*68d75effSDimitry Andric   LW->WriteAll(reinterpret_cast<char *>(&Header),
113*68d75effSDimitry Andric                reinterpret_cast<char *>(&Header) + sizeof(Header));
114*68d75effSDimitry Andric   return LW;
115*68d75effSDimitry Andric }
116*68d75effSDimitry Andric 
117*68d75effSDimitry Andric static LogWriter *getGlobalLog() XRAY_NEVER_INSTRUMENT {
118*68d75effSDimitry Andric   static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
119*68d75effSDimitry Andric   static LogWriter *LW = nullptr;
120*68d75effSDimitry Andric   pthread_once(&OnceInit, +[] { LW = getLog(); });
121*68d75effSDimitry Andric   return LW;
122*68d75effSDimitry Andric }
123*68d75effSDimitry Andric 
124*68d75effSDimitry Andric static ThreadLocalData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
125*68d75effSDimitry Andric   thread_local ThreadLocalData TLD;
126*68d75effSDimitry Andric   thread_local bool UNUSED TOnce = [] {
127*68d75effSDimitry Andric     if (GlobalOptions.ThreadBufferSize == 0) {
128*68d75effSDimitry Andric       if (Verbosity())
129*68d75effSDimitry Andric         Report("Not initializing TLD since ThreadBufferSize == 0.\n");
130*68d75effSDimitry Andric       return false;
131*68d75effSDimitry Andric     }
132*68d75effSDimitry Andric     pthread_setspecific(PThreadKey, &TLD);
133*68d75effSDimitry Andric     TLD.LogWriter = getGlobalLog();
134*68d75effSDimitry Andric     TLD.InMemoryBuffer = reinterpret_cast<XRayRecord *>(
135*68d75effSDimitry Andric         InternalAlloc(sizeof(XRayRecord) * GlobalOptions.ThreadBufferSize,
136*68d75effSDimitry Andric                       nullptr, alignof(XRayRecord)));
137*68d75effSDimitry Andric     TLD.BufferSize = GlobalOptions.ThreadBufferSize;
138*68d75effSDimitry Andric     TLD.BufferOffset = 0;
139*68d75effSDimitry Andric     if (GlobalOptions.MaxStackDepth == 0) {
140*68d75effSDimitry Andric       if (Verbosity())
141*68d75effSDimitry Andric         Report("Not initializing the ShadowStack since MaxStackDepth == 0.\n");
142*68d75effSDimitry Andric       TLD.StackSize = 0;
143*68d75effSDimitry Andric       TLD.StackEntries = 0;
144*68d75effSDimitry Andric       TLD.ShadowStack = nullptr;
145*68d75effSDimitry Andric       return false;
146*68d75effSDimitry Andric     }
147*68d75effSDimitry Andric     TLD.ShadowStack = reinterpret_cast<StackEntry *>(
148*68d75effSDimitry Andric         InternalAlloc(sizeof(StackEntry) * GlobalOptions.MaxStackDepth, nullptr,
149*68d75effSDimitry Andric                       alignof(StackEntry)));
150*68d75effSDimitry Andric     TLD.StackSize = GlobalOptions.MaxStackDepth;
151*68d75effSDimitry Andric     TLD.StackEntries = 0;
152*68d75effSDimitry Andric     return false;
153*68d75effSDimitry Andric   }();
154*68d75effSDimitry Andric   return TLD;
155*68d75effSDimitry Andric }
156*68d75effSDimitry Andric 
157*68d75effSDimitry Andric template <class RDTSC>
158*68d75effSDimitry Andric void InMemoryRawLog(int32_t FuncId, XRayEntryType Type,
159*68d75effSDimitry Andric                     RDTSC ReadTSC) XRAY_NEVER_INSTRUMENT {
160*68d75effSDimitry Andric   auto &TLD = getThreadLocalData();
161*68d75effSDimitry Andric   LogWriter *LW = getGlobalLog();
162*68d75effSDimitry Andric   if (LW == nullptr)
163*68d75effSDimitry Andric     return;
164*68d75effSDimitry Andric 
165*68d75effSDimitry Andric   // Use a simple recursion guard, to handle cases where we're already logging
166*68d75effSDimitry Andric   // and for one reason or another, this function gets called again in the same
167*68d75effSDimitry Andric   // thread.
168*68d75effSDimitry Andric   RecursionGuard G(Guard);
169*68d75effSDimitry Andric   if (!G)
170*68d75effSDimitry Andric     return;
171*68d75effSDimitry Andric 
172*68d75effSDimitry Andric   uint8_t CPU = 0;
173*68d75effSDimitry Andric   uint64_t TSC = ReadTSC(CPU);
174*68d75effSDimitry Andric 
175*68d75effSDimitry Andric   switch (Type) {
176*68d75effSDimitry Andric   case XRayEntryType::ENTRY:
177*68d75effSDimitry Andric   case XRayEntryType::LOG_ARGS_ENTRY: {
178*68d75effSDimitry Andric     // Short circuit if we've reached the maximum depth of the stack.
179*68d75effSDimitry Andric     if (TLD.StackEntries++ >= TLD.StackSize)
180*68d75effSDimitry Andric       return;
181*68d75effSDimitry Andric 
182*68d75effSDimitry Andric     // When we encounter an entry event, we keep track of the TSC and the CPU,
183*68d75effSDimitry Andric     // and put it in the stack.
184*68d75effSDimitry Andric     StackEntry E;
185*68d75effSDimitry Andric     E.FuncId = FuncId;
186*68d75effSDimitry Andric     E.CPU = CPU;
187*68d75effSDimitry Andric     E.Type = Type;
188*68d75effSDimitry Andric     E.TSC = TSC;
189*68d75effSDimitry Andric     auto StackEntryPtr = static_cast<char *>(TLD.ShadowStack) +
190*68d75effSDimitry Andric                          (sizeof(StackEntry) * (TLD.StackEntries - 1));
191*68d75effSDimitry Andric     internal_memcpy(StackEntryPtr, &E, sizeof(StackEntry));
192*68d75effSDimitry Andric     break;
193*68d75effSDimitry Andric   }
194*68d75effSDimitry Andric   case XRayEntryType::EXIT:
195*68d75effSDimitry Andric   case XRayEntryType::TAIL: {
196*68d75effSDimitry Andric     if (TLD.StackEntries == 0)
197*68d75effSDimitry Andric       break;
198*68d75effSDimitry Andric 
199*68d75effSDimitry Andric     if (--TLD.StackEntries >= TLD.StackSize)
200*68d75effSDimitry Andric       return;
201*68d75effSDimitry Andric 
202*68d75effSDimitry Andric     // When we encounter an exit event, we check whether all the following are
203*68d75effSDimitry Andric     // true:
204*68d75effSDimitry Andric     //
205*68d75effSDimitry Andric     // - The Function ID is the same as the most recent entry in the stack.
206*68d75effSDimitry Andric     // - The CPU is the same as the most recent entry in the stack.
207*68d75effSDimitry Andric     // - The Delta of the TSCs is less than the threshold amount of time we're
208*68d75effSDimitry Andric     //   looking to record.
209*68d75effSDimitry Andric     //
210*68d75effSDimitry Andric     // If all of these conditions are true, we pop the stack and don't write a
211*68d75effSDimitry Andric     // record and move the record offset back.
212*68d75effSDimitry Andric     StackEntry StackTop;
213*68d75effSDimitry Andric     auto StackEntryPtr = static_cast<char *>(TLD.ShadowStack) +
214*68d75effSDimitry Andric                          (sizeof(StackEntry) * TLD.StackEntries);
215*68d75effSDimitry Andric     internal_memcpy(&StackTop, StackEntryPtr, sizeof(StackEntry));
216*68d75effSDimitry Andric     if (StackTop.FuncId == FuncId && StackTop.CPU == CPU &&
217*68d75effSDimitry Andric         StackTop.TSC < TSC) {
218*68d75effSDimitry Andric       auto Delta = TSC - StackTop.TSC;
219*68d75effSDimitry Andric       if (Delta < atomic_load(&ThresholdTicks, memory_order_relaxed)) {
220*68d75effSDimitry Andric         DCHECK(TLD.BufferOffset > 0);
221*68d75effSDimitry Andric         TLD.BufferOffset -= StackTop.Type == XRayEntryType::ENTRY ? 1 : 2;
222*68d75effSDimitry Andric         return;
223*68d75effSDimitry Andric       }
224*68d75effSDimitry Andric     }
225*68d75effSDimitry Andric     break;
226*68d75effSDimitry Andric   }
227*68d75effSDimitry Andric   default:
228*68d75effSDimitry Andric     // Should be unreachable.
229*68d75effSDimitry Andric     DCHECK(false && "Unsupported XRayEntryType encountered.");
230*68d75effSDimitry Andric     break;
231*68d75effSDimitry Andric   }
232*68d75effSDimitry Andric 
233*68d75effSDimitry Andric   // First determine whether the delta between the function's enter record and
234*68d75effSDimitry Andric   // the exit record is higher than the threshold.
235*68d75effSDimitry Andric   XRayRecord R;
236*68d75effSDimitry Andric   R.RecordType = RecordTypes::NORMAL;
237*68d75effSDimitry Andric   R.CPU = CPU;
238*68d75effSDimitry Andric   R.TSC = TSC;
239*68d75effSDimitry Andric   R.TId = GetTid();
240*68d75effSDimitry Andric   R.PId = internal_getpid();
241*68d75effSDimitry Andric   R.Type = Type;
242*68d75effSDimitry Andric   R.FuncId = FuncId;
243*68d75effSDimitry Andric   auto FirstEntry = reinterpret_cast<XRayRecord *>(TLD.InMemoryBuffer);
244*68d75effSDimitry Andric   internal_memcpy(FirstEntry + TLD.BufferOffset, &R, sizeof(R));
245*68d75effSDimitry Andric   if (++TLD.BufferOffset == TLD.BufferSize) {
246*68d75effSDimitry Andric     SpinMutexLock Lock(&LogMutex);
247*68d75effSDimitry Andric     LW->WriteAll(reinterpret_cast<char *>(FirstEntry),
248*68d75effSDimitry Andric                  reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
249*68d75effSDimitry Andric     TLD.BufferOffset = 0;
250*68d75effSDimitry Andric     TLD.StackEntries = 0;
251*68d75effSDimitry Andric   }
252*68d75effSDimitry Andric }
253*68d75effSDimitry Andric 
254*68d75effSDimitry Andric template <class RDTSC>
255*68d75effSDimitry Andric void InMemoryRawLogWithArg(int32_t FuncId, XRayEntryType Type, uint64_t Arg1,
256*68d75effSDimitry Andric                            RDTSC ReadTSC) XRAY_NEVER_INSTRUMENT {
257*68d75effSDimitry Andric   auto &TLD = getThreadLocalData();
258*68d75effSDimitry Andric   auto FirstEntry =
259*68d75effSDimitry Andric       reinterpret_cast<XRayArgPayload *>(TLD.InMemoryBuffer);
260*68d75effSDimitry Andric   const auto &BuffLen = TLD.BufferSize;
261*68d75effSDimitry Andric   LogWriter *LW = getGlobalLog();
262*68d75effSDimitry Andric   if (LW == nullptr)
263*68d75effSDimitry Andric     return;
264*68d75effSDimitry Andric 
265*68d75effSDimitry Andric   // First we check whether there's enough space to write the data consecutively
266*68d75effSDimitry Andric   // in the thread-local buffer. If not, we first flush the buffer before
267*68d75effSDimitry Andric   // attempting to write the two records that must be consecutive.
268*68d75effSDimitry Andric   if (TLD.BufferOffset + 2 > BuffLen) {
269*68d75effSDimitry Andric     SpinMutexLock Lock(&LogMutex);
270*68d75effSDimitry Andric     LW->WriteAll(reinterpret_cast<char *>(FirstEntry),
271*68d75effSDimitry Andric                  reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
272*68d75effSDimitry Andric     TLD.BufferOffset = 0;
273*68d75effSDimitry Andric     TLD.StackEntries = 0;
274*68d75effSDimitry Andric   }
275*68d75effSDimitry Andric 
276*68d75effSDimitry Andric   // Then we write the "we have an argument" record.
277*68d75effSDimitry Andric   InMemoryRawLog(FuncId, Type, ReadTSC);
278*68d75effSDimitry Andric 
279*68d75effSDimitry Andric   RecursionGuard G(Guard);
280*68d75effSDimitry Andric   if (!G)
281*68d75effSDimitry Andric     return;
282*68d75effSDimitry Andric 
283*68d75effSDimitry Andric   // And, from here on write the arg payload.
284*68d75effSDimitry Andric   XRayArgPayload R;
285*68d75effSDimitry Andric   R.RecordType = RecordTypes::ARG_PAYLOAD;
286*68d75effSDimitry Andric   R.FuncId = FuncId;
287*68d75effSDimitry Andric   R.TId = GetTid();
288*68d75effSDimitry Andric   R.PId = internal_getpid();
289*68d75effSDimitry Andric   R.Arg = Arg1;
290*68d75effSDimitry Andric   internal_memcpy(FirstEntry + TLD.BufferOffset, &R, sizeof(R));
291*68d75effSDimitry Andric   if (++TLD.BufferOffset == BuffLen) {
292*68d75effSDimitry Andric     SpinMutexLock Lock(&LogMutex);
293*68d75effSDimitry Andric     LW->WriteAll(reinterpret_cast<char *>(FirstEntry),
294*68d75effSDimitry Andric                  reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
295*68d75effSDimitry Andric     TLD.BufferOffset = 0;
296*68d75effSDimitry Andric     TLD.StackEntries = 0;
297*68d75effSDimitry Andric   }
298*68d75effSDimitry Andric }
299*68d75effSDimitry Andric 
300*68d75effSDimitry Andric void basicLoggingHandleArg0RealTSC(int32_t FuncId,
301*68d75effSDimitry Andric                                    XRayEntryType Type) XRAY_NEVER_INSTRUMENT {
302*68d75effSDimitry Andric   InMemoryRawLog(FuncId, Type, readTSC);
303*68d75effSDimitry Andric }
304*68d75effSDimitry Andric 
305*68d75effSDimitry Andric void basicLoggingHandleArg0EmulateTSC(int32_t FuncId, XRayEntryType Type)
306*68d75effSDimitry Andric     XRAY_NEVER_INSTRUMENT {
307*68d75effSDimitry Andric   InMemoryRawLog(FuncId, Type, [](uint8_t &CPU) XRAY_NEVER_INSTRUMENT {
308*68d75effSDimitry Andric     timespec TS;
309*68d75effSDimitry Andric     int result = clock_gettime(CLOCK_REALTIME, &TS);
310*68d75effSDimitry Andric     if (result != 0) {
311*68d75effSDimitry Andric       Report("clock_gettimg(2) return %d, errno=%d.", result, int(errno));
312*68d75effSDimitry Andric       TS = {0, 0};
313*68d75effSDimitry Andric     }
314*68d75effSDimitry Andric     CPU = 0;
315*68d75effSDimitry Andric     return TS.tv_sec * NanosecondsPerSecond + TS.tv_nsec;
316*68d75effSDimitry Andric   });
317*68d75effSDimitry Andric }
318*68d75effSDimitry Andric 
319*68d75effSDimitry Andric void basicLoggingHandleArg1RealTSC(int32_t FuncId, XRayEntryType Type,
320*68d75effSDimitry Andric                                    uint64_t Arg1) XRAY_NEVER_INSTRUMENT {
321*68d75effSDimitry Andric   InMemoryRawLogWithArg(FuncId, Type, Arg1, readTSC);
322*68d75effSDimitry Andric }
323*68d75effSDimitry Andric 
324*68d75effSDimitry Andric void basicLoggingHandleArg1EmulateTSC(int32_t FuncId, XRayEntryType Type,
325*68d75effSDimitry Andric                                       uint64_t Arg1) XRAY_NEVER_INSTRUMENT {
326*68d75effSDimitry Andric   InMemoryRawLogWithArg(
327*68d75effSDimitry Andric       FuncId, Type, Arg1, [](uint8_t &CPU) XRAY_NEVER_INSTRUMENT {
328*68d75effSDimitry Andric         timespec TS;
329*68d75effSDimitry Andric         int result = clock_gettime(CLOCK_REALTIME, &TS);
330*68d75effSDimitry Andric         if (result != 0) {
331*68d75effSDimitry Andric           Report("clock_gettimg(2) return %d, errno=%d.", result, int(errno));
332*68d75effSDimitry Andric           TS = {0, 0};
333*68d75effSDimitry Andric         }
334*68d75effSDimitry Andric         CPU = 0;
335*68d75effSDimitry Andric         return TS.tv_sec * NanosecondsPerSecond + TS.tv_nsec;
336*68d75effSDimitry Andric       });
337*68d75effSDimitry Andric }
338*68d75effSDimitry Andric 
339*68d75effSDimitry Andric static void TLDDestructor(void *P) XRAY_NEVER_INSTRUMENT {
340*68d75effSDimitry Andric   ThreadLocalData &TLD = *reinterpret_cast<ThreadLocalData *>(P);
341*68d75effSDimitry Andric   auto ExitGuard = at_scope_exit([&TLD] {
342*68d75effSDimitry Andric     // Clean up dynamic resources.
343*68d75effSDimitry Andric     if (TLD.InMemoryBuffer)
344*68d75effSDimitry Andric       InternalFree(TLD.InMemoryBuffer);
345*68d75effSDimitry Andric     if (TLD.ShadowStack)
346*68d75effSDimitry Andric       InternalFree(TLD.ShadowStack);
347*68d75effSDimitry Andric     if (Verbosity())
348*68d75effSDimitry Andric       Report("Cleaned up log for TID: %d\n", GetTid());
349*68d75effSDimitry Andric   });
350*68d75effSDimitry Andric 
351*68d75effSDimitry Andric   if (TLD.LogWriter == nullptr || TLD.BufferOffset == 0) {
352*68d75effSDimitry Andric     if (Verbosity())
353*68d75effSDimitry Andric       Report("Skipping buffer for TID: %d; Offset = %llu\n", GetTid(),
354*68d75effSDimitry Andric              TLD.BufferOffset);
355*68d75effSDimitry Andric     return;
356*68d75effSDimitry Andric   }
357*68d75effSDimitry Andric 
358*68d75effSDimitry Andric   {
359*68d75effSDimitry Andric     SpinMutexLock L(&LogMutex);
360*68d75effSDimitry Andric     TLD.LogWriter->WriteAll(reinterpret_cast<char *>(TLD.InMemoryBuffer),
361*68d75effSDimitry Andric                             reinterpret_cast<char *>(TLD.InMemoryBuffer) +
362*68d75effSDimitry Andric                             (sizeof(XRayRecord) * TLD.BufferOffset));
363*68d75effSDimitry Andric   }
364*68d75effSDimitry Andric 
365*68d75effSDimitry Andric   // Because this thread's exit could be the last one trying to write to
366*68d75effSDimitry Andric   // the file and that we're not able to close out the file properly, we
367*68d75effSDimitry Andric   // sync instead and hope that the pending writes are flushed as the
368*68d75effSDimitry Andric   // thread exits.
369*68d75effSDimitry Andric   TLD.LogWriter->Flush();
370*68d75effSDimitry Andric }
371*68d75effSDimitry Andric 
372*68d75effSDimitry Andric XRayLogInitStatus basicLoggingInit(UNUSED size_t BufferSize,
373*68d75effSDimitry Andric                                    UNUSED size_t BufferMax, void *Options,
374*68d75effSDimitry Andric                                    size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
375*68d75effSDimitry Andric   uint8_t Expected = 0;
376*68d75effSDimitry Andric   if (!atomic_compare_exchange_strong(&BasicInitialized, &Expected, 1,
377*68d75effSDimitry Andric                                       memory_order_acq_rel)) {
378*68d75effSDimitry Andric     if (Verbosity())
379*68d75effSDimitry Andric       Report("Basic logging already initialized.\n");
380*68d75effSDimitry Andric     return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
381*68d75effSDimitry Andric   }
382*68d75effSDimitry Andric 
383*68d75effSDimitry Andric   static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
384*68d75effSDimitry Andric   pthread_once(&OnceInit, +[] {
385*68d75effSDimitry Andric     pthread_key_create(&PThreadKey, TLDDestructor);
386*68d75effSDimitry Andric     atomic_store(&UseRealTSC, probeRequiredCPUFeatures(), memory_order_release);
387*68d75effSDimitry Andric     // Initialize the global TicksPerSec value.
388*68d75effSDimitry Andric     atomic_store(&TicksPerSec,
389*68d75effSDimitry Andric                  probeRequiredCPUFeatures() ? getTSCFrequency()
390*68d75effSDimitry Andric                                             : NanosecondsPerSecond,
391*68d75effSDimitry Andric                  memory_order_release);
392*68d75effSDimitry Andric     if (!atomic_load(&UseRealTSC, memory_order_relaxed) && Verbosity())
393*68d75effSDimitry Andric       Report("WARNING: Required CPU features missing for XRay instrumentation, "
394*68d75effSDimitry Andric              "using emulation instead.\n");
395*68d75effSDimitry Andric   });
396*68d75effSDimitry Andric 
397*68d75effSDimitry Andric   FlagParser P;
398*68d75effSDimitry Andric   BasicFlags F;
399*68d75effSDimitry Andric   F.setDefaults();
400*68d75effSDimitry Andric   registerXRayBasicFlags(&P, &F);
401*68d75effSDimitry Andric   P.ParseString(useCompilerDefinedBasicFlags());
402*68d75effSDimitry Andric   auto *EnvOpts = GetEnv("XRAY_BASIC_OPTIONS");
403*68d75effSDimitry Andric   if (EnvOpts == nullptr)
404*68d75effSDimitry Andric     EnvOpts = "";
405*68d75effSDimitry Andric 
406*68d75effSDimitry Andric   P.ParseString(EnvOpts);
407*68d75effSDimitry Andric 
408*68d75effSDimitry Andric   // If XRAY_BASIC_OPTIONS was not defined, then we use the deprecated options
409*68d75effSDimitry Andric   // set through XRAY_OPTIONS instead.
410*68d75effSDimitry Andric   if (internal_strlen(EnvOpts) == 0) {
411*68d75effSDimitry Andric     F.func_duration_threshold_us =
412*68d75effSDimitry Andric         flags()->xray_naive_log_func_duration_threshold_us;
413*68d75effSDimitry Andric     F.max_stack_depth = flags()->xray_naive_log_max_stack_depth;
414*68d75effSDimitry Andric     F.thread_buffer_size = flags()->xray_naive_log_thread_buffer_size;
415*68d75effSDimitry Andric   }
416*68d75effSDimitry Andric 
417*68d75effSDimitry Andric   P.ParseString(static_cast<const char *>(Options));
418*68d75effSDimitry Andric   GlobalOptions.ThreadBufferSize = F.thread_buffer_size;
419*68d75effSDimitry Andric   GlobalOptions.DurationFilterMicros = F.func_duration_threshold_us;
420*68d75effSDimitry Andric   GlobalOptions.MaxStackDepth = F.max_stack_depth;
421*68d75effSDimitry Andric   *basicFlags() = F;
422*68d75effSDimitry Andric 
423*68d75effSDimitry Andric   atomic_store(&ThresholdTicks,
424*68d75effSDimitry Andric                atomic_load(&TicksPerSec, memory_order_acquire) *
425*68d75effSDimitry Andric                    GlobalOptions.DurationFilterMicros / 1000000,
426*68d75effSDimitry Andric                memory_order_release);
427*68d75effSDimitry Andric   __xray_set_handler_arg1(atomic_load(&UseRealTSC, memory_order_acquire)
428*68d75effSDimitry Andric                               ? basicLoggingHandleArg1RealTSC
429*68d75effSDimitry Andric                               : basicLoggingHandleArg1EmulateTSC);
430*68d75effSDimitry Andric   __xray_set_handler(atomic_load(&UseRealTSC, memory_order_acquire)
431*68d75effSDimitry Andric                          ? basicLoggingHandleArg0RealTSC
432*68d75effSDimitry Andric                          : basicLoggingHandleArg0EmulateTSC);
433*68d75effSDimitry Andric 
434*68d75effSDimitry Andric   // TODO: Implement custom event and typed event handling support in Basic
435*68d75effSDimitry Andric   // Mode.
436*68d75effSDimitry Andric   __xray_remove_customevent_handler();
437*68d75effSDimitry Andric   __xray_remove_typedevent_handler();
438*68d75effSDimitry Andric 
439*68d75effSDimitry Andric   return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
440*68d75effSDimitry Andric }
441*68d75effSDimitry Andric 
442*68d75effSDimitry Andric XRayLogInitStatus basicLoggingFinalize() XRAY_NEVER_INSTRUMENT {
443*68d75effSDimitry Andric   uint8_t Expected = 0;
444*68d75effSDimitry Andric   if (!atomic_compare_exchange_strong(&BasicInitialized, &Expected, 0,
445*68d75effSDimitry Andric                                       memory_order_acq_rel) &&
446*68d75effSDimitry Andric       Verbosity())
447*68d75effSDimitry Andric     Report("Basic logging already finalized.\n");
448*68d75effSDimitry Andric 
449*68d75effSDimitry Andric   // Nothing really to do aside from marking state of the global to be
450*68d75effSDimitry Andric   // uninitialized.
451*68d75effSDimitry Andric 
452*68d75effSDimitry Andric   return XRayLogInitStatus::XRAY_LOG_FINALIZED;
453*68d75effSDimitry Andric }
454*68d75effSDimitry Andric 
455*68d75effSDimitry Andric XRayLogFlushStatus basicLoggingFlush() XRAY_NEVER_INSTRUMENT {
456*68d75effSDimitry Andric   // This really does nothing, since flushing the logs happen at the end of a
457*68d75effSDimitry Andric   // thread's lifetime, or when the buffers are full.
458*68d75effSDimitry Andric   return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
459*68d75effSDimitry Andric }
460*68d75effSDimitry Andric 
461*68d75effSDimitry Andric // This is a handler that, effectively, does nothing.
462*68d75effSDimitry Andric void basicLoggingHandleArg0Empty(int32_t, XRayEntryType) XRAY_NEVER_INSTRUMENT {
463*68d75effSDimitry Andric }
464*68d75effSDimitry Andric 
465*68d75effSDimitry Andric bool basicLogDynamicInitializer() XRAY_NEVER_INSTRUMENT {
466*68d75effSDimitry Andric   XRayLogImpl Impl{
467*68d75effSDimitry Andric       basicLoggingInit,
468*68d75effSDimitry Andric       basicLoggingFinalize,
469*68d75effSDimitry Andric       basicLoggingHandleArg0Empty,
470*68d75effSDimitry Andric       basicLoggingFlush,
471*68d75effSDimitry Andric   };
472*68d75effSDimitry Andric   auto RegistrationResult = __xray_log_register_mode("xray-basic", Impl);
473*68d75effSDimitry Andric   if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK &&
474*68d75effSDimitry Andric       Verbosity())
475*68d75effSDimitry Andric     Report("Cannot register XRay Basic Mode to 'xray-basic'; error = %d\n",
476*68d75effSDimitry Andric            RegistrationResult);
477*68d75effSDimitry Andric   if (flags()->xray_naive_log ||
478*68d75effSDimitry Andric       !internal_strcmp(flags()->xray_mode, "xray-basic")) {
479*68d75effSDimitry Andric     auto SelectResult = __xray_log_select_mode("xray-basic");
480*68d75effSDimitry Andric     if (SelectResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK) {
481*68d75effSDimitry Andric       if (Verbosity())
482*68d75effSDimitry Andric         Report("Failed selecting XRay Basic Mode; error = %d\n", SelectResult);
483*68d75effSDimitry Andric       return false;
484*68d75effSDimitry Andric     }
485*68d75effSDimitry Andric 
486*68d75effSDimitry Andric     // We initialize the implementation using the data we get from the
487*68d75effSDimitry Andric     // XRAY_BASIC_OPTIONS environment variable, at this point of the
488*68d75effSDimitry Andric     // implementation.
489*68d75effSDimitry Andric     auto *Env = GetEnv("XRAY_BASIC_OPTIONS");
490*68d75effSDimitry Andric     auto InitResult =
491*68d75effSDimitry Andric         __xray_log_init_mode("xray-basic", Env == nullptr ? "" : Env);
492*68d75effSDimitry Andric     if (InitResult != XRayLogInitStatus::XRAY_LOG_INITIALIZED) {
493*68d75effSDimitry Andric       if (Verbosity())
494*68d75effSDimitry Andric         Report("Failed initializing XRay Basic Mode; error = %d\n", InitResult);
495*68d75effSDimitry Andric       return false;
496*68d75effSDimitry Andric     }
497*68d75effSDimitry Andric 
498*68d75effSDimitry Andric     // At this point we know that we've successfully initialized Basic mode
499*68d75effSDimitry Andric     // tracing, and the only chance we're going to get for the current thread to
500*68d75effSDimitry Andric     // clean-up may be at thread/program exit. To ensure that we're going to get
501*68d75effSDimitry Andric     // the cleanup even without calling the finalization routines, we're
502*68d75effSDimitry Andric     // registering a program exit function that will do the cleanup.
503*68d75effSDimitry Andric     static pthread_once_t DynamicOnce = PTHREAD_ONCE_INIT;
504*68d75effSDimitry Andric     pthread_once(&DynamicOnce, +[] {
505*68d75effSDimitry Andric       static void *FakeTLD = nullptr;
506*68d75effSDimitry Andric       FakeTLD = &getThreadLocalData();
507*68d75effSDimitry Andric       Atexit(+[] { TLDDestructor(FakeTLD); });
508*68d75effSDimitry Andric     });
509*68d75effSDimitry Andric   }
510*68d75effSDimitry Andric   return true;
511*68d75effSDimitry Andric }
512*68d75effSDimitry Andric 
513*68d75effSDimitry Andric } // namespace __xray
514*68d75effSDimitry Andric 
515*68d75effSDimitry Andric static auto UNUSED Unused = __xray::basicLogDynamicInitializer();
516