aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog18
-rw-r--r--gdb/infrun.c48
-rw-r--r--gdb/testsuite/ChangeLog6
-rw-r--r--gdb/testsuite/gdb.base/checkpoint-ns.exp26
-rw-r--r--gdb/testsuite/gdb.base/checkpoint.exp6
-rw-r--r--gdb/thread.c38
6 files changed, 131 insertions, 11 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 67a61d8..7f44bc1 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,21 @@
+2015-08-07 Pedro Alves <palves@redhat.com>
+
+ * infrun.c (handle_inferior_event): If we get
+ TARGET_WAITKIND_SIGNALLED or TARGET_WAITKIND_EXITED in non-stop
+ mode, mark all threads of the exiting process as not-executing.
+ (normal_stop): If we get TARGET_WAITKIND_SIGNALLED or
+ TARGET_WAITKIND_EXITED in non-stop mode, finish all threads of the
+ exiting process, if inferior_ptid still points at a process.
+ * thread.c (struct current_thread_cleanup) <next>: New field.
+ (current_thread_cleanup_chain): New global.
+ (restore_current_thread_ptid_changed): New function.
+ (restore_current_thread_cleanup_dtor): Remove the cleanup from the
+ current_thread_cleanup_chain list.
+ (make_cleanup_restore_current_thread): Add the cleanup data to the
+ current_thread_cleanup_chain list.
+ (_initialize_thread): Install restore_current_thread_ptid_changed
+ as thread_ptid_changed observer.
+
2015-08-07 Joel Brobecker <brobecker@adacore.com>
* dtrace-probe.c (dtrace_process_dof): Ignore the objfile's DOF
diff --git a/gdb/infrun.c b/gdb/infrun.c
index c717ae5..8209e93 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -3785,14 +3785,31 @@ handle_inferior_event_1 (struct execution_control_state *ecs)
/* Mark the non-executing threads accordingly. In all-stop, all
threads of all processes are stopped when we get any event
- reported. In non-stop mode, only the event thread stops. If
- we're handling a process exit in non-stop mode, there's nothing
- to do, as threads of the dead process are gone, and threads of
- any other process were left running. */
+ reported. In non-stop mode, only the event thread stops. */
if (!non_stop)
set_executing (minus_one_ptid, 0);
- else if (ecs->ws.kind != TARGET_WAITKIND_SIGNALLED
- && ecs->ws.kind != TARGET_WAITKIND_EXITED)
+ else if (ecs->ws.kind == TARGET_WAITKIND_SIGNALLED
+ || ecs->ws.kind == TARGET_WAITKIND_EXITED)
+ {
+ ptid_t pid_ptid;
+
+ /* If we're handling a process exit in non-stop mode, even
+ though threads haven't been deleted yet, one would think that
+ there is nothing to do, as threads of the dead process will
+ be soon deleted, and threads of any other process were left
+ running. However, on some targets, threads survive a process
+ exit event. E.g., for the "checkpoint" command, when the
+ current checkpoint/fork exits, linux-fork.c automatically
+ switches to another fork from within target_mourn_inferior,
+ by associating the same inferior/thread to another fork. We
+ haven't mourned yet at this point, but we must mark any
+ threads left in the process as not-executing so that
+ finish_thread_state marks them stopped (in the user's
+ perspective) if/when we present the stop to the user. */
+ pid_ptid = pid_to_ptid (ptid_get_pid (ecs->ptid));
+ set_executing (pid_ptid, 0);
+ }
+ else
set_executing (ecs->ptid, 0);
switch (ecs->ws.kind)
@@ -6554,6 +6571,7 @@ normal_stop (void)
struct target_waitstatus last;
ptid_t last_ptid;
struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
+ ptid_t pid_ptid;
get_last_target_status (&last_ptid, &last);
@@ -6563,9 +6581,21 @@ normal_stop (void)
here, so do this before any filtered output. */
if (!non_stop)
make_cleanup (finish_thread_state_cleanup, &minus_one_ptid);
- else if (last.kind != TARGET_WAITKIND_SIGNALLED
- && last.kind != TARGET_WAITKIND_EXITED
- && last.kind != TARGET_WAITKIND_NO_RESUMED)
+ else if (last.kind == TARGET_WAITKIND_SIGNALLED
+ || last.kind == TARGET_WAITKIND_EXITED)
+ {
+ /* On some targets, we may still have live threads in the
+ inferior when we get a process exit event. E.g., for
+ "checkpoint", when the current checkpoint/fork exits,
+ linux-fork.c automatically switches to another fork from
+ within target_mourn_inferior. */
+ if (!ptid_equal (inferior_ptid, null_ptid))
+ {
+ pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
+ make_cleanup (finish_thread_state_cleanup, &pid_ptid);
+ }
+ }
+ else if (last.kind != TARGET_WAITKIND_NO_RESUMED)
make_cleanup (finish_thread_state_cleanup, &inferior_ptid);
/* As we're presenting a stop, and potentially removing breakpoints,
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 1417188..5e4d5e7 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2015-08-07 Pedro Alves <palves@redhat.com>
+
+ * gdb.base/checkpoint-ns.exp: New file.
+ * gdb.base/checkpoint.exp: Pass explicit "checkpoint.c" to
+ standard_testfile.
+
2015-08-07 Markus Metzger <markus.t.metzger@intel.com>
* lib/gdb.exp (skip_tsx_tests, skip_btrace_pt_tests): New.
diff --git a/gdb/testsuite/gdb.base/checkpoint-ns.exp b/gdb/testsuite/gdb.base/checkpoint-ns.exp
new file mode 100644
index 0000000..d3698ba
--- /dev/null
+++ b/gdb/testsuite/gdb.base/checkpoint-ns.exp
@@ -0,0 +1,26 @@
+# Copyright 2015 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/>. */
+
+# Test gdb checkpoint and restart in non-stop mode.
+
+# We drive non-stop mode from a separate file because the whole test
+# takes a while to run. This way, we can test both modes in parallel.
+
+set saved_gdbflags $GDBFLAGS
+append GDBFLAGS " -ex \"set non-stop on\""
+
+source $srcdir/$subdir/checkpoint.exp
+
+set GDBFLAGS $saved_gdbflags
diff --git a/gdb/testsuite/gdb.base/checkpoint.exp b/gdb/testsuite/gdb.base/checkpoint.exp
index 6d94ab6..4a476be 100644
--- a/gdb/testsuite/gdb.base/checkpoint.exp
+++ b/gdb/testsuite/gdb.base/checkpoint.exp
@@ -24,8 +24,10 @@ if {![istarget "*-*-linux*"]} then {
continue
}
-
-standard_testfile .c
+# Must name the source file explicitly, otherwise when driven by
+# checkpoints-ns.exp, we'd try compiling checkpoints-ns.c, which
+# doesn't exist.
+standard_testfile checkpoint.c
set pi_txt [gdb_remote_download host ${srcdir}/${subdir}/pi.txt]
if {[is_remote host]} {
diff --git a/gdb/thread.c b/gdb/thread.c
index 23dfcc9..46b5947 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -1279,8 +1279,16 @@ restore_selected_frame (struct frame_id a_frame_id, int frame_level)
}
}
+/* Data used by the cleanup installed by
+ 'make_cleanup_restore_current_thread'. */
+
struct current_thread_cleanup
{
+ /* Next in list of currently installed 'struct
+ current_thread_cleanup' cleanups. See
+ 'current_thread_cleanup_chain' below. */
+ struct current_thread_cleanup *next;
+
ptid_t inferior_ptid;
struct frame_id selected_frame_id;
int selected_frame_level;
@@ -1289,6 +1297,29 @@ struct current_thread_cleanup
int was_removable;
};
+/* A chain of currently installed 'struct current_thread_cleanup'
+ cleanups. Restoring the previously selected thread looks up the
+ old thread in the thread list by ptid. If the thread changes ptid,
+ we need to update the cleanup's thread structure so the look up
+ succeeds. */
+static struct current_thread_cleanup *current_thread_cleanup_chain;
+
+/* A thread_ptid_changed observer. Update all currently installed
+ current_thread_cleanup cleanups that want to switch back to
+ OLD_PTID to switch back to NEW_PTID instead. */
+
+static void
+restore_current_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid)
+{
+ struct current_thread_cleanup *it;
+
+ for (it = current_thread_cleanup_chain; it != NULL; it = it->next)
+ {
+ if (ptid_equal (it->inferior_ptid, old_ptid))
+ it->inferior_ptid = new_ptid;
+ }
+}
+
static void
do_restore_current_thread_cleanup (void *arg)
{
@@ -1329,6 +1360,8 @@ restore_current_thread_cleanup_dtor (void *arg)
struct thread_info *tp;
struct inferior *inf;
+ current_thread_cleanup_chain = current_thread_cleanup_chain->next;
+
tp = find_thread_ptid (old->inferior_ptid);
if (tp)
tp->refcount--;
@@ -1362,6 +1395,9 @@ make_cleanup_restore_current_thread (void)
old->inf_id = current_inferior ()->num;
old->was_removable = current_inferior ()->removable;
+ old->next = current_thread_cleanup_chain;
+ current_thread_cleanup_chain = old;
+
if (!ptid_equal (inferior_ptid, null_ptid))
{
old->was_stopped = is_stopped (inferior_ptid);
@@ -1815,4 +1851,6 @@ Show printing of thread events (such as thread start and exit)."), NULL,
&setprintlist, &showprintlist);
create_internalvar_type_lazy ("_thread", &thread_funcs, NULL);
+
+ observer_attach_thread_ptid_changed (restore_current_thread_ptid_changed);
}