Skip to content

(API Overhaul) Trait-based Protocol Extensions#24

Closed
daniel5151 wants to merge 12 commits intomasterfrom
trait-based-opt-features
Closed

(API Overhaul) Trait-based Protocol Extensions#24
daniel5151 wants to merge 12 commits intomasterfrom
trait-based-opt-features

Conversation

@daniel5151
Copy link
Copy Markdown
Owner

This is a pretty massive API overhaul which uses a pretty nifty type-system + compiler optimization trick to emulate optional trait methods at compile time (along with a bunch of other useful properties, such as compile-time trait method grouping / mutual exclusion, and improved performance).

As far as I can tell, I'm the first one to stumble across this hack (and/or make such extensive use of it), so I've given it the working title of "Inlineable Dyn Extension Traits" (IDETs). I'm already working on a writeup that talks about this technique! See https://github.com/daniel5151/optional-trait-methods

Technical details aside, the end-result of this PR is to make the Target API significantly more robust for end users, along with simplifying a lot of the GdbStub internals that handle OptResult and the MaybeUnimpl.

Scope-wise, this PR doesn't touch any code outside of the gdbstub_impl and target modules. Notably, the arch module is left completely unchanged.

Honestly, I'm pretty happy with how this is looking.

This current approach is reminiscent of a C-style
struct-of-function-pointers implementation, but with Traits instead. I
thought of this approach a while back, but never got around to
implementing it. Glad I found the time though, since it's really cool!

While the API is somewhat "unorthadox", the final implementations look
quite clean, and are easier to follow than having the single monolithic
`Target` method as in previous versions. Plus, I was able to remove the
gnarly `OptResult` type, which I always felt was really dirty!

Similarly, the internal implementation could be cleaned up quite a bit
as well, since checking for a supported feature doesn't actually requite
_invoking_ an associated method. Just check if the ops "pointer" is None
or not!

* * *

There are a couple more things I'd like to experiment with:

- is there some way that GdbStub's internal implementation can be
broken-up similarly to the public API? i.e: is there some way to move
the packet parsing + handler code for, say, `handle_monitor_cmd` into
it's own little module, which could then be dead-code-eliminated if a
target doesn't end up using that feature?
- more generally, what's the overhead of this abuse-of-dyn-traits
approach? I vaguely recall playing around with this idea in godbolt, and
being surprised by how effective the compiler was able to inline
everything, though I should probably run some more proper experiments...
  - Heck, that would probably be an interesting topic for a blog post!
oh boy, did I just write a good chunk of my next blog post??
After all, there's no need for the "specify the operation using an
enum" hack anymore. The two operations can be independant methods, and
the Rust compiler will enforce that both must be implemented!
If a target doesn't implement the `MonitorCmd` extension, then it seems
like a shame to have it include a bunch of unused code related to
parsing + handling `qRcmd` packets, right?

Well, with some clever match guard hinting + the power of Inline Dyn
Extension Traits, it's possible to trick the Rust compiler into
optimizing unused packets out of the final binary!

Now, i'm no compiler engineer, so I can't say whether or not this
optimization is "guaranteed" to occur or not. That said, given how
simple the __protocol_hint method is, and how it can monomorphize to a
static `true / false` boolean, I strongly suspect that it will almost
always perform the optimization.

To assist in implementing + debugging this feature, I've included a new
internal `__dead_code_marker!` macro (and associated feature), which are
used by `script/test_dead_code_elim.sh` to check whether or not a
certain packet is optimized out in the example binaries. See the inline
documentation for how that script works...
Ughh, it took a long while, but I've finally come up with an API for
multiprocess support that I'm happy with, _and_ fixed some icky hacks
and bad implementation choices in the core gdbstub implementation.

Some interesting things that come out of this (chonker of a) commit:

- Turns out the `qC` packet is totally useless, so I've gone ahead and
removed it entirely. The less code the better.
- I've finally fixed the incredibly confusing naming scheme surrounding
the thread-id structures. The new names should be much more sane (and
have less overlap)
- I've massively improved the `resume` method for multithreaded targets.
The key insight is that returning a tuple of `(Tid, StopReason)` wasn't
exactly correct, as there are certain stop reasons that are _not_
associated with any Tid (e.g: GDB interrupt), and it effectively forced
the target to "flip a coin" as to which Tid it reported. This old system
wreaked havoc when dealing with breakpoints in multithreaded systems,
but with the new `ThreadStopReason` API, i've managed to clean up the
library internals to keep the GDB nice and happy, and removed the burden
of making arbitrary decisions from the Targer implementation.
- vCont works now! I'm a dummy, and never actually realized that my
vCont implementation was totally busted. I've gone ahead and fixed it,
so it should be working now.
hmm, not sure when this bug was introduced... Well, it's fixed now, so
that's good.
just a few things that I noticed while rummaging around in the codebase.
they don't really merit individial commits...
While there are still many external types annotated with
`#[derive(Debug)]`, I've removed the debug impls for internal types, to
assist in code size reduction.
Upon further thought, I've realized that using the unit type as the
"unimplemented" RegId type is actually quite detrimental to the end-user
experience. It forces the user to fork `gdbstub` if they want to
implement the `read/write_register` methods for a built in architecure,
which could drive users away...

By passing through the raw GDB identifier, it gives users a chance to
"rough it out" on their own, while urging them to upstream their
implementations as well.
@daniel5151 daniel5151 force-pushed the trait-based-opt-features branch from 794f56c to 5b51d61 Compare September 9, 2020 17:14
@daniel5151
Copy link
Copy Markdown
Owner Author

(these changes have been merged into master, so I'm closing this draft PR)

@daniel5151 daniel5151 closed this Sep 9, 2020
@daniel5151 daniel5151 deleted the trait-based-opt-features branch September 9, 2020 17:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant