Confirmed users
496
edits
m (→Pause Types: Clarify; fix grammar.) |
(Frames and grips.) |
||
Line 111: | Line 111: | ||
* At no point may either client or server send an unbounded number of packets without receiving a packet from its counterpart. This avoids deadlock without requiring either side to buffer an arbitrary number of packets per actor. | * At no point may either client or server send an unbounded number of packets without receiving a packet from its counterpart. This avoids deadlock without requiring either side to buffer an arbitrary number of packets per actor. | ||
* In states where a transition can be initiated by either the debugger or the thread, it is always clear to the debugger which state the thread actually entered, and for what reason.<p>For example, if the debugger interrupts a running thread, it cannot be sure whether the thread stopped because of the interruption, paused of its own accord (to report a watchpoint hit, say), or exited. However, the next packet the debugger receives will either be "interrupted", "paused", or "exited", resolving the ambiguity.</p><p>Similarly, when the debugger attaches to a thread, it cannot be sure whether it has succeeded in attaching to the thread, or whether the thread exited before the | * In states where a transition can be initiated by either the debugger or the thread, it is always clear to the debugger which state the thread actually entered, and for what reason.<p>For example, if the debugger interrupts a running thread, it cannot be sure whether the thread stopped because of the interruption, paused of its own accord (to report a watchpoint hit, say), or exited. However, the next packet the debugger receives will either be "interrupted", "paused", or "exited", resolving the ambiguity.</p><p>Similarly, when the debugger attaches to a thread, it cannot be sure whether it has succeeded in attaching to the thread, or whether the thread exited before the "attach" packet arrived. However, in either case the debugger can expect a disambiguating response: if the attach suceeded, it receives an "attached" packet; and in the second case, it receives an "exit" packet.</p><p>To support this property, the thread ignores certain debugger packets in some states (the "interrupt" packet in the <b>Paused</b> and <b>Exited</b> states, for exmple). These cases all handle situations where the ignored packet was preempted by some thread action.</p> | ||
Note that the rules here apply to the client's interactions with each thread agent separately. A client may send an "interrupt" to one thread agent while awaiting a reply to a request sent to a different thread agent. | Note that the rules here apply to the client's interactions with each thread agent separately. A client may send an "interrupt" to one thread agent while awaiting a reply to a request sent to a different thread agent. | ||
Line 175: | Line 175: | ||
<i>Actor</i> is an actor representing this specific pause of the thread. The pause actor lives until the thread leaves the <b>Paused</b> state. Actors referring to stack frames, values, and other entities uncovered during this pause are all children of this actor; when the thread resumes, those actors are automatically freed. This relieves the client from the responsibility to explicitly close communications with every actor mentioned in a pause. If a client wishes to hold a reference to a JavaScript value across pauses, then it must create its own grip on the value, parented by the thread actor, using the [[#Keeping_Values|"keep-value"]] packet. | <i>Actor</i> is an actor representing this specific pause of the thread. The pause actor lives until the thread leaves the <b>Paused</b> state. Actors referring to stack frames, values, and other entities uncovered during this pause are all children of this actor; when the thread resumes, those actors are automatically freed. This relieves the client from the responsibility to explicitly close communications with every actor mentioned in a pause. If a client wishes to hold a reference to a JavaScript value across pauses, then it must create its own grip on the value, parented by the thread actor, using the [[#Keeping_Values|"keep-value"]] packet. | ||
<i>Frame</i> describes the top frame on the JavaScript stack; see [[# | <i>Frame</i> describes the top frame on the JavaScript stack; see [[#Inspecting_the_Stack|Inspecting The Stack]], below. The <i>reason</i> value describes why the thread paused. It has one of the following forms: | ||
{ "type":"breakpoint", "actor":<i>actor</i> } | { "type":"breakpoint", "actor":<i>actor</i> } | ||
Line 229: | Line 229: | ||
The thread responds in one of three ways: | The thread responds in one of three ways: | ||
{ "from":<i>thread</i>, "type":"interrupted" } | { "from":<i>thread</i>, "type":"interrupted", "actor":<i>actor</i>, "frame":<i>frame</i> } | ||
This indicates that the thread stopped due to the client's <tt>interrupt</tt> packet, and is now in the <b>Paused</b> state. | This indicates that the thread stopped due to the client's <tt>interrupt</tt> packet, and is now in the <b>Paused</b> state. | ||
Line 270: | Line 270: | ||
| <tt>pre-throw-by-guard</tt> | | <tt>pre-throw-by-guard</tt> | ||
|} | |} | ||
= Inspecting Paused Threads = | |||
When a thread is in the <b>Paused</b> state, the debugger can make requests to inspect its stack, scope, and values. | |||
== Inspecting the Stack == | |||
To inspect the thread's JavaScript stack, the client can send the following request: | |||
{ "to":<i>thread</i>, "type":"frames", "limit":<i>limit</i> } | |||
The <tt>limit</tt> property is optional. If present, the reply contains at most <i>limit</i> frames from the young end of the stack; if absent, the reply describes the entire stack. | |||
The thread replies as follows: | |||
{ "from":<i>thread</i>, "frames":[<i>frame</i> ...] } | |||
where each <i>frame</i> has the form: | |||
{ "actor":<i>actor</i>, "depth":<i>depth</i>, "where":<i>location</i>, | |||
"callee":<i>callee</i>, "callee-name":<i>callee-name</i>, "host":<i>host</i>, | |||
"this",<i>this</i>, "arguments": [<i>actual</i>, ...], | |||
"scope",<i>scope</i> } | |||
where: | |||
* <i>actor</i> is the name of an actor representing this frame; | |||
* <i>depth</i> is the number of this frame, starting with zero for the youngest frame; | |||
* <i>location</i> is a source location (see [[#Source_Locations|Source Locations]]); | |||
* <i>callee</i> is a grip on the function value being called, or <tt>null</tt> if we are in global code; | |||
* <i>callee-name</i> is the name of the callee, a string; | |||
* <i>host</i> is true if this frame represents a call to a host function (perhaps implemented in C++); | |||
* <i>this</i> is a grip on the value of <tt>this</tt> for this call; | |||
* <i>arguments</i> is an array of grips on the actual values passed to the function; and | |||
* <i>scope</i> is an actor representing the innermost scope contour at the current point of execution. | |||
The argument list may be incomplete or inaccurate, for various reasons. If the program has assigned to its formal parameters, the original values passed may have been lost, and compiler optimizations may drop some argument values. | |||
<i>Location</i> and <i>scope</i> may be omitted for host functions. | |||
All actors mentioned in the frame (<i>actor</i>, <i>depth</i>, <i>callee</i>, <i>this</i>, the elements of <i>arguments</i>, and <i>scope</i>) are parented by the current pause actor, as given in the "paused" or "interrupted" packet. | |||
A "grip" is a value that represents a specific value in the debuggee; for some types of values, a grip is an actor. See [[#Grips|Grips]] for details. | |||
<i>eval-in-frame</i> | |||
== Source Locations == | |||
<i>simple script + line</i> | |||
<i>eval call number + location</i> | |||
<i>Function call number + location</i> | |||
== Scope Contours == | |||
<i>enumerate</i> | |||
<i>assign</i> | |||
= Grips = | = Grips = | ||
== Long | A grip is a value that represents a specific value in the debuggee. A grip has one of the following forms: | ||
<i>value</i> | |||
where <i>value</i> is a string, a number, a boolean value, or null. For these types of values, the grip is simply the JSON form of the value. | |||
{ "type":"undefined" } | |||
This represents the JavaScript <tt>undefined</tt> value, which has no representation in JSON. | |||
{ "type":"long-string", "initial":<i>initial</i>, "length":<i>length</i>, "actor":<i>actor</i> } | |||
This represents a very long string (where "very long" is defined at the server's discretion). <i>Initial</i> is the initial portion of the string, <i>length</i> is its length, and <i>actor</i> can be consulted for the rest of the string, as explained below. | |||
{ "type":"object", "class":<i>class-name</i>, "actor":<i>actor</i> } | |||
This represents a JavaScript object whose class is <i>class-name</i>. <i>Actor</i> can be consulted for its contents, as explained below. | |||
All actors appearing in grips are children of the pause actor; their names become invalid when the thread is resumed. | |||
Garbage collection operates below the level of this protocol, and will never free objects visible to the client via the protocol. Thus, actors representing JavaScript objects are effectively garbage collection roots. | |||
If the client wishes to hold a reference to an object in the debuggee across pauses, it must send a request to the grip's actor of the form: | |||
{ "to":<i>actor</i>, "type":"thread-grip" } | |||
where <i>actor</i> is the pause-parented actor from the existing grip. The grip actor will reply: | |||
{ "from":<i>actor</i>, "thread-grip":<i>thread-grip</i> } | |||
where <i>thread-grip</i> is a new grip on the same object, but whose actor is parented by the thread actor, not the pause actor. The client can release this grip by sending the current pause actor a request of the form: | |||
{ "to":<i>pause-actor</i>, "type":"release-grip", "grip":<i>grip</i> } | |||
The pause actor will reply, simply: | |||
{ "from":<i>pause-actor</i> } | |||
The client may only send messages to grip actors while the thread is paused, even if the grip actors are children of the thread, not the pause. | |||
== Objects == | |||
<i>can only be manipulated while paused</i> | |||
<i>requests modeled after ES5 object inspection API</i> | |||
<i>special stuff for arrays</i> | |||
<i>special stuff for functions</i> | |||
== Long Strings == | |||
The client can find the full contents of a long string by sending a request to the long string grip actor of the form: | |||
{ "to":<i>grip-actor</i>, "type":"substring", "start":<i>start</i>, "length":<i>length</i> } | |||
where <i>start</i> and <i>length</i> are integers. This requests the substring <i>length</i> characters long, starting at the <i>start</i>'th character. The actor replies as follows: | |||
{ "from":<i>grip-actor</i>, "substring":<i>string</i> } | |||
where <i>string</i> is the requested portion of the string the actor represents. |