blob: 9a6172beb4aa1f0e9e4aeed4ba77d721a0677a52 [file] [log] [blame]
Dean Michael Berris47e6b8c2018-05-04 06:27:53 +00001//===-- xray_basic_logging.cc -----------------------------------*- C++ -*-===//
Dean Michael Berrisf50eb932016-08-26 06:39:33 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file is a part of XRay, a dynamic runtime instrumentation system.
11//
12// Implementation of a simple in-memory log of XRay events. This defines a
13// logging function that's compatible with the XRay handler interface, and
14// routines for exporting data to files.
15//
16//===----------------------------------------------------------------------===//
17
18#include <cassert>
Dean Michael Berris8dcba552017-10-05 05:45:51 +000019#include <cstring>
Dean Michael Berris9952d952017-08-02 04:51:40 +000020#include <errno.h>
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000021#include <fcntl.h>
Dean Michael Berris364f11c2017-11-21 07:29:21 +000022#include <pthread.h>
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000023#include <sys/stat.h>
24#include <sys/syscall.h>
25#include <sys/types.h>
Dean Michael Berris9952d952017-08-02 04:51:40 +000026#include <time.h>
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000027#include <unistd.h>
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +000028
Dean Michael Berris364f11c2017-11-21 07:29:21 +000029#include "sanitizer_common/sanitizer_allocator_internal.h"
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000030#include "sanitizer_common/sanitizer_libc.h"
31#include "xray/xray_records.h"
Dean Michael Berrisc5619702018-06-06 06:07:48 +000032#include "xray_recursion_guard.h"
Dean Michael Berris47e6b8c2018-05-04 06:27:53 +000033#include "xray_basic_flags.h"
34#include "xray_basic_logging.h"
Dean Michael Berris4031e4b2016-11-16 01:01:13 +000035#include "xray_defs.h"
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000036#include "xray_flags.h"
37#include "xray_interface_internal.h"
Tim Shen7d0bffb2017-02-10 20:30:43 +000038#include "xray_tsc.h"
Dean Michael Berrise7dbebf2017-01-25 03:50:46 +000039#include "xray_utils.h"
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000040
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000041namespace __xray {
42
Dean Michael Berris5eaaff62018-06-05 06:12:42 +000043SpinMutex LogMutex;
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000044
Dean Michael Berris364f11c2017-11-21 07:29:21 +000045// We use elements of this type to record the entry TSC of every function ID we
46// see as we're tracing a particular thread's execution.
Dean Michael Berris52517d72017-12-05 12:21:14 +000047struct alignas(16) StackEntry {
Dean Michael Berris364f11c2017-11-21 07:29:21 +000048 int32_t FuncId;
Dean Michael Berris52517d72017-12-05 12:21:14 +000049 uint16_t Type;
50 uint8_t CPU;
51 uint8_t Padding;
Dean Michael Berris364f11c2017-11-21 07:29:21 +000052 uint64_t TSC;
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000053};
54
Dean Michael Berris52517d72017-12-05 12:21:14 +000055static_assert(sizeof(StackEntry) == 16, "Wrong size for StackEntry");
56
Dean Michael Berris364f11c2017-11-21 07:29:21 +000057struct alignas(64) ThreadLocalData {
Dean Michael Berris52517d72017-12-05 12:21:14 +000058 void *InMemoryBuffer = nullptr;
Dean Michael Berris364f11c2017-11-21 07:29:21 +000059 size_t BufferSize = 0;
60 size_t BufferOffset = 0;
Dean Michael Berris52517d72017-12-05 12:21:14 +000061 void *ShadowStack = nullptr;
Dean Michael Berris364f11c2017-11-21 07:29:21 +000062 size_t StackSize = 0;
63 size_t StackEntries = 0;
64 int Fd = -1;
Kamil Rytarowski4d4ed0e2018-02-15 14:17:15 +000065 tid_t TID = 0;
Dean Michael Berris364f11c2017-11-21 07:29:21 +000066};
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000067
Dean Michael Berris364f11c2017-11-21 07:29:21 +000068static pthread_key_t PThreadKey;
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000069
Dean Michael Berris5eaaff62018-06-05 06:12:42 +000070static atomic_uint8_t BasicInitialized{0};
Dean Michael Berris364f11c2017-11-21 07:29:21 +000071
72BasicLoggingOptions GlobalOptions;
73
Dean Michael Berrisc5619702018-06-06 06:07:48 +000074thread_local atomic_uint8_t Guard{0};
Dean Michael Berris52517d72017-12-05 12:21:14 +000075
76static uint64_t thresholdTicks() XRAY_NEVER_INSTRUMENT {
77 static uint64_t TicksPerSec = probeRequiredCPUFeatures()
78 ? getTSCFrequency()
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +000079 : NanosecondsPerSecond;
Dean Michael Berris52517d72017-12-05 12:21:14 +000080 static const uint64_t ThresholdTicks =
81 TicksPerSec * GlobalOptions.DurationFilterMicros / 1000000;
82 return ThresholdTicks;
83}
Dean Michael Berris364f11c2017-11-21 07:29:21 +000084
85static int openLogFile() XRAY_NEVER_INSTRUMENT {
Dean Michael Berrise7dbebf2017-01-25 03:50:46 +000086 int F = getLogFD();
Dean Michael Berrise7dbebf2017-01-25 03:50:46 +000087 if (F == -1)
Dean Michael Berris0aba3572017-01-03 04:04:00 +000088 return -1;
Douglas Yung8439c8e2017-04-18 03:25:11 +000089
90 // Test for required CPU features and cache the cycle frequency
91 static bool TSCSupported = probeRequiredCPUFeatures();
Dean Michael Berris8dcba552017-10-05 05:45:51 +000092 static uint64_t CycleFrequency =
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +000093 TSCSupported ? getTSCFrequency() : NanosecondsPerSecond;
Douglas Yung8439c8e2017-04-18 03:25:11 +000094
Dean Michael Berris0aba3572017-01-03 04:04:00 +000095 // Since we're here, we get to write the header. We set it up so that the
96 // header will only be written once, at the start, and let the threads
97 // logging do writes which just append.
98 XRayFileHeader Header;
Dean Michael Berris8dcba552017-10-05 05:45:51 +000099 Header.Version = 2; // Version 2 includes tail exit records.
Dean Michael Berris0aba3572017-01-03 04:04:00 +0000100 Header.Type = FileTypes::NAIVE_LOG;
Douglas Yung8439c8e2017-04-18 03:25:11 +0000101 Header.CycleFrequency = CycleFrequency;
Dean Michael Berris0aba3572017-01-03 04:04:00 +0000102
103 // FIXME: Actually check whether we have 'constant_tsc' and 'nonstop_tsc'
104 // before setting the values in the header.
105 Header.ConstantTSC = 1;
106 Header.NonstopTSC = 1;
Dean Michael Berrise7dbebf2017-01-25 03:50:46 +0000107 retryingWriteAll(F, reinterpret_cast<char *>(&Header),
Dean Michael Berris0aba3572017-01-03 04:04:00 +0000108 reinterpret_cast<char *>(&Header) + sizeof(Header));
Dean Michael Berrise7dbebf2017-01-25 03:50:46 +0000109 return F;
Dean Michael Berris0aba3572017-01-03 04:04:00 +0000110}
111
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000112int getGlobalFd() XRAY_NEVER_INSTRUMENT {
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000113 static int Fd = openLogFile();
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000114 return Fd;
115}
116
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000117ThreadLocalData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
118 thread_local ThreadLocalData TLD;
119 thread_local bool UNUSED TOnce = [] {
Dean Michael Berris52517d72017-12-05 12:21:14 +0000120 if (GlobalOptions.ThreadBufferSize == 0) {
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000121 if (Verbosity())
Dean Michael Berris52517d72017-12-05 12:21:14 +0000122 Report("Not initializing TLD since ThreadBufferSize == 0.\n");
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000123 return false;
Dean Michael Berris52517d72017-12-05 12:21:14 +0000124 }
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000125 TLD.TID = GetTid();
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000126 pthread_setspecific(PThreadKey, &TLD);
127 TLD.Fd = getGlobalFd();
128 TLD.InMemoryBuffer = reinterpret_cast<XRayRecord *>(
129 InternalAlloc(sizeof(XRayRecord) * GlobalOptions.ThreadBufferSize,
130 nullptr, alignof(XRayRecord)));
131 TLD.BufferSize = GlobalOptions.ThreadBufferSize;
132 TLD.BufferOffset = 0;
Dean Michael Berris52517d72017-12-05 12:21:14 +0000133 if (GlobalOptions.MaxStackDepth == 0) {
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000134 if (Verbosity())
Dean Michael Berris52517d72017-12-05 12:21:14 +0000135 Report("Not initializing the ShadowStack since MaxStackDepth == 0.\n");
136 TLD.StackSize = 0;
137 TLD.StackEntries = 0;
138 TLD.ShadowStack = nullptr;
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000139 return false;
Dean Michael Berris52517d72017-12-05 12:21:14 +0000140 }
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000141 TLD.ShadowStack = reinterpret_cast<StackEntry *>(
142 InternalAlloc(sizeof(StackEntry) * GlobalOptions.MaxStackDepth, nullptr,
143 alignof(StackEntry)));
144 TLD.StackSize = GlobalOptions.MaxStackDepth;
145 TLD.StackEntries = 0;
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000146 if (Verbosity() >= 2) {
Dean Michael Berris52517d72017-12-05 12:21:14 +0000147 static auto UNUSED Once = [] {
148 auto ticks = thresholdTicks();
149 Report("Ticks threshold: %d\n", ticks);
150 return false;
151 }();
152 }
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000153 return false;
154 }();
155 return TLD;
156}
157
Dean Michael Berris66443d82017-03-15 02:28:00 +0000158template <class RDTSC>
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000159void InMemoryRawLog(int32_t FuncId, XRayEntryType Type,
160 RDTSC ReadTSC) XRAY_NEVER_INSTRUMENT {
161 auto &TLD = getThreadLocalData();
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000162 int Fd = getGlobalFd();
Dean Michael Berrisf50eb932016-08-26 06:39:33 +0000163 if (Fd == -1)
164 return;
Dean Michael Berrisf50eb932016-08-26 06:39:33 +0000165
Dean Michael Berris484fe0a2017-09-18 06:18:03 +0000166 // Use a simple recursion guard, to handle cases where we're already logging
167 // and for one reason or another, this function gets called again in the same
168 // thread.
Dean Michael Berrisc5619702018-06-06 06:07:48 +0000169 RecursionGuard G(Guard);
170 if (!G)
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000171 return;
Dean Michael Berris484fe0a2017-09-18 06:18:03 +0000172
Dean Michael Berris52517d72017-12-05 12:21:14 +0000173 uint8_t CPU = 0;
174 uint64_t TSC = ReadTSC(CPU);
175
176 switch (Type) {
177 case XRayEntryType::ENTRY:
178 case XRayEntryType::LOG_ARGS_ENTRY: {
179 // Short circuit if we've reached the maximum depth of the stack.
180 if (TLD.StackEntries++ >= TLD.StackSize)
181 return;
182
183 // When we encounter an entry event, we keep track of the TSC and the CPU,
184 // and put it in the stack.
185 StackEntry E;
186 E.FuncId = FuncId;
187 E.CPU = CPU;
188 E.Type = Type;
189 E.TSC = TSC;
190 auto StackEntryPtr = static_cast<char *>(TLD.ShadowStack) +
191 (sizeof(StackEntry) * (TLD.StackEntries - 1));
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000192 internal_memcpy(StackEntryPtr, &E, sizeof(StackEntry));
Dean Michael Berris52517d72017-12-05 12:21:14 +0000193 break;
194 }
195 case XRayEntryType::EXIT:
196 case XRayEntryType::TAIL: {
197 if (TLD.StackEntries == 0)
198 break;
199
200 if (--TLD.StackEntries >= TLD.StackSize)
201 return;
202
203 // When we encounter an exit event, we check whether all the following are
204 // true:
205 //
206 // - The Function ID is the same as the most recent entry in the stack.
207 // - The CPU is the same as the most recent entry in the stack.
208 // - The Delta of the TSCs is less than the threshold amount of time we're
209 // looking to record.
210 //
211 // If all of these conditions are true, we pop the stack and don't write a
212 // record and move the record offset back.
213 StackEntry StackTop;
214 auto StackEntryPtr = static_cast<char *>(TLD.ShadowStack) +
215 (sizeof(StackEntry) * TLD.StackEntries);
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000216 internal_memcpy(&StackTop, StackEntryPtr, sizeof(StackEntry));
Dean Michael Berris52517d72017-12-05 12:21:14 +0000217 if (StackTop.FuncId == FuncId && StackTop.CPU == CPU &&
218 StackTop.TSC < TSC) {
219 auto Delta = TSC - StackTop.TSC;
220 if (Delta < thresholdTicks()) {
221 assert(TLD.BufferOffset > 0);
222 TLD.BufferOffset -= StackTop.Type == XRayEntryType::ENTRY ? 1 : 2;
223 return;
224 }
225 }
226 break;
227 }
228 default:
229 // Should be unreachable.
230 assert(false && "Unsupported XRayEntryType encountered.");
231 break;
232 }
233
234 // First determine whether the delta between the function's enter record and
235 // the exit record is higher than the threshold.
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000236 XRayRecord R;
Dean Michael Berrisf50eb932016-08-26 06:39:33 +0000237 R.RecordType = RecordTypes::NORMAL;
Dean Michael Berris52517d72017-12-05 12:21:14 +0000238 R.CPU = CPU;
239 R.TSC = TSC;
240 R.TId = TLD.TID;
Dean Michael Berrisf50eb932016-08-26 06:39:33 +0000241 R.Type = Type;
242 R.FuncId = FuncId;
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000243 auto FirstEntry = reinterpret_cast<XRayRecord *>(TLD.InMemoryBuffer);
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000244 internal_memcpy(FirstEntry + TLD.BufferOffset, &R, sizeof(R));
Dean Michael Berris52517d72017-12-05 12:21:14 +0000245 if (++TLD.BufferOffset == TLD.BufferSize) {
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000246 SpinMutexLock L(&LogMutex);
Martin Pelikan4834bca2018-01-19 13:18:40 +0000247 retryingWriteAll(Fd, reinterpret_cast<char *>(FirstEntry),
248 reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
Dean Michael Berris52517d72017-12-05 12:21:14 +0000249 TLD.BufferOffset = 0;
250 TLD.StackEntries = 0;
Dean Michael Berrisf50eb932016-08-26 06:39:33 +0000251 }
252}
253
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000254template <class RDTSC>
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000255void InMemoryRawLogWithArg(int32_t FuncId, XRayEntryType Type, uint64_t Arg1,
256 RDTSC ReadTSC) XRAY_NEVER_INSTRUMENT {
257 auto &TLD = getThreadLocalData();
Martin Pelikan4834bca2018-01-19 13:18:40 +0000258 auto FirstEntry =
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000259 reinterpret_cast<XRayArgPayload *>(TLD.InMemoryBuffer);
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000260 const auto &BuffLen = TLD.BufferSize;
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000261 int Fd = getGlobalFd();
262 if (Fd == -1)
263 return;
264
265 // First we check whether there's enough space to write the data consecutively
266 // in the thread-local buffer. If not, we first flush the buffer before
267 // attempting to write the two records that must be consecutive.
Martin Pelikan4834bca2018-01-19 13:18:40 +0000268 if (TLD.BufferOffset + 2 > BuffLen) {
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000269 SpinMutexLock L(&LogMutex);
Martin Pelikan4834bca2018-01-19 13:18:40 +0000270 retryingWriteAll(Fd, reinterpret_cast<char *>(FirstEntry),
271 reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
272 TLD.BufferOffset = 0;
Dean Michael Berris52517d72017-12-05 12:21:14 +0000273 TLD.StackEntries = 0;
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000274 }
275
276 // Then we write the "we have an argument" record.
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000277 InMemoryRawLog(FuncId, Type, ReadTSC);
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000278
Dean Michael Berrisc5619702018-06-06 06:07:48 +0000279 RecursionGuard G(Guard);
280 if (!G)
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000281 return;
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000282
283 // And from here on write the arg payload.
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000284 XRayArgPayload R;
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000285 R.RecordType = RecordTypes::ARG_PAYLOAD;
286 R.FuncId = FuncId;
Dean Michael Berris52517d72017-12-05 12:21:14 +0000287 R.TId = TLD.TID;
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000288 R.Arg = Arg1;
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000289 internal_memcpy(FirstEntry + TLD.BufferOffset, &R, sizeof(R));
Martin Pelikan4834bca2018-01-19 13:18:40 +0000290 if (++TLD.BufferOffset == BuffLen) {
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000291 SpinMutexLock L(&LogMutex);
Martin Pelikan4834bca2018-01-19 13:18:40 +0000292 retryingWriteAll(Fd, reinterpret_cast<char *>(FirstEntry),
293 reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
294 TLD.BufferOffset = 0;
Dean Michael Berris52517d72017-12-05 12:21:14 +0000295 TLD.StackEntries = 0;
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000296 }
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000297}
298
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000299void basicLoggingHandleArg0RealTSC(int32_t FuncId,
300 XRayEntryType Type) XRAY_NEVER_INSTRUMENT {
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000301 InMemoryRawLog(FuncId, Type, readTSC);
Dean Michael Berris66443d82017-03-15 02:28:00 +0000302}
303
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000304void basicLoggingHandleArg0EmulateTSC(int32_t FuncId, XRayEntryType Type)
305 XRAY_NEVER_INSTRUMENT {
306 InMemoryRawLog(FuncId, Type, [](uint8_t &CPU) XRAY_NEVER_INSTRUMENT {
Dean Michael Berris66443d82017-03-15 02:28:00 +0000307 timespec TS;
308 int result = clock_gettime(CLOCK_REALTIME, &TS);
309 if (result != 0) {
310 Report("clock_gettimg(2) return %d, errno=%d.", result, int(errno));
311 TS = {0, 0};
312 }
313 CPU = 0;
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000314 return TS.tv_sec * NanosecondsPerSecond + TS.tv_nsec;
Dean Michael Berris66443d82017-03-15 02:28:00 +0000315 });
316}
317
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000318void basicLoggingHandleArg1RealTSC(int32_t FuncId, XRayEntryType Type,
319 uint64_t Arg1) XRAY_NEVER_INSTRUMENT {
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000320 InMemoryRawLogWithArg(FuncId, Type, Arg1, readTSC);
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000321}
322
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000323void basicLoggingHandleArg1EmulateTSC(int32_t FuncId, XRayEntryType Type,
324 uint64_t Arg1) XRAY_NEVER_INSTRUMENT {
325 InMemoryRawLogWithArg(
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000326 FuncId, Type, Arg1, [](uint8_t &CPU) XRAY_NEVER_INSTRUMENT {
327 timespec TS;
328 int result = clock_gettime(CLOCK_REALTIME, &TS);
329 if (result != 0) {
330 Report("clock_gettimg(2) return %d, errno=%d.", result, int(errno));
331 TS = {0, 0};
332 }
333 CPU = 0;
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000334 return TS.tv_sec * NanosecondsPerSecond + TS.tv_nsec;
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000335 });
336}
337
Dean Michael Berris52517d72017-12-05 12:21:14 +0000338static void TLDDestructor(void *P) XRAY_NEVER_INSTRUMENT {
339 ThreadLocalData &TLD = *reinterpret_cast<ThreadLocalData *>(P);
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000340 auto ExitGuard = at_scope_exit([&TLD] {
Dean Michael Berris52517d72017-12-05 12:21:14 +0000341 // Clean up dynamic resources.
342 if (TLD.InMemoryBuffer)
343 InternalFree(TLD.InMemoryBuffer);
344 if (TLD.ShadowStack)
345 InternalFree(TLD.ShadowStack);
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000346 if (Verbosity())
Dean Michael Berris52517d72017-12-05 12:21:14 +0000347 Report("Cleaned up log for TID: %d\n", TLD.TID);
348 });
349
350 if (TLD.Fd == -1 || TLD.BufferOffset == 0) {
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000351 if (Verbosity())
Dean Michael Berris52517d72017-12-05 12:21:14 +0000352 Report("Skipping buffer for TID: %d; Fd = %d; Offset = %llu\n", TLD.TID,
353 TLD.Fd, TLD.BufferOffset);
354 return;
355 }
356
357 {
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000358 SpinMutexLock L(&LogMutex);
Dean Michael Berris52517d72017-12-05 12:21:14 +0000359 retryingWriteAll(TLD.Fd, reinterpret_cast<char *>(TLD.InMemoryBuffer),
360 reinterpret_cast<char *>(TLD.InMemoryBuffer) +
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000361 (sizeof(XRayRecord) * TLD.BufferOffset));
Dean Michael Berris52517d72017-12-05 12:21:14 +0000362 }
363
364 // Because this thread's exit could be the last one trying to write to
365 // the file and that we're not able to close out the file properly, we
366 // sync instead and hope that the pending writes are flushed as the
367 // thread exits.
368 fsync(TLD.Fd);
369}
370
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000371XRayLogInitStatus basicLoggingInit(size_t BufferSize, size_t BufferMax,
372 void *Options,
373 size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000374 uint8_t Expected = 0;
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000375 if (!atomic_compare_exchange_strong(
376 &BasicInitialized, &Expected, 1, memory_order_acq_rel)) {
377 if (Verbosity())
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000378 Report("Basic logging already initialized.\n");
379 return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000380 }
381
Dean Michael Berris47e6b8c2018-05-04 06:27:53 +0000382 static bool UNUSED Once = [] {
383 pthread_key_create(&PThreadKey, TLDDestructor);
384 return false;
385 }();
386
387 if (BufferSize == 0 && BufferMax == 0 && Options != nullptr) {
388 FlagParser P;
389 BasicFlags F;
390 F.setDefaults();
391 registerXRayBasicFlags(&P, &F);
392 P.ParseString(useCompilerDefinedBasicFlags());
393 auto *EnvOpts = GetEnv("XRAY_BASIC_OPTIONS");
394 if (EnvOpts == nullptr)
395 EnvOpts = "";
396
397 P.ParseString(EnvOpts);
398
399 // If XRAY_BASIC_OPTIONS was not defined, then we use the deprecated options
400 // set through XRAY_OPTIONS instead.
401 if (internal_strlen(EnvOpts) == 0) {
402 F.func_duration_threshold_us =
403 flags()->xray_naive_log_func_duration_threshold_us;
404 F.max_stack_depth = flags()->xray_naive_log_max_stack_depth;
405 F.thread_buffer_size = flags()->xray_naive_log_thread_buffer_size;
406 }
407
408 P.ParseString(static_cast<const char *>(Options));
409 GlobalOptions.ThreadBufferSize = F.thread_buffer_size;
410 GlobalOptions.DurationFilterMicros = F.func_duration_threshold_us;
411 GlobalOptions.MaxStackDepth = F.max_stack_depth;
412 } else if (OptionsSize != sizeof(BasicLoggingOptions)) {
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000413 Report("Invalid options size, potential ABI mismatch; expected %d got %d",
414 sizeof(BasicLoggingOptions), OptionsSize);
415 return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
Dean Michael Berris47e6b8c2018-05-04 06:27:53 +0000416 } else {
417 if (Verbosity())
418 Report("XRay Basic: struct-based init is deprecated, please use "
419 "string-based configuration instead.\n");
420 GlobalOptions = *reinterpret_cast<BasicLoggingOptions *>(Options);
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000421 }
422
423 static auto UseRealTSC = probeRequiredCPUFeatures();
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000424 if (!UseRealTSC && Verbosity())
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000425 Report("WARNING: Required CPU features missing for XRay instrumentation, "
426 "using emulation instead.\n");
427
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000428 __xray_set_handler_arg1(UseRealTSC ? basicLoggingHandleArg1RealTSC
429 : basicLoggingHandleArg1EmulateTSC);
430 __xray_set_handler(UseRealTSC ? basicLoggingHandleArg0RealTSC
431 : basicLoggingHandleArg0EmulateTSC);
432 __xray_remove_customevent_handler();
Keith Wyssadb092e2018-04-17 21:28:53 +0000433 __xray_remove_typedevent_handler();
Dean Michael Berris52517d72017-12-05 12:21:14 +0000434
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000435 return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
436}
437
438XRayLogInitStatus basicLoggingFinalize() XRAY_NEVER_INSTRUMENT {
439 uint8_t Expected = 0;
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000440 if (!atomic_compare_exchange_strong(
441 &BasicInitialized, &Expected, 0, memory_order_acq_rel) &&
442 Verbosity())
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000443 Report("Basic logging already finalized.\n");
444
445 // Nothing really to do aside from marking state of the global to be
446 // uninitialized.
447
448 return XRayLogInitStatus::XRAY_LOG_FINALIZED;
449}
450
451XRayLogFlushStatus basicLoggingFlush() XRAY_NEVER_INSTRUMENT {
452 // This really does nothing, since flushing the logs happen at the end of a
453 // thread's lifetime, or when the buffers are full.
454 return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
455}
456
457// This is a handler that, effectively, does nothing.
458void basicLoggingHandleArg0Empty(int32_t, XRayEntryType) XRAY_NEVER_INSTRUMENT {
459}
460
461bool basicLogDynamicInitializer() XRAY_NEVER_INSTRUMENT {
Dean Michael Berrisc360f412017-12-05 12:08:56 +0000462 XRayLogImpl Impl{
463 basicLoggingInit,
464 basicLoggingFinalize,
465 basicLoggingHandleArg0Empty,
466 basicLoggingFlush,
467 };
468 auto RegistrationResult = __xray_log_register_mode("xray-basic", Impl);
469 if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK &&
Dean Michael Berris47e6b8c2018-05-04 06:27:53 +0000470 Verbosity())
Dean Michael Berrisc360f412017-12-05 12:08:56 +0000471 Report("Cannot register XRay Basic Mode to 'xray-basic'; error = %d\n",
472 RegistrationResult);
473 if (flags()->xray_naive_log ||
Dean Michael Berris47e6b8c2018-05-04 06:27:53 +0000474 !internal_strcmp(flags()->xray_mode, "xray-basic")) {
475 auto SelectResult = __xray_log_select_mode("xray-basic");
476 if (SelectResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK) {
477 if (Verbosity())
478 Report("Failed selecting XRay Basic Mode; error = %d\n", SelectResult);
479 return false;
480 }
481
482 // We initialize the implementation using the data we get from the
483 // XRAY_BASIC_OPTIONS environment variable, at this point of the
484 // implementation.
485 auto *Env = GetEnv("XRAY_BASIC_OPTIONS");
486 auto InitResult =
487 __xray_log_init_mode("xray-basic", Env == nullptr ? "" : Env);
488 if (InitResult != XRayLogInitStatus::XRAY_LOG_INITIALIZED) {
489 if (Verbosity())
490 Report("Failed initializing XRay Basic Mode; error = %d\n", InitResult);
491 return false;
492 }
Dean Michael Berris52517d72017-12-05 12:21:14 +0000493 static auto UNUSED Once = [] {
494 static auto UNUSED &TLD = getThreadLocalData();
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000495 Atexit(+[] { TLDDestructor(&TLD); });
Dean Michael Berris52517d72017-12-05 12:21:14 +0000496 return false;
497 }();
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000498 }
Dean Michael Berrisf50eb932016-08-26 06:39:33 +0000499 return true;
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000500}
501
502} // namespace __xray
503
504static auto UNUSED Unused = __xray::basicLogDynamicInitializer();