| # Zxdb client library |
| |
| This is a library for writing debugger frontends. The client library provides a |
| way to connect to a remote Fuchsia system's running debug agent. It maintains |
| the objects that represent running processes, threads, and stack frames, and |
| provides a way to query and control these things. |
| |
| ## Main objects |
| |
| From largest to smallest. |
| |
| ### Session |
| |
| This is the main global object. It represents the connection to the remote |
| system and provides functions to `Connect` and `Disconnect`. |
| |
| The basic debugger initialization is to make a `Session` object and run the |
| message loop (in `src/developer/debug/shared`). |
| |
| The IPC layer is represented by the `RemoteAPI` which the Session maintains. The |
| `RemoteAPI` is often mocked for testing. |
| |
| ### System |
| |
| This is the client representation of the remote system. Its main job is to |
| maintain the list of `Target` objects. It also maintains breakpoints and |
| filters. There is a 1:1 correspondence between Session and System (they could be |
| the same object but we split them between "connection-related" and |
| "OS-state-related" to keep things smaller). |
| |
| ### Target |
| |
| A target is a holder for a process. The process itself may or may not be |
| running. Requests to start or kill a process are on the target and there is |
| always at least one in the `System`. The target also maintains state that needs |
| to survive across process launches like command line arguments. |
| |
| When running, the `Target` will have a `Process` object. |
| |
| ### Process |
| |
| A running process on the remote system. It holds the running threads and |
| provides an API to read and write memory. There are also functions to do |
| process-wide suspend and resume. |
| |
| In the console frontend, a "process" noun actually represents a target in the |
| client library. The console frontend process may or may not be running or |
| attached, while a client `Process` object always represents a running process. |
| |
| ### Thread |
| |
| A running thread in a process. It provides a nonempty `Stack` when paused, and |
| the main stepping logic is here (see "Thread Conrol" below). |
| |
| ### Stack |
| |
| The backtrace of a `Thread` when the thread is paused. At its simplest it's a |
| list of stack frames, but there is a lot of extra complexity around handling of |
| inline stack frames. |
| |
| In our system when a thread stops at an exception only the top two stack frames |
| are sent from the `debug_agent` running on the remote system. This makes things |
| faster since many exceptions are internally handled to implement stepping and |
| unwinding the whole stack can take time. If you need the whole stack you will |
| need to asynchronously `SyncFrames`. |
| |
| ### Frame |
| |
| One stack frame. Some stack frames are real and some represent inline function |
| calls without any real information on the stack. Usually when you want to get a |
| variable or evaluate an expression you do it in the context of a stack frame. |
| You do this by getting the frame's `EvalContext`. |
| |
| ## Other objects |
| |
| ### Breakpoint |
| |
| Breakpoints are global and are maintained by the System. You can have process- |
| and thread-specific ones but those states are a property of the breakpoint. The |
| breakpoint objects themselves are still global. |
| |
| ### JobContext / Job |
| |
| This is like the Target/Process split. The JobContexts is the holder for a job |
| that may or may not be attached, and the Job represents an attached job on the |
| remote system. |
| |
| In Zircon you use attach to jobs to watch for process launches. It is like a |
| breakpoint for jobs. |
| |
| ### Filter |
| |
| A filter is a way to watch for processes on jobs and automatically attach when |
| the name matches. |
| |
| ## Thread control |
| |
| Stepping is implemented by `ThreadController` objects. To do a certain kind of |
| step operation on a thread, you instantiate the type of `ThreadController` that |
| implements the operation you want and call `Thread::ContinueWith` to run that |
| controller. The thread controller will watch for exceptions and resume as |
| necessary to implement its operation. |
| |
| The main ones are: |
| |
| * `FinishThreadController` |
| * `StepOverThreadController` |
| * `StepThreadController` (implements various kinds of "step into") |
| * `UntilThreadController` |
| |
| Thread controllers ares nested so that you can to a "step over", hit an |
| exception and do some other stepping, and the thread sill still stop when the |
| original "step over" complets. |
| |
| ## Expressions |
| |
| Expression and variable evaluation is provided by the `expr` library. To run |
| this you give it an `EvalContext` in which to do it's job. This context is most |
| commonly provided by a `Frame` which provides access to the local variables and |
| all process state. |