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 debugger_DebugAPI_h
8 #define debugger_DebugAPI_h
10 #include "vm/GlobalObject.h"
11 #include "vm/Interpreter.h"
12 #include "vm/JSContext.h"
17 // This file contains the API which SpiderMonkey should use to interact with any
20 class AbstractGeneratorObject
;
29 * DebugAPI::onNativeCall allows the debugger to call callbacks just before
30 * some native functions are to be executed. It also allows the hooks
31 * themselves to affect the result of the call. This enum represents the
32 * various affects that DebugAPI::onNativeCall may perform.
34 enum class NativeResumeMode
{
36 * If the debugger hook did not return a value to manipulate the result of
37 * the native call, execution can continue unchanged.
39 * Continue indicates that the native function should execute normally.
44 * If the debugger hook returned an explicit return value that is meant to
45 * take the place of the native call's result, execution of the native
46 * function needs to be skipped in favor of the explicit result.
48 * Override indicates that the native function should be skipped and that
49 * the debugger has already stored the return value into the CallArgs.
54 * If the debugger hook returns an explicit termination or an explicit
55 * thrown exception, execution of the native function needs to be skipped
56 * in favor of handling the error condition.
58 * Abort indicates that the native function should be skipped and that
59 * execution should be terminated. The debugger may or may not have set a
70 friend class Debugger
;
72 /*** Methods for interaction with the GC. ***********************************/
75 * Trace (inferred) owning edges from stack frames to Debugger.Frames, as part
78 * Even if a Debugger.Frame for a live stack frame is entirely unreachable
79 * from JS, if it has onStep or onPop hooks set, then collecting it would have
80 * observable side effects - namely, the hooks would fail to run. The effect
81 * is the same as if the stack frame held an owning edge to its
84 * Debugger.Frames must also be retained if the Debugger to which they belong
85 * is reachable, even if they have no hooks set, but we handle that elsewhere;
86 * this function is only concerned with the inferred roots from stack frames
87 * to Debugger.Frames that have hooks set.
89 static void traceFramesWithLiveHooks(JSTracer
* tracer
);
92 * Trace (inferred) owning edges from generator objects to Debugger.Frames.
94 * Even if a Debugger.Frame for a live suspended generator object is entirely
95 * unreachable from JS, if it has onStep or onPop hooks set, then collecting
96 * it would have observable side effects - namely, the hooks would fail to run
97 * if the generator is resumed. The effect is the same as if the generator
98 * object held an owning edge to its Debugger.Frame.
100 static inline void traceGeneratorFrame(JSTracer
* tracer
,
101 AbstractGeneratorObject
* generator
);
103 // Trace cross compartment edges in all debuggers relevant to the current GC.
104 static void traceCrossCompartmentEdges(JSTracer
* tracer
);
106 // Trace all debugger-owned GC things unconditionally, during a moving GC.
107 static void traceAllForMovingGC(JSTracer
* trc
);
109 // Trace the debug script map. Called as part of tracing a zone's roots.
110 static void traceDebugScriptMap(JSTracer
* trc
, DebugScriptMap
* map
);
112 static void traceFromRealm(JSTracer
* trc
, Realm
* realm
);
114 // The garbage collector calls this after everything has been marked, but
115 // before anything has been finalized. We use this to clear Debugger /
116 // debuggee edges at a point where the parties concerned are all still
117 // initialized. This does not update edges to moved GC things which is handled
118 // via the other trace methods.
119 static void sweepAll(JS::GCContext
* gcx
);
121 // Add sweep group edges due to the presence of any debuggers.
122 [[nodiscard
]] static bool findSweepGroupEdges(JSRuntime
* rt
);
124 // Remove the debugging information associated with a script.
125 static void removeDebugScript(JS::GCContext
* gcx
, JSScript
* script
);
127 // Delete a Zone's debug script map. Called when a zone is destroyed.
128 static void deleteDebugScriptMap(DebugScriptMap
* map
);
130 // Validate the debugging information in a script after a moving GC>
131 #ifdef JSGC_HASH_TABLE_CHECKS
132 static void checkDebugScriptAfterMovingGC(DebugScript
* ds
);
136 static bool edgeIsInDebuggerWeakmap(JSRuntime
* rt
, JSObject
* src
,
140 /*** Methods for querying script breakpoint state. **************************/
142 // Query information about whether any debuggers are observing a script.
143 static inline bool stepModeEnabled(JSScript
* script
);
144 static inline bool hasBreakpointsAt(JSScript
* script
, jsbytecode
* pc
);
145 static inline bool hasAnyBreakpointsOrStepMode(JSScript
* script
);
147 /*** Methods for interacting with the JITs. *********************************/
149 // Update Debugger frames when an interpreter frame is replaced with a
151 [[nodiscard
]] static bool handleBaselineOsr(JSContext
* cx
,
152 InterpreterFrame
* from
,
153 jit::BaselineFrame
* to
);
155 // Update Debugger frames when an Ion frame bails out and is replaced with a
157 [[nodiscard
]] static bool handleIonBailout(JSContext
* cx
,
158 jit::RematerializedFrame
* from
,
159 jit::BaselineFrame
* to
);
161 // Detach any Debugger frames from an Ion frame after an error occurred while
163 static void handleUnrecoverableIonBailoutError(
164 JSContext
* cx
, jit::RematerializedFrame
* frame
);
166 // When doing on-stack-replacement of a debuggee interpreter frame with a
167 // baseline frame, ensure that the resulting frame can be observed by the
169 [[nodiscard
]] static bool ensureExecutionObservabilityOfOsrFrame(
170 JSContext
* cx
, AbstractFramePtr osrSourceFrame
);
172 // Describes a set of scripts or frames whose execution observability can
173 // change due to debugger activity.
174 class ExecutionObservableSet
{
176 using ZoneRange
= HashSet
<Zone
*>::Range
;
178 virtual Zone
* singleZone() const { return nullptr; }
179 virtual JSScript
* singleScriptForZoneInvalidation() const {
182 virtual const HashSet
<Zone
*>* zones() const { return nullptr; }
184 virtual bool shouldRecompileOrInvalidate(JSScript
* script
) const = 0;
185 virtual bool shouldMarkAsDebuggee(FrameIter
& iter
) const = 0;
188 // This enum is converted to and compare with bool values; NotObserving
189 // must be 0 and Observing must be 1.
190 enum IsObserving
{ NotObserving
= 0, Observing
= 1 };
192 /*** Methods for calling installed debugger handlers. ***********************/
194 // Called when a new script becomes accessible to debuggers.
195 static void onNewScript(JSContext
* cx
, HandleScript script
);
197 // Called when a new wasm instance becomes accessible to debuggers.
198 static inline void onNewWasmInstance(
199 JSContext
* cx
, Handle
<WasmInstanceObject
*> wasmInstance
);
202 * Announce to the debugger that the context has entered a new JavaScript
203 * frame, |frame|. Call whatever hooks have been registered to observe new
206 [[nodiscard
]] static inline bool onEnterFrame(JSContext
* cx
,
207 AbstractFramePtr frame
);
210 * Like onEnterFrame, but for resuming execution of a generator or async
211 * function. `frame` is a new baseline or interpreter frame, but abstractly
212 * it can be identified with a particular generator frame that was
215 * There is no separate user-visible Debugger.onResumeFrame hook; this
216 * fires .onEnterFrame (again, since we're re-entering the frame).
218 * Unfortunately, the interpreter and the baseline JIT arrange for this to
219 * be called in different ways. The interpreter calls it from JSOp::Resume,
220 * immediately after pushing the resumed frame; the JIT calls it from
221 * JSOp::AfterYield, just after the generator resumes. The difference
222 * should not be user-visible.
224 [[nodiscard
]] static inline bool onResumeFrame(JSContext
* cx
,
225 AbstractFramePtr frame
);
227 static inline NativeResumeMode
onNativeCall(JSContext
* cx
,
228 const CallArgs
& args
,
232 * Announce to the debugger a |debugger;| statement on has been
233 * encountered on the youngest JS frame on |cx|. Call whatever hooks have
234 * been registered to observe this.
236 * Note that this method is called for all |debugger;| statements,
237 * regardless of the frame's debuggee-ness.
239 [[nodiscard
]] static inline bool onDebuggerStatement(JSContext
* cx
,
240 AbstractFramePtr frame
);
243 * Announce to the debugger that an exception has been thrown and propagated
244 * to |frame|. Call whatever hooks have been registered to observe this.
246 [[nodiscard
]] static inline bool onExceptionUnwind(JSContext
* cx
,
247 AbstractFramePtr frame
);
250 * Announce to the debugger that the thread has exited a JavaScript frame,
251 * |frame|. If |ok| is true, the frame is returning normally; if |ok| is
252 * false, the frame is throwing an exception or terminating.
254 * Change cx's current exception and |frame|'s return value to reflect the
255 * changes in behavior the hooks request, if any. Return the new error/success
258 * This function may be called twice for the same outgoing frame; only the
259 * first call has any effect. (Permitting double calls simplifies some
260 * cases where an onPop handler's resumption value changes a return to a
261 * throw, or vice versa: we can redirect to a complete copy of the
262 * alternative path, containing its own call to onLeaveFrame.)
264 [[nodiscard
]] static inline bool onLeaveFrame(JSContext
* cx
,
265 AbstractFramePtr frame
,
266 const jsbytecode
* pc
, bool ok
);
268 // Call any breakpoint handlers for the current scripted location.
269 [[nodiscard
]] static bool onTrap(JSContext
* cx
);
271 // Call any stepping handlers for the current scripted location.
272 [[nodiscard
]] static bool onSingleStep(JSContext
* cx
);
274 // Notify any Debugger instances observing this promise's global that a new
275 // promise was allocated.
276 static inline void onNewPromise(JSContext
* cx
,
277 Handle
<PromiseObject
*> promise
);
279 // Notify any Debugger instances observing this promise's global that the
280 // promise has settled (ie, it has either been fulfilled or rejected). Note
281 // that this is *not* equivalent to the promise resolution (ie, the promise's
282 // fate getting locked in) because you can resolve a promise with another
283 // pending promise, in which case neither promise has settled yet.
285 // This should never be called on the same promise more than once, because a
286 // promise can only make the transition from unsettled to settled once.
287 static inline void onPromiseSettled(JSContext
* cx
,
288 Handle
<PromiseObject
*> promise
);
290 // Notify any Debugger instances that a new global object has been created.
291 static inline void onNewGlobalObject(JSContext
* cx
,
292 Handle
<GlobalObject
*> global
);
294 /*** Methods for querying installed debugger handlers. **********************/
296 // Whether any debugger is observing execution in a global.
297 static bool debuggerObservesAllExecution(GlobalObject
* global
);
299 // Whether any debugger is observing JS execution coverage in a global.
300 static bool debuggerObservesCoverage(GlobalObject
* global
);
302 // Whether any Debugger is observing asm.js execution in a global.
303 static bool debuggerObservesAsmJS(GlobalObject
* global
);
305 // Whether any Debugger is observing WebAssembly execution in a global.
306 static bool debuggerObservesWasm(GlobalObject
* global
);
309 * Return true if the given global is being observed by at least one
310 * Debugger that is tracking allocations.
312 static bool isObservedByDebuggerTrackingAllocations(
313 const GlobalObject
& debuggee
);
315 // If any debuggers are tracking allocations for a global, return the
316 // probability that a given allocation should be tracked. Nothing otherwise.
317 static mozilla::Maybe
<double> allocationSamplingProbability(
318 GlobalObject
* global
);
320 // Whether any debugger is observing exception unwinds in a realm.
321 static bool hasExceptionUnwindHook(GlobalObject
* global
);
323 // Whether any debugger is observing debugger statements in a realm.
324 static bool hasDebuggerStatementHook(GlobalObject
* global
);
326 /*** Assorted methods for interacting with the runtime. *********************/
328 // Checks if the current compartment is allowed to execute code.
329 [[nodiscard
]] static inline bool checkNoExecute(JSContext
* cx
,
330 HandleScript script
);
333 * Announce to the debugger that a generator object has been created,
334 * via JSOp::Generator.
336 * This does not fire user hooks, but it's needed for debugger bookkeeping.
338 [[nodiscard
]] static inline bool onNewGenerator(
339 JSContext
* cx
, AbstractFramePtr frame
,
340 Handle
<AbstractGeneratorObject
*> genObj
);
342 // If necessary, record an object that was just allocated for any observing
344 [[nodiscard
]] static inline bool onLogAllocationSite(
345 JSContext
* cx
, JSObject
* obj
, Handle
<SavedFrame
*> frame
,
346 mozilla::TimeStamp when
);
348 // Announce to the debugger that a global object is being collected by the
349 // specified major GC.
350 static inline void notifyParticipatesInGC(GlobalObject
* global
,
351 uint64_t majorGCNumber
);
354 static bool stepModeEnabledSlow(JSScript
* script
);
355 static bool hasBreakpointsAtSlow(JSScript
* script
, jsbytecode
* pc
);
356 static void slowPathOnNewGlobalObject(JSContext
* cx
,
357 Handle
<GlobalObject
*> global
);
358 static void slowPathNotifyParticipatesInGC(uint64_t majorGCNumber
,
359 JS::Realm::DebuggerVector
& dbgs
,
360 const JS::AutoRequireNoGC
& nogc
);
361 [[nodiscard
]] static bool slowPathOnLogAllocationSite(
362 JSContext
* cx
, HandleObject obj
, Handle
<SavedFrame
*> frame
,
363 mozilla::TimeStamp when
, JS::Realm::DebuggerVector
& dbgs
,
364 const gc::AutoSuppressGC
& nogc
);
365 [[nodiscard
]] static bool slowPathOnLeaveFrame(JSContext
* cx
,
366 AbstractFramePtr frame
,
367 const jsbytecode
* pc
, bool ok
);
368 [[nodiscard
]] static bool slowPathOnNewGenerator(
369 JSContext
* cx
, AbstractFramePtr frame
,
370 Handle
<AbstractGeneratorObject
*> genObj
);
371 [[nodiscard
]] static bool slowPathCheckNoExecute(JSContext
* cx
,
372 HandleScript script
);
373 [[nodiscard
]] static bool slowPathOnEnterFrame(JSContext
* cx
,
374 AbstractFramePtr frame
);
375 [[nodiscard
]] static bool slowPathOnResumeFrame(JSContext
* cx
,
376 AbstractFramePtr frame
);
377 static NativeResumeMode
slowPathOnNativeCall(JSContext
* cx
,
378 const CallArgs
& args
,
380 [[nodiscard
]] static bool slowPathOnDebuggerStatement(JSContext
* cx
,
381 AbstractFramePtr frame
);
382 [[nodiscard
]] static bool slowPathOnExceptionUnwind(JSContext
* cx
,
383 AbstractFramePtr frame
);
384 static void slowPathOnNewWasmInstance(
385 JSContext
* cx
, Handle
<WasmInstanceObject
*> wasmInstance
);
386 static void slowPathOnNewPromise(JSContext
* cx
,
387 Handle
<PromiseObject
*> promise
);
388 static void slowPathOnPromiseSettled(JSContext
* cx
,
389 Handle
<PromiseObject
*> promise
);
390 static bool inFrameMaps(AbstractFramePtr frame
);
391 static void slowPathTraceGeneratorFrame(JSTracer
* tracer
,
392 AbstractGeneratorObject
* generator
);
395 // Suppresses all debuggee NX checks, i.e., allow all execution. Used to allow
396 // certain whitelisted operations to execute code.
399 // WARNING Do not use this unless you know what you are doing!
401 class AutoSuppressDebuggeeNoExecuteChecks
{
402 EnterDebuggeeNoExecute
** stack_
;
403 EnterDebuggeeNoExecute
* prev_
;
406 explicit AutoSuppressDebuggeeNoExecuteChecks(JSContext
* cx
) {
407 stack_
= &cx
->noExecuteDebuggerTop
.ref();
412 ~AutoSuppressDebuggeeNoExecuteChecks() {
413 MOZ_ASSERT(!*stack_
);
420 #endif /* debugger_DebugAPI_h */