diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 67a61d8ca1..7f44bc1f8c 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,21 @@ +2015-08-07 Pedro Alves + + * 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) : 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 * dtrace-probe.c (dtrace_process_dof): Ignore the objfile's DOF diff --git a/gdb/infrun.c b/gdb/infrun.c index c717ae5c11..8209e935f7 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 14171883ab..5e4d5e7b29 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2015-08-07 Pedro Alves + + * gdb.base/checkpoint-ns.exp: New file. + * gdb.base/checkpoint.exp: Pass explicit "checkpoint.c" to + standard_testfile. + 2015-08-07 Markus Metzger * 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 0000000000..d3698bab81 --- /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 . */ + +# 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 6d94ab6aa1..4a476bee98 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 23dfcc915c..46b59476ca 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); }