blob: a11fddcc2c9999ab3e799523548e5f15b99db7f3 [file] [log] [blame]
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +00001//===-- xray_profiling.cc ---------------------------------------*- C++ -*-===//
2//
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// This is the implementation of a profiling handler.
13//
14//===----------------------------------------------------------------------===//
15#include <memory>
Dean Michael Berrisaea8ece2018-06-12 04:06:25 +000016#include <time.h>
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +000017
18#include "sanitizer_common/sanitizer_atomic.h"
19#include "sanitizer_common/sanitizer_flags.h"
20#include "xray/xray_interface.h"
21#include "xray/xray_log_interface.h"
22
23#include "xray_flags.h"
24#include "xray_profile_collector.h"
25#include "xray_profiling_flags.h"
26#include "xray_recursion_guard.h"
27#include "xray_tsc.h"
28#include "xray_utils.h"
29#include <pthread.h>
30
31namespace __xray {
32
33namespace {
34
Dean Michael Berrisaea8ece2018-06-12 04:06:25 +000035constexpr uptr XRayProfilingVersion = 0x20180424;
36
37struct XRayProfilingFileHeader {
38 const u64 MagicBytes = 0x7872617970726f66; // Identifier for XRay profiling
39 // files 'xrayprof' in hex.
40 const uptr Version = XRayProfilingVersion;
41 uptr Timestamp = 0; // System time in nanoseconds.
42 uptr PID = 0; // Process ID.
43};
44
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +000045atomic_sint32_t ProfilerLogFlushStatus = {
46 XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
47
48atomic_sint32_t ProfilerLogStatus = {XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};
49
50SpinMutex ProfilerOptionsMutex;
51
52struct alignas(64) ProfilingData {
53 FunctionCallTrie::Allocators *Allocators = nullptr;
54 FunctionCallTrie *FCT = nullptr;
55};
56
57static pthread_key_t ProfilingKey;
58
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +000059thread_local std::aligned_storage<sizeof(ProfilingData)>::type ThreadStorage{};
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +000060static ProfilingData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
Dean Michael Berris4719c522018-07-18 02:08:39 +000061 thread_local auto ThreadOnce = [] {
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +000062 new (&ThreadStorage) ProfilingData{};
63 pthread_setspecific(ProfilingKey, &ThreadStorage);
Dean Michael Berris4719c522018-07-18 02:08:39 +000064 return false;
65 }();
66 (void)ThreadOnce;
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +000067
68 auto &TLD = *reinterpret_cast<ProfilingData *>(&ThreadStorage);
69
70 // We need to check whether the global flag to finalizing/finalized has been
71 // switched. If it is, then we ought to not actually initialise the data.
72 auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
73 if (Status == XRayLogInitStatus::XRAY_LOG_FINALIZING ||
74 Status == XRayLogInitStatus::XRAY_LOG_FINALIZED)
75 return TLD;
76
77 // If we're live, then we re-initialize TLD if the pointers are not null.
78 if (UNLIKELY(TLD.Allocators == nullptr && TLD.FCT == nullptr)) {
79 TLD.Allocators = reinterpret_cast<FunctionCallTrie::Allocators *>(
80 InternalAlloc(sizeof(FunctionCallTrie::Allocators)));
81 new (TLD.Allocators) FunctionCallTrie::Allocators();
82 *TLD.Allocators = FunctionCallTrie::InitAllocators();
83 TLD.FCT = reinterpret_cast<FunctionCallTrie *>(
84 InternalAlloc(sizeof(FunctionCallTrie)));
85 new (TLD.FCT) FunctionCallTrie(*TLD.Allocators);
86 }
87
88 return TLD;
89}
90
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +000091static void cleanupTLD() XRAY_NEVER_INSTRUMENT {
92 auto &TLD = *reinterpret_cast<ProfilingData *>(&ThreadStorage);
93 if (TLD.Allocators != nullptr && TLD.FCT != nullptr) {
94 TLD.FCT->~FunctionCallTrie();
95 TLD.Allocators->~Allocators();
96 InternalFree(TLD.FCT);
97 InternalFree(TLD.Allocators);
98 TLD.FCT = nullptr;
99 TLD.Allocators = nullptr;
100 }
101}
102
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000103} // namespace
104
105const char *profilingCompilerDefinedFlags() XRAY_NEVER_INSTRUMENT {
106#ifdef XRAY_PROFILER_DEFAULT_OPTIONS
107 return SANITIZER_STRINGIFY(XRAY_PROFILER_DEFAULT_OPTIONS);
108#else
109 return "";
110#endif
111}
112
113atomic_sint32_t ProfileFlushStatus = {
114 XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
115
116XRayLogFlushStatus profilingFlush() XRAY_NEVER_INSTRUMENT {
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000117 if (atomic_load(&ProfilerLogStatus, memory_order_acquire) !=
118 XRayLogInitStatus::XRAY_LOG_FINALIZED) {
119 if (Verbosity())
120 Report("Not flushing profiles, profiling not been finalized.\n");
121 return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
122 }
123
124 s32 Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
125 if (!atomic_compare_exchange_strong(&ProfilerLogFlushStatus, &Result,
126 XRayLogFlushStatus::XRAY_LOG_FLUSHING,
127 memory_order_acq_rel)) {
128 if (Verbosity())
129 Report("Not flushing profiles, implementation still finalizing.\n");
130 }
131
Dean Michael Berrisaea8ece2018-06-12 04:06:25 +0000132 // At this point, we'll create the file that will contain the profile, but
133 // only if the options say so.
134 if (!profilingFlags()->no_flush) {
135 int Fd = -1;
136 Fd = getLogFD();
137 if (Fd == -1) {
138 if (__sanitizer::Verbosity())
139 Report(
140 "profiler: Failed to acquire a file descriptor, dropping data.\n");
141 } else {
142 XRayProfilingFileHeader Header;
143 Header.Timestamp = NanoTime();
144 Header.PID = internal_getpid();
145 retryingWriteAll(Fd, reinterpret_cast<const char *>(&Header),
146 reinterpret_cast<const char *>(&Header) +
147 sizeof(Header));
148
149 // Now for each of the threads, write out the profile data as we would see
150 // it in memory, verbatim.
151 XRayBuffer B = profileCollectorService::nextBuffer({nullptr, 0});
152 while (B.Data != nullptr && B.Size != 0) {
153 retryingWriteAll(Fd, reinterpret_cast<const char *>(B.Data),
154 reinterpret_cast<const char *>(B.Data) + B.Size);
155 B = profileCollectorService::nextBuffer(B);
156 }
157
158 // Then we close out the file.
159 internal_close(Fd);
160 }
161 }
162
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000163 profileCollectorService::reset();
164
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000165 // Flush the current thread's local data structures as well.
166 cleanupTLD();
167
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000168 atomic_store(&ProfilerLogStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED,
169 memory_order_release);
170
171 return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
172}
173
174namespace {
175
176thread_local atomic_uint8_t ReentranceGuard{0};
177
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000178static void postCurrentThreadFCT(ProfilingData &TLD) {
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000179 if (TLD.Allocators == nullptr || TLD.FCT == nullptr)
180 return;
181
182 profileCollectorService::post(*TLD.FCT, GetTid());
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000183 cleanupTLD();
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000184}
185
186} // namespace
187
188void profilingHandleArg0(int32_t FuncId,
189 XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {
190 unsigned char CPU;
191 auto TSC = readTSC(CPU);
192 RecursionGuard G(ReentranceGuard);
193 if (!G)
194 return;
195
196 auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
197 auto &TLD = getThreadLocalData();
198 if (UNLIKELY(Status == XRayLogInitStatus::XRAY_LOG_FINALIZED ||
199 Status == XRayLogInitStatus::XRAY_LOG_FINALIZING)) {
200 postCurrentThreadFCT(TLD);
201 return;
202 }
203
204 switch (Entry) {
205 case XRayEntryType::ENTRY:
206 case XRayEntryType::LOG_ARGS_ENTRY:
207 TLD.FCT->enterFunction(FuncId, TSC);
208 break;
209 case XRayEntryType::EXIT:
210 case XRayEntryType::TAIL:
211 TLD.FCT->exitFunction(FuncId, TSC);
212 break;
213 default:
214 // FIXME: Handle bugs.
215 break;
216 }
217}
218
219void profilingHandleArg1(int32_t FuncId, XRayEntryType Entry,
220 uint64_t) XRAY_NEVER_INSTRUMENT {
221 return profilingHandleArg0(FuncId, Entry);
222}
223
224XRayLogInitStatus profilingFinalize() XRAY_NEVER_INSTRUMENT {
225 s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED;
226 if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
227 XRayLogInitStatus::XRAY_LOG_FINALIZING,
228 memory_order_release)) {
229 if (Verbosity())
230 Report("Cannot finalize profile, the profiling is not initialized.\n");
231 return static_cast<XRayLogInitStatus>(CurrentStatus);
232 }
233
234 // Wait a grace period to allow threads to see that we're finalizing.
235 SleepForMillis(profilingFlags()->grace_period_ms);
236
237 // We also want to make sure that the current thread's data is cleaned up,
238 // if we have any.
239 auto &TLD = getThreadLocalData();
240 postCurrentThreadFCT(TLD);
241
242 // Then we force serialize the log data.
243 profileCollectorService::serialize();
244
245 atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_FINALIZED,
246 memory_order_release);
247 return XRayLogInitStatus::XRAY_LOG_FINALIZED;
248}
249
250XRayLogInitStatus
251profilingLoggingInit(size_t BufferSize, size_t BufferMax, void *Options,
252 size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
253 if (BufferSize != 0 || BufferMax != 0) {
254 if (Verbosity())
255 Report("__xray_log_init() being used, and is unsupported. Use "
256 "__xray_log_init_mode(...) instead. Bailing out.");
257 return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
258 }
259
260 s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
261 if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
262 XRayLogInitStatus::XRAY_LOG_INITIALIZING,
263 memory_order_release)) {
264 if (Verbosity())
265 Report("Cannot initialize already initialised profiling "
266 "implementation.\n");
267 return static_cast<XRayLogInitStatus>(CurrentStatus);
268 }
269
270 {
271 SpinMutexLock Lock(&ProfilerOptionsMutex);
272 FlagParser ConfigParser;
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000273 ProfilerFlags Flags;
274 Flags.setDefaults();
275 registerProfilerFlags(&ConfigParser, &Flags);
Dean Michael Berrisaea8ece2018-06-12 04:06:25 +0000276 ConfigParser.ParseString(profilingCompilerDefinedFlags());
277 const char *Env = GetEnv("XRAY_PROFILING_OPTIONS");
278 if (Env == nullptr)
279 Env = "";
280 ConfigParser.ParseString(Env);
281
282 // Then parse the configuration string provided.
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000283 ConfigParser.ParseString(static_cast<const char *>(Options));
284 if (Verbosity())
285 ReportUnrecognizedFlags();
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000286 *profilingFlags() = Flags;
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000287 }
288
289 // We need to reset the profile data collection implementation now.
290 profileCollectorService::reset();
291
Dean Michael Berris5d92d3e2018-07-13 04:04:18 +0000292 // We need to set up the exit handlers.
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000293 static pthread_once_t Once = PTHREAD_ONCE_INIT;
294 pthread_once(&Once, +[] {
295 pthread_key_create(&ProfilingKey, +[](void *P) {
296 // This is the thread-exit handler.
297 auto &TLD = *reinterpret_cast<ProfilingData *>(P);
298 if (TLD.Allocators == nullptr && TLD.FCT == nullptr)
299 return;
300
301 postCurrentThreadFCT(TLD);
302 });
Dean Michael Berris5d92d3e2018-07-13 04:04:18 +0000303
304 // We also need to set up an exit handler, so that we can get the profile
305 // information at exit time. We use the C API to do this, to not rely on C++
306 // ABI functions for registering exit handlers.
307 Atexit(+[] {
308 // Finalize and flush.
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000309 if (profilingFinalize() != XRAY_LOG_FINALIZED) {
310 cleanupTLD();
Dean Michael Berris5d92d3e2018-07-13 04:04:18 +0000311 return;
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000312 }
313 if (profilingFlush() != XRAY_LOG_FLUSHED) {
314 cleanupTLD();
Dean Michael Berris5d92d3e2018-07-13 04:04:18 +0000315 return;
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000316 }
Dean Michael Berris5d92d3e2018-07-13 04:04:18 +0000317 if (Verbosity())
318 Report("XRay Profile flushed at exit.");
319 });
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000320 });
321
322 __xray_log_set_buffer_iterator(profileCollectorService::nextBuffer);
323 __xray_set_handler(profilingHandleArg0);
324 __xray_set_handler_arg1(profilingHandleArg1);
325
326 atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZED,
327 memory_order_release);
328 if (Verbosity())
329 Report("XRay Profiling init successful.\n");
330
331 return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
332}
333
334bool profilingDynamicInitializer() XRAY_NEVER_INSTRUMENT {
335 // Set up the flag defaults from the static defaults and the
336 // compiler-provided defaults.
337 {
338 SpinMutexLock Lock(&ProfilerOptionsMutex);
339 auto *F = profilingFlags();
340 F->setDefaults();
341 FlagParser ProfilingParser;
342 registerProfilerFlags(&ProfilingParser, F);
Dean Michael Berrisaea8ece2018-06-12 04:06:25 +0000343 ProfilingParser.ParseString(profilingCompilerDefinedFlags());
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000344 }
345
346 XRayLogImpl Impl{
347 profilingLoggingInit,
348 profilingFinalize,
349 profilingHandleArg0,
350 profilingFlush,
351 };
352 auto RegistrationResult = __xray_log_register_mode("xray-profiling", Impl);
Dean Michael Berris5d92d3e2018-07-13 04:04:18 +0000353 if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK) {
354 if (Verbosity())
355 Report("Cannot register XRay Profiling mode to 'xray-profiling'; error = "
356 "%d\n",
357 RegistrationResult);
358 return false;
359 }
360
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000361 if (!internal_strcmp(flags()->xray_mode, "xray-profiling"))
Dean Michael Berris5d92d3e2018-07-13 04:04:18 +0000362 __xray_log_select_mode("xray_profiling");
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000363 return true;
364}
365
366} // namespace __xray
367
368static auto UNUSED Unused = __xray::profilingDynamicInitializer();