/* Multi-process/thread control for GDB, the GNU debugger.
- Copyright (C) 1986-2014 Free Software Foundation, Inc.
+ Copyright (C) 1986-2015 Free Software Foundation, Inc.
Contributed by Lynx Real-Time Systems, Inc. Los Gatos, CA.
spawned new threads we haven't heard of yet. */
static int threads_executing;
-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);
-static void prune_threads (void);
/* Data to cleanup thread array. */
return tp;
}
-void
-delete_step_resume_breakpoint (struct thread_info *tp)
+/* Delete the breakpoint pointed at by BP_P, if there's one. */
+
+static void
+delete_thread_breakpoint (struct breakpoint **bp_p)
{
- if (tp && tp->control.step_resume_breakpoint)
+ if (*bp_p != NULL)
{
- delete_breakpoint (tp->control.step_resume_breakpoint);
- tp->control.step_resume_breakpoint = NULL;
+ delete_breakpoint (*bp_p);
+ *bp_p = NULL;
}
}
+void
+delete_step_resume_breakpoint (struct thread_info *tp)
+{
+ if (tp != NULL)
+ delete_thread_breakpoint (&tp->control.step_resume_breakpoint);
+}
+
void
delete_exception_resume_breakpoint (struct thread_info *tp)
{
- if (tp && tp->control.exception_resume_breakpoint)
+ if (tp != NULL)
+ delete_thread_breakpoint (&tp->control.exception_resume_breakpoint);
+}
+
+/* See gdbthread.h. */
+
+void
+delete_single_step_breakpoints (struct thread_info *tp)
+{
+ if (tp != NULL)
+ delete_thread_breakpoint (&tp->control.single_step_breakpoints);
+}
+
+/* Delete the breakpoint pointed at by BP_P at the next stop, if
+ there's one. */
+
+static void
+delete_at_next_stop (struct breakpoint **bp)
+{
+ if (*bp != NULL)
{
- delete_breakpoint (tp->control.exception_resume_breakpoint);
- tp->control.exception_resume_breakpoint = NULL;
+ (*bp)->disposition = disp_del_at_next_stop;
+ *bp = NULL;
}
}
+/* See gdbthread.h. */
+
+int
+thread_has_single_step_breakpoints_set (struct thread_info *tp)
+{
+ return tp->control.single_step_breakpoints != NULL;
+}
+
+/* See gdbthread.h. */
+
+int
+thread_has_single_step_breakpoint_here (struct thread_info *tp,
+ struct address_space *aspace,
+ CORE_ADDR addr)
+{
+ struct breakpoint *ss_bps = tp->control.single_step_breakpoints;
+
+ return (ss_bps != NULL
+ && breakpoint_has_location_inserted_here (ss_bps, aspace, addr));
+}
+
static void
clear_thread_inferior_resources (struct thread_info *tp)
{
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->control.step_resume_breakpoint)
- {
- tp->control.step_resume_breakpoint->disposition = disp_del_at_next_stop;
- tp->control.step_resume_breakpoint = NULL;
- }
-
- if (tp->control.exception_resume_breakpoint)
- {
- tp->control.exception_resume_breakpoint->disposition
- = disp_del_at_next_stop;
- tp->control.exception_resume_breakpoint = NULL;
- }
+ delete_at_next_stop (&tp->control.step_resume_breakpoint);
+ delete_at_next_stop (&tp->control.exception_resume_breakpoint);
+ delete_at_next_stop (&tp->control.single_step_breakpoints);
delete_longjmp_breakpoint_at_next_stop (tp->num);
static void
free_thread (struct thread_info *tp)
{
- if (tp->private)
+ if (tp->priv)
{
if (tp->private_dtor)
- tp->private_dtor (tp->private);
+ tp->private_dtor (tp->priv);
else
- xfree (tp->private);
+ xfree (tp->priv);
}
xfree (tp->name);
}
struct thread_info *
-add_thread_with_info (ptid_t ptid, struct private_thread_info *private)
+add_thread_with_info (ptid_t ptid, struct private_thread_info *priv)
{
struct thread_info *result = add_thread_silent (ptid);
- result->private = private;
+ result->priv = priv;
if (print_thread_events)
printf_unfiltered (_("[New %s]\n"), target_pid_to_str (ptid));
return 1;
}
-static void
+/* See gdbthreads.h. */
+
+void
prune_threads (void)
{
- struct thread_info *tp, *next;
+ struct thread_info *tp, *tmp;
- for (tp = thread_list; tp; tp = next)
+ ALL_THREADS_SAFE (tp, tmp)
{
- next = tp->next;
if (!thread_alive (tp))
delete_thread (tp->ptid);
}
}
+/* See gdbthreads.h. */
+
+void
+delete_exited_threads (void)
+{
+ struct thread_info *tp, *tmp;
+
+ ALL_THREADS_SAFE (tp, tmp)
+ {
+ if (tp->state == THREAD_EXITED)
+ delete_thread (tp->ptid);
+ }
+}
+
+/* Disable storing stack temporaries for the thread whose id is
+ stored in DATA. */
+
+static void
+disable_thread_stack_temporaries (void *data)
+{
+ ptid_t *pd = data;
+ struct thread_info *tp = find_thread_ptid (*pd);
+
+ if (tp != NULL)
+ {
+ tp->stack_temporaries_enabled = 0;
+ VEC_free (value_ptr, tp->stack_temporaries);
+ }
+
+ xfree (pd);
+}
+
+/* Enable storing stack temporaries for thread with id PTID and return a
+ cleanup which can disable and clear the stack temporaries. */
+
+struct cleanup *
+enable_thread_stack_temporaries (ptid_t ptid)
+{
+ struct thread_info *tp = find_thread_ptid (ptid);
+ ptid_t *data;
+ struct cleanup *c;
+
+ gdb_assert (tp != NULL);
+
+ tp->stack_temporaries_enabled = 1;
+ tp->stack_temporaries = NULL;
+ data = (ptid_t *) xmalloc (sizeof (ptid_t));
+ *data = ptid;
+ c = make_cleanup (disable_thread_stack_temporaries, data);
+
+ return c;
+}
+
+/* Return non-zero value if stack temporaies are enabled for the thread
+ with id PTID. */
+
+int
+thread_stack_temporaries_enabled_p (ptid_t ptid)
+{
+ struct thread_info *tp = find_thread_ptid (ptid);
+
+ if (tp == NULL)
+ return 0;
+ else
+ return tp->stack_temporaries_enabled;
+}
+
+/* Push V on to the stack temporaries of the thread with id PTID. */
+
+void
+push_thread_stack_temporary (ptid_t ptid, struct value *v)
+{
+ struct thread_info *tp = find_thread_ptid (ptid);
+
+ gdb_assert (tp != NULL && tp->stack_temporaries_enabled);
+ VEC_safe_push (value_ptr, tp->stack_temporaries, v);
+}
+
+/* Return 1 if VAL is among the stack temporaries of the thread
+ with id PTID. Return 0 otherwise. */
+
+int
+value_in_thread_stack_temporaries (struct value *val, ptid_t ptid)
+{
+ struct thread_info *tp = find_thread_ptid (ptid);
+
+ gdb_assert (tp != NULL && tp->stack_temporaries_enabled);
+ if (!VEC_empty (value_ptr, tp->stack_temporaries))
+ {
+ struct value *v;
+ int i;
+
+ for (i = 0; VEC_iterate (value_ptr, tp->stack_temporaries, i, v); i++)
+ if (v == val)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return the last of the stack temporaries for thread with id PTID.
+ Return NULL if there are no stack temporaries for the thread. */
+
+struct value *
+get_last_thread_stack_temporary (ptid_t ptid)
+{
+ struct value *lastval = NULL;
+ struct thread_info *tp = find_thread_ptid (ptid);
+
+ gdb_assert (tp != NULL);
+ if (!VEC_empty (value_ptr, tp->stack_temporaries))
+ lastval = VEC_last (value_ptr, tp->stack_temporaries);
+
+ return lastval;
+}
+
void
thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid)
{
/* 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 = find_inferior_ptid (old_ptid);
inf->pid = ptid_get_pid (new_ptid);
tp = find_thread_ptid (old_ptid);
{
struct inferior *inf;
- inf = find_inferior_pid (ptid_get_pid (ptid));
+ inf = find_inferior_ptid (ptid);
gdb_assert (inf != NULL);
set_current_program_space (inf->pspace);
set_current_inferior (inf);
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)
+ && find_inferior_ptid (tp->ptid) != NULL)
restore_current_thread (old->inferior_ptid);
else
{
restore_current_thread_cleanup_dtor);
}
+/* If non-zero tp_array_compar should sort in ascending order, otherwise in
+ descending order. */
+
+static int tp_array_compar_ascending;
+
+/* Sort an array for struct thread_info pointers by their NUM, order is
+ determined by TP_ARRAY_COMPAR_ASCENDING. */
+
+static int
+tp_array_compar (const void *ap_voidp, const void *bp_voidp)
+{
+ const struct thread_info *const *ap = ap_voidp;
+ const struct thread_info *const *bp = bp_voidp;
+
+ return ((((*ap)->num > (*bp)->num) - ((*ap)->num < (*bp)->num))
+ * (tp_array_compar_ascending ? +1 : -1));
+}
+
/* Apply a GDB command to a list of threads. List syntax is a whitespace
seperated list of numbers, or ranges, or the keyword `all'. Ranges consist
of two numbers seperated by a hyphen. Examples:
int tc;
struct thread_array_cleanup ta_cleanup;
+ tp_array_compar_ascending = 0;
+ if (cmd != NULL
+ && check_for_argument (&cmd, "-ascending", strlen ("-ascending")))
+ {
+ cmd = skip_spaces (cmd);
+ tp_array_compar_ascending = 1;
+ }
+
if (cmd == NULL || *cmd == '\000')
error (_("Please specify a command following the thread ID list"));
execute_command. */
saved_cmd = xstrdup (cmd);
make_cleanup (xfree, saved_cmd);
- tc = thread_count ();
- if (tc)
+ /* Note this includes exited threads. */
+ tc = thread_count ();
+ if (tc != 0)
{
struct thread_info **tp_array;
struct thread_info *tp;
command. */
tp_array = xmalloc (sizeof (struct thread_info *) * tc);
make_cleanup (xfree, tp_array);
- ta_cleanup.tp_array = tp_array;
- ta_cleanup.count = tc;
ALL_NON_EXITED_THREADS (tp)
{
tp->refcount++;
i++;
}
+ /* Because we skipped exited threads, we may end up with fewer
+ threads in the array than the total count of threads. */
+ gdb_assert (i <= tc);
+ if (i != 0)
+ qsort (tp_array, i, sizeof (*tp_array), tp_array_compar);
+
+ ta_cleanup.tp_array = tp_array;
+ ta_cleanup.count = i;
make_cleanup (set_thread_refcount, &ta_cleanup);
for (k = 0; k != i; k++)
/* Switch to the specified thread. Will dispatch off to thread_apply_command
if prefix of arg is `apply'. */
-static void
+void
thread_command (char *tidstr, int from_tty)
{
if (!tidstr)
void
update_thread_list (void)
{
- prune_threads ();
- target_find_new_threads ();
+ target_update_thread_list ();
update_threads_executing ();
}
&thread_apply_list, "thread apply ", 1, &thread_cmd_list);
add_cmd ("all", class_run, thread_apply_all_command,
- _("Apply a command to all threads."), &thread_apply_list);
+ _("\
+Apply a command to all threads.\n\
+\n\
+Usage: thread apply all [-ascending] <command>\n\
+-ascending: Call <command> for all threads in ascending order.\n\
+ The default is descending order.\
+"),
+ &thread_apply_list);
add_cmd ("name", class_run, thread_name_command,
_("Set the current thread's name.\n\
Will display thread ids whose name, target ID, or extra info matches REGEXP."),
&thread_cmd_list);
- if (!xdb_commands)
- add_com_alias ("t", "thread", class_run, 1);
+ add_com_alias ("t", "thread", class_run, 1);
add_setshow_boolean_cmd ("thread-events", no_class,
&print_thread_events, _("\