eclipse-cdt-cloud / cdt-gdb-adapter

CDT GDB Debug Adapter
Eclipse Public License 2.0
30 stars 40 forks source link

Send interrupt before disconnecting from remote #287

Closed AdhamRagabMCHP closed 1 year ago

AdhamRagabMCHP commented 1 year ago

Ensure that for a remote target, an interrupt signal is sent before a disconnect request to ensure we only disconnect from a halted target.

This resolves an issue that, on remote targets, can be reproduced as follows:

  1. Start a debug session inside VSCode, setting a breakpoint at a known location beforehand.
  2. Continue after the breakpoint is hit
  3. Stop the debug session.

The disconnect request errors out, returning the following error message:

"Cannot execute this command while the target is running. Use the 'interrupt' command to stop the target and then try again."

This results in the GDB Server not being killed, which can cause issues for future runs.

Note that this only happens when we continue after hitting a breakpoint and try to stop without pausing. If we never set a breakpoint to begin with, we can disconnect without pausing just fine.

@jonahgraham Please let me know if there needs to be a test written for this, and if so, how: I tried to look for a test case that deals with the disconnect request, but I couldn't find one.

jonahgraham commented 1 year ago

I tried to look for a test case that deals with the disconnect request

The shutdown / termination / disconnect code in cdt-gdb-adapter is undertested. The reason I would like to see a test for your change is so that we try not to regress here. However I think we can forgo the test as this is just a "fixup" of #273.

If you are able to write some tests to verify disconnect is working as expected it would be most welcome - but I recommend doing that independently of this PR.

AdhamRagabMCHP commented 1 year ago

@jonahgraham I updated the code, but I was having to introduce some sort of tiny timeout to ensure it actually has time to pause - this.gdb.pause is a sync function it seems to an await will have no effect. If you know of a better way to make use of this, I'd really appreciate it, but this was how I managed to get it to work.

jonahgraham commented 1 year ago

@jonahgraham I updated the code, but I was having to introduce some sort of tiny timeout to ensure it actually has time to pause - this.gdb.pause is a sync function it seems to an await will have no effect. If you know of a better way to make use of this, I'd really appreciate it, but this was how I managed to get it to work.

Hmm. That is problematic to get this right. This snippet of code is how other places handle it in the code that need to ensure target is paused before operating on it. The snippet ensures that the the target actually stops before proceeding. I think we need to do something similar here, but we probably need to refactor it to make it more (re)usable

https://github.com/eclipse-cdt-cloud/cdt-gdb-adapter/blob/250881c64415e076d3b66068a3c7057ab9a3130f/src/GDBDebugSession.ts#L474-L492

But first, lets separate these two issues - for that I am going to take a step back to your original patchset.

What happens in your original patchset (i.e. just sending interrupt) if the target is running but is slow to respond (e.g. in a tight uninterruptible loop). Does it handle things properly? What if the target was already paused and you issue the interrupt, do you get an error message?

I think we can document (in the code) as only supporting this shutdown sequence when using async mode and only sending the interrupt if this.isRunning is true.

AdhamRagabMCHP commented 1 year ago

The test case I've been using is running an infinite loop at the end (by design) when I am trying to stop - Pausing and then stopping doesn't cause any issues, so no error message is received - it just doesn't respond with the SIGTRAP like the case when it's actually running when I attempt to stop.

Sending just "interrupt" generally hasn't caused issues as far as I can see. Would the following be a good solution for this:

if (this.gdb.gdbAsync && this.isRunning) { this.gdb.sendCommand('interrupt'); } this.gdb.sendCommand('disconnect');

This sequence seems to work properly, and when the target is paused, doesn't attempt to send the interrupt signal.

jonahgraham commented 1 year ago

Would the following be a good solution for this

It looks right, I'll double-check once you submit it as a new commit.

jonahgraham commented 1 year ago

Replaced with #289