/work/obj-fuzz/dist/include/GeckoProfiler.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | // The Gecko Profiler is an always-on profiler that takes fast and low overhead |
8 | | // samples of the program execution using only userspace functionality for |
9 | | // portability. The goal of this module is to provide performance data in a |
10 | | // generic cross-platform way without requiring custom tools or kernel support. |
11 | | // |
12 | | // Samples are collected to form a timeline with optional timeline event |
13 | | // (markers) used for filtering. The samples include both native stacks and |
14 | | // platform-independent "label stack" frames. |
15 | | |
16 | | #ifndef GeckoProfiler_h |
17 | | #define GeckoProfiler_h |
18 | | |
19 | | #ifndef MOZ_GECKO_PROFILER |
20 | | |
21 | | // This file can be #included unconditionally. However, everything within this |
22 | | // file must be guarded by a #ifdef MOZ_GECKO_PROFILER, *except* for the |
23 | | // following macros, which encapsulate the most common operations and thus |
24 | | // avoid the need for many #ifdefs. |
25 | | |
26 | | #define AUTO_PROFILER_INIT |
27 | | |
28 | | #define PROFILER_REGISTER_THREAD(name) |
29 | | #define PROFILER_UNREGISTER_THREAD() |
30 | | #define AUTO_PROFILER_REGISTER_THREAD(name) |
31 | | |
32 | | #define AUTO_PROFILER_THREAD_SLEEP |
33 | | #define AUTO_PROFILER_THREAD_WAKE |
34 | | |
35 | | #define PROFILER_JS_INTERRUPT_CALLBACK() |
36 | | |
37 | | #define PROFILER_SET_JS_CONTEXT(cx) |
38 | | #define PROFILER_CLEAR_JS_CONTEXT() |
39 | | |
40 | | #define AUTO_PROFILER_LABEL(label, category) |
41 | | #define AUTO_PROFILER_LABEL_DYNAMIC_CSTR(label, category, cStr) |
42 | | #define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(label, category, nsCStr) |
43 | | #define AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(label, category, nsStr) |
44 | | #define AUTO_PROFILER_LABEL_FAST(label, category, ctx) |
45 | | |
46 | | #define PROFILER_ADD_MARKER(markerName) |
47 | | #define PROFILER_ADD_NETWORK_MARKER(uri, pri, channel, type, start, end, count, timings, redirect) |
48 | | |
49 | | #define PROFILER_TRACING(category, markerName, kind) |
50 | | #define AUTO_PROFILER_TRACING(category, markerName) |
51 | | |
52 | | #else // !MOZ_GECKO_PROFILER |
53 | | |
54 | | #include <functional> |
55 | | #include <signal.h> |
56 | | #include <stdarg.h> |
57 | | #include <stdint.h> |
58 | | #include <stdlib.h> |
59 | | |
60 | | #include "mozilla/Assertions.h" |
61 | | #include "mozilla/Attributes.h" |
62 | | #include "mozilla/GuardObjects.h" |
63 | | #include "mozilla/Maybe.h" |
64 | | #include "mozilla/Sprintf.h" |
65 | | #include "mozilla/ThreadLocal.h" |
66 | | #include "mozilla/TimeStamp.h" |
67 | | #include "mozilla/UniquePtr.h" |
68 | | #include "mozilla/net/TimingStruct.h" |
69 | | #include "js/ProfilingStack.h" |
70 | | #include "js/RootingAPI.h" |
71 | | #include "js/TypeDecls.h" |
72 | | #include "nscore.h" |
73 | | #include "nsIURI.h" |
74 | | |
75 | | // Make sure that we can use std::min here without the Windows headers messing |
76 | | // with us. |
77 | | #ifdef min |
78 | | # undef min |
79 | | #endif |
80 | | |
81 | | class ProfilerBacktrace; |
82 | | class ProfilerMarkerPayload; |
83 | | class SpliceableJSONWriter; |
84 | | |
85 | | namespace mozilla { |
86 | | class MallocAllocPolicy; |
87 | | template <class T, size_t MinInlineCapacity, class AllocPolicy> class Vector; |
88 | | class TimeStamp; |
89 | | } // namespace mozilla |
90 | | |
91 | | // Macros used by the AUTO_PROFILER_* macros below. |
92 | 272 | #define PROFILER_RAII_PASTE(id, line) id ## line |
93 | 272 | #define PROFILER_RAII_EXPAND(id, line) PROFILER_RAII_PASTE(id, line) |
94 | 272 | #define PROFILER_RAII PROFILER_RAII_EXPAND(raiiObject, __LINE__) |
95 | | |
96 | | //--------------------------------------------------------------------------- |
97 | | // Profiler features |
98 | | //--------------------------------------------------------------------------- |
99 | | |
100 | | // Higher-order macro containing all the feature info in one place. Define |
101 | | // |macro| appropriately to extract the relevant parts. Note that the number |
102 | | // values are used internally only and so can be changed without consequence. |
103 | | // Any changes to this list should also be applied to the feature list in |
104 | | // browser/components/extensions/schemas/geckoProfiler.json. |
105 | | #define PROFILER_FOR_EACH_FEATURE(macro) \ |
106 | | /* Profile Java code (Android only). */ \ |
107 | 0 | macro(0, "java", Java) \ |
108 | 0 | \ |
109 | 0 | /* Get the JS engine to expose the JS stack to the profiler */ \ |
110 | 0 | macro(1, "js", JS) \ |
111 | 0 | \ |
112 | 0 | /* Include the C++ leaf node if not stackwalking. */ \ |
113 | 0 | /* The DevTools profiler doesn't want the native addresses. */ \ |
114 | 0 | macro(2, "leaf", Leaf) \ |
115 | 0 | \ |
116 | 0 | /* Add main thread I/O to the profile. */ \ |
117 | 0 | macro(3, "mainthreadio", MainThreadIO) \ |
118 | 0 | \ |
119 | 0 | /* Add memory measurements (e.g. RSS). */ \ |
120 | 0 | macro(4, "memory", Memory) \ |
121 | 0 | \ |
122 | 0 | /* Do not include user-identifiable information. */ \ |
123 | 0 | macro(5, "privacy", Privacy) \ |
124 | 0 | \ |
125 | 0 | /* Collect thread responsiveness information. */ \ |
126 | 0 | macro(6, "responsiveness", Responsiveness) \ |
127 | 0 | \ |
128 | 0 | /* Take a snapshot of the window on every composition. */ \ |
129 | 0 | macro(7, "screenshots", Screenshots) \ |
130 | 0 | \ |
131 | 0 | /* Disable parallel traversal in styling. */ \ |
132 | 0 | macro(8, "seqstyle", SequentialStyle) \ |
133 | 0 | \ |
134 | 0 | /* Walk the C++ stack. Not available on all platforms. */ \ |
135 | 0 | macro(9, "stackwalk", StackWalk) \ |
136 | 0 | \ |
137 | 0 | /* Start profiling with feature TaskTracer. */ \ |
138 | 0 | macro(10, "tasktracer", TaskTracer) \ |
139 | 0 | \ |
140 | 0 | /* Profile the registered secondary threads. */ \ |
141 | 0 | macro(11, "threads", Threads) \ |
142 | 0 | \ |
143 | 0 | /* Have the JavaScript engine track JIT optimizations. */ \ |
144 | 0 | macro(12, "trackopts", TrackOptimizations) |
145 | | |
146 | | struct ProfilerFeature |
147 | | { |
148 | | #define DECLARE(n_, str_, Name_) \ |
149 | | static const uint32_t Name_ = (1u << n_); \ |
150 | 0 | static bool Has##Name_(uint32_t aFeatures) { return aFeatures & Name_; } \ Unexecuted instantiation: ProfilerFeature::HasJava(unsigned int) Unexecuted instantiation: ProfilerFeature::HasJS(unsigned int) Unexecuted instantiation: ProfilerFeature::HasLeaf(unsigned int) Unexecuted instantiation: ProfilerFeature::HasMainThreadIO(unsigned int) Unexecuted instantiation: ProfilerFeature::HasMemory(unsigned int) Unexecuted instantiation: ProfilerFeature::HasPrivacy(unsigned int) Unexecuted instantiation: ProfilerFeature::HasResponsiveness(unsigned int) Unexecuted instantiation: ProfilerFeature::HasScreenshots(unsigned int) Unexecuted instantiation: ProfilerFeature::HasSequentialStyle(unsigned int) Unexecuted instantiation: ProfilerFeature::HasStackWalk(unsigned int) Unexecuted instantiation: ProfilerFeature::HasTaskTracer(unsigned int) Unexecuted instantiation: ProfilerFeature::HasThreads(unsigned int) Unexecuted instantiation: ProfilerFeature::HasTrackOptimizations(unsigned int) |
151 | 0 | static void Set##Name_(uint32_t& aFeatures) { aFeatures |= Name_; } \ Unexecuted instantiation: ProfilerFeature::SetJava(unsigned int&) Unexecuted instantiation: ProfilerFeature::SetJS(unsigned int&) Unexecuted instantiation: ProfilerFeature::SetLeaf(unsigned int&) Unexecuted instantiation: ProfilerFeature::SetMainThreadIO(unsigned int&) Unexecuted instantiation: ProfilerFeature::SetMemory(unsigned int&) Unexecuted instantiation: ProfilerFeature::SetPrivacy(unsigned int&) Unexecuted instantiation: ProfilerFeature::SetResponsiveness(unsigned int&) Unexecuted instantiation: ProfilerFeature::SetScreenshots(unsigned int&) Unexecuted instantiation: ProfilerFeature::SetSequentialStyle(unsigned int&) Unexecuted instantiation: ProfilerFeature::SetStackWalk(unsigned int&) Unexecuted instantiation: ProfilerFeature::SetTaskTracer(unsigned int&) Unexecuted instantiation: ProfilerFeature::SetThreads(unsigned int&) Unexecuted instantiation: ProfilerFeature::SetTrackOptimizations(unsigned int&) |
152 | 0 | static void Clear##Name_(uint32_t& aFeatures) { aFeatures &= ~Name_; } Unexecuted instantiation: ProfilerFeature::ClearJava(unsigned int&) Unexecuted instantiation: ProfilerFeature::ClearJS(unsigned int&) Unexecuted instantiation: ProfilerFeature::ClearLeaf(unsigned int&) Unexecuted instantiation: ProfilerFeature::ClearMainThreadIO(unsigned int&) Unexecuted instantiation: ProfilerFeature::ClearMemory(unsigned int&) Unexecuted instantiation: ProfilerFeature::ClearPrivacy(unsigned int&) Unexecuted instantiation: ProfilerFeature::ClearResponsiveness(unsigned int&) Unexecuted instantiation: ProfilerFeature::ClearScreenshots(unsigned int&) Unexecuted instantiation: ProfilerFeature::ClearSequentialStyle(unsigned int&) Unexecuted instantiation: ProfilerFeature::ClearStackWalk(unsigned int&) Unexecuted instantiation: ProfilerFeature::ClearTaskTracer(unsigned int&) Unexecuted instantiation: ProfilerFeature::ClearThreads(unsigned int&) Unexecuted instantiation: ProfilerFeature::ClearTrackOptimizations(unsigned int&) |
153 | | |
154 | | // Define a bitfield constant, a getter, and two setters for each feature. |
155 | | PROFILER_FOR_EACH_FEATURE(DECLARE) |
156 | | |
157 | | #undef DECLARE |
158 | | }; |
159 | | |
160 | | namespace mozilla { |
161 | | namespace profiler { |
162 | | namespace detail { |
163 | | |
164 | | // RacyFeatures is only defined in this header file so that its methods can |
165 | | // be inlined into profiler_is_active(). Please do not use anything from the |
166 | | // detail namespace outside the profiler. |
167 | | |
168 | | // Within the profiler's code, the preferred way to check profiler activeness |
169 | | // and features is via ActivePS(). However, that requires locking gPSMutex. |
170 | | // There are some hot operations where absolute precision isn't required, so we |
171 | | // duplicate the activeness/feature state in a lock-free manner in this class. |
172 | | class RacyFeatures |
173 | | { |
174 | | public: |
175 | | static void SetActive(uint32_t aFeatures) |
176 | 0 | { |
177 | 0 | sActiveAndFeatures = Active | aFeatures; |
178 | 0 | } |
179 | | |
180 | 0 | static void SetInactive() { sActiveAndFeatures = 0; } |
181 | | |
182 | 352 | static bool IsActive() { return uint32_t(sActiveAndFeatures) & Active; } |
183 | | |
184 | | static bool IsActiveWithFeature(uint32_t aFeature) |
185 | 0 | { |
186 | 0 | uint32_t af = sActiveAndFeatures; // copy it first |
187 | 0 | return (af & Active) && (af & aFeature); |
188 | 0 | } |
189 | | |
190 | | static bool IsActiveWithoutPrivacy() |
191 | 3 | { |
192 | 3 | uint32_t af = sActiveAndFeatures; // copy it first |
193 | 3 | return (af & Active) && !(af & ProfilerFeature::Privacy); |
194 | 3 | } |
195 | | |
196 | | private: |
197 | | static const uint32_t Active = 1u << 31; |
198 | | |
199 | | // Ensure Active doesn't overlap with any of the feature bits. |
200 | | #define NO_OVERLAP(n_, str_, Name_) \ |
201 | | static_assert(ProfilerFeature::Name_ != Active, "bad Active value"); |
202 | | |
203 | | PROFILER_FOR_EACH_FEATURE(NO_OVERLAP); |
204 | | |
205 | | #undef NO_OVERLAP |
206 | | |
207 | | // We combine the active bit with the feature bits so they can be read or |
208 | | // written in a single atomic operation. Accesses to this atomic are not |
209 | | // recorded by web replay as they may occur at non-deterministic points. |
210 | | static mozilla::Atomic<uint32_t, mozilla::MemoryOrdering::Relaxed, |
211 | | recordreplay::Behavior::DontPreserve> sActiveAndFeatures; |
212 | | }; |
213 | | |
214 | | } // namespace detail |
215 | | } // namespace profiler |
216 | | } // namespace mozilla |
217 | | |
218 | | //--------------------------------------------------------------------------- |
219 | | // Start and stop the profiler |
220 | | //--------------------------------------------------------------------------- |
221 | | |
222 | | #if !defined(ARCH_ARMV6) |
223 | 3 | # define PROFILER_DEFAULT_ENTRIES 1000000 |
224 | | #else |
225 | | # define PROFILER_DEFAULT_ENTRIES 100000 |
226 | | #endif |
227 | | |
228 | 3 | #define PROFILER_DEFAULT_INTERVAL 1 |
229 | | |
230 | | // Initialize the profiler. If MOZ_PROFILER_STARTUP is set the profiler will |
231 | | // also be started. This call must happen before any other profiler calls |
232 | | // (except profiler_start(), which will call profiler_init() if it hasn't |
233 | | // already run). |
234 | | void profiler_init(void* stackTop); |
235 | | |
236 | | #define AUTO_PROFILER_INIT \ |
237 | 3 | mozilla::AutoProfilerInit PROFILER_RAII |
238 | | |
239 | | // Clean up the profiler module, stopping it if required. This function may |
240 | | // also save a shutdown profile if requested. No profiler calls should happen |
241 | | // after this point and all profiling stack labels should have been popped. |
242 | | void profiler_shutdown(); |
243 | | |
244 | | // Start the profiler -- initializing it first if necessary -- with the |
245 | | // selected options. Stops and restarts the profiler if it is already active. |
246 | | // After starting the profiler is "active". The samples will be recorded in a |
247 | | // circular buffer. |
248 | | // "aEntries" is the number of entries in the profiler's circular buffer. |
249 | | // "aInterval" the sampling interval, measured in millseconds. |
250 | | // "aFeatures" is the feature set. Features unsupported by this |
251 | | // platform/configuration are ignored. |
252 | | // "aFilters" is the list of thread filters. Threads that do not match any |
253 | | // of the filters are not profiled. A filter matches a thread if |
254 | | // (a) the thread name contains the filter as a case-insensitive |
255 | | // substring, or |
256 | | // (b) the filter is of the form "pid:<n>" where n is the process |
257 | | // id of the process that the thread is running in. |
258 | | void profiler_start(uint32_t aEntries, double aInterval, uint32_t aFeatures, |
259 | | const char** aFilters, uint32_t aFilterCount); |
260 | | |
261 | | // Stop the profiler and discard the profile without saving it. A no-op if the |
262 | | // profiler is inactive. After stopping the profiler is "inactive". |
263 | | void profiler_stop(); |
264 | | |
265 | | // If the profiler is inactive, start it. If it's already active, restart it if |
266 | | // the requested settings differ from the current settings. Both the check and |
267 | | // the state change are performed while the profiler state is locked. |
268 | | // The only difference to profiler_start is that the current buffer contents are |
269 | | // not discarded if the profiler is already running with the requested settings. |
270 | | void profiler_ensure_started(uint32_t aEntries, double aInterval, |
271 | | uint32_t aFeatures, const char** aFilters, |
272 | | uint32_t aFilterCount); |
273 | | |
274 | | //--------------------------------------------------------------------------- |
275 | | // Control the profiler |
276 | | //--------------------------------------------------------------------------- |
277 | | |
278 | | // Register/unregister threads with the profiler. Both functions operate the |
279 | | // same whether the profiler is active or inactive. |
280 | | #define PROFILER_REGISTER_THREAD(name) \ |
281 | 13 | do { char stackTop; profiler_register_thread(name, &stackTop); } while (0) |
282 | | #define PROFILER_UNREGISTER_THREAD() \ |
283 | 13 | profiler_unregister_thread() |
284 | | ProfilingStack* profiler_register_thread(const char* name, void* guessStackTop); |
285 | | void profiler_unregister_thread(); |
286 | | |
287 | | // Register and unregister a thread within a scope. |
288 | | #define AUTO_PROFILER_REGISTER_THREAD(name) \ |
289 | 9 | mozilla::AutoProfilerRegisterThread PROFILER_RAII(name) |
290 | | |
291 | | // Pause and resume the profiler. No-ops if the profiler is inactive. While |
292 | | // paused the profile will not take any samples and will not record any data |
293 | | // into its buffers. The profiler remains fully initialized in this state. |
294 | | // Timeline markers will still be stored. This feature will keep JavaScript |
295 | | // profiling enabled, thus allowing toggling the profiler without invalidating |
296 | | // the JIT. |
297 | | void profiler_pause(); |
298 | | void profiler_resume(); |
299 | | |
300 | | // These functions tell the profiler that a thread went to sleep so that we can |
301 | | // avoid sampling it while it's sleeping. Calling profiler_thread_sleep() |
302 | | // twice without an intervening profiler_thread_wake() is an error. All three |
303 | | // functions operate the same whether the profiler is active or inactive. |
304 | | void profiler_thread_sleep(); |
305 | | void profiler_thread_wake(); |
306 | | |
307 | | // Mark a thread as asleep/awake within a scope. |
308 | | #define AUTO_PROFILER_THREAD_SLEEP \ |
309 | 210 | mozilla::AutoProfilerThreadSleep PROFILER_RAII |
310 | | #define AUTO_PROFILER_THREAD_WAKE \ |
311 | | mozilla::AutoProfilerThreadWake PROFILER_RAII |
312 | | |
313 | | // Called by the JSRuntime's operation callback. This is used to start profiling |
314 | | // on auxiliary threads. Operates the same whether the profiler is active or |
315 | | // not. |
316 | 0 | #define PROFILER_JS_INTERRUPT_CALLBACK() profiler_js_interrupt_callback() |
317 | | void profiler_js_interrupt_callback(); |
318 | | |
319 | | // Set and clear the current thread's JSContext. |
320 | 3 | #define PROFILER_SET_JS_CONTEXT(cx) profiler_set_js_context(cx) |
321 | 0 | #define PROFILER_CLEAR_JS_CONTEXT() profiler_clear_js_context() |
322 | | void profiler_set_js_context(JSContext* aCx); |
323 | | void profiler_clear_js_context(); |
324 | | |
325 | | //--------------------------------------------------------------------------- |
326 | | // Get information from the profiler |
327 | | //--------------------------------------------------------------------------- |
328 | | |
329 | | // Is the profiler active? Note: the return value of this function can become |
330 | | // immediately out-of-date. E.g. the profile might be active but then |
331 | | // profiler_stop() is called immediately afterward. One common and reasonable |
332 | | // pattern of usage is the following: |
333 | | // |
334 | | // if (profiler_is_active()) { |
335 | | // ExpensiveData expensiveData = CreateExpensiveData(); |
336 | | // PROFILER_OPERATION(expensiveData); |
337 | | // } |
338 | | // |
339 | | // where PROFILER_OPERATION is a no-op if the profiler is inactive. In this |
340 | | // case the profiler_is_active() check is just an optimization -- it prevents |
341 | | // us calling CreateExpensiveData() unnecessarily in most cases, but the |
342 | | // expensive data will end up being created but not used if another thread |
343 | | // stops the profiler between the CreateExpensiveData() and PROFILER_OPERATION |
344 | | // calls. |
345 | | inline bool profiler_is_active() |
346 | 352 | { |
347 | 352 | return mozilla::profiler::detail::RacyFeatures::IsActive(); |
348 | 352 | } |
349 | | |
350 | | // Is the profiler active and paused? Returns false if the profiler is inactive. |
351 | | bool profiler_is_paused(); |
352 | | |
353 | | // Is the current thread sleeping? |
354 | | bool profiler_thread_is_sleeping(); |
355 | | |
356 | | // Get all the features supported by the profiler that are accepted by |
357 | | // profiler_start(). The result is the same whether the profiler is active or |
358 | | // not. |
359 | | uint32_t profiler_get_available_features(); |
360 | | |
361 | | // Check if a profiler feature (specified via the ProfilerFeature type) is |
362 | | // active. Returns false if the profiler is inactive. Note: the return value |
363 | | // can become immediately out-of-date, much like the return value of |
364 | | // profiler_is_active(). |
365 | | bool profiler_feature_active(uint32_t aFeature); |
366 | | |
367 | | // Get the params used to start the profiler. Returns 0 and an empty vector |
368 | | // (via outparams) if the profile is inactive. It's possible that the features |
369 | | // returned may be slightly different to those requested due to required |
370 | | // adjustments. |
371 | | void profiler_get_start_params(int* aEntrySize, double* aInterval, |
372 | | uint32_t* aFeatures, |
373 | | mozilla::Vector<const char*, 0, |
374 | | mozilla::MallocAllocPolicy>* |
375 | | aFilters); |
376 | | |
377 | | // The number of milliseconds since the process started. Operates the same |
378 | | // whether the profiler is active or inactive. |
379 | | double profiler_time(); |
380 | | |
381 | | // Get the current thread's ID. |
382 | | int profiler_current_thread_id(); |
383 | | |
384 | | // An object of this class is passed to profiler_suspend_and_sample_thread(). |
385 | | // For each stack frame, one of the Collect methods will be called. |
386 | | class ProfilerStackCollector |
387 | | { |
388 | | public: |
389 | | // Some collectors need to worry about possibly overwriting previous |
390 | | // generations of data. If that's not an issue, this can return Nothing, |
391 | | // which is the default behaviour. |
392 | 0 | virtual mozilla::Maybe<uint64_t> SamplePositionInBuffer() { return mozilla::Nothing(); } |
393 | 0 | virtual mozilla::Maybe<uint64_t> BufferRangeStart() { return mozilla::Nothing(); } |
394 | | |
395 | | // This method will be called once if the thread being suspended is the main |
396 | | // thread. Default behaviour is to do nothing. |
397 | 0 | virtual void SetIsMainThread() {} |
398 | | |
399 | | // WARNING: The target thread is suspended when the Collect methods are |
400 | | // called. Do not try to allocate or acquire any locks, or you could |
401 | | // deadlock. The target thread will have resumed by the time this function |
402 | | // returns. |
403 | | |
404 | | virtual void CollectNativeLeafAddr(void* aAddr) = 0; |
405 | | |
406 | | virtual void CollectJitReturnAddr(void* aAddr) = 0; |
407 | | |
408 | | virtual void CollectWasmFrame(const char* aLabel) = 0; |
409 | | |
410 | | virtual void CollectProfilingStackFrame(const js::ProfilingStackFrame& aFrame) = 0; |
411 | | }; |
412 | | |
413 | | // This method suspends the thread identified by aThreadId, samples its |
414 | | // profiling stack, JS stack, and (optionally) native stack, passing the collected |
415 | | // frames into aCollector. aFeatures dictates which compiler features are used. |
416 | | // |Privacy| and |Leaf| are the only relevant ones. |
417 | | void profiler_suspend_and_sample_thread(int aThreadId, uint32_t aFeatures, |
418 | | ProfilerStackCollector& aCollector, |
419 | | bool aSampleNative = true); |
420 | | |
421 | | struct ProfilerBacktraceDestructor |
422 | | { |
423 | | void operator()(ProfilerBacktrace*); |
424 | | }; |
425 | | |
426 | | using UniqueProfilerBacktrace = |
427 | | mozilla::UniquePtr<ProfilerBacktrace, ProfilerBacktraceDestructor>; |
428 | | |
429 | | // Immediately capture the current thread's call stack and return it. A no-op |
430 | | // if the profiler is inactive or in privacy mode. |
431 | | UniqueProfilerBacktrace profiler_get_backtrace(); |
432 | | |
433 | | struct ProfilerBufferInfo |
434 | | { |
435 | | uint64_t mRangeStart; |
436 | | uint64_t mRangeEnd; |
437 | | uint32_t mEntryCount; |
438 | | }; |
439 | | |
440 | | // Get information about the current buffer status. |
441 | | // Returns Nothing() if the profiler is inactive. |
442 | | // |
443 | | // This information may be useful to a user-interface displaying the current |
444 | | // status of the profiler, allowing the user to get a sense for how fast the |
445 | | // buffer is being written to, and how much data is visible. |
446 | | mozilla::Maybe<ProfilerBufferInfo> profiler_get_buffer_info(); |
447 | | |
448 | | //--------------------------------------------------------------------------- |
449 | | // Put profiling data into the profiler (labels and markers) |
450 | | //--------------------------------------------------------------------------- |
451 | | |
452 | | // Insert an RAII object in this scope to enter a label stack frame. Any |
453 | | // samples collected in this scope will contain this label in their stack. |
454 | | // The label argument must be a static C string. It is usually of the |
455 | | // form "ClassName::FunctionName". (Ideally we'd use the compiler to provide |
456 | | // that for us, but __func__ gives us the function name without the class |
457 | | // name.) If the label applies to only part of a function, you can qualify it |
458 | | // like this: "ClassName::FunctionName:PartName". |
459 | | // |
460 | | // Use AUTO_PROFILER_LABEL_DYNAMIC_* if you want to add additional / dynamic |
461 | | // information to the label stack frame. |
462 | | #define AUTO_PROFILER_LABEL(label, category) \ |
463 | 40 | mozilla::AutoProfilerLabel PROFILER_RAII(label, nullptr, __LINE__, \ |
464 | 40 | js::ProfilingStackFrame::Category::category) |
465 | | |
466 | | // Similar to AUTO_PROFILER_LABEL, but with an additional string. The inserted |
467 | | // RAII object stores the cStr pointer in a field; it does not copy the string. |
468 | | // |
469 | | // WARNING: This means that the string you pass to this macro needs to live at |
470 | | // least until the end of the current scope. Be careful using this macro with |
471 | | // ns[C]String; the other AUTO_PROFILER_LABEL_DYNAMIC_* macros below are |
472 | | // preferred because they avoid this problem. |
473 | | // |
474 | | // If the profiler samples the current thread and walks the label stack while |
475 | | // this RAII object is on the stack, it will copy the supplied string into the |
476 | | // profile buffer. So there's one string copy operation, and it happens at |
477 | | // sample time. |
478 | | // |
479 | | // Compare this to the plain AUTO_PROFILER_LABEL macro, which only accepts |
480 | | // literal strings: When the label stack frames generated by |
481 | | // AUTO_PROFILER_LABEL are sampled, no string copy needs to be made because the |
482 | | // profile buffer can just store the raw pointers to the literal strings. |
483 | | // Consequently, AUTO_PROFILER_LABEL frames take up considerably less space in |
484 | | // the profile buffer than AUTO_PROFILER_LABEL_DYNAMIC_* frames. |
485 | | #define AUTO_PROFILER_LABEL_DYNAMIC_CSTR(label, category, cStr) \ |
486 | 12 | mozilla::AutoProfilerLabel \ |
487 | 12 | PROFILER_RAII(label, cStr, __LINE__, js::ProfilingStackFrame::Category::category) |
488 | | |
489 | | // Similar to AUTO_PROFILER_LABEL_DYNAMIC_CSTR, but takes an nsACString. |
490 | | // |
491 | | // Note: The use of the Maybe<>s ensures the scopes for the dynamic string and |
492 | | // the AutoProfilerLabel are appropriate, while also not incurring the runtime |
493 | | // cost of the string assignment unless the profiler is active. Therefore, |
494 | | // unlike AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC_CSTR, this macro |
495 | | // doesn't push/pop a label when the profiler is inactive. |
496 | | #define AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(label, category, nsCStr) \ |
497 | 18 | mozilla::Maybe<nsAutoCString> autoCStr; \ |
498 | 18 | mozilla::Maybe<AutoProfilerLabel> raiiObjectNsCString; \ |
499 | 18 | if (profiler_is_active()) { \ |
500 | 0 | autoCStr.emplace(nsCStr); \ |
501 | 0 | raiiObjectNsCString.emplace(label, autoCStr->get(), __LINE__, \ |
502 | 0 | js::ProfilingStackFrame::Category::category); \ |
503 | 0 | } |
504 | | |
505 | | // Similar to AUTO_PROFILER_LABEL_DYNAMIC_CSTR, but takes an nsString that is |
506 | | // is lossily converted to an ASCII string. |
507 | | // |
508 | | // Note: The use of the Maybe<>s ensures the scopes for the converted dynamic |
509 | | // string and the AutoProfilerLabel are appropriate, while also not incurring |
510 | | // the runtime cost of the string conversion unless the profiler is active. |
511 | | // Therefore, unlike AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC_CSTR, |
512 | | // this macro doesn't push/pop a label when the profiler is inactive. |
513 | | #define AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(label, category, nsStr) \ |
514 | 0 | mozilla::Maybe<NS_LossyConvertUTF16toASCII> asciiStr; \ |
515 | 0 | mozilla::Maybe<AutoProfilerLabel> raiiObjectLossyNsString; \ |
516 | 0 | if (profiler_is_active()) { \ |
517 | 0 | asciiStr.emplace(nsStr); \ |
518 | 0 | raiiObjectLossyNsString.emplace(label, asciiStr->get(), __LINE__, \ |
519 | 0 | js::ProfilingStackFrame::Category::category); \ |
520 | 0 | } |
521 | | |
522 | | // Similar to AUTO_PROFILER_LABEL, but accepting a JSContext* parameter, and a |
523 | | // no-op if the profiler is disabled. |
524 | | // Used to annotate functions for which overhead in the range of nanoseconds is |
525 | | // noticeable. It avoids overhead from the TLS lookup because it can get the |
526 | | // ProfilingStack from the JS context, and avoids almost all overhead in the case |
527 | | // where the profiler is disabled. |
528 | | #define AUTO_PROFILER_LABEL_FAST(label, category, ctx) \ |
529 | 14 | mozilla::AutoProfilerLabel PROFILER_RAII(ctx, label, nullptr, __LINE__, \ |
530 | 14 | js::ProfilingStackFrame::Category::category) |
531 | | |
532 | | // Insert a marker in the profile timeline. This is useful to delimit something |
533 | | // important happening such as the first paint. Unlike labels, which are only |
534 | | // recorded in the profile buffer if a sample is collected while the label is |
535 | | // on the label stack, markers will always be recorded in the profile buffer. |
536 | | // aMarkerName is copied, so the caller does not need to ensure it lives for a |
537 | | // certain length of time. A no-op if the profiler is inactive or in privacy |
538 | | // mode. |
539 | | #define PROFILER_ADD_MARKER(markerName) \ |
540 | 3 | profiler_add_marker(markerName) |
541 | | void profiler_add_marker(const char* aMarkerName); |
542 | | void profiler_add_marker(const char* aMarkerName, |
543 | | mozilla::UniquePtr<ProfilerMarkerPayload> aPayload); |
544 | | |
545 | | // Insert a marker in the profile timeline for a specified thread. |
546 | | void profiler_add_marker_for_thread(int aThreadId, |
547 | | const char* aMarkerName, |
548 | | mozilla::UniquePtr<ProfilerMarkerPayload> aPayload); |
549 | | |
550 | | enum class NetworkLoadType { |
551 | | LOAD_START, |
552 | | LOAD_STOP, |
553 | | LOAD_REDIRECT |
554 | | }; |
555 | | |
556 | | #define PROFILER_ADD_NETWORK_MARKER(uri, pri, channel, type, start, end, count, timings, redirect) \ |
557 | 0 | profiler_add_network_marker(uri, pri, channel, type, start, end, count, timings, redirect) |
558 | | |
559 | | void profiler_add_network_marker(nsIURI* aURI, |
560 | | int32_t aPriority, |
561 | | uint64_t aChannelId, |
562 | | NetworkLoadType aType, |
563 | | mozilla::TimeStamp aStart, |
564 | | mozilla::TimeStamp aEnd, |
565 | | int64_t aCount, |
566 | | const mozilla::net::TimingStruct* aTimings = nullptr, |
567 | | nsIURI* aRedirectURI = nullptr); |
568 | | |
569 | | |
570 | | enum TracingKind { |
571 | | TRACING_EVENT, |
572 | | TRACING_INTERVAL_START, |
573 | | TRACING_INTERVAL_END, |
574 | | }; |
575 | | |
576 | | // Adds a tracing marker to the profile. A no-op if the profiler is inactive or |
577 | | // in privacy mode. |
578 | | #define PROFILER_TRACING(category, markerName, kind) \ |
579 | 0 | profiler_tracing(category, markerName, kind) |
580 | | void profiler_tracing(const char* aCategory, const char* aMarkerName, |
581 | | TracingKind aKind); |
582 | | void profiler_tracing(const char* aCategory, const char* aMarkerName, |
583 | | TracingKind aKind, UniqueProfilerBacktrace aCause); |
584 | | |
585 | | // Adds a START/END pair of tracing markers. |
586 | | #define AUTO_PROFILER_TRACING(category, markerName) \ |
587 | 0 | mozilla::AutoProfilerTracing PROFILER_RAII(category, markerName) |
588 | | |
589 | | //--------------------------------------------------------------------------- |
590 | | // Output profiles |
591 | | //--------------------------------------------------------------------------- |
592 | | |
593 | | // Get the profile encoded as a JSON string. A no-op (returning nullptr) if the |
594 | | // profiler is inactive. |
595 | | // If aIsShuttingDown is true, the current time is included as the process |
596 | | // shutdown time in the JSON's "meta" object. |
597 | | mozilla::UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0, |
598 | | bool aIsShuttingDown = false); |
599 | | |
600 | | // Write the profile for this process (excluding subprocesses) into aWriter. |
601 | | // Returns false if the profiler is inactive. |
602 | | bool profiler_stream_json_for_this_process(SpliceableJSONWriter& aWriter, |
603 | | double aSinceTime = 0, |
604 | | bool aIsShuttingDown = false); |
605 | | |
606 | | // Get the profile and write it into a file. A no-op if the profile is |
607 | | // inactive. |
608 | | // |
609 | | // This function is 'extern "C"' so that it is easily callable from a debugger |
610 | | // in a build without debugging information (a workaround for |
611 | | // http://llvm.org/bugs/show_bug.cgi?id=22211). |
612 | | extern "C" { |
613 | | void profiler_save_profile_to_file(const char* aFilename); |
614 | | } |
615 | | |
616 | | //--------------------------------------------------------------------------- |
617 | | // RAII classes |
618 | | //--------------------------------------------------------------------------- |
619 | | |
620 | | namespace mozilla { |
621 | | |
622 | | class MOZ_RAII AutoProfilerInit |
623 | | { |
624 | | public: |
625 | | explicit AutoProfilerInit(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) |
626 | 3 | { |
627 | 3 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
628 | 3 | profiler_init(this); |
629 | 3 | } |
630 | | |
631 | 0 | ~AutoProfilerInit() { profiler_shutdown(); } |
632 | | |
633 | | private: |
634 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
635 | | }; |
636 | | |
637 | | // Convenience class to register and unregister a thread with the profiler. |
638 | | // Needs to be the first object on the stack of the thread. |
639 | | class MOZ_RAII AutoProfilerRegisterThread final |
640 | | { |
641 | | public: |
642 | | explicit AutoProfilerRegisterThread(const char* aName |
643 | | MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
644 | 9 | { |
645 | 9 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
646 | 9 | profiler_register_thread(aName, this); |
647 | 9 | } |
648 | | |
649 | 0 | ~AutoProfilerRegisterThread() { profiler_unregister_thread(); } |
650 | | |
651 | | private: |
652 | | AutoProfilerRegisterThread(const AutoProfilerRegisterThread&) = delete; |
653 | | AutoProfilerRegisterThread& operator=(const AutoProfilerRegisterThread&) = |
654 | | delete; |
655 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
656 | | }; |
657 | | |
658 | | class MOZ_RAII AutoProfilerThreadSleep |
659 | | { |
660 | | public: |
661 | | explicit AutoProfilerThreadSleep(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) |
662 | 210 | { |
663 | 210 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
664 | 210 | profiler_thread_sleep(); |
665 | 210 | } |
666 | | |
667 | 200 | ~AutoProfilerThreadSleep() { profiler_thread_wake(); } |
668 | | |
669 | | private: |
670 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
671 | | }; |
672 | | |
673 | | // Temporarily wake up the profiling of a thread while servicing events such as |
674 | | // Asynchronous Procedure Calls (APCs). |
675 | | class MOZ_RAII AutoProfilerThreadWake |
676 | | { |
677 | | public: |
678 | | explicit AutoProfilerThreadWake(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) |
679 | | : mIssuedWake(profiler_thread_is_sleeping()) |
680 | 0 | { |
681 | 0 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
682 | 0 | if (mIssuedWake) { |
683 | 0 | profiler_thread_wake(); |
684 | 0 | } |
685 | 0 | } |
686 | | |
687 | | ~AutoProfilerThreadWake() |
688 | 0 | { |
689 | 0 | if (mIssuedWake) { |
690 | 0 | MOZ_ASSERT(!profiler_thread_is_sleeping()); |
691 | 0 | profiler_thread_sleep(); |
692 | 0 | } |
693 | 0 | } |
694 | | |
695 | | private: |
696 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
697 | | bool mIssuedWake; |
698 | | }; |
699 | | |
700 | | // This class creates a non-owning ProfilingStack reference. Objects of this class |
701 | | // are stack-allocated, and so exist within a thread, and are thus bounded by |
702 | | // the lifetime of the thread, which ensures that the references held can't be |
703 | | // used after the ProfilingStack is destroyed. |
704 | | class MOZ_RAII AutoProfilerLabel |
705 | | { |
706 | | public: |
707 | | // This is the AUTO_PROFILER_LABEL and AUTO_PROFILER_LABEL_DYNAMIC variant. |
708 | | AutoProfilerLabel(const char* aLabel, const char* aDynamicString, |
709 | | uint32_t aLine, js::ProfilingStackFrame::Category aCategory |
710 | | MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
711 | 1.62M | { |
712 | 1.62M | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
713 | 1.62M | |
714 | 1.62M | // Get the ProfilingStack from TLS. |
715 | 1.62M | Push(sProfilingStack.get(), aLabel, aDynamicString, aLine, aCategory); |
716 | 1.62M | } |
717 | | |
718 | | // This is the AUTO_PROFILER_LABEL_FAST variant. It's guarded on |
719 | | // profiler_is_active() and retrieves the ProfilingStack from the JSContext. |
720 | | AutoProfilerLabel(JSContext* aJSContext, |
721 | | const char* aLabel, const char* aDynamicString, |
722 | | uint32_t aLine, js::ProfilingStackFrame::Category aCategory |
723 | | MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
724 | 14 | { |
725 | 14 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
726 | 14 | if (profiler_is_active()) { |
727 | 0 | Push(js::GetContextProfilingStack(aJSContext), |
728 | 0 | aLabel, aDynamicString, aLine, aCategory); |
729 | 14 | } else { |
730 | 14 | mProfilingStack = nullptr; |
731 | 14 | } |
732 | 14 | } |
733 | | |
734 | | void Push(ProfilingStack* aProfilingStack, |
735 | | const char* aLabel, const char* aDynamicString, |
736 | | uint32_t aLine, js::ProfilingStackFrame::Category aCategory) |
737 | 1.62M | { |
738 | 1.62M | // This function runs both on and off the main thread. |
739 | 1.62M | |
740 | 1.62M | mProfilingStack = aProfilingStack; |
741 | 1.62M | if (mProfilingStack) { |
742 | 1.62M | mProfilingStack->pushLabelFrame(aLabel, aDynamicString, this, aLine, |
743 | 1.62M | aCategory); |
744 | 1.62M | } |
745 | 1.62M | } |
746 | | |
747 | | ~AutoProfilerLabel() |
748 | 1.62M | { |
749 | 1.62M | // This function runs both on and off the main thread. |
750 | 1.62M | |
751 | 1.62M | if (mProfilingStack) { |
752 | 1.62M | mProfilingStack->pop(); |
753 | 1.62M | } |
754 | 1.62M | } |
755 | | |
756 | | private: |
757 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
758 | | |
759 | | // We save a ProfilingStack pointer in the ctor so we don't have to redo the TLS |
760 | | // lookup in the dtor. |
761 | | ProfilingStack* mProfilingStack; |
762 | | |
763 | | public: |
764 | | // See the comment on the definition in platform.cpp for details about this. |
765 | | static MOZ_THREAD_LOCAL(ProfilingStack*) sProfilingStack; |
766 | | }; |
767 | | |
768 | | class MOZ_RAII AutoProfilerTracing |
769 | | { |
770 | | public: |
771 | | AutoProfilerTracing(const char* aCategory, const char* aMarkerName |
772 | | MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
773 | | : mCategory(aCategory) |
774 | | , mMarkerName(aMarkerName) |
775 | 0 | { |
776 | 0 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
777 | 0 | profiler_tracing(mCategory, mMarkerName, TRACING_INTERVAL_START); |
778 | 0 | } |
779 | | |
780 | | AutoProfilerTracing(const char* aCategory, const char* aMarkerName, |
781 | | UniqueProfilerBacktrace aBacktrace |
782 | | MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
783 | | : mCategory(aCategory) |
784 | | , mMarkerName(aMarkerName) |
785 | 0 | { |
786 | 0 | MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
787 | 0 | profiler_tracing(mCategory, mMarkerName, TRACING_INTERVAL_START, |
788 | 0 | std::move(aBacktrace)); |
789 | 0 | } |
790 | | |
791 | | ~AutoProfilerTracing() |
792 | 0 | { |
793 | 0 | profiler_tracing(mCategory, mMarkerName, TRACING_INTERVAL_END); |
794 | 0 | } |
795 | | |
796 | | protected: |
797 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
798 | | const char* mCategory; |
799 | | const char* mMarkerName; |
800 | | }; |
801 | | |
802 | | // Set MOZ_PROFILER_STARTUP* environment variables that will be inherited into |
803 | | // a child process that is about to be launched, in order to make that child |
804 | | // process start with the same profiler settings as in the current process. |
805 | | class MOZ_RAII AutoSetProfilerEnvVarsForChildProcess |
806 | | { |
807 | | public: |
808 | | explicit AutoSetProfilerEnvVarsForChildProcess(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM); |
809 | | ~AutoSetProfilerEnvVarsForChildProcess(); |
810 | | |
811 | | private: |
812 | | MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
813 | | char mSetEntries[64]; |
814 | | char mSetInterval[64]; |
815 | | char mSetFeaturesBitfield[64]; |
816 | | char mSetFilters[1024]; |
817 | | }; |
818 | | |
819 | | } // namespace mozilla |
820 | | |
821 | | #endif // !MOZ_GECKO_PROFILER |
822 | | |
823 | | #endif // GeckoProfiler_h |