Exited 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.
	(add_thread_silent): Take care of PTID reuses.
	(delete_thread): If deleting inferior_ptid or a thread with
	refcount > 0, mark it as exited, but still keep it in the list.
	Only notify of thread exits, if we haven't done so yet.
	(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): Update.  Account for exited threads.  Don't
	warn about missed frame restoring here, its done in the cleanup.
	(switch_to_thread): Don't read from a thread that has gone.
	(restore_current_thread): In non-stop mode, do a full context
	switch.
	(restore_selected_frame): Add a frame_level argument.  Rewrite.
	(struct current_thread_cleanup): Add selected_frame_level and
	was_stopped members.
	(do_restore_current_thread_cleanup): Check if thread was stopped
	and still is, and if the target has registers, stack and memory
	before restoring the selected frame.  Don't delete the cleanup
	argument here.
	(restore_current_thread_cleanup_dtor): New.
	(make_cleanup_restore_current_thread): Remove all arguments.
	Rewrite.
	(thread_apply_all_command): Update.  Prune threads.
	(thread_apply_command): Update.
	(thread_command): Account for currently selected exited thread.
	(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_.
	Add refcount.
	(is_exited, is_stopped): Declare.
	(make_cleanup_restore_current_thread): Remove all arguments.
	* infrun.c: Include "event-top.h".
	(fetch_inferior_event): In non-stop mode, restore selected thread
	and frame after handling the event and running breakpoint
	commands.  Display GDB prompt if needed.
	(normal_stop): In non-stop mode, don't print thread switching
	notice.
	* cli/cli-decode.c (set_cmd_no_selected_thread_ok)
	(get_cmd_no_selected_thread_ok): New.
	* 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-cmds.c: Set "pwd", "help", "info", "show" as
	no-selected-thread ok.
	* top.c (execute_command): Check for non no-selected-thread-ok
	commands.
	* 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.
	* infcmd.c (proceed_thread_callback): Check if !is_stopped instead
	of is_running.  Adjust to make_cleanup_restore_current_thread
	interface change.
	* mi/mi-main.c (mi_cmd_execute): Only allow a few commands if the
	selected thread has exited.
	* inf-loop.c (inferior_event_handler): Don't display the prompt
	here.
	* varobj.c (c_value_of_root): Update.
	* defs.h (make_cleanup_dtor): Declare.
	* utils.c (make_cleanup_dtor): New.

	* Makefile.in (infrun.o): Depend on $(event_top_h).
This commit is contained in:
Pedro Alves 2008-07-11 11:07:39 +00:00
parent 8cae4b3f0b
commit 4f8d22e3b4
16 changed files with 536 additions and 226 deletions

View file

@ -1,3 +1,85 @@
2008-07-11 Pedro Alves <pedro@codesourcery.com>
Exited 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.
(add_thread_silent): Take care of PTID reuses.
(delete_thread): If deleting inferior_ptid or a thread with
refcount > 0, mark it as exited, but still keep it in the list.
Only notify of thread exits, if we haven't done so yet.
(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): Update. Account for exited threads. Don't
warn about missed frame restoring here, its done in the cleanup.
(switch_to_thread): Don't read from a thread that has gone.
(restore_current_thread): In non-stop mode, do a full context
switch.
(restore_selected_frame): Add a frame_level argument. Rewrite.
(struct current_thread_cleanup): Add selected_frame_level and
was_stopped members.
(do_restore_current_thread_cleanup): Check if thread was stopped
and still is, and if the target has registers, stack and memory
before restoring the selected frame. Don't delete the cleanup
argument here.
(restore_current_thread_cleanup_dtor): New.
(make_cleanup_restore_current_thread): Remove all arguments.
Rewrite.
(thread_apply_all_command): Update. Prune threads.
(thread_apply_command): Update.
(thread_command): Account for currently selected exited thread.
(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_.
Add refcount.
(is_exited, is_stopped): Declare.
(make_cleanup_restore_current_thread): Remove all arguments.
* infrun.c: Include "event-top.h".
(fetch_inferior_event): In non-stop mode, restore selected thread
and frame after handling the event and running breakpoint
commands. Display GDB prompt if needed.
(normal_stop): In non-stop mode, don't print thread switching
notice.
* cli/cli-decode.c (set_cmd_no_selected_thread_ok)
(get_cmd_no_selected_thread_ok): New.
* 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-cmds.c: Set "pwd", "help", "info", "show" as
no-selected-thread ok.
* top.c (execute_command): Check for non no-selected-thread-ok
commands.
* 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.
* infcmd.c (proceed_thread_callback): Check if !is_stopped instead
of is_running. Adjust to make_cleanup_restore_current_thread
interface change.
* mi/mi-main.c (mi_cmd_execute): Only allow a few commands if the
selected thread has exited.
* inf-loop.c (inferior_event_handler): Don't display the prompt
here.
* varobj.c (c_value_of_root): Update.
* defs.h (make_cleanup_dtor): Declare.
* utils.c (make_cleanup_dtor): New.
* Makefile.in (infrun.o): Depend on $(event_top_h).
2008-07-11 Pedro Alves <pedro@codesourcery.com>
Add "continue -a" and "interrupt -a" options for non-stop mode.

View file

@ -2335,7 +2335,7 @@ infrun.o: infrun.c $(defs_h) $(gdb_string_h) $(symtab_h) $(frame_h) \
$(gdbcore_h) $(gdbcmd_h) $(cli_script_h) $(target_h) $(gdbthread_h) \
$(annotate_h) $(symfile_h) $(top_h) $(inf_loop_h) $(regcache_h) \
$(value_h) $(observer_h) $(language_h) $(solib_h) $(gdb_assert_h) \
$(mi_common_h) $(main_h)
$(mi_common_h) $(main_h) $(event_top_h)
inf-ttrace.o: inf-ttrace.c $(defs_h) $(command_h) $(gdbcore_h) \
$(gdbthread_h) $(inferior_h) $(target_h) \
$(gdb_assert_h) $(gdb_string_h) $(inf_child_h) $(inf_ttrace_h)

View file

@ -1245,6 +1245,8 @@ The commands below can be used to select other frames by number or address."),
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\
@ -1285,6 +1287,7 @@ when GDB is started."), gdbinit);
_("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 is enabled."),
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 the program being debugged."),
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."));

View file

@ -117,6 +117,18 @@ get_cmd_async_ok (struct cmd_list_element *cmd)
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)
{

View file

@ -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_list_element *);
/* 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);

View file

@ -333,6 +333,9 @@ typedef void (make_cleanup_ftype) (void *);
extern struct cleanup *make_cleanup (make_cleanup_ftype *, void *);
extern struct cleanup *make_cleanup_dtor (make_cleanup_ftype *, void *,
void (*dtor) (void *));
extern struct cleanup *make_cleanup_freeargv (char **);
struct ui_file;

View file

@ -45,15 +45,21 @@ 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_;
/* If this is > 0, then it means there's code out there that relies
on this thread being listed. Don't delete it from the lists even
if we detect it exiting. */
int refcount;
/* State from wait_for_inferior */
CORE_ADDR prev_pc;
@ -207,6 +213,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);
@ -223,8 +236,7 @@ extern int print_thread_events;
extern void print_thread_info (struct ui_out *uiout, int thread);
extern struct cleanup *make_cleanup_restore_current_thread (ptid_t,
struct frame_id);
extern struct cleanup *make_cleanup_restore_current_thread (void);
#endif /* GDBTHREAD_H */

View file

@ -116,20 +116,8 @@ inferior_event_handler (enum inferior_event_type event_type,
bpstat_do_actions (&stop_bpstat);
}
/* If no breakpoint command resumed the inferior, prepare for
interaction with the user. */
if (!is_running (inferior_ptid))
{
if (was_sync)
{
display_gdb_prompt (0);
}
else
{
if (exec_done_display_p)
printf_unfiltered (_("completed.\n"));
}
}
if (!was_sync && !is_running (inferior_ptid) && exec_done_display_p)
printf_unfiltered (_("completed.\n"));
break;
case INF_EXEC_CONTINUE:

View file

@ -611,7 +611,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);
@ -696,19 +696,13 @@ Can't resume all threads and specify proceed count simultaneously."));
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. */
struct cleanup *old_chain;
/* 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 ();
old_chain = make_cleanup_restore_current_thread (inferior_ptid, saved_frame_id);
iterate_over_threads (proceed_thread_callback, NULL);
/* Restore selected ptid. */

View file

@ -48,6 +48,7 @@
#include "gdb_assert.h"
#include "mi/mi-common.h"
#include "event-top.h"
/* Prototypes for local functions */
@ -1530,11 +1531,20 @@ 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);
int was_sync = sync_execution;
memset (ecs, 0, sizeof (*ecs));
overlay_cache_invalid = 1;
if (non_stop)
/* In non-stop mode, the user/frontend should not notice a thread
switch due to internal events. Make sure we reverse to the
user selected thread and frame after handling the event and
running any breakpoint commands. */
make_cleanup_restore_current_thread ();
/* We have to invalidate the registers BEFORE calling target_wait
because they can be loaded from the target while in target_wait.
This makes remote debugging a bit more efficient for those
@ -1571,6 +1581,14 @@ fetch_inferior_event (void *client_data)
else
inferior_event_handler (INF_EXEC_COMPLETE, NULL);
}
/* Revert thread and frame. */
do_cleanups (old_chain);
/* If the inferior was in sync execution mode, and now isn't,
restore the prompt. */
if (was_sync && !sync_execution)
display_gdb_prompt (0);
}
/* Prepare an execution control state for looping through a
@ -3709,6 +3727,11 @@ normal_stop (void)
get_last_target_status (&last_ptid, &last);
/* 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
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.
@ -3716,7 +3739,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 (previous_inferior_ptid, inferior_ptid)
&& target_has_execution
&& last.kind != TARGET_WAITKIND_SIGNALLED
&& last.kind != TARGET_WAITKIND_EXITED)

View file

@ -1077,48 +1077,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. */
@ -1133,11 +1097,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);
@ -1675,8 +1635,6 @@ linux_nat_resume (ptid_t ptid, int step, enum target_signal signo)
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 (sigchld_sync);

View file

@ -1059,6 +1059,24 @@ mi_cmd_execute (struct mi_parse *parse)
if (parse->cmd->argv_func != NULL)
{
if (target_can_async_p ()
&& target_has_execution
&& (is_exited (inferior_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)))
{

View file

@ -41,7 +41,6 @@
#include "ui-out.h"
#include "observer.h"
#include "annotate.h"
#include "cli/cli-decode.h"
/* Definition of struct thread_info exported to gdbthread.h */
@ -65,7 +64,16 @@ static void thread_apply_command (char *, int);
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 = THREAD_STOPPED;
static int main_thread_executing = 0;
void
@ -86,16 +94,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,7 +128,7 @@ 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;
if (!thread_list)
@ -131,6 +148,49 @@ 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 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 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 a new
template thread in the list with an invalid ptid, context
switch to it, delete the original thread, reset the new
thread's ptid, 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 its ptid, and reswitch inferior_ptid to it. */
tp->ptid = ptid;
switch_to_thread (ptid);
observer_notify_new_thread (tp);
/* All 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;
@ -179,17 +239,44 @@ delete_thread_1 (ptid_t ptid, int silent)
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)
{
if (!silent)
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;
if (!silent)
/* Notify thread exit, but only if we haven't already. */
if (!silent && tp->state_ != THREAD_EXITED)
observer_notify_thread_exit (tp);
free_thread (tp);
}
/* 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)
{
@ -245,11 +332,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;
}
@ -328,6 +418,8 @@ 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;
num++;
ui_out_field_int (uiout, "thread-id", tp->num);
}
@ -478,13 +570,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;
}
@ -516,9 +605,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;
}
@ -530,25 +621,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;
@ -556,11 +653,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
@ -572,10 +699,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;
@ -627,7 +754,7 @@ set_executing (ptid_t ptid, int executing)
/* 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
@ -635,24 +762,18 @@ 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;
/* 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;
@ -660,13 +781,16 @@ print_thread_info (struct ui_out *uiout, int requested_thread)
if (requested_thread != -1 && tp->num != requested_thread)
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, " ");
@ -674,15 +798,19 @@ print_thread_info (struct ui_out *uiout, int requested_thread)
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
{
@ -704,24 +832,15 @@ print_thread_info (struct ui_out *uiout, int requested_thread)
if (requested_thread == -1)
{
gdb_assert (current_thread != -1 || !thread_list);
gdb_assert (current_thread != -1
|| !thread_list);
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 && is_exited (current_ptid))
ui_out_message (uiout, 0, "\n\
The current thread <Thread ID %d> has terminated. See `help thread'.\n",
current_thread);
}
}
@ -751,7 +870,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;
@ -762,21 +884,63 @@ restore_current_thread (ptid_t ptid)
{
if (!ptid_equal (ptid, inferior_ptid))
{
switch_to_thread (ptid);
if (non_stop)
context_switch_to (ptid);
else
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;
if (frame_id_eq (a_frame_id, null_frame_id))
return;
gdb_assert (frame_level >= 0);
if ((selected_frame_info = frame_find_by_id (a_frame_id)) != NULL)
/* 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))))
{
select_frame (selected_frame_info);
/* 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;
}
/* Nothing else to do, the frame layout really changed.
Tell the user. */
if (!ui_out_is_mi_like_p (uiout))
{
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 (frame, 1, SRC_LINE);
}
}
@ -784,31 +948,66 @@ 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);
/* 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);
/* The running state of the originally selected thread may have
changed, so we have to recheck it here. */
if (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_pid (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);
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_pid (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
@ -824,27 +1023,17 @@ static void
thread_apply_all_command (char *cmd, int from_tty)
{
struct thread_info *tp;
struct cleanup *old_chain = make_cleanup (null_cleanup, 0);
struct cleanup *old_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;
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 ();
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);
@ -863,13 +1052,7 @@ thread_apply_all_command (char *cmd, int 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
@ -878,11 +1061,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"));
@ -892,18 +1071,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;
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;
@ -941,26 +1112,24 @@ thread_apply_command (char *tidlist, int from_tty)
warning (_("Thread %d has terminated."), start);
else
{
make_cleanup_restore_current_thread ();
if (non_stop)
context_switch_to (tp->ptid);
else
switch_to_thread (tp->ptid);
printf_filtered (_("\nThread %d (%s):\n"), tp->num,
target_tid_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
@ -971,11 +1140,17 @@ thread_command (char *tidstr, int from_tty)
{
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));
{
if (is_exited (inferior_ptid))
printf_filtered (_("[Current thread is %d (%s) (exited)]\n"),
pid_to_thread_id (inferior_ptid),
target_tid_to_str (inferior_ptid));
else
printf_filtered (_("[Current thread is %d (%s)]\n"),
pid_to_thread_id (inferior_ptid),
target_tid_to_str (inferior_ptid));
}
else
error (_("No stack."));
return;
@ -1023,10 +1198,16 @@ do_captured_thread_select (struct ui_out *uiout, void *tidstr)
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);
else
/* 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;
}
@ -1052,19 +1233,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,
_("Apply a command to a list of threads."),
&thread_apply_list, "thread apply ", 1, &thread_cmd_list);
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);

View file

@ -362,6 +362,8 @@ do_chdir_cleanup (void *old_dir)
/* Execute the line P as a command.
Pass FROM_TTY as second argument to the defining function. */
/* Execute command P, in the current user context. */
void
execute_command (char *p, int from_tty)
{
@ -415,12 +417,19 @@ 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 the selected thread has terminated, we allow only a
limited set of commands. */
if (target_can_async_p ()
&& ((!non_stop && any_running ())
|| (non_stop && is_running (inferior_ptid)))
&& !get_cmd_async_ok (c))
&& is_exited (inferior_ptid)
&& !get_cmd_no_selected_thread_ok (c))
error (_("\
Cannot execute this command without a live selected thread. See `help thread'."));
/* If the target is running, we allow only a limited set of
commands. */
else if (target_can_async_p ()
&& ((!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. */

View file

@ -207,6 +207,14 @@ make_cleanup (make_cleanup_ftype *function, void *arg)
return make_my_cleanup (&cleanup_chain, function, arg);
}
struct cleanup *
make_cleanup_dtor (make_cleanup_ftype *function, void *arg,
void (*dtor) (void *))
{
return make_my_cleanup2 (&cleanup_chain,
function, arg, dtor);
}
struct cleanup *
make_final_cleanup (make_cleanup_ftype *function, void *arg)
{

View file

@ -2198,8 +2198,7 @@ c_value_of_root (struct varobj **var_handle)
/* Not a root var */
return NULL;
back_to = make_cleanup_restore_current_thread (
inferior_ptid, get_frame_id (deprecated_safe_get_selected_frame ()));
back_to = make_cleanup_restore_current_thread ();
/* Determine whether the variable is still around. */
if (var->root->valid_block == NULL || var->root->floating)