X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;ds=sidebyside;f=gdb%2Fthread.c;h=536177130c6e009cd7717dc62e3c30419b7e76d2;hb=d80b854b33baf5ebc3940cd5928dc06c8708750d;hp=773ba5c19994468a860b74557bef6d4b0b2bd024;hpb=d4fc5b1ea748aa8f06b41b582dcf178e9d8af580;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/thread.c b/gdb/thread.c index 773ba5c199..536177130c 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -1,7 +1,8 @@ /* Multi-process/thread control for GDB, the GNU debugger. Copyright (C) 1986, 1987, 1988, 1993, 1994, 1995, 1996, 1997, 1998, 1999, - 2000, 2001, 2002, 2003, 2004, 2007, 2008 Free Software Foundation, Inc. + 2000, 2001, 2002, 2003, 2004, 2007, 2008, 2009 + Free Software Foundation, Inc. Contributed by Lynx Real-Time Systems, Inc. Los Gatos, CA. @@ -41,6 +42,7 @@ #include "ui-out.h" #include "observer.h" #include "annotate.h" +#include "cli/cli-decode.h" /* Definition of struct thread_info exported to gdbthread.h */ @@ -53,8 +55,6 @@ void _initialize_thread (void); static struct thread_info *thread_list = NULL; static int highest_thread_num; -static struct thread_info *find_thread_id (int num); - static void thread_command (char *tidstr, int from_tty); static void thread_apply_all_command (char *, int); static int thread_alive (struct thread_info *); @@ -63,32 +63,56 @@ static void thread_apply_command (char *, int); static void restore_current_thread (ptid_t); static void prune_threads (void); -void -delete_step_resume_breakpoint (void *arg) +/* Frontend view of the thread state. Possible extensions: stepping, + finishing, until(ling),... */ +enum thread_state { - struct breakpoint **breakpointp = (struct breakpoint **) arg; - struct thread_info *tp; + THREAD_STOPPED, + THREAD_RUNNING, + THREAD_EXITED, +}; - if (*breakpointp != NULL) - { - delete_breakpoint (*breakpointp); - for (tp = thread_list; tp; tp = tp->next) - if (tp->step_resume_breakpoint == *breakpointp) - tp->step_resume_breakpoint = NULL; +struct thread_info* +inferior_thread (void) +{ + struct thread_info *tp = find_thread_ptid (inferior_ptid); + gdb_assert (tp); + return tp; +} - *breakpointp = NULL; +void +delete_step_resume_breakpoint (struct thread_info *tp) +{ + if (tp && tp->step_resume_breakpoint) + { + delete_breakpoint (tp->step_resume_breakpoint); + tp->step_resume_breakpoint = NULL; } } 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); + + discard_all_intermediate_continuations_thread (tp); + discard_all_continuations_thread (tp); +} + +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? */ @@ -104,6 +128,7 @@ init_thread_list (void) struct thread_info *tp, *tpnext; highest_thread_num = 0; + if (!thread_list) return; @@ -116,18 +141,74 @@ init_thread_list (void) thread_list = NULL; } -struct thread_info * -add_thread_silent (ptid_t ptid) +/* Allocate a new thread with target id PTID and add it to the thread + list. */ + +static struct thread_info * +new_thread (ptid_t ptid) { struct thread_info *tp; - tp = (struct thread_info *) xmalloc (sizeof (*tp)); - memset (tp, 0, sizeof (*tp)); + tp = xcalloc (1, sizeof (*tp)); + tp->ptid = ptid; tp->num = ++highest_thread_num; tp->next = thread_list; thread_list = tp; + /* Nothing to follow yet. */ + tp->pending_follow.kind = TARGET_WAITKIND_SPURIOUS; + tp->state_ = THREAD_STOPPED; + + return tp; +} + +struct thread_info * +add_thread_silent (ptid_t ptid) +{ + struct thread_info *tp; + + tp = find_thread_ptid (ptid); + if (tp) + /* Found an old thread with the same id. It has to be dead, + otherwise we wouldn't be adding 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 take care that delete_thread doesn't + really delete the thread if it is inferior_ptid. Create a + new template thread in the list with an invalid ptid, switch + to it, delete the original thread, reset the new thread's + ptid, and switch to it. */ + + if (ptid_equal (inferior_ptid, ptid)) + { + tp = new_thread (ptid); + + /* Make switch_to_thread not read from the thread. */ + tp->state_ = THREAD_EXITED; + switch_to_thread (minus_one_ptid); + + /* Now we can delete it. */ + delete_thread (ptid); + + /* Now reset its ptid, and reswitch inferior_ptid to it. */ + tp->ptid = ptid; + tp->state_ = THREAD_STOPPED; + switch_to_thread (ptid); + + observer_notify_new_thread (tp); + + /* All done. */ + return tp; + } + else + /* Just go ahead and delete it. */ + delete_thread (ptid); + } + + tp = new_thread (ptid); observer_notify_new_thread (tp); return tp; @@ -153,8 +234,10 @@ add_thread (ptid_t ptid) return add_thread_with_info (ptid, NULL); } -void -delete_thread (ptid_t ptid) +/* Delete thread PTID. If SILENT, don't notify the observer of this + exit. */ +static void +delete_thread_1 (ptid_t ptid, int silent) { struct thread_info *tp, *tpprev; @@ -167,17 +250,56 @@ delete_thread (ptid_t ptid) if (!tp) return; + /* If this is the current thread, or there's code out there that + relies on it existing (refcount > 0) we can't delete yet. Mark + it as exited, and notify it. */ + if (tp->refcount > 0 + || ptid_equal (tp->ptid, inferior_ptid)) + { + if (tp->state_ != THREAD_EXITED) + { + observer_notify_thread_exit (tp, silent); + + /* 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, silent); free_thread (tp); } -static struct thread_info * +/* 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) +{ + delete_thread_1 (ptid, 0 /* not silent */); +} + +void +delete_thread_silent (ptid_t ptid) +{ + delete_thread_1 (ptid, 1 /* silent */); +} + +struct thread_info * find_thread_id (int num) { struct thread_info *tp; @@ -191,7 +313,7 @@ find_thread_id (int num) /* Find a thread_info by matching PTID. */ struct thread_info * -find_thread_pid (ptid_t ptid) +find_thread_ptid (ptid_t ptid) { struct thread_info *tp; @@ -220,13 +342,28 @@ struct thread_info * iterate_over_threads (int (*callback) (struct thread_info *, void *), void *data) { + struct thread_info *tp, *next; + + for (tp = thread_list; tp; tp = next) + { + next = tp->next; + if ((*callback) (tp, data)) + return tp; + } + + return NULL; +} + +int +thread_count (void) +{ + int result = 0; struct thread_info *tp; for (tp = thread_list; tp; tp = tp->next) - if ((*callback) (tp, data)) - return tp; + ++result; - return NULL; + return result; } int @@ -275,6 +412,22 @@ in_thread_list (ptid_t ptid) return 0; /* Never heard of 'im */ } +/* Finds the first thread of the inferior given by PID. If PID is -1, + return the first thread in the list. */ + +struct thread_info * +first_thread_of_process (int pid) +{ + struct thread_info *tp, *ret = NULL; + + for (tp = thread_list; tp; tp = tp->next) + if (pid == -1 || ptid_get_pid (tp->ptid) == pid) + if (ret == NULL || tp->num < ret->num) + ret = tp; + + return ret; +} + /* Print a list of thread ids currently known, and the total number of threads. To be used from within catch_errors. */ static int @@ -283,6 +436,7 @@ do_captured_list_thread_ids (struct ui_out *uiout, void *arg) struct thread_info *tp; int num = 0; struct cleanup *cleanup_chain; + int current_thread = -1; prune_threads (); target_find_new_threads (); @@ -291,11 +445,20 @@ do_captured_list_thread_ids (struct ui_out *uiout, void *arg) for (tp = thread_list; tp; tp = tp->next) { + if (tp->state_ == THREAD_EXITED) + continue; + + if (ptid_equal (tp->ptid, inferior_ptid)) + current_thread = tp->num; + num++; ui_out_field_int (uiout, "thread-id", tp->num); } do_cleanups (cleanup_chain); + + if (current_thread != -1) + ui_out_field_int (uiout, "current-thread-id", current_thread); ui_out_field_int (uiout, "number-of-threads", num); return GDB_RC_OK; } @@ -311,135 +474,258 @@ gdb_list_thread_ids (struct ui_out *uiout, char **error_message) return GDB_RC_OK; } -/* Load infrun state for the thread PID. */ +/* Return true if TP is an active thread. */ +static int +thread_alive (struct thread_info *tp) +{ + if (tp->state_ == THREAD_EXITED) + return 0; + if (!target_thread_alive (tp->ptid)) + return 0; + return 1; +} + +static void +prune_threads (void) +{ + struct thread_info *tp, *next; + + for (tp = thread_list; tp; tp = next) + { + next = tp->next; + if (!thread_alive (tp)) + delete_thread (tp->ptid); + } +} void -load_infrun_state (ptid_t ptid, - CORE_ADDR *prev_pc, - int *trap_expected, - struct breakpoint **step_resume_breakpoint, - CORE_ADDR *step_range_start, - CORE_ADDR *step_range_end, - struct frame_id *step_frame_id, - int *stepping_over_breakpoint, - int *stepping_through_solib_after_catch, - bpstat *stepping_through_solib_catchpoints, - int *current_line, - struct symtab **current_symtab) +thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid) { + struct inferior *inf; struct thread_info *tp; - /* If we can't find the thread, then we're debugging a single threaded - process. No need to do anything in that case. */ - tp = find_thread_id (pid_to_thread_id (ptid)); - if (tp == NULL) - return; + /* It can happen that what we knew as the target inferior id + changes. E.g, target remote may only discover the remote process + pid after adding the inferior to GDB's list. */ + inf = find_inferior_pid (ptid_get_pid (old_ptid)); + inf->pid = ptid_get_pid (new_ptid); - *prev_pc = tp->prev_pc; - *trap_expected = tp->trap_expected; - *step_resume_breakpoint = tp->step_resume_breakpoint; - *step_range_start = tp->step_range_start; - *step_range_end = tp->step_range_end; - *step_frame_id = tp->step_frame_id; - *stepping_over_breakpoint = tp->stepping_over_breakpoint; - *stepping_through_solib_after_catch = - tp->stepping_through_solib_after_catch; - *stepping_through_solib_catchpoints = - tp->stepping_through_solib_catchpoints; - *current_line = tp->current_line; - *current_symtab = tp->current_symtab; -} + tp = find_thread_ptid (old_ptid); + tp->ptid = new_ptid; -/* Save infrun state for the thread PID. */ + observer_notify_thread_ptid_changed (old_ptid, new_ptid); +} void -save_infrun_state (ptid_t ptid, - CORE_ADDR prev_pc, - int trap_expected, - struct breakpoint *step_resume_breakpoint, - CORE_ADDR step_range_start, - CORE_ADDR step_range_end, - const struct frame_id *step_frame_id, - int stepping_over_breakpoint, - int stepping_through_solib_after_catch, - bpstat stepping_through_solib_catchpoints, - int current_line, - struct symtab *current_symtab) +set_running (ptid_t ptid, int running) { struct thread_info *tp; + int all = ptid_equal (ptid, minus_one_ptid); - /* If we can't find the thread, then we're debugging a single-threaded - process. Nothing to do in that case. */ - tp = find_thread_id (pid_to_thread_id (ptid)); - if (tp == NULL) - return; - - tp->prev_pc = prev_pc; - tp->trap_expected = trap_expected; - tp->step_resume_breakpoint = step_resume_breakpoint; - tp->step_range_start = step_range_start; - tp->step_range_end = step_range_end; - tp->step_frame_id = (*step_frame_id); - tp->stepping_over_breakpoint = stepping_over_breakpoint; - tp->stepping_through_solib_after_catch = stepping_through_solib_after_catch; - tp->stepping_through_solib_catchpoints = stepping_through_solib_catchpoints; - tp->current_line = current_line; - tp->current_symtab = current_symtab; + /* We try not to notify the observer if no thread has actually changed + the running state -- merely to reduce the number of messages to + frontend. Frontend is supposed to handle multiple *running just fine. */ + if (all || ptid_is_pid (ptid)) + { + int any_started = 0; + for (tp = thread_list; tp; tp = tp->next) + if (all || ptid_get_pid (tp->ptid) == ptid_get_pid (ptid)) + { + 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) + observer_notify_target_resumed (ptid); + } + else + { + int started = 0; + tp = find_thread_ptid (ptid); + gdb_assert (tp); + gdb_assert (tp->state_ != THREAD_EXITED); + if (running && tp->state_ == THREAD_STOPPED) + started = 1; + tp->state_ = running ? THREAD_RUNNING : THREAD_STOPPED; + if (started) + observer_notify_target_resumed (ptid); + } } -/* Return true if TP is an active thread. */ static int -thread_alive (struct thread_info *tp) +is_thread_state (ptid_t ptid, enum thread_state state) { - if (PIDGET (tp->ptid) == -1) - return 0; - if (!target_thread_alive (tp->ptid)) + struct thread_info *tp; + + tp = find_thread_ptid (ptid); + gdb_assert (tp); + return tp->state_ == state; +} + +int +is_stopped (ptid_t ptid) +{ + return is_thread_state (ptid, THREAD_STOPPED); +} + +int +is_exited (ptid_t ptid) +{ + return is_thread_state (ptid, THREAD_EXITED); +} + +int +is_running (ptid_t ptid) +{ + return is_thread_state (ptid, THREAD_RUNNING); +} + +int +any_running (void) +{ + struct thread_info *tp; + + for (tp = thread_list; tp; tp = tp->next) + if (tp->state_ == THREAD_RUNNING) + return 1; + + return 0; +} + +int +is_executing (ptid_t ptid) +{ + struct thread_info *tp; + + tp = find_thread_ptid (ptid); + gdb_assert (tp); + return tp->executing_; +} + +void +set_executing (ptid_t ptid, int executing) +{ + struct thread_info *tp; + int all = ptid_equal (ptid, minus_one_ptid); + + if (all || ptid_is_pid (ptid)) { - tp->ptid = pid_to_ptid (-1); /* Mark it as dead */ - return 0; + for (tp = thread_list; tp; tp = tp->next) + if (all || ptid_get_pid (tp->ptid) == ptid_get_pid (ptid)) + tp->executing_ = executing; + } + else + { + tp = find_thread_ptid (ptid); + gdb_assert (tp); + tp->executing_ = executing; } - return 1; } -static void -prune_threads (void) +void +set_stop_requested (ptid_t ptid, int stop) { - struct thread_info *tp, *next; + struct thread_info *tp; + int all = ptid_equal (ptid, minus_one_ptid); - for (tp = thread_list; tp; tp = next) + if (all || ptid_is_pid (ptid)) { - next = tp->next; - if (!thread_alive (tp)) - delete_thread (tp->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_ptid (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); +} + +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_ptid (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. - 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. */ + printed. + If PID is not -1, only print threads from the process PID. + Otherwise, threads from all attached PIDs are printed. + If both REQUESTED_THREAD and PID are not -1, then the thread + is printed if it belongs to the specified process. Otherwise, + an error is raised. */ void -print_thread_info (struct ui_out *uiout, int requested_thread) +print_thread_info (struct ui_out *uiout, int requested_thread, int pid) { 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. */ - saved_frame_id = get_frame_id (get_selected_frame (NULL)); - 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; + + /* We'll be switching threads temporarily. */ + old_chain = make_cleanup_restore_current_thread (); + + make_cleanup_ui_out_list_begin_end (uiout, "threads"); for (tp = thread_list; tp; tp = tp->next) { struct cleanup *chain2; @@ -447,19 +733,29 @@ print_thread_info (struct ui_out *uiout, int requested_thread) if (requested_thread != -1 && tp->num != requested_thread) continue; + if (pid != -1 && PIDGET (tp->ptid) != pid) + { + if (requested_thread != -1) + error (_("Requested thread not found in requested process")); + continue; + } + + if (ptid_equal (tp->ptid, current_ptid)) + current_thread = tp->num; + + if (tp->state_ == THREAD_EXITED) + continue; + chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL); if (ptid_equal (tp->ptid, current_ptid)) - { - current_thread = tp->num; - ui_out_text (uiout, "* "); - } + ui_out_text (uiout, "* "); else ui_out_text (uiout, " "); ui_out_field_int (uiout, "id", tp->num); ui_out_text (uiout, " "); - ui_out_field_string (uiout, "target-id", target_tid_to_str (tp->ptid)); + ui_out_field_string (uiout, "target-id", target_pid_to_str (tp->ptid)); extra_info = target_extra_thread_info (tp); if (extra_info) @@ -469,12 +765,27 @@ print_thread_info (struct ui_out *uiout, int requested_thread) ui_out_text (uiout, ")"); } ui_out_text (uiout, " "); - /* That switch put us at the top of the stack (leaf frame). */ - switch_to_thread (tp->ptid); - print_stack_frame (get_selected_frame (NULL), - /* For MI output, print frame level. */ - ui_out_is_mi_like_p (uiout), - LOCATION); + + if (tp->state_ == THREAD_RUNNING) + ui_out_text (uiout, "(running)\n"); + else + { + /* The switch below puts us at the top of the stack (leaf + frame). */ + switch_to_thread (tp->ptid); + print_stack_frame (get_selected_frame (NULL), + /* For MI output, print frame level. */ + ui_out_is_mi_like_p (uiout), + LOCATION); + } + + if (ui_out_is_mi_like_p (uiout)) + { + char *state = "stopped"; + if (tp->state_ == THREAD_RUNNING) + state = "running"; + ui_out_field_string (uiout, "state", state); + } do_cleanups (chain2); } @@ -483,23 +794,23 @@ print_thread_info (struct ui_out *uiout, int requested_thread) the "info threads" command. */ do_cleanups (old_chain); - if (requested_thread == -1) + if (pid == -1 && requested_thread == -1) { - gdb_assert (current_thread != -1 || !thread_list); + gdb_assert (current_thread != -1 + || !thread_list + || ptid_equal (inferior_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 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 && is_exited (current_ptid)) + ui_out_message (uiout, 0, "\n\ +The current thread has terminated. See `help thread'.\n", + current_thread); + else if (thread_list + && current_thread == -1 + && ptid_equal (current_ptid, null_ptid)) + ui_out_message (uiout, 0, "\n\ +No selected thread. See `help thread'.\n"); } } @@ -514,7 +825,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread) static void info_threads_command (char *arg, int from_tty) { - print_thread_info (uiout, -1); + print_thread_info (uiout, -1, -1); } /* Switch from one thread to another. */ @@ -528,29 +839,77 @@ switch_to_thread (ptid_t ptid) inferior_ptid = ptid; reinit_frame_cache (); registers_changed (); - stop_pc = read_pc (); + + /* 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 (!ptid_equal (inferior_ptid, null_ptid) + && !is_exited (ptid) + && !is_executing (ptid)) + stop_pc = regcache_read_pc (get_thread_regcache (ptid)); + else + stop_pc = ~(CORE_ADDR) 0; } static void restore_current_thread (ptid_t ptid) { - if (!ptid_equal (ptid, inferior_ptid)) - { - switch_to_thread (ptid); - } + switch_to_thread (ptid); } static void -restore_selected_frame (struct frame_id a_frame_id) +restore_selected_frame (struct frame_id a_frame_id, int frame_level) { - struct frame_info *selected_frame_info = NULL; + struct frame_info *frame = NULL; + int count; + + gdb_assert (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 = 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 + latter case is not failsafe, but since it's highly unlikely + the search by level finds the wrong frame, it's 99.9(9)% of + the time (for all practical purposes) safe. */ + && (frame_id_eq (get_frame_id (frame), a_frame_id) + /* Note: could be better to check every frame_id + member for equality here. */ + || (!frame_id_p (get_frame_id (frame)) + && !frame_id_p (a_frame_id)))) + { + /* Cool, all is fine. */ + select_frame (frame); + return; + } + + frame = frame_find_by_id (a_frame_id); + if (frame != NULL) + { + /* Cool, refound it. */ + select_frame (frame); + return; + } - if (frame_id_eq (a_frame_id, null_frame_id)) - return; + /* Nothing else to do, the frame layout really changed. Select the + innermost stack frame. */ + select_frame (get_current_frame ()); - if ((selected_frame_info = frame_find_by_id (a_frame_id)) != NULL) + /* Warn the user. */ + if (!ui_out_is_mi_like_p (uiout)) { - select_frame (selected_frame_info); + warning (_("\ +Couldn't restore frame #%d in current thread, at reparsed frame #0\n"), + 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 (get_selected_frame (NULL), 1, SRC_LINE); } } @@ -558,26 +917,82 @@ struct current_thread_cleanup { ptid_t inferior_ptid; struct frame_id selected_frame_id; + int selected_frame_level; + int was_stopped; }; static void do_restore_current_thread_cleanup (void *arg) { + struct thread_info *tp; struct current_thread_cleanup *old = arg; - restore_current_thread (old->inferior_ptid); - restore_selected_frame (old->selected_frame_id); + + tp = find_thread_ptid (old->inferior_ptid); + + /* If the previously selected thread belonged to a process that has + in the mean time been deleted (due to normal exit, detach, etc.), + then don't revert back to it, but instead simply drop back to no + thread selected. */ + if (tp + && find_inferior_pid (ptid_get_pid (tp->ptid)) != NULL) + restore_current_thread (old->inferior_ptid); + else + restore_current_thread (null_ptid); + + /* The running state of the originally selected thread may have + changed, so we have to recheck it here. */ + if (!ptid_equal (inferior_ptid, null_ptid) + && old->was_stopped + && is_stopped (inferior_ptid) + && target_has_registers + && target_has_stack + && target_has_memory) + restore_selected_frame (old->selected_frame_id, + old->selected_frame_level); +} + +static void +restore_current_thread_cleanup_dtor (void *arg) +{ + struct current_thread_cleanup *old = arg; + struct thread_info *tp; + tp = find_thread_ptid (old->inferior_ptid); + if (tp) + tp->refcount--; xfree (old); } struct cleanup * -make_cleanup_restore_current_thread (ptid_t inferior_ptid, - struct frame_id a_frame_id) +make_cleanup_restore_current_thread (void) { - struct current_thread_cleanup *old - = xmalloc (sizeof (struct current_thread_cleanup)); + struct thread_info *tp; + struct frame_info *frame; + struct current_thread_cleanup *old; + + old = xmalloc (sizeof (struct current_thread_cleanup)); old->inferior_ptid = inferior_ptid; - old->selected_frame_id = a_frame_id; - return make_cleanup (do_restore_current_thread_cleanup, old); + + if (!ptid_equal (inferior_ptid, null_ptid)) + { + old->was_stopped = is_stopped (inferior_ptid); + if (old->was_stopped + && target_has_registers + && target_has_stack + && target_has_memory) + frame = get_selected_frame (NULL); + else + frame = NULL; + + old->selected_frame_id = get_frame_id (frame); + old->selected_frame_level = frame_relative_level (frame); + + tp = find_thread_ptid (inferior_ptid); + if (tp) + tp->refcount++; + } + + return make_cleanup_dtor (do_restore_current_thread_cleanup, old, + restore_current_thread_cleanup_dtor); } /* Apply a GDB command to a list of threads. List syntax is a whitespace @@ -594,46 +1009,32 @@ thread_apply_all_command (char *cmd, int from_tty) { struct thread_info *tp; 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 (cmd == NULL || *cmd == '\000') error (_("Please specify a command following the thread ID list")); - - current_ptid = inferior_ptid; - saved_frame_id = get_frame_id (get_selected_frame (NULL)); - old_chain = 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 (); + old_chain = make_cleanup_restore_current_thread (); + /* 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); + make_cleanup (xfree, saved_cmd); for (tp = thread_list; tp; tp = tp->next) if (thread_alive (tp)) { switch_to_thread (tp->ptid); + printf_filtered (_("\nThread %d (%s):\n"), - tp->num, target_tid_to_str (inferior_ptid)); + tp->num, target_pid_to_str (inferior_ptid)); execute_command (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); - } static void @@ -642,11 +1043,7 @@ thread_apply_command (char *tidlist, int from_tty) 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")); @@ -656,14 +1053,10 @@ thread_apply_command (char *tidlist, int from_tty) if (*cmd == '\000') error (_("Please specify a command following the thread ID list")); - current_ptid = inferior_ptid; - saved_frame_id = get_frame_id (get_selected_frame (NULL)); - 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; @@ -691,6 +1084,8 @@ thread_apply_command (char *tidlist, int from_tty) else end = start; + make_cleanup_restore_current_thread (); + for (; start <= end; start++) { tp = find_thread_id (start); @@ -702,22 +1097,18 @@ thread_apply_command (char *tidlist, int from_tty) else { switch_to_thread (tp->ptid); + printf_filtered (_("\nThread %d (%s):\n"), tp->num, - target_tid_to_str (inferior_ptid)); + target_pid_to_str (inferior_ptid)); execute_command (cmd, from_tty); - strcpy (cmd, saved_cmd); /* Restore exact command used previously */ + + /* Restore exact command used previously. */ + strcpy (cmd, saved_cmd); } } } - 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 @@ -728,11 +1119,20 @@ thread_command (char *tidstr, int from_tty) { if (!tidstr) { - /* Don't generate an error, just say which thread is current. */ + if (ptid_equal (inferior_ptid, null_ptid)) + error (_("No thread selected")); + if (target_has_stack) - printf_filtered (_("[Current thread is %d (%s)]\n"), - pid_to_thread_id (inferior_ptid), - target_tid_to_str (inferior_ptid)); + { + if (is_exited (inferior_ptid)) + printf_filtered (_("[Current thread is %d (%s) (exited)]\n"), + pid_to_thread_id (inferior_ptid), + target_pid_to_str (inferior_ptid)); + else + printf_filtered (_("[Current thread is %d (%s)]\n"), + pid_to_thread_id (inferior_ptid), + target_pid_to_str (inferior_ptid)); + } else error (_("No stack.")); return; @@ -770,13 +1170,25 @@ do_captured_thread_select (struct ui_out *uiout, void *tidstr) switch_to_thread (tp->ptid); + annotate_thread_changed (); + ui_out_text (uiout, "[Switching to thread "); ui_out_field_int (uiout, "new-thread-id", pid_to_thread_id (inferior_ptid)); ui_out_text (uiout, " ("); - ui_out_text (uiout, target_tid_to_str (inferior_ptid)); + ui_out_text (uiout, target_pid_to_str (inferior_ptid)); ui_out_text (uiout, ")]"); - 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"); + else + print_stack_frame (get_selected_frame (NULL), 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; }