-
-
Notifications
You must be signed in to change notification settings - Fork 45
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
Make single-stepping optional (removing ResumeAction) #92
Conversation
Just find you use |
As I've started doing more hands-on work in While I appreciate your offer, I'm probably going to do my own naming-consistency pass over the codebase at some point in the future, whereby I settle on a clear naming convention for IDETs. |
Update: While working on #89, we discovered that the mainline GDB client doesn't seem to respect the optional nature of single-stepping on certain architectures! Some context: #89 (comment) I spent some time digging into GDB's source code to see if I could figure out why this was happening (including running GDB under GDB while the innner GDB was connected to gdbstub, just so I could see what GDB was going), but so far, I haven't been able to figure out the root cause of why this was happening. All I know is that on the armv4t arch, things seem to work as expected (i.e: if the stub doesn't report support for single-stepping, the GDB client won't send single-step commands), whereas on x86, the GDB client will entirely ignore the lack of reported support, and send a single-step command regardless. I've created a basic repro of this phenomenon over at https://github.com/daniel5151/gdb-optional-step-bug, and will be filing a bug upstream shortly. In the meantime, I'm trying to think how I want to tackle this bug in I suspect I'll have to introduce something similar to the i.e: armv4t works as expected, so it would report Similarly, there would be a new top-level Target method called |
The bug is now filed at https://sourceware.org/bugzilla/show_bug.cgi?id=28440 Lets hope it hasn't actually been sent to |
Just pushed up some changes that add a guard rail to work around this bug. The At the moment, only With this new guard rail, I will probably merge these changes into |
I finally have time to figure out the reason why GDB client doesn't respect the optional nature of single-stepping on certain architectures: GDB parses the result of That means that, although we guess from the spec ( I don't find the clear explanation of The result of above: only the behavior of ARM will be affected by Then the code currently should be changed:
|
Thanks for the deep-dive @bet4it. This is super valuable stuff. Indeed, the idea behind the
Yep, this behavior is definitely not "in the spec", and is strictly an issue with the GDB client implementation. Unfortunately, it's enough of a usability concern that it makes sense for
As is precedent in the project, I would not be comfortable encoding these sorts of invariants without getting sign-off from someone with a working target implementation for a particular architecture. i.e: I'll only merge a arch-level To reiterate though: the baseline behavior of the What I should do is spin up a tracking issue that tracks which current Arch imlpls in |
Also, if you don't mind, could you post that exploration over on the upstream bug report at https://sourceware.org/bugzilla/show_bug.cgi?id=28440? |
I think it's not a bug of GDB. And you did't understand what I meant before. You think step can be optional because if it's not implemented, GDB should emulate it by temporary breakpoints. But I think step can be optional because if the user don't use commands like
According to common sense, the user shouldn't expect step can work if they not implement it ( Yes ARM can give us a suprise but it's not the common behaviour ). And if a user uses gdbstub on MIPS, he will find that step still can't work even if he implements the step function, because GDB will only use breakpoints to implement single step on such architectures, and doesn't consider if you support step by yourself. You use
I'm working on a project that uses |
This is definitely a bug in GDB. If you boil it down to the bare minimum, the bug is very simple: if a client doesn't respond with support for The issue at hand is that when the target doesn't report support for single stepping, and the user tries to use In other words, the GDB client is not respecting the optional nature of single stepping, as specified by the GDB spec, and it needs to be updated to either a) force remote targets on certain arch's (e.g: x86) to support single-stepping, or b) properly report "stepi is not supported" when the target doesn't respond with support for single-stepping.
That's not true at all. The purpose of this guard rail is to work around the aforementioned GDB bug, and avoid the The reason I've made this guard rail configurable on a per-arch level is that the GDB client clearly has the ability to work around the lack of single-stepping support on certain platforms (e.g: ARM), and it would be a shame to avoid exposing that functionality.
Ah, nice! Based on our previous interactions, I had assumed you were working with primarily x86. Sure, if you've got an implementation that works across multiple architectures, I'd me more than happy to take your word on how various features work on a certain arch. e.g: if you have the time, it would be great if you could check whether other platforms exhibit the aforementioned bug, so that we can update their archs'
I don't have access to a MIPS target, so I can't double check that behavior, nor do I fully understand what behavior you're describing here... I interpret what you wrote as as "the GDB client doesn't use the single-step handler on MIPS targets, even if its present". If that's the right interpretation, then that's not great, but it's also not something Though it might be worthwhile to document this behavior somewhere anyways...
You really need to get out of the C/C++ programmer mindset! 😄 Rust the language, and The ethos of e.g: with checked array indexing, Rust has a guard rail for most casual users, ensuring they don't accidentally corrupt their own memory, while also providing e.g: with The crux of all these guard rails, API contortions, and extra engineering effort boils down to two very simple words: user empathy. I hate debugging API misuse issues in C/C++ projects, and I appreciate that Rust's language and community is of the opinion that APIs should be as difficulty to misuse as possible. That makes my life a lot easier as a library consumer, and as such, I try and pass on these gains to folks that end up consuming my library. |
Oh, and as for releasing That said, if you're squeamish about pulling deps directly from git + tying to a particular commit hash, I don't mind pushing out a |
Oh no, you totally don't understand me...
But the spec doesn't point it out. Don't you think we should always follow the spec and if the spec doesn't say something we should assume that it doesn't exist? According to the code of GDB, this behaviour does exist, but only on ARM. And even client responds with support for
How can GDB do this? For example, how can GDB force remote targets support
If the GDB server doesn't respond with support for
Didn't I tell you that according to the code and the behaviour, only GDB on ARM has the ability to work around the lack of single-stepping support? This work around is a special case!
Yes, that's what I want to say.
But the |
Oh no, there should be some small changes we discussed before. For example, make
Oh, it's not necessary. I can just target on the |
That's the entire point of the
Apologies, allow me clarify: when I say "force" I mean "report an error". i.e: if you're trying to resume an x86 target that doesn't support single step, the GDB client should simply refuse error when attempting to Again, if you take a step back for a moment, and look at the bigger picture (ignoring the specific cases of x86, ARM, MIPS, etc...), the core bug is simple and undeniable: the GDB client must respect the feature negotiation that happens as part of It should not send explicitly unsupported vCont packets to the remote target after
Careful now, you're doing the thing again! You know, the thing thing where you make some short-term design decisions based on the concrete implementation details of a particular GDB client 👀 Based on my reading of the spec (and in this case, I'm almost certain that I'm reading it right), all targets should be allowed to omit support for single-stepping (as part of The fact that the current GDB client has a bug, and doesn't follow the spec is not
Oops, yep, you're totally right, and that's something I'd intended to handle in the guard rail, and simply forgot to do. It's very easy to check for - simply augment the guard rail to check if the target has implemented breakpoint support :) Thank you for pointing this out - I'll push out a fix for this shortly...
Shoot, I did say that I'd look into that, didn't I... I'll try and land that change before pushing out |
After digging into this a bit, I realized that this isn't actually the case. If the target doesn't implement breakpoint packets, the GDB client will not send breakpoint packets, and will instead attempt to overwrite target memory with breakpoint instructions (which would only happen if the user enabled the |
I'm a bit busy recently. Why not you just support more architectures (such as MIPS) in gdb-optional-step-bug so you can try it by yourself? I think it's quite simple. |
This is indeed a reasonable approach, though I suspect its one that will sit on the backburner for a while as I work on finishing other things up. I'm also quite busy myself (with the recent thanksgiving long weekend offering a brief window where I could really sit down and work on The key thing to remember is that testing various architectures is something that can be done asynchronously from mainline |
Hi Daniel, I notice you are about to release gdbstub |
Hey @bet4it, To refresh your memory on how the current implementation works:
AFAICT, these two guard rails should work in tandem to cover the cases you care about (notably, your quibbles about MIPS). Please remember that the whole point of these various guard rails is to work around the fact that the mainline GDB client has some edge cases in how it handles targets with "non-standard subsets" of supported features (i.e: spec-compliant, but rarely seen "in the wild"). I'd much rather document + work around these upstream bugs in the context of And of course, if you don't like these guard rails - you can just turn them off! Overriding |
I want to ask you, what's the meaning of You use it to represent whether or not the target supports single-stepping as an optional feature.
And if the target doesn't support it (such as
Is my understanding correctly? Firstly, as I said before, the spec never guarantees the behavior of GDB client need to change base on the result of As for |
So, it does indeed seem that you're misunderstanding the purpose of this guard rail infrastructure. Maybe this is indicative of poor naming or incomplete docs, in which case I'll have to think about what a better name might be... I think the docs make it clear enough, but maybe you can offer some clarifications / suggestions if the current docs aren't sufficient: https://github.com/daniel5151/gdbstub/blob/3e5d2bc/src/arch.rs#L161-L196 In any case, let me go through your explanation and correct some things:
So, I think this might be a root of confusion. The intent here is that the value this method returns is dependant on whether or not the upstream GDB client supports optional single step for the architecture, which in turn will guide whether or not the target is allowed to treat single step as optional. With that in mind...
If you implement single step, This behaviour has nothing to do with the value of this guard rail.
So, clarification: GDB will emulate it by breakpoint, but what kind of breakpoint will depend on whether or not sw breakpoints are supported. If a target doesn't implement single step, AND doesn't implement sw breakpoints, then GDB will literally overwrite instruction memory with undefined instructions to insert a "breakpoint". This is very weird behaviour in some contexts (notably, emulators/hypervisors), which is why the
This is exactly correct. On x86, GDB will NOT use breakpoints (software, or implicit sw) to implement single step, and ALSO ignores the response from
Right, well, that's where I fundamentally have to disagree. I suspect the reason no one has reported this in the past is because no one has ever written a truly generic
So, this is exactly the use case for the other guard rail: In other words, this scenario you're explaining has nothing to do with optionally supporting single step! Please let me know if that makes sense, and if there's anything you still don't understand. I'm very open to improving the docs to avoid this sort of confusion in the future. |
So the key contradiction between us is still that you focus on spec but I focus on implementation.
You think that an ideal GDB client should always respect the result of But it seems that you don't think about the situation that always implements single step by breakpoints in your ideal GDB client? You don't answer me that what value I don't want to discuss on whether it's a bug. I just want to say that the situation will most likely not change during the lifetime of
I agree with you on this. In fact, I expressed the same meaning before:
That's why I think the current situation will most likely not be changed. But you insist to add code to deal with it. I just want to ask you, if I'm a normal user of You think guard rail is so important, then we may add some guard rails to force the user to implement breakpoint if he want to support single step on MIPS? Another question: I find the check of |
Alright, it seems you're on the same page as me wrt how I'm thinking about the situation. That's a start.
Once again, the value of That's it. No more, no less. I will try to make the docs clearer in this respect, and make it more "front and center" that this guard rail is about working around a mainline GDB client bug.
A few very pertinent points in regards to this one statement:
Dude, that is literally the current behavior of the If the user implements support for single-step on MIPS (via the This is an explanation I've included many times, and it seems you're just not listening to me:
I dunno what to tell you man. That's the ethos of my open source project. If you don't like it, feel free to fork And for what it's worth, I've recently had the pleasure (displeasure?) to
The earliest point where I can insert these checks are after the If this is something you'd really like, I can probably expose some kind of "validate_target" method (somewhere) that can be manually invoked to run these checks, though idk if it's really worth it, given that the checks are moreso a development aid, that shouldn't fire during "steady state" operation of the library. |
I'm very clear of it, but I also know that
There are two reasons:
I have confirmed it and I really suggest you to test it before
I just don't want to see the situation that you find the name of
I know it, but how can a new user of That's what may happen when a new user tries to use
Then it's even more improper to add And if I write my GDB client that doesn't totally follow the spec and will panic at some point, will you add guard rails for me? Certainly you won't. But why you add a guard rails for GNU GDB which panics because it doesn't follow the spec?
Oh, if it's not easy to change we can just leave it as this. |
Based on what you've told me, it should be set to It just so happens that the default value is also And like I said, this is not a 0.6 blocker, so if you'd like to open that PR, be my guest. As part of that PR, please include the protocol logs that show the GDB implementation sending a response to
You're conflating single step functionality and single step at the protocol level. If the GDB client request a single step via the GDP RSP, it's my duty as the library author to notify the user of this request - regardless of how they want to handle it. ...unfortunately, the only way to route this message to the user is via the single step IDET + its associated handler, which if left unimplemented, entirely stubs out the packet-parsing codepath related to the Unfortunately, that means that only notification the user gets in these situations would be an opaque With this guard rail in place, this error is avoided entirely, since the user is notified that the GDB client will not respect the spec-compliant optional nature of single step for their chosen arch, and it's up to them to implement the single-step IDET (even if it is just a stub implementation).
I don't entirely understand this statement, but ooookay.
I can't keep responding to this question with the same answer. Please see my previous responses and really think about the implications here. I'm really patient, but responding for a 5th time with the same answer is something I simply cannot do.
This is the reality of software engineering in the real world my friend ¯\_(ツ)_/¯ Specifically, I know for a fact that 99% of users will be using upstream GDB, which means that any upstream GDB bugs that affect the In an ideal world, the mainline GDB client would've followed its own spec to-a-tee, the same way it would expect all other implementations to conform to the spec. Alas, either by historical accident, and/or human oversight, it doesn't (and hasn't!) conformed to this edge case of the spec in a looooong time, which means that as the author of That said, if you are one of the rare folks using Again, to be perfectly crystal clear: these are guard rails, not maximum security holding cells! |
Anyways, I'm not sure if I have the time or energy to keep going on in this back and forth. I'm happy to read + ack any response you might have, but I'm pretty convinced that this functionality should remain in If you don't like it, you can just turn it off (or in your case, for MIPS, keep the default behavior, because it sounds like you'll need to support single step packets anyways). And to be clear: these guard rails are a new idea I'm testing out as part of the 0.6 release. If it turns out folks categorically don't like them, then I might remove them at some point in the future. I'm not claiming to know what's perfect - I'm only human. Cheers |
Skimming through #59, I remember why I went with this approach. It's a two-parter: Pragmatically: In part due to the fact that Ideally: The spec states the following:
Admittedly, this is a bit ambiguous, but I comprehend this statement as: IF you support resuming the target, you MUST support continue, and OPTIONALLY support single-stepping (IF the target support hardware-assisted single-stepping). FWIW, I can understand why single step and continue should be orthogonal features, but the reality is that they seem to be inexorably intertwined with one another. I can see a future where I end up circling back to this issue and making the two orthogonal within This is a niche enough edge case that I'm not going to be blocking 0.6 to change this, but if you feel strongly about this, consider opening a dedicated tracking issue for this. Oh, and lastly - if you really only want to support single step without continue, you can just ignore continue requests (or treat them as single-step requests). Not ideal, but I think the GDB client should be resilient enough to handle that. |
These changes are guided by discussion in: - daniel5151/gdb-optional-step-bug#1 - #92 And in addition, address this minor nit: - 5bc5831#commitcomment-63859961
Quite late to acknowledge this, but it seems there's actually been some movement on https://sourceware.org/bugzilla/show_bug.cgi?id=28440 (re: how certain platforms don't respect optional single stepping). I haven't tested it, but things might finally work properly on GDB main? Would be good to check and see if we can update certain docs. |
Closes #59
See the associated issue for context and rationale.
This is definitely a big API change, but one that is important to make. Single stepping should not be required by the base protocol, as while it's trivial to implement on some targets (e.g: emulators), it is not trivial to implement in others.
The GDB client is able to spoof single-stepping by setting temporary breakpoint instructions in the guest. While this is undoubtedly slower than an optimized single-stepping implementation, it has the key benefit of working "out-of-the-box", without any explicit effort required by the
gdbstub
user to get it working.