Bug 1865597 - Add error checking when initializing parallel marking and disable on...
[gecko.git] / js / src / debugger / DebugAPI.h
blob27b2cd8ba4fe5dbcb96fdc701ac073108b757a1f
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"
13 #include "vm/Realm.h"
15 namespace js {
17 // This file contains the API which SpiderMonkey should use to interact with any
18 // active Debuggers.
20 class AbstractGeneratorObject;
21 class DebugScriptMap;
22 class PromiseObject;
24 namespace gc {
25 class AutoSuppressGC;
26 } // namespace gc
28 /**
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 {
35 /**
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.
41 Continue,
43 /**
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.
51 Override,
53 /**
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
60 * pending exception.
62 Abort,
65 class DebugScript;
66 class DebuggerVector;
68 class DebugAPI {
69 public:
70 friend class Debugger;
72 /*** Methods for interaction with the GC. ***********************************/
75 * Trace (inferred) owning edges from stack frames to Debugger.Frames, as part
76 * of root marking.
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
82 * Debugger.Frame.
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);
133 #endif
135 #ifdef DEBUG
136 static bool edgeIsInDebuggerWeakmap(JSRuntime* rt, JSObject* src,
137 JS::GCCellPtr dst);
138 #endif
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
150 // baseline frame.
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
156 // baseline frame.
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
162 // it bailed out.
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
168 // debugger.
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 {
175 public:
176 using ZoneRange = HashSet<Zone*>::Range;
178 virtual Zone* singleZone() const { return nullptr; }
179 virtual JSScript* singleScriptForZoneInvalidation() const {
180 return nullptr;
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
204 * frames.
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
213 * suspended earlier.
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,
229 CallReason reason);
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
256 * value.
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
343 // debuggers.
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);
353 private:
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,
379 CallReason reason);
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.
398 // WARNING
399 // WARNING Do not use this unless you know what you are doing!
400 // WARNING
401 class AutoSuppressDebuggeeNoExecuteChecks {
402 EnterDebuggeeNoExecute** stack_;
403 EnterDebuggeeNoExecute* prev_;
405 public:
406 explicit AutoSuppressDebuggeeNoExecuteChecks(JSContext* cx) {
407 stack_ = &cx->noExecuteDebuggerTop.ref();
408 prev_ = *stack_;
409 *stack_ = nullptr;
412 ~AutoSuppressDebuggeeNoExecuteChecks() {
413 MOZ_ASSERT(!*stack_);
414 *stack_ = prev_;
418 } /* namespace js */
420 #endif /* debugger_DebugAPI_h */