#include "defs.h"
#include "symtab.h"
#include "frame.h"
#include "inferior.h"
#include "environ.h"
#include "value.h"
#include "target.h"
#include "gdbthread.h"
#include "exceptions.h"
#include "command.h"
#include "gdbcmd.h"
#include "regcache.h"
#include "gdb.h"
#include "gdb_string.h"
#include "wrapper.h"
#include <ctype.h>
#include <sys/types.h>
#include <signal.h>
#include "ui-out.h"
void _initialize_thread (void);
struct thread_info *thread_list = NULL;
int highest_thread_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 *);
static void info_threads_command (char *, int);
static void thread_apply_command (char *, int);
static void restore_current_thread (ptid_t, int);
static void prune_threads (void);
void
delete_step_resume_breakpoint (void *arg)
{
struct breakpoint **breakpointp = (struct breakpoint **) arg;
struct thread_info *tp;
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;
*breakpointp = NULL;
}
}
static void
free_thread (struct thread_info *tp)
{
if (tp->step_resume_breakpoint)
delete_breakpoint (tp->step_resume_breakpoint);
if (tp->private)
xfree (tp->private);
xfree (tp);
}
void
init_thread_list (void)
{
struct thread_info *tp, *tpnext;
highest_thread_num = 0;
if (!thread_list)
return;
for (tp = thread_list; tp; tp = tpnext)
{
tpnext = tp->next;
free_thread (tp);
}
thread_list = NULL;
}
struct thread_info *
add_thread (ptid_t ptid)
{
struct thread_info *tp;
tp = (struct thread_info *) xmalloc (sizeof (*tp));
memset (tp, 0, sizeof (*tp));
tp->ptid = ptid;
tp->num = ++highest_thread_num;
tp->next = thread_list;
thread_list = tp;
return tp;
}
void
delete_thread (ptid_t ptid)
{
struct thread_info *tp, *tpprev;
tpprev = NULL;
for (tp = thread_list; tp; tpprev = tp, tp = tp->next)
if (ptid_equal (tp->ptid, ptid))
break;
if (!tp)
return;
if (tpprev)
tpprev->next = tp->next;
else
thread_list = tp->next;
free_thread (tp);
}
struct thread_info *
find_thread_id (int num)
{
struct thread_info *tp;
for (tp = thread_list; tp; tp = tp->next)
if (tp->num == num)
return tp;
return NULL;
}
struct thread_info *
find_thread_pid (ptid_t ptid)
{
struct thread_info *tp;
for (tp = thread_list; tp; tp = tp->next)
if (ptid_equal (tp->ptid, ptid))
return tp;
return NULL;
}
struct thread_info *
iterate_over_threads (int (*callback) (struct thread_info *, void *),
void *data)
{
struct thread_info *tp;
for (tp = thread_list; tp; tp = tp->next)
if ((*callback) (tp, data))
return tp;
return NULL;
}
int
valid_thread_id (int num)
{
struct thread_info *tp;
for (tp = thread_list; tp; tp = tp->next)
if (tp->num == num)
return 1;
return 0;
}
int
pid_to_thread_id (ptid_t ptid)
{
struct thread_info *tp;
for (tp = thread_list; tp; tp = tp->next)
if (ptid_equal (tp->ptid, ptid))
return tp->num;
return 0;
}
ptid_t
thread_id_to_pid (int num)
{
struct thread_info *thread = find_thread_id (num);
if (thread)
return thread->ptid;
else
return pid_to_ptid (-1);
}
int
in_thread_list (ptid_t ptid)
{
struct thread_info *tp;
for (tp = thread_list; tp; tp = tp->next)
if (ptid_equal (tp->ptid, ptid))
return 1;
return 0;
}
static int
do_captured_list_thread_ids (struct ui_out *uiout, void *arg)
{
struct thread_info *tp;
int num = 0;
struct cleanup *cleanup_chain;
cleanup_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "thread-ids");
if (!target_has_stack)
error ("No stack.");
prune_threads ();
target_find_new_threads ();
for (tp = thread_list; tp; tp = tp->next)
{
num++;
ui_out_field_int (uiout, "thread-id", tp->num);
}
do_cleanups (cleanup_chain);
ui_out_field_int (uiout, "number-of-threads", num);
return GDB_RC_OK;
}
enum gdb_rc
gdb_list_thread_ids (struct ui_out *uiout, char **error_message)
{
return catch_exceptions_with_msg (uiout, do_captured_list_thread_ids, NULL,
error_message, RETURN_MASK_ALL);
}
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 *handling_longjmp,
int *another_trap,
int *stepping_through_solib_after_catch,
bpstat *stepping_through_solib_catchpoints,
int *current_line,
struct symtab **current_symtab)
{
struct thread_info *tp;
tp = find_thread_id (pid_to_thread_id (ptid));
if (tp == NULL)
return;
*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;
*handling_longjmp = tp->handling_longjmp;
*another_trap = tp->another_trap;
*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;
}
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 handling_longjmp,
int another_trap,
int stepping_through_solib_after_catch,
bpstat stepping_through_solib_catchpoints,
int current_line,
struct symtab *current_symtab)
{
struct thread_info *tp;
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->handling_longjmp = handling_longjmp;
tp->another_trap = another_trap;
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;
}
static int
thread_alive (struct thread_info *tp)
{
if (PIDGET (tp->ptid) == -1)
return 0;
if (!target_thread_alive (tp->ptid))
{
tp->ptid = pid_to_ptid (-1);
return 0;
}
return 1;
}
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);
}
}
static void
info_threads_command (char *arg, int from_tty)
{
struct thread_info *tp;
ptid_t current_ptid;
struct frame_info *cur_frame;
struct frame_id saved_frame_id = get_frame_id (get_selected_frame (NULL));
char *extra_info;
prune_threads ();
target_find_new_threads ();
current_ptid = inferior_ptid;
for (tp = thread_list; tp; tp = tp->next)
{
if (ptid_equal (tp->ptid, current_ptid))
printf_filtered ("* ");
else
printf_filtered (" ");
printf_filtered ("%d %s", tp->num, target_tid_to_str (tp->ptid));
extra_info = target_extra_thread_info (tp);
if (extra_info)
printf_filtered (" (%s)", extra_info);
puts_filtered (" ");
switch_to_thread (tp->ptid);
print_stack_frame (get_selected_frame (NULL), 0, LOCATION);
}
switch_to_thread (current_ptid);
cur_frame = frame_find_by_id (saved_frame_id);
if (cur_frame == NULL)
{
warning (_("Couldn't restore frame in current thread, at frame 0"));
print_stack_frame (get_selected_frame (NULL), 0, LOCATION);
}
else
{
select_frame (cur_frame);
show_stack_frame (cur_frame);
}
}
void
switch_to_thread (ptid_t ptid)
{
if (ptid_equal (ptid, inferior_ptid))
return;
inferior_ptid = ptid;
flush_cached_frames ();
registers_changed ();
stop_pc = read_pc ();
select_frame (get_current_frame ());
if (scheduler_lock_on_p ())
scheduler_run_this_ptid (inferior_ptid);
}
static void
restore_current_thread (ptid_t ptid, int print)
{
if (!ptid_equal (ptid, inferior_ptid))
{
switch_to_thread (ptid);
if (print)
print_stack_frame (get_current_frame (), 0, -1);
}
}
struct current_thread_cleanup
{
ptid_t inferior_ptid;
int print;
};
static void
do_restore_current_thread_cleanup (void *arg)
{
struct current_thread_cleanup *old = arg;
restore_current_thread (old->inferior_ptid, old->print);
xfree (old);
}
struct cleanup *
make_cleanup_restore_current_thread (ptid_t inferior_ptid, int print)
{
struct current_thread_cleanup *old
= xmalloc (sizeof (struct current_thread_cleanup));
old->inferior_ptid = inferior_ptid;
old->print = print;
return make_cleanup (do_restore_current_thread_cleanup, old);
}
static void
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;
if (cmd == NULL || *cmd == '\000')
error (_("Please specify a command following the thread ID list"));
old_chain = make_cleanup_restore_current_thread (inferior_ptid, 0);
target_find_new_threads ();
saved_cmd = xstrdup (cmd);
saved_cmd_cleanup_chain = make_cleanup (xfree, (void *) 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));
safe_execute_command (uiout, cmd, from_tty);
strcpy (cmd, saved_cmd);
}
do_cleanups (saved_cmd_cleanup_chain);
do_cleanups (old_chain);
}
static void
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;
if (tidlist == NULL || *tidlist == '\000')
error (_("Please specify a thread ID list"));
for (cmd = tidlist; *cmd != '\000' && !isalpha (*cmd); cmd++);
if (*cmd == '\000')
error (_("Please specify a command following the thread ID list"));
old_chain = make_cleanup_restore_current_thread (inferior_ptid, 0);
saved_cmd = xstrdup (cmd);
saved_cmd_cleanup_chain = make_cleanup (xfree, (void *) saved_cmd);
while (tidlist < cmd)
{
struct thread_info *tp;
int start, end;
start = strtol (tidlist, &p, 10);
if (p == tidlist)
error (_("Error parsing %s"), tidlist);
tidlist = p;
while (*tidlist == ' ' || *tidlist == '\t')
tidlist++;
if (*tidlist == '-')
{
tidlist++;
end = strtol (tidlist, &p, 10);
if (p == tidlist)
error (_("Error parsing %s"), tidlist);
tidlist = p;
while (*tidlist == ' ' || *tidlist == '\t')
tidlist++;
}
else
end = start;
for (; start <= end; start++)
{
tp = find_thread_id (start);
if (!tp)
warning (_("Unknown thread %d."), start);
else if (!thread_alive (tp))
warning (_("Thread %d has terminated."), start);
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);
}
}
}
do_cleanups (saved_cmd_cleanup_chain);
do_cleanups (old_chain);
}
struct select_thread_args
{
char *tidstr;
int print;
};
static void
thread_command (char *tidstr, int from_tty)
{
if (!tidstr)
{
if (target_has_stack)
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;
}
gdb_thread_select (uiout, tidstr, 1, NULL);
}
extern int scheduler_lock_on ();
extern struct ptid scheduler_lock_ptid;
static int
do_captured_thread_select (struct ui_out *uiout,
void *in_args)
{
int num;
struct thread_info *tp;
struct select_thread_args *args = (struct select_thread_args *) in_args;
num = value_as_long (parse_and_eval (args->tidstr));
tp = find_thread_id (num);
if (!tp)
error (_("Thread ID %d not known."), num);
if (!thread_alive (tp))
error (_("Thread ID %d has terminated."), num);
switch_to_thread (tp->ptid);
if (args->print)
{
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, " (");
#if defined(HPUXHPPA)
ui_out_text (uiout, target_tid_to_str (inferior_ptid));
#else
ui_out_text (uiout, target_pid_to_str (inferior_ptid));
#endif
ui_out_text (uiout, ")]\n");
print_stack_frame (deprecated_selected_frame,
frame_relative_level (deprecated_selected_frame), 1);
}
if (deprecated_context_hook)
deprecated_context_hook (pid_to_thread_id (inferior_ptid));
return GDB_RC_OK;
}
enum gdb_rc
gdb_thread_select (struct ui_out *uiout, char *tidstr, int print,
char **error_message)
{
struct select_thread_args args;
args.tidstr = tidstr;
args.print = print;
return catch_exceptions_with_msg (uiout, do_captured_thread_select, &args,
error_message, RETURN_MASK_ALL);
}
struct cmd_list_element *thread_cmd_list = NULL;
void
_initialize_thread (void)
{
static struct cmd_list_element *thread_apply_list = NULL;
add_info ("threads", info_threads_command,
_("IDs of currently known threads."));
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);
add_prefix_cmd ("apply", class_run, thread_apply_command,
_("Apply a command to a list of threads."),
&thread_apply_list, "apply ", 1, &thread_cmd_list);
add_cmd ("all", class_run, thread_apply_all_command,
_("Apply a command to all threads."), &thread_apply_list);
if (!xdb_commands)
add_com_alias ("t", "thread", class_run, 1);
}