/* Multi-process/thread control for GDB, the GNU debugger.
- Copyright (C) 1986-2017 Free Software Foundation, Inc.
+ Copyright (C) 1986-2018 Free Software Foundation, Inc.
Contributed by Lynx Real-Time Systems, Inc. Los Gatos, CA.
#include "command.h"
#include "gdbcmd.h"
#include "regcache.h"
-#include "gdb.h"
#include "btrace.h"
#include <ctype.h>
#include <sys/types.h>
#include <signal.h>
#include "ui-out.h"
-#include "observer.h"
+#include "observable.h"
#include "annotate.h"
#include "cli/cli-decode.h"
#include "gdb_regex.h"
#include "thread-fsm.h"
#include "tid-parse.h"
#include <algorithm>
+#include "common/gdb_optional.h"
/* Definition of struct thread_info exported to gdbthread.h. */
-/* Prototypes for exported functions. */
-
-void _initialize_thread (void);
-
/* Prototypes for local functions. */
struct thread_info *thread_list = NULL;
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);
/* RAII type used to increase / decrease the refcount of each thread
in a given list of threads. */
int
thread_has_single_step_breakpoint_here (struct thread_info *tp,
- struct address_space *aspace,
+ const address_space *aspace,
CORE_ADDR addr)
{
struct breakpoint *ss_bps = tp->control.single_step_breakpoints;
if (tp->state != THREAD_EXITED)
{
- observer_notify_thread_exit (tp, silent);
+ gdb::observers::thread_exit.notify (tp, silent);
/* Tag it as exited. */
tp->state = THREAD_EXITED;
tp->state = THREAD_STOPPED;
switch_to_thread (ptid);
- observer_notify_new_thread (tp);
+ gdb::observers::new_thread.notify (tp);
/* All done. */
return tp;
}
tp = new_thread (inf, ptid);
- observer_notify_new_thread (tp);
+ gdb::observers::new_thread.notify (tp);
return tp;
}
struct thread_info *
-add_thread_with_info (ptid_t ptid, struct private_thread_info *priv)
+add_thread_with_info (ptid_t ptid, private_thread_info *priv)
{
struct thread_info *result = add_thread_silent (ptid);
- result->priv = priv;
+ result->priv.reset (priv);
if (print_thread_events)
printf_unfiltered (_("[New %s]\n"), target_pid_to_str (ptid));
return add_thread_with_info (ptid, NULL);
}
+private_thread_info::~private_thread_info () = default;
+
thread_info::thread_info (struct inferior *inf_, ptid_t ptid_)
: ptid (ptid_), inf (inf_)
{
thread_info::~thread_info ()
{
- if (this->priv)
- {
- if (this->private_dtor)
- this->private_dtor (this->priv);
- else
- xfree (this->priv);
- }
-
xfree (this->name);
}
}
/* Find a thread_info by matching PTID. */
+
struct thread_info *
find_thread_ptid (ptid_t ptid)
{
return NULL;
}
+/* See gdbthread.h. */
+
+struct thread_info *
+find_thread_by_handle (struct value *thread_handle, struct inferior *inf)
+{
+ return target_thread_handle_to_thread_info
+ (value_contents_all (thread_handle),
+ TYPE_LENGTH (value_type (thread_handle)),
+ inf);
+}
+
/*
* Thread iterator function.
*
return tp_executing;
}
-/* Print a list of thread ids currently known, and the total number of
- threads. To be used from within catch_errors. */
-static int
-do_captured_list_thread_ids (struct ui_out *uiout, void *arg)
-{
- struct thread_info *tp;
- int num = 0;
- int current_thread = -1;
-
- update_thread_list ();
-
- {
- ui_out_emit_tuple tuple_emitter (uiout, "thread-ids");
-
- for (tp = thread_list; tp; tp = tp->next)
- {
- if (tp->state == THREAD_EXITED)
- continue;
-
- if (tp->ptid == inferior_ptid)
- current_thread = tp->global_num;
-
- num++;
- uiout->field_int ("thread-id", tp->global_num);
- }
- }
-
- if (current_thread != -1)
- uiout->field_int ("current-thread-id", current_thread);
- uiout->field_int ("number-of-threads", num);
- return GDB_RC_OK;
-}
-
-/* Official gdblib interface function to get a list of thread ids and
- the total number. */
-enum gdb_rc
-gdb_list_thread_ids (struct ui_out *uiout, char **error_message)
-{
- if (catch_exceptions_with_msg (uiout, do_captured_list_thread_ids, NULL,
- error_message, RETURN_MASK_ALL) < 0)
- return GDB_RC_FAIL;
- return GDB_RC_OK;
-}
-
/* Return true if TP is an active thread. */
static int
thread_alive (struct thread_info *tp)
}
}
-/* Disable storing stack temporaries for the thread whose id is
- stored in DATA. */
-
-static void
-disable_thread_stack_temporaries (void *data)
-{
- ptid_t *pd = (ptid_t *) 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 = XNEW (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
+/* Return true value if stack temporaies are enabled for the thread
with id PTID. */
-int
+bool
thread_stack_temporaries_enabled_p (ptid_t ptid)
{
struct thread_info *tp = find_thread_ptid (ptid);
if (tp == NULL)
- return 0;
+ return false;
else
return tp->stack_temporaries_enabled;
}
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);
+ tp->stack_temporaries.push_back (v);
}
-/* Return 1 if VAL is among the stack temporaries of the thread
- with id PTID. Return 0 otherwise. */
+/* Return true if VAL is among the stack temporaries of the thread
+ with id PTID. Return false otherwise. */
-int
+bool
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;
- }
+ for (struct value *v : tp->stack_temporaries)
+ if (v == val)
+ return true;
- return 0;
+ return false;
}
/* Return the last of the stack temporaries for thread with id PTID.
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);
+ if (!tp->stack_temporaries.empty ())
+ lastval = tp->stack_temporaries.back ();
return lastval;
}
tp = find_thread_ptid (old_ptid);
tp->ptid = new_ptid;
- observer_notify_thread_ptid_changed (old_ptid, new_ptid);
+ gdb::observers::thread_ptid_changed.notify (old_ptid, new_ptid);
}
/* See gdbthread.h. */
any_started = 1;
}
if (any_started)
- observer_notify_target_resumed (ptid);
+ gdb::observers::target_resumed.notify (ptid);
}
static int
/* Call the stop requested observer so other components of GDB can
react to this request. */
if (stop)
- observer_notify_thread_stop_requested (ptid);
+ gdb::observers::thread_stop_requested.notify (ptid);
}
void
}
if (any_started)
- observer_notify_target_resumed (ptid);
-}
-
-void
-finish_thread_state_cleanup (void *arg)
-{
- ptid_t *ptid_p = (ptid_t *) arg;
-
- gdb_assert (arg);
-
- finish_thread_state (*ptid_p);
+ gdb::observers::target_resumed.notify (ptid);
}
/* See gdbthread.h. */
thread ids. */
static void
-print_thread_info_1 (struct ui_out *uiout, char *requested_threads,
+print_thread_info_1 (struct ui_out *uiout, const char *requested_threads,
int global_ids, int pid,
int show_global_ids)
{
update_thread_list ();
current_ptid = inferior_ptid;
- struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
-
- /* For backward compatibility, we make a list for MI. A table is
- preferable for the CLI, though, because it shows table
- headers. */
- if (uiout->is_mi_like_p ())
- make_cleanup_ui_out_list_begin_end (uiout, "threads");
- else
- {
- int n_threads = 0;
-
- for (tp = thread_list; tp; tp = tp->next)
- {
- if (!should_print_thread (requested_threads, default_inf_num,
- global_ids, pid, tp))
- continue;
+ {
+ /* For backward compatibility, we make a list for MI. A table is
+ preferable for the CLI, though, because it shows table
+ headers. */
+ gdb::optional<ui_out_emit_list> list_emitter;
+ gdb::optional<ui_out_emit_table> table_emitter;
+
+ if (uiout->is_mi_like_p ())
+ list_emitter.emplace (uiout, "threads");
+ else
+ {
+ int n_threads = 0;
- ++n_threads;
- }
+ for (tp = thread_list; tp; tp = tp->next)
+ {
+ if (!should_print_thread (requested_threads, default_inf_num,
+ global_ids, pid, tp))
+ continue;
- if (n_threads == 0)
- {
- if (requested_threads == NULL || *requested_threads == '\0')
- uiout->message (_("No threads.\n"));
- else
- uiout->message (_("No threads match '%s'.\n"),
- requested_threads);
- do_cleanups (old_chain);
- return;
- }
+ ++n_threads;
+ }
- if (show_global_ids || uiout->is_mi_like_p ())
- make_cleanup_ui_out_table_begin_end (uiout, 5, n_threads, "threads");
- else
- make_cleanup_ui_out_table_begin_end (uiout, 4, n_threads, "threads");
+ if (n_threads == 0)
+ {
+ if (requested_threads == NULL || *requested_threads == '\0')
+ uiout->message (_("No threads.\n"));
+ else
+ uiout->message (_("No threads match '%s'.\n"),
+ requested_threads);
+ return;
+ }
- uiout->table_header (1, ui_left, "current", "");
+ table_emitter.emplace (uiout, show_global_ids ? 5 : 4,
+ n_threads, "threads");
- if (!uiout->is_mi_like_p ())
+ uiout->table_header (1, ui_left, "current", "");
uiout->table_header (4, ui_left, "id-in-tg", "Id");
- if (show_global_ids || uiout->is_mi_like_p ())
- uiout->table_header (4, ui_left, "id", "GId");
- uiout->table_header (17, ui_left, "target-id", "Target Id");
- uiout->table_header (1, ui_left, "frame", "Frame");
- uiout->table_body ();
- }
+ if (show_global_ids)
+ uiout->table_header (4, ui_left, "id", "GId");
+ uiout->table_header (17, ui_left, "target-id", "Target Id");
+ uiout->table_header (1, ui_left, "frame", "Frame");
+ uiout->table_body ();
+ }
- /* We'll be switching threads temporarily. */
- {
+ /* We'll be switching threads temporarily. */
scoped_restore_current_thread restore_thread;
ALL_THREADS_BY_INFERIOR (inf, tp)
uiout->field_string ("current", "*");
else
uiout->field_skip ("current");
- }
- if (!uiout->is_mi_like_p ())
- uiout->field_string ("id-in-tg", print_thread_id (tp));
+ uiout->field_string ("id-in-tg", print_thread_id (tp));
+ }
if (show_global_ids || uiout->is_mi_like_p ())
uiout->field_int ("id", tp->global_num);
core = target_core_of_thread (tp->ptid);
if (uiout->is_mi_like_p () && core != -1)
uiout->field_int ("core", core);
- }
+ }
/* This end scope restores the current thread and the frame
- selected before the "info threads" command. */
+ selected before the "info threads" command, and it finishes the
+ ui-out list or table. */
}
- do_cleanups (old_chain);
-
if (pid == -1 && requested_threads == NULL)
{
if (uiout->is_mi_like_p ()
effects info-threads command would be nicer. */
static void
-info_threads_command (char *arg, int from_tty)
+info_threads_command (const char *arg, int from_tty)
{
int show_global_ids = 0;
thread apply all p x/i $pc Apply x/i $pc cmd to all threads. */
static void
-thread_apply_all_command (char *cmd, int from_tty)
+thread_apply_all_command (const char *cmd, int from_tty)
{
tp_array_compar_ascending = false;
if (cmd != NULL
update_thread_list ();
- /* Save a copy of the command in case it is clobbered by
- execute_command. */
- std::string saved_cmd = cmd;
-
int tc = live_threads_count ();
if (tc != 0)
{
printf_filtered (_("\nThread %s (%s):\n"),
print_thread_id (thr),
target_pid_to_str (inferior_ptid));
- execute_command (cmd, from_tty);
- /* Restore exact command used previously. */
- strcpy (cmd, saved_cmd.c_str ());
+ execute_command (cmd, from_tty);
}
}
}
/* Implementation of the "thread apply" command. */
static void
-thread_apply_command (char *tidlist, int from_tty)
+thread_apply_command (const char *tidlist, int from_tty)
{
- char *cmd = NULL;
+ const char *cmd = NULL;
tid_range_parser parser;
if (tidlist == NULL || *tidlist == '\000')
if (!parser.get_tid_range (&inf_num, &thr_start, &thr_end))
{
- cmd = (char *) parser.cur_tok ();
+ cmd = parser.cur_tok ();
break;
}
}
if (tidlist == cmd || !isalpha (cmd[0]))
invalid_thread_id_error (cmd);
- /* Save a copy of the command in case it is clobbered by
- execute_command. */
- std::string saved_cmd = cmd;
-
scoped_restore_current_thread restore_thread;
parser.init (tidlist, current_inferior ()->num);
printf_filtered (_("\nThread %s (%s):\n"), print_thread_id (tp),
target_pid_to_str (inferior_ptid));
execute_command (cmd, from_tty);
-
- /* Restore exact command used previously. */
- strcpy (cmd, saved_cmd.c_str ());
}
}
if prefix of arg is `apply'. */
void
-thread_command (char *tidstr, int from_tty)
+thread_command (const char *tidstr, int from_tty)
{
if (tidstr == NULL)
{
else
{
ptid_t previous_ptid = inferior_ptid;
- enum gdb_rc result;
- result = gdb_thread_select (current_uiout, tidstr, NULL);
-
- /* If thread switch did not succeed don't notify or print. */
- if (result == GDB_RC_FAIL)
- return;
+ thread_select (tidstr, parse_thread_id (tidstr, NULL));
/* Print if the thread has not changed, otherwise an event will
be sent. */
}
else
{
- observer_notify_user_selected_context_changed (USER_SELECTED_THREAD
- | USER_SELECTED_FRAME);
+ gdb::observers::user_selected_context_changed.notify
+ (USER_SELECTED_THREAD | USER_SELECTED_FRAME);
}
}
}
/* Implementation of `thread name'. */
static void
-thread_name_command (char *arg, int from_tty)
+thread_name_command (const char *arg, int from_tty)
{
struct thread_info *info;
/* Find thread ids with a name, target pid, or extra info matching ARG. */
static void
-thread_find_command (char *arg, int from_tty)
+thread_find_command (const char *arg, int from_tty)
{
struct thread_info *tp;
const char *tmp;
value);
}
-static int
-do_captured_thread_select (struct ui_out *uiout, void *tidstr_v)
-{
- const char *tidstr = (const char *) tidstr_v;
- struct thread_info *tp;
-
- if (uiout->is_mi_like_p ())
- {
- int num = value_as_long (parse_and_eval (tidstr));
-
- tp = find_thread_global_id (num);
- if (tp == NULL)
- error (_("Thread ID %d not known."), num);
- }
- else
- {
- tp = parse_thread_id (tidstr, NULL);
- gdb_assert (tp != NULL);
- }
+/* See gdbthread.h. */
+void
+thread_select (const char *tidstr, thread_info *tp)
+{
if (!thread_alive (tp))
error (_("Thread ID %s has terminated."), tidstr);
/* Since the current thread may have changed, see if there is any
exited thread we can now delete. */
prune_threads ();
-
- return GDB_RC_OK;
}
/* Print thread and frame switch command response. */
user_selected_what selection)
{
struct thread_info *tp = inferior_thread ();
- struct inferior *inf = current_inferior ();
if (selection & USER_SELECTED_THREAD)
{
}
}
-enum gdb_rc
-gdb_thread_select (struct ui_out *uiout, char *tidstr, char **error_message)
-{
- if (catch_exceptions_with_msg (uiout, do_captured_thread_select, tidstr,
- error_message, RETURN_MASK_ALL) < 0)
- return GDB_RC_FAIL;
- return GDB_RC_OK;
-}
-
/* Update the 'threads_executing' global based on the threads we know
about right now. */