diff options
-rw-r--r-- | gdb/infcmd.c | 79 | ||||
-rw-r--r-- | gdb/infrun.c | 13 | ||||
-rw-r--r-- | gdb/infrun.h | 3 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/catch-follow-exec.exp | 17 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/info-program.c | 66 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/info-program.exp | 131 |
6 files changed, 263 insertions, 46 deletions
diff --git a/gdb/infcmd.c b/gdb/infcmd.c index bffe8d5..7445875 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -1937,37 +1937,74 @@ finish_command (const char *arg, int from_tty) static void info_program_command (const char *args, int from_tty) { - bpstat *bs; - int num, stat; - ptid_t ptid; - process_stratum_target *proc_target; + scoped_restore_current_thread restore_thread; - if (!target_has_execution ()) - { - gdb_printf (_("The program being debugged is not being run.\n")); - return; - } + thread_info *tp; + + /* In non-stop, since every thread is controlled individually, we'll + show execution info about the current thread. In all-stop, we'll + show execution info about the last stop. */ if (non_stop) { - ptid = inferior_ptid; - proc_target = current_inferior ()->process_target (); + if (!target_has_execution ()) + { + gdb_printf (_("The program being debugged is not being run.\n")); + return; + } + + if (inferior_ptid == null_ptid) + error (_("No selected thread.")); + + tp = inferior_thread (); + + gdb_printf (_("Selected thread %s (%s).\n"), + print_thread_id (tp), + target_pid_to_str (tp->ptid).c_str ()); + + if (tp->state == THREAD_EXITED) + { + gdb_printf (_("Selected thread has exited.\n")); + return; + } + else if (tp->state == THREAD_RUNNING) + { + gdb_printf (_("Selected thread is running.\n")); + return; + } } else - get_last_target_status (&proc_target, &ptid, nullptr); + { + tp = get_previous_thread (); + + if (tp == nullptr) + { + gdb_printf (_("The program being debugged is not being run.\n")); + return; + } - if (ptid == null_ptid || ptid == minus_one_ptid) - error (_("No selected thread.")); + switch_to_thread (tp); - thread_info *tp = find_thread_ptid (proc_target, ptid); + gdb_printf (_("Last stopped for thread %s (%s).\n"), + print_thread_id (tp), + target_pid_to_str (tp->ptid).c_str ()); - if (tp->state == THREAD_EXITED) - error (_("Invalid selected thread.")); - else if (tp->state == THREAD_RUNNING) - error (_("Selected thread is running.")); + if (tp->state == THREAD_EXITED) + { + gdb_printf (_("Thread has since exited.\n")); + return; + } + + if (tp->state == THREAD_RUNNING) + { + gdb_printf (_("Thread is now running.\n")); + return; + } + } - bs = tp->control.stop_bpstat; - stat = bpstat_num (&bs, &num); + int num; + bpstat *bs = tp->control.stop_bpstat; + int stat = bpstat_num (&bs, &num); target_files_info (); gdb_printf (_("Program stopped at %s.\n"), diff --git a/gdb/infrun.c b/gdb/infrun.c index a936f18..252f6e5 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -149,8 +149,9 @@ show_step_stop_if_no_debug (struct ui_file *file, int from_tty, } /* proceed and normal_stop use this to notify the user when the - inferior stopped in a different thread than it had been running - in. */ + inferior stopped in a different thread than it had been running in. + It can also be used to find for which thread normal_stop last + reported a stop. */ static thread_info_ref previous_thread; /* See infrun.h. */ @@ -164,6 +165,14 @@ update_previous_thread () previous_thread = thread_info_ref::new_reference (inferior_thread ()); } +/* See infrun.h. */ + +thread_info * +get_previous_thread () +{ + return previous_thread.get (); +} + /* If set (default for legacy reasons), when following a fork, GDB will detach from one of the fork branches, child or parent. Exactly which branch is detached depends on 'set follow-fork-mode' diff --git a/gdb/infrun.h b/gdb/infrun.h index acaec9b..18dd3c2 100644 --- a/gdb/infrun.h +++ b/gdb/infrun.h @@ -121,6 +121,9 @@ extern enum exec_direction_kind execution_direction; inferior_thread, or at nullptr, if there's no selected thread. */ extern void update_previous_thread (); +/* Get a weak reference to 'previous_thread'. */ +extern thread_info *get_previous_thread (); + extern void start_remote (int from_tty); /* Clear out all variables saying what to do when inferior is diff --git a/gdb/testsuite/gdb.base/catch-follow-exec.exp b/gdb/testsuite/gdb.base/catch-follow-exec.exp index 08aab08..e2e1998 100644 --- a/gdb/testsuite/gdb.base/catch-follow-exec.exp +++ b/gdb/testsuite/gdb.base/catch-follow-exec.exp @@ -31,16 +31,25 @@ proc catch_follow_exec { } { return -1 } - gdb_test "catch exec" \ - {Catchpoint [0-9]+ \(exec\)} + set bpnum "" + gdb_test_multiple "catch exec" "" { + -wrap -re "Catchpoint ($::decimal) \\\(exec\\\)" { + set bpnum $expect_out(1,string) + } + } + if {$bpnum == ""} { + return + } gdb_test_no_output "set follow-exec-mode new" gdb_test "continue" \ - ".*hit Catchpoint.*" + "Thread 2.1 .*hit Catchpoint $bpnum.*" + + set any "\[^\r\n\]*" gdb_test "info prog" \ - "No selected thread." + "Last stopped for thread 2.1 \\\($any\\\)\\..*It stopped at breakpoint $bpnum\\..*" } catch_follow_exec diff --git a/gdb/testsuite/gdb.base/info-program.c b/gdb/testsuite/gdb.base/info-program.c new file mode 100644 index 0000000..fcb3c61 --- /dev/null +++ b/gdb/testsuite/gdb.base/info-program.c @@ -0,0 +1,66 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +#ifdef USE_THREADS + +#include <pthread.h> +#include <unistd.h> +#include <stdlib.h> + +pthread_barrier_t barrier; +pthread_t child_thread; + +void * +child_function (void *arg) +{ + pthread_barrier_wait (&barrier); + + while (1) + usleep (100); + + pthread_exit (NULL); +} + +#endif /* USE_THREADS */ + +static void +done (void) +{ +} + +int +main (void) +{ +#ifdef USE_THREADS + int res; + + alarm (300); + + pthread_barrier_init (&barrier, NULL, 2); + + res = pthread_create (&child_thread, NULL, child_function, NULL); + pthread_barrier_wait (&barrier); +#endif /* USE_THREADS */ + + done (); + +#ifdef USE_THREADS + pthread_join (child_thread, NULL); +#endif /* USE_THREADS */ + + return 0; +} diff --git a/gdb/testsuite/gdb.base/info-program.exp b/gdb/testsuite/gdb.base/info-program.exp index 1496ea5..a1c7793 100644 --- a/gdb/testsuite/gdb.base/info-program.exp +++ b/gdb/testsuite/gdb.base/info-program.exp @@ -13,31 +13,124 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -standard_testfile normal.c +# Test "info program". +# +# We build both single-threaded and multi-threaded programs so that if +# the target doesn't support multi-threading, we still exercise the +# command. +# +# With the multi-threaded program, we test that in all-stop mode, GDB +# prints information about the last thread that stopped, not the +# current thread. In non-stop mode, the command always prints info +# about the selected thread, so we test that. -if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } { - return -1 -} +standard_testfile -if { ![runto_main] } { - return -1 -} +# Run the test with the given parameters: +# +# - THREADS: threads flavor, either single-threaded or +# multi-threaded. +# - NON-STOP: "set non-stop" value, "on" or "off". + +proc do_test { threads non-stop } { + save_vars { ::GDBFLAGS } { + append ::GDBFLAGS " -ex \"set non-stop ${non-stop}\"" + clean_restart $::binfile-$threads + } + + gdb_test "info program" \ + "The program being debugged is not being run." \ + "info program before run" + + if { ![runto done] } { + return -1 + } + + if {${non-stop} == "on"} { + set thread_line "Selected" + } else { + set thread_line "Last stopped for" + } -gdb_test "info program" "Program stopped at $hex\.\r\nIt stopped at breakpoint $decimal\.\r\nType \"info stack\" or \"info registers\" for more information\." \ - "info program after run to main" + gdb_test "info program" \ + [multi_line \ + "$thread_line thread 1 (\[^\r\n\]+)\\." \ + ".*" \ + "Program stopped at $::hex\." \ + "It stopped at breakpoint $::decimal\." \ + "Type \"info stack\" or \"info registers\" for more information\."] \ + "info program after run to main" -# We don't really care where this step lands, so long as GDB reports -# that the inferior stopped due to a step in the subsequent test. -gdb_test "next" ".*" "step before info program" + # We don't really care where this step lands, so long as GDB reports + # that the inferior stopped due to a step in the subsequent test. + gdb_test "next" ".*" "step before info program" -gdb_test "info program" "Program stopped at $hex\.\r\nIt stopped after being stepped\.\r\nType \"info stack\" or \"info registers\" for more information\." \ - "info program after next" + gdb_test "info program" \ + [multi_line \ + "$thread_line thread 1 (\[^\r\n\]+)\\." \ + ".*" \ + "Program stopped at $::hex\." \ + "It stopped after being stepped\." \ + "Type \"info stack\" or \"info registers\" for more information\."] \ + "info program after next" -if {![runto_main]} { - return -1 + if {$threads == "mt"} { + gdb_test "thread 2" "\\\[Switching to thread 2 .*" + + if {${non-stop} == "on"} { + gdb_test "info program" \ + [multi_line \ + "$thread_line thread 2 (\[^\r\n\]+)\\." \ + "Selected thread is running\\."] \ + "info program after next, other thread" + } else { + gdb_test "info program" \ + [multi_line \ + "$thread_line thread 1 (\[^\r\n\]+)\\." \ + ".*" \ + "Program stopped at $::hex\." \ + "It stopped after being stepped\." \ + "Type \"info stack\" or \"info registers\" for more information\."] \ + "info program after next, other thread" + } + } + + gdb_test "kill" "" "kill program" \ + "Kill the program being debugged.*y or n. $" "y" + + gdb_test "info program" "The program being debugged is not being run." \ + "info program, after kill" + + if { ![runto done] } { + return -1 + } + + delete_breakpoints + + gdb_test "info program" \ + [multi_line \ + "$thread_line thread 1 (\[^\r\n\]+)\\." \ + ".*" \ + "Program stopped at $::hex\." \ + "It stopped at a breakpoint that has since been deleted\." \ + "Type \"info stack\" or \"info registers\" for more information\."] \ + "info program after deleting all breakpoints" } -delete_breakpoints +# Build executables and test them, one for each +# single-thread/multi-thread flavor. +foreach_with_prefix threads {st mt} { + set opts {debug} + if {$threads == "mt"} { + lappend opts pthreads "additional_flags=-DUSE_THREADS" + } -gdb_test "info program" "Program stopped at $hex\.\r\nIt stopped at a breakpoint that has since been deleted\.\r\nType \"info stack\" or \"info registers\" for more information\." \ - "info program after deleting all breakpoints" + if { [build_executable "failed to prepare $threads" \ + ${testfile}-${threads} ${srcfile} $opts] } { + continue + } + + foreach_with_prefix non-stop {on off} { + do_test ${threads} ${non-stop} + } +} |