-
-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feedback] Overall API design #31
Comments
Overall the API is a delight and painless to use. I'm using this library as a frontend to an emulator and learning Rust at the same time, and this is a breeze compared to our old solution. One tidbit I'm running into now is that the current implementation for handling registers is mostly static - i.e. the target is not passed into For
Though this may conflict with the ability to provide a default implementation. This will require more thought. |
@DrChat thanks for the feedback, I really appreciate it! Funny enough, the API you're proposing is actually what the API was like back in In Now, that being said, it's important to note that you are not locked-in to using the built-in // not actually tested, but something like this ought to work
pub enum PassthroughArch {}
impl Arch for PassthroughArch {
type Registers = PassthroughRegs;
type RegId = PassthroughRegId;
...
}
pub struct PassthroughRegs {
len: usize,
buffer: [u8; MAX_SIZE], // a vec could also work
}
// push logic up a level into `Target`
impl Registers for PassthroughRegs {
fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
for byte in &self.buffer[..self.len] { write_byte(Some(byte)) }
}
fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
self.buffer[..bytes.len()].copy_from_slice(bytes)
}
}
pub struct PassthroughRegId(usize);
impl RegId for PassthroughRegId { ... } // elided for brevity, should be straightforward
impl Target {
type Arch = PassthroughArch;
// .... later on, in the appropriate IDET ... //
fn read_registers(
&mut self,
regs: &mut PassthroughRegs<YourTargetSpecificMetadata>
) -> TargetResult<(), Self> {
// write data directly into `regs.buffer` + `regs.len`
if self.cpu.in_foo_mode() { /* one way */ } else { /* other way */ }
}
// similarly for `write_registers`
} While this will work in
Lastly, if you've got any concrete proposals on how to restructure API, I'm all ears! |
I appreciate the detailed write-up! I'll have to take a look at implementing a passthrough like you described. You're right that it's not ideal for users to know the layout of any GDB internals, and the current API is definitely an improvement over the old one. Though, I just had a thought: Could we define the register file for each architecture using a Something like:
This would eliminate one copy when dealing with registers, and we'd be able to keep the register file implementation details internal to gdbstub (with the exception of |
To preface, here is a brief overview of the current
Theoretically, your proposal to use zerocopy::byteorder would indeed work, and enable an "automatic" implementation of Unfortunately, this approach does come with a few cons:
Now, none of these cons are deal-breakers, but they do make me wonder whether or not saving the extra copy is worth it. If you want to keep riffing on this idea, how about you open a new issue and link back to these comments? |
Ah - you have some really good points! I'm still learning a lot about the language, and it's easy to think "ah, well I can just do it the way I would've done it in C" :) The extra copy and serialize/deserialize isn't that important for me to eliminate (as of now), so I probably won't pursue it too much further. My thought process was moreso to have the ability to directly reuse the existing register definitions in a generic architecture, and on further research it looks like that is do-able using the passthrough method you described. There are a couple of other issues though that need further research, namely that I wonder if there's a way to support both the current static implementation and a more dynamic way (should a I'll work on a PR and we can move this discussion there once I open it. |
Ah, this is a very fair point, since the register size parameter determines the size of the buffer sent back to the GDB client... One quick (albeit breaking) fix would be to change
On a broader note, I've been meaning to overhaul and rework Thanks for bringing up these valuable points about runtime configurable I'm excited to see what you come up with, and if you need any help / want to spitball some ideas, don't hesitate to open it as a draft and get some early feedback. |
Another thing that recently came up - the implementation of However, when single-stepping a core, the GDB client will request a single-step on the target core and a continue on all other cores. The interface may need some rethinking. Per the GDB protocol, the list of actions are processed from left-to-right, wherein the leftmost action overrides actions specified later. |
Heck, I don't even provide a proper example of how to work with the current But yes, the current If at all possible, it'd be great if you could give this new API a shot. It's lumped in as part of some other upcoming changes, but they're mostly superficial code reorganization and/or internal refactoring, so it shouldn't be too hard to upgrade from As always, any and all suggestions are much appreciated.
This is a very good point, and is something I really ought to explicitly document as part of the |
I'm looking to add gdb support to my kernel by hooking an interrupt. Currently, the interrupt handler for the UART supports several single-key sequences to do things like dump the process table, count memory usage, and print process memory maps. I would like to add a new single-key sequence From what I understand, the This doesn't work inside an interrupt handler. What I'd rather have is a state machine into which I can pass characters. As this state machine operates, it calls various functions on the It almost looks like I could do this by turning the |
Thanks for reaching out @xobs! I'm really glad you did, since validating Now, before responding with some preliminary thoughts on how to get In addition, instead of cluttering this general "feedback" issue with our discussion, how about you open a dedicated tracking issue? Maybe something along the lines of "Validate bare-metal EDIT: I ended up spending a few hours after work today hacking together an implementation of a state machine based packet parser. Here's what I came up with: I've given it some thought, and I think that your state machine approach to reading packets may very well work! I'll be honest, I never even considered using a state machine to ingest packets, since in the back of my mind, I was always planning on just spinning up a dedicated Diving into the implementation details, it seems that I only ever call Another thing to keep in mind is how to handle GDB client interrupts. Let me know if that answered your question! Like I mentioned above, if you want to keep this convo going, lets continue in a dedicated tracking issue. |
Did you end up using
gdbstub
in your project?Or maybe you considered using it, but found some major flaw in the API?
In either case, please leave a comment letting me know what you think of
gdbstub
!Also, if it's not "classified info", please share what kind of project you're integrating
gdbstub
into (e.g: emulation, virtualization, embedded, etc...).1.0.0
release.Please don't comment about missing protocol features or bugs. Those should be filed as separate issues.
This tracking issue will remain open until
gdbstub
hits version1.0.0
.The text was updated successfully, but these errors were encountered: