Bug 1690340 - Part 4: Insert the "Page Source" before the "Extensions for Developers...
[gecko.git] / js / public / ProfilingFrameIterator.h
blobc231bc140c32fdea372b85d7eea75aba727ceb14
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/Attributes.h"
11 #include "mozilla/Maybe.h"
13 #include "js/GCAPI.h"
14 #include "js/TypeDecls.h"
15 #include "js/Utility.h"
17 namespace js {
18 class Activation;
19 namespace jit {
20 class JitActivation;
21 class JSJitProfilingFrameIterator;
22 class JitcodeGlobalEntry;
23 } // namespace jit
24 namespace wasm {
25 class ProfilingFrameIterator;
26 } // namespace wasm
27 } // namespace js
29 namespace JS {
31 // This iterator can be used to walk the stack of a thread suspended at an
32 // arbitrary pc. To provide accurate results, profiling must have been enabled
33 // (via EnableRuntimeProfilingStack) before executing the callstack being
34 // unwound.
36 // Note that the caller must not do anything that could cause GC to happen while
37 // the iterator is alive, since this could invalidate Ion code and cause its
38 // contents to become out of date.
39 class MOZ_NON_PARAM JS_PUBLIC_API ProfilingFrameIterator {
40 public:
41 enum class Kind : bool { JSJit, Wasm };
43 private:
44 JSContext* cx_;
45 mozilla::Maybe<uint64_t> samplePositionInProfilerBuffer_;
46 js::Activation* activation_;
47 Kind kind_;
49 static const unsigned StorageSpace = 8 * sizeof(void*);
50 alignas(void*) unsigned char storage_[StorageSpace];
52 void* storage() { return storage_; }
53 const void* storage() const { return storage_; }
55 js::wasm::ProfilingFrameIterator& wasmIter() {
56 MOZ_ASSERT(!done());
57 MOZ_ASSERT(isWasm());
58 return *static_cast<js::wasm::ProfilingFrameIterator*>(storage());
60 const js::wasm::ProfilingFrameIterator& wasmIter() const {
61 MOZ_ASSERT(!done());
62 MOZ_ASSERT(isWasm());
63 return *static_cast<const js::wasm::ProfilingFrameIterator*>(storage());
66 js::jit::JSJitProfilingFrameIterator& jsJitIter() {
67 MOZ_ASSERT(!done());
68 MOZ_ASSERT(isJSJit());
69 return *static_cast<js::jit::JSJitProfilingFrameIterator*>(storage());
72 const js::jit::JSJitProfilingFrameIterator& jsJitIter() const {
73 MOZ_ASSERT(!done());
74 MOZ_ASSERT(isJSJit());
75 return *static_cast<const js::jit::JSJitProfilingFrameIterator*>(storage());
78 void settleFrames();
79 void settle();
81 public:
82 struct RegisterState {
83 RegisterState() : pc(nullptr), sp(nullptr), fp(nullptr), lr(nullptr) {}
84 void* pc;
85 void* sp;
86 void* fp;
87 void* lr;
90 ProfilingFrameIterator(
91 JSContext* cx, const RegisterState& state,
92 const mozilla::Maybe<uint64_t>& samplePositionInProfilerBuffer =
93 mozilla::Nothing());
94 ~ProfilingFrameIterator();
95 void operator++();
96 bool done() const { return !activation_; }
98 // Assuming the stack grows down (we do), the return value:
99 // - always points into the stack
100 // - is weakly monotonically increasing (may be equal for successive frames)
101 // - will compare greater than newer native and psuedo-stack frame addresses
102 // and less than older native and psuedo-stack frame addresses
103 void* stackAddress() const;
105 enum FrameKind {
106 Frame_BaselineInterpreter,
107 Frame_Baseline,
108 Frame_Ion,
109 Frame_Wasm
112 struct Frame {
113 FrameKind kind;
114 void* stackAddress;
115 union {
116 void* returnAddress_;
117 jsbytecode* interpreterPC_;
119 void* activation;
120 void* endStackAddress;
121 const char* label;
122 JSScript* interpreterScript;
123 uint64_t realmID;
125 public:
126 void* returnAddress() const {
127 MOZ_ASSERT(kind != Frame_BaselineInterpreter);
128 return returnAddress_;
130 jsbytecode* interpreterPC() const {
131 MOZ_ASSERT(kind == Frame_BaselineInterpreter);
132 return interpreterPC_;
134 } JS_HAZ_GC_INVALIDATED;
136 bool isWasm() const;
137 bool isJSJit() const;
139 uint32_t extractStack(Frame* frames, uint32_t offset, uint32_t end) const;
141 mozilla::Maybe<Frame> getPhysicalFrameWithoutLabel() const;
143 private:
144 mozilla::Maybe<Frame> getPhysicalFrameAndEntry(
145 js::jit::JitcodeGlobalEntry* entry) const;
147 void iteratorConstruct(const RegisterState& state);
148 void iteratorConstruct();
149 void iteratorDestroy();
150 bool iteratorDone();
151 } JS_HAZ_GC_INVALIDATED;
153 JS_FRIEND_API bool IsProfilingEnabledForContext(JSContext* cx);
156 * After each sample run, this method should be called with the current buffer
157 * position at which the buffer contents start. This will update the
158 * corresponding field on the JSRuntime.
160 * See the field |profilerSampleBufferRangeStart| on JSRuntime for documentation
161 * about what this value is used for.
163 JS_FRIEND_API void SetJSContextProfilerSampleBufferRangeStart(
164 JSContext* cx, uint64_t rangeStart);
166 class ProfiledFrameRange;
168 // A handle to the underlying JitcodeGlobalEntry, so as to avoid repeated
169 // lookups on JitcodeGlobalTable.
170 class MOZ_STACK_CLASS ProfiledFrameHandle {
171 friend class ProfiledFrameRange;
173 JSRuntime* rt_;
174 js::jit::JitcodeGlobalEntry& entry_;
175 void* addr_;
176 void* canonicalAddr_;
177 const char* label_;
178 uint32_t depth_;
180 ProfiledFrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry,
181 void* addr, const char* label, uint32_t depth);
183 public:
184 const char* label() const { return label_; }
185 uint32_t depth() const { return depth_; }
186 void* canonicalAddress() const { return canonicalAddr_; }
188 JS_PUBLIC_API ProfilingFrameIterator::FrameKind frameKind() const;
190 JS_PUBLIC_API uint64_t realmID() const;
193 class ProfiledFrameRange {
194 public:
195 class Iter final {
196 public:
197 Iter(const ProfiledFrameRange& range, uint32_t index)
198 : range_(range), index_(index) {}
200 JS_PUBLIC_API ProfiledFrameHandle operator*() const;
202 // Provide the bare minimum of iterator methods that are needed for
203 // C++ ranged for loops.
204 Iter& operator++() {
205 ++index_;
206 return *this;
208 bool operator==(const Iter& rhs) { return index_ == rhs.index_; }
209 bool operator!=(const Iter& rhs) { return !(*this == rhs); }
211 private:
212 const ProfiledFrameRange& range_;
213 uint32_t index_;
216 Iter begin() const { return Iter(*this, 0); }
217 Iter end() const { return Iter(*this, depth_); }
219 private:
220 friend JS_PUBLIC_API ProfiledFrameRange GetProfiledFrames(JSContext* cx,
221 void* addr);
223 ProfiledFrameRange(JSRuntime* rt, void* addr,
224 js::jit::JitcodeGlobalEntry* entry)
225 : rt_(rt), addr_(addr), entry_(entry), depth_(0) {}
227 JSRuntime* rt_;
228 void* addr_;
229 js::jit::JitcodeGlobalEntry* entry_;
230 // Assume maximum inlining depth is <64
231 const char* labels_[64];
232 uint32_t depth_;
235 // Returns a range that can be iterated over using C++ ranged for loops.
236 JS_PUBLIC_API ProfiledFrameRange GetProfiledFrames(JSContext* cx, void* addr);
238 } // namespace JS
240 #endif /* js_ProfilingFrameIterator_h */