summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/Makefile.in2
-rw-r--r--gdb/cli-out.h9
-rw-r--r--gdb/cli/cli-interp.c17
-rw-r--r--gdb/doc/observer.texi7
-rw-r--r--gdb/event-top.c4
-rw-r--r--gdb/frame.c2
-rw-r--r--gdb/frame.h2
-rw-r--r--gdb/gdb.h4
-rw-r--r--gdb/gdbthread.h26
-rw-r--r--gdb/infcall.c2
-rw-r--r--gdb/inferior.c44
-rw-r--r--gdb/inferior.h5
-rw-r--r--gdb/infrun.c49
-rw-r--r--gdb/jit.c3
-rw-r--r--gdb/mi/mi-cmd-stack.c6
-rw-r--r--gdb/mi/mi-interp.c30
-rw-r--r--gdb/mi/mi-main.c89
-rwxr-xr-xgdb/observer.sh1
-rw-r--r--gdb/stack.c85
-rw-r--r--gdb/stack.h5
-rw-r--r--gdb/testsuite/gdb.mi/mi-pthreads.exp2
-rw-r--r--gdb/testsuite/gdb.mi/mi-return.exp2
-rw-r--r--gdb/testsuite/gdb.mi/user-selected-context-sync.exp20
-rw-r--r--gdb/thread.c167
-rw-r--r--gdb/top.c2
-rw-r--r--gdb/tracefile-tfile.c2
-rw-r--r--gdb/tui/tui-interp.c20
-rw-r--r--gdb/ui-out.h20
-rw-r--r--gdb/user-selection.c357
-rw-r--r--gdb/user-selection.h100
30 files changed, 766 insertions, 318 deletions
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 268c2c6ead3..85706a59094 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -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 \
diff --git a/gdb/cli-out.h b/gdb/cli-out.h
index 1b6a1ade812..5a775e9881f 100644
--- a/gdb/cli-out.h
+++ b/gdb/cli-out.h
@@ -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 ();
diff --git a/gdb/cli/cli-interp.c b/gdb/cli/cli-interp.c
index 8712c75f396..d460a68771b 100644
--- a/gdb/cli/cli-interp.c
+++ b/gdb/cli/cli-interp.c
@@ -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);
}
diff --git a/gdb/doc/observer.texi b/gdb/doc/observer.texi
index 606ddfe5368..f786b1e2b7d 100644
--- a/gdb/doc/observer.texi
+++ b/gdb/doc/observer.texi
@@ -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
diff --git a/gdb/event-top.c b/gdb/event-top.c
index 5d8d077a9a4..9056b46c60a 100644
--- a/gdb/event-top.c
+++ b/gdb/event-top.c
@@ -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++)
;
diff --git a/gdb/frame.c b/gdb/frame.c
index d98003dee7c..93f658534d1 100644
--- a/gdb/frame.c
+++ b/gdb/frame.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");
diff --git a/gdb/frame.h b/gdb/frame.h
index 1d0644f12de..5c68febde67 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -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
diff --git a/gdb/gdb.h b/gdb/gdb.h
index ac1e683314d..c224d419be6 100644
--- a/gdb/gdb.h
+++ b/gdb/gdb.h
@@ -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);
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 06ed78f5681..47cf1258a12 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -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;
diff --git a/gdb/infcall.c b/gdb/infcall.c
index f55acb5c27e..fc3e4e7e75f 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -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);
diff --git a/gdb/inferior.c b/gdb/inferior.c
index c4ab6d7d7f6..d9d026a936c 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -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;
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 7c0ddf37f1e..b06866f4900 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -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) */
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 1e5e9f14a64..a55acdf6dd4 100644
--- a/gdb/infrun.c
+++ b/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);
diff --git a/gdb/jit.c b/gdb/jit.c
index 158d6d82154..d58457da020 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -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);
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index acb44a28ee5..fadfdaa4352 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -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
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index 86340e4a081..958f154130c 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -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);
}
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index cf4e45ad413..63458d91139 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -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"));
diff --git a/gdb/observer.sh b/gdb/observer.sh
index 49db6b8cef4..02a3e7be221 100755
--- a/gdb/observer.sh
+++ b/gdb/observer.sh
@@ -65,6 +65,7 @@ struct objfile;
struct thread_info;
struct inferior;
struct trace_state_variable;
+class user_selection;
EOF
;;
esac
diff --git a/gdb/stack.c b/gdb/stack.c
index aa3a80e9a82..26fb40dbec7 100644
--- a/gdb/stack.c
+++ b/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 ();
+
+ uiout->suppress_output (true);
- 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)
+ {
+ 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
diff --git a/gdb/stack.h b/gdb/stack.h
index 1583200cb29..75fa4d66920 100644
--- a/gdb/stack.h
+++ b/gdb/stack.h
@@ -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);
diff --git a/gdb/testsuite/gdb.mi/mi-pthreads.exp b/gdb/testsuite/gdb.mi/mi-pthreads.exp
index 1b569d7e64e..07e02ab446b 100644
--- a/gdb/testsuite/gdb.mi/mi-pthreads.exp
+++ b/gdb/testsuite/gdb.mi/mi-pthreads.exp
@@ -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 {
diff --git a/gdb/testsuite/gdb.mi/mi-return.exp b/gdb/testsuite/gdb.mi/mi-return.exp
index 91f41461074..6b099bf001f 100644
--- a/gdb/testsuite/gdb.mi/mi-return.exp
+++ b/gdb/testsuite/gdb.mi/mi-return.exp
@@ -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
diff --git a/gdb/testsuite/gdb.mi/user-selected-context-sync.exp b/gdb/testsuite/gdb.mi/user-selected-context-sync.exp
index 77734aec05c..f7697f5b126 100644
--- a/gdb/testsuite/gdb.mi/user-selected-context-sync.exp
+++ b/gdb/testsuite/gdb.mi/user-selected-context-sync.exp
@@ -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 ""
diff --git a/gdb/thread.c b/gdb/thread.c
index 99fe4247178..b40d8cd36ae 100644
--- a/gdb/thread.c
+++ b/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. */
diff --git a/gdb/top.c b/gdb/top.c
index 6bf9d8c0216..3a8b9616dff 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -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. */
diff --git a/gdb/tracefile-tfile.c b/gdb/tracefile-tfile.c
index dbcd65d5dfa..91f97cfa9fd 100644
--- a/gdb/tracefile-tfile.c
+++ b/gdb/tracefile-tfile.c
@@ -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);
diff --git a/gdb/tui/tui-interp.c b/gdb/tui/tui-interp.c
index 702c34269c0..66423efee4e 100644
--- a/gdb/tui/tui-interp.c
+++ b/gdb/tui/tui-interp.c
@@ -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);
}
diff --git a/gdb/ui-out.h b/gdb/ui-out.h
index 9278cabdaab..17933d12f3c 100644
--- a/gdb/ui-out.h
+++ b/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 */
diff --git a/gdb/user-selection.c b/gdb/user-selection.c
new file mode 100644
index 00000000000..098f9154a80
--- /dev/null
+++ b/gdb/user-selection.c
@@ -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);
+}
diff --git a/gdb/user-selection.h b/gdb/user-selection.h
new file mode 100644
index 00000000000..a6f9af30c91
--- /dev/null
+++ b/gdb/user-selection.h
@@ -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 */