Bug 1874684 - Part 25: Editorial updates. r=dminor
[gecko.git] / js / public / ProfilingFrameIterator.h
blobbbad2b1244cebcd724ec154dedae11bcbe3bfe5a
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"
14 #include "jstypes.h"
16 #include "js/GCAnnotations.h"
17 #include "js/ProfilingCategory.h"
18 #include "js/TypeDecls.h"
20 namespace js {
21 class Activation;
22 namespace jit {
23 class JitActivation;
24 class JSJitProfilingFrameIterator;
25 class JitcodeGlobalEntry;
26 } // namespace jit
27 namespace wasm {
28 class ProfilingFrameIterator;
29 } // namespace wasm
30 } // namespace js
32 namespace JS {
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
37 // unwound.
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 {
43 public:
44 enum class Kind : bool { JSJit, Wasm };
46 private:
47 JSContext* cx_;
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
54 // endStackAddress_.
55 void* endStackAddress_ = nullptr;
56 Kind kind_;
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() {
65 MOZ_ASSERT(!done());
66 MOZ_ASSERT(isWasm());
67 return *static_cast<js::wasm::ProfilingFrameIterator*>(storage());
69 const js::wasm::ProfilingFrameIterator& wasmIter() const {
70 MOZ_ASSERT(!done());
71 MOZ_ASSERT(isWasm());
72 return *static_cast<const js::wasm::ProfilingFrameIterator*>(storage());
75 js::jit::JSJitProfilingFrameIterator& jsJitIter() {
76 MOZ_ASSERT(!done());
77 MOZ_ASSERT(isJSJit());
78 return *static_cast<js::jit::JSJitProfilingFrameIterator*>(storage());
81 const js::jit::JSJitProfilingFrameIterator& jsJitIter() const {
82 MOZ_ASSERT(!done());
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;
95 void settleFrames();
96 void settle();
98 public:
99 struct RegisterState {
100 RegisterState()
101 : pc(nullptr),
102 sp(nullptr),
103 fp(nullptr),
104 unused1(nullptr),
105 unused2(nullptr) {}
106 void* pc;
107 void* sp;
108 void* fp;
109 union {
110 // Value of the LR register on ARM platforms.
111 void* lr;
112 // The return address during a tail call operation.
113 // Note that for ARM is still the value of LR register.
114 void* tempRA;
115 // Undefined on non-ARM plaforms outside tail calls operations.
116 void* unused1;
118 union {
119 // The FP reference during a tail call operation.
120 void* tempFP;
121 // Undefined outside tail calls operations.
122 void* unused2;
126 ProfilingFrameIterator(
127 JSContext* cx, const RegisterState& state,
128 const mozilla::Maybe<uint64_t>& samplePositionInProfilerBuffer =
129 mozilla::Nothing());
130 ~ProfilingFrameIterator();
131 void operator++();
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;
141 enum FrameKind {
142 Frame_BaselineInterpreter,
143 Frame_Baseline,
144 Frame_Ion,
145 Frame_WasmBaseline,
146 Frame_WasmIon,
147 Frame_WasmOther,
150 struct Frame {
151 FrameKind kind;
152 void* stackAddress;
153 union {
154 void* returnAddress_;
155 jsbytecode* interpreterPC_;
157 void* activation;
158 void* endStackAddress;
159 const char* label;
160 JSScript* interpreterScript;
161 uint64_t realmID;
163 public:
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 {
173 switch (kind) {
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;
187 MOZ_CRASH();
189 } JS_HAZ_GC_INVALIDATED;
191 bool isWasm() const;
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;
203 private:
204 mozilla::Maybe<Frame> getPhysicalFrameAndEntry(
205 const js::jit::JitcodeGlobalEntry** entry) const;
207 void iteratorConstruct(const RegisterState& state);
208 void iteratorConstruct();
209 void iteratorDestroy();
210 bool iteratorDone();
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;
233 JSRuntime* rt_;
234 js::jit::JitcodeGlobalEntry& entry_;
235 void* addr_;
236 void* canonicalAddr_;
237 const char* label_;
238 uint32_t depth_;
240 ProfiledFrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry,
241 void* addr, const char* label, uint32_t depth);
243 public:
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 {
254 public:
255 class Iter final {
256 public:
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.
264 Iter& operator++() {
265 ++index_;
266 return *this;
268 bool operator==(const Iter& rhs) const { return index_ == rhs.index_; }
269 bool operator!=(const Iter& rhs) const { return !(*this == rhs); }
271 private:
272 const ProfiledFrameRange& range_;
273 uint32_t index_;
276 Iter begin() const { return Iter(*this, 0); }
277 Iter end() const { return Iter(*this, depth_); }
279 private:
280 friend JS_PUBLIC_API ProfiledFrameRange GetProfiledFrames(JSContext* cx,
281 void* addr);
283 ProfiledFrameRange(JSRuntime* rt, void* addr,
284 js::jit::JitcodeGlobalEntry* entry)
285 : rt_(rt), addr_(addr), entry_(entry), depth_(0) {}
287 JSRuntime* rt_;
288 void* addr_;
289 js::jit::JitcodeGlobalEntry* entry_;
290 // Assume maximum inlining depth is <64
291 const char* labels_[64];
292 uint32_t depth_;
295 // Returns a range that can be iterated over using C++ ranged for loops.
296 JS_PUBLIC_API ProfiledFrameRange GetProfiledFrames(JSContext* cx, void* addr);
298 } // namespace JS
300 #endif /* js_ProfilingFrameIterator_h */