/work/obj-fuzz/dist/include/js/ProfilingFrameIterator.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 | | * vim: set ts=8 sts=4 et sw=4 tw=99: |
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 | | #ifndef js_ProfilingFrameIterator_h |
8 | | #define js_ProfilingFrameIterator_h |
9 | | |
10 | | #include "mozilla/Attributes.h" |
11 | | #include "mozilla/Maybe.h" |
12 | | |
13 | | #include "js/GCAPI.h" |
14 | | #include "js/TypeDecls.h" |
15 | | #include "js/Utility.h" |
16 | | |
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 |
28 | | |
29 | | namespace JS { |
30 | | |
31 | | struct ForEachTrackedOptimizationAttemptOp; |
32 | | struct ForEachTrackedOptimizationTypeInfoOp; |
33 | | |
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. |
38 | | // |
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 | | { |
44 | | public: |
45 | | enum class Kind : bool { |
46 | | JSJit, |
47 | | Wasm |
48 | | }; |
49 | | |
50 | | private: |
51 | | JSContext* cx_; |
52 | | mozilla::Maybe<uint64_t> samplePositionInProfilerBuffer_; |
53 | | js::Activation* activation_; |
54 | | Kind kind_; |
55 | | |
56 | | static const unsigned StorageSpace = 8 * sizeof(void*); |
57 | | alignas(void*) unsigned char storage_[StorageSpace]; |
58 | | |
59 | 0 | void* storage() { return storage_; } |
60 | 0 | const void* storage() const { return storage_; } |
61 | | |
62 | 0 | js::wasm::ProfilingFrameIterator& wasmIter() { |
63 | 0 | MOZ_ASSERT(!done()); |
64 | 0 | MOZ_ASSERT(isWasm()); |
65 | 0 | return *static_cast<js::wasm::ProfilingFrameIterator*>(storage()); |
66 | 0 | } |
67 | 0 | const js::wasm::ProfilingFrameIterator& wasmIter() const { |
68 | 0 | MOZ_ASSERT(!done()); |
69 | 0 | MOZ_ASSERT(isWasm()); |
70 | 0 | return *static_cast<const js::wasm::ProfilingFrameIterator*>(storage()); |
71 | 0 | } |
72 | | |
73 | 0 | js::jit::JSJitProfilingFrameIterator& jsJitIter() { |
74 | 0 | MOZ_ASSERT(!done()); |
75 | 0 | MOZ_ASSERT(isJSJit()); |
76 | 0 | return *static_cast<js::jit::JSJitProfilingFrameIterator*>(storage()); |
77 | 0 | } |
78 | | |
79 | 0 | const js::jit::JSJitProfilingFrameIterator& jsJitIter() const { |
80 | 0 | MOZ_ASSERT(!done()); |
81 | 0 | MOZ_ASSERT(isJSJit()); |
82 | 0 | return *static_cast<const js::jit::JSJitProfilingFrameIterator*>(storage()); |
83 | 0 | } |
84 | | |
85 | | void settleFrames(); |
86 | | void settle(); |
87 | | |
88 | | public: |
89 | | struct RegisterState |
90 | | { |
91 | 0 | RegisterState() : pc(nullptr), sp(nullptr), fp(nullptr), lr(nullptr) {} |
92 | | void* pc; |
93 | | void* sp; |
94 | | void* fp; |
95 | | void* lr; |
96 | | }; |
97 | | |
98 | | ProfilingFrameIterator(JSContext* cx, const RegisterState& state, |
99 | | const mozilla::Maybe<uint64_t>& samplePositionInProfilerBuffer = mozilla::Nothing()); |
100 | | ~ProfilingFrameIterator(); |
101 | | void operator++(); |
102 | 0 | bool done() const { return !activation_; } |
103 | | |
104 | | // Assuming the stack grows down (we do), the return value: |
105 | | // - always points into the stack |
106 | | // - is weakly monotonically increasing (may be equal for successive frames) |
107 | | // - will compare greater than newer native and psuedo-stack frame addresses |
108 | | // and less than older native and psuedo-stack frame addresses |
109 | | void* stackAddress() const; |
110 | | |
111 | | enum FrameKind |
112 | | { |
113 | | Frame_Baseline, |
114 | | Frame_Ion, |
115 | | Frame_Wasm |
116 | | }; |
117 | | |
118 | | struct Frame |
119 | | { |
120 | | FrameKind kind; |
121 | | void* stackAddress; |
122 | | void* returnAddress; |
123 | | void* activation; |
124 | | void* endStackAddress; |
125 | | const char* label; |
126 | | } JS_HAZ_GC_INVALIDATED; |
127 | | |
128 | | bool isWasm() const; |
129 | | bool isJSJit() const; |
130 | | |
131 | | uint32_t extractStack(Frame* frames, uint32_t offset, uint32_t end) const; |
132 | | |
133 | | mozilla::Maybe<Frame> getPhysicalFrameWithoutLabel() const; |
134 | | |
135 | | private: |
136 | | mozilla::Maybe<Frame> getPhysicalFrameAndEntry(js::jit::JitcodeGlobalEntry* entry) const; |
137 | | |
138 | | void iteratorConstruct(const RegisterState& state); |
139 | | void iteratorConstruct(); |
140 | | void iteratorDestroy(); |
141 | | bool iteratorDone(); |
142 | | } JS_HAZ_GC_INVALIDATED; |
143 | | |
144 | | JS_FRIEND_API(bool) |
145 | | IsProfilingEnabledForContext(JSContext* cx); |
146 | | |
147 | | /** |
148 | | * After each sample run, this method should be called with the current buffer |
149 | | * position at which the buffer contents start. This will update the |
150 | | * corresponding field on the JSRuntime. |
151 | | * |
152 | | * See the field |profilerSampleBufferRangeStart| on JSRuntime for documentation |
153 | | * about what this value is used for. |
154 | | */ |
155 | | JS_FRIEND_API(void) |
156 | | SetJSContextProfilerSampleBufferRangeStart(JSContext* cx, uint64_t rangeStart); |
157 | | |
158 | | class ProfiledFrameRange; |
159 | | |
160 | | // A handle to the underlying JitcodeGlobalEntry, so as to avoid repeated |
161 | | // lookups on JitcodeGlobalTable. |
162 | | class MOZ_STACK_CLASS ProfiledFrameHandle |
163 | | { |
164 | | friend class ProfiledFrameRange; |
165 | | |
166 | | JSRuntime* rt_; |
167 | | js::jit::JitcodeGlobalEntry& entry_; |
168 | | void* addr_; |
169 | | void* canonicalAddr_; |
170 | | const char* label_; |
171 | | uint32_t depth_; |
172 | | mozilla::Maybe<uint8_t> optsIndex_; |
173 | | |
174 | | ProfiledFrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry, |
175 | | void* addr, const char* label, uint32_t depth); |
176 | | |
177 | | void updateHasTrackedOptimizations(); |
178 | | |
179 | | public: |
180 | 0 | const char* label() const { return label_; } |
181 | 0 | uint32_t depth() const { return depth_; } |
182 | 0 | bool hasTrackedOptimizations() const { return optsIndex_.isSome(); } |
183 | 0 | void* canonicalAddress() const { return canonicalAddr_; } |
184 | | |
185 | | JS_PUBLIC_API(ProfilingFrameIterator::FrameKind) frameKind() const; |
186 | | JS_PUBLIC_API(void) forEachOptimizationAttempt(ForEachTrackedOptimizationAttemptOp& op, |
187 | | JSScript** scriptOut, |
188 | | jsbytecode** pcOut) const; |
189 | | |
190 | | JS_PUBLIC_API(void) |
191 | | forEachOptimizationTypeInfo(ForEachTrackedOptimizationTypeInfoOp& op) const; |
192 | | }; |
193 | | |
194 | | class ProfiledFrameRange |
195 | | { |
196 | | public: |
197 | | class Iter final |
198 | | { |
199 | | public: |
200 | | Iter(const ProfiledFrameRange& range, uint32_t index) |
201 | | : range_(range) |
202 | | , index_(index) |
203 | 0 | {} |
204 | | |
205 | | JS_PUBLIC_API(ProfiledFrameHandle) operator*() const; |
206 | | |
207 | | // Provide the bare minimum of iterator methods that are needed for |
208 | | // C++ ranged for loops. |
209 | 0 | Iter& operator++() { ++index_; return *this; } |
210 | 0 | bool operator==(const Iter& rhs) { return index_ == rhs.index_; } |
211 | 0 | bool operator!=(const Iter& rhs) { return !(*this == rhs); } |
212 | | |
213 | | private: |
214 | | const ProfiledFrameRange& range_; |
215 | | uint32_t index_; |
216 | | }; |
217 | | |
218 | 0 | Iter begin() const { return Iter(*this, 0); } |
219 | 0 | Iter end() const { return Iter(*this, depth_); } |
220 | | |
221 | | private: |
222 | | friend JS_PUBLIC_API(ProfiledFrameRange) GetProfiledFrames(JSContext* cx, |
223 | | void* addr); |
224 | | |
225 | | ProfiledFrameRange(JSRuntime* rt, void* addr, js::jit::JitcodeGlobalEntry* entry) |
226 | | : rt_(rt) |
227 | | , addr_(addr) |
228 | | , entry_(entry) |
229 | | , depth_(0) |
230 | 0 | {} |
231 | | |
232 | | JSRuntime* rt_; |
233 | | void* addr_; |
234 | | js::jit::JitcodeGlobalEntry* entry_; |
235 | | // Assume maximum inlining depth is <64 |
236 | | const char* labels_[64]; |
237 | | uint32_t depth_; |
238 | | }; |
239 | | |
240 | | // Returns a range that can be iterated over using C++ ranged for loops. |
241 | | JS_PUBLIC_API(ProfiledFrameRange) |
242 | | GetProfiledFrames(JSContext* cx, void* addr); |
243 | | |
244 | | } // namespace JS |
245 | | |
246 | | #endif /* js_ProfilingFrameIterator_h */ |