From: Pedro Alves Date: Sun, 18 Jan 2009 17:42:16 +0000 (+0000) Subject: PR gdb/9747: X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=29f49a6a4f3f165090de6c85fdb0f29dcb579ae7;p=binutils-gdb.git PR gdb/9747: * gdbthread.h (finish_thread_state, finish_thread_state_cleanup): Declare. * thread.c (finish_thread_state, finish_thread_state_cleanup): New. * infrun.c (wait_for_inferior, fetch_inferior_event): If an error is thrown while handling an event, finish the thread state. (normal_stop): Use finish_thread_state cleanup. * infcmd.c (run_command_1): If an error is thrown while starting the inferior, finish the thread state. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 93d72b0213c..79d3ed707d5 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,15 @@ +2009-01-18 Pedro Alves + + PR gdb/9747: + * gdbthread.h (finish_thread_state, finish_thread_state_cleanup): + Declare. + * thread.c (finish_thread_state, finish_thread_state_cleanup): New. + * infrun.c (wait_for_inferior, fetch_inferior_event): If an error + is thrown while handling an event, finish the thread state. + (normal_stop): Use finish_thread_state cleanup. + * infcmd.c (run_command_1): If an error is thrown while starting + the inferior, finish the thread state. + 2009-01-18 Pedro Alves * tui/tui-winsource.c (tui_update_breakpoint_info): In asm layout, diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index ab95247171d..3a405a86eeb 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -288,6 +288,23 @@ extern void set_executing (ptid_t ptid, int executing); /* Reports if thread PTID is executing. */ extern int is_executing (ptid_t ptid); +/* Merge the executing property of thread PTID over to its thread + state property (frontend running/stopped view). + + "not executing" -> "stopped" + "executing" -> "running" + "exited" -> "exited" + + If PIDGET (PTID) is -1, go over all threads. + + Notifications are only emitted if the thread state did change. */ +extern void finish_thread_state (ptid_t ptid); + +/* Same as FINISH_THREAD_STATE, but with an interface suitable to be + registered as a cleanup. PTID_P points to the ptid_t that is + passed to FINISH_THREAD_STATE. */ +extern void finish_thread_state_cleanup (void *ptid_p); + /* Commands with a prefix of `thread'. */ extern struct cmd_list_element *thread_cmd_list; diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 3ed654590fc..2cd583cdb87 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -456,6 +456,8 @@ static void run_command_1 (char *args, int from_tty, int tbreak_at_main) { char *exec_file; + struct cleanup *old_chain; + ptid_t ptid; dont_repeat (); @@ -544,14 +546,29 @@ run_command_1 (char *args, int from_tty, int tbreak_at_main) target_create_inferior (exec_file, get_inferior_args (), environ_vector (inferior_environ), from_tty); + /* We're starting off a new process. When we get out of here, in + non-stop mode, finish the state of all threads of that process, + but leave other threads alone, as they may be stopped in internal + events --- the frontend shouldn't see them as stopped. In + all-stop, always finish the state of all threads, as we may be + resuming more than just the new process. */ + if (non_stop) + ptid = pid_to_ptid (ptid_get_pid (inferior_ptid)); + else + ptid = minus_one_ptid; + old_chain = make_cleanup (finish_thread_state_cleanup, &ptid); + /* Pass zero for FROM_TTY, because at this point the "run" command has done its thing; now we are setting up the running program. */ post_create_inferior (¤t_target, 0); /* Start the target running. */ proceed ((CORE_ADDR) -1, TARGET_SIGNAL_0, 0); -} + /* Since there was no error, there's no need to finish the thread + states here. */ + discard_cleanups (old_chain); +} static void run_command (char *args, int from_tty) diff --git a/gdb/infrun.c b/gdb/infrun.c index 09869394511..228e7434c11 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -1783,6 +1783,8 @@ wait_for_inferior (int treat_exec_as_sigtrap) while (1) { + struct cleanup *old_chain; + if (deprecated_target_wait_hook) ecs->ptid = deprecated_target_wait_hook (waiton_ptid, &ecs->ws); else @@ -1795,9 +1797,17 @@ wait_for_inferior (int treat_exec_as_sigtrap) ecs->ws.value.sig = TARGET_SIGNAL_TRAP; } + /* If an error happens while handling the event, propagate GDB's + knowledge of the executing state to the frontend/user running + state. */ + old_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid); + /* Now figure out what to do with the result of the result. */ handle_inferior_event (ecs); + /* No error, don't finish the state yet. */ + discard_cleanups (old_chain); + if (!ecs->wait_some_more) break; } @@ -1820,6 +1830,7 @@ fetch_inferior_event (void *client_data) struct execution_control_state ecss; struct execution_control_state *ecs = &ecss; struct cleanup *old_chain = make_cleanup (null_cleanup, NULL); + struct cleanup *ts_old_chain; int was_sync = sync_execution; memset (ecs, 0, sizeof (*ecs)); @@ -1863,6 +1874,14 @@ fetch_inferior_event (void *client_data) thread. */ context_switch (ecs->ptid); + /* If an error happens while handling the event, propagate GDB's + knowledge of the executing state to the frontend/user running + state. */ + if (!non_stop) + ts_old_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid); + else + ts_old_chain = make_cleanup (finish_thread_state_cleanup, &ecs->ptid); + /* Now figure out what to do with the result of the result. */ handle_inferior_event (ecs); @@ -1886,6 +1905,9 @@ fetch_inferior_event (void *client_data) inferior_event_handler (INF_EXEC_COMPLETE, NULL); } + /* No error, don't finish the thread states yet. */ + discard_cleanups (ts_old_chain); + /* Revert thread and frame. */ do_cleanups (old_chain); @@ -4161,9 +4183,23 @@ normal_stop (void) { struct target_waitstatus last; ptid_t last_ptid; + struct cleanup *old_chain = make_cleanup (null_cleanup, NULL); get_last_target_status (&last_ptid, &last); + /* If an exception is thrown from this point on, make sure to + propagate GDB's knowledge of the executing state to the + frontend/user running state. A QUIT is an easy exception to see + here, so do this before any filtered output. */ + if (target_has_execution) + { + if (!non_stop) + old_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid); + else if (last.kind != TARGET_WAITKIND_SIGNALLED + && last.kind != TARGET_WAITKIND_EXITED) + old_chain = make_cleanup (finish_thread_state_cleanup, &inferior_ptid); + } + /* In non-stop mode, we don't want GDB to switch threads behind the user's back, to avoid races where the user is typing a command to apply to thread x, but GDB switches to thread y before the user @@ -4383,20 +4419,11 @@ done: /* Delete the breakpoint we stopped at, if it wants to be deleted. Delete any breakpoint that is to be deleted at the next stop. */ breakpoint_auto_delete (inferior_thread ()->stop_bpstat); - - /* Mark the stopped 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. */ - if (!non_stop) - set_running (minus_one_ptid, 0); - else if (last.kind != TARGET_WAITKIND_SIGNALLED - && last.kind != TARGET_WAITKIND_EXITED) - set_running (inferior_ptid, 0); } + /* Tell the frontend about the new thread states. */ + do_cleanups (old_chain); + /* Look up the hook_stop and run it (CLI internally handles problem of stop_command's pre-hook not existing). */ if (stop_command) diff --git a/gdb/thread.c b/gdb/thread.c index 44e4ba2dc49..8a98b8ec4b6 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -632,6 +632,55 @@ set_stop_requested (ptid_t ptid, int stop) observer_notify_thread_stop_requested (ptid); } +void +finish_thread_state (ptid_t ptid) +{ + struct thread_info *tp; + int all; + int any_started = 0; + + all = ptid_equal (ptid, minus_one_ptid); + + if (all || ptid_is_pid (ptid)) + { + for (tp = thread_list; tp; tp = tp->next) + { + if (tp->state_ == THREAD_EXITED) + continue; + if (all || ptid_get_pid (ptid) == ptid_get_pid (tp->ptid)) + { + if (tp->executing_ && tp->state_ == THREAD_STOPPED) + any_started = 1; + tp->state_ = tp->executing_ ? THREAD_RUNNING : THREAD_STOPPED; + } + } + } + else + { + tp = find_thread_pid (ptid); + gdb_assert (tp); + if (tp->state_ != THREAD_EXITED) + { + if (tp->executing_ && tp->state_ == THREAD_STOPPED) + any_started = 1; + tp->state_ = tp->executing_ ? THREAD_RUNNING : THREAD_STOPPED; + } + } + + if (any_started) + observer_notify_target_resumed (ptid); +} + +void +finish_thread_state_cleanup (void *arg) +{ + ptid_t *ptid_p = arg; + + gdb_assert (arg); + + finish_thread_state (*ptid_p); +} + /* Prints the list of threads and their details on UIOUT. This is a version of 'info_thread_command' suitable for use from MI.