daniel5151 / gdbstub

An ergonomic, featureful, and easy-to-integrate implementation of the GDB Remote Serial Protocol in Rust (with no-compromises #![no_std] support)
Other
306 stars 49 forks source link

Support GDB Agent Expressions [for conditional breakpoints, breakpoint commands, tracepoints, etc...] #40

Open daniel5151 opened 3 years ago

daniel5151 commented 3 years ago

See https://sourceware.org/gdb/current/onlinedocs/gdb/Agent-Expressions.html#Agent-Expressions

In some applications, it is not feasible for the debugger to interrupt the program’s execution long enough for the developer to learn anything helpful about its behavior. If the program’s correctness depends on its real-time behavior, delays introduced by a debugger might cause the program to fail, even when the code itself is correct. It is useful to be able to observe the program’s behavior without interrupting it.

When GDB is debugging a remote target, the GDB agent code running on the target computes the values of the expressions itself. To avoid having a full symbolic expression evaluator on the agent, GDB translates expressions in the source language into a simpler bytecode language, and then sends the bytecode to the agent; the agent then executes the bytecode, and records the values for GDB to retrieve later.

The bytecode language is simple; there are forty-odd opcodes, the bulk of which are the usual vocabulary of C operands (addition, subtraction, shifts, and so on) and various sizes of literals and memory reference operations. The bytecode interpreter operates strictly on machine-level values — various sizes of integers and floating point numbers — and requires no information about types or symbols; thus, the interpreter’s internal data structures are simple, and each bytecode requires only a few native machine instructions to implement it. The interpreter is small, and strict limits on the memory and time required to evaluate an expression are easy to determine, making it suitable for use by the debugging agent in real-time applications.

One notable application of Agent Expressions not included in this overview is that breakpoint packets support specifying conditions and/or commands as bytecode expressions to be executed directly on the device. This enables significantly faster conditional breakpoints, as the target does not have to stop execution, communicate with the remote GDB client, and wait for the client to execute the condition.


There are several considerations to keep in mind while working on this feature and designing its API:

Some final things of note:

daniel5151 commented 3 years ago

I have spent quite a bit of time toying around with this, and ended up hacking together an implementation that works, but misses the mark in terms of API ergonomics. The code is available on the feature-draft/agent branch.

The biggest issue with that implementation is that the API forces users to do some "ownership gymnastics" in order to work correctly. In a nutshell, I structured the Agent API the same way I would any other protocol extension: as a direct IDET of the Target trait. This had the unfortunate side-effect that it tied that BytecodeStorage and BytecodeExecutor lifetimes directly to the target, which is really bad, since the BytecodeExecutor needs to mutably modify the target while at the same time immutably borrowing the bytecode expression from the BytecodeStorage - which is part of the target itself!

I spent a bit more time poking away at the implementation, and while I feel like it is salvageable, I don't think I have the mental energy to keep iterating on it. As such, I've extracted the relevant code and put it into the aforementioned feature-draft/agent branch, with the expectation that I will salvage bits-and-pieces of the implementation when I eventually get around to re-visiting this feature.

That said, it does in-fact work, and if you run the armv4t example on that branch, you should be able to set conditional breakpoints that get executed on the target! It also demonstrates how to use khuey/gdb-agent as a bytecode executor.


If you stumble across this issue, and you're interested in seeing this get implemented, feel free to leave a comment and/or help out with an implementation!

khuey commented 3 years ago

FWIW I think the heavy part of making gdb-agent work in no_std is dealing with the error handling. Beyond that I doubt it would be too bad.