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 js_ProfilingFrameIterator_h
8 #define js_ProfilingFrameIterator_h
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/Maybe.h"
16 #include "js/GCAnnotations.h"
17 #include "js/ProfilingCategory.h"
18 #include "js/TypeDecls.h"
24 class JSJitProfilingFrameIterator
;
25 class JitcodeGlobalEntry
;
28 class ProfilingFrameIterator
;
34 // This iterator can be used to walk the stack of a thread suspended at an
35 // arbitrary pc. To provide accurate results, profiling must have been enabled
36 // (via EnableRuntimeProfilingStack) before executing the callstack being
39 // Note that the caller must not do anything that could cause GC to happen while
40 // the iterator is alive, since this could invalidate Ion code and cause its
41 // contents to become out of date.
42 class MOZ_NON_PARAM JS_PUBLIC_API ProfilingFrameIterator
{
44 enum class Kind
: bool { JSJit
, Wasm
};
48 mozilla::Maybe
<uint64_t> samplePositionInProfilerBuffer_
;
49 js::Activation
* activation_
;
50 // For each JitActivation, this records the lowest (most recent) stack
51 // address. This will usually be either the exitFP of the activation or the
52 // frame or stack pointer of currently executing JIT/Wasm code. The Gecko
53 // profiler uses this to skip native frames between the activation and
55 void* endStackAddress_
= nullptr;
58 static const unsigned StorageSpace
= 8 * sizeof(void*);
59 alignas(void*) unsigned char storage_
[StorageSpace
];
61 void* storage() { return storage_
; }
62 const void* storage() const { return storage_
; }
64 js::wasm::ProfilingFrameIterator
& wasmIter() {
67 return *static_cast<js::wasm::ProfilingFrameIterator
*>(storage());
69 const js::wasm::ProfilingFrameIterator
& wasmIter() const {
72 return *static_cast<const js::wasm::ProfilingFrameIterator
*>(storage());
75 js::jit::JSJitProfilingFrameIterator
& jsJitIter() {
77 MOZ_ASSERT(isJSJit());
78 return *static_cast<js::jit::JSJitProfilingFrameIterator
*>(storage());
81 const js::jit::JSJitProfilingFrameIterator
& jsJitIter() const {
83 MOZ_ASSERT(isJSJit());
84 return *static_cast<const js::jit::JSJitProfilingFrameIterator
*>(storage());
87 void maybeSetEndStackAddress(void* addr
) {
88 // If endStackAddress_ has already been set, don't change it because we
89 // want this to correspond to the most recent frame.
90 if (!endStackAddress_
) {
91 endStackAddress_
= addr
;
99 struct RegisterState
{
110 // Value of the LR register on ARM platforms.
112 // The return address during a tail call operation.
113 // Note that for ARM is still the value of LR register.
115 // Undefined on non-ARM plaforms outside tail calls operations.
119 // The FP reference during a tail call operation.
121 // Undefined outside tail calls operations.
126 ProfilingFrameIterator(
127 JSContext
* cx
, const RegisterState
& state
,
128 const mozilla::Maybe
<uint64_t>& samplePositionInProfilerBuffer
=
130 ~ProfilingFrameIterator();
132 bool done() const { return !activation_
; }
134 // Assuming the stack grows down (we do), the return value:
135 // - always points into the stack
136 // - is weakly monotonically increasing (may be equal for successive frames)
137 // - will compare greater than newer native and psuedo-stack frame addresses
138 // and less than older native and psuedo-stack frame addresses
139 void* stackAddress() const;
142 Frame_BaselineInterpreter
,
154 void* returnAddress_
;
155 jsbytecode
* interpreterPC_
;
158 void* endStackAddress
;
160 JSScript
* interpreterScript
;
164 void* returnAddress() const {
165 MOZ_ASSERT(kind
!= Frame_BaselineInterpreter
);
166 return returnAddress_
;
168 jsbytecode
* interpreterPC() const {
169 MOZ_ASSERT(kind
== Frame_BaselineInterpreter
);
170 return interpreterPC_
;
172 ProfilingCategoryPair
profilingCategory() const {
174 case FrameKind::Frame_BaselineInterpreter
:
175 return JS::ProfilingCategoryPair::JS_BaselineInterpret
;
176 case FrameKind::Frame_Baseline
:
177 return JS::ProfilingCategoryPair::JS_Baseline
;
178 case FrameKind::Frame_Ion
:
179 return JS::ProfilingCategoryPair::JS_IonMonkey
;
180 case FrameKind::Frame_WasmBaseline
:
181 return JS::ProfilingCategoryPair::JS_WasmBaseline
;
182 case FrameKind::Frame_WasmIon
:
183 return JS::ProfilingCategoryPair::JS_WasmIon
;
184 case FrameKind::Frame_WasmOther
:
185 return JS::ProfilingCategoryPair::JS_WasmOther
;
189 } JS_HAZ_GC_INVALIDATED
;
192 bool isJSJit() const;
194 uint32_t extractStack(Frame
* frames
, uint32_t offset
, uint32_t end
) const;
196 mozilla::Maybe
<Frame
> getPhysicalFrameWithoutLabel() const;
198 // Return the registers from the native caller frame.
199 // Nothing{} if this iterator is NOT pointing at a native-to-JIT entry frame,
200 // or if the information is not accessible/implemented on this platform.
201 mozilla::Maybe
<RegisterState
> getCppEntryRegisters() const;
204 mozilla::Maybe
<Frame
> getPhysicalFrameAndEntry(
205 const js::jit::JitcodeGlobalEntry
** entry
) const;
207 void iteratorConstruct(const RegisterState
& state
);
208 void iteratorConstruct();
209 void iteratorDestroy();
211 } JS_HAZ_GC_INVALIDATED
;
213 JS_PUBLIC_API
bool IsProfilingEnabledForContext(JSContext
* cx
);
216 * After each sample run, this method should be called with the current buffer
217 * position at which the buffer contents start. This will update the
218 * corresponding field on the JSRuntime.
220 * See the field |profilerSampleBufferRangeStart| on JSRuntime for documentation
221 * about what this value is used for.
223 JS_PUBLIC_API
void SetJSContextProfilerSampleBufferRangeStart(
224 JSContext
* cx
, uint64_t rangeStart
);
226 class ProfiledFrameRange
;
228 // A handle to the underlying JitcodeGlobalEntry, so as to avoid repeated
229 // lookups on JitcodeGlobalTable.
230 class MOZ_STACK_CLASS ProfiledFrameHandle
{
231 friend class ProfiledFrameRange
;
234 js::jit::JitcodeGlobalEntry
& entry_
;
236 void* canonicalAddr_
;
240 ProfiledFrameHandle(JSRuntime
* rt
, js::jit::JitcodeGlobalEntry
& entry
,
241 void* addr
, const char* label
, uint32_t depth
);
244 const char* label() const { return label_
; }
245 uint32_t depth() const { return depth_
; }
246 void* canonicalAddress() const { return canonicalAddr_
; }
248 JS_PUBLIC_API
ProfilingFrameIterator::FrameKind
frameKind() const;
250 JS_PUBLIC_API
uint64_t realmID() const;
253 class ProfiledFrameRange
{
257 Iter(const ProfiledFrameRange
& range
, uint32_t index
)
258 : range_(range
), index_(index
) {}
260 JS_PUBLIC_API ProfiledFrameHandle
operator*() const;
262 // Provide the bare minimum of iterator methods that are needed for
263 // C++ ranged for loops.
268 bool operator==(const Iter
& rhs
) const { return index_
== rhs
.index_
; }
269 bool operator!=(const Iter
& rhs
) const { return !(*this == rhs
); }
272 const ProfiledFrameRange
& range_
;
276 Iter
begin() const { return Iter(*this, 0); }
277 Iter
end() const { return Iter(*this, depth_
); }
280 friend JS_PUBLIC_API ProfiledFrameRange
GetProfiledFrames(JSContext
* cx
,
283 ProfiledFrameRange(JSRuntime
* rt
, void* addr
,
284 js::jit::JitcodeGlobalEntry
* entry
)
285 : rt_(rt
), addr_(addr
), entry_(entry
), depth_(0) {}
289 js::jit::JitcodeGlobalEntry
* entry_
;
290 // Assume maximum inlining depth is <64
291 const char* labels_
[64];
295 // Returns a range that can be iterated over using C++ ranged for loops.
296 JS_PUBLIC_API ProfiledFrameRange
GetProfiledFrames(JSContext
* cx
, void* addr
);
300 #endif /* js_ProfilingFrameIterator_h */