aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/infcmd.c79
-rw-r--r--gdb/infrun.c13
-rw-r--r--gdb/infrun.h3
-rw-r--r--gdb/testsuite/gdb.base/catch-follow-exec.exp17
-rw-r--r--gdb/testsuite/gdb.base/info-program.c66
-rw-r--r--gdb/testsuite/gdb.base/info-program.exp131
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}
+ }
+}