|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 // IWYU pragma: private, include "GeckoProfiler.h" |
|
6 |
|
7 #ifndef TOOLS_SPS_SAMPLER_H_ |
|
8 #define TOOLS_SPS_SAMPLER_H_ |
|
9 |
|
10 #include <stdlib.h> |
|
11 #include <signal.h> |
|
12 #include <stdarg.h> |
|
13 #include "mozilla/ThreadLocal.h" |
|
14 #include "mozilla/Assertions.h" |
|
15 #include "nscore.h" |
|
16 #include "GeckoProfilerFunc.h" |
|
17 #include "PseudoStack.h" |
|
18 #include "nsISupports.h" |
|
19 |
|
20 #ifdef MOZ_TASK_TRACER |
|
21 #include "GeckoTaskTracerImpl.h" |
|
22 #endif |
|
23 |
|
24 /* QT has a #define for the word "slots" and jsfriendapi.h has a struct with |
|
25 * this variable name, causing compilation problems. Alleviate this for now by |
|
26 * removing this #define */ |
|
27 #ifdef MOZ_WIDGET_QT |
|
28 #undef slots |
|
29 #endif |
|
30 |
|
31 // Make sure that we can use std::min here without the Windows headers messing with us. |
|
32 #ifdef min |
|
33 #undef min |
|
34 #endif |
|
35 |
|
36 class TableTicker; |
|
37 class JSCustomObject; |
|
38 |
|
39 namespace mozilla { |
|
40 class TimeStamp; |
|
41 } |
|
42 |
|
43 extern mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack; |
|
44 extern mozilla::ThreadLocal<TableTicker *> tlsTicker; |
|
45 extern mozilla::ThreadLocal<void *> tlsStackTop; |
|
46 extern bool stack_key_initialized; |
|
47 |
|
48 #ifndef SAMPLE_FUNCTION_NAME |
|
49 # ifdef __GNUC__ |
|
50 # define SAMPLE_FUNCTION_NAME __FUNCTION__ |
|
51 # elif defined(_MSC_VER) |
|
52 # define SAMPLE_FUNCTION_NAME __FUNCTION__ |
|
53 # else |
|
54 # define SAMPLE_FUNCTION_NAME __func__ // defined in C99, supported in various C++ compilers. Just raw function name. |
|
55 # endif |
|
56 #endif |
|
57 |
|
58 static inline |
|
59 void profiler_init(void* stackTop) |
|
60 { |
|
61 #ifdef MOZ_TASK_TRACER |
|
62 mozilla::tasktracer::InitTaskTracer(); |
|
63 #endif |
|
64 mozilla_sampler_init(stackTop); |
|
65 } |
|
66 |
|
67 static inline |
|
68 void profiler_shutdown() |
|
69 { |
|
70 #ifdef MOZ_TASK_TRACER |
|
71 mozilla::tasktracer::ShutdownTaskTracer(); |
|
72 #endif |
|
73 mozilla_sampler_shutdown(); |
|
74 } |
|
75 |
|
76 static inline |
|
77 void profiler_start(int aProfileEntries, int aInterval, |
|
78 const char** aFeatures, uint32_t aFeatureCount, |
|
79 const char** aThreadNameFilters, uint32_t aFilterCount) |
|
80 { |
|
81 mozilla_sampler_start(aProfileEntries, aInterval, aFeatures, aFeatureCount, aThreadNameFilters, aFilterCount); |
|
82 } |
|
83 |
|
84 static inline |
|
85 void profiler_stop() |
|
86 { |
|
87 mozilla_sampler_stop(); |
|
88 } |
|
89 |
|
90 static inline |
|
91 bool profiler_is_paused() |
|
92 { |
|
93 return mozilla_sampler_is_paused(); |
|
94 } |
|
95 |
|
96 static inline |
|
97 void profiler_pause() |
|
98 { |
|
99 mozilla_sampler_pause(); |
|
100 } |
|
101 |
|
102 static inline |
|
103 void profiler_resume() |
|
104 { |
|
105 mozilla_sampler_resume(); |
|
106 } |
|
107 |
|
108 static inline |
|
109 ProfilerBacktrace* profiler_get_backtrace() |
|
110 { |
|
111 return mozilla_sampler_get_backtrace(); |
|
112 } |
|
113 |
|
114 static inline |
|
115 void profiler_free_backtrace(ProfilerBacktrace* aBacktrace) |
|
116 { |
|
117 mozilla_sampler_free_backtrace(aBacktrace); |
|
118 } |
|
119 |
|
120 static inline |
|
121 bool profiler_is_active() |
|
122 { |
|
123 return mozilla_sampler_is_active(); |
|
124 } |
|
125 |
|
126 static inline |
|
127 void profiler_responsiveness(const mozilla::TimeStamp& aTime) |
|
128 { |
|
129 mozilla_sampler_responsiveness(aTime); |
|
130 } |
|
131 |
|
132 static inline |
|
133 const double* profiler_get_responsiveness() |
|
134 { |
|
135 return mozilla_sampler_get_responsiveness(); |
|
136 } |
|
137 |
|
138 static inline |
|
139 void profiler_set_frame_number(int frameNumber) |
|
140 { |
|
141 return mozilla_sampler_frame_number(frameNumber); |
|
142 } |
|
143 |
|
144 static inline |
|
145 char* profiler_get_profile() |
|
146 { |
|
147 return mozilla_sampler_get_profile(); |
|
148 } |
|
149 |
|
150 static inline |
|
151 JSObject* profiler_get_profile_jsobject(JSContext* aCx) |
|
152 { |
|
153 return mozilla_sampler_get_profile_data(aCx); |
|
154 } |
|
155 |
|
156 static inline |
|
157 void profiler_save_profile_to_file(const char* aFilename) |
|
158 { |
|
159 return mozilla_sampler_save_profile_to_file(aFilename); |
|
160 } |
|
161 |
|
162 static inline |
|
163 const char** profiler_get_features() |
|
164 { |
|
165 return mozilla_sampler_get_features(); |
|
166 } |
|
167 |
|
168 static inline |
|
169 void profiler_print_location() |
|
170 { |
|
171 if (!sps_version2()) { |
|
172 return mozilla_sampler_print_location1(); |
|
173 } else { |
|
174 return mozilla_sampler_print_location2(); |
|
175 } |
|
176 } |
|
177 |
|
178 static inline |
|
179 void profiler_lock() |
|
180 { |
|
181 return mozilla_sampler_lock(); |
|
182 } |
|
183 |
|
184 static inline |
|
185 void profiler_unlock() |
|
186 { |
|
187 return mozilla_sampler_unlock(); |
|
188 } |
|
189 |
|
190 static inline |
|
191 void profiler_register_thread(const char* name, void* stackTop) |
|
192 { |
|
193 mozilla_sampler_register_thread(name, stackTop); |
|
194 } |
|
195 |
|
196 static inline |
|
197 void profiler_unregister_thread() |
|
198 { |
|
199 mozilla_sampler_unregister_thread(); |
|
200 } |
|
201 |
|
202 static inline |
|
203 void profiler_sleep_start() |
|
204 { |
|
205 mozilla_sampler_sleep_start(); |
|
206 } |
|
207 |
|
208 static inline |
|
209 void profiler_sleep_end() |
|
210 { |
|
211 mozilla_sampler_sleep_end(); |
|
212 } |
|
213 |
|
214 static inline |
|
215 void profiler_js_operation_callback() |
|
216 { |
|
217 PseudoStack *stack = tlsPseudoStack.get(); |
|
218 if (!stack) { |
|
219 return; |
|
220 } |
|
221 |
|
222 stack->jsOperationCallback(); |
|
223 } |
|
224 |
|
225 static inline |
|
226 double profiler_time() |
|
227 { |
|
228 return mozilla_sampler_time(); |
|
229 } |
|
230 |
|
231 static inline |
|
232 double profiler_time(const mozilla::TimeStamp& aTime) |
|
233 { |
|
234 return mozilla_sampler_time(aTime); |
|
235 } |
|
236 |
|
237 static inline |
|
238 bool profiler_in_privacy_mode() |
|
239 { |
|
240 PseudoStack *stack = tlsPseudoStack.get(); |
|
241 if (!stack) { |
|
242 return false; |
|
243 } |
|
244 return stack->mPrivacyMode; |
|
245 } |
|
246 |
|
247 static inline void profiler_tracing(const char* aCategory, const char* aInfo, |
|
248 TracingMetadata aMetaData = TRACING_DEFAULT) |
|
249 { |
|
250 if (!stack_key_initialized) |
|
251 return; |
|
252 |
|
253 // Don't insert a marker if we're not profiling to avoid |
|
254 // the heap copy (malloc). |
|
255 if (!profiler_is_active()) { |
|
256 return; |
|
257 } |
|
258 |
|
259 mozilla_sampler_tracing(aCategory, aInfo, aMetaData); |
|
260 } |
|
261 |
|
262 // Uncomment this to turn on systrace or build with |
|
263 // ac_add_options --enable-systace |
|
264 //#define MOZ_USE_SYSTRACE |
|
265 #ifdef MOZ_USE_SYSTRACE |
|
266 # define ATRACE_TAG ATRACE_TAG_GRAPHICS |
|
267 // We need HAVE_ANDROID_OS to be defined for Trace.h. |
|
268 // If its not set we will set it temporary and remove it. |
|
269 # ifndef HAVE_ANDROID_OS |
|
270 # define HAVE_ANDROID_OS |
|
271 # define REMOVE_HAVE_ANDROID_OS |
|
272 # endif |
|
273 # include <utils/Trace.h> |
|
274 # define MOZ_PLATFORM_TRACING ATRACE_CALL(); |
|
275 # ifdef REMOVE_HAVE_ANDROID_OS |
|
276 # undef HAVE_ANDROID_OS |
|
277 # undef REMOVE_HAVE_ANDROID_OS |
|
278 # endif |
|
279 #else |
|
280 # define MOZ_PLATFORM_TRACING |
|
281 #endif |
|
282 |
|
283 // we want the class and function name but can't easily get that using preprocessor macros |
|
284 // __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters |
|
285 |
|
286 #define SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) id ## line |
|
287 #define SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, line) SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) |
|
288 #define SAMPLER_APPEND_LINE_NUMBER(id) SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, __LINE__) |
|
289 |
|
290 #define PROFILER_LABEL(name_space, info) MOZ_PLATFORM_TRACING mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__) |
|
291 #define PROFILER_LABEL_PRINTF(name_space, info, ...) MOZ_PLATFORM_TRACING mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__, __VA_ARGS__) |
|
292 |
|
293 #define PROFILER_MARKER(info) mozilla_sampler_add_marker(info) |
|
294 #define PROFILER_MARKER_PAYLOAD(info, payload) mozilla_sampler_add_marker(info, payload) |
|
295 #define PROFILER_MAIN_THREAD_MARKER(info) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla_sampler_add_marker(info) |
|
296 |
|
297 #define PROFILER_MAIN_THREAD_LABEL(name_space, info) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__) |
|
298 #define PROFILER_MAIN_THREAD_LABEL_PRINTF(name_space, info, ...) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__, __VA_ARGS__) |
|
299 |
|
300 |
|
301 /* FIXME/bug 789667: memory constraints wouldn't much of a problem for |
|
302 * this small a sample buffer size, except that serializing the |
|
303 * profile data is extremely, unnecessarily memory intensive. */ |
|
304 #ifdef MOZ_WIDGET_GONK |
|
305 # define PLATFORM_LIKELY_MEMORY_CONSTRAINED |
|
306 #endif |
|
307 |
|
308 #if !defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED) && !defined(ARCH_ARMV6) |
|
309 # define PROFILE_DEFAULT_ENTRY 1000000 |
|
310 #else |
|
311 # define PROFILE_DEFAULT_ENTRY 100000 |
|
312 #endif |
|
313 |
|
314 // In the case of profiler_get_backtrace we know that we only need enough space |
|
315 // for a single backtrace. |
|
316 #define GET_BACKTRACE_DEFAULT_ENTRY 1000 |
|
317 |
|
318 #if defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED) |
|
319 /* A 1ms sampling interval has been shown to be a large perf hit |
|
320 * (10fps) on memory-contrained (low-end) platforms, and additionally |
|
321 * to yield different results from the profiler. Where this is the |
|
322 * important case, b2g, there are also many gecko processes which |
|
323 * magnify these effects. */ |
|
324 # define PROFILE_DEFAULT_INTERVAL 10 |
|
325 #elif defined(ANDROID) |
|
326 // We use a lower frequency on Android, in order to make things work |
|
327 // more smoothly on phones. This value can be adjusted later with |
|
328 // some libunwind optimizations. |
|
329 // In one sample measurement on Galaxy Nexus, out of about 700 backtraces, |
|
330 // 60 of them took more than 25ms, and the average and standard deviation |
|
331 // were 6.17ms and 9.71ms respectively. |
|
332 |
|
333 // For now since we don't support stackwalking let's use 1ms since it's fast |
|
334 // enough. |
|
335 #define PROFILE_DEFAULT_INTERVAL 1 |
|
336 #else |
|
337 #define PROFILE_DEFAULT_INTERVAL 1 |
|
338 #endif |
|
339 #define PROFILE_DEFAULT_FEATURES NULL |
|
340 #define PROFILE_DEFAULT_FEATURE_COUNT 0 |
|
341 |
|
342 namespace mozilla { |
|
343 |
|
344 class MOZ_STACK_CLASS SamplerStackFrameRAII { |
|
345 public: |
|
346 // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then. |
|
347 SamplerStackFrameRAII(const char *aInfo, uint32_t line) { |
|
348 mHandle = mozilla_sampler_call_enter(aInfo, this, false, line); |
|
349 } |
|
350 ~SamplerStackFrameRAII() { |
|
351 mozilla_sampler_call_exit(mHandle); |
|
352 } |
|
353 private: |
|
354 void* mHandle; |
|
355 }; |
|
356 |
|
357 static const int SAMPLER_MAX_STRING = 128; |
|
358 class MOZ_STACK_CLASS SamplerStackFramePrintfRAII { |
|
359 public: |
|
360 // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then. |
|
361 SamplerStackFramePrintfRAII(const char *aDefault, uint32_t line, const char *aFormat, ...) { |
|
362 if (profiler_is_active() && !profiler_in_privacy_mode()) { |
|
363 va_list args; |
|
364 va_start(args, aFormat); |
|
365 char buff[SAMPLER_MAX_STRING]; |
|
366 |
|
367 // We have to use seperate printf's because we're using |
|
368 // the vargs. |
|
369 #if _MSC_VER |
|
370 _vsnprintf(buff, SAMPLER_MAX_STRING, aFormat, args); |
|
371 _snprintf(mDest, SAMPLER_MAX_STRING, "%s %s", aDefault, buff); |
|
372 #else |
|
373 ::vsnprintf(buff, SAMPLER_MAX_STRING, aFormat, args); |
|
374 ::snprintf(mDest, SAMPLER_MAX_STRING, "%s %s", aDefault, buff); |
|
375 #endif |
|
376 mHandle = mozilla_sampler_call_enter(mDest, this, true, line); |
|
377 va_end(args); |
|
378 } else { |
|
379 mHandle = mozilla_sampler_call_enter(aDefault, this, false, line); |
|
380 } |
|
381 } |
|
382 ~SamplerStackFramePrintfRAII() { |
|
383 mozilla_sampler_call_exit(mHandle); |
|
384 } |
|
385 private: |
|
386 char mDest[SAMPLER_MAX_STRING]; |
|
387 void* mHandle; |
|
388 }; |
|
389 |
|
390 } //mozilla |
|
391 |
|
392 inline PseudoStack* mozilla_get_pseudo_stack(void) |
|
393 { |
|
394 if (!stack_key_initialized) |
|
395 return nullptr; |
|
396 return tlsPseudoStack.get(); |
|
397 } |
|
398 |
|
399 inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress, |
|
400 bool aCopy, uint32_t line) |
|
401 { |
|
402 // check if we've been initialized to avoid calling pthread_getspecific |
|
403 // with a null tlsStack which will return undefined results. |
|
404 if (!stack_key_initialized) |
|
405 return nullptr; |
|
406 |
|
407 PseudoStack *stack = tlsPseudoStack.get(); |
|
408 // we can't infer whether 'stack' has been initialized |
|
409 // based on the value of stack_key_intiailized because |
|
410 // 'stack' is only intialized when a thread is being |
|
411 // profiled. |
|
412 if (!stack) { |
|
413 return stack; |
|
414 } |
|
415 stack->push(aInfo, aFrameAddress, aCopy, line); |
|
416 |
|
417 // The handle is meant to support future changes |
|
418 // but for now it is simply use to save a call to |
|
419 // pthread_getspecific on exit. It also supports the |
|
420 // case where the sampler is initialized between |
|
421 // enter and exit. |
|
422 return stack; |
|
423 } |
|
424 |
|
425 inline void mozilla_sampler_call_exit(void *aHandle) |
|
426 { |
|
427 if (!aHandle) |
|
428 return; |
|
429 |
|
430 PseudoStack *stack = (PseudoStack*)aHandle; |
|
431 stack->pop(); |
|
432 } |
|
433 |
|
434 void mozilla_sampler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPayload); |
|
435 |
|
436 #endif /* ndef TOOLS_SPS_SAMPLER_H_ */ |