gdb: better handling of 'S' packets
authorAndrew Burgess <andrew.burgess@embecosm.com>
Thu, 14 Jan 2021 01:26:58 +0000 (20:26 -0500)
committerSimon Marchi <simon.marchi@polymtl.ca>
Thu, 14 Jan 2021 01:26:58 +0000 (20:26 -0500)
commit8f66807b98f7634c43149ea62e454ea8f877691d
tree37c83dcb4a88de879934dab8e89684b8589fac2f
parentbd497355ea57d629a5c1ac610308bafd5b0eff8f
gdb: better handling of 'S' packets

This commit builds on work started in the following two commits:

  commit 24ed6739b699f329c2c45aedee5f8c7d2f54e493
  Date:   Thu Jan 30 14:35:40 2020 +0000

      gdb/remote: Restore support for 'S' stop reply packet

  commit cada5fc921e39a1945c422eea055c8b326d8d353
  Date:   Wed Mar 11 12:30:13 2020 +0000

      gdb: Handle W and X remote packets without giving a warning

This is related to how GDB handles remote targets that send back 'S'
packets.

In the first of the above commits we fixed GDB's ability to handle a
single process, single threaded target that sends back 'S' packets.
Although the 'T' packet would always be preferred to 'S' these days,
there's nothing really wrong with 'S' for this situation.

The second commit above fixed an oversight in the first commit, a
single-process, multi-threaded target can send back a process wide
event, for example the process exited event 'W' without including a
process-id, this also is fine as there is no ambiguity in this case.

In PR gdb/26819 we run into yet another problem with the above
commits.  In this case we have a single process with two threads, GDB
hits a breakpoint in thread 2 and then performs a stepi:

  (gdb) b main
  Breakpoint 1 at 0x1212340830: file infinite_loop.S, line 10.
  (gdb) c
  Continuing.

  Thread 2 hit Breakpoint 1, main () at infinite_loop.S:10
  10    in infinite_loop.S
  (gdb) set debug remote 1
  (gdb) stepi
  Sending packet: $vCont;s:2#24...Packet received: S05
  ../binutils-gdb/gdb/infrun.c:5807: internal-error: int finish_step_over(execution_control_state*): Assertion `ecs->event_thread->control.trap_expected' failed.

What happens in this case is that on the RISC-V target displaced
stepping is not supported, so when the stepi is issued GDB steps just
thread 2.  As only a single thread was set running the target decides
that is can get away with sending back an 'S' packet without a
thread-id.  GDB then associates the stop with thread 1 (the first
non-exited thread), but as thread 1 was not previously set executing
the assertion seen above triggers.

As an aside I am surprised that the target sends pack 'S' in this
situation.  The target is happy to send back 'T' (including thread-id)
when multiple threads are set running, so (to me) it would seem easier
to just always use the 'T' packet when multiple threads are in use.
However, the target only uses 'T' when multiple threads are actually
executing, otherwise an 'S' packet it used.

Still, when looking at the above situation we can see that GDB should
be able to understand which thread the 'S' reply is referring too.

The problem is that is that in commit 24ed6739b699 (above) when a stop
reply comes in with no thread-id we look for the first non-exited
thread and select that as the thread the stop applies too.

What we should really do is select the first non-exited, resumed thread,
and associate the stop event with this thread.  In the above example
both thread 1 and 2 are non-exited, but only thread 2 is resumed, so
this is what we should use.

There's a test for this issue included which works with stock
gdbserver by disabling use of the 'T' packet, and enabling
'scheduler-locking' within GDB so only one thread is set running.

gdb/ChangeLog:

PR gdb/26819
* remote.c
(remote_target::select_thread_for_ambiguous_stop_reply): New
member function.
(remote_target::process_stop_reply): Call
select_thread_for_ambiguous_stop_reply.

gdb/testsuite/ChangeLog:

PR gdb/26819
* gdb.server/stop-reply-no-thread-multi.c: New file.
* gdb.server/stop-reply-no-thread-multi.exp: New file.

Change-Id: I9b49d76c2a99063dcc76203fa0f5270a72825d15
gdb/ChangeLog
gdb/remote.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.server/stop-reply-no-thread-multi.c [new file with mode: 0644]
gdb/testsuite/gdb.server/stop-reply-no-thread-multi.exp [new file with mode: 0644]