RemoteDebugging: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
(Describe nested debugger invocation.)
(Explain nested debugging. Remove discussion of same-stack debugging and its limitations.)
Line 36: Line 36:
evaluateInFrame facility:
evaluateInFrame facility:


[[File:expression-evaluation.png]]
[[File:Expression-evaluation.png]]


If evaluation of the expression throws an exception or hits a breakpoint,
If evaluation of the expression throws an exception or hits a breakpoint,
Line 45: Line 45:
resulting in a nested debug server invocation:
resulting in a nested debug server invocation:


[[File:expression-nested-exception.png]]
[[File:Expression-nested-exception.png]]


If the debugger elects to debug a worker thread, the main thread acts as a
If the debugger elects to debug a worker thread, the main thread acts as a
Line 62: Line 62:


[[File:content-process.png]]
[[File:content-process.png]]
== Same-Stack Debugging ==
In the current model for debugging Firefox, the debugger runs in the same
process as the debuggee. Since the XUL user interface only allows one
thread to interact with it, the debugger's user interface must share a
thread, and thus a stack, with the debuggee. Thus, when the debuggee is
paused and the user is interacting with the debugger's user interface, the
C++ stack looks like this:
{| border="1"
| debugger UI frames<br>(that is, more interpreted/JITted JS frames)
|-
| nested event loop invocation
|-
| debugger machinery frames
|-
| interpreter/JITted frames for debuggee
|-
| top-level event loop
|}
There are a number of complications that arise from this model:
* The debugger's UI and the debuggee share a DOM, and may interact with each other in unexpected ways through that DOM.
* The debugger should never refer to the debuggee's objects directly --- it is too easy to introduce bugs and security holes by doing so. However, avoiding this is similar to the problem of ensuring that references between Firefox chrome and content go through the proper wrapper objects. This seems to be challenging in practice.

Revision as of 22:44, 19 May 2010

Remote Debugging

Mozilla will support remote debugging with a two-level protocol, roughly modeled after the Debugger Protocol and the Chrome Developer Tools Protocol.

The remote protocol operates at the JavaScript level, not at the C++ or machine level. It assumes that the JavaScript implementation itself is healthy and responsive: the JavaScript program being executed may have gone wrong, but the JavaScript implementation's internal state must not be corrupt. Bugs in the implementation may cause the debugger to fail; bugs in the interpreted program must not.

Debugging States

When a debugger client first makes a connection to a Mozilla process, no debugging sphere has yet been selected, and the server uses the js::dbg2 sphere discovery facilities to answer client requests about what is available to be debugged in that process:

Initial-connection.png

Selecting a debugging sphere establishes event handlers which invoke the debug server to communicate the news to the debugger. Assuming the selected sphere runs on the main thread, the stack looks like this while the event is being reported:

Main-thread-event.png

When the debugger asks the debuggee to continue, the frames for the js::dbg2 dispatch facilities simply return, and the code that generated the event resumes execution.

If the user asks the debugger to evaluate an expression that requires evaluating JavaScript code (like e.x()), then that evaluation takes place without leaving the dynamic scope of the trap handlers, but in the static scope in which the event was generated, using js::dbg2's evaluateInFrame facility:

Expression-evaluation.png

If evaluation of the expression throws an exception or hits a breakpoint, then the result is a matter of user interface. Either we abandon evaluation of the expression, and control returns to the original debug protocol server frames, or we treat the event as something to be investigated, just as if it had occurred in the debuggee's normal course of execution, resulting in a nested debug server invocation:

Expression-nested-exception.png

If the debugger elects to debug a worker thread, the main thread acts as a proxy, relaying messages to the worker thread's server:

Worker-thread-event.png

(I am not sure whether we want to keep things this way, or have the debugger connect directly to its debuggees. Being able to debug many threads with a single socket connection seems like a win, and we still get the inter-thread synchronization benefits; but latency might affect the debuggers' users' experience. Let's code it and see!)

If Mozilla puts content and chrome in separate processes, then the remote protocol can be readily used to continue to support content debugging:

Content-process.png