Source code

Revision control

Other Tools

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/. */
6
7
#ifndef debugger_DebugAPI_h
8
#define debugger_DebugAPI_h
9
10
#include "vm/GlobalObject.h"
11
#include "vm/Interpreter.h"
12
#include "vm/JSContext.h"
13
#include "vm/Realm.h"
14
15
namespace js {
16
17
// This file contains the API which SpiderMonkey should use to interact with any
18
// active Debuggers.
19
20
class AbstractGeneratorObject;
21
class PromiseObject;
22
23
/**
24
* Tells how the JS engine should resume debuggee execution after firing a
25
* debugger hook. Most debugger hooks get to choose how the debuggee proceeds;
26
* see js/src/doc/Debugger/Conventions.md under "Resumption Values".
27
*
28
* Debugger::processHandlerResult() translates between JavaScript values and
29
* this enum.
30
*
31
* The values `ResumeMode::Throw` and `ResumeMode::Return` are always
32
* associated with a value (the exception value or return value). Sometimes
33
* this is represented as an explicit `JS::Value` variable or parameter,
34
* declared alongside the `ResumeMode`. In other cases, especially when
35
* ResumeMode is used as a return type (as in Debugger::onEnterFrame), the
36
* value is stashed in `cx`'s pending exception slot or the topmost frame's
37
* return value slot.
38
*/
39
enum class ResumeMode {
40
/**
41
* The debuggee should continue unchanged.
42
*
43
* This corresponds to a resumption value of `undefined`.
44
*/
45
Continue,
46
47
/**
48
* Throw an exception in the debuggee.
49
*
50
* This corresponds to a resumption value of `{throw: <value>}`.
51
*/
52
Throw,
53
54
/**
55
* Terminate the debuggee, as if it had been cancelled via the "slow
56
* script" ribbon.
57
*
58
* This corresponds to a resumption value of `null`.
59
*/
60
Terminate,
61
62
/**
63
* Force the debuggee to return from the current frame.
64
*
65
* This corresponds to a resumption value of `{return: <value>}`.
66
*/
67
Return,
68
};
69
70
class DebugScript;
71
class DebuggerVector;
72
73
class DebugAPI {
74
public:
75
friend class Debugger;
76
77
/*** Methods for interaction with the GC. ***********************************/
78
79
/*
80
* Trace (inferred) owning edges from stack frames to Debugger.Frames, as part
81
* of root marking.
82
*
83
* Even if a Debugger.Frame for a live stack frame is entirely unreachable
84
* from JS, if it has onStep or onPop hooks set, then collecting it would have
85
* observable side effects - namely, the hooks would fail to run. The effect
86
* is the same as if the stack frame held an owning edge to its
87
* Debugger.Frame.
88
*
89
* Debugger.Frames must also be retained if the Debugger to which they belong
90
* is reachable, even if they have no hooks set, but we handle that elsewhere;
91
* this function is only concerned with the inferred roots from stack frames
92
* to Debugger.Frames that have hooks set.
93
*/
94
static void traceFramesWithLiveHooks(JSTracer* tracer);
95
96
/*
97
* Trace (inferred) owning edges from generator objects to Debugger.Frames.
98
*
99
* Even if a Debugger.Frame for a live suspended generator object is entirely
100
* unreachable from JS, if it has onStep or onPop hooks set, then collecting
101
* it would have observable side effects - namely, the hooks would fail to run
102
* if the generator is resumed. The effect is the same as if the generator
103
* object held an owning edge to its Debugger.Frame.
104
*/
105
static inline void traceGeneratorFrame(JSTracer* tracer,
106
AbstractGeneratorObject* generator);
107
108
// Trace cross compartment edges in all debuggers relevant to the current GC.
109
static void traceCrossCompartmentEdges(JSTracer* tracer);
110
111
// Trace all debugger-owned GC things unconditionally, during a moving GC.
112
static void traceAllForMovingGC(JSTracer* trc);
113
114
// Trace debugging information for a JSScript.
115
static void traceDebugScript(JSTracer* trc, JSScript* script);
116
117
static void traceFromRealm(JSTracer* trc, Realm* realm);
118
119
// The garbage collector calls this after everything has been marked, but
120
// before anything has been finalized. We use this to clear Debugger /
121
// debuggee edges at a point where the parties concerned are all still
122
// initialized.
123
static void sweepAll(JSFreeOp* fop);
124
125
// Add sweep group edges due to the presence of any debuggers.
126
static MOZ_MUST_USE bool findSweepGroupEdges(JSRuntime* rt);
127
128
// Destroy the debugging information associated with a script.
129
static void destroyDebugScript(JSFreeOp* fop, JSScript* script);
130
131
// Validate the debugging information in a script after a moving GC>
132
#ifdef JSGC_HASH_TABLE_CHECKS
133
static void checkDebugScriptAfterMovingGC(DebugScript* ds);
134
#endif
135
136
#ifdef DEBUG
137
static bool edgeIsInDebuggerWeakmap(JSRuntime* rt, JSObject* src,
138
JS::GCCellPtr dst);
139
#endif
140
141
/*** Methods for querying script breakpoint state. **************************/
142
143
// Query information about whether any debuggers are observing a script.
144
static inline bool stepModeEnabled(JSScript* script);
145
static inline bool hasBreakpointsAt(JSScript* script, jsbytecode* pc);
146
static inline bool hasAnyBreakpointsOrStepMode(JSScript* script);
147
148
/*** Methods for interacting with the JITs. *********************************/
149
150
// Update Debugger frames when an interpreter frame is replaced with a
151
// baseline frame.
152
static MOZ_MUST_USE bool handleBaselineOsr(JSContext* cx,
153
InterpreterFrame* from,
154
jit::BaselineFrame* to);
155
156
// Update Debugger frames when an Ion frame bails out and is replaced with a
157
// baseline frame.
158
static MOZ_MUST_USE bool handleIonBailout(JSContext* cx,
159
jit::RematerializedFrame* from,
160
jit::BaselineFrame* to);
161
162
// Detach any Debugger frames from an Ion frame after an error occurred while
163
// it bailed out.
164
static void handleUnrecoverableIonBailoutError(
165
JSContext* cx, jit::RematerializedFrame* frame);
166
167
// When doing on-stack-replacement of a debuggee interpreter frame with a
168
// baseline frame, ensure that the resulting frame can be observed by the
169
// debugger.
170
static MOZ_MUST_USE bool ensureExecutionObservabilityOfOsrFrame(
171
JSContext* cx, AbstractFramePtr osrSourceFrame);
172
173
// Describes a set of scripts or frames whose execution observability can
174
// change due to debugger activity.
175
class ExecutionObservableSet {
176
public:
177
typedef HashSet<Zone*>::Range ZoneRange;
178
179
virtual Zone* singleZone() const { return nullptr; }
180
virtual JSScript* singleScriptForZoneInvalidation() const {
181
return nullptr;
182
}
183
virtual const HashSet<Zone*>* zones() const { return nullptr; }
184
185
virtual bool shouldRecompileOrInvalidate(JSScript* script) const = 0;
186
virtual bool shouldMarkAsDebuggee(FrameIter& iter) const = 0;
187
};
188
189
// This enum is converted to and compare with bool values; NotObserving
190
// must be 0 and Observing must be 1.
191
enum IsObserving { NotObserving = 0, Observing = 1 };
192
193
/*** Methods for calling installed debugger handlers. ***********************/
194
195
// Called when a new script becomes accessible to debuggers.
196
static inline void onNewScript(JSContext* cx, HandleScript script);
197
198
// Called when a new wasm instance becomes accessible to debuggers.
199
static inline void onNewWasmInstance(
200
JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
201
202
/*
203
* Announce to the debugger that the context has entered a new JavaScript
204
* frame, |frame|. Call whatever hooks have been registered to observe new
205
* frames.
206
*/
207
static inline ResumeMode onEnterFrame(JSContext* cx, AbstractFramePtr frame);
208
209
/*
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.
214
*
215
* There is no separate user-visible Debugger.onResumeFrame hook; this
216
* fires .onEnterFrame (again, since we're re-entering the frame).
217
*
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.
223
*/
224
static inline ResumeMode onResumeFrame(JSContext* cx, AbstractFramePtr frame);
225
226
static inline ResumeMode onNativeCall(JSContext* cx, const CallArgs& args,
227
CallReason reason);
228
229
/*
230
* Announce to the debugger a |debugger;| statement on has been
231
* encountered on the youngest JS frame on |cx|. Call whatever hooks have
232
* been registered to observe this.
233
*
234
* Note that this method is called for all |debugger;| statements,
235
* regardless of the frame's debuggee-ness.
236
*/
237
static inline ResumeMode onDebuggerStatement(JSContext* cx,
238
AbstractFramePtr frame);
239
240
/*
241
* Announce to the debugger that an exception has been thrown and propagated
242
* to |frame|. Call whatever hooks have been registered to observe this.
243
*/
244
static inline ResumeMode onExceptionUnwind(JSContext* cx,
245
AbstractFramePtr frame);
246
247
/*
248
* Announce to the debugger that the thread has exited a JavaScript frame,
249
* |frame|. If |ok| is true, the frame is returning normally; if |ok| is
250
* false, the frame is throwing an exception or terminating.
251
*
252
* Change cx's current exception and |frame|'s return value to reflect the
253
* changes in behavior the hooks request, if any. Return the new error/success
254
* value.
255
*
256
* This function may be called twice for the same outgoing frame; only the
257
* first call has any effect. (Permitting double calls simplifies some
258
* cases where an onPop handler's resumption value changes a return to a
259
* throw, or vice versa: we can redirect to a complete copy of the
260
* alternative path, containing its own call to onLeaveFrame.)
261
*/
262
static inline MOZ_MUST_USE bool onLeaveFrame(JSContext* cx,
263
AbstractFramePtr frame,
264
jsbytecode* pc, bool ok);
265
266
// Call any breakpoint handlers for the current scripted location.
267
static ResumeMode onTrap(JSContext* cx, MutableHandleValue vp);
268
269
// Call any stepping handlers for the current scripted location.
270
static ResumeMode onSingleStep(JSContext* cx, MutableHandleValue vp);
271
272
// Notify any Debugger instances observing this promise's global that a new
273
// promise was allocated.
274
static inline void onNewPromise(JSContext* cx,
275
Handle<PromiseObject*> promise);
276
277
// Notify any Debugger instances observing this promise's global that the
278
// promise has settled (ie, it has either been fulfilled or rejected). Note
279
// that this is *not* equivalent to the promise resolution (ie, the promise's
280
// fate getting locked in) because you can resolve a promise with another
281
// pending promise, in which case neither promise has settled yet.
282
//
283
// This should never be called on the same promise more than once, because a
284
// promise can only make the transition from unsettled to settled once.
285
static inline void onPromiseSettled(JSContext* cx,
286
Handle<PromiseObject*> promise);
287
288
// Notify any Debugger instances that a new global object has been created.
289
static inline void onNewGlobalObject(JSContext* cx,
290
Handle<GlobalObject*> global);
291
292
/*** Methods for querying installed debugger handlers. **********************/
293
294
// Whether any debugger is observing execution in a global.
295
static bool debuggerObservesAllExecution(GlobalObject* global);
296
297
// Whether any debugger is observing JS execution coverage in a global.
298
static bool debuggerObservesCoverage(GlobalObject* global);
299
300
// Whether any Debugger is observing asm.js execution in a global.
301
static bool debuggerObservesAsmJS(GlobalObject* global);
302
303
/*
304
* Return true if the given global is being observed by at least one
305
* Debugger that is tracking allocations.
306
*/
307
static bool isObservedByDebuggerTrackingAllocations(
308
const GlobalObject& debuggee);
309
310
// If any debuggers are tracking allocations for a global, return the
311
// probability that a given allocation should be tracked. Nothing otherwise.
312
static mozilla::Maybe<double> allocationSamplingProbability(
313
GlobalObject* global);
314
315
// Whether any debugger is observing exception unwinds in a realm.
316
static bool hasExceptionUnwindHook(GlobalObject* global);
317
318
// Whether any debugger is observing debugger statements in a realm.
319
static bool hasDebuggerStatementHook(GlobalObject* global);
320
321
/*** Assorted methods for interacting with the runtime. *********************/
322
323
// When a step handler called during the interrupt callback forces the current
324
// frame to return, set state in the frame and context so that the exception
325
// handler will perform the forced return.
326
static void propagateForcedReturn(JSContext* cx, AbstractFramePtr frame,
327
HandleValue rval);
328
329
// Checks if the current compartment is allowed to execute code.
330
static inline MOZ_MUST_USE bool checkNoExecute(JSContext* cx,
331
HandleScript script);
332
333
/*
334
* Announce to the debugger that a generator object has been created,
335
* via JSOP_GENERATOR.
336
*
337
* This does not fire user hooks, but it's needed for debugger bookkeeping.
338
*/
339
static inline MOZ_MUST_USE bool onNewGenerator(
340
JSContext* cx, AbstractFramePtr frame,
341
Handle<AbstractGeneratorObject*> genObj);
342
343
// If necessary, record an object that was just allocated for any observing
344
// debuggers.
345
static inline MOZ_MUST_USE bool onLogAllocationSite(JSContext* cx,
346
JSObject* obj,
347
HandleSavedFrame frame,
348
mozilla::TimeStamp when);
349
350
// Announce to the debugger that a global object is being collected by the
351
// specified major GC.
352
static inline void notifyParticipatesInGC(GlobalObject* global,
353
uint64_t majorGCNumber);
354
355
/*
356
* Get any instrumentation ID which has been associated with a script using
357
* the specified debugger object.
358
*/
359
static bool getScriptInstrumentationId(JSContext* cx, HandleObject dbgObject,
360
HandleScript script,
361
MutableHandleValue rval);
362
363
private:
364
static bool stepModeEnabledSlow(JSScript* script);
365
static bool hasBreakpointsAtSlow(JSScript* script, jsbytecode* pc);
366
static void slowPathOnNewScript(JSContext* cx, HandleScript script);
367
static void slowPathOnNewGlobalObject(JSContext* cx,
368
Handle<GlobalObject*> global);
369
static void slowPathNotifyParticipatesInGC(uint64_t majorGCNumber,
370
JS::Realm::DebuggerVector& dbgs);
371
static MOZ_MUST_USE bool slowPathOnLogAllocationSite(
372
JSContext* cx, HandleObject obj, HandleSavedFrame frame,
373
mozilla::TimeStamp when, JS::Realm::DebuggerVector& dbgs);
374
static MOZ_MUST_USE bool slowPathOnLeaveFrame(JSContext* cx,
375
AbstractFramePtr frame,
376
jsbytecode* pc, bool ok);
377
static MOZ_MUST_USE bool slowPathOnNewGenerator(
378
JSContext* cx, AbstractFramePtr frame,
379
Handle<AbstractGeneratorObject*> genObj);
380
static MOZ_MUST_USE bool slowPathCheckNoExecute(JSContext* cx,
381
HandleScript script);
382
static ResumeMode slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame);
383
static ResumeMode slowPathOnResumeFrame(JSContext* cx,
384
AbstractFramePtr frame);
385
static ResumeMode slowPathOnNativeCall(JSContext* cx, const CallArgs& args,
386
CallReason reason);
387
static ResumeMode slowPathOnDebuggerStatement(JSContext* cx,
388
AbstractFramePtr frame);
389
static ResumeMode slowPathOnExceptionUnwind(JSContext* cx,
390
AbstractFramePtr frame);
391
static void slowPathOnNewWasmInstance(
392
JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
393
static void slowPathOnNewPromise(JSContext* cx,
394
Handle<PromiseObject*> promise);
395
static void slowPathOnPromiseSettled(JSContext* cx,
396
Handle<PromiseObject*> promise);
397
static bool inFrameMaps(AbstractFramePtr frame);
398
static void slowPathTraceGeneratorFrame(JSTracer* tracer,
399
AbstractGeneratorObject* generator);
400
};
401
402
// Suppresses all debuggee NX checks, i.e., allow all execution. Used to allow
403
// certain whitelisted operations to execute code.
404
//
405
// WARNING
406
// WARNING Do not use this unless you know what you are doing!
407
// WARNING
408
class AutoSuppressDebuggeeNoExecuteChecks {
409
EnterDebuggeeNoExecute** stack_;
410
EnterDebuggeeNoExecute* prev_;
411
412
public:
413
explicit AutoSuppressDebuggeeNoExecuteChecks(JSContext* cx) {
414
stack_ = &cx->noExecuteDebuggerTop.ref();
415
prev_ = *stack_;
416
*stack_ = nullptr;
417
}
418
419
~AutoSuppressDebuggeeNoExecuteChecks() {
420
MOZ_ASSERT(!*stack_);
421
*stack_ = prev_;
422
}
423
};
424
425
} /* namespace js */
426
427
#endif /* debugger_DebugAPI_h */