/* 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, 2009, 2010, 2011
- Free Software Foundation, Inc.
+ Copyright (C) 1986-2015 Free Software Foundation, Inc.
Contributed by Lynx Real-Time Systems, Inc. Los Gatos, CA.
#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 "btrace.h"
#include <ctype.h>
#include <sys/types.h>
#include "cli/cli-decode.h"
#include "gdb_regex.h"
#include "cli/cli-utils.h"
+#include "continuations.h"
/* Definition of struct thread_info exported to gdbthread.h. */
/* Prototypes for local functions. */
-static struct thread_info *thread_list = NULL;
+struct thread_info *thread_list = NULL;
static int highest_thread_num;
-static void thread_command (char *tidstr, int from_tty);
+/* True if any thread is, or may be executing. We need to track this
+ separately because until we fully sync the thread list, we won't
+ know whether the target is fully stopped, even if we see stop
+ events for all known threads, because any of those threads may have
+ spawned new threads we haven't heard of yet. */
+static int threads_executing;
+
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);
-/* Frontend view of the thread state. Possible extensions: stepping,
- finishing, until(ling),... */
-enum thread_state
+/* Data to cleanup thread array. */
+
+struct thread_array_cleanup
{
- THREAD_STOPPED,
- THREAD_RUNNING,
- THREAD_EXITED,
+ /* Array of thread pointers used to set
+ reference count. */
+ struct thread_info **tp_array;
+
+ /* Thread count in the array. */
+ int count;
};
+
struct thread_info*
inferior_thread (void)
{
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;
- }
+ 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);
- if (tp->control.exception_resume_breakpoint)
- {
- tp->control.exception_resume_breakpoint->disposition
- = disp_del_at_next_stop;
- tp->control.exception_resume_breakpoint = NULL;
- }
+ delete_longjmp_breakpoint_at_next_stop (tp->num);
bpstat_clear (&tp->control.stop_bpstat);
- discard_all_intermediate_continuations_thread (tp);
- discard_all_continuations_thread (tp);
+ btrace_teardown (tp);
- delete_longjmp_breakpoint (tp->num);
+ do_all_intermediate_continuations_thread (tp, 1);
+ do_all_continuations_thread (tp, 1);
}
static void
free_thread (struct thread_info *tp)
{
- clear_thread_inferior_resources (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);
}
thread_list = NULL;
+ threads_executing = 0;
}
/* Allocate a new thread with target id PTID and add it to the thread
/* Nothing to follow yet. */
tp->pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
- tp->state_ = THREAD_STOPPED;
+ tp->state = THREAD_STOPPED;
return tp;
}
tp = new_thread (null_ptid);
/* Make switch_to_thread not read from the thread. */
- tp->state_ = THREAD_EXITED;
+ tp->state = THREAD_EXITED;
switch_to_thread (null_ptid);
/* Now we can delete it. */
/* Now reset its ptid, and reswitch inferior_ptid to it. */
tp->ptid = ptid;
- tp->state_ = THREAD_STOPPED;
+ tp->state = THREAD_STOPPED;
switch_to_thread (ptid);
observer_notify_new_thread (tp);
}
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 add_thread_with_info (ptid, NULL);
}
+/* Add TP to the end of the step-over chain LIST_P. */
+
+static void
+step_over_chain_enqueue (struct thread_info **list_p, struct thread_info *tp)
+{
+ gdb_assert (tp->step_over_next == NULL);
+ gdb_assert (tp->step_over_prev == NULL);
+
+ if (*list_p == NULL)
+ {
+ *list_p = tp;
+ tp->step_over_prev = tp->step_over_next = tp;
+ }
+ else
+ {
+ struct thread_info *head = *list_p;
+ struct thread_info *tail = head->step_over_prev;
+
+ tp->step_over_prev = tail;
+ tp->step_over_next = head;
+ head->step_over_prev = tp;
+ tail->step_over_next = tp;
+ }
+}
+
+/* Remove TP from step-over chain LIST_P. */
+
+static void
+step_over_chain_remove (struct thread_info **list_p, struct thread_info *tp)
+{
+ gdb_assert (tp->step_over_next != NULL);
+ gdb_assert (tp->step_over_prev != NULL);
+
+ if (*list_p == tp)
+ {
+ if (tp == tp->step_over_next)
+ *list_p = NULL;
+ else
+ *list_p = tp->step_over_next;
+ }
+
+ tp->step_over_prev->step_over_next = tp->step_over_next;
+ tp->step_over_next->step_over_prev = tp->step_over_prev;
+ tp->step_over_prev = tp->step_over_next = NULL;
+}
+
+/* See gdbthread.h. */
+
+struct thread_info *
+thread_step_over_chain_next (struct thread_info *tp)
+{
+ struct thread_info *next = tp->step_over_next;
+
+ return (next == step_over_queue_head ? NULL : next);
+}
+
+/* See gdbthread.h. */
+
+int
+thread_is_in_step_over_chain (struct thread_info *tp)
+{
+ return (tp->step_over_next != NULL);
+}
+
+/* See gdbthread.h. */
+
+void
+thread_step_over_chain_enqueue (struct thread_info *tp)
+{
+ step_over_chain_enqueue (&step_over_queue_head, tp);
+}
+
+/* See gdbthread.h. */
+
+void
+thread_step_over_chain_remove (struct thread_info *tp)
+{
+ step_over_chain_remove (&step_over_queue_head, tp);
+}
+
/* Delete thread PTID. If SILENT, don't notify the observer of this
exit. */
static void
if (!tp)
return;
+ /* Dead threads don't need to step-over. Remove from queue. */
+ if (tp->step_over_next != NULL)
+ thread_step_over_chain_remove (tp);
+
/* 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 (tp->state != THREAD_EXITED)
{
observer_notify_thread_exit (tp, silent);
/* Tag it as exited. */
- tp->state_ = THREAD_EXITED;
+ tp->state = THREAD_EXITED;
/* Clear breakpoints, etc. associated with this thread. */
clear_thread_inferior_resources (tp);
return;
}
+ /* Notify thread exit, but only if we haven't already. */
+ if (tp->state != THREAD_EXITED)
+ observer_notify_thread_exit (tp, silent);
+
+ /* Tag it as exited. */
+ tp->state = THREAD_EXITED;
+ clear_thread_inferior_resources (tp);
+
if (tpprev)
tpprev->next = tp->next;
else
thread_list = tp->next;
- /* Notify thread exit, but only if we haven't already. */
- if (tp->state_ != THREAD_EXITED)
- observer_notify_thread_exit (tp, silent);
-
free_thread (tp);
}
{
struct thread_info *tp;
- for (tp = thread_list; tp; tp = tp->next)
+ gdb_assert (pid != 0);
+
+ /* Prefer the current thread. */
+ if (ptid_get_pid (inferior_ptid) == pid)
+ return inferior_thread ();
+
+ ALL_NON_EXITED_THREADS (tp)
if (ptid_get_pid (tp->ptid) == pid)
return tp;
struct thread_info *
any_live_thread_of_process (int pid)
{
+ struct thread_info *curr_tp = NULL;
struct thread_info *tp;
- struct thread_info *tp_running = NULL;
+ struct thread_info *tp_executing = NULL;
- for (tp = thread_list; tp; tp = tp->next)
+ gdb_assert (pid != 0);
+
+ /* Prefer the current thread if it's not executing. */
+ if (ptid_get_pid (inferior_ptid) == pid)
+ {
+ /* If the current thread is dead, forget it. If it's not
+ executing, use it. Otherwise, still choose it (below), but
+ only if no other non-executing thread is found. */
+ curr_tp = inferior_thread ();
+ if (curr_tp->state == THREAD_EXITED)
+ curr_tp = NULL;
+ else if (!curr_tp->executing)
+ return curr_tp;
+ }
+
+ ALL_NON_EXITED_THREADS (tp)
if (ptid_get_pid (tp->ptid) == pid)
{
- if (tp->state_ == THREAD_STOPPED)
+ if (!tp->executing)
return tp;
- else if (tp->state_ == THREAD_RUNNING)
- tp_running = tp;
+
+ tp_executing = tp;
}
- return tp_running;
+ /* If both the current thread and all live threads are executing,
+ prefer the current thread. */
+ if (curr_tp != NULL)
+ return curr_tp;
+
+ /* Otherwise, just return an executing thread, if any. */
+ return tp_executing;
}
/* Print a list of thread ids currently known, and the total number of
for (tp = thread_list; tp; tp = tp->next)
{
- if (tp->state_ == THREAD_EXITED)
+ if (tp->state == THREAD_EXITED)
continue;
if (ptid_equal (tp->ptid, inferior_ptid))
static int
thread_alive (struct thread_info *tp)
{
- if (tp->state_ == THREAD_EXITED)
+ if (tp->state == THREAD_EXITED)
return 0;
if (!target_thread_alive (tp->ptid))
return 0;
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);
observer_notify_thread_ptid_changed (old_ptid, new_ptid);
}
+/* Helper for set_running, that marks one thread either running or
+ stopped. */
+
+static int
+set_running_thread (struct thread_info *tp, int running)
+{
+ int started = 0;
+
+ if (running && tp->state == THREAD_STOPPED)
+ started = 1;
+ tp->state = running ? THREAD_RUNNING : THREAD_STOPPED;
+
+ if (!running)
+ {
+ /* If the thread is now marked stopped, remove it from
+ the step-over queue, so that we don't try to resume
+ it until the user wants it to. */
+ if (tp->step_over_next != NULL)
+ thread_step_over_chain_remove (tp);
+ }
+
+ return started;
+}
+
void
set_running (ptid_t ptid, int running)
{
struct thread_info *tp;
int all = ptid_equal (ptid, minus_one_ptid);
+ int any_started = 0;
/* 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)
+ if (tp->state == THREAD_EXITED)
continue;
- if (running && tp->state_ == THREAD_STOPPED)
+
+ if (set_running_thread (tp, running))
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);
+ gdb_assert (tp != NULL);
+ gdb_assert (tp->state != THREAD_EXITED);
+ if (set_running_thread (tp, running))
+ any_started = 1;
}
+ if (any_started)
+ observer_notify_target_resumed (ptid);
}
static int
tp = find_thread_ptid (ptid);
gdb_assert (tp);
- return tp->state_ == state;
+ return tp->state == state;
}
int
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)
{
tp = find_thread_ptid (ptid);
gdb_assert (tp);
- return tp->executing_;
+ return tp->executing;
}
void
{
for (tp = thread_list; tp; tp = tp->next)
if (all || ptid_get_pid (tp->ptid) == ptid_get_pid (ptid))
- tp->executing_ = executing;
+ tp->executing = executing;
}
else
{
tp = find_thread_ptid (ptid);
gdb_assert (tp);
- tp->executing_ = executing;
+ tp->executing = executing;
}
+
+ /* It only takes one running thread to spawn more threads.*/
+ if (executing)
+ threads_executing = 1;
+ /* Only clear the flag if the caller is telling us everything is
+ stopped. */
+ else if (ptid_equal (minus_one_ptid, ptid))
+ threads_executing = 0;
+}
+
+/* See gdbthread.h. */
+
+int
+threads_are_executing (void)
+{
+ return threads_executing;
}
void
{
for (tp = thread_list; tp; tp = tp->next)
{
- if (tp->state_ == THREAD_EXITED)
+ if (tp->state == THREAD_EXITED)
continue;
if (all || ptid_get_pid (ptid) == ptid_get_pid (tp->ptid))
{
- if (tp->executing_ && tp->state_ == THREAD_STOPPED)
+ if (set_running_thread (tp, tp->executing))
any_started = 1;
- tp->state_ = tp->executing_ ? THREAD_RUNNING : THREAD_STOPPED;
}
}
}
{
tp = find_thread_ptid (ptid);
gdb_assert (tp);
- if (tp->state_ != THREAD_EXITED)
+ if (tp->state != THREAD_EXITED)
{
- if (tp->executing_ && tp->state_ == THREAD_STOPPED)
+ if (set_running_thread (tp, tp->executing))
any_started = 1;
- tp->state_ = tp->executing_ ? THREAD_RUNNING : THREAD_STOPPED;
}
}
finish_thread_state (*ptid_p);
}
+int
+pc_in_thread_step_range (CORE_ADDR pc, struct thread_info *thread)
+{
+ return (pc >= thread->control.step_range_start
+ && pc < thread->control.step_range_end);
+}
+
/* Prints the list of threads and their details on UIOUT.
This is a version of 'info_threads_command' suitable for
use from MI.
if (!number_is_in_list (requested_threads, tp->num))
continue;
- if (pid != -1 && PIDGET (tp->ptid) != pid)
+ if (pid != -1 && ptid_get_pid (tp->ptid) != pid)
continue;
- if (tp->state_ == THREAD_EXITED)
+ if (tp->state == THREAD_EXITED)
continue;
++n_threads;
if (!number_is_in_list (requested_threads, tp->num))
continue;
- if (pid != -1 && PIDGET (tp->ptid) != pid)
+ if (pid != -1 && ptid_get_pid (tp->ptid) != pid)
{
if (requested_threads != NULL && *requested_threads != '\0')
error (_("Requested thread not found in requested process"));
if (ptid_equal (tp->ptid, current_ptid))
current_thread = tp->num;
- if (tp->state_ == THREAD_EXITED)
+ if (tp->state == THREAD_EXITED)
continue;
chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
do_cleanups (str_cleanup);
}
- if (tp->state_ == THREAD_RUNNING)
+ if (tp->state == THREAD_RUNNING)
ui_out_text (uiout, "(running)\n");
else
{
print_stack_frame (get_selected_frame (NULL),
/* For MI output, print frame level. */
ui_out_is_mi_like_p (uiout),
- LOCATION);
+ LOCATION, 0);
}
if (ui_out_is_mi_like_p (uiout))
{
char *state = "stopped";
- if (tp->state_ == THREAD_RUNNING)
+ if (tp->state == THREAD_RUNNING)
state = "running";
ui_out_field_string (uiout, "state", state);
}
static void
info_threads_command (char *arg, int from_tty)
{
- print_thread_info (uiout, arg, -1);
+ print_thread_info (current_uiout, arg, -1);
}
/* Switch from one thread to another. */
{
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);
inferior_ptid = ptid;
reinit_frame_cache ();
- registers_changed ();
/* We don't check for is_stopped, because we're called at times
while in the TARGET_RUNNING state, e.g., while handling an
struct frame_info *frame = NULL;
int count;
+ /* This means there was no selected frame. */
+ if (frame_level == -1)
+ {
+ select_frame (NULL);
+ return;
+ }
+
gdb_assert (frame_level >= 0);
/* Restore by level first, check if the frame id is the same as
select_frame (get_current_frame ());
/* Warn the user. */
- if (frame_level > 0 && !ui_out_is_mi_like_p (uiout))
+ if (frame_level > 0 && !ui_out_is_mi_like_p (current_uiout))
{
warning (_("Couldn't restore frame #%d in "
- "current thread, at reparsed frame #0\n"),
+ "current thread. Bottom (innermost) frame selected:"),
frame_level);
/* For MI, we should probably have a notification about
current frame change. But this error is not very
likely, so don't bother for now. */
- print_stack_frame (get_selected_frame (NULL), 1, SRC_LINE);
+ print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
}
}
+/* Data used by the cleanup installed by
+ 'make_cleanup_restore_current_thread'. */
+
struct current_thread_cleanup
{
+ /* Next in list of currently installed 'struct
+ current_thread_cleanup' cleanups. See
+ 'current_thread_cleanup_chain' below. */
+ struct current_thread_cleanup *next;
+
ptid_t inferior_ptid;
struct frame_id selected_frame_id;
int selected_frame_level;
int was_stopped;
int inf_id;
+ int was_removable;
};
+/* A chain of currently installed 'struct current_thread_cleanup'
+ cleanups. Restoring the previously selected thread looks up the
+ old thread in the thread list by ptid. If the thread changes ptid,
+ we need to update the cleanup's thread structure so the look up
+ succeeds. */
+static struct current_thread_cleanup *current_thread_cleanup_chain;
+
+/* A thread_ptid_changed observer. Update all currently installed
+ current_thread_cleanup cleanups that want to switch back to
+ OLD_PTID to switch back to NEW_PTID instead. */
+
+static void
+restore_current_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid)
+{
+ struct current_thread_cleanup *it;
+
+ for (it = current_thread_cleanup_chain; it != NULL; it = it->next)
+ {
+ if (ptid_equal (it->inferior_ptid, old_ptid))
+ it->inferior_ptid = new_ptid;
+ }
+}
+
static void
do_restore_current_thread_cleanup (void *arg)
{
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
{
{
struct current_thread_cleanup *old = arg;
struct thread_info *tp;
+ struct inferior *inf;
+
+ current_thread_cleanup_chain = current_thread_cleanup_chain->next;
tp = find_thread_ptid (old->inferior_ptid);
if (tp)
tp->refcount--;
+ inf = find_inferior_id (old->inf_id);
+ if (inf != NULL)
+ inf->removable = old->was_removable;
xfree (old);
}
+/* Set the thread reference count. */
+
+static void
+set_thread_refcount (void *data)
+{
+ int k;
+ struct thread_array_cleanup *ta_cleanup = data;
+
+ for (k = 0; k != ta_cleanup->count; k++)
+ ta_cleanup->tp_array[k]->refcount--;
+}
+
struct cleanup *
make_cleanup_restore_current_thread (void)
{
old = xmalloc (sizeof (struct current_thread_cleanup));
old->inferior_ptid = inferior_ptid;
old->inf_id = current_inferior ()->num;
+ old->was_removable = current_inferior ()->removable;
+
+ old->next = current_thread_cleanup_chain;
+ current_thread_cleanup_chain = old;
if (!ptid_equal (inferior_ptid, null_ptid))
{
&& target_has_registers
&& target_has_stack
&& target_has_memory)
- frame = get_selected_frame (NULL);
+ {
+ /* When processing internal events, there might not be a
+ selected frame. If we naively call get_selected_frame
+ here, then we can end up reading debuginfo for the
+ current frame, but we don't generally need the debuginfo
+ at this point. */
+ frame = get_selected_frame_if_set ();
+ }
else
frame = NULL;
tp->refcount++;
}
+ current_inferior ()->removable = 0;
+
return make_cleanup_dtor (do_restore_current_thread_cleanup, old,
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:
static void
thread_apply_all_command (char *cmd, int from_tty)
{
- struct thread_info *tp;
struct cleanup *old_chain;
char *saved_cmd;
+ 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);
- 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_pid_to_str (inferior_ptid));
- execute_command (cmd, from_tty);
- strcpy (cmd, saved_cmd); /* Restore exact command used
- previously. */
- }
+ /* Note this includes exited threads. */
+ tc = thread_count ();
+ if (tc != 0)
+ {
+ struct thread_info **tp_array;
+ struct thread_info *tp;
+ int i = 0, k;
+
+ /* Save a copy of the thread_list in case we execute detach
+ command. */
+ tp_array = xmalloc (sizeof (struct thread_info *) * tc);
+ make_cleanup (xfree, tp_array);
+
+ ALL_NON_EXITED_THREADS (tp)
+ {
+ tp_array[i] = 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++)
+ if (thread_alive (tp_array[k]))
+ {
+ switch_to_thread (tp_array[k]->ptid);
+ printf_filtered (_("\nThread %d (%s):\n"),
+ tp_array[k]->num,
+ target_pid_to_str (inferior_ptid));
+ execute_command (cmd, from_tty);
+
+ /* Restore exact command used previously. */
+ strcpy (cmd, saved_cmd);
+ }
+ }
do_cleanups (old_chain);
}
char *cmd;
struct cleanup *old_chain;
char *saved_cmd;
+ struct get_number_or_range_state state;
if (tidlist == NULL || *tidlist == '\000')
error (_("Please specify a thread ID list"));
execute_command. */
saved_cmd = xstrdup (cmd);
old_chain = make_cleanup (xfree, saved_cmd);
- while (tidlist < cmd)
+
+ init_number_or_range (&state, tidlist);
+ while (!state.finished && state.string < cmd)
{
struct thread_info *tp;
int start;
- char *p = tidlist;
- start = get_number_or_range (&tidlist);
+ start = get_number_or_range (&state);
make_cleanup_restore_current_thread ();
/* 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)
return;
}
- gdb_thread_select (uiout, tidstr, NULL);
+ gdb_thread_select (current_uiout, tidstr, NULL);
}
/* Implementation of `thread name'. */
if (ptid_equal (inferior_ptid, null_ptid))
error (_("No thread selected"));
- while (arg && isspace (*arg))
- ++arg;
+ arg = skip_spaces (arg);
info = inferior_thread ();
xfree (info->name);
/* Note that we can't reach this with an exited thread, due to the
thread_alive check above. */
- if (tp->state_ == THREAD_RUNNING)
+ if (tp->state == THREAD_RUNNING)
ui_out_text (uiout, "(running)\n");
else
{
ui_out_text (uiout, "\n");
- print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+ print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
}
/* Since the current thread may have changed, see if there is any
return GDB_RC_OK;
}
+/* Update the 'threads_executing' global based on the threads we know
+ about right now. */
+
+static void
+update_threads_executing (void)
+{
+ struct thread_info *tp;
+
+ threads_executing = 0;
+ ALL_NON_EXITED_THREADS (tp)
+ {
+ if (tp->executing)
+ {
+ threads_executing = 1;
+ break;
+ }
+ }
+}
+
void
update_thread_list (void)
{
- prune_threads ();
- target_find_new_threads ();
+ target_update_thread_list ();
+ update_threads_executing ();
}
/* Return a new value for the selected thread's id. Return a value of 0 if
no thread is selected, or no threads exist. */
static struct value *
-thread_id_make_value (struct gdbarch *gdbarch, struct internalvar *var)
+thread_id_make_value (struct gdbarch *gdbarch, struct internalvar *var,
+ void *ignore)
{
struct thread_info *tp = find_thread_ptid (inferior_ptid);
/* Commands with a prefix of `thread'. */
struct cmd_list_element *thread_cmd_list = NULL;
+/* Implementation of `thread' variable. */
+
+static const struct internalvar_funcs thread_funcs =
+{
+ thread_id_make_value,
+ NULL,
+ NULL
+};
+
void
_initialize_thread (void)
{
&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, _("\
show_print_thread_events,
&setprintlist, &showprintlist);
- create_internalvar_type_lazy ("_thread", thread_id_make_value);
+ create_internalvar_type_lazy ("_thread", &thread_funcs, NULL);
+
+ observer_attach_thread_ptid_changed (restore_current_thread_ptid_changed);
}