forked from Imagelibrary/binutils-gdb
Compare commits
1 Commits
master
...
users/sima
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44c04ee9bf |
@@ -1189,6 +1189,7 @@ SFILES = \
|
||||
ui-file.h \
|
||||
ui-out.c \
|
||||
user-regs.c \
|
||||
user-selection.c \
|
||||
utils.c \
|
||||
valarith.c \
|
||||
valops.c \
|
||||
@@ -1791,6 +1792,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
|
||||
ui-file.o \
|
||||
ui-out.o \
|
||||
user-regs.o \
|
||||
user-selection.o \
|
||||
utils.o \
|
||||
utils-selftests.o \
|
||||
valarith.o \
|
||||
|
||||
@@ -32,6 +32,12 @@ public:
|
||||
|
||||
ui_file *set_stream (ui_file *stream);
|
||||
|
||||
bool suppress_output ()
|
||||
{ return m_suppress_output; }
|
||||
|
||||
void suppress_output (bool val)
|
||||
{ m_suppress_output = val; }
|
||||
|
||||
protected:
|
||||
|
||||
virtual void do_table_begin (int nbrofcols, int nr_rows,
|
||||
@@ -62,9 +68,6 @@ protected:
|
||||
virtual void do_flush () override;
|
||||
virtual void do_redirect (struct ui_file *outstream) override;
|
||||
|
||||
bool suppress_output ()
|
||||
{ return m_suppress_output; }
|
||||
|
||||
private:
|
||||
|
||||
void field_separator ();
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "observer.h"
|
||||
#include "gdbthread.h"
|
||||
#include "thread-fsm.h"
|
||||
#include "user-selection.h"
|
||||
|
||||
cli_interp_base::cli_interp_base (const char *name)
|
||||
: interp (name)
|
||||
@@ -250,15 +251,15 @@ cli_on_command_error (void)
|
||||
/* Observer for the user_selected_context_changed notification. */
|
||||
|
||||
static void
|
||||
cli_on_user_selected_context_changed (user_selected_what selection)
|
||||
cli_on_global_user_selection_changed (user_selection *us,
|
||||
user_selected_what selection)
|
||||
{
|
||||
struct thread_info *tp;
|
||||
|
||||
/* This event is suppressed. */
|
||||
if (cli_suppress_notification.user_selected_context)
|
||||
return;
|
||||
|
||||
tp = find_thread_ptid (inferior_ptid);
|
||||
struct thread_info *tp = us->thread ();
|
||||
struct inferior *inf = us->inferior ();
|
||||
|
||||
SWITCH_THRU_ALL_UIS ()
|
||||
{
|
||||
@@ -268,11 +269,11 @@ cli_on_user_selected_context_changed (user_selected_what selection)
|
||||
continue;
|
||||
|
||||
if (selection & USER_SELECTED_INFERIOR)
|
||||
print_selected_inferior (cli->cli_uiout);
|
||||
print_selected_inferior (cli->cli_uiout, inf);
|
||||
|
||||
if (tp != NULL
|
||||
&& ((selection & (USER_SELECTED_THREAD | USER_SELECTED_FRAME))))
|
||||
print_selected_thread_frame (cli->cli_uiout, selection);
|
||||
print_selected_thread_frame (cli->cli_uiout, us, selection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,6 +475,6 @@ _initialize_cli_interp (void)
|
||||
observer_attach_no_history (cli_on_no_history);
|
||||
observer_attach_sync_execution_done (cli_on_sync_execution_done);
|
||||
observer_attach_command_error (cli_on_command_error);
|
||||
observer_attach_user_selected_context_changed
|
||||
(cli_on_user_selected_context_changed);
|
||||
observer_attach_global_user_selection_changed
|
||||
(cli_on_global_user_selection_changed);
|
||||
}
|
||||
|
||||
@@ -307,7 +307,8 @@ This observer is used for internal testing. Do not use.
|
||||
See testsuite/gdb.gdb/observer.exp.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun void user_selected_context_changed (user_selected_what @var{selection})
|
||||
The user-selected inferior, thread and/or frame has changed. The user_select_what
|
||||
flag specifies if the inferior, thread and/or frame has changed.
|
||||
@deftypefun void global_user_selection_changed (user_selection *@var{us}, user_selected_what @var{selection})
|
||||
The user-selected inferior, thread and/or frame in US has changed. The
|
||||
user_select_what flag specifies if the inferior, thread and/or frame has
|
||||
changed.
|
||||
@end deftypefun
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "buffer.h"
|
||||
#include "ser-event.h"
|
||||
#include "gdb_select.h"
|
||||
#include "user-selection.h"
|
||||
|
||||
/* readline include files. */
|
||||
#include "readline/readline.h"
|
||||
@@ -582,6 +583,9 @@ command_handler (char *command)
|
||||
|
||||
scoped_command_stats stat_reporter (true);
|
||||
|
||||
/* Before executing the command, apply the user selection to the gdb core. */
|
||||
apply_global_user_selection ();
|
||||
|
||||
/* Do not execute commented lines. */
|
||||
for (c = command; *c == ' ' || *c == '\t'; c++)
|
||||
;
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include "tracepoint.h"
|
||||
#include "hashtab.h"
|
||||
#include "valprint.h"
|
||||
#include "user-selection.h"
|
||||
|
||||
/* The sentinel frame terminates the innermost end of the frame chain.
|
||||
If unwound, it returns the information needed to construct an
|
||||
@@ -1791,6 +1792,7 @@ reinit_frame_cache (void)
|
||||
|
||||
sentinel_frame = NULL; /* Invalidate cache */
|
||||
select_frame (NULL);
|
||||
global_user_selection ()->select_frame (NULL, false);
|
||||
frame_stash_invalidate ();
|
||||
if (frame_debug)
|
||||
fprintf_unfiltered (gdb_stdlog, "{ reinit_frame_cache () }\n");
|
||||
|
||||
@@ -79,6 +79,8 @@ struct gdbarch;
|
||||
struct ui_file;
|
||||
struct ui_out;
|
||||
|
||||
#define INVALID_FRAME_LEVEL -1
|
||||
|
||||
/* Status of a given frame's stack. */
|
||||
|
||||
enum frame_id_stack_status
|
||||
|
||||
@@ -47,10 +47,6 @@ enum gdb_rc {
|
||||
enum gdb_rc gdb_breakpoint_query (struct ui_out *uiout, int bnum,
|
||||
char **error_message);
|
||||
|
||||
/* Switch thread and print notification. */
|
||||
enum gdb_rc gdb_thread_select (struct ui_out *uiout, char *tidstr,
|
||||
char **error_message);
|
||||
|
||||
/* Print a list of known thread ids. */
|
||||
enum gdb_rc gdb_list_thread_ids (struct ui_out *uiout,
|
||||
char **error_message);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#define GDBTHREAD_H
|
||||
|
||||
struct symtab;
|
||||
class user_selection;
|
||||
|
||||
#include "breakpoint.h"
|
||||
#include "frame.h"
|
||||
@@ -253,7 +254,13 @@ struct thread_info
|
||||
/* If this is > 0, then it means there's code out there that relies
|
||||
on this thread being listed. Don't delete it from the lists even
|
||||
if we detect it exiting. */
|
||||
int refcount;
|
||||
int refcount_;
|
||||
|
||||
void get ()
|
||||
{ refcount_++; }
|
||||
|
||||
void put ()
|
||||
{ refcount_--; }
|
||||
|
||||
/* State of GDB control of inferior thread execution.
|
||||
See `struct thread_control_state'. */
|
||||
@@ -440,8 +447,9 @@ void thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid);
|
||||
|
||||
/* Iterator function to call a user-provided callback function
|
||||
once for each known thread. */
|
||||
typedef int (*thread_callback_func) (struct thread_info *, void *);
|
||||
extern struct thread_info *iterate_over_threads (thread_callback_func, void *);
|
||||
typedef std::function<int(struct thread_info *, void*)> thread_callback_func;
|
||||
extern struct thread_info *iterate_over_threads (thread_callback_func callback,
|
||||
void *data = nullptr);
|
||||
|
||||
/* Traverse all threads. */
|
||||
#define ALL_THREADS(T) \
|
||||
@@ -469,6 +477,9 @@ extern struct thread_info *iterate_over_threads (thread_callback_func, void *);
|
||||
|
||||
extern int thread_count (void);
|
||||
|
||||
/* Change the user-selected thread. */
|
||||
extern bool thread_select (const char *tidstr, bool tid_is_qualified);
|
||||
|
||||
/* Switch from one thread to another. Also sets the STOP_PC
|
||||
global. */
|
||||
extern void switch_to_thread (ptid_t ptid);
|
||||
@@ -508,15 +519,17 @@ extern void set_stop_requested (ptid_t ptid, int stop);
|
||||
The latter also returns true on exited threads, most likelly not
|
||||
what you want. */
|
||||
|
||||
/* Reports if in the frontend's perpective, thread PTID is running. */
|
||||
/* Reports if in the frontend's perspective, thread PTID is running. */
|
||||
extern int is_running (ptid_t ptid);
|
||||
|
||||
/* Is this thread listed, but known to have exited? We keep it listed
|
||||
(but not visible) until it's safe to delete. */
|
||||
extern int is_exited (ptid_t ptid);
|
||||
extern bool is_exited (struct thread_info *thread);
|
||||
|
||||
/* In the frontend's perpective, is this thread stopped? */
|
||||
/* In the frontend's perspective, is this thread stopped? */
|
||||
extern int is_stopped (ptid_t ptid);
|
||||
extern bool is_stopped (struct thread_info *thread);
|
||||
|
||||
/* Marks thread PTID as executing, or not. If PTID is minus_one_ptid,
|
||||
marks all threads.
|
||||
@@ -636,7 +649,8 @@ extern int show_thread_that_caused_stop (void);
|
||||
|
||||
/* Print the message for a thread or/and frame selected. */
|
||||
extern void print_selected_thread_frame (struct ui_out *uiout,
|
||||
user_selected_what selection);
|
||||
user_selection *us,
|
||||
user_selected_what selection);
|
||||
|
||||
extern struct thread_info *thread_list;
|
||||
|
||||
|
||||
@@ -1147,7 +1147,7 @@ call_function_by_hand_dummy (struct value *function,
|
||||
observer_notify_inferior_call_post (call_thread_ptid, funaddr);
|
||||
|
||||
tp = find_thread_ptid (call_thread_ptid);
|
||||
if (tp != NULL)
|
||||
if (tp != NULL && !is_exited (tp))
|
||||
{
|
||||
/* The FSM should still be the same. */
|
||||
gdb_assert (tp->thread_fsm == &sm->thread_fsm);
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "arch-utils.h"
|
||||
#include "target-descriptions.h"
|
||||
#include "readline/tilde.h"
|
||||
#include "user-selection.h"
|
||||
|
||||
void _initialize_inferiors (void);
|
||||
|
||||
@@ -554,9 +555,8 @@ inferior_pid_to_str (int pid)
|
||||
/* See inferior.h. */
|
||||
|
||||
void
|
||||
print_selected_inferior (struct ui_out *uiout)
|
||||
print_selected_inferior (struct ui_out *uiout, struct inferior *inf)
|
||||
{
|
||||
struct inferior *inf = current_inferior ();
|
||||
const char *filename = inf->pspace->pspace_exec_filename;
|
||||
|
||||
if (filename == NULL)
|
||||
@@ -566,8 +566,7 @@ print_selected_inferior (struct ui_out *uiout)
|
||||
inf->num, inferior_pid_to_str (inf->pid), filename);
|
||||
}
|
||||
|
||||
/* Prints the list of inferiors and their details on UIOUT. This is a
|
||||
version of 'info_inferior_command' suitable for use from MI.
|
||||
/* Prints the list of inferiors and their details on UIOUT.
|
||||
|
||||
If REQUESTED_INFERIORS is not NULL, it's a list of GDB ids of the
|
||||
inferiors that should be printed. Otherwise, all inferiors are
|
||||
@@ -579,6 +578,7 @@ print_inferior (struct ui_out *uiout, char *requested_inferiors)
|
||||
struct inferior *inf;
|
||||
struct cleanup *old_chain;
|
||||
int inf_count = 0;
|
||||
struct inferior *selected_inferior = global_user_selection ()->inferior ();
|
||||
|
||||
/* Compute number of inferiors we will print. */
|
||||
for (inf = inferior_list; inf; inf = inf->next)
|
||||
@@ -612,7 +612,7 @@ print_inferior (struct ui_out *uiout, char *requested_inferiors)
|
||||
|
||||
chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
|
||||
|
||||
if (inf == current_inferior ())
|
||||
if (inf == selected_inferior)
|
||||
uiout->field_string ("current", "*");
|
||||
else
|
||||
uiout->field_skip ("current");
|
||||
@@ -732,6 +732,7 @@ inferior_command (char *args, int from_tty)
|
||||
{
|
||||
struct inferior *inf;
|
||||
int num;
|
||||
user_selection *us = global_user_selection ();
|
||||
|
||||
num = parse_and_eval_long (args);
|
||||
|
||||
@@ -739,31 +740,12 @@ inferior_command (char *args, int from_tty)
|
||||
if (inf == NULL)
|
||||
error (_("Inferior ID %d not known."), num);
|
||||
|
||||
if (inf->pid != 0)
|
||||
/* Keep the old behavior of printing "Switching to inferior X" even if it was
|
||||
already the selected inferior. */
|
||||
if (!us->select_inferior (inf, true))
|
||||
{
|
||||
if (inf->pid != ptid_get_pid (inferior_ptid))
|
||||
{
|
||||
struct thread_info *tp;
|
||||
|
||||
tp = any_thread_of_process (inf->pid);
|
||||
if (!tp)
|
||||
error (_("Inferior has no threads."));
|
||||
|
||||
switch_to_thread (tp->ptid);
|
||||
}
|
||||
|
||||
observer_notify_user_selected_context_changed
|
||||
(USER_SELECTED_INFERIOR
|
||||
| USER_SELECTED_THREAD
|
||||
| USER_SELECTED_FRAME);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_current_inferior (inf);
|
||||
switch_to_thread (null_ptid);
|
||||
set_current_program_space (inf->pspace);
|
||||
|
||||
observer_notify_user_selected_context_changed (USER_SELECTED_INFERIOR);
|
||||
print_selected_inferior (current_uiout, us->inferior ());
|
||||
print_selected_thread_frame (current_uiout, us, USER_SELECTED_THREAD | USER_SELECTED_FRAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,6 +765,8 @@ remove_inferior_command (char *args, int from_tty)
|
||||
if (args == NULL || *args == '\0')
|
||||
error (_("Requires an argument (inferior id(s) to remove)"));
|
||||
|
||||
struct inferior *selected_inferior = global_user_selection ()->inferior ();
|
||||
|
||||
number_or_range_parser parser (args);
|
||||
while (!parser.finished ())
|
||||
{
|
||||
@@ -795,7 +779,7 @@ remove_inferior_command (char *args, int from_tty)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inf == current_inferior ())
|
||||
if (inf == selected_inferior)
|
||||
{
|
||||
warning (_("Can not remove current inferior %d."), num);
|
||||
continue;
|
||||
|
||||
@@ -544,7 +544,8 @@ extern int number_of_inferiors (void);
|
||||
|
||||
extern struct inferior *add_inferior_with_spaces (void);
|
||||
|
||||
/* Print the current selected inferior. */
|
||||
extern void print_selected_inferior (struct ui_out *uiout);
|
||||
/* Print the "Switching to inferior X" message using INF. */
|
||||
extern void print_selected_inferior (struct ui_out *uiout,
|
||||
struct inferior *inf);
|
||||
|
||||
#endif /* !defined (INFERIOR_H) */
|
||||
|
||||
49
gdb/infrun.c
49
gdb/infrun.c
@@ -64,6 +64,7 @@
|
||||
#include "event-loop.h"
|
||||
#include "thread-fsm.h"
|
||||
#include "common/enum-flags.h"
|
||||
#include "user-selection.h"
|
||||
|
||||
/* Prototypes for local functions */
|
||||
|
||||
@@ -152,12 +153,6 @@ show_step_stop_if_no_debug (struct ui_file *file, int from_tty,
|
||||
fprintf_filtered (file, _("Mode of the step operation is %s.\n"), value);
|
||||
}
|
||||
|
||||
/* proceed and normal_stop use this to notify the user when the
|
||||
inferior stopped in a different thread than it had been running
|
||||
in. */
|
||||
|
||||
static ptid_t previous_inferior_ptid;
|
||||
|
||||
/* If set (default for legacy reasons), when following a fork, GDB
|
||||
will detach from one of the fork branches, child or parent.
|
||||
Exactly which branch is detached depends on 'set follow-fork-mode'
|
||||
@@ -779,6 +774,11 @@ follow_fork (void)
|
||||
{
|
||||
switch_to_thread (child);
|
||||
|
||||
/* Switch the user-selected thread as well. */
|
||||
struct thread_info *child_tp = find_thread_ptid (child);
|
||||
gdb_assert (child_tp != nullptr);
|
||||
global_user_selection ()->select_thread (child_tp, false);
|
||||
|
||||
/* ... and preserve the stepping state, in case the
|
||||
user was stepping over the fork call. */
|
||||
if (should_resume)
|
||||
@@ -809,7 +809,14 @@ follow_fork (void)
|
||||
follow_inferior_reset_breakpoints ();
|
||||
}
|
||||
else
|
||||
switch_to_thread (parent);
|
||||
{
|
||||
switch_to_thread (parent);
|
||||
|
||||
/* Switch the user-selected thread as well. */
|
||||
struct thread_info *parent_tp = find_thread_ptid (parent);
|
||||
gdb_assert (parent_tp != nullptr);
|
||||
global_user_selection ()->select_thread (parent_tp, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -2999,9 +3006,6 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
|
||||
return;
|
||||
}
|
||||
|
||||
/* We'll update this if & when we switch to a new thread. */
|
||||
previous_inferior_ptid = inferior_ptid;
|
||||
|
||||
regcache = get_current_regcache ();
|
||||
gdbarch = get_regcache_arch (regcache);
|
||||
aspace = get_regcache_aspace (regcache);
|
||||
@@ -3252,8 +3256,6 @@ init_wait_for_inferior (void)
|
||||
|
||||
target_last_wait_ptid = minus_one_ptid;
|
||||
|
||||
previous_inferior_ptid = inferior_ptid;
|
||||
|
||||
/* Discard any skipped inlined frames. */
|
||||
clear_inline_frame_state (minus_one_ptid);
|
||||
}
|
||||
@@ -8157,7 +8159,7 @@ save_stop_context (void)
|
||||
/* Take a strong reference so that the thread can't be deleted
|
||||
yet. */
|
||||
sc->thread = inferior_thread ();
|
||||
sc->thread->refcount++;
|
||||
sc->thread->get ();
|
||||
}
|
||||
else
|
||||
sc->thread = NULL;
|
||||
@@ -8174,7 +8176,8 @@ release_stop_context_cleanup (void *arg)
|
||||
struct stop_context *sc = (struct stop_context *) arg;
|
||||
|
||||
if (sc->thread != NULL)
|
||||
sc->thread->refcount--;
|
||||
sc->thread->put ();
|
||||
|
||||
xfree (sc);
|
||||
}
|
||||
|
||||
@@ -8262,20 +8265,16 @@ normal_stop (void)
|
||||
after this event is handled, so we're not really switching, only
|
||||
informing of a stop. */
|
||||
if (!non_stop
|
||||
&& !ptid_equal (previous_inferior_ptid, inferior_ptid)
|
||||
&& target_has_execution
|
||||
&& last.kind != TARGET_WAITKIND_SIGNALLED
|
||||
&& last.kind != TARGET_WAITKIND_EXITED
|
||||
&& last.kind != TARGET_WAITKIND_NO_RESUMED)
|
||||
{
|
||||
SWITCH_THRU_ALL_UIS ()
|
||||
{
|
||||
target_terminal_ours_for_output ();
|
||||
printf_filtered (_("[Switching to %s]\n"),
|
||||
target_pid_to_str (inferior_ptid));
|
||||
annotate_thread_changed ();
|
||||
}
|
||||
previous_inferior_ptid = inferior_ptid;
|
||||
struct thread_info *thread = find_thread_ptid (inferior_ptid);
|
||||
|
||||
gdb_assert (thread != nullptr);
|
||||
|
||||
global_user_selection ()->select_thread (thread, false);
|
||||
}
|
||||
|
||||
if (last.kind == TARGET_WAITKIND_NO_RESUMED)
|
||||
@@ -8986,7 +8985,7 @@ struct infcall_control_state
|
||||
enum stop_stack_kind stop_stack_dummy;
|
||||
int stopped_by_random_signal;
|
||||
|
||||
/* ID if the selected frame when the inferior function call was made. */
|
||||
/* ID of the selected frame when the inferior function call was made. */
|
||||
struct frame_id selected_frame_id;
|
||||
};
|
||||
|
||||
@@ -9080,6 +9079,8 @@ restore_infcall_control_state (struct infcall_control_state *inf_status)
|
||||
/* Error in restoring the selected frame. Select the innermost
|
||||
frame. */
|
||||
select_frame (get_current_frame ());
|
||||
|
||||
global_user_selection ()->select_frame (get_selected_frame (NULL), false);
|
||||
}
|
||||
|
||||
xfree (inf_status);
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "gdb_bfd.h"
|
||||
#include "readline/tilde.h"
|
||||
#include "completer.h"
|
||||
#include "user-selection.h"
|
||||
|
||||
static const char *jit_reader_dir = NULL;
|
||||
|
||||
@@ -226,6 +227,7 @@ jit_reader_load_command (char *args, int from_tty)
|
||||
|
||||
loaded_jit_reader = jit_reader_load (so_name);
|
||||
reinit_frame_cache ();
|
||||
global_user_selection ()->select_frame (NULL, false);
|
||||
jit_inferior_created_hook ();
|
||||
do_cleanups (prev_cleanup);
|
||||
}
|
||||
@@ -239,6 +241,7 @@ jit_reader_unload_command (char *args, int from_tty)
|
||||
error (_("No JIT reader loaded."));
|
||||
|
||||
reinit_frame_cache ();
|
||||
global_user_selection ()->select_frame (NULL, false);
|
||||
jit_inferior_exit_hook (current_inferior ());
|
||||
loaded_jit_reader->functions->destroy (loaded_jit_reader->functions);
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "extension.h"
|
||||
#include <ctype.h>
|
||||
#include "mi-parse.h"
|
||||
#include "user-selection.h"
|
||||
|
||||
enum what_to_list { locals, arguments, all };
|
||||
|
||||
@@ -696,7 +697,10 @@ mi_cmd_stack_select_frame (char *command, char **argv, int argc)
|
||||
if (argc == 0 || argc > 1)
|
||||
error (_("-stack-select-frame: Usage: FRAME_SPEC"));
|
||||
|
||||
select_frame_command (argv[0], 1 /* not used */ );
|
||||
struct frame_info *frame = parse_frame_specification (argv[0], NULL);
|
||||
user_selection *us = global_user_selection ();
|
||||
|
||||
us->select_frame (frame, true);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "cli-out.h"
|
||||
#include "thread-fsm.h"
|
||||
#include "cli/cli-interp.h"
|
||||
#include "user-selection.h"
|
||||
|
||||
/* These are the interpreter setup, etc. functions for the MI
|
||||
interpreter. */
|
||||
@@ -1282,16 +1283,17 @@ mi_memory_changed (struct inferior *inferior, CORE_ADDR memaddr,
|
||||
changed. */
|
||||
|
||||
static void
|
||||
mi_user_selected_context_changed (user_selected_what selection)
|
||||
mi_on_global_user_selection_changed (user_selection *us,
|
||||
user_selected_what selection)
|
||||
{
|
||||
struct thread_info *tp;
|
||||
struct inferior *inf = us->inferior ();
|
||||
struct thread_info *thread = us->thread ();
|
||||
struct frame_info *frame = us->frame ();
|
||||
|
||||
/* Don't send an event if we're responding to an MI command. */
|
||||
if (mi_suppress_notification.user_selected_context)
|
||||
return;
|
||||
|
||||
tp = find_thread_ptid (inferior_ptid);
|
||||
|
||||
SWITCH_THRU_ALL_UIS ()
|
||||
{
|
||||
struct mi_interp *mi = as_mi_interp (top_level_interpreter ());
|
||||
@@ -1311,23 +1313,19 @@ mi_user_selected_context_changed (user_selected_what selection)
|
||||
target_terminal_ours_for_output ();
|
||||
|
||||
if (selection & USER_SELECTED_INFERIOR)
|
||||
print_selected_inferior (mi->cli_uiout);
|
||||
print_selected_inferior (mi->cli_uiout, inf);
|
||||
|
||||
if (tp != NULL
|
||||
if (thread != NULL
|
||||
&& (selection & (USER_SELECTED_THREAD | USER_SELECTED_FRAME)))
|
||||
{
|
||||
print_selected_thread_frame (mi->cli_uiout, selection);
|
||||
print_selected_thread_frame (mi->cli_uiout, us, selection);
|
||||
|
||||
fprintf_unfiltered (mi->event_channel,
|
||||
"thread-selected,id=\"%d\"",
|
||||
tp->global_num);
|
||||
thread->global_num);
|
||||
|
||||
if (tp->state != THREAD_RUNNING)
|
||||
{
|
||||
if (has_stack_frames ())
|
||||
print_stack_frame_to_uiout (mi_uiout, get_selected_frame (NULL),
|
||||
1, SRC_AND_LOC, 1);
|
||||
}
|
||||
if (thread->state != THREAD_RUNNING && frame != nullptr)
|
||||
print_stack_frame_to_uiout (mi_uiout, frame, 1, SRC_AND_LOC, 1);
|
||||
}
|
||||
|
||||
gdb_flush (mi->event_channel);
|
||||
@@ -1439,6 +1437,6 @@ _initialize_mi_interp (void)
|
||||
observer_attach_command_param_changed (mi_command_param_changed);
|
||||
observer_attach_memory_changed (mi_memory_changed);
|
||||
observer_attach_sync_execution_done (mi_on_sync_execution_done);
|
||||
observer_attach_user_selected_context_changed
|
||||
(mi_user_selected_context_changed);
|
||||
observer_attach_global_user_selection_changed
|
||||
(mi_on_global_user_selection_changed);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include "extension.h"
|
||||
#include "gdbcmd.h"
|
||||
#include "observer.h"
|
||||
#include "user-selection.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include "run-time-clock.h"
|
||||
@@ -560,31 +561,15 @@ mi_cmd_target_flash_erase (char *command, char **argv, int argc)
|
||||
void
|
||||
mi_cmd_thread_select (char *command, char **argv, int argc)
|
||||
{
|
||||
enum gdb_rc rc;
|
||||
char *mi_error_message;
|
||||
ptid_t previous_ptid = inferior_ptid;
|
||||
|
||||
if (argc != 1)
|
||||
error (_("-thread-select: USAGE: threadnum."));
|
||||
|
||||
rc = gdb_thread_select (current_uiout, argv[0], &mi_error_message);
|
||||
thread_select (argv[0], false);
|
||||
|
||||
/* If thread switch did not succeed don't notify or print. */
|
||||
if (rc == GDB_RC_FAIL)
|
||||
{
|
||||
make_cleanup (xfree, mi_error_message);
|
||||
error ("%s", mi_error_message);
|
||||
}
|
||||
user_selection *us = global_user_selection ();
|
||||
|
||||
print_selected_thread_frame (current_uiout,
|
||||
print_selected_thread_frame (current_uiout, us,
|
||||
USER_SELECTED_THREAD | USER_SELECTED_FRAME);
|
||||
|
||||
/* Notify if the thread has effectively changed. */
|
||||
if (!ptid_equal (inferior_ptid, previous_ptid))
|
||||
{
|
||||
observer_notify_user_selected_context_changed (USER_SELECTED_THREAD
|
||||
| USER_SELECTED_FRAME);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2025,6 +2010,8 @@ captured_mi_execute_command (struct ui_out *uiout, struct mi_parse *context)
|
||||
{
|
||||
char *argv[2];
|
||||
|
||||
apply_global_user_selection ();
|
||||
|
||||
/* A CLI command was read from the input stream. */
|
||||
/* This "feature" will be removed as soon as we have a
|
||||
complete set of mi commands. */
|
||||
@@ -2085,34 +2072,6 @@ mi_print_exception (const char *token, struct gdb_exception exception)
|
||||
fputs_unfiltered ("\n", mi->raw_stdout);
|
||||
}
|
||||
|
||||
/* Determine whether the parsed command already notifies the
|
||||
user_selected_context_changed observer. */
|
||||
|
||||
static int
|
||||
command_notifies_uscc_observer (struct mi_parse *command)
|
||||
{
|
||||
if (command->op == CLI_COMMAND)
|
||||
{
|
||||
/* CLI commands "thread" and "inferior" already send it. */
|
||||
return (strncmp (command->command, "thread ", 7) == 0
|
||||
|| strncmp (command->command, "inferior ", 9) == 0);
|
||||
}
|
||||
else /* MI_COMMAND */
|
||||
{
|
||||
if (strcmp (command->command, "interpreter-exec") == 0
|
||||
&& command->argc > 1)
|
||||
{
|
||||
/* "thread" and "inferior" again, but through -interpreter-exec. */
|
||||
return (strncmp (command->argv[1], "thread ", 7) == 0
|
||||
|| strncmp (command->argv[1], "inferior ", 9) == 0);
|
||||
}
|
||||
|
||||
else
|
||||
/* -thread-select already sends it. */
|
||||
return strcmp (command->command, "thread-select") == 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mi_execute_command (const char *cmd, int from_tty)
|
||||
{
|
||||
@@ -2139,7 +2098,6 @@ mi_execute_command (const char *cmd, int from_tty)
|
||||
|
||||
if (command != NULL)
|
||||
{
|
||||
ptid_t previous_ptid = inferior_ptid;
|
||||
struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
|
||||
|
||||
command->token = token;
|
||||
@@ -2178,39 +2136,6 @@ mi_execute_command (const char *cmd, int from_tty)
|
||||
|
||||
bpstat_do_actions ();
|
||||
|
||||
if (/* The notifications are only output when the top-level
|
||||
interpreter (specified on the command line) is MI. */
|
||||
interp_ui_out (top_level_interpreter ())->is_mi_like_p ()
|
||||
/* Don't try report anything if there are no threads --
|
||||
the program is dead. */
|
||||
&& thread_count () != 0
|
||||
/* If the command already reports the thread change, no need to do it
|
||||
again. */
|
||||
&& !command_notifies_uscc_observer (command))
|
||||
{
|
||||
struct mi_interp *mi = (struct mi_interp *) top_level_interpreter ();
|
||||
int report_change = 0;
|
||||
|
||||
if (command->thread == -1)
|
||||
{
|
||||
report_change = (!ptid_equal (previous_ptid, null_ptid)
|
||||
&& !ptid_equal (inferior_ptid, previous_ptid)
|
||||
&& !ptid_equal (inferior_ptid, null_ptid));
|
||||
}
|
||||
else if (!ptid_equal (inferior_ptid, null_ptid))
|
||||
{
|
||||
struct thread_info *ti = inferior_thread ();
|
||||
|
||||
report_change = (ti->global_num != command->thread);
|
||||
}
|
||||
|
||||
if (report_change)
|
||||
{
|
||||
observer_notify_user_selected_context_changed
|
||||
(USER_SELECTED_THREAD | USER_SELECTED_FRAME);
|
||||
}
|
||||
}
|
||||
|
||||
mi_parse_free (command);
|
||||
|
||||
do_cleanups (cleanup);
|
||||
@@ -2224,6 +2149,8 @@ mi_cmd_execute (struct mi_parse *parse)
|
||||
|
||||
cleanup = prepare_execute_command ();
|
||||
|
||||
apply_global_user_selection ();
|
||||
|
||||
if (parse->all && parse->thread_group != -1)
|
||||
error (_("Cannot specify --thread-group together with --all"));
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ struct objfile;
|
||||
struct thread_info;
|
||||
struct inferior;
|
||||
struct trace_state_variable;
|
||||
class user_selection;
|
||||
EOF
|
||||
;;
|
||||
esac
|
||||
|
||||
85
gdb/stack.c
85
gdb/stack.c
@@ -52,6 +52,9 @@
|
||||
#include "symfile.h"
|
||||
#include "extension.h"
|
||||
#include "observer.h"
|
||||
#include "user-selection.h"
|
||||
#include "common/scoped_restore.h"
|
||||
#include "cli-out.h"
|
||||
|
||||
/* The possible choices of "set print frame-arguments", and the value
|
||||
of this setting. */
|
||||
@@ -1281,7 +1284,7 @@ print_frame (struct frame_info *frame, int print_level,
|
||||
this function never returns NULL). When SELECTED_FRAME_P is non-NULL
|
||||
set its target to indicate that the default selected frame was used. */
|
||||
|
||||
static struct frame_info *
|
||||
struct frame_info *
|
||||
parse_frame_specification (const char *frame_exp, int *selected_frame_p)
|
||||
{
|
||||
int numargs;
|
||||
@@ -2300,14 +2303,21 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr)
|
||||
See parse_frame_specification for more info on proper frame
|
||||
expressions. */
|
||||
|
||||
void
|
||||
static void
|
||||
select_frame_command (char *level_exp, int from_tty)
|
||||
{
|
||||
struct frame_info *prev_frame = get_selected_frame_if_set ();
|
||||
cli_ui_out *uiout = dynamic_cast<cli_ui_out *> (current_uiout);
|
||||
scoped_restore_suppress_output<cli_ui_out> restore (uiout);
|
||||
user_selection *us = global_user_selection ();
|
||||
|
||||
select_frame (parse_frame_specification (level_exp, NULL));
|
||||
if (get_selected_frame_if_set () != prev_frame)
|
||||
observer_notify_user_selected_context_changed (USER_SELECTED_FRAME);
|
||||
uiout->suppress_output (true);
|
||||
|
||||
if (level_exp != nullptr)
|
||||
{
|
||||
struct frame_info *frame = parse_frame_specification (level_exp, NULL);
|
||||
|
||||
us->select_frame (frame, true);
|
||||
}
|
||||
}
|
||||
|
||||
/* The "frame" command. With no argument, print the selected frame
|
||||
@@ -2317,20 +2327,30 @@ select_frame_command (char *level_exp, int from_tty)
|
||||
static void
|
||||
frame_command (char *level_exp, int from_tty)
|
||||
{
|
||||
struct frame_info *prev_frame = get_selected_frame_if_set ();
|
||||
user_selection *us = global_user_selection ();
|
||||
|
||||
select_frame (parse_frame_specification (level_exp, NULL));
|
||||
if (get_selected_frame_if_set () != prev_frame)
|
||||
observer_notify_user_selected_context_changed (USER_SELECTED_FRAME);
|
||||
if (level_exp == nullptr)
|
||||
{
|
||||
if (us->thread () != nullptr
|
||||
&& is_stopped (us->thread ()))
|
||||
print_selected_thread_frame (current_uiout, us, USER_SELECTED_FRAME);
|
||||
else
|
||||
current_uiout->message (_("No stack.\n"));
|
||||
}
|
||||
else
|
||||
print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
|
||||
{
|
||||
struct frame_info *frame = parse_frame_specification (level_exp, NULL);
|
||||
|
||||
if (!us->select_frame (frame, true))
|
||||
print_selected_thread_frame (current_uiout, us, USER_SELECTED_FRAME);
|
||||
}
|
||||
}
|
||||
|
||||
/* Select the frame up one or COUNT_EXP stack levels from the
|
||||
previously selected frame, and print it briefly. */
|
||||
|
||||
static void
|
||||
up_silently_base (const char *count_exp)
|
||||
up_command (char *count_exp, int from_tty)
|
||||
{
|
||||
struct frame_info *frame;
|
||||
int count = 1;
|
||||
@@ -2341,27 +2361,29 @@ up_silently_base (const char *count_exp)
|
||||
frame = find_relative_frame (get_selected_frame ("No stack."), &count);
|
||||
if (count != 0 && count_exp == NULL)
|
||||
error (_("Initial frame selected; you cannot go up."));
|
||||
select_frame (frame);
|
||||
|
||||
user_selection *us = global_user_selection ();
|
||||
us->select_frame (frame, true);
|
||||
}
|
||||
|
||||
static void
|
||||
up_silently_command (char *count_exp, int from_tty)
|
||||
{
|
||||
up_silently_base (count_exp);
|
||||
}
|
||||
cli_ui_out *uiout = dynamic_cast<cli_ui_out *> (current_uiout);
|
||||
|
||||
static void
|
||||
up_command (char *count_exp, int from_tty)
|
||||
{
|
||||
up_silently_base (count_exp);
|
||||
observer_notify_user_selected_context_changed (USER_SELECTED_FRAME);
|
||||
gdb_assert (uiout != nullptr);
|
||||
|
||||
scoped_restore_suppress_output<cli_ui_out> restore (uiout);
|
||||
uiout->suppress_output (true);
|
||||
|
||||
up_command (count_exp, from_tty);
|
||||
}
|
||||
|
||||
/* Select the frame down one or COUNT_EXP stack levels from the previously
|
||||
selected frame, and print it briefly. */
|
||||
|
||||
static void
|
||||
down_silently_base (const char *count_exp)
|
||||
down_command (char *count_exp, int from_tty)
|
||||
{
|
||||
struct frame_info *frame;
|
||||
int count = -1;
|
||||
@@ -2380,20 +2402,21 @@ down_silently_base (const char *count_exp)
|
||||
error (_("Bottom (innermost) frame selected; you cannot go down."));
|
||||
}
|
||||
|
||||
select_frame (frame);
|
||||
user_selection *us = global_user_selection ();
|
||||
us->select_frame (frame, true);
|
||||
}
|
||||
|
||||
static void
|
||||
down_silently_command (char *count_exp, int from_tty)
|
||||
{
|
||||
down_silently_base (count_exp);
|
||||
}
|
||||
cli_ui_out *uiout = dynamic_cast<cli_ui_out *> (current_uiout);
|
||||
|
||||
static void
|
||||
down_command (char *count_exp, int from_tty)
|
||||
{
|
||||
down_silently_base (count_exp);
|
||||
observer_notify_user_selected_context_changed (USER_SELECTED_FRAME);
|
||||
gdb_assert (uiout != nullptr);
|
||||
|
||||
scoped_restore_suppress_output<cli_ui_out> restore (uiout);
|
||||
uiout->suppress_output (true);
|
||||
|
||||
down_command (count_exp, from_tty);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2518,9 +2541,7 @@ return_command (char *retval_exp, int from_tty)
|
||||
frame_pop (get_current_frame ());
|
||||
|
||||
select_frame (get_current_frame ());
|
||||
/* If interactive, print the frame that is now current. */
|
||||
if (from_tty)
|
||||
print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
|
||||
global_user_selection ()->select_frame (get_selected_frame (NULL), true);
|
||||
}
|
||||
|
||||
/* Sets the scope to input function name, provided that the function
|
||||
|
||||
@@ -20,11 +20,12 @@
|
||||
#ifndef STACK_H
|
||||
#define STACK_H
|
||||
|
||||
void select_frame_command (char *level_exp, int from_tty);
|
||||
|
||||
void find_frame_funname (struct frame_info *frame, char **funname,
|
||||
enum language *funlang, struct symbol **funcp);
|
||||
|
||||
struct frame_info *parse_frame_specification (const char *frame_exp,
|
||||
int *selected_frame_p);
|
||||
|
||||
typedef void (*iterate_over_block_arg_local_vars_cb) (const char *print_name,
|
||||
struct symbol *sym,
|
||||
void *cb_data);
|
||||
|
||||
@@ -39,7 +39,7 @@ proc check_mi_thread_command_set {} {
|
||||
"check_mi_thread_command_set: -thread-select"
|
||||
|
||||
mi_gdb_test "-thread-select 123456789" \
|
||||
{&.*\^error,msg="Thread ID 123456789 not known\."} \
|
||||
{\^error,msg="Thread ID 123456789 not known\."} \
|
||||
"check_mi_thread_command_set: -thread-select 123456789"
|
||||
|
||||
foreach thread $thread_list {
|
||||
|
||||
@@ -50,7 +50,7 @@ proc test_return_simple {} {
|
||||
set line_callee3_call [expr $line_callee3_head + 2]
|
||||
set line_callee3_close_brace [expr $line_callee3_head + 3]
|
||||
|
||||
mi_gdb_test "111-exec-return" "111\\^done,frame=\{level=\"0\",addr=\"$hex\",func=\"callee3\",args=\\\[.*\\\],file=\".*basics.c\",fullname=\"${fullname_syntax}${srcfile}\",line=\"($line_callee3_call|$line_callee3_close_brace)\"\}" "return from callee4 now"
|
||||
mi_gdb_test "111-exec-return" ".*=thread-selected,.*111\\^done,frame=\{level=\"0\",addr=\"$hex\",func=\"callee3\",args=\\\[.*\\\],file=\".*basics.c\",fullname=\"${fullname_syntax}${srcfile}\",line=\"($line_callee3_call|$line_callee3_close_brace)\"\}" "return from callee4 now"
|
||||
}
|
||||
|
||||
mi_runto callee4
|
||||
|
||||
@@ -112,7 +112,7 @@ proc make_cli_re { mode inf thread frame } {
|
||||
set thread_re $all_stop_thread_re
|
||||
|
||||
if [thread_is_running $mode $thread] {
|
||||
set thread_re "$thread_re\\\(running\\\)"
|
||||
set thread_re "$thread_re \\\(running\\\)"
|
||||
}
|
||||
|
||||
append cli_re $thread_re
|
||||
@@ -543,8 +543,10 @@ proc_with_prefix test_cli_inferior { mode } {
|
||||
match_re_or_ensure_not_output $mi_re "event on MI"
|
||||
}
|
||||
|
||||
# Do the 'inferior' command on the currently selected inferior. For now,
|
||||
# GDB naively re-outputs everything.
|
||||
# Do the 'inferior' command on the currently selected inferior.
|
||||
|
||||
set mi_re ""
|
||||
|
||||
with_spawn_id $gdb_main_spawn_id {
|
||||
gdb_test "inferior 2" $cli_re "CLI select inferior again"
|
||||
}
|
||||
@@ -927,8 +929,7 @@ proc_with_prefix test_mi_thread_select { mode } {
|
||||
with_spawn_id $gdb_main_spawn_id {
|
||||
# This doesn't work as of now, no event is sent on CLI. It is
|
||||
# commented out so we don't have to wait for the timeout every time.
|
||||
# match_re_or_ensure_not_output "$cli_re\r\n" "-thread-select, event on cli"
|
||||
kfail "gdb/20631" "thread-select, event on cli"
|
||||
match_re_or_ensure_not_output "$cli_re\r\n" "-thread-select, event on cli"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1027,8 +1028,11 @@ proc_with_prefix test_cli_in_mi_inferior { mode cli_in_mi_mode } {
|
||||
match_re_or_ensure_not_output "$cli_re\r\n" "select inferior, event on CLI"
|
||||
}
|
||||
|
||||
# Do the 'inferior' command on the currently selected inferior. For now,
|
||||
# GDB naively re-outputs everything.
|
||||
# Do the 'inferior' command on the currently selected inferior.
|
||||
|
||||
set mi_re ""
|
||||
set cli_re ""
|
||||
|
||||
with_spawn_id $mi_spawn_id {
|
||||
mi_gdb_test $command $mi_re "select inferior again"
|
||||
}
|
||||
@@ -1232,7 +1236,7 @@ proc_with_prefix test_cli_in_mi_frame { mode cli_in_mi_mode } {
|
||||
if { $mode == "all-stop" } {
|
||||
set mi_re [make_cli_in_mi_re $command $cli_in_mi_mode $mode 0 -1 -1 -1 1]
|
||||
} else {
|
||||
set mi_re "\\^error,msg=\"No stack\\.\""
|
||||
set mi_re "\\^done"
|
||||
}
|
||||
set cli_re ""
|
||||
|
||||
|
||||
167
gdb/thread.c
167
gdb/thread.c
@@ -44,6 +44,7 @@
|
||||
#include "cli/cli-utils.h"
|
||||
#include "thread-fsm.h"
|
||||
#include "tid-parse.h"
|
||||
#include "user-selection.h"
|
||||
|
||||
/* Definition of struct thread_info exported to gdbthread.h. */
|
||||
|
||||
@@ -283,6 +284,8 @@ add_thread_silent (ptid_t ptid)
|
||||
new template thread in the list with an invalid ptid, switch
|
||||
to it, delete the original thread, reset the new thread's
|
||||
ptid, and switch to it. */
|
||||
if (tp == global_user_selection ()->thread ())
|
||||
global_user_selection ()->select_thread (NULL, false);
|
||||
|
||||
if (ptid_equal (inferior_ptid, ptid))
|
||||
{
|
||||
@@ -439,7 +442,7 @@ delete_thread_1 (ptid_t ptid, int silent)
|
||||
/* 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
|
||||
if (tp->refcount_ > 0
|
||||
|| ptid_equal (tp->ptid, inferior_ptid))
|
||||
{
|
||||
if (tp->state != THREAD_EXITED)
|
||||
@@ -541,7 +544,7 @@ find_thread_ptid (ptid_t ptid)
|
||||
*/
|
||||
|
||||
struct thread_info *
|
||||
iterate_over_threads (int (*callback) (struct thread_info *, void *),
|
||||
iterate_over_threads (thread_callback_func callback,
|
||||
void *data)
|
||||
{
|
||||
struct thread_info *tp, *next;
|
||||
@@ -549,7 +552,7 @@ iterate_over_threads (int (*callback) (struct thread_info *, void *),
|
||||
for (tp = thread_list; tp; tp = next)
|
||||
{
|
||||
next = tp->next;
|
||||
if ((*callback) (tp, data))
|
||||
if (callback (tp, data))
|
||||
return tp;
|
||||
}
|
||||
|
||||
@@ -989,12 +992,24 @@ is_stopped (ptid_t ptid)
|
||||
return is_thread_state (ptid, THREAD_STOPPED);
|
||||
}
|
||||
|
||||
bool
|
||||
is_stopped (struct thread_info *thread)
|
||||
{
|
||||
return thread->state == THREAD_STOPPED;
|
||||
}
|
||||
|
||||
int
|
||||
is_exited (ptid_t ptid)
|
||||
{
|
||||
return is_thread_state (ptid, THREAD_EXITED);
|
||||
}
|
||||
|
||||
bool
|
||||
is_exited (struct thread_info *thread)
|
||||
{
|
||||
return thread->state == THREAD_EXITED;
|
||||
}
|
||||
|
||||
int
|
||||
is_running (ptid_t ptid)
|
||||
{
|
||||
@@ -1218,14 +1233,14 @@ print_thread_info_1 (struct ui_out *uiout, char *requested_threads,
|
||||
int show_global_ids)
|
||||
{
|
||||
struct thread_info *tp;
|
||||
ptid_t current_ptid;
|
||||
const struct thread_info *current_thread
|
||||
= global_user_selection ()->thread ();
|
||||
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 ();
|
||||
@@ -1289,14 +1304,14 @@ print_thread_info_1 (struct ui_out *uiout, char *requested_threads,
|
||||
if (uiout->is_mi_like_p ())
|
||||
{
|
||||
/* Compatibility. */
|
||||
if (ptid_equal (tp->ptid, current_ptid))
|
||||
if (tp == current_thread)
|
||||
uiout->text ("* ");
|
||||
else
|
||||
uiout->text (" ");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ptid_equal (tp->ptid, current_ptid))
|
||||
if (tp == current_thread)
|
||||
uiout->field_string ("current", "*");
|
||||
else
|
||||
uiout->field_skip ("current");
|
||||
@@ -1632,7 +1647,8 @@ restore_current_thread_cleanup_dtor (void *arg)
|
||||
|
||||
tp = find_thread_ptid (old->inferior_ptid);
|
||||
if (tp)
|
||||
tp->refcount--;
|
||||
tp->put ();
|
||||
|
||||
inf = find_inferior_id (old->inf_id);
|
||||
if (inf != NULL)
|
||||
inf->removable = old->was_removable;
|
||||
@@ -1649,7 +1665,7 @@ set_thread_refcount (void *data)
|
||||
= (struct thread_array_cleanup *) data;
|
||||
|
||||
for (k = 0; k != ta_cleanup->count; k++)
|
||||
ta_cleanup->tp_array[k]->refcount--;
|
||||
ta_cleanup->tp_array[k]->put ();
|
||||
}
|
||||
|
||||
struct cleanup *
|
||||
@@ -1689,7 +1705,7 @@ make_cleanup_restore_current_thread (void)
|
||||
|
||||
tp = find_thread_ptid (inferior_ptid);
|
||||
if (tp)
|
||||
tp->refcount++;
|
||||
tp->get ();
|
||||
}
|
||||
|
||||
current_inferior ()->removable = 0;
|
||||
@@ -1806,7 +1822,7 @@ thread_apply_all_command (char *cmd, int from_tty)
|
||||
ALL_NON_EXITED_THREADS (tp)
|
||||
{
|
||||
tp_array[i] = tp;
|
||||
tp->refcount++;
|
||||
tp->get ();
|
||||
i++;
|
||||
}
|
||||
/* Because we skipped exited threads, we may end up with fewer
|
||||
@@ -1940,50 +1956,34 @@ thread_apply_command (char *tidlist, int from_tty)
|
||||
void
|
||||
thread_command (char *tidstr, int from_tty)
|
||||
{
|
||||
user_selection *us = global_user_selection ();
|
||||
|
||||
if (tidstr == NULL)
|
||||
{
|
||||
if (ptid_equal (inferior_ptid, null_ptid))
|
||||
struct thread_info *thread = us->thread ();
|
||||
|
||||
if (thread == nullptr)
|
||||
error (_("No thread selected"));
|
||||
|
||||
if (target_has_stack)
|
||||
{
|
||||
struct thread_info *tp = inferior_thread ();
|
||||
|
||||
if (is_exited (inferior_ptid))
|
||||
if (is_exited (thread))
|
||||
printf_filtered (_("[Current thread is %s (%s) (exited)]\n"),
|
||||
print_thread_id (tp),
|
||||
target_pid_to_str (inferior_ptid));
|
||||
print_thread_id (thread),
|
||||
target_pid_to_str (thread->ptid));
|
||||
else
|
||||
printf_filtered (_("[Current thread is %s (%s)]\n"),
|
||||
print_thread_id (tp),
|
||||
target_pid_to_str (inferior_ptid));
|
||||
print_thread_id (thread),
|
||||
target_pid_to_str (thread->ptid));
|
||||
}
|
||||
else
|
||||
error (_("No stack."));
|
||||
}
|
||||
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;
|
||||
|
||||
/* Print if the thread has not changed, otherwise an event will be sent. */
|
||||
if (ptid_equal (inferior_ptid, previous_ptid))
|
||||
{
|
||||
print_selected_thread_frame (current_uiout,
|
||||
USER_SELECTED_THREAD
|
||||
| USER_SELECTED_FRAME);
|
||||
}
|
||||
else
|
||||
{
|
||||
observer_notify_user_selected_context_changed (USER_SELECTED_THREAD
|
||||
| USER_SELECTED_FRAME);
|
||||
}
|
||||
if (!thread_select (tidstr, true))
|
||||
print_selected_thread_frame (current_uiout, us,
|
||||
USER_SELECTED_THREAD | USER_SELECTED_FRAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2069,48 +2069,56 @@ show_print_thread_events (struct ui_file *file, int from_tty,
|
||||
value);
|
||||
}
|
||||
|
||||
static int
|
||||
do_captured_thread_select (struct ui_out *uiout, void *tidstr_v)
|
||||
bool
|
||||
thread_select (const char *tidstr, bool tid_is_qualified)
|
||||
{
|
||||
const char *tidstr = (const char *) tidstr_v;
|
||||
struct thread_info *tp;
|
||||
struct thread_info *thread;
|
||||
|
||||
if (uiout->is_mi_like_p ())
|
||||
if (tid_is_qualified)
|
||||
{
|
||||
int num = value_as_long (parse_and_eval (tidstr));
|
||||
thread = parse_thread_id (tidstr, NULL);
|
||||
|
||||
tp = find_thread_global_id (num);
|
||||
if (tp == NULL)
|
||||
error (_("Thread ID %d not known."), num);
|
||||
/* parse_thread_id is not supposed to return NULL. */
|
||||
gdb_assert (thread != NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
tp = parse_thread_id (tidstr, NULL);
|
||||
gdb_assert (tp != NULL);
|
||||
int num = value_as_long (parse_and_eval (tidstr));
|
||||
|
||||
thread = find_thread_global_id (num);
|
||||
if (thread == NULL)
|
||||
error (_("Thread ID %d not known."), num);
|
||||
}
|
||||
|
||||
if (!thread_alive (tp))
|
||||
if (!thread_alive (thread))
|
||||
error (_("Thread ID %s has terminated."), tidstr);
|
||||
|
||||
switch_to_thread (tp->ptid);
|
||||
|
||||
annotate_thread_changed ();
|
||||
|
||||
/* Since the current thread may have changed, see if there is any
|
||||
exited thread we can now delete. */
|
||||
prune_threads ();
|
||||
if (global_user_selection ()->select_thread (thread, true))
|
||||
{
|
||||
/* Since the current thread may have changed, see if there is any
|
||||
exited thread we can now delete. */
|
||||
prune_threads ();
|
||||
|
||||
return GDB_RC_OK;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Print thread and frame switch command response. */
|
||||
|
||||
void
|
||||
print_selected_thread_frame (struct ui_out *uiout,
|
||||
user_selection *us,
|
||||
user_selected_what selection)
|
||||
{
|
||||
struct thread_info *tp = inferior_thread ();
|
||||
struct inferior *inf = current_inferior ();
|
||||
struct inferior *inf = us->inferior ();
|
||||
struct thread_info *thread = us->thread ();
|
||||
|
||||
if (thread == nullptr)
|
||||
return;
|
||||
|
||||
if (selection & USER_SELECTED_THREAD)
|
||||
{
|
||||
@@ -2121,39 +2129,28 @@ print_selected_thread_frame (struct ui_out *uiout,
|
||||
}
|
||||
else
|
||||
{
|
||||
uiout->text ("[Switching to thread ");
|
||||
uiout->field_string ("new-thread-id", print_thread_id (tp));
|
||||
uiout->text (" (");
|
||||
uiout->text (target_pid_to_str (inferior_ptid));
|
||||
uiout->text (")]");
|
||||
const char *thread_id = print_thread_id (thread);
|
||||
const char *pid_str = target_pid_to_str (thread->ptid);
|
||||
const char *running_str
|
||||
= thread->state == THREAD_RUNNING ? " (running)" : "";
|
||||
|
||||
uiout->field_fmt (NULL, "[Switching to thread %s (%s)]%s\n",
|
||||
thread_id, pid_str, running_str);
|
||||
}
|
||||
}
|
||||
|
||||
if (tp->state == THREAD_RUNNING)
|
||||
if ((selection & USER_SELECTED_FRAME)
|
||||
&& thread->state == THREAD_STOPPED)
|
||||
{
|
||||
if (selection & USER_SELECTED_THREAD)
|
||||
uiout->text ("(running)\n");
|
||||
}
|
||||
else if (selection & USER_SELECTED_FRAME)
|
||||
{
|
||||
if (selection & USER_SELECTED_THREAD)
|
||||
uiout->text ("\n");
|
||||
struct frame_info *frame = us->frame ();
|
||||
|
||||
if (has_stack_frames ())
|
||||
print_stack_frame_to_uiout (uiout, get_selected_frame (NULL),
|
||||
1, SRC_AND_LOC, 1);
|
||||
if (frame != nullptr)
|
||||
print_stack_frame_to_uiout (uiout, frame, 1, SRC_AND_LOC, 1);
|
||||
else
|
||||
uiout->message (_("No selected frame.\n"));
|
||||
}
|
||||
}
|
||||
|
||||
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. */
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
#include "cli-out.h"
|
||||
#include "tracepoint.h"
|
||||
#include "inf-loop.h"
|
||||
#include "user-selection.h"
|
||||
|
||||
#if defined(TUI)
|
||||
# include "tui/tui.h"
|
||||
@@ -2179,6 +2180,7 @@ gdb_init (char *argv0)
|
||||
exec_bfd of the current program space. */
|
||||
initialize_progspace ();
|
||||
initialize_inferiors ();
|
||||
init_global_user_selection ();
|
||||
initialize_current_architecture ();
|
||||
init_cli_cmds();
|
||||
init_main (); /* But that omits this file! Do it now. */
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "target-descriptions.h"
|
||||
#include "buffer.h"
|
||||
#include <algorithm>
|
||||
#include "user-selection.h"
|
||||
|
||||
#ifndef O_LARGEFILE
|
||||
#define O_LARGEFILE 0
|
||||
@@ -601,6 +602,7 @@ tfile_close (struct target_ops *self)
|
||||
|
||||
pid = ptid_get_pid (inferior_ptid);
|
||||
inferior_ptid = null_ptid; /* Avoid confusion from thread stuff. */
|
||||
global_user_selection ()->select_thread (NULL, false);
|
||||
exit_inferior_silent (pid);
|
||||
|
||||
close (trace_fd);
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "infrun.h"
|
||||
#include "observer.h"
|
||||
#include "gdbthread.h"
|
||||
#include "user-selection.h"
|
||||
|
||||
/* Set to 1 when the TUI mode must be activated when we first start
|
||||
gdb. */
|
||||
@@ -209,15 +210,15 @@ tui_on_command_error (void)
|
||||
/* Observer for the user_selected_context_changed notification. */
|
||||
|
||||
static void
|
||||
tui_on_user_selected_context_changed (user_selected_what selection)
|
||||
tui_on_global_user_selection_changed (user_selection *us,
|
||||
user_selected_what selection)
|
||||
{
|
||||
struct thread_info *tp;
|
||||
|
||||
/* This event is suppressed. */
|
||||
if (cli_suppress_notification.user_selected_context)
|
||||
return;
|
||||
|
||||
tp = find_thread_ptid (inferior_ptid);
|
||||
struct inferior *inf = us->inferior ();
|
||||
struct thread_info *thread = us->thread ();
|
||||
|
||||
SWITCH_THRU_ALL_UIS ()
|
||||
{
|
||||
@@ -227,12 +228,11 @@ tui_on_user_selected_context_changed (user_selected_what selection)
|
||||
continue;
|
||||
|
||||
if (selection & USER_SELECTED_INFERIOR)
|
||||
print_selected_inferior (tui->interp_ui_out ());
|
||||
print_selected_inferior (tui->interp_ui_out (), inf);
|
||||
|
||||
if (tp != NULL
|
||||
if (thread != NULL
|
||||
&& ((selection & (USER_SELECTED_THREAD | USER_SELECTED_FRAME))))
|
||||
print_selected_thread_frame (tui->interp_ui_out (), selection);
|
||||
|
||||
print_selected_thread_frame (tui->interp_ui_out (), us, selection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,6 +337,6 @@ _initialize_tui_interp (void)
|
||||
observer_attach_no_history (tui_on_no_history);
|
||||
observer_attach_sync_execution_done (tui_on_sync_execution_done);
|
||||
observer_attach_command_error (tui_on_command_error);
|
||||
observer_attach_user_selected_context_changed
|
||||
(tui_on_user_selected_context_changed);
|
||||
observer_attach_global_user_selection_changed
|
||||
(tui_on_global_user_selection_changed);
|
||||
}
|
||||
|
||||
20
gdb/ui-out.h
20
gdb/ui-out.h
@@ -220,4 +220,24 @@ private:
|
||||
typedef ui_out_emit_type<ui_out_type_tuple> ui_out_emit_tuple;
|
||||
typedef ui_out_emit_type<ui_out_type_list> ui_out_emit_list;
|
||||
|
||||
template <class T>
|
||||
class scoped_restore_suppress_output
|
||||
{
|
||||
public:
|
||||
scoped_restore_suppress_output (T* obj)
|
||||
: m_obj (obj),
|
||||
m_val (m_obj->suppress_output ())
|
||||
{}
|
||||
|
||||
~scoped_restore_suppress_output ()
|
||||
{
|
||||
m_obj->suppress_output (m_val);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
T* m_obj;
|
||||
bool m_val;
|
||||
};
|
||||
|
||||
#endif /* UI_OUT_H */
|
||||
|
||||
357
gdb/user-selection.c
Normal file
357
gdb/user-selection.c
Normal file
@@ -0,0 +1,357 @@
|
||||
#include "defs.h"
|
||||
#include "user-selection.h"
|
||||
#include "inferior.h"
|
||||
#include "gdbthread.h"
|
||||
#include "observer.h"
|
||||
#include "gdbcmd.h"
|
||||
|
||||
/* The user-visible selection. */
|
||||
static user_selection main_user_selection;
|
||||
|
||||
/* Knob for user-selection related debug traces. */
|
||||
static int debug_user_selection = 0;
|
||||
|
||||
/* See user-selection.h. */
|
||||
|
||||
user_selection *
|
||||
global_user_selection ()
|
||||
{
|
||||
return &main_user_selection;
|
||||
}
|
||||
|
||||
/* See user-selection.h. */
|
||||
|
||||
void
|
||||
init_global_user_selection ()
|
||||
{
|
||||
/* Fetch the initial inferior, which should have been added by now. The
|
||||
initial inferior is selected on startup. */
|
||||
struct inferior *inf = find_inferior_id (1);
|
||||
|
||||
gdb_assert (inf != nullptr);
|
||||
|
||||
global_user_selection ()->select_inferior (inf, false);
|
||||
}
|
||||
|
||||
/* See user-selection.h. */
|
||||
|
||||
bool
|
||||
user_selection::select_inferior (struct inferior *inf, bool notify)
|
||||
{
|
||||
const char *debug_prefix = "user_selection::select_thread";
|
||||
|
||||
/* There is always a selected inferior. */
|
||||
gdb_assert (inf != nullptr);
|
||||
|
||||
if (debug_user_selection)
|
||||
printf_unfiltered ("%s: num=%d\n", debug_prefix, inf->num);
|
||||
|
||||
/* No-op if this is already the currently selected inferior. */
|
||||
if (inf == m_inferior)
|
||||
{
|
||||
if (debug_user_selection)
|
||||
printf_unfiltered ("%s: already selected inferior", debug_prefix);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* When we change inferior, thread and frame will change as well. */
|
||||
user_selected_what what = USER_SELECTED_INFERIOR | USER_SELECTED_THREAD | USER_SELECTED_FRAME;
|
||||
|
||||
/* INF becomes selected. */
|
||||
m_inferior = inf;
|
||||
|
||||
/* Clear the thread and frame fields. */
|
||||
if (m_thread != nullptr)
|
||||
{
|
||||
m_thread->put ();
|
||||
m_thread = nullptr;
|
||||
}
|
||||
|
||||
m_frame_id = null_frame_id;
|
||||
m_frame_level = INVALID_FRAME_LEVEL;
|
||||
|
||||
if (m_inferior->pid != 0)
|
||||
{
|
||||
/* This inferior is executing, so it should have threads. Select the first
|
||||
one. */
|
||||
m_thread = iterate_over_threads (
|
||||
[inf] (struct thread_info *thread, void *) -> int
|
||||
{
|
||||
return inf->pid == ptid_get_pid (thread->ptid);
|
||||
}
|
||||
);
|
||||
|
||||
/* We expect this inferior to have at least one thread. If we didn't
|
||||
find it, we have a problem. */
|
||||
gdb_assert (m_thread != nullptr);
|
||||
|
||||
/* Acquire a strong reference, so the thread_info object doesn't get freed
|
||||
while it's selected. */
|
||||
m_thread->get ();
|
||||
}
|
||||
|
||||
sanity_check ();
|
||||
|
||||
if (notify)
|
||||
observer_notify_global_user_selection_changed (this, what);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* See user-selection.h. */
|
||||
|
||||
bool
|
||||
user_selection::select_thread (struct thread_info *thread, bool notify)
|
||||
{
|
||||
const char *debug_prefix = "user_selection::select_thread";
|
||||
|
||||
/* When changing thread, the frame will necessarily change as well. */
|
||||
user_selected_what what = USER_SELECTED_THREAD | USER_SELECTED_FRAME;
|
||||
|
||||
if (debug_user_selection)
|
||||
printf_unfiltered ("%s: num=%d, ptid=%s",
|
||||
debug_prefix, thread->global_num,
|
||||
target_pid_to_str (thread->ptid));
|
||||
|
||||
/* No-op if this is already the currently selected thread. */
|
||||
if (thread == m_thread)
|
||||
{
|
||||
if (debug_user_selection)
|
||||
printf_unfiltered ("%s: already selected thread", debug_prefix);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Clear the frame fields. */
|
||||
m_frame_id = null_frame_id;
|
||||
m_frame_level = INVALID_FRAME_LEVEL;
|
||||
|
||||
/* Release the reference. */
|
||||
if (m_thread != nullptr)
|
||||
m_thread->put ();
|
||||
|
||||
m_thread = thread;
|
||||
|
||||
if (m_thread != nullptr)
|
||||
{
|
||||
/* Acquire a strong reference, so the thread_info object doesn't get freed
|
||||
while it's selected. */
|
||||
m_thread->get ();
|
||||
|
||||
/* The inferior of the thread becomes the newly selected inferior, if it's
|
||||
not already. */
|
||||
if (m_inferior != thread->inf)
|
||||
{
|
||||
m_inferior = thread->inf;
|
||||
|
||||
what |= USER_SELECTED_INFERIOR;
|
||||
}
|
||||
}
|
||||
|
||||
sanity_check ();
|
||||
|
||||
if (notify)
|
||||
observer_notify_global_user_selection_changed (this, what);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
user_selection::select_frame (struct frame_info *frame, bool notify)
|
||||
{
|
||||
const char *debug_prefix = "user_selection::select_frame";
|
||||
|
||||
/* No-op if this is already the selected frame. */
|
||||
if (frame_id_eq (m_frame_id, get_frame_id (frame))
|
||||
&& m_frame_level == frame_relative_level (frame))
|
||||
return false;
|
||||
|
||||
m_frame_id = get_frame_id (frame);
|
||||
m_frame_level = frame_relative_level (frame);
|
||||
|
||||
if (debug_user_selection)
|
||||
{
|
||||
string_file buf;
|
||||
|
||||
fprint_frame_id (&buf, m_frame_id);
|
||||
printf_unfiltered ("%s: Selected frame #%d %s\n", debug_prefix,
|
||||
m_frame_level, buf.c_str ());
|
||||
}
|
||||
|
||||
sanity_check ();
|
||||
|
||||
if (notify)
|
||||
observer_notify_global_user_selection_changed (this, USER_SELECTED_FRAME);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Do some basic checks to verify that the selection is coherent. */
|
||||
|
||||
void
|
||||
user_selection::sanity_check () const
|
||||
{
|
||||
/* We always have a current inferior. */
|
||||
gdb_assert (m_inferior != nullptr);
|
||||
|
||||
/* The selected thread must match the selected inferior. */
|
||||
if (m_thread != nullptr)
|
||||
gdb_assert (m_thread->inf == m_inferior);
|
||||
|
||||
/* Can't have a current frame without a current thread. */
|
||||
if (m_frame_level != INVALID_FRAME_LEVEL)
|
||||
gdb_assert (m_thread != nullptr);
|
||||
}
|
||||
|
||||
/* Apply US to the core of gdb. This makes the internally selected inferior,
|
||||
thread and frame reflect the selection in US. */
|
||||
|
||||
static void
|
||||
apply_user_selection (user_selection *us)
|
||||
{
|
||||
/* Select inferior. */
|
||||
set_current_inferior (us->inferior ());
|
||||
set_current_program_space (us->inferior ()->pspace);
|
||||
|
||||
/* Select thread. */
|
||||
if (us->thread () != nullptr)
|
||||
switch_to_thread (us->thread ()->ptid);
|
||||
else
|
||||
switch_to_thread (null_ptid);
|
||||
|
||||
/* Select frame. */
|
||||
if (us->has_valid_frame ())
|
||||
{
|
||||
struct frame_info *fi = us->frame ();
|
||||
|
||||
select_frame (fi);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to make the current (as in: where the program is currently stopped) frame
|
||||
the selected one. */
|
||||
|
||||
void
|
||||
user_selection::try_select_current_frame ()
|
||||
{
|
||||
/* This function should only be called when we don't have a selected frame
|
||||
yet. */
|
||||
gdb_assert (!has_valid_frame ());
|
||||
|
||||
/* We need to select the relevant inferior/thread internally in order for
|
||||
get_current_frame to work. */
|
||||
apply_user_selection (this);
|
||||
|
||||
TRY
|
||||
{
|
||||
struct frame_info *fi = get_current_frame ();
|
||||
|
||||
m_frame_id = get_frame_id (fi);
|
||||
m_frame_level = frame_relative_level (fi);
|
||||
}
|
||||
CATCH (exception, RETURN_MASK_ALL)
|
||||
{
|
||||
/* We can't determine the current frame, too bad. */
|
||||
}
|
||||
END_CATCH
|
||||
}
|
||||
|
||||
/* See user-selection.h. */
|
||||
|
||||
void
|
||||
apply_global_user_selection ()
|
||||
{
|
||||
apply_user_selection (global_user_selection ());
|
||||
}
|
||||
|
||||
/* Callback for the new_thread observer. */
|
||||
|
||||
static void
|
||||
global_user_selection_on_new_thread (struct thread_info *tp)
|
||||
{
|
||||
user_selection *us = global_user_selection ();
|
||||
|
||||
/* If a new thread is created while:
|
||||
|
||||
1. We don't have a currently selected thread,
|
||||
2. The inferior of the new thread is the currently selected inferior,
|
||||
|
||||
then we silently make that new thread the selected one. It covers the case
|
||||
of automatically selecting the initial thread when starting an
|
||||
inferior. */
|
||||
|
||||
if (us->thread () == nullptr && tp->inf == us->inferior ())
|
||||
us->select_thread (tp, false);
|
||||
}
|
||||
|
||||
/* Callback for the on_exited observer. */
|
||||
|
||||
static void
|
||||
global_user_selection_on_exited (struct inferior *inferior)
|
||||
{
|
||||
user_selection *us = global_user_selection ();
|
||||
|
||||
/* When an inferior exits and it's the current inferior, it means we have one
|
||||
of its thread currently selected. De-select it. */
|
||||
|
||||
if (inferior == us->inferior ())
|
||||
us->select_thread (NULL, false);
|
||||
}
|
||||
|
||||
/* Callback for the on_target_resumed observer. */
|
||||
|
||||
static void
|
||||
global_user_selection_on_target_resumed (ptid_t ptid)
|
||||
{
|
||||
user_selection *us = global_user_selection ();
|
||||
|
||||
/* If the selected thread has been resumed, our frame isn't valid anymore. */
|
||||
if (us->thread () != nullptr && ptid_match (us->thread ()->ptid, ptid))
|
||||
us->select_frame (NULL, false);
|
||||
}
|
||||
|
||||
/* Implementation of the "maintenance print user-selection" command. */
|
||||
|
||||
static void
|
||||
maint_print_user_selection (char *cmd, int from_tty)
|
||||
{
|
||||
user_selection *us = global_user_selection ();
|
||||
|
||||
struct inferior *inf = us->inferior ();
|
||||
struct thread_info *tp = us->thread ();
|
||||
struct frame_id frame_id = us->raw_frame_id ();
|
||||
int frame_level = us->raw_frame_level ();
|
||||
|
||||
/* Print inferior. */
|
||||
fprintf_filtered(gdb_stdout, "inferior %p (num=%d)\n", inf, inf->num);
|
||||
|
||||
/* Print thread. */
|
||||
if (tp != nullptr)
|
||||
fprintf_filtered (gdb_stdout,
|
||||
"thread %p (gnum=%d, per-inf-num=%d, inf=%p)\n", tp,
|
||||
tp->global_num, tp->per_inf_num, tp->inf);
|
||||
else
|
||||
fprintf_filtered(gdb_stdout, "thread null\n");
|
||||
|
||||
/* Print frame. */
|
||||
fprint_frame_id (gdb_stdout, frame_id);
|
||||
fprintf_filtered (gdb_stdout, ", level=%d\n", frame_level);
|
||||
}
|
||||
|
||||
/* Initialize observer callbacks and commands. */
|
||||
|
||||
void
|
||||
_initialize_user_selection ()
|
||||
{
|
||||
observer_attach_new_thread (global_user_selection_on_new_thread);
|
||||
observer_attach_inferior_exit (global_user_selection_on_exited);
|
||||
observer_attach_target_resumed (global_user_selection_on_target_resumed);
|
||||
|
||||
add_setshow_boolean_cmd ("user-selection", class_maintenance,
|
||||
&debug_user_selection, "blah", "blah", "blah", NULL,
|
||||
NULL, &setdebuglist, &showdebuglist);
|
||||
|
||||
add_cmd ("user-selection", class_maintenance, maint_print_user_selection,
|
||||
"foo", &maintenanceprintlist);
|
||||
}
|
||||
100
gdb/user-selection.h
Normal file
100
gdb/user-selection.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef USER_SELECTION_H
|
||||
#define USER_SELECTION_H
|
||||
|
||||
class user_selection {
|
||||
public:
|
||||
|
||||
/* Default constructor, nothing is selected. */
|
||||
|
||||
user_selection ()
|
||||
: m_inferior (nullptr),
|
||||
m_thread (nullptr),
|
||||
m_frame_id (null_frame_id),
|
||||
m_frame_level (INVALID_FRAME_LEVEL)
|
||||
{}
|
||||
|
||||
/* Make INF the selected inferior. If NOTIFY is true, call the observer
|
||||
indicating a selection change.
|
||||
|
||||
Return true if the newly selected inferior is different than the previously
|
||||
selected inferior. */
|
||||
|
||||
bool select_inferior (struct inferior *inf, bool notify);
|
||||
|
||||
/* Make THREAD the selected thread. If NOTIFY is true, call the observer
|
||||
indicating a selection change.
|
||||
|
||||
Return true if the newly selected thread is different than the previously
|
||||
selected thread. */
|
||||
|
||||
bool select_thread (struct thread_info *thread, bool notify);
|
||||
|
||||
/* Make FRAME the selected frame. If NOTIFY is true, call the observer
|
||||
indicating a selection change.
|
||||
|
||||
Return true if the newly selected frame is different than the previously
|
||||
selected frame. */
|
||||
|
||||
bool select_frame (struct frame_info *frame, bool notify);
|
||||
|
||||
/* Get the selected inferior. */
|
||||
|
||||
struct inferior *inferior () const
|
||||
{ return m_inferior; }
|
||||
|
||||
/* Get the selected thread. */
|
||||
|
||||
struct thread_info *thread () const
|
||||
{ return m_thread; }
|
||||
|
||||
/* Get the selected frame. */
|
||||
|
||||
struct frame_info *
|
||||
frame ()
|
||||
{
|
||||
if (!has_valid_frame ())
|
||||
try_select_current_frame ();
|
||||
|
||||
if (!has_valid_frame ())
|
||||
return NULL;
|
||||
|
||||
return frame_find_by_id (m_frame_id);
|
||||
}
|
||||
|
||||
frame_id
|
||||
raw_frame_id () const
|
||||
{ return m_frame_id; }
|
||||
|
||||
int
|
||||
raw_frame_level () const
|
||||
{ return m_frame_level; }
|
||||
|
||||
bool has_valid_frame () const
|
||||
{ return m_frame_level != INVALID_FRAME_LEVEL; }
|
||||
|
||||
private:
|
||||
|
||||
struct inferior *m_inferior;
|
||||
struct thread_info *m_thread;
|
||||
|
||||
struct frame_id m_frame_id;
|
||||
int m_frame_level;
|
||||
|
||||
void sanity_check () const;
|
||||
void try_select_current_frame ();
|
||||
};
|
||||
|
||||
/* Get the global user selection. */
|
||||
|
||||
user_selection *global_user_selection ();
|
||||
|
||||
/* Initialize the global user selection. This must be called after the initial
|
||||
inferior has been created. */
|
||||
|
||||
void init_global_user_selection ();
|
||||
|
||||
/* Apply the global user selection to core of gdb. */
|
||||
|
||||
void apply_global_user_selection ();
|
||||
|
||||
#endif /* USER_SELECTION_H */
|
||||
Reference in New Issue
Block a user