Compare commits

...

1 Commits

Author SHA1 Message Date
Simon Marchi
44c04ee9bf Decouple user selection from internal selection
I am sending this as an RFC because it's far from complete and
definitive, but I'd like to gather some comments and opinions before
going further in this direction.

The goal of this patch is to decouple the notion of the user-selected
inferior/thread/frame from GDB's internally selected
inferior/thread/frame.

Currently, for example, the inferior_ptid variable has two jobs:

 - it's the user-selected thread: it's changed by the "thread" command.
   Other commands (continue, backtrace, etc) apply to this thread.
 - it's the internally-selected thread: it defines the thread GDB is
   currently "working" on.  For example, implementations of
   to_xfer_partial will refer to it to know from which thread to
   read/write memory.

Because of this dual usage, if we want to do some operations on a thread
other than the currently selected one, we have to save the current
inferior/thread/frame and restore them when we're done.  Failing to do
so would result in an unexpected selection switch for the user.

To improve this, Pedro suggested in [1] to decouple the two concepts.  This
is essentially what this patch is trying to do.

A new "user_selection" object is introduced, which contains the selected
inferior/thread/frame from the point of view of the user.  Before every
command, we "apply" this selection to the core of GDB to make sure the
internal selection matches the user selection.

There is a single user selection for the whole GDB (named "global
user-selection"), but as was mentioned in the linked thread, it opens
the door to having different selections for different UIs.  This means
that each UI would have its own user-selection object, which would be
applied to the core prior to executing commands from this UI.

The global user-selection object only gets modified when we really
intend to change it.  It can be because of the thread / -thread-select /
up / down / frame / inferior commands, a breakpoint hit in all-stop, an
inferior exit, etc.

The problem that initially prompted this effort is that the "--thread"
flag of MI commands changes the user-selected thread under the user's
feet.  My initial attempt to fix it was to restore the selection after
the MI command execution.  However, some cases are hard to get right.
For example:

  (thread 1 is currently selected)
  -interpreter-exec --thread 2 console "thread 3"

Restoring the selected thread to thread 1 after the MI command execution
wrongfully cancels the switch to thread 3.  So it's hard to determine
when we should or shouldn't restore.   With the current patch, it works
naturally: the --thread flag doesn't touch the user-selected thread,
only the internal one.  The "thread 3" command updates the user
selection.

Another difficulty is to send the right notifications to MI when the
user selection changes.  That means to not miss any, but not send too
many either.  Getting it somewhat right lead to ugly hacks (see the
command_notifies_uscc_observer function) and even then it's not perfect
(see the kfails in user-selected-context-sync.exp test).  With the
proposed method, it's easy to know when the user-selection changes and
send notifications.

With this patch, there are probably a few usage of
make_cleanup_restore_current_thread that are not needed anymore, if they
are only used to restore the user selection.  I kept removing them for a
later time though.

In the current state, there are a few minor regressions in the testsuite
(especially some follow-fork stuff I'm not sure how to handle), but the
vast majority of the previously passing tests still pass.

Comments are welcome!

Thanks,

Simon

[1] https://sourceware.org/ml/gdb-patches/2016-08/msg00031.html
2017-02-23 17:25:30 -05:00
30 changed files with 766 additions and 318 deletions

View File

@@ -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 \

View File

@@ -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 ();

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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++)
;

View File

@@ -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");

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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) */

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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"));

View File

@@ -65,6 +65,7 @@ struct objfile;
struct thread_info;
struct inferior;
struct trace_state_variable;
class user_selection;
EOF
;;
esac

View File

@@ -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

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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

View File

@@ -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 ""

View File

@@ -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. */

View File

@@ -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. */

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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
View 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
View 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 */