/work/obj-fuzz/dist/include/js/ProfilingStack.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_ProfilingStack_h |
8 | | #define js_ProfilingStack_h |
9 | | |
10 | | #include <algorithm> |
11 | | #include <stdint.h> |
12 | | |
13 | | #include "jstypes.h" |
14 | | |
15 | | #include "js/TypeDecls.h" |
16 | | #include "js/Utility.h" |
17 | | |
18 | | #ifdef JS_BROKEN_GCC_ATTRIBUTE_WARNING |
19 | | #pragma GCC diagnostic push |
20 | | #pragma GCC diagnostic ignored "-Wattributes" |
21 | | #endif // JS_BROKEN_GCC_ATTRIBUTE_WARNING |
22 | | |
23 | | class JS_PUBLIC_API(JSTracer); |
24 | | |
25 | | #ifdef JS_BROKEN_GCC_ATTRIBUTE_WARNING |
26 | | #pragma GCC diagnostic pop |
27 | | #endif // JS_BROKEN_GCC_ATTRIBUTE_WARNING |
28 | | |
29 | | class ProfilingStack; |
30 | | |
31 | | // This file defines the classes ProfilingStack and ProfilingStackFrame. |
32 | | // The ProfilingStack manages an array of ProfilingStackFrames. |
33 | | // It keeps track of the "label stack" and the JS interpreter stack. |
34 | | // The two stack types are interleaved. |
35 | | // |
36 | | // Usage: |
37 | | // |
38 | | // ProfilingStack* profilingStack = ...; |
39 | | // |
40 | | // // For label frames: |
41 | | // profilingStack->pushLabelFrame(...); |
42 | | // // Execute some code. When finished, pop the frame: |
43 | | // profilingStack->pop(); |
44 | | // |
45 | | // // For JS stack frames: |
46 | | // profilingStack->pushJSFrame(...); |
47 | | // // Execute some code. When finished, pop the frame: |
48 | | // profilingStack->pop(); |
49 | | // |
50 | | // |
51 | | // Concurrency considerations |
52 | | // |
53 | | // A thread's profiling stack (and the frames inside it) is only modified by |
54 | | // that thread. However, the profiling stack can be *read* by a different thread, |
55 | | // the sampler thread: Whenever the profiler wants to sample a given thread A, |
56 | | // the following happens: |
57 | | // (1) Thread A is suspended. |
58 | | // (2) The sampler thread (thread S) reads the ProfilingStack of thread A, |
59 | | // including all ProfilingStackFrames that are currently in that stack |
60 | | // (profilingStack->frames[0..profilingStack->stackSize()]). |
61 | | // (3) Thread A is resumed. |
62 | | // |
63 | | // Thread suspension is achieved using platform-specific APIs; refer to each |
64 | | // platform's Sampler::SuspendAndSampleAndResumeThread implementation in |
65 | | // platform-*.cpp for details. |
66 | | // |
67 | | // When the thread is suspended, the values in profilingStack->stackPointer and in |
68 | | // the stack frame range profilingStack->frames[0..profilingStack->stackPointer] need |
69 | | // to be in a consistent state, so that thread S does not read partially- |
70 | | // constructed stack frames. More specifically, we have two requirements: |
71 | | // (1) When adding a new frame at the top of the stack, its ProfilingStackFrame |
72 | | // data needs to be put in place *before* the stackPointer is incremented, |
73 | | // and the compiler + CPU need to know that this order matters. |
74 | | // (2) When popping an frame from the stack and then preparing the |
75 | | // ProfilingStackFrame data for the next frame that is about to be pushed, |
76 | | // the decrement of the stackPointer in pop() needs to happen *before* the |
77 | | // ProfilingStackFrame for the new frame is being popuplated, and the |
78 | | // compiler + CPU need to know that this order matters. |
79 | | // |
80 | | // We can express the relevance of these orderings in multiple ways. |
81 | | // Option A is to make stackPointer an atomic with SequentiallyConsistent |
82 | | // memory ordering. This would ensure that no writes in thread A would be |
83 | | // reordered across any writes to stackPointer, which satisfies requirements |
84 | | // (1) and (2) at the same time. Option A is the simplest. |
85 | | // Option B is to use ReleaseAcquire memory ordering both for writes to |
86 | | // stackPointer *and* for writes to ProfilingStackFrame fields. Release-stores |
87 | | // ensure that all writes that happened *before this write in program order* are |
88 | | // not reordered to happen after this write. ReleaseAcquire ordering places no |
89 | | // requirements on the ordering of writes that happen *after* this write in |
90 | | // program order. |
91 | | // Using release-stores for writes to stackPointer expresses requirement (1), |
92 | | // and using release-stores for writes to the ProfilingStackFrame fields |
93 | | // expresses requirement (2). |
94 | | // |
95 | | // Option B is more complicated than option A, but has much better performance |
96 | | // on x86/64: In a microbenchmark run on a Macbook Pro from 2017, switching |
97 | | // from option A to option B reduced the overhead of pushing+popping a |
98 | | // ProfilingStackFrame by 10 nanoseconds. |
99 | | // On x86/64, release-stores require no explicit hardware barriers or lock |
100 | | // instructions. |
101 | | // On ARM/64, option B may be slower than option A, because the compiler will |
102 | | // generate hardware barriers for every single release-store instead of just |
103 | | // for the writes to stackPointer. However, the actual performance impact of |
104 | | // this has not yet been measured on ARM, so we're currently using option B |
105 | | // everywhere. This is something that we may want to change in the future once |
106 | | // we've done measurements. |
107 | | |
108 | | namespace js { |
109 | | |
110 | | // A call stack can be specified to the JS engine such that all JS entry/exits |
111 | | // to functions push/pop a stack frame to/from the specified stack. |
112 | | // |
113 | | // For more detailed information, see vm/GeckoProfiler.h. |
114 | | // |
115 | | class ProfilingStackFrame |
116 | | { |
117 | | // A ProfilingStackFrame represents either a label frame or a JS frame. |
118 | | |
119 | | // WARNING WARNING WARNING |
120 | | // |
121 | | // All the fields below are Atomic<...,ReleaseAcquire>. This is needed so |
122 | | // that writes to these fields are release-writes, which ensures that |
123 | | // earlier writes in this thread don't get reordered after the writes to |
124 | | // these fields. In particular, the decrement of the stack pointer in |
125 | | // ProfilingStack::pop() is a write that *must* happen before the values in |
126 | | // this ProfilingStackFrame are changed. Otherwise, the sampler thread might |
127 | | // see an inconsistent state where the stack pointer still points to a |
128 | | // ProfilingStackFrame which has already been popped off the stack and whose |
129 | | // fields have now been partially repopulated with new values. |
130 | | // See the "Concurrency considerations" paragraph at the top of this file |
131 | | // for more details. |
132 | | |
133 | | // Descriptive label for this stack frame. Must be a static string! Can be |
134 | | // an empty string, but not a null pointer. |
135 | | mozilla::Atomic<const char*, mozilla::ReleaseAcquire, |
136 | | mozilla::recordreplay::Behavior::DontPreserve> label_; |
137 | | |
138 | | // An additional descriptive string of this frame which is combined with |
139 | | // |label_| in profiler output. Need not be (and usually isn't) static. Can |
140 | | // be null. |
141 | | mozilla::Atomic<const char*, mozilla::ReleaseAcquire, |
142 | | mozilla::recordreplay::Behavior::DontPreserve> dynamicString_; |
143 | | |
144 | | // Stack pointer for non-JS stack frames, the script pointer otherwise. |
145 | | mozilla::Atomic<void*, mozilla::ReleaseAcquire, |
146 | | mozilla::recordreplay::Behavior::DontPreserve> spOrScript; |
147 | | |
148 | | // Line number for non-JS stack frames, the bytecode offset otherwise. |
149 | | mozilla::Atomic<int32_t, mozilla::ReleaseAcquire, |
150 | | mozilla::recordreplay::Behavior::DontPreserve> lineOrPcOffset; |
151 | | |
152 | | // Bits 0...1 hold the Kind. Bits 2...31 hold the category. |
153 | | mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire, |
154 | | mozilla::recordreplay::Behavior::DontPreserve> kindAndCategory_; |
155 | | |
156 | | static int32_t pcToOffset(JSScript* aScript, jsbytecode* aPc); |
157 | | |
158 | | public: |
159 | 5.12k | ProfilingStackFrame() = default; |
160 | | ProfilingStackFrame& operator=(const ProfilingStackFrame& other) |
161 | 0 | { |
162 | 0 | label_ = other.label(); |
163 | 0 | dynamicString_ = other.dynamicString(); |
164 | 0 | void* spScript = other.spOrScript; |
165 | 0 | spOrScript = spScript; |
166 | 0 | int32_t offset = other.lineOrPcOffset; |
167 | 0 | lineOrPcOffset = offset; |
168 | 0 | uint32_t kindAndCategory = other.kindAndCategory_; |
169 | 0 | kindAndCategory_ = kindAndCategory; |
170 | 0 | return *this; |
171 | 0 | } |
172 | | |
173 | | enum class Kind : uint32_t { |
174 | | // A regular label frame. These usually come from AutoProfilerLabel. |
175 | | LABEL = 0, |
176 | | |
177 | | // A special frame indicating the start of a run of JS profiling stack |
178 | | // frames. SP_MARKER frames are ignored, except for the sp field. |
179 | | // These frames are needed to get correct ordering between JS and LABEL |
180 | | // frames because JS frames don't carry sp information. |
181 | | // SP is short for "stack pointer". |
182 | | SP_MARKER = 1, |
183 | | |
184 | | // A normal JS frame. |
185 | | JS_NORMAL = 2, |
186 | | |
187 | | // An interpreter JS frame that has OSR-ed into baseline. JS_NORMAL |
188 | | // frames can be converted to JS_OSR and back. JS_OSR frames are |
189 | | // ignored. |
190 | | JS_OSR = 3, |
191 | | |
192 | | KIND_BITCOUNT = 2, |
193 | | KIND_MASK = (1 << KIND_BITCOUNT) - 1 |
194 | | }; |
195 | | |
196 | | // Keep these in sync with devtools/client/performance/modules/categories.js |
197 | | enum class Category : uint32_t { |
198 | | IDLE, |
199 | | OTHER, |
200 | | LAYOUT, |
201 | | JS, |
202 | | GCCC, |
203 | | NETWORK, |
204 | | GRAPHICS, |
205 | | DOM, |
206 | | |
207 | | FIRST = OTHER, |
208 | | LAST = DOM, |
209 | | }; |
210 | | |
211 | | static_assert(uint32_t(Category::LAST) <= (UINT32_MAX >> uint32_t(Kind::KIND_BITCOUNT)), |
212 | | "Too many categories to fit into u32 with two bits reserved for the kind"); |
213 | | |
214 | | bool isLabelFrame() const |
215 | 0 | { |
216 | 0 | return kind() == Kind::LABEL; |
217 | 0 | } |
218 | | |
219 | | bool isSpMarkerFrame() const |
220 | 0 | { |
221 | 0 | return kind() == Kind::SP_MARKER; |
222 | 0 | } |
223 | | |
224 | | bool isJsFrame() const |
225 | | { |
226 | | Kind k = kind(); |
227 | | return k == Kind::JS_NORMAL || k == Kind::JS_OSR; |
228 | | } |
229 | | |
230 | 0 | void setLabel(const char* aLabel) { label_ = aLabel; } |
231 | 0 | const char* label() const { return label_; } |
232 | | |
233 | 0 | const char* dynamicString() const { return dynamicString_; } |
234 | | |
235 | | void initLabelFrame(const char* aLabel, const char* aDynamicString, void* sp, |
236 | | uint32_t aLine, Category aCategory) |
237 | 1.62M | { |
238 | 1.62M | label_ = aLabel; |
239 | 1.62M | dynamicString_ = aDynamicString; |
240 | 1.62M | spOrScript = sp; |
241 | 1.62M | lineOrPcOffset = static_cast<int32_t>(aLine); |
242 | 1.62M | kindAndCategory_ = uint32_t(Kind::LABEL) | (uint32_t(aCategory) << uint32_t(Kind::KIND_BITCOUNT)); |
243 | 1.62M | MOZ_ASSERT(isLabelFrame()); |
244 | 1.62M | } |
245 | | |
246 | | void initSpMarkerFrame(void* sp) |
247 | 1.62M | { |
248 | 1.62M | label_ = ""; |
249 | 1.62M | dynamicString_ = nullptr; |
250 | 1.62M | spOrScript = sp; |
251 | 1.62M | lineOrPcOffset = 0; |
252 | 1.62M | kindAndCategory_ = uint32_t(Kind::SP_MARKER) | (uint32_t(Category::OTHER) << uint32_t(Kind::KIND_BITCOUNT)); |
253 | 1.62M | MOZ_ASSERT(isSpMarkerFrame()); |
254 | 1.62M | } |
255 | | |
256 | | void initJsFrame(const char* aLabel, const char* aDynamicString, JSScript* aScript, |
257 | | jsbytecode* aPc) |
258 | 1.62M | { |
259 | 1.62M | label_ = aLabel; |
260 | 1.62M | dynamicString_ = aDynamicString; |
261 | 1.62M | spOrScript = aScript; |
262 | 1.62M | lineOrPcOffset = pcToOffset(aScript, aPc); |
263 | 1.62M | kindAndCategory_ = uint32_t(Kind::JS_NORMAL) | (uint32_t(Category::JS) << uint32_t(Kind::KIND_BITCOUNT)); |
264 | 1.62M | MOZ_ASSERT(isJsFrame()); |
265 | 1.62M | } |
266 | | |
267 | 0 | void setKind(Kind aKind) { |
268 | 0 | kindAndCategory_ = uint32_t(aKind) | (uint32_t(category()) << uint32_t(Kind::KIND_BITCOUNT)); |
269 | 0 | } |
270 | | |
271 | | Kind kind() const { |
272 | | return Kind(kindAndCategory_ & uint32_t(Kind::KIND_MASK)); |
273 | | } |
274 | | |
275 | 0 | Category category() const { |
276 | 0 | return Category(kindAndCategory_ >> uint32_t(Kind::KIND_BITCOUNT)); |
277 | 0 | } |
278 | | |
279 | 0 | void* stackAddress() const { |
280 | 0 | MOZ_ASSERT(!isJsFrame()); |
281 | 0 | return spOrScript; |
282 | 0 | } Unexecuted instantiation: js::ProfilingStackFrame::stackAddress() const Unexecuted instantiation: js::ProfilingStackFrame::stackAddress() const |
283 | | |
284 | | JS_PUBLIC_API(JSScript*) script() const; |
285 | | |
286 | 0 | uint32_t line() const { |
287 | 0 | MOZ_ASSERT(!isJsFrame()); |
288 | 0 | return static_cast<uint32_t>(lineOrPcOffset); |
289 | 0 | } Unexecuted instantiation: js::ProfilingStackFrame::line() const Unexecuted instantiation: js::ProfilingStackFrame::line() const |
290 | | |
291 | | // Note that the pointer returned might be invalid. |
292 | 14 | JSScript* rawScript() const { |
293 | 14 | MOZ_ASSERT(isJsFrame()); |
294 | 14 | void* script = spOrScript; |
295 | 14 | return static_cast<JSScript*>(script); |
296 | 14 | } |
297 | | |
298 | | // We can't know the layout of JSScript, so look in vm/GeckoProfiler.cpp. |
299 | | JS_FRIEND_API(jsbytecode*) pc() const; |
300 | | void setPC(jsbytecode* pc); |
301 | | |
302 | | void trace(JSTracer* trc); |
303 | | |
304 | | // The offset of a pc into a script's code can actually be 0, so to |
305 | | // signify a nullptr pc, use a -1 index. This is checked against in |
306 | | // pc() and setPC() to set/get the right pc. |
307 | | static const int32_t NullPCOffset = -1; |
308 | | }; |
309 | | |
310 | | JS_FRIEND_API(void) |
311 | | SetContextProfilingStack(JSContext* cx, ProfilingStack* profilingStack); |
312 | | |
313 | | // GetContextProfilingStack also exists, but it's defined in RootingAPI.h. |
314 | | |
315 | | JS_FRIEND_API(void) |
316 | | EnableContextProfilingStack(JSContext* cx, bool enabled); |
317 | | |
318 | | JS_FRIEND_API(void) |
319 | | RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*)); |
320 | | |
321 | | } // namespace js |
322 | | |
323 | | namespace JS { |
324 | | |
325 | | typedef ProfilingStack* |
326 | | (* RegisterThreadCallback)(const char* threadName, void* stackBase); |
327 | | |
328 | | typedef void |
329 | | (* UnregisterThreadCallback)(); |
330 | | |
331 | | JS_FRIEND_API(void) |
332 | | SetProfilingThreadCallbacks(RegisterThreadCallback registerThread, |
333 | | UnregisterThreadCallback unregisterThread); |
334 | | |
335 | | } // namespace JS |
336 | | |
337 | | // Each thread has its own ProfilingStack. That thread modifies the ProfilingStack, |
338 | | // pushing and popping elements as necessary. |
339 | | // |
340 | | // The ProfilingStack is also read periodically by the profiler's sampler thread. |
341 | | // This happens only when the thread that owns the ProfilingStack is suspended. |
342 | | // So there are no genuine parallel accesses. |
343 | | // |
344 | | // However, it is possible for pushing/popping to be interrupted by a periodic |
345 | | // sample. Because of this, we need pushing/popping to be effectively atomic. |
346 | | // |
347 | | // - When pushing a new frame, we increment the stack pointer -- making the new |
348 | | // frame visible to the sampler thread -- only after the new frame has been |
349 | | // fully written. The stack pointer is Atomic<uint32_t,ReleaseAcquire>, so |
350 | | // the increment is a release-store, which ensures that this store is not |
351 | | // reordered before the writes of the frame. |
352 | | // |
353 | | // - When popping an old frame, the only operation is the decrementing of the |
354 | | // stack pointer, which is obviously atomic. |
355 | | // |
356 | | class ProfilingStack final |
357 | | { |
358 | | public: |
359 | | ProfilingStack() |
360 | | : stackPointer(0) |
361 | 0 | {} |
362 | | |
363 | | ~ProfilingStack(); |
364 | | |
365 | | void pushLabelFrame(const char* label, const char* dynamicString, void* sp, |
366 | 1.62M | uint32_t line, js::ProfilingStackFrame::Category category) { |
367 | 1.62M | uint32_t oldStackPointer = stackPointer; |
368 | 1.62M | |
369 | 1.62M | if (MOZ_LIKELY(capacity > oldStackPointer) || MOZ_LIKELY(ensureCapacitySlow())) { |
370 | 1.62M | frames[oldStackPointer].initLabelFrame(label, dynamicString, sp, line, category); |
371 | 1.62M | } |
372 | 1.62M | |
373 | 1.62M | // This must happen at the end! The compiler will not reorder this |
374 | 1.62M | // update because stackPointer is Atomic<..., ReleaseAcquire>, so any |
375 | 1.62M | // the writes above will not be reordered below the stackPointer store. |
376 | 1.62M | // Do the read and the write as two separate statements, in order to |
377 | 1.62M | // make it clear that we don't need an atomic increment, which would be |
378 | 1.62M | // more expensive on x86 than the separate operations done here. |
379 | 1.62M | // This thread is the only one that ever changes the value of |
380 | 1.62M | // stackPointer. |
381 | 1.62M | stackPointer = oldStackPointer + 1; |
382 | 1.62M | } |
383 | | |
384 | 1.62M | void pushSpMarkerFrame(void* sp) { |
385 | 1.62M | uint32_t oldStackPointer = stackPointer; |
386 | 1.62M | |
387 | 1.62M | if (MOZ_LIKELY(capacity > oldStackPointer) || MOZ_LIKELY(ensureCapacitySlow())) { |
388 | 1.62M | frames[oldStackPointer].initSpMarkerFrame(sp); |
389 | 1.62M | } |
390 | 1.62M | |
391 | 1.62M | // This must happen at the end, see the comment in pushLabelFrame. |
392 | 1.62M | stackPointer = oldStackPointer + 1; |
393 | 1.62M | } |
394 | | |
395 | | void pushJsFrame(const char* label, const char* dynamicString, JSScript* script, |
396 | 1.62M | jsbytecode* pc) { |
397 | 1.62M | uint32_t oldStackPointer = stackPointer; |
398 | 1.62M | |
399 | 1.62M | if (MOZ_LIKELY(capacity > oldStackPointer) || MOZ_LIKELY(ensureCapacitySlow())) { |
400 | 1.62M | frames[oldStackPointer].initJsFrame(label, dynamicString, script, pc); |
401 | 1.62M | } |
402 | 1.62M | |
403 | 1.62M | // This must happen at the end, see the comment in pushLabelFrame. |
404 | 1.62M | stackPointer = oldStackPointer + 1; |
405 | 1.62M | } |
406 | | |
407 | 4.87M | void pop() { |
408 | 4.87M | MOZ_ASSERT(stackPointer > 0); |
409 | 4.87M | // Do the read and the write as two separate statements, in order to |
410 | 4.87M | // make it clear that we don't need an atomic decrement, which would be |
411 | 4.87M | // more expensive on x86 than the separate operations done here. |
412 | 4.87M | // This thread is the only one that ever changes the value of |
413 | 4.87M | // stackPointer. |
414 | 4.87M | uint32_t oldStackPointer = stackPointer; |
415 | 4.87M | stackPointer = oldStackPointer - 1; |
416 | 4.87M | } |
417 | | |
418 | | uint32_t stackSize() const { return std::min(uint32_t(stackPointer), stackCapacity()); } |
419 | | uint32_t stackCapacity() const { return capacity; } |
420 | | |
421 | | private: |
422 | | // Out of line path for expanding the buffer, since otherwise this would get inlined in every |
423 | | // DOM WebIDL call. |
424 | | MOZ_COLD MOZ_MUST_USE bool ensureCapacitySlow(); |
425 | | |
426 | | // No copying. |
427 | | ProfilingStack(const ProfilingStack&) = delete; |
428 | | void operator=(const ProfilingStack&) = delete; |
429 | | |
430 | | // No moving either. |
431 | | ProfilingStack(ProfilingStack&&) = delete; |
432 | | void operator=(ProfilingStack&&) = delete; |
433 | | |
434 | | uint32_t capacity = 0; |
435 | | |
436 | | public: |
437 | | |
438 | | // The pointer to the stack frames, this is read from the profiler thread and written from the |
439 | | // current thread. |
440 | | // |
441 | | // This is effectively a unique pointer. |
442 | | mozilla::Atomic<js::ProfilingStackFrame*, mozilla::SequentiallyConsistent, |
443 | | mozilla::recordreplay::Behavior::DontPreserve> frames { nullptr }; |
444 | | |
445 | | // This may exceed the capacity, so instead use the stackSize() method to |
446 | | // determine the number of valid frames in stackFrames. When this is less |
447 | | // than stackCapacity(), it refers to the first free stackframe past the top |
448 | | // of the in-use stack (i.e. frames[stackPointer - 1] is the top stack |
449 | | // frame). |
450 | | // |
451 | | // WARNING WARNING WARNING |
452 | | // |
453 | | // This is an atomic variable that uses ReleaseAcquire memory ordering. |
454 | | // See the "Concurrency considerations" paragraph at the top of this file |
455 | | // for more details. |
456 | | mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire, |
457 | | mozilla::recordreplay::Behavior::DontPreserve> stackPointer; |
458 | | }; |
459 | | |
460 | | namespace js { |
461 | | |
462 | | class AutoGeckoProfilerEntry; |
463 | | class GeckoProfilerEntryMarker; |
464 | | class GeckoProfilerBaselineOSRMarker; |
465 | | |
466 | | class GeckoProfilerThread |
467 | | { |
468 | | friend class AutoGeckoProfilerEntry; |
469 | | friend class GeckoProfilerEntryMarker; |
470 | | friend class GeckoProfilerBaselineOSRMarker; |
471 | | |
472 | | ProfilingStack* profilingStack_; |
473 | | |
474 | | public: |
475 | | GeckoProfilerThread(); |
476 | | |
477 | 0 | uint32_t stackPointer() { MOZ_ASSERT(installed()); return profilingStack_->stackPointer; } Unexecuted instantiation: js::GeckoProfilerThread::stackPointer() Unexecuted instantiation: js::GeckoProfilerThread::stackPointer() |
478 | 0 | ProfilingStackFrame* stack() { return profilingStack_->frames; } |
479 | 0 | ProfilingStack* getProfilingStack() { return profilingStack_; } |
480 | | |
481 | | /* management of whether instrumentation is on or off */ |
482 | | bool installed() { return profilingStack_ != nullptr; } |
483 | | |
484 | | void setProfilingStack(ProfilingStack* profilingStack); |
485 | | void trace(JSTracer* trc); |
486 | | |
487 | | /* |
488 | | * Functions which are the actual instrumentation to track run information |
489 | | * |
490 | | * - enter: a function has started to execute |
491 | | * - updatePC: updates the pc information about where a function |
492 | | * is currently executing |
493 | | * - exit: this function has ceased execution, and no further |
494 | | * entries/exits will be made |
495 | | */ |
496 | | bool enter(JSContext* cx, JSScript* script, JSFunction* maybeFun); |
497 | | void exit(JSScript* script, JSFunction* maybeFun); |
498 | | inline void updatePC(JSContext* cx, JSScript* script, jsbytecode* pc); |
499 | | }; |
500 | | |
501 | | } // namespace js |
502 | | |
503 | | #endif /* js_ProfilingStack_h */ |