aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Marchi <simon.marchi@ericsson.com>2016-12-08 13:06:14 -0500
committerSimon Marchi <simon.marchi@ericsson.com>2017-02-23 17:25:30 -0500
commit44c04ee9bf959b40819de6327500557fbb5d8c4a (patch)
treef2fae0b73585f6b67cfbb0f7c29d65f9d70ebc77
parenta567769b813b2538bebc97d689fc0739f172028e (diff)
downloadgdb-users/simark/user-selection-rfc.zip
gdb-users/simark/user-selection-rfc.tar.gz
gdb-users/simark/user-selection-rfc.tar.bz2
Decouple user selection from internal selectionusers/simark/user-selection-rfc
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
-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 268c2c6..85706a5 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 1b6a1ad..5a775e9 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 8712c75..d460a68 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 606ddfe..f786b1e 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 5d8d077..9056b46 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 d98003d..93f6585 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 1d0644f..5c68feb 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 ac1e683..c224d41 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 06ed78f..47cf125 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 f55acb5..fc3e4e7 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 c4ab6d7..d9d026a 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 7c0ddf3..b06866f 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 1e5e9f1..a55acdf 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 158d6d8..d58457d 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 acb44a2..fadfdaa 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 86340e4..958f154 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 cf4e45a..63458d9 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 49db6b8..02a3e7b 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 aa3a80e..26fb40d 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 1583200..75fa4d6 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 1b569d7..07e02ab 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 91f4146..6b099bf 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 77734ae..f7697f5 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 99fe424..b40d8cd 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 6bf9d8c..3a8b961 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 dbcd65d..91f97cf 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 702c342..66423ef 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 9278cab..17933d1 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 0000000..098f915
--- /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 0000000..a6f9af3
--- /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 */