1 /* -*- Mode: C++; tab-width: 8; 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/. */
7 #ifndef vm_GeckoProfiler_h
8 #define vm_GeckoProfiler_h
10 #include "mozilla/Attributes.h"
11 #include "mozilla/DebugOnly.h"
18 #include "js/AllocPolicy.h"
19 #include "js/HashTable.h"
20 #include "js/ProfilingCategory.h"
21 #include "js/TypeDecls.h"
22 #include "js/Utility.h"
23 #include "threading/ProtectedData.h"
26 * Gecko Profiler integration with the JS Engine
27 * https://developer.mozilla.org/en/Performance/Profiling_with_the_Built-in_Profiler
29 * The Gecko Profiler (found in tools/profiler) is an implementation of a
30 * profiler which has the ability to walk the C++ stack as well as use
31 * instrumentation to gather information. When dealing with JS, however, the
32 * profiler needs integration with the engine because otherwise it is very
33 * difficult to figure out what javascript is executing.
35 * The current method of integration with the profiler is a form of
36 * instrumentation: every time a JS function is entered, a bit of information
37 * is pushed onto a stack that the profiler owns and maintains. This
38 * information is then popped at the end of the JS function. The profiler
39 * informs the JS engine of this stack at runtime, and it can by turned on/off
40 * dynamically. Each stack frame has type ProfilingStackFrame.
42 * Throughout execution, the size of the stack recorded in memory may exceed the
43 * maximum. The JS engine will not write any information past the maximum limit,
44 * but it will still maintain the size of the stack. Profiler code is aware of
45 * this and iterates the stack accordingly.
47 * There is some information pushed on the profiler stack for every JS function
48 * that is entered. First is a char* label with a description of what function
49 * was entered. Currently this string is of the form "function (file:line)" if
50 * there's a function name, or just "file:line" if there's no function name
51 * available. The other bit of information is the relevant C++ (native) stack
52 * pointer. This stack pointer is what enables the interleaving of the C++ and
53 * the JS stack. Finally, throughout execution of the function, some extra
54 * information may be updated on the ProfilingStackFrame structure.
58 * The profile strings' allocations and deallocation must be carefully
59 * maintained, and ideally at a very low overhead cost. For this reason, the JS
60 * engine maintains a mapping of all known profile strings. These strings are
61 * keyed in lookup by a JSScript*, but are serialized with a JSFunction*,
62 * JSScript* pair. A JSScript will destroy its corresponding profile string when
63 * the script is finalized.
65 * For this reason, a char* pointer pushed on the profiler stack is valid only
66 * while it is on the profiler stack. The profiler uses sampling to read off
67 * information from this instrumented stack, and it therefore copies the string
68 * byte for byte when a JS function is encountered during sampling.
70 * = Native Stack Pointer
72 * The actual value pushed as the native pointer is nullptr for most JS
73 * functions. The reason for this is that there's actually very little
74 * correlation between the JS stack and the C++ stack because many JS functions
75 * all run in the same C++ frame, or can even go backwards in C++ when going
76 * from the JIT back to the interpreter.
78 * To alleviate this problem, all JS functions push nullptr as their "native
79 * stack pointer" to indicate that it's a JS function call. The function
80 * RunScript(), however, pushes an actual C++ stack pointer onto the profiler
81 * stack. This way when interleaving C++ and JS, if the Gecko Profiler sees a
82 * nullptr native stack pointer on the profiler stack, it looks backwards for
83 * the first non-nullptr pointer and uses that for all subsequent nullptr
84 * native stack pointers.
88 * One goal of sampling is to get both a backtrace of the JS stack, but also
89 * know where within each function on the stack execution currently is. For
90 * this, each ProfilingStackFrame has a 'pc' field to tell where its execution
91 * currently is. This field is updated whenever a call is made to another JS
92 * function, and for the JIT it is also updated whenever the JIT is left.
94 * This field is in a union with a uint32_t 'line' so that C++ can make use of
95 * the field as well. It was observed that tracking 'line' via PCToLineNumber in
96 * JS was far too expensive, so that is why the pc instead of the translated
97 * line number is stored.
99 * As an invariant, if the pc is nullptr, then the JIT is currently executing
100 * generated code. Otherwise execution is in another JS function or in C++. With
101 * this in place, only the top frame of the stack can ever have nullptr as its
102 * pc. Additionally with this invariant, it is possible to maintain mappings of
103 * JIT code to pc which can be accessed safely because they will only be
104 * accessed from a signal handler when the JIT code is executing.
107 class JS_PUBLIC_API ProfilingStack
;
112 class GeckoProfilerThread
;
114 // The `ProfileStringMap` weakly holds its `BaseScript*` keys and owns its
115 // string values. Entries are removed when the `BaseScript` is finalized; see
116 // `GeckoProfiler::onScriptFinalized`.
117 using ProfileStringMap
= HashMap
<BaseScript
*, JS::UniqueChars
,
118 DefaultHasher
<BaseScript
*>, SystemAllocPolicy
>;
120 class GeckoProfilerRuntime
{
122 MainThreadData
<ProfileStringMap
> strings_
;
125 void (*eventMarker_
)(const char*, const char*);
128 explicit GeckoProfilerRuntime(JSRuntime
* rt
);
130 /* management of whether instrumentation is on or off */
131 bool enabled() { return enabled_
; }
132 void enable(bool enabled
);
133 void enableSlowAssertions(bool enabled
) { slowAssertions
= enabled
; }
134 bool slowAssertionsEnabled() { return slowAssertions
; }
136 void setEventMarker(void (*fn
)(const char*, const char*));
138 static JS::UniqueChars
allocProfileString(JSContext
* cx
, BaseScript
* script
);
139 const char* profileString(JSContext
* cx
, BaseScript
* script
);
141 void onScriptFinalized(BaseScript
* script
);
143 void markEvent(const char* event
, const char* details
);
145 ProfileStringMap
& strings() { return strings_
.ref(); }
147 /* meant to be used for testing, not recommended to call in normal code */
148 size_t stringsCount();
151 uint32_t* addressOfEnabled() { return &enabled_
; }
153 void fixupStringsMapAfterMovingGC();
154 #ifdef JSGC_HASH_TABLE_CHECKS
155 void checkStringsMapAfterMovingGC();
159 inline size_t GeckoProfilerRuntime::stringsCount() { return strings().count(); }
161 inline void GeckoProfilerRuntime::stringsReset() { strings().clear(); }
164 * This class is used in RunScript() to push the marker onto the sampling stack
165 * that we're about to enter JS function calls. This is the only time in which a
166 * valid stack pointer is pushed to the sampling stack.
168 class MOZ_RAII GeckoProfilerEntryMarker
{
170 explicit MOZ_ALWAYS_INLINE
GeckoProfilerEntryMarker(JSContext
* cx
,
172 MOZ_ALWAYS_INLINE
~GeckoProfilerEntryMarker();
175 GeckoProfilerThread
* profiler_
;
182 * RAII class to automatically add Gecko Profiler profiling stack frames.
183 * It retrieves the ProfilingStack from the JSContext and does nothing if the
184 * profiler is inactive.
186 * NB: The `label` string must be statically allocated.
188 class MOZ_RAII AutoGeckoProfilerEntry
{
190 explicit MOZ_ALWAYS_INLINE
AutoGeckoProfilerEntry(
191 JSContext
* cx
, const char* label
, const char* dynamicString
,
192 JS::ProfilingCategoryPair categoryPair
= JS::ProfilingCategoryPair::JS
,
194 explicit MOZ_ALWAYS_INLINE
AutoGeckoProfilerEntry(
195 JSContext
* cx
, const char* label
,
196 JS::ProfilingCategoryPair categoryPair
= JS::ProfilingCategoryPair::JS
,
198 MOZ_ALWAYS_INLINE
~AutoGeckoProfilerEntry();
201 ProfilingStack
* profilingStack_
;
203 GeckoProfilerThread
* profiler_
;
209 * Use this RAII class to add Gecko Profiler label frames for methods of the
210 * JavaScript builtin API.
211 * These frames will be exposed to JavaScript developers (ie they won't be
212 * filtered out when using the "JavaScript" filtering option in the Firefox
214 * Technical note: the label and dynamicString values will be joined with a dot
215 * separator if dynamicString is present.
217 class MOZ_RAII AutoJSMethodProfilerEntry
: public AutoGeckoProfilerEntry
{
219 explicit MOZ_ALWAYS_INLINE
AutoJSMethodProfilerEntry(
220 JSContext
* cx
, const char* label
, const char* dynamicString
= nullptr);
224 * Use this RAII class to add Gecko Profiler label frames for constructors of
225 * the JavaScript builtin API.
226 * These frames will be exposed to JavaScript developers (ie they won't be
227 * filtered out when using the "JavaScript" filtering option in the Firefox
229 * Technical note: the word "constructor" will be appended to the label (with a
232 class MOZ_RAII AutoJSConstructorProfilerEntry
: public AutoGeckoProfilerEntry
{
234 explicit MOZ_ALWAYS_INLINE
AutoJSConstructorProfilerEntry(JSContext
* cx
,
239 * This class is used in the interpreter to bound regions where the baseline JIT
240 * being entered via OSR. It marks the current top profiling stack frame as
243 class MOZ_RAII GeckoProfilerBaselineOSRMarker
{
245 explicit GeckoProfilerBaselineOSRMarker(JSContext
* cx
, bool hasProfilerFrame
);
246 ~GeckoProfilerBaselineOSRMarker();
249 GeckoProfilerThread
* profiler
;
250 mozilla::DebugOnly
<uint32_t> spBefore_
;
255 #endif /* vm_GeckoProfiler_h */