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