|
1 # The `Debugger` Interface |
|
2 |
|
3 Mozilla's JavaScript engine, SpiderMonkey, provides a debugging interface |
|
4 named `Debugger` which lets JavaScript code observe and manipulate the |
|
5 execution of other JavaScript code. Both Firefox's built-in developer tools |
|
6 and the Firebug add-on use `Debugger` to implement their JavaScript |
|
7 debuggers. However, `Debugger` is quite general, and can be used to |
|
8 implement other kinds of tools like tracers, coverage analysis, |
|
9 patch-and-continue, and so on. |
|
10 |
|
11 `Debugger` has three essential qualities: |
|
12 |
|
13 - It is a *source level* interface: it operates in terms of the JavaScript |
|
14 language, not machine language. It operates on JavaScript objects, stack |
|
15 frames, environments, and code, and presents a consistent interface |
|
16 regardless of whether the debuggee is interpreted, compiled, or |
|
17 optimized. If you have a strong command of the JavaScript language, you |
|
18 should have all the background you need to use `Debugger` successfully, |
|
19 even if you have never looked into the language's implementation. |
|
20 |
|
21 - It is for use *by JavaScript code*. JavaScript is both the debuggee |
|
22 language and the tool implementation language, so the qualities that make |
|
23 JavaScript effective on the web can be brought to bear in crafting tools |
|
24 for developers. As is expected of JavaScript APIs, `Debugger` is a |
|
25 *sound* interface: using (or even misusing) `Debugger` should never cause |
|
26 Gecko to crash. Errors throw proper JavaScript exceptions. |
|
27 |
|
28 - It is an *intra-thread* debugging API. Both the debuggee and the code |
|
29 using `Debugger` to observe it must run in the same thread. Cross-thread, |
|
30 cross-process, and cross-device tools must use `Debugger` to observe the |
|
31 debuggee from within the same thread, and then handle any needed |
|
32 communication themselves. (Firefox's builtin tools have a |
|
33 [protocol][protocol] defined for this purpose.) |
|
34 |
|
35 In Gecko, the `Debugger` API is available to chrome code only. By design, |
|
36 it ought not to introduce security holes, so in principle it could be made |
|
37 available to content as well; but it is hard to justify the security risks |
|
38 of the additional attack surface. |
|
39 |
|
40 |
|
41 ## Debugger Instances and Shadow Objects |
|
42 |
|
43 `Debugger` reflects every aspect of the debuggee's state as a JavaScript |
|
44 value—not just actual JavaScript values like objects and primitives, |
|
45 but also stack frames, environments, scripts, and compilation units, which |
|
46 are not normally accessible as objects in their own right. |
|
47 |
|
48 Here is a JavaScript program in the process of running a timer callback function: |
|
49 |
|
50 ![A running JavaScript program and its Debugger shadows][img-shadows] |
|
51 |
|
52 This diagram shows the various types of shadow objects that make up the |
|
53 Debugger API (which all follow some [general conventions][conventions]): |
|
54 |
|
55 - A [`Debugger.Object`][object] represents a debuggee object, offering a |
|
56 reflection-oriented API that protects the debugger from accidentally |
|
57 invoking getters, setters, proxy traps, and so on. |
|
58 |
|
59 - A [`Debugger.Script`][script] represents a block of JavaScript |
|
60 code—either a function body or a top-level script. Given a |
|
61 `Debugger.Script`, one can set breakpoints, translate between source |
|
62 positions and bytecode offsets (a deviation from the "source level" |
|
63 design principle), and find other static characteristics of the code. |
|
64 |
|
65 - A [`Debugger.Frame`][frame] represents a running stack frame. You can use |
|
66 these to walk the stack and find each frame's script and environment. You |
|
67 can also set `onStep` and `onPop` handlers on frames. |
|
68 |
|
69 - A [`Debugger.Environment`][environment] represents an environment, |
|
70 associating variable names with storage locations. Environments may |
|
71 belong to a running stack frame, captured by a function closure, or |
|
72 reflect some global object's properties as variables. |
|
73 |
|
74 The [`Debugger`][debugger-object] instance itself is not really a shadow of |
|
75 anything in the debuggee; rather, it maintains the set of global objects |
|
76 which are to be considered debuggees. A `Debugger` observes only execution |
|
77 taking place in the scope of these global objects. You can set functions to |
|
78 be called when new stack frames are pushed; when new code is loaded; and so |
|
79 on. |
|
80 |
|
81 Omitted from this picture are [`Debugger.Source`][source] instances, which |
|
82 represent JavaScript compilation units. A `Debugger.Source` can furnish a |
|
83 full copy of its source code, and explain how the code entered the system, |
|
84 whether via a call to `eval`, a `<script>` element, or otherwise. A |
|
85 `Debugger.Script` points to the `Debugger.Source` from which it is derived. |
|
86 |
|
87 All these types follow some [general conventions][conventions], which you |
|
88 should look through before drilling down into any particular type's |
|
89 specification. |
|
90 |
|
91 All shadow objects are unique per `Debugger` and per referent. For a given |
|
92 `Debugger`, there is exactly one `Debugger.Object` that refers to a |
|
93 particular debuggee object; exactly one `Debugger.Frame` for a particular |
|
94 stack frame; and so on. Thus, a tool can store metadata about a shadow's |
|
95 referent as a property on the shadow itself, and count on finding that |
|
96 metadata again if it comes across the same referent. And since shadows are |
|
97 per-`Debugger`, tools can do so without worrying about interfering with |
|
98 other tools that use their own `Debugger` instances. |
|
99 |
|
100 |
|
101 ## Example |
|
102 |
|
103 You can try out `Debugger` yourself in Firefox's Scratchpad. |
|
104 |
|
105 1) Visit the URL `about:config`, and set the `devtools.chrome.enabled` |
|
106 preference to `true`: |
|
107 |
|
108 ![Setting the 'devtools.chrome.enabled' preference][img-chrome-pref] |
|
109 |
|
110 2) Save the following HTML text to a file, and visit the file in your |
|
111 browser: |
|
112 |
|
113 <div onclick="var x = 'snoo'; debugger;">Click me!</div> |
|
114 |
|
115 3) Open a developer Scratchpad (Menu button > Developer > Scratchpad), and |
|
116 select "Browser" from the "Environment" menu. (This menu will not be |
|
117 present unless you have changed the preference as explained above.) |
|
118 |
|
119 ![Selecting the 'browser' context in the Scratchpad][img-scratchpad-browser] |
|
120 |
|
121 4) Enter the following code in the Scratchpad: |
|
122 |
|
123 // This simply defines 'Debugger' in this Scratchpad; |
|
124 // it doesn't actually start debugging anything. |
|
125 Cu.import("resource://gre/modules/jsdebugger.jsm"); |
|
126 addDebuggerToGlobal(window); |
|
127 |
|
128 // Create a 'Debugger' instance. |
|
129 var dbg = new Debugger; |
|
130 |
|
131 // Get the current tab's content window, and make it a debuggee. |
|
132 var w = gBrowser.selectedBrowser.contentWindow.wrappedJSObject; |
|
133 dbg.addDebuggee(w); |
|
134 |
|
135 // When the debuggee executes a 'debugger' statement, evaluate |
|
136 // the expression 'x' in that stack frame, and show its value. |
|
137 dbg.onDebuggerStatement = function (frame) { |
|
138 alert('hit debugger statement; x = ' + frame.eval('x').return); |
|
139 } |
|
140 |
|
141 5) In the Scratchpad, ensure that no text is selected, and press the "Run" |
|
142 button. |
|
143 |
|
144 6) Now, click on the text that says "Click me!" in the web page. This runs |
|
145 the `div` element's `onclick` handler. When control reaches the |
|
146 `debugger;` statement, `Debugger` calls your callback function, passing |
|
147 a `Debugger.Frame` instance. Your callback function evaluates the |
|
148 expression `x` in the given stack frame, and displays the alert: |
|
149 |
|
150 ![The Debugger callback displaying an alert][img-example-alert] |
|
151 |
|
152 7) Press "Run" in the Scratchpad again. Now, clicking on the "Click me!" |
|
153 text causes *two* alerts to show—one for each `Debugger` |
|
154 instance. |
|
155 |
|
156 Multiple `Debugger` instances can observe the same debuggee. Re-running |
|
157 the code in the Scratchpad created a fresh `Debugger` instance, added |
|
158 the same web page as its debuggee, and then registered a fresh |
|
159 `debugger;` statement handler with the new instance. When you clicked |
|
160 on the `div` element, both of them ran. This shows how any number of |
|
161 `Debugger`-based tools can observe a single web page |
|
162 simultaneously—although, since the order in which their handlers |
|
163 run is not specified, such tools should probably only observe, and not |
|
164 influence, the debuggee's behavior. |
|
165 |
|
166 8) Close the web page and the Scratchpad. |
|
167 |
|
168 Since both the Scratchpad's global object and the debuggee window are |
|
169 now gone, the `Debugger` instances will be garbage collected, since |
|
170 they can no longer have any visible effect on Firefox's behavior. The |
|
171 `Debugger` API tries to interact with garbage collection as |
|
172 transparently as possible; for example, if both a `Debugger.Object` |
|
173 instance and its referent are not reachable, they will both be |
|
174 collected, even while the `Debugger` instance to which the shadow |
|
175 belonged continues to exist. |
|
176 |
|
177 |
|
178 ## Gecko-specific features |
|
179 |
|
180 While the `Debugger` core API deals only with concepts common to any |
|
181 JavaScript implementation, it also includes some Gecko-specific features: |
|
182 |
|
183 - [Global tracking][global] supports debugging all the code running in a |
|
184 Gecko instance at once—the 'chrome debugging' model. |
|
185 - [Object wrapper][wrapper] functions help manipulate object references |
|
186 that cross privilege boundaries. |