blob: 1e7922c1e8eca720a267268c04a1d081f3743d2a [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>
16
17#include "sanitizer_common/sanitizer_atomic.h"
18#include "sanitizer_common/sanitizer_flags.h"
19#include "xray/xray_interface.h"
20#include "xray/xray_log_interface.h"
21
22#include "xray_flags.h"
23#include "xray_profile_collector.h"
24#include "xray_profiling_flags.h"
25#include "xray_recursion_guard.h"
26#include "xray_tsc.h"
27#include "xray_utils.h"
28#include <pthread.h>
29
30namespace __xray {
31
32namespace {
33
34atomic_sint32_t ProfilerLogFlushStatus = {
35 XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
36
37atomic_sint32_t ProfilerLogStatus = {XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};
38
39SpinMutex ProfilerOptionsMutex;
40
41struct alignas(64) ProfilingData {
42 FunctionCallTrie::Allocators *Allocators = nullptr;
43 FunctionCallTrie *FCT = nullptr;
44};
45
46static pthread_key_t ProfilingKey;
47
48ProfilingData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
49 thread_local std::aligned_storage<sizeof(ProfilingData)>::type ThreadStorage;
50 if (pthread_getspecific(ProfilingKey) == NULL) {
51 new (&ThreadStorage) ProfilingData{};
52 pthread_setspecific(ProfilingKey, &ThreadStorage);
53 }
54
55 auto &TLD = *reinterpret_cast<ProfilingData *>(&ThreadStorage);
56
57 // We need to check whether the global flag to finalizing/finalized has been
58 // switched. If it is, then we ought to not actually initialise the data.
59 auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
60 if (Status == XRayLogInitStatus::XRAY_LOG_FINALIZING ||
61 Status == XRayLogInitStatus::XRAY_LOG_FINALIZED)
62 return TLD;
63
64 // If we're live, then we re-initialize TLD if the pointers are not null.
65 if (UNLIKELY(TLD.Allocators == nullptr && TLD.FCT == nullptr)) {
66 TLD.Allocators = reinterpret_cast<FunctionCallTrie::Allocators *>(
67 InternalAlloc(sizeof(FunctionCallTrie::Allocators)));
68 new (TLD.Allocators) FunctionCallTrie::Allocators();
69 *TLD.Allocators = FunctionCallTrie::InitAllocators();
70 TLD.FCT = reinterpret_cast<FunctionCallTrie *>(
71 InternalAlloc(sizeof(FunctionCallTrie)));
72 new (TLD.FCT) FunctionCallTrie(*TLD.Allocators);
73 }
74
75 return TLD;
76}
77
78} // namespace
79
80const char *profilingCompilerDefinedFlags() XRAY_NEVER_INSTRUMENT {
81#ifdef XRAY_PROFILER_DEFAULT_OPTIONS
82 return SANITIZER_STRINGIFY(XRAY_PROFILER_DEFAULT_OPTIONS);
83#else
84 return "";
85#endif
86}
87
88atomic_sint32_t ProfileFlushStatus = {
89 XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
90
91XRayLogFlushStatus profilingFlush() XRAY_NEVER_INSTRUMENT {
92 // When flushing, all we really do is reset the global state, and only when
93 // the log has already been finalized.
94 if (atomic_load(&ProfilerLogStatus, memory_order_acquire) !=
95 XRayLogInitStatus::XRAY_LOG_FINALIZED) {
96 if (Verbosity())
97 Report("Not flushing profiles, profiling not been finalized.\n");
98 return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
99 }
100
101 s32 Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
102 if (!atomic_compare_exchange_strong(&ProfilerLogFlushStatus, &Result,
103 XRayLogFlushStatus::XRAY_LOG_FLUSHING,
104 memory_order_acq_rel)) {
105 if (Verbosity())
106 Report("Not flushing profiles, implementation still finalizing.\n");
107 }
108
109 profileCollectorService::reset();
110
111 atomic_store(&ProfilerLogStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED,
112 memory_order_release);
113
114 return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
115}
116
117namespace {
118
119thread_local atomic_uint8_t ReentranceGuard{0};
120
121void postCurrentThreadFCT(ProfilingData &TLD) {
122 if (TLD.Allocators == nullptr || TLD.FCT == nullptr)
123 return;
124
125 profileCollectorService::post(*TLD.FCT, GetTid());
126 TLD.FCT->~FunctionCallTrie();
127 TLD.Allocators->~Allocators();
128 InternalFree(TLD.FCT);
129 InternalFree(TLD.Allocators);
130 TLD.FCT = nullptr;
131 TLD.Allocators = nullptr;
132}
133
134} // namespace
135
136void profilingHandleArg0(int32_t FuncId,
137 XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {
138 unsigned char CPU;
139 auto TSC = readTSC(CPU);
140 RecursionGuard G(ReentranceGuard);
141 if (!G)
142 return;
143
144 auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
145 auto &TLD = getThreadLocalData();
146 if (UNLIKELY(Status == XRayLogInitStatus::XRAY_LOG_FINALIZED ||
147 Status == XRayLogInitStatus::XRAY_LOG_FINALIZING)) {
148 postCurrentThreadFCT(TLD);
149 return;
150 }
151
152 switch (Entry) {
153 case XRayEntryType::ENTRY:
154 case XRayEntryType::LOG_ARGS_ENTRY:
155 TLD.FCT->enterFunction(FuncId, TSC);
156 break;
157 case XRayEntryType::EXIT:
158 case XRayEntryType::TAIL:
159 TLD.FCT->exitFunction(FuncId, TSC);
160 break;
161 default:
162 // FIXME: Handle bugs.
163 break;
164 }
165}
166
167void profilingHandleArg1(int32_t FuncId, XRayEntryType Entry,
168 uint64_t) XRAY_NEVER_INSTRUMENT {
169 return profilingHandleArg0(FuncId, Entry);
170}
171
172XRayLogInitStatus profilingFinalize() XRAY_NEVER_INSTRUMENT {
173 s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_INITIALIZED;
174 if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
175 XRayLogInitStatus::XRAY_LOG_FINALIZING,
176 memory_order_release)) {
177 if (Verbosity())
178 Report("Cannot finalize profile, the profiling is not initialized.\n");
179 return static_cast<XRayLogInitStatus>(CurrentStatus);
180 }
181
182 // Wait a grace period to allow threads to see that we're finalizing.
183 SleepForMillis(profilingFlags()->grace_period_ms);
184
185 // We also want to make sure that the current thread's data is cleaned up,
186 // if we have any.
187 auto &TLD = getThreadLocalData();
188 postCurrentThreadFCT(TLD);
189
190 // Then we force serialize the log data.
191 profileCollectorService::serialize();
192
193 atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_FINALIZED,
194 memory_order_release);
195 return XRayLogInitStatus::XRAY_LOG_FINALIZED;
196}
197
198XRayLogInitStatus
199profilingLoggingInit(size_t BufferSize, size_t BufferMax, void *Options,
200 size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
201 if (BufferSize != 0 || BufferMax != 0) {
202 if (Verbosity())
203 Report("__xray_log_init() being used, and is unsupported. Use "
204 "__xray_log_init_mode(...) instead. Bailing out.");
205 return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
206 }
207
208 s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
209 if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
210 XRayLogInitStatus::XRAY_LOG_INITIALIZING,
211 memory_order_release)) {
212 if (Verbosity())
213 Report("Cannot initialize already initialised profiling "
214 "implementation.\n");
215 return static_cast<XRayLogInitStatus>(CurrentStatus);
216 }
217
218 {
219 SpinMutexLock Lock(&ProfilerOptionsMutex);
220 FlagParser ConfigParser;
221 auto *F = profilingFlags();
222 F->setDefaults();
223 registerProfilerFlags(&ConfigParser, F);
224 const char *ProfilerCompileFlags = profilingCompilerDefinedFlags();
225 ConfigParser.ParseString(ProfilerCompileFlags);
226 ConfigParser.ParseString(static_cast<const char *>(Options));
227 if (Verbosity())
228 ReportUnrecognizedFlags();
229 }
230
231 // We need to reset the profile data collection implementation now.
232 profileCollectorService::reset();
233
234 // We need to set up the at-thread-exit handler.
235 static pthread_once_t Once = PTHREAD_ONCE_INIT;
236 pthread_once(&Once, +[] {
237 pthread_key_create(&ProfilingKey, +[](void *P) {
238 // This is the thread-exit handler.
239 auto &TLD = *reinterpret_cast<ProfilingData *>(P);
240 if (TLD.Allocators == nullptr && TLD.FCT == nullptr)
241 return;
242
243 postCurrentThreadFCT(TLD);
244 });
245 });
246
247 __xray_log_set_buffer_iterator(profileCollectorService::nextBuffer);
248 __xray_set_handler(profilingHandleArg0);
249 __xray_set_handler_arg1(profilingHandleArg1);
250
251 atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_INITIALIZED,
252 memory_order_release);
253 if (Verbosity())
254 Report("XRay Profiling init successful.\n");
255
256 return XRayLogInitStatus::XRAY_LOG_INITIALIZED;
257}
258
259bool profilingDynamicInitializer() XRAY_NEVER_INSTRUMENT {
260 // Set up the flag defaults from the static defaults and the
261 // compiler-provided defaults.
262 {
263 SpinMutexLock Lock(&ProfilerOptionsMutex);
264 auto *F = profilingFlags();
265 F->setDefaults();
266 FlagParser ProfilingParser;
267 registerProfilerFlags(&ProfilingParser, F);
268 const char *ProfilerCompileFlags = profilingCompilerDefinedFlags();
269 ProfilingParser.ParseString(ProfilerCompileFlags);
270 }
271
272 XRayLogImpl Impl{
273 profilingLoggingInit,
274 profilingFinalize,
275 profilingHandleArg0,
276 profilingFlush,
277 };
278 auto RegistrationResult = __xray_log_register_mode("xray-profiling", Impl);
279 if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK &&
280 Verbosity())
281 Report("Cannot register XRay Profiling mode to 'xray-profiling'; error = "
282 "%d\n",
283 RegistrationResult);
284 if (!internal_strcmp(flags()->xray_mode, "xray-profiling"))
285 __xray_set_log_impl(Impl);
286 return true;
287}
288
289} // namespace __xray
290
291static auto UNUSED Unused = __xray::profilingDynamicInitializer();