blob: 3a0c0b90e9a4b8bfa2970d533c713818c281ebd4 [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{};
60
61static ProfilingData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +000062 if (pthread_getspecific(ProfilingKey) == NULL) {
63 new (&ThreadStorage) ProfilingData{};
64 pthread_setspecific(ProfilingKey, &ThreadStorage);
65 }
66
67 auto &TLD = *reinterpret_cast<ProfilingData *>(&ThreadStorage);
68
69 // We need to check whether the global flag to finalizing/finalized has been
70 // switched. If it is, then we ought to not actually initialise the data.
71 auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
72 if (Status == XRayLogInitStatus::XRAY_LOG_FINALIZING ||
73 Status == XRayLogInitStatus::XRAY_LOG_FINALIZED)
74 return TLD;
75
76 // If we're live, then we re-initialize TLD if the pointers are not null.
77 if (UNLIKELY(TLD.Allocators == nullptr && TLD.FCT == nullptr)) {
78 TLD.Allocators = reinterpret_cast<FunctionCallTrie::Allocators *>(
79 InternalAlloc(sizeof(FunctionCallTrie::Allocators)));
80 new (TLD.Allocators) FunctionCallTrie::Allocators();
81 *TLD.Allocators = FunctionCallTrie::InitAllocators();
82 TLD.FCT = reinterpret_cast<FunctionCallTrie *>(
83 InternalAlloc(sizeof(FunctionCallTrie)));
84 new (TLD.FCT) FunctionCallTrie(*TLD.Allocators);
85 }
86
87 return TLD;
88}
89
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +000090static void cleanupTLD() XRAY_NEVER_INSTRUMENT {
91 auto &TLD = *reinterpret_cast<ProfilingData *>(&ThreadStorage);
92 if (TLD.Allocators != nullptr && TLD.FCT != nullptr) {
93 TLD.FCT->~FunctionCallTrie();
94 TLD.Allocators->~Allocators();
95 InternalFree(TLD.FCT);
96 InternalFree(TLD.Allocators);
97 TLD.FCT = nullptr;
98 TLD.Allocators = nullptr;
99 }
100}
101
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000102} // namespace
103
104const char *profilingCompilerDefinedFlags() XRAY_NEVER_INSTRUMENT {
105#ifdef XRAY_PROFILER_DEFAULT_OPTIONS
106 return SANITIZER_STRINGIFY(XRAY_PROFILER_DEFAULT_OPTIONS);
107#else
108 return "";
109#endif
110}
111
112atomic_sint32_t ProfileFlushStatus = {
113 XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
114
115XRayLogFlushStatus profilingFlush() XRAY_NEVER_INSTRUMENT {
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000116 if (atomic_load(&ProfilerLogStatus, memory_order_acquire) !=
117 XRayLogInitStatus::XRAY_LOG_FINALIZED) {
118 if (Verbosity())
119 Report("Not flushing profiles, profiling not been finalized.\n");
120 return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
121 }
122
123 s32 Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
124 if (!atomic_compare_exchange_strong(&ProfilerLogFlushStatus, &Result,
125 XRayLogFlushStatus::XRAY_LOG_FLUSHING,
126 memory_order_acq_rel)) {
127 if (Verbosity())
128 Report("Not flushing profiles, implementation still finalizing.\n");
129 }
130
Dean Michael Berrisaea8ece2018-06-12 04:06:25 +0000131 // At this point, we'll create the file that will contain the profile, but
132 // only if the options say so.
133 if (!profilingFlags()->no_flush) {
134 int Fd = -1;
135 Fd = getLogFD();
136 if (Fd == -1) {
137 if (__sanitizer::Verbosity())
138 Report(
139 "profiler: Failed to acquire a file descriptor, dropping data.\n");
140 } else {
141 XRayProfilingFileHeader Header;
142 Header.Timestamp = NanoTime();
143 Header.PID = internal_getpid();
144 retryingWriteAll(Fd, reinterpret_cast<const char *>(&Header),
145 reinterpret_cast<const char *>(&Header) +
146 sizeof(Header));
147
148 // Now for each of the threads, write out the profile data as we would see
149 // it in memory, verbatim.
150 XRayBuffer B = profileCollectorService::nextBuffer({nullptr, 0});
151 while (B.Data != nullptr && B.Size != 0) {
152 retryingWriteAll(Fd, reinterpret_cast<const char *>(B.Data),
153 reinterpret_cast<const char *>(B.Data) + B.Size);
154 B = profileCollectorService::nextBuffer(B);
155 }
156
157 // Then we close out the file.
158 internal_close(Fd);
159 }
160 }
161
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000162 profileCollectorService::reset();
163
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000164 // Flush the current thread's local data structures as well.
165 cleanupTLD();
166
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000167 atomic_store(&ProfilerLogStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED,
168 memory_order_release);
169
170 return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
171}
172
173namespace {
174
175thread_local atomic_uint8_t ReentranceGuard{0};
176
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000177static void postCurrentThreadFCT(ProfilingData &TLD) {
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000178 if (TLD.Allocators == nullptr || TLD.FCT == nullptr)
179 return;
180
181 profileCollectorService::post(*TLD.FCT, GetTid());
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000182 cleanupTLD();
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000183}
184
185} // namespace
186
187void profilingHandleArg0(int32_t FuncId,
188 XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {
189 unsigned char CPU;
190 auto TSC = readTSC(CPU);
191 RecursionGuard G(ReentranceGuard);
192 if (!G)
193 return;
194
195 auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
196 auto &TLD = getThreadLocalData();
197 if (UNLIKELY(Status == XRayLogInitStatus::XRAY_LOG_FINALIZED ||
198 Status == XRayLogInitStatus::XRAY_LOG_FINALIZING)) {
199 postCurrentThreadFCT(TLD);
200 return;
201 }
202
203 switch (Entry) {
204 case XRayEntryType::ENTRY:
205 case XRayEntryType::LOG_ARGS_ENTRY:
206 TLD.FCT->enterFunction(FuncId, TSC);
207 break;
208 case XRayEntryType::EXIT:
209 case XRayEntryType::TAIL:
210 TLD.FCT->exitFunction(FuncId, TSC);
211 break;
212 default:
213 // FIXME: Handle bugs.
214 break;
215 }
216}
217
218void profilingHandleArg1(int32_t FuncId, XRayEntryType Entry,
219 uint64_t) XRAY_NEVER_INSTRUMENT {
220 return profilingHandleArg0(FuncId, Entry);
221}
222
223XRayLogInitStatus profilingFinalize() XRAY_NEVER_INSTRUMENT {
224 s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED;
225 if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
226 XRayLogInitStatus::XRAY_LOG_FINALIZING,
227 memory_order_release)) {
228 if (Verbosity())
229 Report("Cannot finalize profile, the profiling is not initialized.\n");
230 return static_cast<XRayLogInitStatus>(CurrentStatus);
231 }
232
233 // Wait a grace period to allow threads to see that we're finalizing.
234 SleepForMillis(profilingFlags()->grace_period_ms);
235
236 // We also want to make sure that the current thread's data is cleaned up,
237 // if we have any.
238 auto &TLD = getThreadLocalData();
239 postCurrentThreadFCT(TLD);
240
241 // Then we force serialize the log data.
242 profileCollectorService::serialize();
243
244 atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_FINALIZED,
245 memory_order_release);
246 return XRayLogInitStatus::XRAY_LOG_FINALIZED;
247}
248
249XRayLogInitStatus
250profilingLoggingInit(size_t BufferSize, size_t BufferMax, void *Options,
251 size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
252 if (BufferSize != 0 || BufferMax != 0) {
253 if (Verbosity())
254 Report("__xray_log_init() being used, and is unsupported. Use "
255 "__xray_log_init_mode(...) instead. Bailing out.");
256 return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
257 }
258
259 s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
260 if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
261 XRayLogInitStatus::XRAY_LOG_INITIALIZING,
262 memory_order_release)) {
263 if (Verbosity())
264 Report("Cannot initialize already initialised profiling "
265 "implementation.\n");
266 return static_cast<XRayLogInitStatus>(CurrentStatus);
267 }
268
269 {
270 SpinMutexLock Lock(&ProfilerOptionsMutex);
271 FlagParser ConfigParser;
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000272 ProfilerFlags Flags;
273 Flags.setDefaults();
274 registerProfilerFlags(&ConfigParser, &Flags);
Dean Michael Berrisaea8ece2018-06-12 04:06:25 +0000275 ConfigParser.ParseString(profilingCompilerDefinedFlags());
276 const char *Env = GetEnv("XRAY_PROFILING_OPTIONS");
277 if (Env == nullptr)
278 Env = "";
279 ConfigParser.ParseString(Env);
280
281 // Then parse the configuration string provided.
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000282 ConfigParser.ParseString(static_cast<const char *>(Options));
283 if (Verbosity())
284 ReportUnrecognizedFlags();
Dean Michael Berris0dd4f9f2018-07-10 08:25:44 +0000285 *profilingFlags() = Flags;
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000286 }
287
288 // We need to reset the profile data collection implementation now.
289 profileCollectorService::reset();
290
Dean Michael Berris5d92d3e2018-07-13 04:04:18 +0000291 // We need to set up the exit handlers.
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000292 static pthread_once_t Once = PTHREAD_ONCE_INIT;
293 pthread_once(&Once, +[] {
294 pthread_key_create(&ProfilingKey, +[](void *P) {
295 // This is the thread-exit handler.
296 auto &TLD = *reinterpret_cast<ProfilingData *>(P);
297 if (TLD.Allocators == nullptr && TLD.FCT == nullptr)
298 return;
299
300 postCurrentThreadFCT(TLD);
301 });
Dean Michael Berris5d92d3e2018-07-13 04:04:18 +0000302
303 // We also need to set up an exit handler, so that we can get the profile
304 // information at exit time. We use the C API to do this, to not rely on C++
305 // ABI functions for registering exit handlers.
306 Atexit(+[] {
307 // Finalize and flush.
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000308 if (profilingFinalize() != XRAY_LOG_FINALIZED) {
309 cleanupTLD();
Dean Michael Berris5d92d3e2018-07-13 04:04:18 +0000310 return;
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000311 }
312 if (profilingFlush() != XRAY_LOG_FLUSHED) {
313 cleanupTLD();
Dean Michael Berris5d92d3e2018-07-13 04:04:18 +0000314 return;
Dean Michael Berris9d6b7a52018-07-18 01:53:39 +0000315 }
Dean Michael Berris5d92d3e2018-07-13 04:04:18 +0000316 if (Verbosity())
317 Report("XRay Profile flushed at exit.");
318 });
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000319 });
320
321 __xray_log_set_buffer_iterator(profileCollectorService::nextBuffer);
322 __xray_set_handler(profilingHandleArg0);
323 __xray_set_handler_arg1(profilingHandleArg1);
324
325 atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZED,
326 memory_order_release);
327 if (Verbosity())
328 Report("XRay Profiling init successful.\n");
329
330 return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
331}
332
333bool profilingDynamicInitializer() XRAY_NEVER_INSTRUMENT {
334 // Set up the flag defaults from the static defaults and the
335 // compiler-provided defaults.
336 {
337 SpinMutexLock Lock(&ProfilerOptionsMutex);
338 auto *F = profilingFlags();
339 F->setDefaults();
340 FlagParser ProfilingParser;
341 registerProfilerFlags(&ProfilingParser, F);
Dean Michael Berrisaea8ece2018-06-12 04:06:25 +0000342 ProfilingParser.ParseString(profilingCompilerDefinedFlags());
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000343 }
344
345 XRayLogImpl Impl{
346 profilingLoggingInit,
347 profilingFinalize,
348 profilingHandleArg0,
349 profilingFlush,
350 };
351 auto RegistrationResult = __xray_log_register_mode("xray-profiling", Impl);
Dean Michael Berris5d92d3e2018-07-13 04:04:18 +0000352 if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK) {
353 if (Verbosity())
354 Report("Cannot register XRay Profiling mode to 'xray-profiling'; error = "
355 "%d\n",
356 RegistrationResult);
357 return false;
358 }
359
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000360 if (!internal_strcmp(flags()->xray_mode, "xray-profiling"))
Dean Michael Berris5d92d3e2018-07-13 04:04:18 +0000361 __xray_log_select_mode("xray_profiling");
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000362 return true;
363}
364
365} // namespace __xray
366
367static auto UNUSED Unused = __xray::profilingDynamicInitializer();