diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 591ef40286..47481bdc08 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,13 @@ +2020-03-02 Andrew Burgess + + * remote.c (remote_target::remote_parse_stop_reply): Don't use the + general_thread if the stop reply is missing a thread-id. + (remote_target::process_stop_reply): Use the first non-exited + thread if the target didn't pass a thread-id. + * infrun.c (do_target_wait): Move call to + switch_to_inferior_no_thread to .... + (do_target_wait_1): ... here. + 2020-02-29 Jon Turney * debuginfod-support.c: Include defs.h first. diff --git a/gdb/infrun.c b/gdb/infrun.c index d9a6f73351..0f2b9a5412 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -3456,6 +3456,12 @@ do_target_wait_1 (inferior *inf, ptid_t ptid, ptid_t event_ptid; struct thread_info *tp; + /* We know that we are looking for an event in the target of inferior + INF, but we don't know which thread the event might come from. As + such we want to make sure that INFERIOR_PTID is reset so that none of + the wait code relies on it - doing so is always a mistake. */ + switch_to_inferior_no_thread (inf); + /* First check if there is a resumed thread with a wait status pending. */ if (ptid == minus_one_ptid || ptid.is_pid ()) @@ -3651,8 +3657,6 @@ do_target_wait (ptid_t wait_ptid, execution_control_state *ecs, int options) auto do_wait = [&] (inferior *inf) { - switch_to_inferior_no_thread (inf); - ecs->ptid = do_target_wait_1 (inf, wait_ptid, &ecs->ws, options); ecs->target = inf->process_target (); return (ecs->ws.kind != TARGET_WAITKIND_IGNORE); diff --git a/gdb/remote.c b/gdb/remote.c index 4a70ab3fb0..9b73faf9a3 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -7402,18 +7402,14 @@ Packet: '%s'\n"), reported expedited registers. */ if (event->ptid == null_ptid) { + /* If there is no thread-id information then leave + the event->ptid as null_ptid. Later in + process_stop_reply we will pick a suitable + thread. */ const char *thr = strstr (p1 + 1, ";thread:"); if (thr != NULL) event->ptid = read_ptid (thr + strlen (";thread:"), NULL); - else - { - /* Either the current thread hasn't changed, - or the inferior is not multi-threaded. - The event must be for the thread we last - set as (or learned as being) current. */ - event->ptid = event->rs->general_thread; - } } if (rsa == NULL) @@ -7668,10 +7664,35 @@ remote_target::process_stop_reply (struct stop_reply *stop_reply, *status = stop_reply->ws; ptid = stop_reply->ptid; - /* If no thread/process was reported by the stub, assume the current - inferior. */ + /* If no thread/process was reported by the stub then use the first + non-exited thread in the current target. */ if (ptid == null_ptid) - ptid = inferior_ptid; + { + for (thread_info *thr : all_non_exited_threads (this)) + { + if (ptid != null_ptid) + { + static bool warned = false; + + if (!warned) + { + /* If you are seeing this warning then the remote target + has multiple threads and either sent an 'S' stop + packet, or a 'T' stop packet without a thread-id. In + both of these cases GDB is unable to know which thread + just stopped and is now having to guess. The correct + action is to fix the remote target to send the correct + packet (a 'T' packet and include a thread-id). */ + warning (_("multi-threaded target stopped without sending " + "a thread-id, using first non-exited thread")); + warned = true; + } + break; + } + ptid = thr->ptid; + } + gdb_assert (ptid != null_ptid); + } if (status->kind != TARGET_WAITKIND_EXITED && status->kind != TARGET_WAITKIND_SIGNALLED diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index e5ea91dde8..488a328021 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2020-03-02 Andrew Burgess + + * gdb.server/stop-reply-no-thread.exp: Add test where T packet is + disabled. + 2020-03-02 Pedro Alves Tom de Vries diff --git a/gdb/testsuite/gdb.server/stop-reply-no-thread.exp b/gdb/testsuite/gdb.server/stop-reply-no-thread.exp index 45407bc31d..ffc1c27dcb 100644 --- a/gdb/testsuite/gdb.server/stop-reply-no-thread.exp +++ b/gdb/testsuite/gdb.server/stop-reply-no-thread.exp @@ -32,43 +32,59 @@ if [prepare_for_testing "failed to prepare" $testfile $srcfile] { return -1 } -# Make sure we're disconnected, in case we're testing with an -# extended-remote board, therefore already connected. -gdb_test "disconnect" ".*" +# Run the tests with different features of GDBserver disabled. +proc run_test { disable_feature } { + global binfile gdb_prompt decimal -# Start GDBserver, with ";thread:NNN" in T stop replies disabled, -# emulating old gdbservers when debugging single-threaded programs. -set res [gdbserver_start "--disable-packet=Tthread" $binfile] -set gdbserver_protocol [lindex $res 0] -set gdbserver_gdbport [lindex $res 1] + clean_restart ${binfile} -# Disable XML-based thread listing, and multi-process extensions. -gdb_test_no_output "set remote threads-packet off" -gdb_test_no_output "set remote multiprocess-feature-packet off" + # Make sure we're disconnected, in case we're testing with an + # extended-remote board, therefore already connected. + gdb_test "disconnect" ".*" -set res [gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport] -if ![gdb_assert {$res == 0} "connect"] { - return + set res [gdbserver_start "--disable-packet=${disable_feature}" $binfile] + set gdbserver_protocol [lindex $res 0] + set gdbserver_gdbport [lindex $res 1] + + # Disable XML-based thread listing, and multi-process extensions. + gdb_test_no_output "set remote threads-packet off" + gdb_test_no_output "set remote multiprocess-feature-packet off" + + set res [gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport] + if ![gdb_assert {$res == 0} "connect"] { + return + } + + # There should be only one thread listed. + set test "info threads" + gdb_test_multiple $test $test { + -re "2 Thread.*$gdb_prompt $" { + fail $test + } + -re "has terminated.*$gdb_prompt $" { + fail $test + } + -re "\\\* 1\[\t \]*Thread\[^\r\n\]*\r\n$gdb_prompt $" { + pass $test + } + } + + gdb_breakpoint "main" + + # Bad GDB behaved like this: + # (gdb) c + # Cannot execute this command without a live selected thread. + # (gdb) + gdb_test "c" "Breakpoint $decimal, main.*" "continue to main" } -# There should be only one thread listed. -set test "info threads" -gdb_test_multiple $test $test { - -re "2 Thread.*$gdb_prompt $" { - fail $test - } - -re "has terminated.*$gdb_prompt $" { - fail $test - } - -re "\\\* 1\[\t \]*Thread\[^\r\n\]*\r\n$gdb_prompt $" { - pass $test - } +# Disable different features within gdbserver: +# +# Tthread: Start GDBserver, with ";thread:NNN" in T stop replies disabled, +# emulating old gdbservers when debugging single-threaded programs. +# +# T: Start GDBserver with the entire 'T' stop reply packet disabled, +# GDBserver will instead send the 'S' stop reply. +foreach_with_prefix to_disable { Tthread T } { + run_test $to_disable } - -gdb_breakpoint "main" - -# Bad GDB behaved like this: -# (gdb) c -# Cannot execute this command without a live selected thread. -# (gdb) -gdb_test "c" "Breakpoint $decimal, main.*" "continue to main"