xref: /openbsd-src/gnu/llvm/compiler-rt/lib/xray/xray_basic_logging.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- xray_basic_logging.cpp ----------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of XRay, a dynamic runtime instrumentation system.
103cab2bb3Spatrick //
113cab2bb3Spatrick // Implementation of a simple in-memory log of XRay events. This defines a
123cab2bb3Spatrick // logging function that's compatible with the XRay handler interface, and
133cab2bb3Spatrick // routines for exporting data to files.
143cab2bb3Spatrick //
153cab2bb3Spatrick //===----------------------------------------------------------------------===//
163cab2bb3Spatrick 
173cab2bb3Spatrick #include <errno.h>
183cab2bb3Spatrick #include <fcntl.h>
193cab2bb3Spatrick #include <pthread.h>
203cab2bb3Spatrick #include <sys/stat.h>
21*810390e3Srobert #if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_APPLE
223cab2bb3Spatrick #include <sys/syscall.h>
233cab2bb3Spatrick #endif
243cab2bb3Spatrick #include <sys/types.h>
253cab2bb3Spatrick #include <time.h>
263cab2bb3Spatrick #include <unistd.h>
273cab2bb3Spatrick 
283cab2bb3Spatrick #include "sanitizer_common/sanitizer_allocator_internal.h"
293cab2bb3Spatrick #include "sanitizer_common/sanitizer_libc.h"
303cab2bb3Spatrick #include "xray/xray_records.h"
313cab2bb3Spatrick #include "xray_recursion_guard.h"
323cab2bb3Spatrick #include "xray_basic_flags.h"
333cab2bb3Spatrick #include "xray_basic_logging.h"
343cab2bb3Spatrick #include "xray_defs.h"
353cab2bb3Spatrick #include "xray_flags.h"
363cab2bb3Spatrick #include "xray_interface_internal.h"
373cab2bb3Spatrick #include "xray_tsc.h"
383cab2bb3Spatrick #include "xray_utils.h"
393cab2bb3Spatrick 
403cab2bb3Spatrick namespace __xray {
413cab2bb3Spatrick 
423cab2bb3Spatrick static SpinMutex LogMutex;
433cab2bb3Spatrick 
443cab2bb3Spatrick namespace {
453cab2bb3Spatrick // We use elements of this type to record the entry TSC of every function ID we
463cab2bb3Spatrick // see as we're tracing a particular thread's execution.
473cab2bb3Spatrick struct alignas(16) StackEntry {
483cab2bb3Spatrick   int32_t FuncId;
493cab2bb3Spatrick   uint16_t Type;
503cab2bb3Spatrick   uint8_t CPU;
513cab2bb3Spatrick   uint8_t Padding;
523cab2bb3Spatrick   uint64_t TSC;
533cab2bb3Spatrick };
543cab2bb3Spatrick 
553cab2bb3Spatrick static_assert(sizeof(StackEntry) == 16, "Wrong size for StackEntry");
563cab2bb3Spatrick 
573cab2bb3Spatrick struct XRAY_TLS_ALIGNAS(64) ThreadLocalData {
583cab2bb3Spatrick   void *InMemoryBuffer = nullptr;
593cab2bb3Spatrick   size_t BufferSize = 0;
603cab2bb3Spatrick   size_t BufferOffset = 0;
613cab2bb3Spatrick   void *ShadowStack = nullptr;
623cab2bb3Spatrick   size_t StackSize = 0;
633cab2bb3Spatrick   size_t StackEntries = 0;
643cab2bb3Spatrick   __xray::LogWriter *LogWriter = nullptr;
653cab2bb3Spatrick };
663cab2bb3Spatrick 
673cab2bb3Spatrick struct BasicLoggingOptions {
683cab2bb3Spatrick   int DurationFilterMicros = 0;
693cab2bb3Spatrick   size_t MaxStackDepth = 0;
703cab2bb3Spatrick   size_t ThreadBufferSize = 0;
713cab2bb3Spatrick };
723cab2bb3Spatrick } // namespace
733cab2bb3Spatrick 
743cab2bb3Spatrick static pthread_key_t PThreadKey;
753cab2bb3Spatrick 
763cab2bb3Spatrick static atomic_uint8_t BasicInitialized{0};
773cab2bb3Spatrick 
783cab2bb3Spatrick struct BasicLoggingOptions GlobalOptions;
793cab2bb3Spatrick 
803cab2bb3Spatrick thread_local atomic_uint8_t Guard{0};
813cab2bb3Spatrick 
823cab2bb3Spatrick static atomic_uint8_t UseRealTSC{0};
833cab2bb3Spatrick static atomic_uint64_t ThresholdTicks{0};
843cab2bb3Spatrick static atomic_uint64_t TicksPerSec{0};
853cab2bb3Spatrick static atomic_uint64_t CycleFrequency{NanosecondsPerSecond};
863cab2bb3Spatrick 
getLog()873cab2bb3Spatrick static LogWriter *getLog() XRAY_NEVER_INSTRUMENT {
883cab2bb3Spatrick   LogWriter* LW = LogWriter::Open();
893cab2bb3Spatrick   if (LW == nullptr)
903cab2bb3Spatrick     return LW;
913cab2bb3Spatrick 
923cab2bb3Spatrick   static pthread_once_t DetectOnce = PTHREAD_ONCE_INIT;
933cab2bb3Spatrick   pthread_once(&DetectOnce, +[] {
943cab2bb3Spatrick     if (atomic_load(&UseRealTSC, memory_order_acquire))
953cab2bb3Spatrick       atomic_store(&CycleFrequency, getTSCFrequency(), memory_order_release);
963cab2bb3Spatrick   });
973cab2bb3Spatrick 
983cab2bb3Spatrick   // Since we're here, we get to write the header. We set it up so that the
993cab2bb3Spatrick   // header will only be written once, at the start, and let the threads
1003cab2bb3Spatrick   // logging do writes which just append.
1013cab2bb3Spatrick   XRayFileHeader Header;
1023cab2bb3Spatrick   // Version 2 includes tail exit records.
1033cab2bb3Spatrick   // Version 3 includes pid inside records.
1043cab2bb3Spatrick   Header.Version = 3;
1053cab2bb3Spatrick   Header.Type = FileTypes::NAIVE_LOG;
1063cab2bb3Spatrick   Header.CycleFrequency = atomic_load(&CycleFrequency, memory_order_acquire);
1073cab2bb3Spatrick 
1083cab2bb3Spatrick   // FIXME: Actually check whether we have 'constant_tsc' and 'nonstop_tsc'
1093cab2bb3Spatrick   // before setting the values in the header.
1103cab2bb3Spatrick   Header.ConstantTSC = 1;
1113cab2bb3Spatrick   Header.NonstopTSC = 1;
1123cab2bb3Spatrick   LW->WriteAll(reinterpret_cast<char *>(&Header),
1133cab2bb3Spatrick                reinterpret_cast<char *>(&Header) + sizeof(Header));
1143cab2bb3Spatrick   return LW;
1153cab2bb3Spatrick }
1163cab2bb3Spatrick 
getGlobalLog()1173cab2bb3Spatrick static LogWriter *getGlobalLog() XRAY_NEVER_INSTRUMENT {
1183cab2bb3Spatrick   static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
1193cab2bb3Spatrick   static LogWriter *LW = nullptr;
1203cab2bb3Spatrick   pthread_once(&OnceInit, +[] { LW = getLog(); });
1213cab2bb3Spatrick   return LW;
1223cab2bb3Spatrick }
1233cab2bb3Spatrick 
getThreadLocalData()1243cab2bb3Spatrick static ThreadLocalData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
1253cab2bb3Spatrick   thread_local ThreadLocalData TLD;
1263cab2bb3Spatrick   thread_local bool UNUSED TOnce = [] {
1273cab2bb3Spatrick     if (GlobalOptions.ThreadBufferSize == 0) {
1283cab2bb3Spatrick       if (Verbosity())
1293cab2bb3Spatrick         Report("Not initializing TLD since ThreadBufferSize == 0.\n");
1303cab2bb3Spatrick       return false;
1313cab2bb3Spatrick     }
1323cab2bb3Spatrick     pthread_setspecific(PThreadKey, &TLD);
1333cab2bb3Spatrick     TLD.LogWriter = getGlobalLog();
1343cab2bb3Spatrick     TLD.InMemoryBuffer = reinterpret_cast<XRayRecord *>(
1353cab2bb3Spatrick         InternalAlloc(sizeof(XRayRecord) * GlobalOptions.ThreadBufferSize,
1363cab2bb3Spatrick                       nullptr, alignof(XRayRecord)));
1373cab2bb3Spatrick     TLD.BufferSize = GlobalOptions.ThreadBufferSize;
1383cab2bb3Spatrick     TLD.BufferOffset = 0;
1393cab2bb3Spatrick     if (GlobalOptions.MaxStackDepth == 0) {
1403cab2bb3Spatrick       if (Verbosity())
1413cab2bb3Spatrick         Report("Not initializing the ShadowStack since MaxStackDepth == 0.\n");
1423cab2bb3Spatrick       TLD.StackSize = 0;
1433cab2bb3Spatrick       TLD.StackEntries = 0;
1443cab2bb3Spatrick       TLD.ShadowStack = nullptr;
1453cab2bb3Spatrick       return false;
1463cab2bb3Spatrick     }
1473cab2bb3Spatrick     TLD.ShadowStack = reinterpret_cast<StackEntry *>(
1483cab2bb3Spatrick         InternalAlloc(sizeof(StackEntry) * GlobalOptions.MaxStackDepth, nullptr,
1493cab2bb3Spatrick                       alignof(StackEntry)));
1503cab2bb3Spatrick     TLD.StackSize = GlobalOptions.MaxStackDepth;
1513cab2bb3Spatrick     TLD.StackEntries = 0;
1523cab2bb3Spatrick     return false;
1533cab2bb3Spatrick   }();
1543cab2bb3Spatrick   return TLD;
1553cab2bb3Spatrick }
1563cab2bb3Spatrick 
1573cab2bb3Spatrick template <class RDTSC>
InMemoryRawLog(int32_t FuncId,XRayEntryType Type,RDTSC ReadTSC)1583cab2bb3Spatrick void InMemoryRawLog(int32_t FuncId, XRayEntryType Type,
1593cab2bb3Spatrick                     RDTSC ReadTSC) XRAY_NEVER_INSTRUMENT {
1603cab2bb3Spatrick   auto &TLD = getThreadLocalData();
1613cab2bb3Spatrick   LogWriter *LW = getGlobalLog();
1623cab2bb3Spatrick   if (LW == nullptr)
1633cab2bb3Spatrick     return;
1643cab2bb3Spatrick 
1653cab2bb3Spatrick   // Use a simple recursion guard, to handle cases where we're already logging
1663cab2bb3Spatrick   // and for one reason or another, this function gets called again in the same
1673cab2bb3Spatrick   // thread.
1683cab2bb3Spatrick   RecursionGuard G(Guard);
1693cab2bb3Spatrick   if (!G)
1703cab2bb3Spatrick     return;
1713cab2bb3Spatrick 
1723cab2bb3Spatrick   uint8_t CPU = 0;
1733cab2bb3Spatrick   uint64_t TSC = ReadTSC(CPU);
1743cab2bb3Spatrick 
1753cab2bb3Spatrick   switch (Type) {
1763cab2bb3Spatrick   case XRayEntryType::ENTRY:
1773cab2bb3Spatrick   case XRayEntryType::LOG_ARGS_ENTRY: {
1783cab2bb3Spatrick     // Short circuit if we've reached the maximum depth of the stack.
1793cab2bb3Spatrick     if (TLD.StackEntries++ >= TLD.StackSize)
1803cab2bb3Spatrick       return;
1813cab2bb3Spatrick 
1823cab2bb3Spatrick     // When we encounter an entry event, we keep track of the TSC and the CPU,
1833cab2bb3Spatrick     // and put it in the stack.
1843cab2bb3Spatrick     StackEntry E;
1853cab2bb3Spatrick     E.FuncId = FuncId;
1863cab2bb3Spatrick     E.CPU = CPU;
1873cab2bb3Spatrick     E.Type = Type;
1883cab2bb3Spatrick     E.TSC = TSC;
1893cab2bb3Spatrick     auto StackEntryPtr = static_cast<char *>(TLD.ShadowStack) +
1903cab2bb3Spatrick                          (sizeof(StackEntry) * (TLD.StackEntries - 1));
1913cab2bb3Spatrick     internal_memcpy(StackEntryPtr, &E, sizeof(StackEntry));
1923cab2bb3Spatrick     break;
1933cab2bb3Spatrick   }
1943cab2bb3Spatrick   case XRayEntryType::EXIT:
1953cab2bb3Spatrick   case XRayEntryType::TAIL: {
1963cab2bb3Spatrick     if (TLD.StackEntries == 0)
1973cab2bb3Spatrick       break;
1983cab2bb3Spatrick 
1993cab2bb3Spatrick     if (--TLD.StackEntries >= TLD.StackSize)
2003cab2bb3Spatrick       return;
2013cab2bb3Spatrick 
2023cab2bb3Spatrick     // When we encounter an exit event, we check whether all the following are
2033cab2bb3Spatrick     // true:
2043cab2bb3Spatrick     //
2053cab2bb3Spatrick     // - The Function ID is the same as the most recent entry in the stack.
2063cab2bb3Spatrick     // - The CPU is the same as the most recent entry in the stack.
2073cab2bb3Spatrick     // - The Delta of the TSCs is less than the threshold amount of time we're
2083cab2bb3Spatrick     //   looking to record.
2093cab2bb3Spatrick     //
2103cab2bb3Spatrick     // If all of these conditions are true, we pop the stack and don't write a
2113cab2bb3Spatrick     // record and move the record offset back.
2123cab2bb3Spatrick     StackEntry StackTop;
2133cab2bb3Spatrick     auto StackEntryPtr = static_cast<char *>(TLD.ShadowStack) +
2143cab2bb3Spatrick                          (sizeof(StackEntry) * TLD.StackEntries);
2153cab2bb3Spatrick     internal_memcpy(&StackTop, StackEntryPtr, sizeof(StackEntry));
2163cab2bb3Spatrick     if (StackTop.FuncId == FuncId && StackTop.CPU == CPU &&
2173cab2bb3Spatrick         StackTop.TSC < TSC) {
2183cab2bb3Spatrick       auto Delta = TSC - StackTop.TSC;
2193cab2bb3Spatrick       if (Delta < atomic_load(&ThresholdTicks, memory_order_relaxed)) {
2203cab2bb3Spatrick         DCHECK(TLD.BufferOffset > 0);
2213cab2bb3Spatrick         TLD.BufferOffset -= StackTop.Type == XRayEntryType::ENTRY ? 1 : 2;
2223cab2bb3Spatrick         return;
2233cab2bb3Spatrick       }
2243cab2bb3Spatrick     }
2253cab2bb3Spatrick     break;
2263cab2bb3Spatrick   }
2273cab2bb3Spatrick   default:
2283cab2bb3Spatrick     // Should be unreachable.
2293cab2bb3Spatrick     DCHECK(false && "Unsupported XRayEntryType encountered.");
2303cab2bb3Spatrick     break;
2313cab2bb3Spatrick   }
2323cab2bb3Spatrick 
2333cab2bb3Spatrick   // First determine whether the delta between the function's enter record and
2343cab2bb3Spatrick   // the exit record is higher than the threshold.
2353cab2bb3Spatrick   XRayRecord R;
2363cab2bb3Spatrick   R.RecordType = RecordTypes::NORMAL;
2373cab2bb3Spatrick   R.CPU = CPU;
2383cab2bb3Spatrick   R.TSC = TSC;
2393cab2bb3Spatrick   R.TId = GetTid();
2403cab2bb3Spatrick   R.PId = internal_getpid();
2413cab2bb3Spatrick   R.Type = Type;
2423cab2bb3Spatrick   R.FuncId = FuncId;
2433cab2bb3Spatrick   auto FirstEntry = reinterpret_cast<XRayRecord *>(TLD.InMemoryBuffer);
2443cab2bb3Spatrick   internal_memcpy(FirstEntry + TLD.BufferOffset, &R, sizeof(R));
2453cab2bb3Spatrick   if (++TLD.BufferOffset == TLD.BufferSize) {
2463cab2bb3Spatrick     SpinMutexLock Lock(&LogMutex);
2473cab2bb3Spatrick     LW->WriteAll(reinterpret_cast<char *>(FirstEntry),
2483cab2bb3Spatrick                  reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
2493cab2bb3Spatrick     TLD.BufferOffset = 0;
2503cab2bb3Spatrick     TLD.StackEntries = 0;
2513cab2bb3Spatrick   }
2523cab2bb3Spatrick }
2533cab2bb3Spatrick 
2543cab2bb3Spatrick template <class RDTSC>
InMemoryRawLogWithArg(int32_t FuncId,XRayEntryType Type,uint64_t Arg1,RDTSC ReadTSC)2553cab2bb3Spatrick void InMemoryRawLogWithArg(int32_t FuncId, XRayEntryType Type, uint64_t Arg1,
2563cab2bb3Spatrick                            RDTSC ReadTSC) XRAY_NEVER_INSTRUMENT {
2573cab2bb3Spatrick   auto &TLD = getThreadLocalData();
2583cab2bb3Spatrick   auto FirstEntry =
2593cab2bb3Spatrick       reinterpret_cast<XRayArgPayload *>(TLD.InMemoryBuffer);
2603cab2bb3Spatrick   const auto &BuffLen = TLD.BufferSize;
2613cab2bb3Spatrick   LogWriter *LW = getGlobalLog();
2623cab2bb3Spatrick   if (LW == nullptr)
2633cab2bb3Spatrick     return;
2643cab2bb3Spatrick 
2653cab2bb3Spatrick   // First we check whether there's enough space to write the data consecutively
2663cab2bb3Spatrick   // in the thread-local buffer. If not, we first flush the buffer before
2673cab2bb3Spatrick   // attempting to write the two records that must be consecutive.
2683cab2bb3Spatrick   if (TLD.BufferOffset + 2 > BuffLen) {
2693cab2bb3Spatrick     SpinMutexLock Lock(&LogMutex);
2703cab2bb3Spatrick     LW->WriteAll(reinterpret_cast<char *>(FirstEntry),
2713cab2bb3Spatrick                  reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
2723cab2bb3Spatrick     TLD.BufferOffset = 0;
2733cab2bb3Spatrick     TLD.StackEntries = 0;
2743cab2bb3Spatrick   }
2753cab2bb3Spatrick 
2763cab2bb3Spatrick   // Then we write the "we have an argument" record.
2773cab2bb3Spatrick   InMemoryRawLog(FuncId, Type, ReadTSC);
2783cab2bb3Spatrick 
2793cab2bb3Spatrick   RecursionGuard G(Guard);
2803cab2bb3Spatrick   if (!G)
2813cab2bb3Spatrick     return;
2823cab2bb3Spatrick 
2833cab2bb3Spatrick   // And, from here on write the arg payload.
2843cab2bb3Spatrick   XRayArgPayload R;
2853cab2bb3Spatrick   R.RecordType = RecordTypes::ARG_PAYLOAD;
2863cab2bb3Spatrick   R.FuncId = FuncId;
2873cab2bb3Spatrick   R.TId = GetTid();
2883cab2bb3Spatrick   R.PId = internal_getpid();
2893cab2bb3Spatrick   R.Arg = Arg1;
2903cab2bb3Spatrick   internal_memcpy(FirstEntry + TLD.BufferOffset, &R, sizeof(R));
2913cab2bb3Spatrick   if (++TLD.BufferOffset == BuffLen) {
2923cab2bb3Spatrick     SpinMutexLock Lock(&LogMutex);
2933cab2bb3Spatrick     LW->WriteAll(reinterpret_cast<char *>(FirstEntry),
2943cab2bb3Spatrick                  reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
2953cab2bb3Spatrick     TLD.BufferOffset = 0;
2963cab2bb3Spatrick     TLD.StackEntries = 0;
2973cab2bb3Spatrick   }
2983cab2bb3Spatrick }
2993cab2bb3Spatrick 
basicLoggingHandleArg0RealTSC(int32_t FuncId,XRayEntryType Type)3003cab2bb3Spatrick void basicLoggingHandleArg0RealTSC(int32_t FuncId,
3013cab2bb3Spatrick                                    XRayEntryType Type) XRAY_NEVER_INSTRUMENT {
3023cab2bb3Spatrick   InMemoryRawLog(FuncId, Type, readTSC);
3033cab2bb3Spatrick }
3043cab2bb3Spatrick 
basicLoggingHandleArg0EmulateTSC(int32_t FuncId,XRayEntryType Type)3053cab2bb3Spatrick void basicLoggingHandleArg0EmulateTSC(int32_t FuncId, XRayEntryType Type)
3063cab2bb3Spatrick     XRAY_NEVER_INSTRUMENT {
3073cab2bb3Spatrick   InMemoryRawLog(FuncId, Type, [](uint8_t &CPU) XRAY_NEVER_INSTRUMENT {
3083cab2bb3Spatrick     timespec TS;
3093cab2bb3Spatrick     int result = clock_gettime(CLOCK_REALTIME, &TS);
3103cab2bb3Spatrick     if (result != 0) {
3113cab2bb3Spatrick       Report("clock_gettimg(2) return %d, errno=%d.", result, int(errno));
3123cab2bb3Spatrick       TS = {0, 0};
3133cab2bb3Spatrick     }
3143cab2bb3Spatrick     CPU = 0;
3153cab2bb3Spatrick     return TS.tv_sec * NanosecondsPerSecond + TS.tv_nsec;
3163cab2bb3Spatrick   });
3173cab2bb3Spatrick }
3183cab2bb3Spatrick 
basicLoggingHandleArg1RealTSC(int32_t FuncId,XRayEntryType Type,uint64_t Arg1)3193cab2bb3Spatrick void basicLoggingHandleArg1RealTSC(int32_t FuncId, XRayEntryType Type,
3203cab2bb3Spatrick                                    uint64_t Arg1) XRAY_NEVER_INSTRUMENT {
3213cab2bb3Spatrick   InMemoryRawLogWithArg(FuncId, Type, Arg1, readTSC);
3223cab2bb3Spatrick }
3233cab2bb3Spatrick 
basicLoggingHandleArg1EmulateTSC(int32_t FuncId,XRayEntryType Type,uint64_t Arg1)3243cab2bb3Spatrick void basicLoggingHandleArg1EmulateTSC(int32_t FuncId, XRayEntryType Type,
3253cab2bb3Spatrick                                       uint64_t Arg1) XRAY_NEVER_INSTRUMENT {
3263cab2bb3Spatrick   InMemoryRawLogWithArg(
3273cab2bb3Spatrick       FuncId, Type, Arg1, [](uint8_t &CPU) XRAY_NEVER_INSTRUMENT {
3283cab2bb3Spatrick         timespec TS;
3293cab2bb3Spatrick         int result = clock_gettime(CLOCK_REALTIME, &TS);
3303cab2bb3Spatrick         if (result != 0) {
3313cab2bb3Spatrick           Report("clock_gettimg(2) return %d, errno=%d.", result, int(errno));
3323cab2bb3Spatrick           TS = {0, 0};
3333cab2bb3Spatrick         }
3343cab2bb3Spatrick         CPU = 0;
3353cab2bb3Spatrick         return TS.tv_sec * NanosecondsPerSecond + TS.tv_nsec;
3363cab2bb3Spatrick       });
3373cab2bb3Spatrick }
3383cab2bb3Spatrick 
TLDDestructor(void * P)3393cab2bb3Spatrick static void TLDDestructor(void *P) XRAY_NEVER_INSTRUMENT {
3403cab2bb3Spatrick   ThreadLocalData &TLD = *reinterpret_cast<ThreadLocalData *>(P);
3413cab2bb3Spatrick   auto ExitGuard = at_scope_exit([&TLD] {
3423cab2bb3Spatrick     // Clean up dynamic resources.
3433cab2bb3Spatrick     if (TLD.InMemoryBuffer)
3443cab2bb3Spatrick       InternalFree(TLD.InMemoryBuffer);
3453cab2bb3Spatrick     if (TLD.ShadowStack)
3463cab2bb3Spatrick       InternalFree(TLD.ShadowStack);
3473cab2bb3Spatrick     if (Verbosity())
348*810390e3Srobert       Report("Cleaned up log for TID: %llu\n", GetTid());
3493cab2bb3Spatrick   });
3503cab2bb3Spatrick 
3513cab2bb3Spatrick   if (TLD.LogWriter == nullptr || TLD.BufferOffset == 0) {
3523cab2bb3Spatrick     if (Verbosity())
353*810390e3Srobert       Report("Skipping buffer for TID: %llu; Offset = %zu\n", GetTid(),
3543cab2bb3Spatrick              TLD.BufferOffset);
3553cab2bb3Spatrick     return;
3563cab2bb3Spatrick   }
3573cab2bb3Spatrick 
3583cab2bb3Spatrick   {
3593cab2bb3Spatrick     SpinMutexLock L(&LogMutex);
3603cab2bb3Spatrick     TLD.LogWriter->WriteAll(reinterpret_cast<char *>(TLD.InMemoryBuffer),
3613cab2bb3Spatrick                             reinterpret_cast<char *>(TLD.InMemoryBuffer) +
3623cab2bb3Spatrick                             (sizeof(XRayRecord) * TLD.BufferOffset));
3633cab2bb3Spatrick   }
3643cab2bb3Spatrick 
3653cab2bb3Spatrick   // Because this thread's exit could be the last one trying to write to
3663cab2bb3Spatrick   // the file and that we're not able to close out the file properly, we
3673cab2bb3Spatrick   // sync instead and hope that the pending writes are flushed as the
3683cab2bb3Spatrick   // thread exits.
3693cab2bb3Spatrick   TLD.LogWriter->Flush();
3703cab2bb3Spatrick }
3713cab2bb3Spatrick 
basicLoggingInit(UNUSED size_t BufferSize,UNUSED size_t BufferMax,void * Options,size_t OptionsSize)3723cab2bb3Spatrick XRayLogInitStatus basicLoggingInit(UNUSED size_t BufferSize,
3733cab2bb3Spatrick                                    UNUSED size_t BufferMax, void *Options,
3743cab2bb3Spatrick                                    size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
3753cab2bb3Spatrick   uint8_t Expected = 0;
3763cab2bb3Spatrick   if (!atomic_compare_exchange_strong(&BasicInitialized, &Expected, 1,
3773cab2bb3Spatrick                                       memory_order_acq_rel)) {
3783cab2bb3Spatrick     if (Verbosity())
3793cab2bb3Spatrick       Report("Basic logging already initialized.\n");
3803cab2bb3Spatrick     return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
3813cab2bb3Spatrick   }
3823cab2bb3Spatrick 
3833cab2bb3Spatrick   static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
3843cab2bb3Spatrick   pthread_once(&OnceInit, +[] {
3853cab2bb3Spatrick     pthread_key_create(&PThreadKey, TLDDestructor);
3863cab2bb3Spatrick     atomic_store(&UseRealTSC, probeRequiredCPUFeatures(), memory_order_release);
3873cab2bb3Spatrick     // Initialize the global TicksPerSec value.
3883cab2bb3Spatrick     atomic_store(&TicksPerSec,
3893cab2bb3Spatrick                  probeRequiredCPUFeatures() ? getTSCFrequency()
3903cab2bb3Spatrick                                             : NanosecondsPerSecond,
3913cab2bb3Spatrick                  memory_order_release);
3923cab2bb3Spatrick     if (!atomic_load(&UseRealTSC, memory_order_relaxed) && Verbosity())
3933cab2bb3Spatrick       Report("WARNING: Required CPU features missing for XRay instrumentation, "
3943cab2bb3Spatrick              "using emulation instead.\n");
3953cab2bb3Spatrick   });
3963cab2bb3Spatrick 
3973cab2bb3Spatrick   FlagParser P;
3983cab2bb3Spatrick   BasicFlags F;
3993cab2bb3Spatrick   F.setDefaults();
4003cab2bb3Spatrick   registerXRayBasicFlags(&P, &F);
4013cab2bb3Spatrick   P.ParseString(useCompilerDefinedBasicFlags());
4023cab2bb3Spatrick   auto *EnvOpts = GetEnv("XRAY_BASIC_OPTIONS");
4033cab2bb3Spatrick   if (EnvOpts == nullptr)
4043cab2bb3Spatrick     EnvOpts = "";
4053cab2bb3Spatrick 
4063cab2bb3Spatrick   P.ParseString(EnvOpts);
4073cab2bb3Spatrick 
4083cab2bb3Spatrick   // If XRAY_BASIC_OPTIONS was not defined, then we use the deprecated options
4093cab2bb3Spatrick   // set through XRAY_OPTIONS instead.
4103cab2bb3Spatrick   if (internal_strlen(EnvOpts) == 0) {
4113cab2bb3Spatrick     F.func_duration_threshold_us =
4123cab2bb3Spatrick         flags()->xray_naive_log_func_duration_threshold_us;
4133cab2bb3Spatrick     F.max_stack_depth = flags()->xray_naive_log_max_stack_depth;
4143cab2bb3Spatrick     F.thread_buffer_size = flags()->xray_naive_log_thread_buffer_size;
4153cab2bb3Spatrick   }
4163cab2bb3Spatrick 
4173cab2bb3Spatrick   P.ParseString(static_cast<const char *>(Options));
4183cab2bb3Spatrick   GlobalOptions.ThreadBufferSize = F.thread_buffer_size;
4193cab2bb3Spatrick   GlobalOptions.DurationFilterMicros = F.func_duration_threshold_us;
4203cab2bb3Spatrick   GlobalOptions.MaxStackDepth = F.max_stack_depth;
4213cab2bb3Spatrick   *basicFlags() = F;
4223cab2bb3Spatrick 
4233cab2bb3Spatrick   atomic_store(&ThresholdTicks,
4243cab2bb3Spatrick                atomic_load(&TicksPerSec, memory_order_acquire) *
4253cab2bb3Spatrick                    GlobalOptions.DurationFilterMicros / 1000000,
4263cab2bb3Spatrick                memory_order_release);
4273cab2bb3Spatrick   __xray_set_handler_arg1(atomic_load(&UseRealTSC, memory_order_acquire)
4283cab2bb3Spatrick                               ? basicLoggingHandleArg1RealTSC
4293cab2bb3Spatrick                               : basicLoggingHandleArg1EmulateTSC);
4303cab2bb3Spatrick   __xray_set_handler(atomic_load(&UseRealTSC, memory_order_acquire)
4313cab2bb3Spatrick                          ? basicLoggingHandleArg0RealTSC
4323cab2bb3Spatrick                          : basicLoggingHandleArg0EmulateTSC);
4333cab2bb3Spatrick 
4343cab2bb3Spatrick   // TODO: Implement custom event and typed event handling support in Basic
4353cab2bb3Spatrick   // Mode.
4363cab2bb3Spatrick   __xray_remove_customevent_handler();
4373cab2bb3Spatrick   __xray_remove_typedevent_handler();
4383cab2bb3Spatrick 
4393cab2bb3Spatrick   return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
4403cab2bb3Spatrick }
4413cab2bb3Spatrick 
basicLoggingFinalize()4423cab2bb3Spatrick XRayLogInitStatus basicLoggingFinalize() XRAY_NEVER_INSTRUMENT {
4433cab2bb3Spatrick   uint8_t Expected = 0;
4443cab2bb3Spatrick   if (!atomic_compare_exchange_strong(&BasicInitialized, &Expected, 0,
4453cab2bb3Spatrick                                       memory_order_acq_rel) &&
4463cab2bb3Spatrick       Verbosity())
4473cab2bb3Spatrick     Report("Basic logging already finalized.\n");
4483cab2bb3Spatrick 
4493cab2bb3Spatrick   // Nothing really to do aside from marking state of the global to be
4503cab2bb3Spatrick   // uninitialized.
4513cab2bb3Spatrick 
4523cab2bb3Spatrick   return XRayLogInitStatus::XRAY_LOG_FINALIZED;
4533cab2bb3Spatrick }
4543cab2bb3Spatrick 
basicLoggingFlush()4553cab2bb3Spatrick XRayLogFlushStatus basicLoggingFlush() XRAY_NEVER_INSTRUMENT {
4563cab2bb3Spatrick   // This really does nothing, since flushing the logs happen at the end of a
4573cab2bb3Spatrick   // thread's lifetime, or when the buffers are full.
4583cab2bb3Spatrick   return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
4593cab2bb3Spatrick }
4603cab2bb3Spatrick 
4613cab2bb3Spatrick // This is a handler that, effectively, does nothing.
basicLoggingHandleArg0Empty(int32_t,XRayEntryType)4623cab2bb3Spatrick void basicLoggingHandleArg0Empty(int32_t, XRayEntryType) XRAY_NEVER_INSTRUMENT {
4633cab2bb3Spatrick }
4643cab2bb3Spatrick 
basicLogDynamicInitializer()4653cab2bb3Spatrick bool basicLogDynamicInitializer() XRAY_NEVER_INSTRUMENT {
4663cab2bb3Spatrick   XRayLogImpl Impl{
4673cab2bb3Spatrick       basicLoggingInit,
4683cab2bb3Spatrick       basicLoggingFinalize,
4693cab2bb3Spatrick       basicLoggingHandleArg0Empty,
4703cab2bb3Spatrick       basicLoggingFlush,
4713cab2bb3Spatrick   };
4723cab2bb3Spatrick   auto RegistrationResult = __xray_log_register_mode("xray-basic", Impl);
4733cab2bb3Spatrick   if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK &&
4743cab2bb3Spatrick       Verbosity())
4753cab2bb3Spatrick     Report("Cannot register XRay Basic Mode to 'xray-basic'; error = %d\n",
4763cab2bb3Spatrick            RegistrationResult);
4773cab2bb3Spatrick   if (flags()->xray_naive_log ||
4783cab2bb3Spatrick       !internal_strcmp(flags()->xray_mode, "xray-basic")) {
4793cab2bb3Spatrick     auto SelectResult = __xray_log_select_mode("xray-basic");
4803cab2bb3Spatrick     if (SelectResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK) {
4813cab2bb3Spatrick       if (Verbosity())
4823cab2bb3Spatrick         Report("Failed selecting XRay Basic Mode; error = %d\n", SelectResult);
4833cab2bb3Spatrick       return false;
4843cab2bb3Spatrick     }
4853cab2bb3Spatrick 
4863cab2bb3Spatrick     // We initialize the implementation using the data we get from the
4873cab2bb3Spatrick     // XRAY_BASIC_OPTIONS environment variable, at this point of the
4883cab2bb3Spatrick     // implementation.
4893cab2bb3Spatrick     auto *Env = GetEnv("XRAY_BASIC_OPTIONS");
4903cab2bb3Spatrick     auto InitResult =
4913cab2bb3Spatrick         __xray_log_init_mode("xray-basic", Env == nullptr ? "" : Env);
4923cab2bb3Spatrick     if (InitResult != XRayLogInitStatus::XRAY_LOG_INITIALIZED) {
4933cab2bb3Spatrick       if (Verbosity())
4943cab2bb3Spatrick         Report("Failed initializing XRay Basic Mode; error = %d\n", InitResult);
4953cab2bb3Spatrick       return false;
4963cab2bb3Spatrick     }
4973cab2bb3Spatrick 
4983cab2bb3Spatrick     // At this point we know that we've successfully initialized Basic mode
4993cab2bb3Spatrick     // tracing, and the only chance we're going to get for the current thread to
5003cab2bb3Spatrick     // clean-up may be at thread/program exit. To ensure that we're going to get
5013cab2bb3Spatrick     // the cleanup even without calling the finalization routines, we're
5023cab2bb3Spatrick     // registering a program exit function that will do the cleanup.
5033cab2bb3Spatrick     static pthread_once_t DynamicOnce = PTHREAD_ONCE_INIT;
5043cab2bb3Spatrick     pthread_once(&DynamicOnce, +[] {
5053cab2bb3Spatrick       static void *FakeTLD = nullptr;
5063cab2bb3Spatrick       FakeTLD = &getThreadLocalData();
5073cab2bb3Spatrick       Atexit(+[] { TLDDestructor(FakeTLD); });
5083cab2bb3Spatrick     });
5093cab2bb3Spatrick   }
5103cab2bb3Spatrick   return true;
5113cab2bb3Spatrick }
5123cab2bb3Spatrick 
5133cab2bb3Spatrick } // namespace __xray
5143cab2bb3Spatrick 
5153cab2bb3Spatrick static auto UNUSED Unused = __xray::basicLogDynamicInitializer();
516