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