blob: 9a4dd90c60177c846c5af83cd3ea492812ffe5e4 [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
59ProfilingData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
60 thread_local std::aligned_storage<sizeof(ProfilingData)>::type ThreadStorage;
61 if (pthread_getspecific(ProfilingKey) == NULL) {
62 new (&ThreadStorage) ProfilingData{};
63 pthread_setspecific(ProfilingKey, &ThreadStorage);
64 }
65
66 auto &TLD = *reinterpret_cast<ProfilingData *>(&ThreadStorage);
67
68 // We need to check whether the global flag to finalizing/finalized has been
69 // switched. If it is, then we ought to not actually initialise the data.
70 auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
71 if (Status == XRayLogInitStatus::XRAY_LOG_FINALIZING ||
72 Status == XRayLogInitStatus::XRAY_LOG_FINALIZED)
73 return TLD;
74
75 // If we're live, then we re-initialize TLD if the pointers are not null.
76 if (UNLIKELY(TLD.Allocators == nullptr && TLD.FCT == nullptr)) {
77 TLD.Allocators = reinterpret_cast<FunctionCallTrie::Allocators *>(
78 InternalAlloc(sizeof(FunctionCallTrie::Allocators)));
79 new (TLD.Allocators) FunctionCallTrie::Allocators();
80 *TLD.Allocators = FunctionCallTrie::InitAllocators();
81 TLD.FCT = reinterpret_cast<FunctionCallTrie *>(
82 InternalAlloc(sizeof(FunctionCallTrie)));
83 new (TLD.FCT) FunctionCallTrie(*TLD.Allocators);
84 }
85
86 return TLD;
87}
88
89} // namespace
90
91const char *profilingCompilerDefinedFlags() XRAY_NEVER_INSTRUMENT {
92#ifdef XRAY_PROFILER_DEFAULT_OPTIONS
93 return SANITIZER_STRINGIFY(XRAY_PROFILER_DEFAULT_OPTIONS);
94#else
95 return "";
96#endif
97}
98
99atomic_sint32_t ProfileFlushStatus = {
100 XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
101
102XRayLogFlushStatus profilingFlush() XRAY_NEVER_INSTRUMENT {
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000103 if (atomic_load(&ProfilerLogStatus, memory_order_acquire) !=
104 XRayLogInitStatus::XRAY_LOG_FINALIZED) {
105 if (Verbosity())
106 Report("Not flushing profiles, profiling not been finalized.\n");
107 return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
108 }
109
110 s32 Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
111 if (!atomic_compare_exchange_strong(&ProfilerLogFlushStatus, &Result,
112 XRayLogFlushStatus::XRAY_LOG_FLUSHING,
113 memory_order_acq_rel)) {
114 if (Verbosity())
115 Report("Not flushing profiles, implementation still finalizing.\n");
116 }
117
Dean Michael Berrisaea8ece2018-06-12 04:06:25 +0000118 // At this point, we'll create the file that will contain the profile, but
119 // only if the options say so.
120 if (!profilingFlags()->no_flush) {
121 int Fd = -1;
122 Fd = getLogFD();
123 if (Fd == -1) {
124 if (__sanitizer::Verbosity())
125 Report(
126 "profiler: Failed to acquire a file descriptor, dropping data.\n");
127 } else {
128 XRayProfilingFileHeader Header;
129 Header.Timestamp = NanoTime();
130 Header.PID = internal_getpid();
131 retryingWriteAll(Fd, reinterpret_cast<const char *>(&Header),
132 reinterpret_cast<const char *>(&Header) +
133 sizeof(Header));
134
135 // Now for each of the threads, write out the profile data as we would see
136 // it in memory, verbatim.
137 XRayBuffer B = profileCollectorService::nextBuffer({nullptr, 0});
138 while (B.Data != nullptr && B.Size != 0) {
139 retryingWriteAll(Fd, reinterpret_cast<const char *>(B.Data),
140 reinterpret_cast<const char *>(B.Data) + B.Size);
141 B = profileCollectorService::nextBuffer(B);
142 }
143
144 // Then we close out the file.
145 internal_close(Fd);
146 }
147 }
148
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000149 profileCollectorService::reset();
150
151 atomic_store(&ProfilerLogStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED,
152 memory_order_release);
153
154 return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
155}
156
157namespace {
158
159thread_local atomic_uint8_t ReentranceGuard{0};
160
161void postCurrentThreadFCT(ProfilingData &TLD) {
162 if (TLD.Allocators == nullptr || TLD.FCT == nullptr)
163 return;
164
165 profileCollectorService::post(*TLD.FCT, GetTid());
166 TLD.FCT->~FunctionCallTrie();
167 TLD.Allocators->~Allocators();
168 InternalFree(TLD.FCT);
169 InternalFree(TLD.Allocators);
170 TLD.FCT = nullptr;
171 TLD.Allocators = nullptr;
172}
173
174} // namespace
175
176void profilingHandleArg0(int32_t FuncId,
177 XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {
178 unsigned char CPU;
179 auto TSC = readTSC(CPU);
180 RecursionGuard G(ReentranceGuard);
181 if (!G)
182 return;
183
184 auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
185 auto &TLD = getThreadLocalData();
186 if (UNLIKELY(Status == XRayLogInitStatus::XRAY_LOG_FINALIZED ||
187 Status == XRayLogInitStatus::XRAY_LOG_FINALIZING)) {
188 postCurrentThreadFCT(TLD);
189 return;
190 }
191
192 switch (Entry) {
193 case XRayEntryType::ENTRY:
194 case XRayEntryType::LOG_ARGS_ENTRY:
195 TLD.FCT->enterFunction(FuncId, TSC);
196 break;
197 case XRayEntryType::EXIT:
198 case XRayEntryType::TAIL:
199 TLD.FCT->exitFunction(FuncId, TSC);
200 break;
201 default:
202 // FIXME: Handle bugs.
203 break;
204 }
205}
206
207void profilingHandleArg1(int32_t FuncId, XRayEntryType Entry,
208 uint64_t) XRAY_NEVER_INSTRUMENT {
209 return profilingHandleArg0(FuncId, Entry);
210}
211
212XRayLogInitStatus profilingFinalize() XRAY_NEVER_INSTRUMENT {
213 s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED;
214 if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
215 XRayLogInitStatus::XRAY_LOG_FINALIZING,
216 memory_order_release)) {
217 if (Verbosity())
218 Report("Cannot finalize profile, the profiling is not initialized.\n");
219 return static_cast<XRayLogInitStatus>(CurrentStatus);
220 }
221
222 // Wait a grace period to allow threads to see that we're finalizing.
223 SleepForMillis(profilingFlags()->grace_period_ms);
224
225 // We also want to make sure that the current thread's data is cleaned up,
226 // if we have any.
227 auto &TLD = getThreadLocalData();
228 postCurrentThreadFCT(TLD);
229
230 // Then we force serialize the log data.
231 profileCollectorService::serialize();
232
233 atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_FINALIZED,
234 memory_order_release);
235 return XRayLogInitStatus::XRAY_LOG_FINALIZED;
236}
237
238XRayLogInitStatus
239profilingLoggingInit(size_t BufferSize, size_t BufferMax, void *Options,
240 size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
241 if (BufferSize != 0 || BufferMax != 0) {
242 if (Verbosity())
243 Report("__xray_log_init() being used, and is unsupported. Use "
244 "__xray_log_init_mode(...) instead. Bailing out.");
245 return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
246 }
247
248 s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
249 if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
250 XRayLogInitStatus::XRAY_LOG_INITIALIZING,
251 memory_order_release)) {
252 if (Verbosity())
253 Report("Cannot initialize already initialised profiling "
254 "implementation.\n");
255 return static_cast<XRayLogInitStatus>(CurrentStatus);
256 }
257
258 {
259 SpinMutexLock Lock(&ProfilerOptionsMutex);
260 FlagParser ConfigParser;
261 auto *F = profilingFlags();
262 F->setDefaults();
263 registerProfilerFlags(&ConfigParser, F);
Dean Michael Berrisaea8ece2018-06-12 04:06:25 +0000264 ConfigParser.ParseString(profilingCompilerDefinedFlags());
265 const char *Env = GetEnv("XRAY_PROFILING_OPTIONS");
266 if (Env == nullptr)
267 Env = "";
268 ConfigParser.ParseString(Env);
269
270 // Then parse the configuration string provided.
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000271 ConfigParser.ParseString(static_cast<const char *>(Options));
272 if (Verbosity())
273 ReportUnrecognizedFlags();
274 }
275
276 // We need to reset the profile data collection implementation now.
277 profileCollectorService::reset();
278
279 // We need to set up the at-thread-exit handler.
280 static pthread_once_t Once = PTHREAD_ONCE_INIT;
281 pthread_once(&Once, +[] {
282 pthread_key_create(&ProfilingKey, +[](void *P) {
283 // This is the thread-exit handler.
284 auto &TLD = *reinterpret_cast<ProfilingData *>(P);
285 if (TLD.Allocators == nullptr && TLD.FCT == nullptr)
286 return;
287
288 postCurrentThreadFCT(TLD);
289 });
290 });
291
292 __xray_log_set_buffer_iterator(profileCollectorService::nextBuffer);
293 __xray_set_handler(profilingHandleArg0);
294 __xray_set_handler_arg1(profilingHandleArg1);
295
296 atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZED,
297 memory_order_release);
298 if (Verbosity())
299 Report("XRay Profiling init successful.\n");
300
301 return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
302}
303
304bool profilingDynamicInitializer() XRAY_NEVER_INSTRUMENT {
305 // Set up the flag defaults from the static defaults and the
306 // compiler-provided defaults.
307 {
308 SpinMutexLock Lock(&ProfilerOptionsMutex);
309 auto *F = profilingFlags();
310 F->setDefaults();
311 FlagParser ProfilingParser;
312 registerProfilerFlags(&ProfilingParser, F);
Dean Michael Berrisaea8ece2018-06-12 04:06:25 +0000313 ProfilingParser.ParseString(profilingCompilerDefinedFlags());
Dean Michael Berriscfd7eec2018-06-12 03:29:39 +0000314 }
315
316 XRayLogImpl Impl{
317 profilingLoggingInit,
318 profilingFinalize,
319 profilingHandleArg0,
320 profilingFlush,
321 };
322 auto RegistrationResult = __xray_log_register_mode("xray-profiling", Impl);
323 if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK &&
324 Verbosity())
325 Report("Cannot register XRay Profiling mode to 'xray-profiling'; error = "
326 "%d\n",
327 RegistrationResult);
328 if (!internal_strcmp(flags()->xray_mode, "xray-profiling"))
329 __xray_set_log_impl(Impl);
330 return true;
331}
332
333} // namespace __xray
334
335static auto UNUSED Unused = __xray::profilingDynamicInitializer();