diff options
-rw-r--r-- | gdb/Makefile.in | 2 | ||||
-rw-r--r-- | gdb/cli-out.h | 9 | ||||
-rw-r--r-- | gdb/cli/cli-interp.c | 17 | ||||
-rw-r--r-- | gdb/doc/observer.texi | 7 | ||||
-rw-r--r-- | gdb/event-top.c | 4 | ||||
-rw-r--r-- | gdb/frame.c | 2 | ||||
-rw-r--r-- | gdb/frame.h | 2 | ||||
-rw-r--r-- | gdb/gdb.h | 4 | ||||
-rw-r--r-- | gdb/gdbthread.h | 26 | ||||
-rw-r--r-- | gdb/infcall.c | 2 | ||||
-rw-r--r-- | gdb/inferior.c | 44 | ||||
-rw-r--r-- | gdb/inferior.h | 5 | ||||
-rw-r--r-- | gdb/infrun.c | 49 | ||||
-rw-r--r-- | gdb/jit.c | 3 | ||||
-rw-r--r-- | gdb/mi/mi-cmd-stack.c | 6 | ||||
-rw-r--r-- | gdb/mi/mi-interp.c | 30 | ||||
-rw-r--r-- | gdb/mi/mi-main.c | 89 | ||||
-rwxr-xr-x | gdb/observer.sh | 1 | ||||
-rw-r--r-- | gdb/stack.c | 85 | ||||
-rw-r--r-- | gdb/stack.h | 5 | ||||
-rw-r--r-- | gdb/testsuite/gdb.mi/mi-pthreads.exp | 2 | ||||
-rw-r--r-- | gdb/testsuite/gdb.mi/mi-return.exp | 2 | ||||
-rw-r--r-- | gdb/testsuite/gdb.mi/user-selected-context-sync.exp | 20 | ||||
-rw-r--r-- | gdb/thread.c | 167 | ||||
-rw-r--r-- | gdb/top.c | 2 | ||||
-rw-r--r-- | gdb/tracefile-tfile.c | 2 | ||||
-rw-r--r-- | gdb/tui/tui-interp.c | 20 | ||||
-rw-r--r-- | gdb/ui-out.h | 20 | ||||
-rw-r--r-- | gdb/user-selection.c | 357 | ||||
-rw-r--r-- | gdb/user-selection.h | 100 |
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 @@ -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); @@ -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. */ @@ -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 */ |