aboutsummaryrefslogtreecommitdiff
path: root/gdb/thread.c
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2015-08-07 17:23:55 +0100
committerPedro Alves <palves@redhat.com>2015-08-07 17:23:55 +0100
commite1316e60d4d1fe406efc6e7536b2bdb43733e9d2 (patch)
tree3a8dc08596e896676fed44a85da399b302511d1c /gdb/thread.c
parent47e9c225c1cb6fb1809218f5f546a70fc85f705c (diff)
downloadgdb-e1316e60d4d1fe406efc6e7536b2bdb43733e9d2.zip
gdb-e1316e60d4d1fe406efc6e7536b2bdb43733e9d2.tar.gz
gdb-e1316e60d4d1fe406efc6e7536b2bdb43733e9d2.tar.bz2
Fix and test "checkpoint" in non-stop mode
Letting a "checkpoint" run to exit with "set non-stop on" behaves differently compared to the default all-stop mode ("set non-stop off"). Currently, in non-stop mode: (gdb) start Temporary breakpoint 1 at 0x40086b: file src/gdb/testsuite/gdb.base/checkpoint.c, line 28. Starting program: build/gdb/testsuite/gdb.base/checkpoint Temporary breakpoint 1, main () at src/gdb/testsuite/gdb.base/checkpoint.c:28 28 char *tmp = &linebuf[0]; (gdb) checkpoint checkpoint 1: fork returned pid 24948. (gdb) c Continuing. Copy complete. Deleting copy. [Inferior 1 (process 24944) exited normally] [Switching to process 24948] (gdb) info threads Id Target Id Frame 1 process 24948 "checkpoint" (running) No selected thread. See `help thread'. (gdb) c The program is not being run. (gdb) Two issues above: 1. Thread 1 got stuck in "(running)" state (it isn't really running) 2. While checkpoints try to preserve the illusion that the thread is still the same when the process exits, GDB switched to "No thread selected." instead of staying with thread 1 selected. Problem #1 is caused by handle_inferior_event and normal_stop not considering that when a TARGET_WAITKIND_SIGNALLED/TARGET_WAITKIND_EXITED event is reported, and the inferior is mourned, the target may still have execution. Problem #2 is caused by the make_cleanup_restore_current_thread cleanup installed by fetch_inferior_event not being able to find the original thread 1's ptid in the thread list, thus not being able to restore thread 1 as selected thread. The fix is to make the cleanup installed by make_cleanup_restore_current_thread aware of thread ptid changes, by installing a thread_ptid_changed observer that adjusts the cleanup's data. After the patch, we get the same in all-stop and non-stop modes: (gdb) c Continuing. Copy complete. Deleting copy. [Inferior 1 (process 25109) exited normally] [Switching to process 25113] (gdb) info threads Id Target Id Frame * 1 process 25113 "checkpoint" main () at src/gdb/testsuite/gdb.base/checkpoint.c:28 (gdb) Turns out the whole checkpoints.exp file can run in non-stop mode unmodified. I thought of moving most of the test file's contents to a procedure that can be called twice, once in non-stop mode and another in all-stop mode. But then, the test already takes close to 30 seconds to run on my machine, so I thought it'd be nicer to run all-stop and non-stop mode in parallel. Thus I added a new checkpoint-ns.exp file that just appends "set non-stop on" to GDBFLAGS and sources checkpoint.exp. gdb/ChangeLog: 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. gdb/testsuite/ChangeLog: 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.
Diffstat (limited to 'gdb/thread.c')
-rw-r--r--gdb/thread.c38
1 files changed, 38 insertions, 0 deletions
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);
}