blob: 585ca641cd0cd4dd9deeb464fb90e23cb1944fda [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
Dean Michael Berris9952d952017-08-02 04:51:40 +000018#include <errno.h>
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000019#include <fcntl.h>
Dean Michael Berris364f11c2017-11-21 07:29:21 +000020#include <pthread.h>
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000021#include <sys/stat.h>
22#include <sys/syscall.h>
23#include <sys/types.h>
Dean Michael Berris9952d952017-08-02 04:51:40 +000024#include <time.h>
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000025#include <unistd.h>
Dean Michael Berrisd1617cd2016-09-20 14:35:57 +000026
Dean Michael Berris364f11c2017-11-21 07:29:21 +000027#include "sanitizer_common/sanitizer_allocator_internal.h"
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000028#include "sanitizer_common/sanitizer_libc.h"
29#include "xray/xray_records.h"
Dean Michael Berrisc5619702018-06-06 06:07:48 +000030#include "xray_recursion_guard.h"
Dean Michael Berris47e6b8c2018-05-04 06:27:53 +000031#include "xray_basic_flags.h"
32#include "xray_basic_logging.h"
Dean Michael Berris4031e4b2016-11-16 01:01:13 +000033#include "xray_defs.h"
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000034#include "xray_flags.h"
35#include "xray_interface_internal.h"
Tim Shen7d0bffb2017-02-10 20:30:43 +000036#include "xray_tsc.h"
Dean Michael Berrise7dbebf2017-01-25 03:50:46 +000037#include "xray_utils.h"
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000038
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000039namespace __xray {
40
Dean Michael Berris5eaaff62018-06-05 06:12:42 +000041SpinMutex LogMutex;
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000042
Dean Michael Berris364f11c2017-11-21 07:29:21 +000043// We use elements of this type to record the entry TSC of every function ID we
44// see as we're tracing a particular thread's execution.
Dean Michael Berris52517d72017-12-05 12:21:14 +000045struct alignas(16) StackEntry {
Dean Michael Berris364f11c2017-11-21 07:29:21 +000046 int32_t FuncId;
Dean Michael Berris52517d72017-12-05 12:21:14 +000047 uint16_t Type;
48 uint8_t CPU;
49 uint8_t Padding;
Dean Michael Berris364f11c2017-11-21 07:29:21 +000050 uint64_t TSC;
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000051};
52
Dean Michael Berris52517d72017-12-05 12:21:14 +000053static_assert(sizeof(StackEntry) == 16, "Wrong size for StackEntry");
54
Dean Michael Berris364f11c2017-11-21 07:29:21 +000055struct alignas(64) ThreadLocalData {
Dean Michael Berris52517d72017-12-05 12:21:14 +000056 void *InMemoryBuffer = nullptr;
Dean Michael Berris364f11c2017-11-21 07:29:21 +000057 size_t BufferSize = 0;
58 size_t BufferOffset = 0;
Dean Michael Berris52517d72017-12-05 12:21:14 +000059 void *ShadowStack = nullptr;
Dean Michael Berris364f11c2017-11-21 07:29:21 +000060 size_t StackSize = 0;
61 size_t StackEntries = 0;
62 int Fd = -1;
63};
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000064
Dean Michael Berris364f11c2017-11-21 07:29:21 +000065static pthread_key_t PThreadKey;
Dean Michael Berrisf50eb932016-08-26 06:39:33 +000066
Dean Michael Berris5eaaff62018-06-05 06:12:42 +000067static atomic_uint8_t BasicInitialized{0};
Dean Michael Berris364f11c2017-11-21 07:29:21 +000068
69BasicLoggingOptions GlobalOptions;
70
Dean Michael Berrisc5619702018-06-06 06:07:48 +000071thread_local atomic_uint8_t Guard{0};
Dean Michael Berris52517d72017-12-05 12:21:14 +000072
Dean Michael Berrisaf4d5832018-06-08 04:00:07 +000073static atomic_uint8_t UseRealTSC{0};
74static atomic_uint64_t ThresholdTicks{0};
75static atomic_uint64_t TicksPerSec{0};
76static atomic_uint64_t CycleFrequency{NanosecondsPerSecond};
Dean Michael Berris364f11c2017-11-21 07:29:21 +000077
78static int openLogFile() XRAY_NEVER_INSTRUMENT {
Dean Michael Berrise7dbebf2017-01-25 03:50:46 +000079 int F = getLogFD();
Dean Michael Berrise7dbebf2017-01-25 03:50:46 +000080 if (F == -1)
Dean Michael Berris0aba3572017-01-03 04:04:00 +000081 return -1;
Douglas Yung8439c8e2017-04-18 03:25:11 +000082
Dean Michael Berrisaf4d5832018-06-08 04:00:07 +000083 static pthread_once_t DetectOnce = PTHREAD_ONCE_INIT;
84 pthread_once(&DetectOnce, +[] {
85 if (atomic_load(&UseRealTSC, memory_order_acquire))
86 atomic_store(&CycleFrequency, getTSCFrequency(), memory_order_release);
87 });
Douglas Yung8439c8e2017-04-18 03:25:11 +000088
Dean Michael Berris0aba3572017-01-03 04:04:00 +000089 // Since we're here, we get to write the header. We set it up so that the
90 // header will only be written once, at the start, and let the threads
91 // logging do writes which just append.
92 XRayFileHeader Header;
Dean Michael Berris10141262018-07-13 05:38:22 +000093 // Version 2 includes tail exit records.
94 // Version 3 includes pid inside records.
95 Header.Version = 3;
Dean Michael Berris0aba3572017-01-03 04:04:00 +000096 Header.Type = FileTypes::NAIVE_LOG;
Dean Michael Berrisaf4d5832018-06-08 04:00:07 +000097 Header.CycleFrequency = atomic_load(&CycleFrequency, memory_order_acquire);
Dean Michael Berris0aba3572017-01-03 04:04:00 +000098
99 // FIXME: Actually check whether we have 'constant_tsc' and 'nonstop_tsc'
100 // before setting the values in the header.
101 Header.ConstantTSC = 1;
102 Header.NonstopTSC = 1;
Dean Michael Berrise7dbebf2017-01-25 03:50:46 +0000103 retryingWriteAll(F, reinterpret_cast<char *>(&Header),
Dean Michael Berris0aba3572017-01-03 04:04:00 +0000104 reinterpret_cast<char *>(&Header) + sizeof(Header));
Dean Michael Berrise7dbebf2017-01-25 03:50:46 +0000105 return F;
Dean Michael Berris0aba3572017-01-03 04:04:00 +0000106}
107
Dean Michael Berrisaf4d5832018-06-08 04:00:07 +0000108static int getGlobalFd() XRAY_NEVER_INSTRUMENT {
109 static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
110 static int Fd = 0;
111 pthread_once(&OnceInit, +[] { Fd = openLogFile(); });
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000112 return Fd;
113}
114
Dean Michael Berrisaf4d5832018-06-08 04:00:07 +0000115static ThreadLocalData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000116 thread_local ThreadLocalData TLD;
117 thread_local bool UNUSED TOnce = [] {
Dean Michael Berris52517d72017-12-05 12:21:14 +0000118 if (GlobalOptions.ThreadBufferSize == 0) {
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000119 if (Verbosity())
Dean Michael Berris52517d72017-12-05 12:21:14 +0000120 Report("Not initializing TLD since ThreadBufferSize == 0.\n");
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000121 return false;
Dean Michael Berris52517d72017-12-05 12:21:14 +0000122 }
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000123 pthread_setspecific(PThreadKey, &TLD);
124 TLD.Fd = getGlobalFd();
125 TLD.InMemoryBuffer = reinterpret_cast<XRayRecord *>(
126 InternalAlloc(sizeof(XRayRecord) * GlobalOptions.ThreadBufferSize,
127 nullptr, alignof(XRayRecord)));
128 TLD.BufferSize = GlobalOptions.ThreadBufferSize;
129 TLD.BufferOffset = 0;
Dean Michael Berris52517d72017-12-05 12:21:14 +0000130 if (GlobalOptions.MaxStackDepth == 0) {
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000131 if (Verbosity())
Dean Michael Berris52517d72017-12-05 12:21:14 +0000132 Report("Not initializing the ShadowStack since MaxStackDepth == 0.\n");
133 TLD.StackSize = 0;
134 TLD.StackEntries = 0;
135 TLD.ShadowStack = nullptr;
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000136 return false;
Dean Michael Berris52517d72017-12-05 12:21:14 +0000137 }
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000138 TLD.ShadowStack = reinterpret_cast<StackEntry *>(
139 InternalAlloc(sizeof(StackEntry) * GlobalOptions.MaxStackDepth, nullptr,
140 alignof(StackEntry)));
141 TLD.StackSize = GlobalOptions.MaxStackDepth;
142 TLD.StackEntries = 0;
143 return false;
144 }();
145 return TLD;
146}
147
Dean Michael Berris66443d82017-03-15 02:28:00 +0000148template <class RDTSC>
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000149void InMemoryRawLog(int32_t FuncId, XRayEntryType Type,
150 RDTSC ReadTSC) XRAY_NEVER_INSTRUMENT {
151 auto &TLD = getThreadLocalData();
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000152 int Fd = getGlobalFd();
Dean Michael Berrisf50eb932016-08-26 06:39:33 +0000153 if (Fd == -1)
154 return;
Dean Michael Berrisf50eb932016-08-26 06:39:33 +0000155
Dean Michael Berris484fe0a2017-09-18 06:18:03 +0000156 // Use a simple recursion guard, to handle cases where we're already logging
157 // and for one reason or another, this function gets called again in the same
158 // thread.
Dean Michael Berrisc5619702018-06-06 06:07:48 +0000159 RecursionGuard G(Guard);
160 if (!G)
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000161 return;
Dean Michael Berris484fe0a2017-09-18 06:18:03 +0000162
Dean Michael Berris52517d72017-12-05 12:21:14 +0000163 uint8_t CPU = 0;
164 uint64_t TSC = ReadTSC(CPU);
165
166 switch (Type) {
167 case XRayEntryType::ENTRY:
168 case XRayEntryType::LOG_ARGS_ENTRY: {
169 // Short circuit if we've reached the maximum depth of the stack.
170 if (TLD.StackEntries++ >= TLD.StackSize)
171 return;
172
173 // When we encounter an entry event, we keep track of the TSC and the CPU,
174 // and put it in the stack.
175 StackEntry E;
176 E.FuncId = FuncId;
177 E.CPU = CPU;
178 E.Type = Type;
179 E.TSC = TSC;
180 auto StackEntryPtr = static_cast<char *>(TLD.ShadowStack) +
181 (sizeof(StackEntry) * (TLD.StackEntries - 1));
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000182 internal_memcpy(StackEntryPtr, &E, sizeof(StackEntry));
Dean Michael Berris52517d72017-12-05 12:21:14 +0000183 break;
184 }
185 case XRayEntryType::EXIT:
186 case XRayEntryType::TAIL: {
187 if (TLD.StackEntries == 0)
188 break;
189
190 if (--TLD.StackEntries >= TLD.StackSize)
191 return;
192
193 // When we encounter an exit event, we check whether all the following are
194 // true:
195 //
196 // - The Function ID is the same as the most recent entry in the stack.
197 // - The CPU is the same as the most recent entry in the stack.
198 // - The Delta of the TSCs is less than the threshold amount of time we're
199 // looking to record.
200 //
201 // If all of these conditions are true, we pop the stack and don't write a
202 // record and move the record offset back.
203 StackEntry StackTop;
204 auto StackEntryPtr = static_cast<char *>(TLD.ShadowStack) +
205 (sizeof(StackEntry) * TLD.StackEntries);
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000206 internal_memcpy(&StackTop, StackEntryPtr, sizeof(StackEntry));
Dean Michael Berris52517d72017-12-05 12:21:14 +0000207 if (StackTop.FuncId == FuncId && StackTop.CPU == CPU &&
208 StackTop.TSC < TSC) {
209 auto Delta = TSC - StackTop.TSC;
Dean Michael Berrisaf4d5832018-06-08 04:00:07 +0000210 if (Delta < atomic_load(&ThresholdTicks, memory_order_relaxed)) {
211 DCHECK(TLD.BufferOffset > 0);
Dean Michael Berris52517d72017-12-05 12:21:14 +0000212 TLD.BufferOffset -= StackTop.Type == XRayEntryType::ENTRY ? 1 : 2;
213 return;
214 }
215 }
216 break;
217 }
218 default:
219 // Should be unreachable.
Dean Michael Berrisaf4d5832018-06-08 04:00:07 +0000220 DCHECK(false && "Unsupported XRayEntryType encountered.");
Dean Michael Berris52517d72017-12-05 12:21:14 +0000221 break;
222 }
223
224 // First determine whether the delta between the function's enter record and
225 // the exit record is higher than the threshold.
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000226 XRayRecord R;
Dean Michael Berrisf50eb932016-08-26 06:39:33 +0000227 R.RecordType = RecordTypes::NORMAL;
Dean Michael Berris52517d72017-12-05 12:21:14 +0000228 R.CPU = CPU;
229 R.TSC = TSC;
Dean Michael Berris8299e4b2018-07-11 07:14:27 +0000230 R.TId = GetTid();
231 R.PId = internal_getpid();
Dean Michael Berrisf50eb932016-08-26 06:39:33 +0000232 R.Type = Type;
233 R.FuncId = FuncId;
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000234 auto FirstEntry = reinterpret_cast<XRayRecord *>(TLD.InMemoryBuffer);
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000235 internal_memcpy(FirstEntry + TLD.BufferOffset, &R, sizeof(R));
Dean Michael Berris52517d72017-12-05 12:21:14 +0000236 if (++TLD.BufferOffset == TLD.BufferSize) {
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000237 SpinMutexLock L(&LogMutex);
Martin Pelikan4834bca2018-01-19 13:18:40 +0000238 retryingWriteAll(Fd, reinterpret_cast<char *>(FirstEntry),
239 reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
Dean Michael Berris52517d72017-12-05 12:21:14 +0000240 TLD.BufferOffset = 0;
241 TLD.StackEntries = 0;
Dean Michael Berrisf50eb932016-08-26 06:39:33 +0000242 }
243}
244
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000245template <class RDTSC>
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000246void InMemoryRawLogWithArg(int32_t FuncId, XRayEntryType Type, uint64_t Arg1,
247 RDTSC ReadTSC) XRAY_NEVER_INSTRUMENT {
248 auto &TLD = getThreadLocalData();
Martin Pelikan4834bca2018-01-19 13:18:40 +0000249 auto FirstEntry =
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000250 reinterpret_cast<XRayArgPayload *>(TLD.InMemoryBuffer);
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000251 const auto &BuffLen = TLD.BufferSize;
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000252 int Fd = getGlobalFd();
253 if (Fd == -1)
254 return;
255
256 // First we check whether there's enough space to write the data consecutively
257 // in the thread-local buffer. If not, we first flush the buffer before
258 // attempting to write the two records that must be consecutive.
Martin Pelikan4834bca2018-01-19 13:18:40 +0000259 if (TLD.BufferOffset + 2 > BuffLen) {
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000260 SpinMutexLock L(&LogMutex);
Martin Pelikan4834bca2018-01-19 13:18:40 +0000261 retryingWriteAll(Fd, reinterpret_cast<char *>(FirstEntry),
262 reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
263 TLD.BufferOffset = 0;
Dean Michael Berris52517d72017-12-05 12:21:14 +0000264 TLD.StackEntries = 0;
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000265 }
266
267 // Then we write the "we have an argument" record.
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000268 InMemoryRawLog(FuncId, Type, ReadTSC);
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000269
Dean Michael Berrisc5619702018-06-06 06:07:48 +0000270 RecursionGuard G(Guard);
271 if (!G)
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000272 return;
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000273
Henry Zhuebc68ff2018-07-16 14:54:29 +0000274 // And, from here on write the arg payload.
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000275 XRayArgPayload R;
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000276 R.RecordType = RecordTypes::ARG_PAYLOAD;
277 R.FuncId = FuncId;
Dean Michael Berris8299e4b2018-07-11 07:14:27 +0000278 R.TId = GetTid();
279 R.PId = internal_getpid();
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000280 R.Arg = Arg1;
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000281 internal_memcpy(FirstEntry + TLD.BufferOffset, &R, sizeof(R));
Martin Pelikan4834bca2018-01-19 13:18:40 +0000282 if (++TLD.BufferOffset == BuffLen) {
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000283 SpinMutexLock L(&LogMutex);
Martin Pelikan4834bca2018-01-19 13:18:40 +0000284 retryingWriteAll(Fd, reinterpret_cast<char *>(FirstEntry),
285 reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
286 TLD.BufferOffset = 0;
Dean Michael Berris52517d72017-12-05 12:21:14 +0000287 TLD.StackEntries = 0;
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000288 }
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000289}
290
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000291void basicLoggingHandleArg0RealTSC(int32_t FuncId,
292 XRayEntryType Type) XRAY_NEVER_INSTRUMENT {
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000293 InMemoryRawLog(FuncId, Type, readTSC);
Dean Michael Berris66443d82017-03-15 02:28:00 +0000294}
295
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000296void basicLoggingHandleArg0EmulateTSC(int32_t FuncId, XRayEntryType Type)
297 XRAY_NEVER_INSTRUMENT {
298 InMemoryRawLog(FuncId, Type, [](uint8_t &CPU) XRAY_NEVER_INSTRUMENT {
Dean Michael Berris66443d82017-03-15 02:28:00 +0000299 timespec TS;
300 int result = clock_gettime(CLOCK_REALTIME, &TS);
301 if (result != 0) {
302 Report("clock_gettimg(2) return %d, errno=%d.", result, int(errno));
303 TS = {0, 0};
304 }
305 CPU = 0;
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000306 return TS.tv_sec * NanosecondsPerSecond + TS.tv_nsec;
Dean Michael Berris66443d82017-03-15 02:28:00 +0000307 });
308}
309
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000310void basicLoggingHandleArg1RealTSC(int32_t FuncId, XRayEntryType Type,
311 uint64_t Arg1) XRAY_NEVER_INSTRUMENT {
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000312 InMemoryRawLogWithArg(FuncId, Type, Arg1, readTSC);
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000313}
314
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000315void basicLoggingHandleArg1EmulateTSC(int32_t FuncId, XRayEntryType Type,
316 uint64_t Arg1) XRAY_NEVER_INSTRUMENT {
317 InMemoryRawLogWithArg(
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000318 FuncId, Type, Arg1, [](uint8_t &CPU) XRAY_NEVER_INSTRUMENT {
319 timespec TS;
320 int result = clock_gettime(CLOCK_REALTIME, &TS);
321 if (result != 0) {
322 Report("clock_gettimg(2) return %d, errno=%d.", result, int(errno));
323 TS = {0, 0};
324 }
325 CPU = 0;
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000326 return TS.tv_sec * NanosecondsPerSecond + TS.tv_nsec;
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000327 });
328}
329
Dean Michael Berris52517d72017-12-05 12:21:14 +0000330static void TLDDestructor(void *P) XRAY_NEVER_INSTRUMENT {
331 ThreadLocalData &TLD = *reinterpret_cast<ThreadLocalData *>(P);
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000332 auto ExitGuard = at_scope_exit([&TLD] {
Dean Michael Berris52517d72017-12-05 12:21:14 +0000333 // Clean up dynamic resources.
334 if (TLD.InMemoryBuffer)
335 InternalFree(TLD.InMemoryBuffer);
336 if (TLD.ShadowStack)
337 InternalFree(TLD.ShadowStack);
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000338 if (Verbosity())
Dean Michael Berris8299e4b2018-07-11 07:14:27 +0000339 Report("Cleaned up log for TID: %d\n", GetTid());
Dean Michael Berris52517d72017-12-05 12:21:14 +0000340 });
341
342 if (TLD.Fd == -1 || TLD.BufferOffset == 0) {
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000343 if (Verbosity())
Dean Michael Berris8299e4b2018-07-11 07:14:27 +0000344 Report("Skipping buffer for TID: %d; Fd = %d; Offset = %llu\n", GetTid(),
Dean Michael Berris52517d72017-12-05 12:21:14 +0000345 TLD.Fd, TLD.BufferOffset);
346 return;
347 }
348
349 {
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000350 SpinMutexLock L(&LogMutex);
Dean Michael Berris52517d72017-12-05 12:21:14 +0000351 retryingWriteAll(TLD.Fd, reinterpret_cast<char *>(TLD.InMemoryBuffer),
352 reinterpret_cast<char *>(TLD.InMemoryBuffer) +
Dean Michael Berrisabbeb4c2018-06-05 10:12:58 +0000353 (sizeof(XRayRecord) * TLD.BufferOffset));
Dean Michael Berris52517d72017-12-05 12:21:14 +0000354 }
355
356 // Because this thread's exit could be the last one trying to write to
357 // the file and that we're not able to close out the file properly, we
358 // sync instead and hope that the pending writes are flushed as the
359 // thread exits.
360 fsync(TLD.Fd);
361}
362
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000363XRayLogInitStatus basicLoggingInit(size_t BufferSize, size_t BufferMax,
364 void *Options,
365 size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000366 uint8_t Expected = 0;
Dean Michael Berrisaf4d5832018-06-08 04:00:07 +0000367 if (!atomic_compare_exchange_strong(&BasicInitialized, &Expected, 1,
368 memory_order_acq_rel)) {
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000369 if (Verbosity())
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000370 Report("Basic logging already initialized.\n");
371 return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
Dean Michael Berris8dcba552017-10-05 05:45:51 +0000372 }
373
Dean Michael Berrisaf4d5832018-06-08 04:00:07 +0000374 static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
375 pthread_once(&OnceInit, +[] {
Dean Michael Berris47e6b8c2018-05-04 06:27:53 +0000376 pthread_key_create(&PThreadKey, TLDDestructor);
Dean Michael Berrisaf4d5832018-06-08 04:00:07 +0000377 atomic_store(&UseRealTSC, probeRequiredCPUFeatures(), memory_order_release);
378 // Initialize the global TicksPerSec value.
379 atomic_store(&TicksPerSec,
380 probeRequiredCPUFeatures() ? getTSCFrequency()
381 : NanosecondsPerSecond,
382 memory_order_release);
383 if (!atomic_load(&UseRealTSC, memory_order_relaxed) && Verbosity())
384 Report("WARNING: Required CPU features missing for XRay instrumentation, "
385 "using emulation instead.\n");
386 });
Dean Michael Berris47e6b8c2018-05-04 06:27:53 +0000387
388 if (BufferSize == 0 && BufferMax == 0 && Options != nullptr) {
389 FlagParser P;
390 BasicFlags F;
391 F.setDefaults();
392 registerXRayBasicFlags(&P, &F);
393 P.ParseString(useCompilerDefinedBasicFlags());
394 auto *EnvOpts = GetEnv("XRAY_BASIC_OPTIONS");
395 if (EnvOpts == nullptr)
396 EnvOpts = "";
397
398 P.ParseString(EnvOpts);
399
400 // If XRAY_BASIC_OPTIONS was not defined, then we use the deprecated options
401 // set through XRAY_OPTIONS instead.
402 if (internal_strlen(EnvOpts) == 0) {
403 F.func_duration_threshold_us =
404 flags()->xray_naive_log_func_duration_threshold_us;
405 F.max_stack_depth = flags()->xray_naive_log_max_stack_depth;
406 F.thread_buffer_size = flags()->xray_naive_log_thread_buffer_size;
407 }
408
409 P.ParseString(static_cast<const char *>(Options));
410 GlobalOptions.ThreadBufferSize = F.thread_buffer_size;
411 GlobalOptions.DurationFilterMicros = F.func_duration_threshold_us;
412 GlobalOptions.MaxStackDepth = F.max_stack_depth;
Dean Michael Berrisaf4d5832018-06-08 04:00:07 +0000413 *basicFlags() = F;
Dean Michael Berris47e6b8c2018-05-04 06:27:53 +0000414 } else if (OptionsSize != sizeof(BasicLoggingOptions)) {
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000415 Report("Invalid options size, potential ABI mismatch; expected %d got %d",
416 sizeof(BasicLoggingOptions), OptionsSize);
417 return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
Dean Michael Berris47e6b8c2018-05-04 06:27:53 +0000418 } else {
419 if (Verbosity())
420 Report("XRay Basic: struct-based init is deprecated, please use "
421 "string-based configuration instead.\n");
422 GlobalOptions = *reinterpret_cast<BasicLoggingOptions *>(Options);
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000423 }
424
Dean Michael Berrisaf4d5832018-06-08 04:00:07 +0000425 atomic_store(&ThresholdTicks,
426 atomic_load(&TicksPerSec, memory_order_acquire) *
427 GlobalOptions.DurationFilterMicros / 1000000,
428 memory_order_release);
429 __xray_set_handler_arg1(atomic_load(&UseRealTSC, memory_order_acquire)
430 ? basicLoggingHandleArg1RealTSC
431 : basicLoggingHandleArg1EmulateTSC);
432 __xray_set_handler(atomic_load(&UseRealTSC, memory_order_acquire)
433 ? basicLoggingHandleArg0RealTSC
434 : basicLoggingHandleArg0EmulateTSC);
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000435
Dean Michael Berrisaf4d5832018-06-08 04:00:07 +0000436 // TODO: Implement custom event and typed event handling support in Basic
437 // Mode.
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000438 __xray_remove_customevent_handler();
Keith Wyssadb092e2018-04-17 21:28:53 +0000439 __xray_remove_typedevent_handler();
Dean Michael Berris52517d72017-12-05 12:21:14 +0000440
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000441 return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
442}
443
444XRayLogInitStatus basicLoggingFinalize() XRAY_NEVER_INSTRUMENT {
445 uint8_t Expected = 0;
Dean Michael Berrisaf4d5832018-06-08 04:00:07 +0000446 if (!atomic_compare_exchange_strong(&BasicInitialized, &Expected, 0,
447 memory_order_acq_rel) &&
Dean Michael Berris5eaaff62018-06-05 06:12:42 +0000448 Verbosity())
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000449 Report("Basic logging already finalized.\n");
450
451 // Nothing really to do aside from marking state of the global to be
452 // uninitialized.
453
454 return XRayLogInitStatus::XRAY_LOG_FINALIZED;
455}
456
457XRayLogFlushStatus basicLoggingFlush() XRAY_NEVER_INSTRUMENT {
458 // This really does nothing, since flushing the logs happen at the end of a
459 // thread's lifetime, or when the buffers are full.
460 return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
461}
462
463// This is a handler that, effectively, does nothing.
464void basicLoggingHandleArg0Empty(int32_t, XRayEntryType) XRAY_NEVER_INSTRUMENT {
465}
466
467bool basicLogDynamicInitializer() XRAY_NEVER_INSTRUMENT {
Dean Michael Berrisc360f412017-12-05 12:08:56 +0000468 XRayLogImpl Impl{
469 basicLoggingInit,
470 basicLoggingFinalize,
471 basicLoggingHandleArg0Empty,
472 basicLoggingFlush,
473 };
474 auto RegistrationResult = __xray_log_register_mode("xray-basic", Impl);
475 if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK &&
Dean Michael Berris47e6b8c2018-05-04 06:27:53 +0000476 Verbosity())
Dean Michael Berrisc360f412017-12-05 12:08:56 +0000477 Report("Cannot register XRay Basic Mode to 'xray-basic'; error = %d\n",
478 RegistrationResult);
479 if (flags()->xray_naive_log ||
Dean Michael Berris47e6b8c2018-05-04 06:27:53 +0000480 !internal_strcmp(flags()->xray_mode, "xray-basic")) {
481 auto SelectResult = __xray_log_select_mode("xray-basic");
482 if (SelectResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK) {
483 if (Verbosity())
484 Report("Failed selecting XRay Basic Mode; error = %d\n", SelectResult);
485 return false;
486 }
487
488 // We initialize the implementation using the data we get from the
489 // XRAY_BASIC_OPTIONS environment variable, at this point of the
490 // implementation.
491 auto *Env = GetEnv("XRAY_BASIC_OPTIONS");
492 auto InitResult =
493 __xray_log_init_mode("xray-basic", Env == nullptr ? "" : Env);
494 if (InitResult != XRayLogInitStatus::XRAY_LOG_INITIALIZED) {
495 if (Verbosity())
496 Report("Failed initializing XRay Basic Mode; error = %d\n", InitResult);
497 return false;
498 }
Dean Michael Berrisaf4d5832018-06-08 04:00:07 +0000499
500 // At this point we know that we've successfully initialized Basic mode
501 // tracing, and the only chance we're going to get for the current thread to
502 // clean-up may be at thread/program exit. To ensure that we're going to get
503 // the cleanup even without calling the finalization routines, we're
504 // registering a program exit function that will do the cleanup.
505 static pthread_once_t DynamicOnce = PTHREAD_ONCE_INIT;
506 pthread_once(&DynamicOnce, +[] {
507 static void *FakeTLD = nullptr;
508 FakeTLD = &getThreadLocalData();
509 Atexit(+[] { TLDDestructor(FakeTLD); });
510 });
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000511 }
Dean Michael Berrisf50eb932016-08-26 06:39:33 +0000512 return true;
Dean Michael Berris364f11c2017-11-21 07:29:21 +0000513}
514
515} // namespace __xray
516
517static auto UNUSED Unused = __xray::basicLogDynamicInitializer();