This is an archive of the discontinued LLVM Phabricator instance.

[lldb] [llgs] Make `k` kill all processes, and fix multiple exits
ClosedPublic

Authored by mgorny on Jun 10 2022, 7:33 AM.

Details

Summary

Modify the behavior of the k packet to kill all inferiors rather than
just the current one. The specification leaves the exact behavior
of this packet up to the implementation but since vKill is specifically
meant to be used to kill a single process, it seems logical to use k
to provide the alternate function of killing all of them.

Move starting stdio forwarding from the "running" response
to the packet handlers that trigger the process to start. This avoids
attempting to start it multiple times when multiple processes are killed
on Linux which implicitly causes LLGS to receive "started" events
for all of them. This is probably also more correct as the ability
to send "O" packets is implied by the continue-like command being issued
(and therefore the client waiting for responses) rather than the start
notification.

Sponsored by: The FreeBSD Foundation

Diff Detail

Event Timeline

mgorny created this revision.Jun 10 2022, 7:33 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 10 2022, 7:33 AM
Herald added a subscriber: arichardson. · View Herald Transcript
mgorny requested review of this revision.Jun 10 2022, 7:33 AM
mgorny updated this revision to Diff 435939.Jun 10 2022, 9:18 AM

I've gotten so absorbed by fixing server code that I've forgotten to actually finish the test case ;-).

I've ran into the "dynamic uninitialization order fiasco" problem as well. I'm wondering if, instead of the trick (which is pretty neat, but still a trick) with the limbo member variable, we could add something like the asyncio.loop.call_soon function in python to our MainLoop class. So, instead of immediately calling the exited callback (and possibly others as well), the process class would schedule the call to be made upon return to the top-level loop.

What do you think about that?

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
1232

I don't think this is right. I believe the important distinction is whether we have non-stop mode enabled or not, because in all-stop mode we are not able to send stdio while stopped, regardless of how many processes we're debugging.

I've ran into the "dynamic uninitialization order fiasco" problem as well. I'm wondering if, instead of the trick (which is pretty neat, but still a trick) with the limbo member variable, we could add something like the asyncio.loop.call_soon function in python to our MainLoop class. So, instead of immediately calling the exited callback (and possibly others as well), the process class would schedule the call to be made upon return to the top-level loop.

What do you think about that?

Not sure if I follow you. If you mean delaying the whole callback logic, I don't think that would change anything — the problem is that the object gets destroyed in the middle of it. If you mean delaying just the part where it's removed from m_debugged_processes, then I suppose that could work.

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
1232

Well, the code exploded in all-stop mode as well because in order to kill multiple processes, we effectively resume them all. I suppose there could be a way around it (synchronously killing one process after another and waiting for everything to happen) but I'm not convinced this is really worth the added complexity.

mgorny updated this revision to Diff 438643.Jun 21 2022, 5:07 AM

Updated to delay removing the process (and therefore stopping lldb-server) until the last process exits.

mgorny planned changes to this revision.Jun 21 2022, 10:12 AM

This is now pending rebase due to the nonstop patch landing.

mgorny updated this revision to Diff 438818.Jun 21 2022, 1:38 PM

Rebase and fix for nonstop.

labath added inline comments.Jun 22 2022, 11:33 PM
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
1050

The universal reference capture is dangerous for callbacks that outlive the enclosing function. I think that [this,pid] would suffice.

1232

I don't think this needs to be complex at all. What we need to basically do, is call StartSTDIOForwarding whenever the protocol allows us to send stdio, and StopSTDIOForwarding when we cannot. When we supported just a single process, the easiest way to achieve this was to hook into the started/stopped events of that process. Now that's no longer true, so maybe we just need to hook it up elsewhere.

I think starting could be done directly from the packet handlers (c/s/vCont/...). And those handlers already have to check for nonstop mode, so any deviations could be handled there:

if (all_stop) {
  StartSTDIOForwarding(); // Can forward from now until the stop-reply packet is send
  return Success;
} else {
  SendOKResponse();
  // Can we forward now? Or maybe it can be sent all the time?
}

Stopping could probably stay coupled with the sending of the stop-reply packet (i.e., handling of the process event), since it's the stop reply which determines whether the stdio packet can be sent.

mgorny updated this revision to Diff 439405.Jun 23 2022, 8:17 AM
mgorny marked an inline comment as done.

Limited variables passed into the lambda. Reworked starting stdio forwarding per the suggestion.

TODO: rework stopping.

mgorny added inline comments.Jun 23 2022, 8:48 AM
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
1232

Thanks, this makes sense. Good news is that it seems that I can just wire it into our current SendContinueSuccessResponse(), unless I've missed some other case (I've grepped for everything calling Resume() or Kill()). So far I didn't special-case non-stop mode, as I still need to rework O packets for it.

That said, do we want to enable forwarding for kill actions at all? I suppose this was kinda implicit due to the whole Linux PTRACE_EVENT_EXIT ping-pong but I honestly doubt any output can happen there.

mgorny added inline comments.Jun 23 2022, 9:23 AM
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
1232

Oh, and regarding non-stop mode, I've left it as TODO for now. The whole stdio forwarding needs to be fixed for non-stop mode anyway, and since we don't expect to have two processes running simultaneously yet, I'm going to look into it separately.

That said, this is probably going to be a "LLDB extension". FWICS gdb pretty much uses O packets only to deliver debugger-specific messages and doesn't forward stdio at all, nor special-cases O in non-stop mode. My initial idea is to always send O as asynchronous notifications. I suppose we could then enable stdio forwarding as soon as non-stop mode is enabled, and keep it enabled until the very end.

mgorny updated this revision to Diff 439433.Jun 23 2022, 9:25 AM
mgorny edited the summary of this revision. (Show Details)

Remove the extra check from StopSTDIOForwarding() — we currently don't have to account for multiple processes actually running simultaneously (only the weird Linux PTRACE_EVENT_EXIT handling), so we can just stop it when the first of killed processes exits.

labath accepted this revision.Jun 24 2022, 6:54 AM
labath added inline comments.
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
1232

Using async notifications sounds reasonable. Also, ew, I did not realize that's what O packets were meant for.

I don't think we need to enable forwarding for kill packets. In fact, I would be surprised if lldb was prepared to handle them here.

This revision is now accepted and ready to land.Jun 24 2022, 6:54 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 24 2022, 8:20 AM