2008-06-15 Pedro Alves Split external/internal threads. * thread.c (enum thread_state): New. (thread_state main_thread_running): Delete, in favor of... (thread_state main_thread_state): ... this. Update throughout. (clear_thread_inferior_resources): New, split from free_thread. (free_thread): Call clear_thread_inferior_resources. (init_thread_list): Set main thread to stopped state. Clear the selected thread and frame. (add_thread_silent): Take care of PTID reuses. (delete_thread): If the selected thread is being deleted, clear the selected thread. If deleting inferior_ptid, mark it as exited, and keep it in the list. (iterate_over_threads): Make it safe to delete threads while iterating over them. (do_captured_list_thread_ids): Don't account for exited threads. (thread_alive): Check for the THREAD_EXITED state, and don't set ptid to -1 on exited threads. (set_running): Update to account for extra possible states. (is_thread_state): New. (is_stopped, is_exited): New. (is_running): Implement in terms of is_thread_state. (any_running): Update. (print_thread_info): Don't do thread and frame restoring here. Account for exited threads. Consider user_selected_ptid the selected thread, not inferior_ptid. If there's no selected thread, warn the user. (switch_to_thread): Don't read from a thread that has gone. (struct current_thread_cleanup): Remove the is_running check. (thread_apply_all_command): Don't do thread and frame restoring here. Prune threads. Call execute_command_internal instead of execute_command. (thread_command): If there's no selected thread, say so. (do_captured_thread_select): Check for a running thread. Prune threads. (_initialize_thread): Make "info threads", "thread", "thread apply", and "thread apply all" appliable without a selected thread. * gdbthread.h (struct thread_info): Replace running_ by state_. (is_exited, is_stopped): Declare. * inferior.h (proceed_to_inferior_function_call): Declare. (user_selected_ptid): Declare. (store_selected_thread_and_frame) (restore_selected_thread_and_frame) (select_thread_if_no_thread_selected): Declare. * infrun.c (previous_inferior_ptid): Delete. (proceed_to_inferior_function_call): New. (proceed): Don't set previous_inferior_ptid. (init_wait_for_inferior): Clear selected thread and frame. (fetch_inferior_event): In non-stop mode, restore the user selected frame after handling an event. (user_selected_ptid, user_selected_frame_level, frame_id) (user_selected_frame): New. (store_selected_thread_and_frame) (restore_selected_thread_and_frame) (select_thread_if_no_thread_selected): New. (normal_stop): Store selected thread and frame. In non-stop mode, don't switch the selected thread to the thread that had an event. * top.h (execute_command_internal): Declare. * gdbcmd.h (execute_command_internal): Declare. * top.c (do_restore_selected_thread_and_frame_cleanup): New. (execute_command): Rename to ... (execute_command_1): ... this. Add INTERNAL parameter. If not INTERNAL switch to the user selected thread, before and after running the command. Check for non no-selected-thread-ok commands. (execute_command_internal, execute_command): New as wrappers to execute_command_1. * stack.c (select_frame_command, up_silently_base) (down_silently_base): Record new user selected frame. * infcmd.c (run_command_1): Select thread. (proceed_thread_callback): Check if !is_stopped instead of is_running. Remove thread and frame restoring here. (attach_command_post_wait): Select thread. * infcall.c (call_function_by_hand): Set and restore proceed_to_inferior_function_call. * target.c (generic_mourn_inferior): Clear the selected thread and frame. * corelow.c (core_open): Select thread and frame. * linux-nat.c (struct saved_ptids, threads_to_delete) (record_dead_thread, prune_lwps): Delete. (exit_lwp): Unconditionally delete thread. (linux_nat_resume): Remove prune_lwps call. * linux-fork.c (fork_load_infrun_state): Store selected thread and frame. * cli/cli-decode.h (CMD_NO_SELECTED_THREAD_OK): New. (set_cmd_no_selected_thread_ok, get_cmd_no_selected_thread_ok): Declare. * cli/cli-decode.c (set_cmd_no_selected_thread_ok) (get_cmd_no_selected_thread_ok): New. * cli/cli-cmds.c: Set "pwd", "help", "info", "show" as no-selected-thread ok. * mi/mi-main.c (mi_cmd_target_download, mi_cmd_target_select): Use execute_command_internal instead of execute_command. (do_restore_selected_thread_and_frame_cleanup): New. (mi_cmd_execute): Restore the user selected thread and frame before and after running a command. Only "thread-info", "thread-list-ids", and "thread-select" if there's no selected thread. (mi_execute_cli_command, mi_execute_async_cli_command): Use execute_command_internal instead of execute_command. * mi/mi-cmd-env.c (env_execute_cli_command): Call execute_command_internal instead of execute_command. --- gdb/cli/cli-cmds.c | 7 gdb/cli/cli-decode.c | 12 + gdb/cli/cli-decode.h | 11 + gdb/corelow.c | 1 gdb/gdbcmd.h | 2 gdb/gdbthread.h | 22 ++- gdb/infcall.c | 10 - gdb/infcmd.c | 40 ++--- gdb/inferior.h | 24 +++ gdb/infrun.c | 225 +++++++++++++++++++++++++++++-- gdb/linux-fork.c | 3 gdb/linux-nat.c | 48 ------ gdb/mi/mi-cmd-env.c | 2 gdb/mi/mi-main.c | 50 ++++++- gdb/stack.c | 13 + gdb/target.c | 1 gdb/thread.c | 364 +++++++++++++++++++++++++++++++++------------------ gdb/top.c | 61 +++++++- gdb/top.h | 5 19 files changed, 673 insertions(+), 228 deletions(-) Index: src/gdb/thread.c =================================================================== --- src.orig/gdb/thread.c 2008-06-15 20:56:55.000000000 +0100 +++ src/gdb/thread.c 2008-06-15 20:57:46.000000000 +0100 @@ -65,7 +65,16 @@ static void thread_apply_command (char * static void restore_current_thread (ptid_t); static void prune_threads (void); -static int main_thread_running = 0; +/* Frontend view of the thread state. Possible extensions: stepping, + finishing, until(ling),... */ +enum thread_state +{ + THREAD_STOPPED, + THREAD_RUNNING, + THREAD_EXITED, +}; + +static enum thread_state main_thread_state = 0; static int main_thread_executing = 0; void @@ -86,16 +95,25 @@ delete_step_resume_breakpoint (void *arg } static void -free_thread (struct thread_info *tp) +clear_thread_inferior_resources (struct thread_info *tp) { /* NOTE: this will take care of any left-over step_resume breakpoints, but not any user-specified thread-specific breakpoints. We can not delete the breakpoint straight-off, because the inferior might not be stopped at the moment. */ if (tp->step_resume_breakpoint) - tp->step_resume_breakpoint->disposition = disp_del_at_next_stop; + { + tp->step_resume_breakpoint->disposition = disp_del_at_next_stop; + tp->step_resume_breakpoint = NULL; + } bpstat_clear (&tp->stop_bpstat); +} + +static void +free_thread (struct thread_info *tp) +{ + clear_thread_inferior_resources (tp); /* FIXME: do I ever need to call the back-end to give it a chance at this private data before deleting the thread? */ @@ -111,9 +129,11 @@ init_thread_list (void) struct thread_info *tp, *tpnext; highest_thread_num = 0; - main_thread_running = 0; + main_thread_state = THREAD_STOPPED; main_thread_executing = 0; + store_selected_thread_and_frame (null_ptid, NULL); + if (!thread_list) return; @@ -131,6 +151,46 @@ add_thread_silent (ptid_t ptid) { struct thread_info *tp; + tp = find_thread_pid (ptid); + if (tp) + /* Found an old thread with the same id. It has to be dead, + otherwise we wouldn't see a new thread with the same id. The + OS is reusing this id --- delete it, and recreate a new + one. */ + { + /* In addition to deleting the thread, if this is the current + thread, then we need to also get rid of the current infrun + context, and take care that delete_thread doesn't really + delete the thread if it is inferior_ptid. Create an invalid + thread in the list, context switch to it, delete the original + thread, create a new one, and switch to it. */ + + if (ptid_equal (inferior_ptid, ptid)) + { + tp = xmalloc (sizeof (*tp)); + memset (tp, 0, sizeof (*tp)); + tp->ptid = minus_one_ptid; + tp->num = ++highest_thread_num; + tp->next = thread_list; + thread_list = tp; + context_switch_to (minus_one_ptid); + + /* Now we can delete it. */ + delete_thread (ptid); + + /* Since the context is already set to this new thread, + reset it's ptid, and reswitch inferior_ptid to it. */ + tp->ptid = ptid; + switch_to_thread (ptid); + + /* Done. */ + return tp; + } + else + /* Just go ahead and delete it. */ + delete_thread (ptid); + } + tp = (struct thread_info *) xmalloc (sizeof (*tp)); memset (tp, 0, sizeof (*tp)); tp->ptid = ptid; @@ -163,6 +223,10 @@ add_thread (ptid_t ptid) return add_thread_with_info (ptid, NULL); } +/* Delete thread PTID and notify of thread exit. If this is + inferior_ptid, don't actually delete it, but tag it as exited and + do the notification. If PTID is the user selected thread, clear + it. */ void delete_thread (ptid_t ptid) { @@ -177,12 +241,36 @@ delete_thread (ptid_t ptid) if (!tp) return; + if (ptid_equal (tp->ptid, user_selected_ptid)) + /* User selected thread no longer exists. */ + store_selected_thread_and_frame (null_ptid, NULL); + + if (ptid_equal (tp->ptid, inferior_ptid)) + { + /* Can't delete yet. Mark it as exited, and notify it. */ + if (tp->state_ != THREAD_EXITED) + { + observer_notify_thread_exit (tp); + + /* Tag it as exited. */ + tp->state_ = THREAD_EXITED; + + /* Clear breakpoints, etc. associated with this thread. */ + clear_thread_inferior_resources (tp); + } + + /* Will be really deleted some other time. */ + return; + } + if (tpprev) tpprev->next = tp->next; else thread_list = tp->next; - observer_notify_thread_exit (tp); + /* Notify thread exit, but only if we haven't already. */ + if (tp->state_ != THREAD_EXITED) + observer_notify_thread_exit (tp); free_thread (tp); } @@ -230,11 +318,14 @@ struct thread_info * iterate_over_threads (int (*callback) (struct thread_info *, void *), void *data) { - struct thread_info *tp; + struct thread_info *tp, *next; - for (tp = thread_list; tp; tp = tp->next) - if ((*callback) (tp, data)) - return tp; + for (tp = thread_list; tp; tp = next) + { + next = tp->next; + if ((*callback) (tp, data)) + return tp; + } return NULL; } @@ -301,6 +392,8 @@ do_captured_list_thread_ids (struct ui_o for (tp = thread_list; tp; tp = tp->next) { + if (tp->state_ == THREAD_EXITED) + continue; num++; ui_out_field_int (uiout, "thread-id", tp->num); } @@ -451,13 +544,10 @@ save_infrun_state (ptid_t ptid, static int thread_alive (struct thread_info *tp) { - if (PIDGET (tp->ptid) == -1) + if (tp->state_ == THREAD_EXITED) return 0; if (!target_thread_alive (tp->ptid)) - { - tp->ptid = pid_to_ptid (-1); /* Mark it as dead */ - return 0; - } + return 0; return 1; } @@ -489,9 +579,11 @@ set_running (ptid_t ptid, int running) the main thread is always present in the thread list. If it's not, the first call to context_switch will mess up GDB internal state. */ - if (running && !main_thread_running && !suppress_resume_observer) + if (running + && main_thread_state != THREAD_RUNNING + && !suppress_resume_observer) observer_notify_target_resumed (ptid); - main_thread_running = running; + main_thread_state = running ? THREAD_RUNNING : THREAD_STOPPED; return; } @@ -503,25 +595,31 @@ set_running (ptid_t ptid, int running) int any_started = 0; for (tp = thread_list; tp; tp = tp->next) { - if (running && !tp->running_) - any_started = 1; - tp->running_ = running; + if (tp->state_ == THREAD_EXITED) + continue; + if (running && tp->state_ == THREAD_STOPPED) + any_started = 1; + tp->state_ = running ? THREAD_RUNNING : THREAD_STOPPED; } if (any_started && !suppress_resume_observer) observer_notify_target_resumed (ptid); } else { + int started = 0; tp = find_thread_pid (ptid); gdb_assert (tp); - if (running && !tp->running_ && !suppress_resume_observer) - observer_notify_target_resumed (ptid); - tp->running_ = running; - } + gdb_assert (tp->state_ != THREAD_EXITED); + if (running && tp->state_ == THREAD_STOPPED) + started = 1; + tp->state_ = running ? THREAD_RUNNING : THREAD_STOPPED; + if (started && !suppress_resume_observer) + observer_notify_target_resumed (ptid); + } } -int -is_running (ptid_t ptid) +static int +is_thread_state (ptid_t ptid, enum thread_state state) { struct thread_info *tp; @@ -529,11 +627,41 @@ is_running (ptid_t ptid) return 0; if (!thread_list) - return main_thread_running; + return main_thread_state == state; tp = find_thread_pid (ptid); gdb_assert (tp); - return tp->running_; + return tp->state_ == state; +} + +int +is_stopped (ptid_t ptid) +{ + /* Without execution, this property is always true. */ + if (!target_has_execution) + return 1; + + return is_thread_state (ptid, THREAD_STOPPED); +} + +int +is_exited (ptid_t ptid) +{ + /* Without execution, this property is always false. */ + if (!target_has_execution) + return 0; + + return is_thread_state (ptid, THREAD_EXITED); +} + +int +is_running (ptid_t ptid) +{ + /* Without execution, this property is always false. */ + if (!target_has_execution) + return 0; + + return is_thread_state (ptid, THREAD_RUNNING); } int @@ -545,10 +673,10 @@ any_running (void) return 0; if (!thread_list) - return main_thread_running; + return main_thread_state == THREAD_RUNNING; for (tp = thread_list; tp; tp = tp->next) - if (tp->running_) + if (tp->state_ == THREAD_RUNNING) return 1; return 0; @@ -600,42 +728,36 @@ set_executing (ptid_t ptid, int executin /* Prints the list of threads and their details on UIOUT. This is a version of 'info_thread_command' suitable for use from MI. - If REQESTED_THREAD is not -1, it's the GDB id of the thread + If REQUESTED_THREAD is not -1, it's the GDB id of the thread that should be printed. Otherwise, all threads are printed. */ void print_thread_info (struct ui_out *uiout, int requested_thread) { struct thread_info *tp; - ptid_t current_ptid; - struct frame_info *cur_frame; struct cleanup *old_chain; - struct frame_id saved_frame_id; char *extra_info; int current_thread = -1; - /* Backup current thread and selected frame. */ - if (!is_running (inferior_ptid)) - saved_frame_id = get_frame_id (get_selected_frame (NULL)); - else - saved_frame_id = null_frame_id; - - old_chain = make_cleanup_restore_current_thread (inferior_ptid, saved_frame_id); - make_cleanup_ui_out_list_begin_end (uiout, "threads"); - prune_threads (); target_find_new_threads (); - current_ptid = inferior_ptid; + + old_chain = make_cleanup_ui_out_list_begin_end (uiout, "threads"); + for (tp = thread_list; tp; tp = tp->next) { struct cleanup *chain2; + ptid_t ptid; + + if (tp->state_ == THREAD_EXITED) + continue; if (requested_thread != -1 && tp->num != requested_thread) continue; chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); - if (ptid_equal (tp->ptid, current_ptid)) + if (ptid_equal (tp->ptid, user_selected_ptid)) { current_thread = tp->num; ui_out_text (uiout, "* "); @@ -647,15 +769,19 @@ print_thread_info (struct ui_out *uiout, ui_out_text (uiout, " "); ui_out_field_string (uiout, "target-id", target_tid_to_str (tp->ptid)); - extra_info = target_extra_thread_info (tp); - if (extra_info) + if (tp->state_ != THREAD_EXITED) { - ui_out_text (uiout, " ("); - ui_out_field_string (uiout, "details", extra_info); - ui_out_text (uiout, ")"); + extra_info = target_extra_thread_info (tp); + if (extra_info) + { + ui_out_text (uiout, " ("); + ui_out_field_string (uiout, "details", extra_info); + ui_out_text (uiout, ")"); + } + ui_out_text (uiout, " "); } - ui_out_text (uiout, " "); - if (tp->running_) + + if (tp->state_ == THREAD_RUNNING) ui_out_text (uiout, "(running)\n"); else { @@ -677,24 +803,14 @@ print_thread_info (struct ui_out *uiout, if (requested_thread == -1) { - gdb_assert (current_thread != -1 || !thread_list); + gdb_assert (current_thread != -1 + || !thread_list + || ptid_equal (user_selected_ptid, null_ptid)); if (current_thread != -1 && ui_out_is_mi_like_p (uiout)) ui_out_field_int (uiout, "current-thread-id", current_thread); - } - if (is_running (inferior_ptid)) - return; - - /* If case we were not able to find the original frame, print the - new selected frame. */ - if (frame_find_by_id (saved_frame_id) == NULL) - { - warning (_("Couldn't restore frame in current thread, at frame 0")); - /* For MI, we should probably have a notification about - current frame change. But this error is not very likely, so - don't bother for now. */ - if (!ui_out_is_mi_like_p (uiout)) - print_stack_frame (get_selected_frame (NULL), 0, LOCATION); + if (current_thread == -1 && thread_list) + ui_out_text (uiout, "\nNo selected thread. See `help thread'\n"); } } @@ -724,7 +840,10 @@ switch_to_thread (ptid_t ptid) reinit_frame_cache (); registers_changed (); - if (!is_executing (ptid)) + /* We don't check for is_stopped, because we're called at times + while in the TARGET_RUNNING state, e.g., while handling an + internal event. */ + if (!is_exited (ptid) && !is_executing (ptid)) stop_pc = read_pc (); else stop_pc = ~(CORE_ADDR) 0; @@ -753,6 +872,9 @@ restore_selected_frame (struct frame_id } } +/* Only safe to use this cleanup on a stopped thread, and + inferior_ptid isn't ran before running the cleanup. */ + struct current_thread_cleanup { ptid_t inferior_ptid; @@ -765,11 +887,7 @@ do_restore_current_thread_cleanup (void struct current_thread_cleanup *old = arg; restore_current_thread (old->inferior_ptid); - /* A command like 'thread apply all $exec_command&' may change the - running state of the originally selected thread, so we have to - recheck it here. */ - if (!is_running (old->inferior_ptid)) - restore_selected_frame (old->selected_frame_id); + restore_selected_frame (old->selected_frame_id); xfree (old); } @@ -799,23 +917,11 @@ thread_apply_all_command (char *cmd, int struct thread_info *tp; struct cleanup *old_chain = make_cleanup (null_cleanup, 0); char *saved_cmd; - struct frame_id saved_frame_id; - ptid_t current_ptid; - int thread_has_changed = 0; if (cmd == NULL || *cmd == '\000') error (_("Please specify a command following the thread ID list")); - - current_ptid = inferior_ptid; - if (!is_running (inferior_ptid)) - saved_frame_id = get_frame_id (get_selected_frame (NULL)); - else - saved_frame_id = null_frame_id; - make_cleanup_restore_current_thread (inferior_ptid, saved_frame_id); - - /* It is safe to update the thread list now, before - traversing it for "thread apply all". MVS */ + prune_threads (); target_find_new_threads (); /* Save a copy of the command in case it is clobbered by @@ -832,17 +938,17 @@ thread_apply_all_command (char *cmd, int printf_filtered (_("\nThread %d (%s):\n"), tp->num, target_tid_to_str (inferior_ptid)); - execute_command (cmd, from_tty); + /* execute_command switches to the user selected thread. We + don't want that switch. We want the command to apply to + inferior_ptid instead, hence use the _internal version. We + don't change temporarily the user selected thread and + restore it with a cleanup, because the command may change + it. */ + execute_command_internal (cmd, from_tty); strcpy (cmd, saved_cmd); /* Restore exact command used previously */ } - if (!ptid_equal (current_ptid, inferior_ptid)) - thread_has_changed = 1; - do_cleanups (old_chain); - /* Print stack frame only if we changed thread. */ - if (thread_has_changed && !is_running (inferior_ptid)) - print_stack_frame (get_current_frame (), 1, SRC_LINE); } static void @@ -851,11 +957,7 @@ thread_apply_command (char *tidlist, int char *cmd; char *p; struct cleanup *old_chain; - struct cleanup *saved_cmd_cleanup_chain; char *saved_cmd; - struct frame_id saved_frame_id; - ptid_t current_ptid; - int thread_has_changed = 0; if (tidlist == NULL || *tidlist == '\000') error (_("Please specify a thread ID list")); @@ -865,18 +967,10 @@ thread_apply_command (char *tidlist, int if (*cmd == '\000') error (_("Please specify a command following the thread ID list")); - current_ptid = inferior_ptid; - - if (!is_running (inferior_ptid)) - saved_frame_id = get_frame_id (get_selected_frame (NULL)); - else - saved_frame_id = null_frame_id; - old_chain = make_cleanup_restore_current_thread (inferior_ptid, saved_frame_id); - /* Save a copy of the command in case it is clobbered by execute_command */ saved_cmd = xstrdup (cmd); - saved_cmd_cleanup_chain = make_cleanup (xfree, (void *) saved_cmd); + old_chain = make_cleanup (xfree, saved_cmd); while (tidlist < cmd) { struct thread_info *tp; @@ -920,20 +1014,19 @@ thread_apply_command (char *tidlist, int switch_to_thread (tp->ptid); printf_filtered (_("\nThread %d (%s):\n"), tp->num, target_tid_to_str (inferior_ptid)); - execute_command (cmd, from_tty); + /* execute_command switches to the user selected thread. + We don't want that switch. We want the command to + apply to inferior_ptid instead, hence use the + _internal version. We don't change temporarily the + user selected thread and restore it with a cleanup, + because the command may change it. */ + execute_command_internal (cmd, from_tty); strcpy (cmd, saved_cmd); /* Restore exact command used previously */ } } } - if (!ptid_equal (current_ptid, inferior_ptid)) - thread_has_changed = 1; - - do_cleanups (saved_cmd_cleanup_chain); do_cleanups (old_chain); - /* Print stack frame only if we changed thread. */ - if (thread_has_changed) - print_stack_frame (get_current_frame (), 1, SRC_LINE); } /* Switch to the specified thread. Will dispatch off to thread_apply_command @@ -944,11 +1037,17 @@ thread_command (char *tidstr, int from_t { if (!tidstr) { - /* Don't generate an error, just say which thread is current. */ if (target_has_stack) - printf_filtered (_("[Current thread is %d (%s)]\n"), - pid_to_thread_id (inferior_ptid), - target_tid_to_str (inferior_ptid)); + { + /* Don't generate an error, just say which thread is + current. */ + if (ptid_equal (user_selected_ptid, null_ptid)) + printf_filtered (_("No selected thread.\n")); + else + printf_filtered (_("[Current thread is %d (%s)]\n"), + pid_to_thread_id (user_selected_ptid), + target_tid_to_str (user_selected_ptid)); + } else error (_("No stack.")); return; @@ -996,10 +1095,23 @@ do_captured_thread_select (struct ui_out ui_out_text (uiout, target_tid_to_str (inferior_ptid)); ui_out_text (uiout, ")]"); - if (!tp->running_) - print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC); + /* Note that we can't reach this with an exited thread, due to the + thread_alive check above. */ + if (tp->state_ == THREAD_RUNNING) + { + ui_out_text (uiout, "(running)\n"); + store_selected_thread_and_frame (tp->ptid, NULL); + } else - ui_out_text (uiout, "(running)\n"); + { + struct frame_info *frame = get_selected_frame (NULL); + store_selected_thread_and_frame (tp->ptid, frame); + print_stack_frame (frame, 1, SRC_AND_LOC); + } + + /* Since the current thread may have changed, see if there is any + exited thread we can now delete. */ + prune_threads (); return GDB_RC_OK; } @@ -1025,19 +1137,25 @@ _initialize_thread (void) c = add_info ("threads", info_threads_command, _("IDs of currently known threads.")); set_cmd_async_ok (c); + set_cmd_no_selected_thread_ok (c); c = add_prefix_cmd ("thread", class_run, thread_command, _("\ Use this command to switch between threads.\n\ The new thread ID must be currently known."), &thread_cmd_list, "thread ", 1, &cmdlist); set_cmd_async_ok (c); + set_cmd_no_selected_thread_ok (c); - add_prefix_cmd ("apply", class_run, thread_apply_command, + c = add_prefix_cmd ("apply", class_run, thread_apply_command, _("Apply a command to a list of threads."), &thread_apply_list, "thread apply ", 1, &thread_cmd_list); + set_cmd_async_ok (c); + set_cmd_no_selected_thread_ok (c); - add_cmd ("all", class_run, thread_apply_all_command, - _("Apply a command to all threads."), &thread_apply_list); + c = add_cmd ("all", class_run, thread_apply_all_command, + _("Apply a command to all threads."), &thread_apply_list); + set_cmd_async_ok (c); + set_cmd_no_selected_thread_ok (c); if (!xdb_commands) add_com_alias ("t", "thread", class_run, 1); Index: src/gdb/gdbthread.h =================================================================== --- src.orig/gdb/gdbthread.h 2008-06-15 20:56:55.000000000 +0100 +++ src/gdb/gdbthread.h 2008-06-15 20:57:46.000000000 +0100 @@ -45,15 +45,16 @@ struct thread_info use is_executing instead. */ int executing_; - /* Frontend view of the running state. Note that this is different - from EXECUTING. When the thread is stopped internally while - handling an internal event, like a software single-step - breakpoint, executing will be false, but running will still be - true. As a possible future extension, this could turn into - enum { stopped, stepping, finishing, until(ling), ... } */ + /* Frontend view of the thread state. Note that the RUNNING/STOPPED + states are different from EXECUTING. When the thread is stopped + internally while handling an internal event, like a software + single-step breakpoint, EXECUTING will be false, but running will + still be true. As a possible future extension, this could turn + into enum { stopped, exited, stepping, finishing, until(ling), + running ... } */ /* This field is internal to thread.c. Never access it directly, use is_running instead. */ - int running_; + int state_; /* State from wait_for_inferior */ CORE_ADDR prev_pc; @@ -200,6 +201,13 @@ extern int is_running (ptid_t ptid); /* Reports if any thread is known to be running right now. */ extern int any_running (void); +/* Is this thread listed, but known to have exited? We keep it listed + (but not visible) until it's safe to delete. */ +extern int is_exited (ptid_t ptid); + +/* Is this thread stopped? */ +extern int is_stopped (ptid_t ptid); + /* Marks thread PTID as executing, or as stopped. If PIDGET (PTID) is -1, marks all threads. */ extern void set_executing (ptid_t ptid, int executing); Index: src/gdb/inferior.h =================================================================== --- src.orig/gdb/inferior.h 2008-06-15 20:56:56.000000000 +0100 +++ src/gdb/inferior.h 2008-06-15 20:57:46.000000000 +0100 @@ -388,6 +388,11 @@ extern int proceed_to_finish; extern struct regcache *stop_registers; +/* Nonzero if proceed is being used while calling an inferior + function. */ + +extern int proceed_to_inferior_function_call; + /* Nonzero if the child process in inferior_ptid was attached rather than forked. */ @@ -400,6 +405,25 @@ extern int debug_displaced; void displaced_step_dump_bytes (struct ui_file *file, const gdb_byte *buf, size_t len); +/* This points to the user/external selected thread and frame. In + all-stop mode, a stop event changes the external thread to the + thread that got the event (in normal_stop), while in non-stop mode, + GDB never changes the external thread automatically (although the + internal thread may change). */ +extern ptid_t user_selected_ptid; + +/* Records PTID and FRAME as user/frontend/external selected thread + and frame. */ +extern void store_selected_thread_and_frame (ptid_t ptid, + struct frame_info *frame); + +/* Make the internal thread and frame point to the external thread and + frame. */ +extern void restore_selected_thread_and_frame (void); + +/* If no external thread is selected, make it point to the internal + thread. */ +extern void select_thread_if_no_thread_selected (void); /* When set, normal_stop will not call the normal_stop observer. */ extern int suppress_stop_observer; Index: src/gdb/infrun.c =================================================================== --- src.orig/gdb/infrun.c 2008-06-15 20:56:56.000000000 +0100 +++ src/gdb/infrun.c 2008-06-15 20:57:46.000000000 +0100 @@ -45,6 +45,7 @@ #include "language.h" #include "solib.h" #include "main.h" +#include "interps.h" #include "gdb_assert.h" #include "mi/mi-common.h" @@ -97,12 +98,6 @@ show_step_stop_if_no_debug (struct ui_fi int sync_execution = 0; -/* wait_for_inferior and normal_stop use this to notify the user - when the inferior stopped in a different thread than it had been - running in. */ - -static ptid_t previous_inferior_ptid; - int debug_displaced = 0; static void show_debug_displaced (struct ui_file *file, int from_tty, @@ -275,6 +270,11 @@ int proceed_to_finish; struct regcache *stop_registers; +/* Nonzero if proceed is being used while calling an inferior + function. */ + +int proceed_to_inferior_function_call; + /* Nonzero after stop if current stack frame should be printed. */ static int stop_print_frame; @@ -1301,9 +1301,6 @@ proceed (CORE_ADDR addr, enum target_sig /* Fill in with reasonable starting values. */ init_thread_stepping_state (tss); - /* We'll update this if & when we switch to a new thread. */ - previous_inferior_ptid = inferior_ptid; - /* Reset to normal state. */ init_infwait_state (); @@ -1380,7 +1377,7 @@ init_wait_for_inferior (void) target_last_wait_ptid = minus_one_ptid; init_thread_stepping_state (tss); - previous_inferior_ptid = null_ptid; + store_selected_thread_and_frame (null_ptid, NULL); init_infwait_state (); displaced_step_clear (); @@ -1565,6 +1562,16 @@ fetch_inferior_event (void *client_data) else inferior_event_handler (INF_EXEC_COMPLETE, NULL); } + + /* In non-stop mode, the user/frontend should not notice a thread + switch due to internal events. Revert to the user selected + thread and frame. */ + if (non_stop + && target_has_execution + && ecs->ws.kind != TARGET_WAITKIND_IGNORE + && ecs->ws.kind != TARGET_WAITKIND_EXITED + && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED) + restore_selected_thread_and_frame (); } /* Prepare an execution control state for looping through a @@ -1681,6 +1688,174 @@ context_switch_to (ptid_t ptid) return current_ptid; } +/* These point to the user/external selected thread and frame. In + all-stop mode, a stop event changes the external thread to the + thread that got the event (in normal_stop), while in non-stop mode, + GDB never changes the external thread automatically (although the + internal thread (inferior_ptid) may change). */ + +/* Selected thread/process. If NULL_PTID, there is no selected + thread. */ +ptid_t user_selected_ptid; + +/* Selected frame of the selected thread. We store both frame id and + level. See restore_selected_thread_and_frame. */ +static struct frame_id user_selected_frame; +static int user_selected_frame_level; + +/* Register PTID and FRAME as user selected thread and frame. If PTID + is NULL_PTID, FRAME has to be NULL. */ +void +store_selected_thread_and_frame (ptid_t ptid, struct frame_info *frame) +{ + /* If a frame is passed, we need to be able to build an id from it. + GDB only parses frames of inferior_ptid, and frames don't + registers which thread they belong to. */ + gdb_assert (frame == NULL || ptid_equal (inferior_ptid, ptid)); + + /* If clearing the selected thread, we should clear the frame + too. */ + if (ptid_equal (ptid, null_ptid)) + gdb_assert (frame == NULL); + + user_selected_ptid = ptid; + user_selected_frame = get_frame_id (frame); + user_selected_frame_level = frame_relative_level (frame); + + if (debug_infrun) + { + fprintf_unfiltered (gdb_stdlog, "\ +infrun.c: Storing user selected thread and frame [%s], ", + target_pid_to_str (user_selected_ptid)); + fprint_frame_id (gdb_stdlog, user_selected_frame); + fprintf_unfiltered (gdb_stdlog, ", #%d.\n", + user_selected_frame_level); + } +} + +/* Restore the external selected thread. */ +void +restore_selected_thread_and_frame (void) +{ + if (!target_has_registers || !target_has_stack || !target_has_memory) + return; + + /* Don't switch the internal current thread to the null thread. Too + many places require inferior_ptid to point at a valid process + (even if the thread component is invalid) currently. */ + + if (!ptid_equal (user_selected_ptid, null_ptid)) + { + if (debug_infrun) + { + fprintf_unfiltered (gdb_stdlog, "\ +infrun.c: restoring_selected_thread_and_frame: [%s], ", + target_pid_to_str (user_selected_ptid)); + fprint_frame_id (gdb_stdlog, user_selected_frame); + fprintf_unfiltered (gdb_stdlog, ", #%d\n", + user_selected_frame_level); + } + + if (non_stop) + context_switch_to (user_selected_ptid); + else + switch_to_thread (user_selected_ptid); + + if (is_stopped (user_selected_ptid)) + { + struct frame_info *frame; + int count; + + gdb_assert (user_selected_frame_level >= 0); + + /* Restore by level first, check if the frame id is the same + as expected. If that fails, try restoring by frame id. + If that fails, nothing to do, just warn the user. */ + + count = user_selected_frame_level; + frame = find_relative_frame (get_current_frame (), &count); + if (count == 0 + && frame != NULL + /* Either the frame ids match, of they're both invalid. + The later case is not failsafe, but since it's highly + unlikelly the search by level finds the wrong frame, + it's 99.9(9)% of the times (for all practical + purposes) safe. */ + && (frame_id_eq (get_frame_id (frame), user_selected_frame) + /* Note: could be better to check every frame_id + member for equality here. */ + || (!frame_id_p (get_frame_id (frame)) + && !frame_id_p (user_selected_frame)))) + { + /* Cool, all is fine. */ + select_frame (frame); + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "infrun.c: success (level)\n"); + return; + } + + frame = frame_find_by_id (user_selected_frame); + if (frame != NULL) + { + /* Cool, refound it. */ + select_frame (frame); + + /* The relative level changed, otherwise we would have + found it above. */ + user_selected_frame_level = frame_relative_level (frame); + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "\ +infrun.c: success (id): new level #%d\n", user_selected_frame_level); + return; + } + + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "infrun.c: fail"); + + /* Nothing else to do, the frame layout really changed. + Tell the user. */ + if (!ui_out_is_mi_like_p (interp_ui_out (top_level_interpreter ()))) + { + warning (_("\ +Couldn't restore frame #%d in current thread, at reparsed frame #0\n"), + user_selected_frame_level); + /* For MI, we should probably have a notification about + current frame change. But this error is not very + likely, so don't bother for now. */ + print_stack_frame (frame, 0, LOCATION); + } + + frame = get_selected_frame (NULL); + user_selected_frame = get_frame_id (frame); + user_selected_frame_level = frame_relative_level (frame); + + if (debug_infrun) + { + fprintf_unfiltered (gdb_stdlog, "\ +infrun.c: user selected frame changed to "); + fprint_frame_id (gdb_stdlog, user_selected_frame); + fprintf_unfiltered (gdb_stdlog, ", #%d.\n", + user_selected_frame_level); + } + } + } +} + +void +select_thread_if_no_thread_selected (void) +{ + if (ptid_equal (user_selected_ptid, null_ptid) + && !ptid_equal (inferior_ptid, null_ptid)) + { + if (is_stopped (inferior_ptid)) + store_selected_thread_and_frame (inferior_ptid, + get_selected_frame (NULL)); + else + store_selected_thread_and_frame (inferior_ptid, + NULL); + } +} + static void adjust_pc_after_break (struct execution_control_state *ecs) { @@ -2199,7 +2374,7 @@ targets should add new threads to the th switch_to_thread (deferred_step_ptid); deferred_step_ptid = null_ptid; /* Suppress spurious "Switching to ..." message. */ - previous_inferior_ptid = inferior_ptid; + store_selected_thread_and_frame (inferior_ptid, NULL); resume (1, TARGET_SIGNAL_0); prepare_to_wait (ecs); @@ -3701,6 +3876,11 @@ normal_stop (void) get_last_target_status (&last_ptid, &last); + /* In non-stop mode, we don't want GDB to switch threads on the + users 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 + finishes entering the command. */ + /* As with the notification of thread events, we want to delay notifying the user that we've switched thread context until the inferior actually stops. @@ -3708,7 +3888,8 @@ normal_stop (void) There's no point in saying anything if the inferior has exited. Note that SIGNALLED here means "exited with a signal", not "received a signal". */ - if (!ptid_equal (previous_inferior_ptid, inferior_ptid) + if (!non_stop + && !ptid_equal (user_selected_ptid, inferior_ptid) && target_has_execution && last.kind != TARGET_WAITKIND_SIGNALLED && last.kind != TARGET_WAITKIND_EXITED) @@ -3717,7 +3898,6 @@ normal_stop (void) printf_filtered (_("[Switching to %s]\n"), target_pid_to_str (inferior_ptid)); annotate_thread_changed (); - previous_inferior_ptid = inferior_ptid; } /* NOTE drow/2004-01-17: Is this still necessary? */ @@ -3889,15 +4069,28 @@ done: Delete any breakpoint that is to be deleted at the next stop. */ breakpoint_auto_delete (stop_bpstat); - if (target_has_execution - && last.kind != TARGET_WAITKIND_SIGNALLED - && last.kind != TARGET_WAITKIND_EXITED) + if (target_has_execution) { + if ((!stop_stack_dummy || !proceed_to_inferior_function_call) + && (!non_stop + || ptid_equal (user_selected_ptid, inferior_ptid))) + /* Selected thread just stopped. Record the selected frame. + In all-stop, the thread that has the event is always made + the current. Don't do this on return from a stack dummy + routine, unless we're no longer in call_function_by_hand + evaluating an expression --- if we are, then this is + considered an internal event, and we should not change the + user selected frame. */ + store_selected_thread_and_frame (inferior_ptid, + get_selected_frame (NULL)); if (!non_stop) set_running (pid_to_ptid (-1), 0); else set_running (inferior_ptid, 0); } + else + /* execution ended; Clear the selected thread and frame. */ + store_selected_thread_and_frame (null_ptid, NULL); } static int Index: src/gdb/cli/cli-decode.c =================================================================== --- src.orig/gdb/cli/cli-decode.c 2008-06-15 20:56:56.000000000 +0100 +++ src/gdb/cli/cli-decode.c 2008-06-15 20:57:46.000000000 +0100 @@ -117,6 +117,18 @@ get_cmd_async_ok (struct cmd_list_elemen return cmd->flags & CMD_ASYNC_OK; } +void +set_cmd_no_selected_thread_ok (struct cmd_list_element *cmd) +{ + cmd->flags |= CMD_NO_SELECTED_THREAD_OK; +} + +int +get_cmd_no_selected_thread_ok (struct cmd_list_element *cmd) +{ + return cmd->flags & CMD_NO_SELECTED_THREAD_OK; +} + enum cmd_types cmd_type (struct cmd_list_element *cmd) { Index: src/gdb/cli/cli-decode.h =================================================================== --- src.orig/gdb/cli/cli-decode.h 2008-06-15 20:56:56.000000000 +0100 +++ src/gdb/cli/cli-decode.h 2008-06-15 20:57:46.000000000 +0100 @@ -51,6 +51,10 @@ cmd_types; /* This flag is set if the command is allowed during async execution. */ #define CMD_ASYNC_OK 0x8 +/* This flag is set if the command is allowed to run when the target + has execution, but there's no selected thread. */ +#define CMD_NO_SELECTED_THREAD_OK 0x10 + struct cmd_list_element { /* Points to next command in this list. */ @@ -253,6 +257,13 @@ extern void set_cmd_async_ok (struct cmd /* Return true if command is async-ok. */ extern int get_cmd_async_ok (struct cmd_list_element *); +/* Mark command as ok to call when there is no selected thread. There + is no way to disable this once set. */ +extern void set_cmd_no_selected_thread_ok (struct cmd_list_element *); + +/* Return true if command is no-selected-thread-ok. */ +extern int get_cmd_no_selected_thread_ok (struct cmd_list_element *); + extern struct cmd_list_element *lookup_cmd (char **, struct cmd_list_element *, char *, int, int); Index: src/gdb/top.c =================================================================== --- src.orig/gdb/top.c 2008-06-15 20:56:56.000000000 +0100 +++ src/gdb/top.c 2008-06-15 20:57:46.000000000 +0100 @@ -359,11 +359,17 @@ do_chdir_cleanup (void *old_dir) } #endif +static void +do_restore_selected_thread_and_frame_cleanup (void *arg) +{ + restore_selected_thread_and_frame (); +} + /* Execute the line P as a command. Pass FROM_TTY as second argument to the defining function. */ void -execute_command (char *p, int from_tty) +execute_command_1 (char *p, int from_tty, int internal) { struct cmd_list_element *c; enum language flang; @@ -407,6 +413,7 @@ execute_command (char *p, int from_tty) p++; if (*p) { + struct cleanup *old_chain = make_cleanup (null_cleanup, NULL); char *arg; line = p; @@ -415,13 +422,35 @@ execute_command (char *p, int from_tty) c = lookup_cmd (&p, cmdlist, "", 0, 1); - /* If the target is running, we allow only a limited set of - commands. */ + if (!internal) + { + /* Command applies to the external thread. Switch the + internal thread to it before invoking the command, so the + command implementation sees the correct context. */ + restore_selected_thread_and_frame (); + /* Switch to the external thread again after invoking the + command, so if for some reason restoring the selected + frame fails, the user notices immediatelly. Note that + the command may change the selected thread. */ + make_cleanup (do_restore_selected_thread_and_frame_cleanup, NULL); + } + + /* If there is no selected thread, we allow only a limited set + of commands. */ if (target_can_async_p () && target_has_execution - && ((!non_stop && any_running ()) - || (non_stop && is_running (inferior_ptid))) - && !get_cmd_async_ok (c)) + && (!internal + && ptid_equal (user_selected_ptid, null_ptid)) + && !get_cmd_no_selected_thread_ok (c)) + error (_("\ +Cannot execute this command without a selected thread. See `help thread'")); + /* If the target is running, we allow only a limited set of + commands. */ + else if (target_can_async_p () + && target_has_execution + && ((!non_stop && any_running ()) + || (non_stop && is_running (inferior_ptid))) + && !get_cmd_async_ok (c)) error (_("Cannot execute this command while the target is running.")); /* Pass null arg rather than an empty one. */ @@ -467,6 +496,8 @@ execute_command (char *p, int from_tty) /* If this command has been post-hooked, run the hook last. */ execute_cmd_post_hook (c); + /* Restore user selected thread and frame. */ + do_cleanups (old_chain); } /* Tell the user if the language has changed (except first time). */ @@ -498,6 +529,24 @@ execute_command (char *p, int from_tty) } } +/* Execute command P, and use the already selected internal thread + (inferior_ptid) as current context. Use in situations like the + "thread apply" command. */ + +void +execute_command_internal (char *p, int from_tty) +{ + execute_command_1 (p, from_tty, 1); +} + +/* Execute command P, with the user/frontend/external thread as + context. */ +void +execute_command (char *p, int from_tty) +{ + execute_command_1 (p, from_tty, 0); +} + /* Read commands from `instream' and execute them until end of file or error reading instream. */ Index: src/gdb/linux-nat.c =================================================================== --- src.orig/gdb/linux-nat.c 2008-06-15 20:57:27.000000000 +0100 +++ src/gdb/linux-nat.c 2008-06-15 20:57:46.000000000 +0100 @@ -977,48 +977,12 @@ linux_nat_switch_fork (ptid_t new_ptid) { struct lwp_info *lp; - init_thread_list (); init_lwp_list (); lp = add_lwp (new_ptid); - add_thread_silent (new_ptid); lp->stopped = 1; -} - -/* Record a PTID for later deletion. */ - -struct saved_ptids -{ - ptid_t ptid; - struct saved_ptids *next; -}; -static struct saved_ptids *threads_to_delete; - -static void -record_dead_thread (ptid_t ptid) -{ - struct saved_ptids *p = xmalloc (sizeof (struct saved_ptids)); - p->ptid = ptid; - p->next = threads_to_delete; - threads_to_delete = p; -} -/* Delete any dead threads which are not the current thread. */ - -static void -prune_lwps (void) -{ - struct saved_ptids **p = &threads_to_delete; - - while (*p) - if (! ptid_equal ((*p)->ptid, inferior_ptid)) - { - struct saved_ptids *tmp = *p; - delete_thread (tmp->ptid); - *p = tmp->next; - xfree (tmp); - } - else - p = &(*p)->next; + init_thread_list (); + add_thread_silent (new_ptid); } /* Handle the exit of a single thread LP. */ @@ -1033,11 +997,7 @@ exit_lwp (struct lwp_info *lp) if (print_thread_events) printf_unfiltered (_("[%s exited]\n"), target_pid_to_str (lp->ptid)); - /* Core GDB cannot deal with us deleting the current thread. */ - if (!ptid_equal (lp->ptid, inferior_ptid)) - delete_thread (lp->ptid); - else - record_dead_thread (lp->ptid); + delete_thread (lp->ptid); } delete_lwp (lp->ptid); @@ -1542,8 +1502,6 @@ linux_nat_resume (ptid_t ptid, int step, signo ? strsignal (signo) : "0", target_pid_to_str (inferior_ptid)); - prune_lwps (); - if (target_can_async_p ()) /* Block events while we're here. */ linux_nat_async_events (0); Index: src/gdb/stack.c =================================================================== --- src.orig/gdb/stack.c 2008-06-15 20:56:55.000000000 +0100 +++ src/gdb/stack.c 2008-06-15 20:57:46.000000000 +0100 @@ -1697,6 +1697,11 @@ void select_frame_command (char *level_exp, int from_tty) { select_frame (parse_frame_specification_1 (level_exp, "No stack.", NULL)); + + /* Record user selected frame. */ + if (ptid_equal (inferior_ptid, user_selected_ptid)) + store_selected_thread_and_frame (inferior_ptid, + get_selected_frame (NULL)); } /* The "frame" command. With no argument, print the selected frame @@ -1734,6 +1739,10 @@ up_silently_base (char *count_exp) if (count != 0 && count_exp == NULL) error (_("Initial frame selected; you cannot go up.")); select_frame (frame); + + /* Record user selected frame. */ + if (ptid_equal (inferior_ptid, user_selected_ptid)) + store_selected_thread_and_frame (inferior_ptid, frame); } static void @@ -1772,6 +1781,10 @@ down_silently_base (char *count_exp) } select_frame (frame); + + /* Record user selected frame. */ + if (ptid_equal (inferior_ptid, user_selected_ptid)) + store_selected_thread_and_frame (inferior_ptid, frame); } static void Index: src/gdb/infcmd.c =================================================================== --- src.orig/gdb/infcmd.c 2008-06-15 20:57:45.000000000 +0100 +++ src/gdb/infcmd.c 2008-06-15 20:57:46.000000000 +0100 @@ -569,6 +569,11 @@ run_command_1 (char *args, int from_tty, target_create_inferior (exec_file, get_inferior_args (), environ_vector (inferior_environ), from_tty); + /* If no selected thread, select the current one. */ + if (ptid_equal (user_selected_ptid, null_ptid)) + /* Going to proceed, no need to record a frame. */ + store_selected_thread_and_frame (inferior_ptid, NULL); + /* 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); @@ -611,7 +616,7 @@ start_command (char *args, int from_tty) static int proceed_thread_callback (struct thread_info *thread, void *arg) { - if (is_running (thread->ptid)) + if (!is_stopped (thread->ptid)) return 0; context_switch_to (thread->ptid); @@ -695,25 +700,9 @@ Can't resume all threads and specify pro printf_filtered (_("Continuing.\n")); if (non_stop && all_threads) - { - struct cleanup *old_chain; - struct frame_id saved_frame_id; - - /* Don't error out if the current thread is running, because - there may be other stopped threads. */ - - /* Backup current thread and selected frame. */ - if (!is_running (inferior_ptid)) - saved_frame_id = get_frame_id (get_selected_frame (NULL)); - else - saved_frame_id = null_frame_id; - - old_chain = make_cleanup_restore_current_thread (inferior_ptid, saved_frame_id); - iterate_over_threads (proceed_thread_callback, NULL); - - /* Restore selected ptid. */ - do_cleanups (old_chain); - } + /* Don't error out if the current thread is running, because + there may be other stopped threads. */ + iterate_over_threads (proceed_thread_callback, NULL); else { ensure_not_running (); @@ -1980,12 +1969,21 @@ attach_command_post_wait (char *args, in target_terminal_inferior (); if (async_exec) - proceed ((CORE_ADDR) -1, TARGET_SIGNAL_0, 0); + { + /* If no selected thread, select the current one. */ + if (ptid_equal (user_selected_ptid, null_ptid)) + /* Going to proceed, no need to record a frame. */ + store_selected_thread_and_frame (inferior_ptid, NULL); + proceed ((CORE_ADDR) -1, TARGET_SIGNAL_0, 0); + } else { if (target_can_async_p ()) async_enable_stdin (); normal_stop (); + + select_thread_if_no_thread_selected (); + if (deprecated_attach_hook) deprecated_attach_hook (); } Index: src/gdb/top.h =================================================================== --- src.orig/gdb/top.h 2008-06-15 20:56:56.000000000 +0100 +++ src/gdb/top.h 2008-06-15 20:57:46.000000000 +0100 @@ -47,8 +47,13 @@ extern int quit_confirm (void); extern void quit_force (char *, int); extern void quit_command (char *, int); extern int quit_cover (void *); + +/* Execute command, after changing to the user selected thread. */ extern void execute_command (char *, int); +/* Execute command, without changing to the user selected thread. */ +extern void execute_command_internal (char *, int); + /* This function returns a pointer to the string that is used by gdb for its command prompt. */ extern char *get_prompt (void); Index: src/gdb/corelow.c =================================================================== --- src.orig/gdb/corelow.c 2008-06-15 20:56:56.000000000 +0100 +++ src/gdb/corelow.c 2008-06-15 20:57:46.000000000 +0100 @@ -376,6 +376,7 @@ core_open (char *filename, int from_tty) /* Now, set up the frame cache, and print the top of stack. */ reinit_frame_cache (); print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC); + store_selected_thread_and_frame (inferior_ptid, get_selected_frame (NULL)); } else { Index: src/gdb/gdbcmd.h =================================================================== --- src.orig/gdb/gdbcmd.h 2008-06-15 20:56:55.000000000 +0100 +++ src/gdb/gdbcmd.h 2008-06-15 20:57:46.000000000 +0100 @@ -122,6 +122,8 @@ extern struct cmd_list_element *showchec extern void execute_command (char *, int); +extern void execute_command_internal (char *, int); + enum command_control_type execute_control_command (struct command_line *); extern void print_command_line (struct command_line *, unsigned int, Index: src/gdb/cli/cli-cmds.c =================================================================== --- src.orig/gdb/cli/cli-cmds.c 2008-06-15 20:56:56.000000000 +0100 +++ src/gdb/cli/cli-cmds.c 2008-06-15 20:57:46.000000000 +0100 @@ -1245,6 +1245,8 @@ The commands below can be used to select c = add_com ("pwd", class_files, pwd_command, _("\ Print working directory. This is used for your program as well.")); set_cmd_async_ok (c); + set_cmd_no_selected_thread_ok (c); + c = add_cmd ("cd", class_files, cd_command, _("\ Set working directory to DIR for debugger and program being debugged.\n\ The change does not take effect for the program being debugged\n\ @@ -1280,11 +1282,12 @@ when GDB is started."), gdbinit); source_help_text, &cmdlist); set_cmd_completer (c, filename_completer); - add_com ("quit", class_support, quit_command, _("Exit gdb.")); + c = add_com ("quit", class_support, quit_command, _("Exit gdb.")); c = add_com ("help", class_support, help_command, _("Print list of commands.")); set_cmd_completer (c, command_completer); set_cmd_async_ok (c); + set_cmd_no_selected_thread_ok (c); add_com_alias ("q", "quit", class_support, 1); add_com_alias ("h", "help", class_support, 1); @@ -1314,6 +1317,7 @@ Without an argument, history expansion i Generic command for showing things about the program being debugged."), &infolist, "info ", 0, &cmdlist); set_cmd_async_ok (c); + set_cmd_no_selected_thread_ok (c); add_com_alias ("i", "info", class_info, 1); add_com ("complete", class_obscure, complete_command, @@ -1323,6 +1327,7 @@ Generic command for showing things about Generic command for showing things about the debugger."), &showlist, "show ", 0, &cmdlist); set_cmd_async_ok (c); + set_cmd_no_selected_thread_ok (c); /* Another way to get at the same thing. */ add_info ("set", show_command, _("Show all GDB settings.")); Index: src/gdb/linux-fork.c =================================================================== --- src.orig/gdb/linux-fork.c 2008-06-15 20:56:56.000000000 +0100 +++ src/gdb/linux-fork.c 2008-06-15 20:57:46.000000000 +0100 @@ -248,6 +248,9 @@ fork_load_infrun_state (struct fork_info registers_changed (); reinit_frame_cache (); + store_selected_thread_and_frame (inferior_ptid, + get_selected_frame (NULL)); + stop_pc = read_pc (); nullify_last_target_wait_ptid (); Index: src/gdb/mi/mi-main.c =================================================================== --- src.orig/gdb/mi/mi-main.c 2008-06-15 20:56:55.000000000 +0100 +++ src/gdb/mi/mi-main.c 2008-06-15 20:57:46.000000000 +0100 @@ -647,7 +647,7 @@ mi_cmd_target_download (char *command, c file to download. */ run = xstrprintf ("load %s", argc ? *argv : ""); old_cleanups = make_cleanup (xfree, run); - execute_command (run, 0); + execute_command_internal (run, 0); do_cleanups (old_cleanups); return MI_CMD_DONE; @@ -683,7 +683,7 @@ mi_cmd_target_select (char *command, cha /* NOTE: At present all targets that are connected are also (implicitly) talking to a halted target. In the future this may change. */ - execute_command (run, 0); + execute_command_internal (run, 0); do_cleanups (old_cleanups); @@ -1186,15 +1186,50 @@ mi_execute_command (char *cmd, int from_ /* ..... */ } + +static void +do_restore_selected_thread_and_frame_cleanup (void *arg) +{ + restore_selected_thread_and_frame (); +} + static enum mi_cmd_result mi_cmd_execute (struct mi_parse *parse) { - struct cleanup *cleanup; + struct cleanup *cleanup = make_cleanup (null_cleanup, NULL); enum mi_cmd_result r; free_all_values (); + /* Switch to the frontend selected thread, before invoking the + command, so the command implementation sees the correct + context. */ + restore_selected_thread_and_frame (); + /* Switch to the external thread again after invoking the command, + so if for some reason restoring the selected frame fails, the + frontend notices immediatelly. Note that the command may change + the selected thread. */ + make_cleanup (do_restore_selected_thread_and_frame_cleanup, NULL); + if (parse->cmd->argv_func != NULL) { + if (target_can_async_p () + && target_has_execution + && (ptid_equal (user_selected_ptid, null_ptid)) + && (strcmp (parse->command, "thread-info") != 0 + && strcmp (parse->command, "thread-list-ids") != 0 + && strcmp (parse->command, "thread-select") != 0)) + { + struct ui_file *stb; + stb = mem_fileopen (); + + fputs_unfiltered ("Cannot execute command ", stb); + fputstr_unfiltered (parse->command, '"', stb); + fputs_unfiltered (" without a selected thread", stb); + + make_cleanup_ui_file_delete (stb); + error_stream (stb); + } + if ((!non_stop && any_running ()) || (non_stop && is_running (inferior_ptid))) { @@ -1265,7 +1300,12 @@ mi_execute_cli_command (const char *cmd, fprintf_unfiltered (gdb_stdout, "cli=%s run=%s\n", cmd, run); old_cleanups = make_cleanup (xfree, run); - execute_command ( /*ui */ run, 0 /*from_tty */ ); + /* execute_command switches to the user selected thread. We + don't want that switch. We want the command to apply to + inferior_ptid instead, hence use the _internal version. We + don't change temporarily the user selected thread and restore + it with a cleanup, because the command may change it. */ + execute_command_internal ( /*ui */ run, 0 /*from_tty */ ); do_cleanups (old_cleanups); return; } @@ -1306,7 +1346,7 @@ mi_execute_async_cli_command (char *cli_ } - execute_command ( /*ui */ run, 0 /*from_tty */ ); + execute_command_internal ( /*ui */ run, 0 /*from_tty */ ); if (target_can_async_p ()) { Index: src/gdb/infcall.c =================================================================== --- src.orig/gdb/infcall.c 2008-06-15 20:56:55.000000000 +0100 +++ src/gdb/infcall.c 2008-06-15 20:57:46.000000000 +0100 @@ -724,14 +724,18 @@ call_function_by_hand (struct value *fun suppress_resume_observer = 1; make_cleanup_restore_integer (&suppress_stop_observer); suppress_stop_observer = 1; + make_cleanup_restore_integer (&proceed_to_inferior_function_call); + proceed_to_inferior_function_call = 1; /* Don't change the user + selected frame in + normal_stop. */ proceed (real_pc, TARGET_SIGNAL_0, 0); do_cleanups (old_cleanups2); - + if (saved_async) target_async_mask (saved_async); - + enable_watchpoints_after_interactive_call_stop (); - + discard_cleanups (old_cleanups); } Index: src/gdb/target.c =================================================================== --- src.orig/gdb/target.c 2008-06-15 20:56:56.000000000 +0100 +++ src/gdb/target.c 2008-06-15 20:57:46.000000000 +0100 @@ -2238,6 +2238,7 @@ generic_mourn_inferior (void) extern int show_breakpoint_hit_counts; inferior_ptid = null_ptid; + store_selected_thread_and_frame (null_ptid, NULL); attach_flag = 0; breakpoint_init_inferior (inf_exited); registers_changed (); Index: src/gdb/mi/mi-cmd-env.c =================================================================== --- src.orig/gdb/mi/mi-cmd-env.c 2008-06-15 20:56:55.000000000 +0100 +++ src/gdb/mi/mi-cmd-env.c 2008-06-15 20:57:46.000000000 +0100 @@ -56,7 +56,7 @@ env_execute_cli_command (const char *cmd else run = xstrdup (cmd); old_cleanups = make_cleanup (xfree, run); - execute_command ( /*ui */ run, 0 /*from_tty */ ); + execute_command_internal ( /*ui */ run, 0 /*from_tty */ ); do_cleanups (old_cleanups); return; }