-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[lldb] Claim to support swbreak and hwbreak packets when debugging a gdbremote #102873
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
Conversation
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write If you have received no comments on your PR for a week, you can request a review If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
@llvm/pr-subscribers-lldb Author: xusheng (xusheng6) ChangesThis fixes #56125 and vadimcn/codelldb#666, as well as the downstream issue in our binary ninja debugger: Vector35/debugger#535 Basically, lldb does not claim to support the No new code is added to add support I am unable to add a unit test for this. I browsed the gdbserver unit test code and it seems to be testing against the gdbserver in lldb, which also does not use the To test this PR, you can use lldb to connect to a gdbserver running on e.g., Ubuntu 22.04, and see the program counter is off-by-one without the patch. With the patch, things work as expected Full diff: https://github.com/llvm/llvm-project/pull/102873.diff 2 Files Affected:
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 74e392249a94eb..d8b17a8ff59baf 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -353,7 +353,7 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
// build the qSupported packet
std::vector<std::string> features = {"xmlRegisters=i386,arm,mips,arc",
"multiprocess+", "fork-events+",
- "vfork-events+"};
+ "vfork-events+", "swbreak+", "hwbreak+"};
StreamString packet;
packet.PutCString("qSupported");
for (uint32_t i = 0; i < features.size(); ++i) {
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 6f9c2cc1e4b4e8..b8fe8fdc9b8742 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -2349,6 +2349,9 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) {
if (!value.getAsInteger(0, addressing_bits)) {
addressable_bits.SetHighmemAddressableBits(addressing_bits);
}
+ } else if (key.compare("swbreak") == 0 || key.compare("hwbreak") == 0) {
+ // There is nothing needs to be done for swbreak or hwbreak since
+ // the value is expected to be empty
} else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) {
uint32_t reg = UINT32_MAX;
if (!key.getAsInteger(16, reg))
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
On the testing, lldb sends this qsupported value regardless of what the remote sends. So there's nothing to do there unless you just repeat the feature list. There are some tests that look at lldb-server's qsupported values, but I don't see any for the client lldb. You could check we don't crash when seeing swbreak/hwbreak in a stop response, but it's the same as checking we don't crash on any other unknown key in there. Since you can't observe any behaviour change from swbreak/hwbreak, so I don't think that has much value either. What you could do is write a test where lldb connects to a mock gdbserver that claims to be an architecture where the correction to addresses would be done by a gdb server (GDB's gdbserver I mean). Then via lldb check that the stopped location is unmodified. You could then run the test with swbreak / hwbreak / neither of those in the stop info and each time lldb will report the value unchanged. So in the unlikely case that we decided to change how lldb behaves, the test would fail. |
Also there is probably a qsupported parsing function in lldb-server, please add a comment there to say we consume lldb's swbreak/hwbreak feature but it doesn't change the behaviour of lldb-server. |
Fixed in d0851d4 |
I totally agree with you that the first two cases, even if added, would not provide much value. For the third case, i.e., writing a mock gdbserver, I think that is quite a bit of work -- or do we already have some infra that I can use? By the way, do I always need some unit test change to get a PR accepted? In this particular case I do not see a compelling reason to add a new test, though if this is a LLVM development policy then I will start looking into it. |
In fact, if i386 is an architecture where this correction of PC might happen (or not) you could just extend that test. The result should be the same whether the mock gdbserver returns "swbreak" "hwbreak" or leaves that field out completely. If i386 isn't an architecture where correction would be done, you should be able to find some other target xml stubs elsewhere in tests. I don't think this test case is i386 specific. It can be tricky because that XML needs to include a minimal set of registers before lldb will function, so if in doubt, copy paste a working one.
Not 100% of the time but you will always have to justify why it does not have tests, or has the sort of tests that it has. This justification is useful for auditing code later, it also serves as a guide to anyone trying to reproduce an old bug that has no test cases. https://llvm.org/docs/DeveloperPolicy.html#test-cases is written from the llvm perspective but applies to lldb as well https://lldb.llvm.org/resources/contributing.html#test-infrastructure. In this case because no one does lldb -> gdbserver testing within the llvm project, so my worry is that this "easy fix" will get forgotten over time and someone will remove it as dead code or forget it in a refactoring. This is not a case of "if it has no tests it will get deleted" but it will certainly be at higher risk. We have had cases like this before for example, someone fixed a bug handling an msp430 simulator and the test for that replays the packets that it sent to lldb so we don't have to have the actual simulator to hand. |
AArch64 for example would be:
Which I got by working out what |
Yes, i386 is an architecture where the PC adjustment happens, and I have tentatively added a unit test for it in 55f9473 |
The test steps are fine, but I was assuming that we'd extend the existing |
✅ With the latest revision this PR passed the Python code formatter. |
Thanks for the contribution! The test especially, it will keep us honest and protect your downstream tools going forward. |
@xusheng6 Congratulations on having your first Pull Request (PR) merged into the LLVM Project! Your changes will be combined with recent changes from other authors, then tested Please check whether problems have been caused by your change specifically, as How to do this, and the rest of the post-merge process, is covered in detail here. If your change does cause a problem, it may be reverted, or you can revert it yourself. If you don't get any reports, no action is required from you. Your changes are working as expected, well done! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for writing up the patch. We do see some handling of this behavior for x86 in lldb, e.g. ProcessWindowsNative, and KDP protocol support with x86 Darwin kernel debugging, also breakpoint-pc-offset
in ProcessGDBRemote. But we do assume that the pc value reported by a thread that hit a breakpoint will be the start of the breakpoint instruction, not the end.
The only question I have is -- if we are talking to a stub that supports swbreak
/hwbreak
, and we get a stop packet without those that hit a breakpoint, do we need to decr the pc value? I'm guessing gdbserver et al don't report a reason:breakpoint
key-value pair in the stop packet. Should we treat the presence of swbreak
or hwbreak
as equivalent to receiving a reason:breakpoint
in the packet (see the reason
argument passed to ProcessGDBRemote::SetThreadStopInfo
) to unambiguously indicate that a breakpoint was hit by this thread?
@@ -4245,6 +4245,10 @@ std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures( | |||
.Case("vfork-events+", Extension::vfork) | |||
.Default({}); | |||
|
|||
// We consume lldb's swbreak/hwbreak feature, but it doesn't change the | |||
// behaviour of lldb-server. We always adjust the program counter for targets | |||
// like x86 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please end the comment with a period.
@@ -2354,6 +2354,9 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) { | |||
if (!key.getAsInteger(16, reg)) | |||
expedited_register_map[reg] = std::string(std::move(value)); | |||
} | |||
// swbreak and hwbreak are also expected keys, but we don't need to | |||
// change our behaviour for them because lldb always expects the remote | |||
// to adjust the program counter (if relevant, e.g., for x86 targets) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please end the comment with a period.
oh lol I missed that this was already merged. But I am curious what you think about treating |
Just to be clear, if I'm understanding the packet we'll be getting back, we have no indication that we hit the breakpoint, we only show that we are stopped at an address which has a breakpoint. Current lldb stepping behavior will work -- because the rule is, when we stop at a breakpoint address, we will say we hit the breakpoint. I am refining a patch #96260 which changes this behavior -- we handle "instruction stepped / stopped at a breakpoint" differently than "hit a breakpoint". I worry this difference will be lost with a stub that reports |
My work mainly concerns the case when lldb is connected to a gdbserver, and as far as I can tell, gdbserver does not send a Also, I do not think lldb itself is adjusting the PC -- I think lldb-server does it, and that is also the reason why my PR did not need to alter the lldb behavior at all. lldb always expects the remote to have adjusted the PC if necessary, and it just uses whatever value that is reported |
Yes, it does.
That is true, but I believe Jason is saying something slightly different. Currently you are able to use lldb+gdbserver because lldb treats a stop as a breakpoint hit every time we stop and the PC value is equal to one of the breakpoints. Jason's patch will change that and have lldb report a breakpoint hit only if it server tells it that was the reason for the stop. lldb-server does that ( To (preemtively) fix that, you should probably change lldb to recognize the gdb response ( |
Oh I see your point now, and I also see the lldb is handling a @jasonmolenda do you think you can handle this along with your patch, or you think I should do something for it preemptively as suggested by labath? I personally prefer the former case because I am quite new to the lldb code base |
No problem, I'll make that change in ProcessGDBRemote when I re-land my patch (I have a few outstanding issues the CI bots found when I first landed it). My change will be untested, but it is a simple change. We already fake up a reason:watchpoint stop reason when we get |
This fixes #56125 and vadimcn/codelldb#666, as well as the downstream issue in our binary ninja debugger: Vector35/debugger#535
Basically, lldb does not claim to support the
swbreak
packet so the gdbserver would not use it. As a result, the gdbserver always sends the unmodified program counter value which, on systems like x86, causes the program counter to be off-by-one (or otherwise wrong). For reference, the lldb-server always sends the modified program counter value so it works perfectly with lldb.https://sourceware.org/gdb/current/onlinedocs/gdb.html/Stop-Reply-Packets.html#swbreak-stop-reason
No new code is added to add support
swbreak
, since the way lldb works already expects the remote to have adjusted the program counter. The change just lets the gdbserver know that lldb supports it, so that it will send the adjusted program counter.To test this PR, you can use lldb to connect to a gdbserver running on e.g., Ubuntu 22.04, and see the program counter is off-by-one without the patch. With the patch, things work as expected