#include "command.h"
#include "gdbcmd.h"
#include "regcache.h"
-#include "gdb.h"
#include "btrace.h"
#include <ctype.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;
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. */
}
/* 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;
- struct cleanup *cleanup_chain;
- int current_thread = -1;
-
- update_thread_list ();
-
- cleanup_chain = make_cleanup_ui_out_tuple_begin_end (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);
- }
-
- do_cleanups (cleanup_chain);
-
- 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)
{
struct thread_info *tp;
ptid_t current_ptid;
- struct cleanup *old_chain;
const char *extra_info, *name, *target_id;
struct inferior *inf;
int default_inf_num = current_inferior ()->num;
update_thread_list ();
current_ptid = inferior_ptid;
- /* We'll be switching threads temporarily. */
- old_chain = make_cleanup_restore_current_thread ();
-
- /* 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 ();
- }
-
- ALL_THREADS_BY_INFERIOR (inf, tp)
- {
- struct cleanup *chain2;
- int core;
+ 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 ();
+ }
- if (!should_print_thread (requested_threads, default_inf_num,
- global_ids, pid, tp))
- continue;
+ /* We'll be switching threads temporarily. */
+ scoped_restore_current_thread restore_thread;
- chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+ ALL_THREADS_BY_INFERIOR (inf, tp)
+ {
+ int core;
- if (!uiout->is_mi_like_p ())
- {
- if (tp->ptid == current_ptid)
- uiout->field_string ("current", "*");
- else
- uiout->field_skip ("current");
- }
+ if (!should_print_thread (requested_threads, default_inf_num,
+ global_ids, pid, tp))
+ continue;
- if (!uiout->is_mi_like_p ())
- uiout->field_string ("id-in-tg", print_thread_id (tp));
+ ui_out_emit_tuple tuple_emitter (uiout, NULL);
- if (show_global_ids || uiout->is_mi_like_p ())
- uiout->field_int ("id", tp->global_num);
+ if (!uiout->is_mi_like_p ())
+ {
+ if (tp->ptid == current_ptid)
+ uiout->field_string ("current", "*");
+ else
+ uiout->field_skip ("current");
- /* For the CLI, we stuff everything into the target-id field.
- This is a gross hack to make the output come out looking
- correct. The underlying problem here is that ui-out has no
- way to specify that a field's space allocation should be
- shared by several fields. For MI, we do the right thing
- instead. */
+ uiout->field_string ("id-in-tg", print_thread_id (tp));
+ }
- target_id = target_pid_to_str (tp->ptid);
- extra_info = target_extra_thread_info (tp);
- name = tp->name ? tp->name : target_thread_name (tp);
+ if (show_global_ids || uiout->is_mi_like_p ())
+ uiout->field_int ("id", tp->global_num);
- if (uiout->is_mi_like_p ())
- {
- uiout->field_string ("target-id", target_id);
- if (extra_info)
- uiout->field_string ("details", extra_info);
- if (name)
- uiout->field_string ("name", name);
- }
- else
- {
- struct cleanup *str_cleanup;
- char *contents;
-
- if (extra_info && name)
- contents = xstrprintf ("%s \"%s\" (%s)", target_id,
- name, extra_info);
- else if (extra_info)
- contents = xstrprintf ("%s (%s)", target_id, extra_info);
- else if (name)
- contents = xstrprintf ("%s \"%s\"", target_id, name);
- else
- contents = xstrdup (target_id);
- str_cleanup = make_cleanup (xfree, contents);
+ /* For the CLI, we stuff everything into the target-id field.
+ This is a gross hack to make the output come out looking
+ correct. The underlying problem here is that ui-out has no
+ way to specify that a field's space allocation should be
+ shared by several fields. For MI, we do the right thing
+ instead. */
- uiout->field_string ("target-id", contents);
- do_cleanups (str_cleanup);
- }
+ target_id = target_pid_to_str (tp->ptid);
+ extra_info = target_extra_thread_info (tp);
+ name = tp->name ? tp->name : target_thread_name (tp);
- if (tp->state == THREAD_RUNNING)
- uiout->text ("(running)\n");
- else
- {
- /* The switch below puts us at the top of the stack (leaf
- frame). */
- switch_to_thread (tp->ptid);
- print_stack_frame (get_selected_frame (NULL),
- /* For MI output, print frame level. */
- uiout->is_mi_like_p (),
- LOCATION, 0);
- }
+ if (uiout->is_mi_like_p ())
+ {
+ uiout->field_string ("target-id", target_id);
+ if (extra_info)
+ uiout->field_string ("details", extra_info);
+ if (name)
+ uiout->field_string ("name", name);
+ }
+ else
+ {
+ std::string contents;
+
+ if (extra_info && name)
+ contents = string_printf ("%s \"%s\" (%s)", target_id,
+ name, extra_info);
+ else if (extra_info)
+ contents = string_printf ("%s (%s)", target_id, extra_info);
+ else if (name)
+ contents = string_printf ("%s \"%s\"", target_id, name);
+ else
+ contents = target_id;
+
+ uiout->field_string ("target-id", contents.c_str ());
+ }
- if (uiout->is_mi_like_p ())
- {
- const char *state = "stopped";
+ if (tp->state == THREAD_RUNNING)
+ uiout->text ("(running)\n");
+ else
+ {
+ /* The switch below puts us at the top of the stack (leaf
+ frame). */
+ switch_to_thread (tp->ptid);
+ print_stack_frame (get_selected_frame (NULL),
+ /* For MI output, print frame level. */
+ uiout->is_mi_like_p (),
+ LOCATION, 0);
+ }
- if (tp->state == THREAD_RUNNING)
- state = "running";
- uiout->field_string ("state", state);
- }
+ if (uiout->is_mi_like_p ())
+ {
+ const char *state = "stopped";
- core = target_core_of_thread (tp->ptid);
- if (uiout->is_mi_like_p () && core != -1)
- uiout->field_int ("core", core);
+ if (tp->state == THREAD_RUNNING)
+ state = "running";
+ uiout->field_string ("state", state);
+ }
- do_cleanups (chain2);
- }
+ core = target_core_of_thread (tp->ptid);
+ if (uiout->is_mi_like_p () && core != -1)
+ uiout->field_int ("core", core);
+ }
- /* Restores the current thread and the frame selected before
- the "info threads" command. */
- do_cleanups (old_chain);
+ /* This end scope restores the current thread and the frame
+ selected before the "info threads" command, and it finishes the
+ ui-out list or table. */
+ }
if (pid == -1 && requested_threads == NULL)
{
}
}
-/* Data used by the cleanup installed by
- 'make_cleanup_restore_current_thread'. */
-
-struct current_thread_cleanup
-{
- thread_info *thread;
- struct frame_id selected_frame_id;
- int selected_frame_level;
- int was_stopped;
- inferior *inf;
-};
-
-static void
-do_restore_current_thread_cleanup (void *arg)
+scoped_restore_current_thread::~scoped_restore_current_thread ()
{
- struct current_thread_cleanup *old = (struct current_thread_cleanup *) arg;
-
/* If an entry of thread_info was previously selected, it won't be
deleted because we've increased its refcount. The thread represented
by this thread_info entry may have already exited (due to normal exit,
detach, etc), so the thread_info.state is THREAD_EXITED. */
- if (old->thread != NULL
+ if (m_thread != NULL
/* If the previously selected thread belonged to a process that has
in the mean time exited (or killed, detached, etc.), then don't revert
back to it, but instead simply drop back to no thread selected. */
- && old->inf->pid != 0)
- switch_to_thread (old->thread);
+ && m_inf->pid != 0)
+ switch_to_thread (m_thread);
else
{
switch_to_no_thread ();
- set_current_inferior (old->inf);
+ set_current_inferior (m_inf);
}
/* The running state of the originally selected thread may have
changed, so we have to recheck it here. */
if (inferior_ptid != null_ptid
- && old->was_stopped
+ && m_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 = (struct current_thread_cleanup *) arg;
-
- if (old->thread != NULL)
- old->thread->decref ();
+ restore_selected_frame (m_selected_frame_id, m_selected_frame_level);
- old->inf->decref ();
- xfree (old);
+ if (m_thread != NULL)
+ m_thread->decref ();
+ m_inf->decref ();
}
-struct cleanup *
-make_cleanup_restore_current_thread (void)
+scoped_restore_current_thread::scoped_restore_current_thread ()
{
- struct current_thread_cleanup *old = XNEW (struct current_thread_cleanup);
-
- old->thread = NULL;
- old->inf = current_inferior ();
+ m_thread = NULL;
+ m_inf = current_inferior ();
if (inferior_ptid != null_ptid)
{
+ thread_info *tp = find_thread_ptid (inferior_ptid);
struct frame_info *frame;
- old->was_stopped = is_stopped (inferior_ptid);
- if (old->was_stopped
+ gdb_assert (tp != NULL);
+
+ m_was_stopped = tp->state == THREAD_STOPPED;
+ if (m_was_stopped
&& target_has_registers
&& target_has_stack
&& target_has_memory)
else
frame = NULL;
- old->selected_frame_id = get_frame_id (frame);
- old->selected_frame_level = frame_relative_level (frame);
+ m_selected_frame_id = get_frame_id (frame);
+ m_selected_frame_level = frame_relative_level (frame);
- struct thread_info *tp = find_thread_ptid (inferior_ptid);
-
- if (tp)
- tp->incref ();
- old->thread = tp;
+ tp->incref ();
+ m_thread = tp;
}
- old->inf->incref ();
-
- return make_cleanup_dtor (do_restore_current_thread_cleanup, old,
- restore_current_thread_cleanup_dtor);
+ m_inf->incref ();
}
/* See gdbthread.h. */
static void
thread_apply_all_command (char *cmd, int from_tty)
{
- struct cleanup *old_chain;
- char *saved_cmd;
-
tp_array_compar_ascending = false;
if (cmd != NULL
&& check_for_argument (&cmd, "-ascending", strlen ("-ascending")))
update_thread_list ();
- 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);
- make_cleanup (xfree, saved_cmd);
+ std::string saved_cmd = cmd;
int tc = live_threads_count ();
if (tc != 0)
std::sort (thr_list_cpy.begin (), thr_list_cpy.end (), tp_array_compar);
+ scoped_restore_current_thread restore_thread;
+
for (thread_info *thr : thr_list_cpy)
if (thread_alive (thr))
{
execute_command (cmd, from_tty);
/* Restore exact command used previously. */
- strcpy (cmd, saved_cmd);
+ strcpy (cmd, saved_cmd.c_str ());
}
}
-
- do_cleanups (old_chain);
}
/* 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;
- struct cleanup *old_chain;
- char *saved_cmd;
tid_range_parser parser;
if (tidlist == NULL || *tidlist == '\000')
/* Save a copy of the command in case it is clobbered by
execute_command. */
- saved_cmd = xstrdup (cmd);
- old_chain = make_cleanup (xfree, saved_cmd);
+ std::string saved_cmd = cmd;
- make_cleanup_restore_current_thread ();
+ scoped_restore_current_thread restore_thread;
parser.init (tidlist, current_inferior ()->num);
while (!parser.finished () && parser.cur_tok () < cmd)
execute_command (cmd, from_tty);
/* Restore exact command used previously. */
- strcpy (cmd, saved_cmd);
+ strcpy (cmd, saved_cmd.c_str ());
}
-
- do_cleanups (old_chain);
}
/* Switch to the specified thread. Will dispatch off to thread_apply_command
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. */
/* 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. */
}
}
-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. */