Skip to content

feat: gdb SIGINT interrupt mode #1124

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

deermichel
Copy link

We use a NXP S32 Debug Probe together with the provided gdb server. I configured your (awesome) extension on "external" mode, but the (funky) server does not react to "exec-interrupt" commands. To get it working, I added functionality to send a SIGINT signal to the gdb process instead - and thought maybe others might be interested in this addition as well.

This PR:

  • adds gdbInterruptMode to launch props, default is exec-interrupt == current stock behavior, new option SIGINT
  • handles interrupt accordingly in the mi2 backend
  • refactors interrupt calls in gdb.ts

@haneefdm
Copy link
Collaborator

Thanks for the PR and the kind words.

Hmmm. Could it be that your gdb-server senses that the stdin is interactive because of how it is started and thus only responds to Control-C? Normally, when cortex-debug starts a gdb-server, the gdb-server is a pipe. Programs can tell what kind of stdin they have and adapt their behavior .. albeit inappropriately.

But there is one big difference between send GDB a Control-C vs us sending an interrupt request to GDB. Note that we are not requesting this of the server. We are talking to gdb. So, why is gdb not doing the right thing? How do other debugger frontends deal with it? How does NXP's own frontend deal with it? I can't believe they don't support exec-interrups. I have a feeling we are solving the wrong problem.

Now, here is the difference. sending gdb exec-interrupt is a synchronous operation and we expect a response for success. Yes, it can hang. Here, sending a SIGINT does not produce a response. SIGNAL may be delivered to the GDB process but what happens after than, how do we know?

Could you point me to some NXP docs for their gdb-server? Is the following correct?

https://mcuoneclipse.com/2023/05/14/linkserver-for-microcontrollers/

@haneefdm
Copy link
Collaborator

I don't understand what we are doing here, and why/how it is working.

  • You send SIGINT (equiv of Ctrl-C) to gdb
  • What does gdb do with it? How does it end up sending an interrupt/halt request to the gdb-server
  • Note that the only connection between gdb and the server is the TCP port. You can't send a Ctrl-C/SIGINT via a TCP port?!?!?
  • So, gdb is probably sending something already defined in the GDB serial protocol
  • Note that GDB does not have the PID of the server....so it can't even send a Ctrl-C to it.

So, how is this working? Sending gdb exec-interrupt should send the same request via the TCP port. Did you debug the gdb to server communication? You can enable this in gdb and it will show you all the traffic. You can also enable it from the server side in most cases.

Or, did you end up pausing gdb and not the FW (program/inferior) itself? I am at a total loss here

@deermichel
Copy link
Author

Right, honestly I'm sharing your feeling about fixing symptoms rather than issues - though finally happy to get it working at least "somehow" :). But your point about the missing feedback loop makes sense.

There's not much information about their gdb server (gta) available, and the --help didn't yield any relevant configuration parameters. If you have access to S32 Design Studio (it's probably free?), you can look through this guide - grep for "gta" (gdb target access server).

How I understand that it works right now (and I can confirm it's actually interrupting the FW in my setup):

  • send SIGINT to gdb (frontend)
  • gdb frontend will "somehow" request an interrupt from the gta server (can you please elaborate how to enable logging here?)
  • gta halts the core

It's probably worth exploring how their own S32DS IDE interacts with the gta server.

@haneefdm
Copy link
Collaborator

Yup. We need to investigate why it is working. Please look at the debug traces between GDB and the server. You can do the following in GDB

set debug remote 1

Use that command as part of preLaunchCommands or on the gdb command line or the Debug Console in VSCode or your gdbinit file. You should be familiar with the remote serial protocol (RSP).

https://ftp.gnu.org/old-gnu/Manuals/gdb/html_node/gdb_124.html#SEC129

Whatever is happening via SIGINT, should also be happening exec-interrupt.

Also, why not use other GDB servers that support their probes? I don't like magic. I will end up supporting this and I need to understand the mechanics. I am sure you do too

@deermichel
Copy link
Author

I gathered some data with logging enabled:

SIGINT interrupt mode

gdb log from Debug Console

image

gta server log

image

wireshark

image

exec-interrupt mode

gdb log from Debug Console

image
after issuing the command, gdb hangs and I cannot send other commands

gta server log

  • no log written when issuing exec-interrupt

wireshark

  • no packets send

Summary

Looks like signaling SIGINT (or Ctrl+C) to gdb emits a "03" to the gdb server. The same behavior can be observed with the S32 Design Studio IDE, no exec-interrupt is used there. With "exec-interrupt" the gdb client does nothing. The gdb client is supplied by NXP with version string GNU gdb (GDB src=g2b2d27aa26 bld=gd2333b8c -v s=GDB-12-1 -L64 -W32 Earm) 12.1.

Some background research on the '03' packet: https://sourceware.org/gdb/current/onlinedocs/gdb.html/Interrupts.html

The probe cannot be used with other servers as gta interfaces with another proprietary tool that finally talks to the probe. Another (generic) probe cannot be used as they lack support for some proprietary cores on the chip in question.

@deermichel
Copy link
Author

Did some manual testing with the gdb client and --interpreter=mi2 with mi-async on, pagination off, non-stop on. While the target is running, I cannot issue commands - but - they will be stacked and processed as soon as I stop execution with Ctrl+C. I believe the problem comes down to:
image

So it's not possible to send commands to the gdb client while the target is running, it can only be interrupted by an external signal.

Oh and btw. -exec-interrupt indeed issues the same '03' packet once processed - the only problem is, I doesn't process the interrupt command in the first place 🤡

@haneefdm
Copy link
Collaborator

Thanks.

Lets enter the sausage factory for a bit. We need to understand what is going, why and if there other established methods of doing the same.

At the start of the session (initial connection between GDB and the server), they negotiate capabilities. GDB queries the server of its capabilities. GDB will behave accordingly based on the responses. Could you capture the first part of the session where a bunch of 'Q' packets/responses are exchanged?

https://sourceware.org/gdb/current/onlinedocs/gdb.html/General-Query-Packets.html#qSupported
https://sourceware.org/gdb/current/onlinedocs/gdb.html/General-Query-Packets.html#General-Query-Packets

There may be QPassSignals/QProgramSignals/qSupported being exchanged.

See also interrupt-sequence/remotebreak in the following

https://sourceware.org/gdb/current/onlinedocs/gdb.html/Remote-Configuration.html#Remote-Configuration

Shoould we have used one of these remote settings where gdb could translate an exec-interrupt to the appropriate thing for this server.

I have a feeling that the authors of the gdb-server have not fully advertised their capabilities. It would be interesting to see what happens on a server where exec-interrupt actually works.

If you are thinking 'why do I have to do this because I already implemented the SIGINT?` -- it is because both of us should understand why we are doing this and was there a better way.

@haneefdm
Copy link
Collaborator

Yes, I know about the Ctrl-C packet (interrupt event packet). See

https://github.com/openocd-org/openocd/blob/4d4c45cfd25703f39f9e2413b22d91a48c4b4565/src/server/gdb_server.c#L695

https://github.com/pyocd/pyOCD/blob/90ee63a57a73ccca6da10b4ad5e3ebd5a6775e58/pyocd/gdbserver/gdbserver.py#L608

All servers support it I am sure. Question is why GDB is not translating an exec-interrupt into a Ctrol-C (0x3) command. 0x3 does not follow the normal Q packet mechanism but all servers implement this as a means of stopping the inferior.

@haneefdm
Copy link
Collaborator

So it's not possible to send commands to the gdb client while the target is running, it can only be interrupted by an external signal.

I don't think this is true. Whether the target/server support non-stop mode or not, exec-interrupt is expressly allowed while target is running. Most other commands that require examining the target are not allowed. And, most servers do not support non-stop mode and even if they do, not very well.

The probe cannot be used with other servers as gta interfaces with another proprietary tool that finally talks to the prob

But, we are not talking about the probe here. We are concerned about why gdb is not sending an interrupt packet like it does for all other servers.

I looked at non-stop mode and decided it is highly unreliable or unsupported by almost all the servers. See section 20.5 below

https://openocd.org/doc/html/GDB-and-OpenOCD.html

Oh and btw. -exec-interrupt indeed issues the same '03' packet once processed - the only problem is, I doesn't process the interrupt command in the first place

But why, though? Why does it do it for everyone else?

@haneefdm
Copy link
Collaborator

Our messages are getting crossed :-). As I am typing a message, you sent additional messages and vice versa :-)

@haneefdm
Copy link
Collaborator

So it's not possible to send commands to the gdb client while the target is running, it can only be interrupted by an external signal.

This is true of most gdb servers. It is not actually an external signal is it? It is the 0x3 packet on stdin of the server. This is because you cannot send signals to a process running on another machine and maybe even on a different OS. The external signal was on GDB's stdin that starts the process but the SIGINT does not propagate. Like I said, gdb does know how to send a SIGINT to any gdb server. It will only send an 0x3 packet (very different from a SIGINT).

The whole GDB RSP is designed so it could truly be used "remotely". I remember brining up linux like kernels and this is all we had. Even today, that is true.

@haneefdm
Copy link
Collaborator

I will go silent on this issue for a day. Will give you some time to investigate. I have to attend to other things in the meantime.

@deermichel
Copy link
Author

deermichel commented Apr 29, 2025

This is the data exchanged during client and server at start:

client-to-server:
$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+#ec"

server-to-client:
$PacketSize=4000;qXfer:features:read+;hwbreak+;swbreak+;QStartNoAckMode+;#bf"

also:
image

and:
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants