From 252fbfc86a997c0750804d5cdd3ae395d24bebc4 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Thu, 23 Oct 2008 23:11:21 +0000 Subject: [PATCH] gdb/ 2008-10-23 Pedro Alves * defs.h: Mention ptid_is_pid. * inferior.h (ptid_is_pid): Declare. * gdbthread.h (struct thread_info) : New field. (set_stop_requested): Declare. * infcmd.c (interrupt_target_1): Call set_stop_requested. * infrun.c (clear_proceed_status): Clear stop_requested. (infrun_thread_stop_requested_callback, infrun_thread_stop_requested): New. (handle_inferior_event): If a TARGET_SIGNAL_TRAP is reported on a thread that had an explicit stop request, pretend we got a TARGET_SIGNAL_0. Always stop if the thread had an explicit stop request. (print_stop_reason): In the SIGNAL_RECEIVED case, if we're not outputting to MI, and we got a TARGET_SIGNAL_0, print "# Stopped", instead of mentioning signal 0. (ptid_is_pid): New. * thread.c (set_stop_requested): New. * linux-nat.c (queued_waitpid): Rename to ... (queued_waitpid_1): ... this. Add `peek' argument. Handle it. (queued_waitpid): New, as wrapper to queued_waitpid_1. (push_waitpid): Push the SIGTRAP to the local event queue, to the kernel's. (send_sigint_callback): Delete. (linux_nat_stop_lwp): New. (linux_nat_stop): Use it. gdb/doc/ 2008-10-23 Pedro Alves * observer.texi (thread_stop_requested): New. gdb/testsuite/ 2008-10-23 Pedro Alves * lib/mi-support.exp (mi_expect_interrupt): Expect signal 0 instead of SIGINT. --- gdb/ChangeLog | 29 +++++ gdb/defs.h | 1 + gdb/doc/ChangeLog | 4 + gdb/doc/observer.texi | 8 ++ gdb/gdbthread.h | 10 ++ gdb/infcmd.c | 9 ++ gdb/inferior.h | 3 + gdb/infrun.c | 177 +++++++++++++++++++++++++++---- gdb/linux-nat.c | 128 +++++++++++++++++----- gdb/testsuite/ChangeLog | 5 + gdb/testsuite/lib/mi-support.exp | 2 +- gdb/thread.c | 25 +++++ 12 files changed, 351 insertions(+), 50 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 361667feee5..b8003a4121c 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,32 @@ +2008-10-23 Pedro Alves + + * defs.h: Mention ptid_is_pid. + * inferior.h (ptid_is_pid): Declare. + * gdbthread.h (struct thread_info) : New field. + (set_stop_requested): Declare. + * infcmd.c (interrupt_target_1): Call set_stop_requested. + * infrun.c (clear_proceed_status): Clear stop_requested. + (infrun_thread_stop_requested_callback, + infrun_thread_stop_requested): New. + (handle_inferior_event): If a TARGET_SIGNAL_TRAP is reported on a + thread that had an explicit stop request, pretend we got a + TARGET_SIGNAL_0. Always stop if the thread had an explicit stop + request. + (print_stop_reason): In the SIGNAL_RECEIVED case, if we're not + outputting to MI, and we got a TARGET_SIGNAL_0, print "# Stopped", + instead of mentioning signal 0. + (ptid_is_pid): New. + * thread.c (set_stop_requested): New. + + * linux-nat.c (queued_waitpid): Rename to ... + (queued_waitpid_1): ... this. Add `peek' argument. Handle it. + (queued_waitpid): New, as wrapper to queued_waitpid_1. + (push_waitpid): Push the SIGTRAP to the local event queue, to the + kernel's. + (send_sigint_callback): Delete. + (linux_nat_stop_lwp): New. + (linux_nat_stop): Use it. + 2008-10-23 Paul Pluzhnikov * python/python-value (valpy_getitem): Fix heap corruption. diff --git a/gdb/defs.h b/gdb/defs.h index 5b718a35bc6..42dd821a607 100644 --- a/gdb/defs.h +++ b/gdb/defs.h @@ -752,6 +752,7 @@ enum val_prettyprint ptid_get_lwp - Fetch the lwp component of a ptid. ptid_get_tid - Fetch the tid component of a ptid. ptid_equal - Test to see if two ptids are equal. + ptid_is_pid - Test to see if this ptid represents a process id. Please do NOT access the struct ptid members directly (except, of course, in the implementation of the above ptid manipulation diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 5a79cea9860..56a04963054 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,7 @@ +2008-10-23 Pedro Alves + + * observer.texi (thread_stop_requested): New. + 2008-10-22 Joel Brobecker * gdb.texinfo (Ada Tasks, Ada Tasks and Core Files): New nodes. diff --git a/gdb/doc/observer.texi b/gdb/doc/observer.texi index 3e10c5f17bf..f76bf9ff366 100644 --- a/gdb/doc/observer.texi +++ b/gdb/doc/observer.texi @@ -135,6 +135,14 @@ The thread specified by @var{t} has been created. The thread specified by @var{t} has exited. @end deftypefun +@deftypefun void thread_stop_requested (ptid_t @var{ptid}) +An explicit stop request was issued to @var{ptid}. If @var{ptid} +equals @var{minus_one_ptid}, the request applied to all threads. If +@code{ptid_is_pid(ptid)} returns true, the request applied to all +threads of the process pointed at by @var{ptid}. Otherwise, the +request applied to the single thread pointed at by @var{ptid}. +@end deftypefun + @deftypefun void target_resumed (ptid_t @var{ptid}) The target was resumed. The @var{ptid} parameter specifies which thread was resume, and may be RESUME_ALL if all threads are resumed. diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index 0fb53fbbc4a..55c848df2a4 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -168,6 +168,9 @@ struct thread_info at. */ bpstat stop_bpstat; + /* True if this thread has been explicitly requested to stop. */ + int stop_requested; + /* Private data used by the target vector implementation. */ struct private_thread_info *private; }; @@ -239,6 +242,13 @@ extern void switch_to_thread (ptid_t ptid); If PIDGET (PTID) is -1, marks all threads. */ extern void set_running (ptid_t ptid, int running); +/* Marks or clears thread(s) PTID as having been requested to stop. + If PTID is MINUS_ONE_PTID, applies to all threads. If + ptid_is_pid(PTID) is true, applies to all threads of the process + pointed at by PTID. If STOP, then the THREAD_STOP_REQUESTED + observer is called with PTID as argument. */ +extern void set_stop_requested (ptid_t ptid, int stop); + /* NOTE: Since the thread state is not a boolean, most times, you do not want to check it with negation. If you really want to check if the thread is stopped, diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 4f55c33adea..06e3bc5fc31 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -2245,6 +2245,15 @@ interrupt_target_1 (int all_threads) else ptid = inferior_ptid; target_stop (ptid); + + /* Tag the thread as having been explicitly requested to stop, so + other parts of gdb know not to resume this thread automatically, + if it was stopped due to an internal event. Limit this to + non-stop mode, as when debugging a multi-threaded application in + all-stop mode, we will only get one stop event --- it's undefined + which thread will report the event. */ + if (non_stop) + set_stop_requested (ptid, 1); } /* Stop the execution of the target while running in async mode, in diff --git a/gdb/inferior.h b/gdb/inferior.h index 8b03b63d18b..c707c36860c 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -89,6 +89,9 @@ long ptid_get_tid (ptid_t ptid); /* Compare two ptids to see if they are equal */ extern int ptid_equal (ptid_t p1, ptid_t p2); +/* Return true if PTID represents a process id. */ +extern int ptid_is_pid (ptid_t ptid); + /* Save value of inferior_ptid so that it may be restored by a later call to do_cleanups(). Returns the struct cleanup pointer needed for later doing the cleanup. */ diff --git a/gdb/infrun.c b/gdb/infrun.c index e2a7b197e94..a6d42448d70 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -1147,6 +1147,7 @@ clear_proceed_status (void) tp->step_range_end = 0; tp->step_frame_id = null_frame_id; tp->step_over_calls = STEP_OVER_UNDEBUGGABLE; + tp->stop_requested = 0; tp->stop_step = 0; @@ -1528,6 +1529,100 @@ static void keep_going (struct execution_control_state *ecs); static void print_stop_reason (enum inferior_stop_reason stop_reason, int stop_info); +/* Callback for iterate over threads. If the thread is stopped, but + the user/frontend doesn't know about that yet, go through + normal_stop, as if the thread had just stopped now. ARG points at + a ptid. If PTID is MINUS_ONE_PTID, applies to all threads. If + ptid_is_pid(PTID) is true, applies to all threads of the process + pointed at by PTID. Otherwise, apply only to the thread pointed by + PTID. */ + +static int +infrun_thread_stop_requested_callback (struct thread_info *info, void *arg) +{ + ptid_t ptid = * (ptid_t *) arg; + + if ((ptid_equal (info->ptid, ptid) + || ptid_equal (minus_one_ptid, ptid) + || (ptid_is_pid (ptid) + && ptid_get_pid (ptid) == ptid_get_pid (info->ptid))) + && is_running (info->ptid) + && !is_executing (info->ptid)) + { + struct cleanup *old_chain; + struct execution_control_state ecss; + struct execution_control_state *ecs = &ecss; + + memset (ecs, 0, sizeof (*ecs)); + + old_chain = make_cleanup_restore_current_thread (); + + switch_to_thread (info->ptid); + + /* Go through handle_inferior_event/normal_stop, so we always + have consistent output as if the stop event had been + reported. */ + ecs->ptid = info->ptid; + ecs->event_thread = find_thread_pid (info->ptid); + ecs->ws.kind = TARGET_WAITKIND_STOPPED; + ecs->ws.value.sig = TARGET_SIGNAL_0; + + handle_inferior_event (ecs); + + if (!ecs->wait_some_more) + { + struct thread_info *tp; + + normal_stop (); + + /* Finish off the continuations. The continations + themselves are responsible for realising the thread + didn't finish what it was supposed to do. */ + tp = inferior_thread (); + do_all_intermediate_continuations_thread (tp); + do_all_continuations_thread (tp); + } + + do_cleanups (old_chain); + } + + return 0; +} + +/* This function is attached as a "thread_stop_requested" observer. + Cleanup local state that assumed the PTID was to be resumed, and + report the stop to the frontend. */ + +void +infrun_thread_stop_requested (ptid_t ptid) +{ + struct displaced_step_request *it, *next, *prev = NULL; + + /* PTID was requested to stop. Remove it from the displaced + stepping queue, so we don't try to resume it automatically. */ + for (it = displaced_step_request_queue; it; it = next) + { + next = it->next; + + if (ptid_equal (it->ptid, ptid) + || ptid_equal (minus_one_ptid, ptid) + || (ptid_is_pid (ptid) + && ptid_get_pid (ptid) == ptid_get_pid (it->ptid))) + { + if (displaced_step_request_queue == it) + displaced_step_request_queue = it->next; + else + prev->next = it->next; + + xfree (it); + } + else + prev = it; + } + + iterate_over_threads (infrun_thread_stop_requested_callback, &ptid); +} + /* Callback for iterate_over_threads. */ static int @@ -2279,11 +2374,21 @@ targets should add new threads to the thread list themselves in non-stop mode.") return; } - /* Do we need to clean up the state of a thread that has completed a - displaced single-step? (Doing so usually affects the PC, so do - it here, before we set stop_pc.) */ if (ecs->ws.kind == TARGET_WAITKIND_STOPPED) - displaced_step_fixup (ecs->ptid, ecs->event_thread->stop_signal); + { + /* Do we need to clean up the state of a thread that has + completed a displaced single-step? (Doing so usually affects + the PC, so do it here, before we set stop_pc.) */ + displaced_step_fixup (ecs->ptid, ecs->event_thread->stop_signal); + + /* If we either finished a single-step or hit a breakpoint, but + the user wanted this thread to be stopped, pretend we got a + SIG0 (generic unsignaled stop). */ + + if (ecs->event_thread->stop_requested + && ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP) + ecs->event_thread->stop_signal = TARGET_SIGNAL_0; + } stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid)); @@ -2779,9 +2884,11 @@ process_event_stop_test: target_terminal_ours_for_output (); print_stop_reason (SIGNAL_RECEIVED, ecs->event_thread->stop_signal); } - /* Always stop on signals if we're just gaining control of the - program. */ + /* Always stop on signals if we're either just gaining control + of the program, or the user explicitly requested this thread + to remain stopped. */ if (stop_soon != NO_STOP_QUIETLY + || ecs->event_thread->stop_requested || signal_stop_state (ecs->event_thread->stop_signal)) { stop_stepping (ecs); @@ -3891,22 +3998,36 @@ print_stop_reason (enum inferior_stop_reason stop_reason, int stop_info) return_child_result_value = stop_info; break; case SIGNAL_RECEIVED: - /* Signal received. The signal table tells us to print about - it. */ + /* Signal received. The signal table tells us to print about + it. */ annotate_signal (); - ui_out_text (uiout, "\nProgram received signal "); - annotate_signal_name (); - if (ui_out_is_mi_like_p (uiout)) - ui_out_field_string - (uiout, "reason", async_reason_lookup (EXEC_ASYNC_SIGNAL_RECEIVED)); - ui_out_field_string (uiout, "signal-name", - target_signal_to_name (stop_info)); - annotate_signal_name_end (); - ui_out_text (uiout, ", "); - annotate_signal_string (); - ui_out_field_string (uiout, "signal-meaning", - target_signal_to_string (stop_info)); - annotate_signal_string_end (); + + if (stop_info == TARGET_SIGNAL_0 && !ui_out_is_mi_like_p (uiout)) + { + struct thread_info *t = inferior_thread (); + + ui_out_text (uiout, "\n["); + ui_out_field_string (uiout, "thread-name", + target_pid_to_str (t->ptid)); + ui_out_field_fmt (uiout, "thread-id", "] #%d", t->num); + ui_out_text (uiout, " stopped"); + } + else + { + ui_out_text (uiout, "\nProgram received signal "); + annotate_signal_name (); + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string + (uiout, "reason", async_reason_lookup (EXEC_ASYNC_SIGNAL_RECEIVED)); + ui_out_field_string (uiout, "signal-name", + target_signal_to_name (stop_info)); + annotate_signal_name_end (); + ui_out_text (uiout, ", "); + annotate_signal_string (); + ui_out_field_string (uiout, "signal-meaning", + target_signal_to_string (stop_info)); + annotate_signal_string_end (); + } ui_out_text (uiout, ".\n"); break; case NO_HISTORY: @@ -4817,6 +4938,19 @@ ptid_equal (ptid_t ptid1, ptid_t ptid2) && ptid1.tid == ptid2.tid); } +/* Returns true if PTID represents a process. */ + +int +ptid_is_pid (ptid_t ptid) +{ + if (ptid_equal (minus_one_ptid, ptid)) + return 0; + if (ptid_equal (null_ptid, ptid)) + return 0; + + return (ptid_get_lwp (ptid) == 0 && ptid_get_tid (ptid) == 0); +} + /* restore_inferior_ptid() will be used by the cleanup machinery to restore the inferior_ptid value saved in a call to save_inferior_ptid(). */ @@ -5134,4 +5268,5 @@ Options are 'forward' or 'reverse'."), displaced_step_ptid = null_ptid; observer_attach_thread_ptid_changed (infrun_thread_ptid_changed); + observer_attach_thread_stop_requested (infrun_thread_stop_requested); } diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index f76eaec3082..d2116e12cd0 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -316,7 +316,6 @@ static void linux_nat_async (void (*callback) static int linux_nat_async_mask (int mask); static int kill_lwp (int lwpid, int signo); -static int send_sigint_callback (struct lwp_info *lp, void *data); static int stop_callback (struct lwp_info *lp, void *data); /* Captures the result of a successful waitpid call, along with the @@ -333,8 +332,12 @@ struct waitpid_result in the async SIGCHLD handler. */ static struct waitpid_result *waitpid_queue = NULL; +/* Similarly to `waitpid', but check the local event queue instead of + querying the kernel queue. If PEEK, don't remove the event found + from the queue. */ + static int -queued_waitpid (int pid, int *status, int flags) +queued_waitpid_1 (int pid, int *status, int flags, int peek) { struct waitpid_result *msg = waitpid_queue, *prev = NULL; @@ -370,12 +373,6 @@ QWPID: linux_nat_async_events_state(%d), linux_nat_num_queued_events(%d)\n", { int pid; - if (prev) - prev->next = msg->next; - else - waitpid_queue = msg->next; - - msg->next = NULL; if (status) *status = msg->status; pid = msg->pid; @@ -383,7 +380,17 @@ QWPID: linux_nat_async_events_state(%d), linux_nat_num_queued_events(%d)\n", if (debug_linux_nat_async) fprintf_unfiltered (gdb_stdlog, "QWPID: pid(%d), status(%x)\n", pid, msg->status); - xfree (msg); + + if (!peek) + { + if (prev) + prev->next = msg->next; + else + waitpid_queue = msg->next; + + msg->next = NULL; + xfree (msg); + } return pid; } @@ -396,6 +403,14 @@ QWPID: linux_nat_async_events_state(%d), linux_nat_num_queued_events(%d)\n", return -1; } +/* Similarly to `waitpid', but check the local event queue. */ + +static int +queued_waitpid (int pid, int *status, int flags) +{ + return queued_waitpid_1 (pid, status, flags, 0); +} + static void push_waitpid (int pid, int status, int options) { @@ -2200,11 +2215,11 @@ stop_wait_callback (struct lwp_info *lp, void *data) /* There was no gdb breakpoint set at pc. Put the event back in the queue. */ if (debug_linux_nat) - fprintf_unfiltered (gdb_stdlog, - "SWC: kill %s, %s\n", - target_pid_to_str (lp->ptid), - status_to_str ((int) status)); - kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (status)); + fprintf_unfiltered (gdb_stdlog, "\ +SWC: leaving SIGTRAP in local queue of %s\n", target_pid_to_str (lp->ptid)); + push_waitpid (GET_LWP (lp->ptid), + W_STOPCODE (SIGTRAP), + lp->cloned ? __WCLONE : 0); } } else @@ -4368,15 +4383,76 @@ linux_nat_async (void (*callback) (enum inferior_event_type event_type, return; } +/* Stop an LWP, and push a TARGET_SIGNAL_0 stop status if no other + event came out. */ + static int -send_sigint_callback (struct lwp_info *lp, void *data) +linux_nat_stop_lwp (struct lwp_info *lwp, void *data) { - /* Use is_running instead of !lp->stopped, because the lwp may be - stopped due to an internal event, and we want to interrupt it in - that case too. What we want is to check if the thread is stopped - from the point of view of the user. */ - if (is_running (lp->ptid)) - kill_lwp (GET_LWP (lp->ptid), SIGINT); + ptid_t ptid = * (ptid_t *) data; + + if (ptid_equal (lwp->ptid, ptid) + || ptid_equal (minus_one_ptid, ptid) + || (ptid_is_pid (ptid) + && ptid_get_pid (ptid) == ptid_get_pid (lwp->ptid))) + { + if (!lwp->stopped) + { + int pid, status; + + if (debug_linux_nat) + fprintf_unfiltered (gdb_stdlog, + "LNSL: running -> suspending %s\n", + target_pid_to_str (lwp->ptid)); + + /* Peek once, to check if we've already waited for this + LWP. */ + pid = queued_waitpid_1 (ptid_get_lwp (lwp->ptid), &status, + lwp->cloned ? __WCLONE : 0, 1 /* peek */); + + if (pid == -1) + { + ptid_t ptid = lwp->ptid; + + stop_callback (lwp, NULL); + stop_wait_callback (lwp, NULL); + + /* If the lwp exits while we try to stop it, there's + nothing else to do. */ + lwp = find_lwp_pid (ptid); + if (lwp == NULL) + return 0; + + pid = queued_waitpid_1 (ptid_get_lwp (lwp->ptid), &status, + lwp->cloned ? __WCLONE : 0, + 1 /* peek */); + } + + /* If we didn't collect any signal other than SIGSTOP while + stopping the LWP, push a SIGNAL_0 event. In either case, + the event-loop will end up calling target_wait which will + collect these. */ + if (pid == -1) + push_waitpid (ptid_get_lwp (lwp->ptid), W_STOPCODE (0), + lwp->cloned ? __WCLONE : 0); + } + else + { + /* Already known to be stopped; do nothing. */ + + if (debug_linux_nat) + { + if (find_thread_pid (lwp->ptid)->stop_requested) + fprintf_unfiltered (gdb_stdlog, "\ +LNSL: already stopped/stop_requested %s\n", + target_pid_to_str (lwp->ptid)); + else + fprintf_unfiltered (gdb_stdlog, "\ +LNSL: already stopped/no stop_requested yet %s\n", + target_pid_to_str (lwp->ptid)); + } + } + } return 0; } @@ -4385,13 +4461,9 @@ linux_nat_stop (ptid_t ptid) { if (non_stop) { - if (ptid_equal (ptid, minus_one_ptid)) - iterate_over_lwps (send_sigint_callback, &ptid); - else - { - struct lwp_info *lp = find_lwp_pid (ptid); - send_sigint_callback (lp, NULL); - } + linux_nat_async_events (sigchld_sync); + iterate_over_lwps (linux_nat_stop_lwp, &ptid); + target_async (inferior_event_handler, 0); } else linux_ops->to_stop (ptid); diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index bee4e0d7e91..4939d28218d 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2008-10-23 Pedro Alves + + * lib/mi-support.exp (mi_expect_interrupt): Expect signal 0 + instead of SIGINT. + 2008-10-22 Joel Brobecker * gdb.base/completion.exp: Update expected output following diff --git a/gdb/testsuite/lib/mi-support.exp b/gdb/testsuite/lib/mi-support.exp index 71c0f5946ae..9ef9df97f79 100644 --- a/gdb/testsuite/lib/mi-support.exp +++ b/gdb/testsuite/lib/mi-support.exp @@ -1050,7 +1050,7 @@ proc mi_expect_interrupt { test } { set prompt_re "$mi_gdb_prompt$" } - set r "reason=\"signal-received\",signal-name=\"SIGINT\",signal-meaning=\"Interrupt\"" + set r "reason=\"signal-received\",signal-name=\"0\",signal-meaning=\"Signal 0\"" set any "\[^\n\]*" diff --git a/gdb/thread.c b/gdb/thread.c index 94e187311a3..02fb845abf5 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -606,6 +606,31 @@ set_executing (ptid_t ptid, int executing) } } +void +set_stop_requested (ptid_t ptid, int stop) +{ + struct thread_info *tp; + int all = ptid_equal (ptid, minus_one_ptid); + + if (all || ptid_is_pid (ptid)) + { + for (tp = thread_list; tp; tp = tp->next) + if (all || ptid_get_pid (tp->ptid) == ptid_get_pid (ptid)) + tp->stop_requested = stop; + } + else + { + tp = find_thread_pid (ptid); + gdb_assert (tp); + tp->stop_requested = stop; + } + + /* Call the stop requested observer so other components of GDB can + react to this request. */ + if (stop) + observer_notify_thread_stop_requested (ptid); +} + /* Prints the list of threads and their details on UIOUT. This is a version of 'info_thread_command' suitable for use from MI. -- 2.30.2