From 3b9ea5ed9e488c808d25eed110af84ea2f80fffb5ffaab9ec7484a580bc9e2df Mon Sep 17 00:00:00 2001 From: Tom de Vries Date: Mon, 12 Dec 2022 15:35:08 +0000 Subject: [PATCH] - Patches added (gdb 12 release branch backports): * gdb-disable-commit-resumed-in-target_kill.patch * gdb-fix-assert-when-quitting-gdb-while-a-thread-is-stepping.patch * gdb-testsuite-add-new-gdb_attach-to-check-attach-command.patch * gdb-testsuite-refactor-gdb.threads-detach-step-over.exp.patch * gdb-testsuite-remove-global-declarations-in-gdb.threads-detach-step-over.exp.patch * gdbserver-switch-to-right-process-in-find_one_thread.patch - Patches removed: * gdb-testsuite-fix-race-in-gdb.threads-detach-step-over.exp.patch - Maintenance script qa.sh: * Disable PR26284 kfails. * Add PR29841 kfails. * Add kfail_powerpc64le_sle12, kfail_s390 and kfail_s390x. * Add -s390 and -s390x. * Add gdb.base/gdb-rhbz1156192-recursive-dlopen.exp kfail. * Add PR26967 kfails. * Move PR27027 kfails from kfail_factory to kfail. * Add -ppc64le alias for -powerpc64le. * Add gdb.threads/interrupt-while-step-over.exp kfail. * Add gdb.tui/tui-layout-asm-short-prog.exp kfail. * Add unix/-fPIE/-fpie overrides -static kfails. * Add gdb.guile/scm-disasm.exp kfail. * Add gdb.base/gnu_vector.exp to existing kfail. * Add gdb.guile/scm-symtab.exp kfail. * Add gdb.base/write_mem.exp kfail. OBS-URL: https://build.opensuse.org/package/show/devel:gcc/gdb?expand=0&rev=344 --- ...isable-commit-resumed-in-target_kill.patch | 272 ++++++++++++++++ ...tting-gdb-while-a-thread-is-stepping.patch | 174 ++++++++++ ...w-gdb_attach-to-check-attach-command.patch | 49 +++ ...-in-gdb.threads-detach-step-over.exp.patch | 76 ----- ...tor-gdb.threads-detach-step-over.exp.patch | 305 ++++++++++++++++++ ...-in-gdb.threads-detach-step-over.exp.patch | 127 ++++++++ gdb.changes | 29 ++ gdb.spec | 18 +- ...-to-right-process-in-find_one_thread.patch | 255 +++++++++++++++ qa.sh | 137 +++++++- 10 files changed, 1353 insertions(+), 89 deletions(-) create mode 100644 gdb-disable-commit-resumed-in-target_kill.patch create mode 100644 gdb-fix-assert-when-quitting-gdb-while-a-thread-is-stepping.patch create mode 100644 gdb-testsuite-add-new-gdb_attach-to-check-attach-command.patch delete mode 100644 gdb-testsuite-fix-race-in-gdb.threads-detach-step-over.exp.patch create mode 100644 gdb-testsuite-refactor-gdb.threads-detach-step-over.exp.patch create mode 100644 gdb-testsuite-remove-global-declarations-in-gdb.threads-detach-step-over.exp.patch create mode 100644 gdbserver-switch-to-right-process-in-find_one_thread.patch diff --git a/gdb-disable-commit-resumed-in-target_kill.patch b/gdb-disable-commit-resumed-in-target_kill.patch new file mode 100644 index 0000000..3da6c67 --- /dev/null +++ b/gdb-disable-commit-resumed-in-target_kill.patch @@ -0,0 +1,272 @@ +gdb: disable commit resumed in target_kill + +New in this version: + + - Better comment in target_kill + - Uncomment line in test to avoid hanging when exiting, when testing on + native-extended-gdbserver + +PR 28275 shows that doing a sequence of: + + - Run inferior in background (run &) + - kill that inferior + - Run again + +We get into this assertion: + + /home/smarchi/src/binutils-gdb/gdb/target.c:2590: internal-error: target_wait: Assertion `!proc_target->commit_resumed_state' failed. + + #0 internal_error_loc (file=0x5606b344e740 "/home/smarchi/src/binutils-gdb/gdb/target.c", line=2590, fmt=0x5606b344d6a0 "%s: Assertion `%s' failed.") at /home/smarchi/src/binutils-gdb/gdbsupport/errors.cc:54 + #1 0x00005606b6296475 in target_wait (ptid=..., status=0x7fffb9390630, options=...) at /home/smarchi/src/binutils-gdb/gdb/target.c:2590 + #2 0x00005606b5767a98 in startup_inferior (proc_target=0x5606bfccb2a0 , pid=3884857, ntraps=1, last_waitstatus=0x0, last_ptid=0x0) at /home/smarchi/src/binutils-gdb/gdb/nat/fork-inferior.c:482 + #3 0x00005606b4e6c9c5 in gdb_startup_inferior (pid=3884857, num_traps=1) at /home/smarchi/src/binutils-gdb/gdb/fork-child.c:132 + #4 0x00005606b50f14a5 in inf_ptrace_target::create_inferior (this=0x5606bfccb2a0 , exec_file=0x604000039f50 "/home/smarchi/build/binutils-gdb/gdb/test", allargs="", env=0x61500000a580, from_tty=1) + at /home/smarchi/src/binutils-gdb/gdb/inf-ptrace.c:105 + #5 0x00005606b53b6d23 in linux_nat_target::create_inferior (this=0x5606bfccb2a0 , exec_file=0x604000039f50 "/home/smarchi/build/binutils-gdb/gdb/test", allargs="", env=0x61500000a580, from_tty=1) + at /home/smarchi/src/binutils-gdb/gdb/linux-nat.c:978 + #6 0x00005606b512b79b in run_command_1 (args=0x0, from_tty=1, run_how=RUN_NORMAL) at /home/smarchi/src/binutils-gdb/gdb/infcmd.c:468 + #7 0x00005606b512c236 in run_command (args=0x0, from_tty=1) at /home/smarchi/src/binutils-gdb/gdb/infcmd.c:526 + +When running the kill command, commit_resumed_state for the +process_stratum_target (linux-nat, here) is true. After the kill, when +there are no more threads, commit_resumed_state is still true, as +nothing touches this flag during the kill operation. During the +subsequent run command, run_command_1 does: + + scoped_disable_commit_resumed disable_commit_resumed ("running"); + +We would think that this would clear the commit_resumed_state flag of +our native target, but that's not the case, because +scoped_disable_commit_resumed iterates on non-exited inferiors in order +to find active process targets. And after the kill, the inferior is +exited, and the native target was unpushed from it anyway. So +scoped_disable_commit_resumed doesn't touch the commit_resumed_state +flag of the native target, it stays true. When reaching target_wait, in +startup_inferior (to consume the initial expect stop events while the +inferior is starting up and working its way through the shell), +commit_resumed_state is true, breaking the contract saying that +commit_resumed_state is always false when calling the targets' wait +method. + +(note: to be correct, I think that startup_inferior should toggle +commit_resumed between the target_wait and target_resume calls, but I'll +ignore that for now) + +I can see multiple ways to fix this. In the end, we need +commit_resumed_state to be cleared by the time we get to that +target_wait. It could be done at the end of the kill command, or at the +beginning of the run command. + +To keep things in a coherent state, I'd like to make it so that after +the kill command, when the target is left with no threads, its +commit_resumed_state flag is left to false. This way, we can keep +working with the assumption that a target with no threads (and therefore +no running threads) has commit_resumed_state == false. + +Do this by adding a scoped_disable_commit_resumed in target_kill. It +clears the target's commit_resumed_state on entry, and leaves it false +if the target does not have any resumed thread on exit. That means, +even if the target has another inferior with stopped threads, +commit_resumed_state will be left to false, which makes sense. + +Add a test that tries to cover various combinations of actions done +while an inferior is running (and therefore while commit_resumed_state +is true on the process target). + +Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28275 +Change-Id: I8e6fe6dc1f475055921520e58cab68024039a1e9 +Approved-By: Andrew Burgess + +--- + gdb/target.c | 9 ++ + .../gdb.base/run-control-while-bg-execution.c | 33 ++++++ + .../gdb.base/run-control-while-bg-execution.exp | 122 +++++++++++++++++++++ + 3 files changed, 164 insertions(+) + +diff --git a/gdb/target.c b/gdb/target.c +index 0c86b571e1c..0eae5307785 100644 +--- a/gdb/target.c ++++ b/gdb/target.c +@@ -908,6 +908,15 @@ add_deprecated_target_alias (const target_info &tinfo, const char *alias) + void + target_kill (void) + { ++ ++ /* If the commit_resume_state of the to-be-killed-inferior's process stratum ++ is true, and this inferior is the last live inferior with resumed threads ++ of that target, then we want to leave commit_resume_state to false, as the ++ target won't have any resumed threads anymore. We achieve this with ++ this scoped_disable_commit_resumed. On construction, it will set the flag ++ to false. On destruction, it will only set it to true if there are resumed ++ threads left. */ ++ scoped_disable_commit_resumed disable ("killing"); + current_inferior ()->top_target ()->kill (); + } + +diff --git a/gdb/testsuite/gdb.base/run-control-while-bg-execution.c b/gdb/testsuite/gdb.base/run-control-while-bg-execution.c +new file mode 100644 +index 00000000000..8092fadc8b9 +--- /dev/null ++++ b/gdb/testsuite/gdb.base/run-control-while-bg-execution.c +@@ -0,0 +1,33 @@ ++/* This testcase is part of GDB, the GNU debugger. ++ ++ Copyright 2020-2022 Free Software Foundation, Inc. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ // ++#include ++ ++static pid_t mypid = -1; ++ ++static void ++after_getpid (void) ++{ ++} ++ ++int ++main (void) ++{ ++ mypid = getpid (); ++ after_getpid (); ++ sleep (30); ++} +diff --git a/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp +new file mode 100644 +index 00000000000..5b4834f0b32 +--- /dev/null ++++ b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp +@@ -0,0 +1,122 @@ ++# Copyright 2022 Free Software Foundation, Inc. ++ ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++# This test aims at testing various operations after getting rid of an inferior ++# that was running in background, or while we have an inferior running in ++# background. The original intent was to expose cases where the commit-resumed ++# state of the process stratum target was not reset properly after killing an ++# inferior running in background, which would be a problem when trying to run ++# again. The test was expanded to test various combinations of ++# run-control-related actions done with an inferior running in background. ++ ++if {[use_gdb_stub]} { ++ unsupported "test requires running" ++ return ++} ++ ++standard_testfile ++ ++if {[build_executable "failed to prepare" $testfile $srcfile]} { ++ return ++} ++ ++# Run one variation of the test: ++# ++# 1. Start an inferior in the background with "run &" ++# 2. Do action 1 ++# 3. Do action 2 ++# ++# Action 1 indicates what to do with the inferior running in background: ++# ++# - kill: kill it ++# - detach: detach it ++# - add: add a new inferior and switch to it, leave the inferior running in ++# background alone ++# - none: do nothing, leave the inferior running in background alone ++# ++# Action 2 indicates what to do after that: ++# ++# - start: use the start command ++# - run: use the run command ++# - attach: start a process outside of GDB and attach it ++proc do_test { action1 action2 } { ++ save_vars { ::GDBFLAGS } { ++ append ::GDBFLAGS " -ex \"maintenance set target-non-stop on\"" ++ clean_restart $::binfile ++ } ++ ++ # Ensure we are at least after the getpid call, should we need it. ++ if { ![runto "after_getpid"] } { ++ return ++ } ++ ++ # Some commands below ask for confirmation. Turn that off for simplicity. ++ gdb_test "set confirm off" ++ gdb_test_multiple "continue &" "" { ++ -re ".*\r\n$::gdb_prompt " { ++ pass $gdb_test_name ++ } ++ } ++ ++ if { $action1 == "kill" } { ++ gdb_test "kill" "Inferior 1 .* killed.*" ++ } elseif { $action1 == "detach" } { ++ set child_pid [get_integer_valueof "mypid" -1] ++ if { $child_pid == -1 } { ++ fail "failed to extract child pid" ++ return ++ } ++ ++ gdb_test "detach" "Inferior 1 .* detached.*" "detach from first instance" ++ ++ # Kill the detached process, to avoid hanging when exiting GDBserver, ++ # when testing with the native-extended-gdbserver board. ++ remote_exec target "kill $child_pid" ++ } elseif { $action1 == "add" } { ++ gdb_test "add-inferior -exec $::binfile" \ ++ "Added inferior 2 on connection 1.*" "add-inferior" ++ gdb_test "inferior 2" "Switching to inferior 2 .*" ++ } elseif { $action1 == "none" } { ++ ++ } else { ++ error "invalid action 1" ++ } ++ ++ if { $action2 == "start" } { ++ gdb_test "start" "Temporary breakpoint $::decimal\(?:\.$::decimal\)?, main .*" ++ } elseif { $action2 == "run" } { ++ gdb_test "break main" "Breakpoint $::decimal at $::hex.*" ++ gdb_test "run" "Breakpoint $::decimal\(?:\.$::decimal\)?, main .*" ++ } elseif { $action2 == "attach" } { ++ set test_spawn_id [spawn_wait_for_attach $::binfile] ++ set test_pid [spawn_id_get_pid $test_spawn_id] ++ ++ if { [gdb_attach $test_pid] } { ++ gdb_test "detach" "Inferior $::decimal .* detached.*" \ ++ "detach from second instance" ++ } ++ ++ # Detach and kill this inferior so we don't leave it around. ++ kill_wait_spawned_process $test_spawn_id ++ } else { ++ error "invalid action 2" ++ } ++} ++ ++foreach_with_prefix action1 { kill detach add none } { ++ foreach_with_prefix action2 { start run attach } { ++ do_test $action1 $action2 ++ } ++} diff --git a/gdb-fix-assert-when-quitting-gdb-while-a-thread-is-stepping.patch b/gdb-fix-assert-when-quitting-gdb-while-a-thread-is-stepping.patch new file mode 100644 index 0000000..7034bb1 --- /dev/null +++ b/gdb-fix-assert-when-quitting-gdb-while-a-thread-is-stepping.patch @@ -0,0 +1,174 @@ +gdb: fix assert when quitting GDB while a thread is stepping + +This commit addresses one of the issues identified in PR gdb/28275. + +Bug gdb/28275 identifies a number of situations in which this assert: + + Assertion `!proc_target->commit_resumed_state' failed. + +could be triggered. There's actually a number of similar places where +this assert is found in GDB, the two of interest in gdb/28275 are in +target_wait and target_stop. + +In one of the comments: + + https://sourceware.org/bugzilla/show_bug.cgi?id=28275#c1 + +steps to trigger the assertion within target_stop were identified when +using a modified version of the gdb.threads/detach-step-over.exp test +script. + +In the gdb.threads/detach-step-over.exp test, we attach to a +multi-threaded inferior, and continue the inferior in asynchronous +(background) mode. Each thread is continuously hitting a conditional +breakpoint where the condition is always false. While the inferior is +running we detach. The goal is that we detach while GDB is performing a +step-over for the conditional breakpoint in at least one thread. + +While detaching, if a step-over is in progress, then GDB has to +complete the step over before we can detach. This involves calling +target_stop and target_wait (see prepare_for_detach). + +As far as gdb/28275 is concerned, the interesting part here, is the +the process_stratum_target::commit_resumed_state variable must be +false when target_stop and target_wait are called. + +This is currently ensured because, in detach_command (infrun.c), we +create an instance of scoped_disable_commit_resumed, this ensures that +when target_detach is called, ::commit_resumed_state will be false. + +The modification to the test that I propose here, and which exposed +the bug, is that, instead of using "detach" to detach from the +inferior, we instead use "quit". Quitting GDB after attaching to an +inferior will cause GDB to first detach, and then exit. + +When we quit GDB we end up calling target_detach via a different code +path, the stack looks like: + + #0 target_detach + #1 kill_or_detach + #2 quit_force + #3 quit_command + +Along this path there is no scoped_disable_commit_resumed created. +::commit_resumed_state can be true when we reach prepare_for_detach, +which calls target_wait and target_stop, so the assertion will trigger. + +In this commit, I propose fixing this by adding the creation of a +scoped_disable_commit_resumed into target_detach. This will ensure +that ::commit_resumed_state is false when calling prepare_for_detach +from within target_detach. + +I did consider placing the scoped_disable_commit_resumed in +prepare_for_detach, however, placing it in target_detach ensures that +the target's commit_resumed_state flag is left to false if the detached +inferior was the last one for that target. It's the same rationale as +for patch "gdb: disable commit resumed in target_kill" that comes later +in this series, but for detach instead of kill. + +detach_command still includes a scoped_disable_commit_resumed too, but I +think it is still relevant to cover the resumption at the end of the +function. + +Co-Authored-By: Simon Marchi +Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28275 +Change-Id: Ie128f7aba6ef0e018859275eca372e6ea738e96f + +--- + gdb/target.c | 5 +++ + gdb/testsuite/gdb.threads/detach-step-over.exp | 52 ++++++++++++++++++++++++++ + 2 files changed, 57 insertions(+) + +diff --git a/gdb/target.c b/gdb/target.c +index 1ee051b520a..0c86b571e1c 100644 +--- a/gdb/target.c ++++ b/gdb/target.c +@@ -2558,6 +2558,9 @@ target_preopen (int from_tty) + void + target_detach (inferior *inf, int from_tty) + { ++ /* Thread's don't need to be resumed until the end of this function. */ ++ scoped_disable_commit_resumed disable_commit_resumed ("detaching"); ++ + /* After we have detached, we will clear the register cache for this inferior + by calling registers_changed_ptid. We must save the pid_ptid before + detaching, as the target detach method will clear inf->pid. */ +@@ -2588,6 +2591,8 @@ target_detach (inferior *inf, int from_tty) + inferior_ptid matches save_pid_ptid, but in our case, it does not + call it, as inferior_ptid has been reset. */ + reinit_frame_cache (); ++ ++ disable_commit_resumed.reset_and_commit (); + } + + void +diff --git a/gdb/testsuite/gdb.threads/detach-step-over.exp b/gdb/testsuite/gdb.threads/detach-step-over.exp +index ad9b08f549e..d2cb52423d9 100644 +--- a/gdb/testsuite/gdb.threads/detach-step-over.exp ++++ b/gdb/testsuite/gdb.threads/detach-step-over.exp +@@ -284,6 +284,56 @@ proc_with_prefix test_detach_command {condition_eval target_non_stop non_stop di + kill_wait_spawned_process $test_spawn_id + } + ++# Similar to the proc above, but this time, instead of detaching using ++# the 'detach' command, we quit GDB, this will also trigger a detach, but ++# through a slightly different path, which can expose different bugs. ++proc_with_prefix test_detach_quit {condition_eval target_non_stop \ ++ non_stop displaced} { ++ # If debugging with target remote, check whether the all-stop variant ++ # of the RSP is being used. If so, we can't run the background tests. ++ if {!$non_stop ++ && [target_info exists gdb_protocol] ++ && ([target_info gdb_protocol] == "remote" ++ || [target_info gdb_protocol] == "extended-remote")} { ++ start_gdb_for_test $condition_eval $target_non_stop \ ++ $non_stop $displaced ++ ++ gdb_test_multiple "maint show target-non-stop" "" { ++ -wrap -re "(is|currently) on.*" { ++ } ++ -wrap -re "(is|currently) off.*" { ++ return ++ } ++ } ++ } ++ ++ set test_spawn_id [spawn_wait_for_attach $::binfile] ++ set testpid [spawn_id_get_pid $test_spawn_id] ++ ++ set attempts 3 ++ for {set attempt 1} { $attempt <= $attempts } { incr attempt } { ++ with_test_prefix "iter $attempt" { ++ ++ start_gdb_for_test $condition_eval $target_non_stop \ ++ $non_stop $displaced ++ ++ if {![prepare_test_iter $testpid $non_stop \ ++ $attempt $attempts "$::decimal"]} { ++ kill_wait_spawned_process $test_spawn_id ++ return ++ } ++ ++ gdb_test_multiple "with confirm off -- quit" "" { ++ eof { ++ pass $gdb_test_name ++ } ++ } ++ } ++ } ++ ++ kill_wait_spawned_process $test_spawn_id ++} ++ + # The test program exits after a while, in case GDB crashes. Make it + # wait at least as long as we may wait before declaring a time out + # failure. +@@ -331,6 +381,8 @@ foreach_with_prefix breakpoint-condition-evaluation {"host" "target"} { + foreach_with_prefix displaced {"off" "auto"} { + test_detach_command ${breakpoint-condition-evaluation} \ + ${target-non-stop} ${non-stop} ${displaced} ++ test_detach_quit ${breakpoint-condition-evaluation} \ ++ ${target-non-stop} ${non-stop} ${displaced} + } + } + } diff --git a/gdb-testsuite-add-new-gdb_attach-to-check-attach-command.patch b/gdb-testsuite-add-new-gdb_attach-to-check-attach-command.patch new file mode 100644 index 0000000..272c878 --- /dev/null +++ b/gdb-testsuite-add-new-gdb_attach-to-check-attach-command.patch @@ -0,0 +1,49 @@ +gdb: testsuite: add new gdb_attach to check "attach" command + +This commit adds new gdb_attach to centralize the failure checking of +"attach" command. Return 0 if attach failed, otherwise return 1. + +Signed-off-by: Tiezhu Yang +Change-Id: I553cf386cef60c67d38e331904b4aa01e132104a + +--- + gdb/testsuite/lib/gdb.exp | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp +index 25c1572a53a..5104835a2a9 100644 +--- a/gdb/testsuite/lib/gdb.exp ++++ b/gdb/testsuite/lib/gdb.exp +@@ -5146,6 +5146,32 @@ proc can_spawn_for_attach { } { + return 1 + } + ++# Centralize the failure checking of "attach" command. ++# Return 0 if attach failed, otherwise return 1. ++ ++proc gdb_attach { testpid args } { ++ parse_args { ++ {pattern ""} ++ } ++ ++ if { [llength $args] != 0 } { ++ error "Unexpected arguments: $args" ++ } ++ ++ gdb_test_multiple "attach $testpid" "attach" { ++ -re -wrap "Attaching to.*ptrace: Operation not permitted\\." { ++ unsupported "$gdb_test_name (Operation not permitted)" ++ return 0 ++ } ++ -re -wrap "$pattern" { ++ pass $gdb_test_name ++ return 1 ++ } ++ } ++ ++ return 0 ++} ++ + # Kill a progress previously started with spawn_wait_for_attach, and + # reap its wait status. PROC_SPAWN_ID is the spawn id associated with + # the process. diff --git a/gdb-testsuite-fix-race-in-gdb.threads-detach-step-over.exp.patch b/gdb-testsuite-fix-race-in-gdb.threads-detach-step-over.exp.patch deleted file mode 100644 index ec3d24c..0000000 --- a/gdb-testsuite-fix-race-in-gdb.threads-detach-step-over.exp.patch +++ /dev/null @@ -1,76 +0,0 @@ -gdb-testsuite-fix-race-in-gdb.threads-detach-step-over.exp - -[gdb/testsuite] Fix race in gdb.threads/detach-step-over.exp - -On OBS with openSUSE Leap 15.2 and target board unix/m32, I run into: -... -FAIL: gdb.threads/detach-step-over.exp: \ - breakpoint-condition-evaluation=host: target-non-stop=off: non-stop=off: \ - displaced=off: iter 1: all threads running -... - -I can easily reproduce this by doing: -... - - # Wait a bit, to give time for the threads to hit the - # breakpoint. -- sleep 1 - - set running_count 0 - set interrupted 0 -... - -Fix this by counting the running threads in a loop. - -Tested on x86_64-linux. - ---- - gdb/testsuite/gdb.threads/detach-step-over.exp | 26 +++++++++++++++++++++++--- - 1 file changed, 23 insertions(+), 3 deletions(-) - -diff --git a/gdb/testsuite/gdb.threads/detach-step-over.exp b/gdb/testsuite/gdb.threads/detach-step-over.exp -index 27718551188..d6fba07b486 100644 ---- a/gdb/testsuite/gdb.threads/detach-step-over.exp -+++ b/gdb/testsuite/gdb.threads/detach-step-over.exp -@@ -203,7 +203,8 @@ proc test {condition_eval target_non_stop non_stop displaced} { - - set running_count 0 - set interrupted 0 -- gdb_test_multiple "info threads" "all threads running" { -+ set running_expected [expr ($n_threads + 1) * 2] -+ gdb_test_multiple "info threads" "threads running" { - -re "\\(running\\)" { - incr running_count - exp_continue -@@ -224,10 +225,29 @@ proc test {condition_eval target_non_stop non_stop displaced} { - } - } - } -- -re "$gdb_prompt $" { -- gdb_assert {$running_count == ($n_threads + 1) * 2} $gdb_test_name -+ -re "$gdb_prompt " { - } - } -+ if { $interrupted == 0 } { -+ while { $running_count < $running_expected } { -+ sleep 10 -+ set prev_running_count $running_count -+ set running_count 0 -+ gdb_test_multiple "info threads" "threads running" { -+ -re "\\(running\\)" { -+ incr running_count -+ exp_continue -+ } -+ -re "$gdb_prompt $" { -+ } -+ } -+ if { $running_count == $prev_running_count } { -+ break -+ } -+ } -+ gdb_assert {$running_count == $running_expected} \ -+ "all threads running" -+ } - - gdb_test "detach" "Detaching from.*" - diff --git a/gdb-testsuite-refactor-gdb.threads-detach-step-over.exp.patch b/gdb-testsuite-refactor-gdb.threads-detach-step-over.exp.patch new file mode 100644 index 0000000..5813b24 --- /dev/null +++ b/gdb-testsuite-refactor-gdb.threads-detach-step-over.exp.patch @@ -0,0 +1,305 @@ +gdb/testsuite: refactor gdb.threads/detach-step-over.exp + +Factor out some bits of gdb.threads/detach-step-over.exp to procs in +preparation to adding some new variations of the test. Rename the +existing "test" proc and make it use proc_with_prefix. + +Co-Authored-By: Simon Marchi +Change-Id: Ib4412545c81c8556029e0f7bfa9dd48d7a9f3189 + +--- + gdb/testsuite/gdb.threads/detach-step-over.exp | 238 ++++++++++++++----------- + 1 file changed, 138 insertions(+), 100 deletions(-) + +diff --git a/gdb/testsuite/gdb.threads/detach-step-over.exp b/gdb/testsuite/gdb.threads/detach-step-over.exp +index 917be2ef378..ad9b08f549e 100644 +--- a/gdb/testsuite/gdb.threads/detach-step-over.exp ++++ b/gdb/testsuite/gdb.threads/detach-step-over.exp +@@ -56,11 +56,11 @@ standard_testfile + + set bp_lineno [gdb_get_line_number "Set breakpoint here"] + +-# The test proper. See description above. +-proc test {condition_eval target_non_stop non_stop displaced} { +- # Number of threads started by the program. +- set n_threads 10 ++# Number of threads started by the program. ++set n_threads 10 + ++# Start GDB, configuring various settings according to the arguments. ++proc start_gdb_for_test {condition_eval target_non_stop non_stop displaced} { + save_vars { ::GDBFLAGS } { + append ::GDBFLAGS " -ex \"maint set target-non-stop $target_non_stop\"" + append ::GDBFLAGS " -ex \"set non-stop $non_stop\"" +@@ -69,10 +69,137 @@ proc test {condition_eval target_non_stop non_stop displaced} { + clean_restart $::binfile + } + ++ gdb_test_no_output "set breakpoint condition-evaluation $condition_eval" ++} ++ ++# Use the 'attach' command to attach to process with pid TESTPID. Return true ++# if we believe GDB has attached and we are back at the GDB prompt, otherwise, ++# return false. ++proc attach_to {testpid} { ++ with_timeout_factor 2 { ++ set attached 0 ++ set saw_attaching 0 ++ gdb_test_multiple "attach $testpid" "attach" { ++ -re "Attaching to program.*process $testpid\r\n" { ++ set saw_attaching 1 ++ exp_continue ++ } ++ -re "new threads in iteration" { ++ # Seen when "set debug libthread_db" is on. ++ exp_continue ++ } ++ -re "Reading symbols from|Expanding full symbols from" { ++ # Prevent -readnow timeout. ++ exp_continue ++ } ++ -re "is a zombie - the process has already terminated.*$::gdb_prompt " { ++ fail $gdb_test_name ++ } ++ -re "Unable to attach: .*$::gdb_prompt " { ++ fail $gdb_test_name ++ } ++ -re "\r\n$::gdb_prompt " { ++ if { $saw_attaching } { ++ set attached 1 ++ pass $gdb_test_name ++ } else { ++ fail $gdb_test_name ++ } ++ } ++ } ++ } ++ ++ return $attached ++} ++ ++# After attaching to a multi-threaded inferior in non-stop mode, we expect to ++# see a stop message from each thread. This proc waits for all of these stop ++# messages. TID_RE is a regexp used to match the thread-id of the stopped ++# thread. ++# ++# Return true if we saw a stop from each of the expected threads (based on the ++# global N_THREADS value), otherwise, return false. ++proc check_stops_after_non_stop_attach {tid_re} { ++ set any "\[^\r\n\]*" ++ ++ # In non-stop, we will see one stop per thread after the prompt. ++ set stops 0 ++ set test "seen all stops" ++ for {set thread 1} { $thread <= $::n_threads } { incr thread } { ++ if {[gdb_test_multiple "" $test { ++ -re "Thread ${tid_re} ${any} stopped" { ++ incr stops ++ } ++ }] != 0} { ++ break ++ } ++ } ++ ++ # If we haven't seen all stops, then the ++ # gdb_test_multiple in the loop above will have ++ # already issued a FAIL. ++ if {$stops != $::n_threads} { ++ return false ++ } ++ pass $test ++ return true ++} ++ ++# Prepare for a single test iteration. TESTPID is the pid of the process GDB ++# will be attached too. NON_STOP indicates if GDB is configured in non-stop ++# mode or not. ATTEMPT is the current attempt number, and ATTEMPTS is the ++# maximum number of attempts we plan to run. TID_RE is a string used to match ++# against a thread-id in GDB's stop messages. ++# ++# Return true if everything is prepared correctly, otherwise return false. ++proc prepare_test_iter {testpid non_stop attempt attempts tid_re} { ++ if {![attach_to $testpid]} { ++ return false ++ } ++ ++ if {$non_stop} { ++ if {![check_stops_after_non_stop_attach $tid_re]} { ++ return false ++ } ++ } ++ ++ gdb_test "break ${::srcfile}:${::bp_lineno} if 0" "Breakpoint.*" \ ++ "break LOC if 0" ++ ++ if {$attempt < $attempts} { ++ # Kick the time out timer for another round. ++ gdb_test "print again = 1" " = 1" "reset timer in the inferior" ++ # Show the time we had left in the logs, in case ++ # something goes wrong. ++ gdb_test "print seconds_left" " = .*" ++ } ++ ++ if {$non_stop} { ++ set cont_cmd "continue -a &" ++ } else { ++ set cont_cmd "continue &" ++ } ++ ++ set cont_cmd_re [string_to_regexp $cont_cmd] ++ gdb_test_multiple $cont_cmd "" { ++ -re "^$cont_cmd_re\r\nContinuing\.\r\n$::gdb_prompt " { ++ pass $gdb_test_name ++ } ++ } ++ ++ # Wait a bit, to give time for the threads to hit the ++ # breakpoint. ++ sleep 1 ++ ++ return true ++} ++ ++# The test proper. See the description at the top of the file. ++proc_with_prefix test_detach_command {condition_eval target_non_stop non_stop displaced} { + set test_spawn_id [spawn_wait_for_attach $::binfile] + set testpid [spawn_id_get_pid $test_spawn_id] + +- set any "\[^\r\n\]*" ++ start_gdb_for_test $condition_eval $target_non_stop $non_stop $displaced + + gdb_test "add-inferior" "Added inferior 2.*" + gdb_test "inferior 2" "Switching to .*" +@@ -84,8 +211,6 @@ proc test {condition_eval target_non_stop non_stop displaced} { + return + } + +- gdb_test_no_output "set breakpoint condition-evaluation $condition_eval" +- + # Get the PID of the test process. + set pid_inf2 "" + gdb_test_multiple "p mypid" "get pid of inferior 2" { +@@ -100,101 +225,12 @@ proc test {condition_eval target_non_stop non_stop displaced} { + with_test_prefix "iter $attempt" { + gdb_test "inferior 1" "Switching to .*" + +- with_timeout_factor 2 { +- set attached 0 +- set saw_attaching 0 +- set eperm 0 +- set test "attach" +- gdb_test_multiple "attach $testpid" $test { +- -re "Attaching to program.*process $testpid\r\n" { +- set saw_attaching 1 +- exp_continue +- } +- -re "new threads in iteration" { +- # Seen when "set debug libthread_db" is on. +- exp_continue +- } +- -re "Reading symbols from|Expanding full symbols from" { +- # Prevent -readnow timeout. +- exp_continue +- } +- -re "is a zombie - the process has already terminated.*$::gdb_prompt " { +- fail $gdb_test_name +- } +- -re "Unable to attach: .*$::gdb_prompt " { +- fail $gdb_test_name +- } +- -re "\r\n$::gdb_prompt " { +- if { $saw_attaching } { +- set attached 1 +- pass $test +- } else { +- fail $test +- } +- } +- } +- } +- +- if {!$attached} { ++ if {![prepare_test_iter $testpid $non_stop \ ++ $attempt $attempts "$::decimal\.$::decimal"]} { + kill_wait_spawned_process $test_spawn_id + return + } + +- if {$non_stop} { +- # In non-stop, we will see one stop per thread after +- # the prompt. +- set stops 0 +- set tid_re "$::decimal\.$::decimal" +- set test "seen all stops" +- for {set thread 1} { $thread <= $n_threads } { incr thread } { +- if {[gdb_test_multiple "" $test { +- -re "Thread ${tid_re} ${any} stopped" { +- incr stops +- } +- }] != 0} { +- break +- } +- } +- +- # If we haven't seen all stops, then the +- # gdb_test_multiple in the loop above will have +- # already issued a FAIL. +- if {$stops != $n_threads} { +- kill_wait_spawned_process $test_spawn_id +- return +- } +- pass $test +- } +- +- # Set threads stepping over a breakpoint continuously. +- gdb_test "break $::srcfile:$::bp_lineno if 0" "Breakpoint.*" \ +- "break LOC if 0" +- +- if {$attempt < $attempts} { +- # Kick the time out timer for another round. +- gdb_test "print again = 1" " = 1" "reset timer in the inferior" +- # Show the time we had left in the logs, in case +- # something goes wrong. +- gdb_test "print seconds_left" " = .*" +- } +- +- if {$non_stop} { +- set cont_cmd "continue -a &" +- } else { +- set cont_cmd "continue &" +- } +- +- set cont_cmd_re [string_to_regexp $cont_cmd] +- gdb_test_multiple $cont_cmd "" { +- -re "^$cont_cmd_re\r\nContinuing\.\r\n$::gdb_prompt " { +- pass $gdb_test_name +- } +- } +- +- # Wait a bit, to give time for the threads to hit the +- # breakpoint. +- sleep 1 +- + set running_count 0 + set interrupted 0 + gdb_test_multiple "info threads" "all threads running" { +@@ -219,7 +255,8 @@ proc test {condition_eval target_non_stop non_stop displaced} { + } + } + -re "$::gdb_prompt $" { +- gdb_assert {$running_count == ($n_threads + 1) * 2} $gdb_test_name ++ gdb_assert {$running_count == ($::n_threads + 1) * 2} \ ++ $gdb_test_name + } + } + +@@ -292,7 +329,8 @@ foreach_with_prefix breakpoint-condition-evaluation {"host" "target"} { + } + + foreach_with_prefix displaced {"off" "auto"} { +- test ${breakpoint-condition-evaluation} ${target-non-stop} ${non-stop} ${displaced} ++ test_detach_command ${breakpoint-condition-evaluation} \ ++ ${target-non-stop} ${non-stop} ${displaced} + } + } + } diff --git a/gdb-testsuite-remove-global-declarations-in-gdb.threads-detach-step-over.exp.patch b/gdb-testsuite-remove-global-declarations-in-gdb.threads-detach-step-over.exp.patch new file mode 100644 index 0000000..a53f78a --- /dev/null +++ b/gdb-testsuite-remove-global-declarations-in-gdb.threads-detach-step-over.exp.patch @@ -0,0 +1,127 @@ +gdb/testsuite: remove global declarations in gdb.threads/detach-step-over.exp + +Before doing further changes to this file, change to use the :: notation +instead of declaring global variables with the `global` keyword. + +Change-Id: I72301fd8f4693fea61aac054ba17245a1f4442fb +Approved-By: Andrew Burgess + +--- + gdb/testsuite/gdb.threads/detach-step-over.exp | 40 +++++++++++--------------- + 1 file changed, 17 insertions(+), 23 deletions(-) + +diff --git a/gdb/testsuite/gdb.threads/detach-step-over.exp b/gdb/testsuite/gdb.threads/detach-step-over.exp +index 15af7e0e723..917be2ef378 100644 +--- a/gdb/testsuite/gdb.threads/detach-step-over.exp ++++ b/gdb/testsuite/gdb.threads/detach-step-over.exp +@@ -58,24 +58,18 @@ set bp_lineno [gdb_get_line_number "Set breakpoint here"] + + # The test proper. See description above. + proc test {condition_eval target_non_stop non_stop displaced} { +- global binfile srcfile +- global gdb_prompt +- global decimal +- global bp_lineno +- global GDBFLAGS +- + # Number of threads started by the program. + set n_threads 10 + +- save_vars { GDBFLAGS } { +- append GDBFLAGS " -ex \"maint set target-non-stop $target_non_stop\"" +- append GDBFLAGS " -ex \"set non-stop $non_stop\"" +- append GDBFLAGS " -ex \"set displaced $displaced\"" +- append GDBFLAGS " -ex \"set schedule-multiple on\"" +- clean_restart $binfile ++ save_vars { ::GDBFLAGS } { ++ append ::GDBFLAGS " -ex \"maint set target-non-stop $target_non_stop\"" ++ append ::GDBFLAGS " -ex \"set non-stop $non_stop\"" ++ append ::GDBFLAGS " -ex \"set displaced $displaced\"" ++ append ::GDBFLAGS " -ex \"set schedule-multiple on\"" ++ clean_restart $::binfile + } + +- set test_spawn_id [spawn_wait_for_attach $binfile] ++ set test_spawn_id [spawn_wait_for_attach $::binfile] + set testpid [spawn_id_get_pid $test_spawn_id] + + set any "\[^\r\n\]*" +@@ -83,7 +77,7 @@ proc test {condition_eval target_non_stop non_stop displaced} { + gdb_test "add-inferior" "Added inferior 2.*" + gdb_test "inferior 2" "Switching to .*" + +- gdb_load $binfile ++ gdb_load $::binfile + if ![runto setup_done] then { + fail "can't run to setup_done" + kill_wait_spawned_process $test_spawn_id +@@ -95,7 +89,7 @@ proc test {condition_eval target_non_stop non_stop displaced} { + # Get the PID of the test process. + set pid_inf2 "" + gdb_test_multiple "p mypid" "get pid of inferior 2" { +- -re " = ($decimal)\r\n$gdb_prompt $" { ++ -re " = ($::decimal)\r\n$::gdb_prompt $" { + set pid_inf2 $expect_out(1,string) + pass $gdb_test_name + } +@@ -124,13 +118,13 @@ proc test {condition_eval target_non_stop non_stop displaced} { + # Prevent -readnow timeout. + exp_continue + } +- -re "is a zombie - the process has already terminated.*$gdb_prompt " { ++ -re "is a zombie - the process has already terminated.*$::gdb_prompt " { + fail $gdb_test_name + } +- -re "Unable to attach: .*$gdb_prompt " { ++ -re "Unable to attach: .*$::gdb_prompt " { + fail $gdb_test_name + } +- -re "\r\n$gdb_prompt " { ++ -re "\r\n$::gdb_prompt " { + if { $saw_attaching } { + set attached 1 + pass $test +@@ -173,7 +167,7 @@ proc test {condition_eval target_non_stop non_stop displaced} { + } + + # Set threads stepping over a breakpoint continuously. +- gdb_test "break $srcfile:$bp_lineno if 0" "Breakpoint.*" \ ++ gdb_test "break $::srcfile:$::bp_lineno if 0" "Breakpoint.*" \ + "break LOC if 0" + + if {$attempt < $attempts} { +@@ -192,7 +186,7 @@ proc test {condition_eval target_non_stop non_stop displaced} { + + set cont_cmd_re [string_to_regexp $cont_cmd] + gdb_test_multiple $cont_cmd "" { +- -re "^$cont_cmd_re\r\nContinuing\.\r\n$gdb_prompt " { ++ -re "^$cont_cmd_re\r\nContinuing\.\r\n$::gdb_prompt " { + pass $gdb_test_name + } + } +@@ -208,14 +202,14 @@ proc test {condition_eval target_non_stop non_stop displaced} { + incr running_count + exp_continue + } +- -re "Cannot execute this command while the target is running.*$gdb_prompt $" { ++ -re "Cannot execute this command while the target is running.*$::gdb_prompt $" { + # Testing against a remote server that doesn't do + # non-stop mode. Explicitly interrupt. This + # doesn't test the same code paths in GDB, but + # it's still something. + set interrupted 1 + gdb_test_multiple "interrupt" "" { +- -re "$gdb_prompt " { ++ -re "$::gdb_prompt " { + gdb_test_multiple "" $gdb_test_name { + -re "received signal SIGINT, Interrupt" { + pass $gdb_test_name +@@ -224,7 +218,7 @@ proc test {condition_eval target_non_stop non_stop displaced} { + } + } + } +- -re "$gdb_prompt $" { ++ -re "$::gdb_prompt $" { + gdb_assert {$running_count == ($n_threads + 1) * 2} $gdb_test_name + } + } diff --git a/gdb.changes b/gdb.changes index 9c55306..7e5d9d7 100644 --- a/gdb.changes +++ b/gdb.changes @@ -1,3 +1,32 @@ +------------------------------------------------------------------- +Thu Dec 1 15:20:21 UTC 2022 - Tom de Vries + +- Patches added (gdb 12 release branch backports): + * gdb-disable-commit-resumed-in-target_kill.patch + * gdb-fix-assert-when-quitting-gdb-while-a-thread-is-stepping.patch + * gdb-testsuite-add-new-gdb_attach-to-check-attach-command.patch + * gdb-testsuite-refactor-gdb.threads-detach-step-over.exp.patch + * gdb-testsuite-remove-global-declarations-in-gdb.threads-detach-step-over.exp.patch + * gdbserver-switch-to-right-process-in-find_one_thread.patch +- Patches removed: + * gdb-testsuite-fix-race-in-gdb.threads-detach-step-over.exp.patch +- Maintenance script qa.sh: + * Disable PR26284 kfails. + * Add PR29841 kfails. + * Add kfail_powerpc64le_sle12, kfail_s390 and kfail_s390x. + * Add -s390 and -s390x. + * Add gdb.base/gdb-rhbz1156192-recursive-dlopen.exp kfail. + * Add PR26967 kfails. + * Move PR27027 kfails from kfail_factory to kfail. + * Add -ppc64le alias for -powerpc64le. + * Add gdb.threads/interrupt-while-step-over.exp kfail. + * Add gdb.tui/tui-layout-asm-short-prog.exp kfail. + * Add unix/-fPIE/-fpie overrides -static kfails. + * Add gdb.guile/scm-disasm.exp kfail. + * Add gdb.base/gnu_vector.exp to existing kfail. + * Add gdb.guile/scm-symtab.exp kfail. + * Add gdb.base/write_mem.exp kfail. + ------------------------------------------------------------------- Tue Nov 22 15:03:04 UTC 2022 - Tom de Vries diff --git a/gdb.spec b/gdb.spec index 2a5f2b5..97cfe65 100644 --- a/gdb.spec +++ b/gdb.spec @@ -299,15 +299,16 @@ Patch1504: fix-gdb.mi-new-ui-mi-sync.exp.patch # FAIL: gdb.base/step-over-syscall.exp: fork: displaced=off: \ # pc after stepi matches insn addr after syscall Patch1505: gdb-testsuite-fix-gdb.base-step-over-syscall.exp-with-m32-amd-case.patch -# Fixes: -# FAIL: gdb.threads/detach-step-over.exp: \ -# breakpoint-condition-evaluation=host: target-non-stop=off: non-stop=off: \ -# displaced=off: iter 1: all threads running -Patch1506: gdb-testsuite-fix-race-in-gdb.threads-detach-step-over.exp.patch # Backports from release branch Patch1700: fix-core-file-detach-crash-corefiles-29275.patch +Patch1701: gdb-testsuite-add-new-gdb_attach-to-check-attach-command.patch +Patch1702: gdb-testsuite-remove-global-declarations-in-gdb.threads-detach-step-over.exp.patch +Patch1703: gdb-testsuite-refactor-gdb.threads-detach-step-over.exp.patch +Patch1704: gdb-fix-assert-when-quitting-gdb-while-a-thread-is-stepping.patch +Patch1705: gdbserver-switch-to-right-process-in-find_one_thread.patch +Patch1706: gdb-disable-commit-resumed-in-target_kill.patch # Backports from master, available in next release. @@ -744,9 +745,14 @@ find -name "*.info*"|xargs rm -f %patch1503 -p1 %patch1504 -p1 %patch1505 -p1 -%patch1506 -p1 %patch1700 -p1 +%patch1701 -p1 +%patch1702 -p1 +%patch1703 -p1 +%patch1704 -p1 +%patch1705 -p1 +%patch1706 -p1 %patch2000 -p1 %patch2001 -p1 diff --git a/gdbserver-switch-to-right-process-in-find_one_thread.patch b/gdbserver-switch-to-right-process-in-find_one_thread.patch new file mode 100644 index 0000000..170b2b4 --- /dev/null +++ b/gdbserver-switch-to-right-process-in-find_one_thread.patch @@ -0,0 +1,255 @@ +gdbserver: switch to right process in find_one_thread + +New in this version: add a dedicated test. + +When I do this: + + $ ./gdb -nx --data-directory=data-directory -q \ + /bin/sleep \ + -ex "maint set target-non-stop on" \ + -ex "tar ext :1234" \ + -ex "set remote exec-file /bin/sleep" \ + -ex "run 1231 &" \ + -ex add-inferior \ + -ex "inferior 2" + Reading symbols from /bin/sleep... + (No debugging symbols found in /bin/sleep) + Remote debugging using :1234 + Starting program: /bin/sleep 1231 + Reading /lib64/ld-linux-x86-64.so.2 from remote target... + warning: File transfers from remote targets can be slow. Use "set sysroot" to access files locally instead. + Reading /lib64/ld-linux-x86-64.so.2 from remote target... + Reading /usr/lib/debug/.build-id/a6/7a1408f18db3576757eea210d07ba3fc560dff.debug from remote target... + [New inferior 2] + Added inferior 2 on connection 1 (extended-remote :1234) + [Switching to inferior 2 [] ()] + (gdb) Reading /lib/x86_64-linux-gnu/libc.so.6 from remote target... + attach 3659848 + Attaching to process 3659848 + /home/smarchi/src/binutils-gdb/gdb/thread.c:85: internal-error: inferior_thread: Assertion `current_thread_ != nullptr' failed. + +Note the "attach" command just above. When doing it on the command-line +with a -ex switch, the bug doesn't trigger. + +The internal error of GDB is actually caused by GDBserver crashing, and +the error recovery of GDB is not on point. This patch aims to fix just +the GDBserver crash, not the GDB problem. + +GDBserver crashes with a segfault here: + + (gdb) bt + #0 0x00005555557fb3f4 in find_one_thread (ptid=...) at /home/smarchi/src/binutils-gdb/gdbserver/thread-db.cc:177 + #1 0x00005555557fd5cf in thread_db_thread_handle (ptid=, handle=0x7fffffffc400, handle_len=0x7fffffffc3f0) + at /home/smarchi/src/binutils-gdb/gdbserver/thread-db.cc:461 + #2 0x000055555578a0b6 in linux_process_target::thread_handle (this=0x5555558a64c0 , ptid=, handle=0x7fffffffc400, + handle_len=0x7fffffffc3f0) at /home/smarchi/src/binutils-gdb/gdbserver/linux-low.cc:6905 + #3 0x00005555556dfcc6 in handle_qxfer_threads_worker (thread=0x60b000000510, buffer=0x7fffffffc8a0) at /home/smarchi/src/binutils-gdb/gdbserver/server.cc:1645 + #4 0x00005555556e00e6 in operator() (__closure=0x7fffffffc5e0, thread=0x60b000000510) at /home/smarchi/src/binutils-gdb/gdbserver/server.cc:1696 + #5 0x00005555556f54be in for_each_thread >(struct {...}) (func=...) at /home/smarchi/src/binutils-gdb/gdbserver/gdbthread.h:159 + #6 0x00005555556e0242 in handle_qxfer_threads_proper (buffer=0x7fffffffc8a0) at /home/smarchi/src/binutils-gdb/gdbserver/server.cc:1694 + #7 0x00005555556e04ba in handle_qxfer_threads (annex=0x629000000213 "", readbuf=0x621000019100 '\276' ..., writebuf=0x0, offset=0, len=4097) + at /home/smarchi/src/binutils-gdb/gdbserver/server.cc:1732 + #8 0x00005555556e1989 in handle_qxfer (own_buf=0x629000000200 "qXfer:threads", packet_len=26, new_packet_len_p=0x7fffffffd630) at /home/smarchi/src/binutils-gdb/gdbserver/server.cc:2045 + #9 0x00005555556e720a in handle_query (own_buf=0x629000000200 "qXfer:threads", packet_len=26, new_packet_len_p=0x7fffffffd630) at /home/smarchi/src/binutils-gdb/gdbserver/server.cc:2685 + #10 0x00005555556f1a01 in process_serial_event () at /home/smarchi/src/binutils-gdb/gdbserver/server.cc:4176 + #11 0x00005555556f4457 in handle_serial_event (err=0, client_data=0x0) at /home/smarchi/src/binutils-gdb/gdbserver/server.cc:4514 + #12 0x0000555555820f56 in handle_file_event (file_ptr=0x607000000250, ready_mask=1) at /home/smarchi/src/binutils-gdb/gdbsupport/event-loop.cc:573 + #13 0x0000555555821895 in gdb_wait_for_event (block=1) at /home/smarchi/src/binutils-gdb/gdbsupport/event-loop.cc:694 + #14 0x000055555581f533 in gdb_do_one_event (mstimeout=-1) at /home/smarchi/src/binutils-gdb/gdbsupport/event-loop.cc:264 + #15 0x00005555556ec9fb in start_event_loop () at /home/smarchi/src/binutils-gdb/gdbserver/server.cc:3512 + #16 0x00005555556f0769 in captured_main (argc=4, argv=0x7fffffffe0d8) at /home/smarchi/src/binutils-gdb/gdbserver/server.cc:3992 + #17 0x00005555556f0e3f in main (argc=4, argv=0x7fffffffe0d8) at /home/smarchi/src/binutils-gdb/gdbserver/server.cc:4078 + +The reason is a wrong current process when find_one_thread is called. +The current process is the 2nd one, which was just attached. It does +not yet have thread_db data (proc->priv->thread_db is nullptr). As we +iterate on all threads of all process to fulfull the qxfer:threads:read +request, we get to a thread of process 1 for which we haven't read +thread_db information yet (lwp_info::thread_known is false), so we get +into find_one_thread. find_one_thread uses +`current_process ()->priv->thread_db`, assuming the current process +matches the ptid passed as a parameter, which is wrong. A segfault +happens when trying to dereference that thread_db pointer. + +Fix this by making find_one_thread not assume what the current process / +current thread is. If it needs to call into libthread_db, which we know +will try to read memory from the current process, then temporarily set +the current process. + +In the case where the thread is already know and we return early, we +don't need to switch process. + +Add a test to reproduce this specific situation. + +Change-Id: I09b00883e8b73b7e5f89d0f47cb4e9c0f3d6caaa +Approved-By: Andrew Burgess + +--- + gdb/testsuite/gdb.multi/attach-while-running.c | 26 +++++++++ + gdb/testsuite/gdb.multi/attach-while-running.exp | 73 ++++++++++++++++++++++++ + gdbserver/thread-db.cc | 29 ++++++---- + 3 files changed, 116 insertions(+), 12 deletions(-) + +diff --git a/gdb/testsuite/gdb.multi/attach-while-running.c b/gdb/testsuite/gdb.multi/attach-while-running.c +new file mode 100644 +index 00000000000..dd321dfe007 +--- /dev/null ++++ b/gdb/testsuite/gdb.multi/attach-while-running.c +@@ -0,0 +1,26 @@ ++/* This testcase is part of GDB, the GNU debugger. ++ ++ Copyright 2022 Free Software Foundation, Inc. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#include ++ ++int global_var = 123; ++ ++int ++main (void) ++{ ++ sleep (30); ++} +diff --git a/gdb/testsuite/gdb.multi/attach-while-running.exp b/gdb/testsuite/gdb.multi/attach-while-running.exp +new file mode 100644 +index 00000000000..125273d0524 +--- /dev/null ++++ b/gdb/testsuite/gdb.multi/attach-while-running.exp +@@ -0,0 +1,73 @@ ++# Copyright 2022 Free Software Foundation, Inc. ++ ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++# This test was introduced to reproduce a specific bug in GDBserver, where ++# attaching an inferior while another one was running would trigger a segfault ++# in GDBserver. Reproducing the bug required specific circumstances: ++# ++# - The first process must be far enough to have loaded its libc or ++# libpthread (whatever triggers the loading of libthread_db), such that ++# its proc->priv->thread_db is not nullptr ++# ++# - However, its lwp must still be in the `!lwp->thread_known` state, ++# meaning GDBserver hasn't asked libthread_db to compute the thread ++# handle yet. That means, GDB must not have refreshed the thread list ++# yet, since that would cause the thread handles to be computed. That ++# means, no stopping on a breakpoint, since that causes a thread list ++# update. That's why the first inferior needs to be started with "run ++# &". ++# ++# - Attaching the second process would segfault GDBserver. ++# ++# All of this to say, if modifying this test, please keep in mind the original ++# intent. ++ ++standard_testfile ++ ++if [use_gdb_stub] { ++ unsupported "test requires running" ++ return ++} ++ ++if { [build_executable "failed to prepare" ${testfile} ${srcfile}] } { ++ return ++} ++ ++proc do_test {} { ++ save_vars { $::GDBFLAGS } { ++ append ::GDBFLAGS " -ex \"maint set target-non-stop on\"" ++ clean_restart $::binfile ++ } ++ ++ gdb_test_multiple "run &" "" { ++ -re ".*$::gdb_prompt " { ++ pass $gdb_test_name ++ } ++ } ++ gdb_test "add-inferior" "Added inferior 2 on connection 1 .*" ++ gdb_test "inferior 2" "Switching to inferior 2 .*" ++ ++ set spawn_id [spawn_wait_for_attach $::binfile] ++ set pid [spawn_id_get_pid $spawn_id] ++ ++ # This call would crash GDBserver. ++ gdb_attach $pid ++ ++ # Read a variable from the inferior, just to make sure the attach worked ++ # fine. ++ gdb_test "print global_var" " = 123" ++} ++ ++do_test +diff --git a/gdbserver/thread-db.cc b/gdbserver/thread-db.cc +index 6e0e2228a5f..bf98ca9557a 100644 +--- a/gdbserver/thread-db.cc ++++ b/gdbserver/thread-db.cc +@@ -155,30 +155,35 @@ thread_db_state_str (td_thr_state_e state) + } + #endif + +-/* Get thread info about PTID, accessing memory via the current +- thread. */ ++/* Get thread info about PTID. */ + + static int + find_one_thread (ptid_t ptid) + { +- td_thrhandle_t th; +- td_thrinfo_t ti; +- td_err_e err; +- struct lwp_info *lwp; +- struct thread_db *thread_db = current_process ()->priv->thread_db; +- int lwpid = ptid.lwp (); +- + thread_info *thread = find_thread_ptid (ptid); +- lwp = get_thread_lwp (thread); ++ lwp_info *lwp = get_thread_lwp (thread); + if (lwp->thread_known) + return 1; + +- /* Get information about this thread. */ +- err = thread_db->td_ta_map_lwp2thr_p (thread_db->thread_agent, lwpid, &th); ++ /* Get information about this thread. libthread_db will need to read some ++ memory, which will be done on the current process, so make PTID's process ++ the current one. */ ++ process_info *proc = find_process_pid (ptid.pid ()); ++ gdb_assert (proc != nullptr); ++ ++ scoped_restore_current_thread restore_thread; ++ switch_to_process (proc); ++ ++ thread_db *thread_db = proc->priv->thread_db; ++ td_thrhandle_t th; ++ int lwpid = ptid.lwp (); ++ td_err_e err = thread_db->td_ta_map_lwp2thr_p (thread_db->thread_agent, lwpid, ++ &th); + if (err != TD_OK) + error ("Cannot get thread handle for LWP %d: %s", + lwpid, thread_db_err_str (err)); + ++ td_thrinfo_t ti; + err = thread_db->td_thr_get_info_p (&th, &ti); + if (err != TD_OK) + error ("Cannot get thread info for LWP %d: %s", diff --git a/qa.sh b/qa.sh index c07557c..21cda4e 100644 --- a/qa.sh +++ b/qa.sh @@ -23,7 +23,7 @@ usage () { echo "usage: $0 <1-5>" - echo " $0 -local [ -sle12 | -factory | -aarch64 | -powerpc64le ] " + echo " $0 -local [ -sle12 | -factory | -aarch64 | -powerpc64le | -s390 | -s390x ] " echo echo "Verify remote results at:" echo " ./binaries-testsuite.distro.arch/gdb-testresults" @@ -50,6 +50,8 @@ have_sle12=false have_factory=false have_aarch64=false have_powerpc64le=false +have_s390=false +have_s390x=false if [ "$n" = "-local" ]; then while [ $# -gt 1 ]; do case $1 in @@ -62,9 +64,15 @@ if [ "$n" = "-local" ]; then -aarch64) have_aarch64=true ;; - -powerpc64le) + -powerpc64le|-ppc64le) have_powerpc64le=true ;; + -s390) + have_s390=true + ;; + -s390x) + have_s390x=true + ;; *) echo "Don't know how to handle arg: $1" usage @@ -172,7 +180,8 @@ kfail=( # https://sourceware.org/bugzilla/show_bug.cgi?id=26284 # https://sourceware.org/bugzilla/show_bug.cgi?id=28275 # https://sourceware.org/bugzilla/show_bug.cgi?id=28343 - "FAIL: gdb.threads/detach-step-over.exp: .*internal error" + #"FAIL: gdb.threads/detach-step-over.exp: .*internal error" + # https://sourceware.org/bugzilla/show_bug.cgi?id=26363 "FAIL: gdb.xml/tdesc-reload.exp: .*internal error" # https://sourceware.org/bugzilla/show_bug.cgi?id=26761 @@ -271,6 +280,19 @@ kfail=( # https://sourceware.org/bugzilla/show_bug.cgi?id=27813 "FAIL: .*.exp: .*tab complete .* \(clearing input line\) \(timeout\)" "FAIL: .*.exp: .*cmd complete .*" + + # https://sourceware.org/bugzilla/show_bug.cgi?id=27027 + # https://sourceware.org/bugzilla/show_bug.cgi?id=28464 + "FAIL: gdb.ada/mi_var_access.exp: Create varobj \(unexpected output\)" + "FAIL: gdb.ada/mi_var_access.exp: update at stop 2 \(unexpected output\)" + + # Fragile test-case, requires glibc to fail in a certain way, ignore. + "FAIL: gdb.base/gdb-rhbz1156192-recursive-dlopen.exp:" + + # GDB fails to print "Thread $x stopped" message for all threads, but + # subsequent info threads shows all threads stopped, and a previous + # info threads show all threads running. Not harmful. + "FAIL: gdb.threads/interrupt-while-step-over.exp: displaced-stepping=off: iter=[0-9]*: wait for stops \(timeout\)" ) # kfail kfail_sle12=( @@ -322,13 +344,39 @@ kfail_sle12=( # https://sourceware.org/bugzilla/show_bug.cgi?id=29245 # Python-2 related. "FAIL: gdb.python/py-mi-cmd.exp: -pycmd bk3 \(unexpected output\)" + + # https://sourceware.org/bugzilla/show_bug.cgi?id=26967 + "FAIL: gdb.base/longjmp.exp: next over call_longjmp \(2\)" + "FAIL: gdb.base/longjmp.exp: next over longjmp\(1\)" + "FAIL: gdb.base/longjmp.exp: next over patt3" + "FAIL: gdb.base/premature-dummy-frame-removal.exp: p some_func \(\)" + "FAIL: gdb.base/premature-dummy-frame-removal.exp: set debug frame on" + + # Commit 2d77a94ff17 ("[gdb/testsuite] Require debug info for + # gdb.tui/tui-layout-asm-short-prog.exp") + "FAIL: gdb.tui/tui-layout-asm-short-prog.exp: check asm box contents" + "FAIL: gdb.tui/tui-layout-asm-short-prog.exp: check asm box contents again" + + # Test-cases that use -static but may turn out to be PIE when using + # unix/-fPIE/-fpie. + "FAIL: gdb.base/break-entry.exp: running to .* in runto" + "FAIL: gdb.base/catch-fork-static.exp: run to fork" + "FAIL: gdb.threads/staticthreads.exp: continue to main's call of sem_post" + "FAIL: gdb.threads/staticthreads.exp: handle SIG32 helps" + "FAIL: gdb.threads/staticthreads.exp: running to main in runto" + "FAIL: gdb.threads/staticthreads.exp: running to main in runto" + "FAIL: gdb.dwarf2/frame-inlined-in-outer-frame.exp: step back into _start" + "FAIL: gdb.dwarf2/frame-inlined-in-outer-frame.exp: step back into foo" + "FAIL: gdb.dwarf2/frame-inlined-in-outer-frame.exp: step into bar" + "FAIL: gdb.dwarf2/frame-inlined-in-outer-frame.exp: step into foo" + + # Fails on both i586 and s390x/-m31 for SLE-12-SP3, but does not reproduce + # on s390x/-m31 for SLE-12-SP5 with trunk. + "FAIL: gdb.guile/scm-disasm.exp: disassemble via memory port" + "FAIL: gdb.guile/scm-disasm.exp: memory-port: disassemble" ) # kfail_sle12 kfail_factory=( - # https://sourceware.org/bugzilla/show_bug.cgi?id=27027 - # https://sourceware.org/bugzilla/show_bug.cgi?id=28464 - "FAIL: gdb.ada/mi_var_access.exp: Create varobj \(unexpected output\)" - "FAIL: gdb.ada/mi_var_access.exp: update at stop 2 \(unexpected output\)" # https://sourceware.org/bugzilla/show_bug.cgi?id=28463 "FAIL: gdb.ada/set_pckd_arr_elt.exp: scenario=minimal: print va.t\(1\) := 15" "FAIL: gdb.ada/set_pckd_arr_elt.exp: scenario=minimal: continue to update_small for va.t" @@ -416,6 +464,7 @@ kfail_powerpc64le=( # return value for non-trivial values"). "FAIL: gdb.cp/non-trivial-retval.exp: finish from" "FAIL: gdb.ada/array_return.exp: value printed by finish of Create_Small_Float_Vector" + "FAIL: gdb.base/gnu_vector.exp: call add_structvecs" # Commit f68eca29d3b ("PowerPC, fix gdb.base/retval-large-struct.exp"). "FAIL: gdb.base/retval-large-struct.exp: finish from return_large_struct" @@ -471,6 +520,66 @@ kfail_powerpc64le=( "FAIL: gdb.ada/float-bits.exp: print 16llf#4000921fb54442d18469898cc51701b8#" "FAIL: gdb.ada/float-bits.exp: print \\\$foo:=16llf#4000921fb54442d18469898cc51701b8#" "FAIL: gdb.ada/float-bits.exp: print internal long double variable after assignment" + + # Commit 8b272d7671f ("[gdb/testsuite] Fix gdb.guile/scm-symtab.exp for + # ppc64le"). + "FAIL: gdb.guile/scm-symtab.exp: test find-pc-line with resume address" +) + +kfail_powerpc64le_sle12=( + # Commit 85819864f7c ("[gdb/testsuite] Fix gdb.arch/altivec-regs.exp with + # gcc 4.8.5"). + "FAIL: gdb.arch/altivec-regs.exp: down to vector_fun" + "FAIL: gdb.arch/altivec-regs.exp: finish returned correct value" + "FAIL: gdb.arch/altivec-regs.exp: print vector parameter a" + "FAIL: gdb.arch/altivec-regs.exp: print vector parameter b" +) + +kfail_s390x_s390=( + # Commit 167f3beb655 ("[gdb/testsuite] Fix gdb.base/write_mem.exp for big + # endian") + "FAIL: gdb.base/write_mem.exp: x /xh main" +) + +# Plain s390 or s390x/-m31. +kfail_s390=( + "${kfail_s390x_s390[@]}" + + # https://sourceware.org/bugzilla/show_bug.cgi?id=29841 + "FAIL: gdb.reverse/.*.exp:" + + # Doesn't reproduce with trunk on SLE-12SP5. + "FAIL: gdb.guile/scm-ports.exp: buffered: test byte at sp, before flush" + + # https://sourceware.org/bugzilla/show_bug.cgi?id=29867 + "FAIL: gdb.guile/scm-lazy-string.exp: ptr: lazy string length 2 value" + "FAIL: gdb.guile/scm-lazy-string.exp: ptr: lazy string value" + "FAIL: gdb.guile/scm-lazy-string.exp: ptr: print ptr" + "FAIL: gdb.base/sym-file.exp: add-symbol-file sym-file-lib.so addr" + "FAIL: gdb.base/sym-file.exp: continue to breakpoint: gdb_add_symbol_file" + "FAIL: gdb.python/py-lazy-string.exp: ptr: lazy string length 2 value" + "FAIL: gdb.python/py-lazy-string.exp: ptr: lazy string value" + "FAIL: gdb.python/py-lazy-string.exp: ptr: print ptr" + "FAIL: gdb.python/py-nested-maps.exp: headers=on: pretty=off: exp='\*mm': depth=1: p \*mm" + "FAIL: gdb.python/py-nested-maps.exp: headers=on: pretty=off: exp='\*mm': depth=2: p \*mm" + "FAIL: gdb.python/py-nested-maps.exp: headers=on: pretty=off: exp='\*mm': depth=3: p \*mm" + "FAIL: gdb.python/py-nested-maps.exp: headers=on: pretty=off: exp='\*mm': depth=unlimited: p \*mm" + "FAIL: gdb.python/py-nested-maps.exp: pretty=off: exp='\*mm': depth=1: p \*mm" + "FAIL: gdb.python/py-nested-maps.exp: pretty=off: exp='\*mm': depth=2: p \*mm" + "FAIL: gdb.python/py-nested-maps.exp: pretty=off: exp='\*mm': depth=3: p \*mm" + "FAIL: gdb.python/py-nested-maps.exp: pretty=off: exp='\*mm': depth=unlimited: p \*mm" + "FAIL: gdb.python/py-nested-maps.exp: pretty=on: exp='\*mm': depth=1: p \*mm" + "FAIL: gdb.python/py-nested-maps.exp: pretty=on: exp='\*mm': depth=2: p \*mm" + "FAIL: gdb.python/py-nested-maps.exp: pretty=on: exp='\*mm': depth=3: p \*mm" + "FAIL: gdb.python/py-nested-maps.exp: pretty=on: exp='\*mm': depth=unlimited: p \*mm" + "FAIL: gdb.base/info-shared.exp:" + "FAIL: gdb.python/py-strfns.exp: p /d {char\[4\]} arg" + "FAIL: gdb.python/py-strfns.exp: p arg" +) + +# s390x/-m64. +kfail_s390x=( + "${kfail_s390x_s390[@]}" ) case $n in @@ -481,6 +590,7 @@ case $n in # Todo: apply kfail_factory/kfail_sle12 only when appropriate. kfail+=("${kfail_factory[@]}") kfail+=("${kfail_sle12[@]}") + kfail+=("${kfail_s390[@]}") kfail_re=$(join "|" "${kfail[@]}") grep "^FAIL:.*internal error" binaries-testsuite*/gdb-testresults/*.sum \ | grep -E -v "$kfail_re" @@ -527,6 +637,9 @@ case $n in # https://sourceware.org/bugzilla/show_bug.cgi?id=29783 "frame.c:[0-9]*: internal-error: get_selected_frame: Assertion \`selected_frame != NULL' failed." + + # https://sourceware.org/bugzilla/show_bug.cgi?id=29841 + "regcache.c:[0-9]*: internal-error: raw_read: Assertion \`buf != NULL' failed." ) kfail_re=$(join "|" "${kfail[@]}") @@ -684,6 +797,16 @@ case $n in if $have_powerpc64le; then kfail+=("${kfail_powerpc64le[@]}") fi + if $have_powerpc64le && $have_sl12; then + kfail+=("${kfail_powerpc64le_sle12[@]}") + fi + if $have_s390; then + kfail+=("${kfail_s390[@]}") + fi + + if $have_s390x; then + kfail+=("${kfail_s390x[@]}") + fi for sum in "${sums[@]}"; do report_sum "$sum"