This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH master+7.12] Send thread, frame and inferior selection events to all uis
- From: Antoine Tremblay <antoine dot tremblay at ericsson dot com>
- To: <gdb-patches at sourceware dot org>
- Cc: Antoine Tremblay <antoine dot tremblay at ericsson dot com>
- Date: Wed, 31 Aug 2016 09:51:48 -0400
- Subject: [PATCH master+7.12] Send thread, frame and inferior selection events to all uis
- Authentication-results: sourceware.org; auth=none
With this patch when a thread, frame or inferior is explicitly selected by
the user in the console or mi all uis will be notified.
This patch fixes PR gdb/20487.
Also, this patch adds a new field to the =thread-selected event for the
frame, since inferior/thread/frame are most often a composition, thread and
frame are sent in the same event.
Front-ends need to handle this new field to properly sync the frame.
Here's a detailed example for each command:
- thread
>From console: thread 1.3
Will generate in the mi channel: =thread-selected,id="3",frame={...}
>From mi: -thread-select 3
Will generate in the console: [Switching to thread 1.3 ...
>From mi: thread 1.3
Will generate the following mi:
&"thread 1.3\n"
~"#0 child_sub_function () ...
=thread-selected,id="3",frame={level="0",...}
^done
- frame
>From console: frame 1
Will generate in the mi channel: =thread-selected,id="3",frame={level="1",...}
>From mi: -stack-select-frame 1
Will generate in the console: #1 0x00000000004007f0 in child_function...
>From mi: frame 1
Will generate the following mi:
&"frame 1\n"
~"#1 0x00000000004007f9 in ..."
=thread-selected,id="3",frame={level="1"...}
^done
- inferior
For inferior selection however it only goes from the console to mi as
there's no way to select the inferior in mi except with a cli command.
- inferior
>From mi: inferior 2
Will generate the following in mi:
&"inferior 2\n"
~"[Switching to inferior 2 ..."
=thread-selected,id="4",frame={level="0"...}
^done
>From console: inferior 2
Will generate in the mi channel: =thread-selected,id="3"
The test thread-selected-sync.exp also tests the select-frame cli command
and corner cases of all selection commands.
Note also that this patch makes it possible to suppress notifications
caused by a cli command, like was done in mi-interp previously.
This means that it's now possible to use the add_com_suppress_notification
function to register a command with some event suppressed like is done
with the select-frame command in this patch.
No regressions, tested on ubuntu 14.04 x86.
With gdb and native-extented-gdbserver
gdb/ChangeLog:
PR gdb/20487
* NEWS: Mention new frame field of =thread-selected event.
* cli/cli-decode.c (add_cmd): Initialize supress_notification.
(add_com_suppress_notification): New function.
(cmd_func): Set the suppress_notification flag and reset it after
cmd->func call.
* cli/cli-decode.h (struct
cmd_list_element)<suppress_notification>: New field.
* cli/cli-interp.c (cli_suppress_notification): Initialize
cli_suppress_notification.
(cli_on_user_selected_inf_thread_frame): New function.
(_initialize_cli_interp): Add user_selected_inf_thread_frame observer.
* command.h (struct cli_suppress_notification): New struct.
(cli_suppress_notification): Struct declaration.
(add_com_suppress_notification): New function.
* defs.h (enum user_selected_what_flag): New enum.
(user_selected_what): New enum flag type.
* frame.h (print_stack_frame_to_uiout): New function.
* gdbthread.h (print_selected_thread_frame): New function declaration.
* inferior.c (print_selected_inferior): New function.
(inferior_command): Remove printing the inferior this is now done
by an event.
(inferior_command): Notify user_selected_inf_thread_frame if the
inferior changed. Let the event print to cli/mi.
* inferior.h (print_selected_inferior): New function declaration.
* mi/mi-cmds.c (struct mi_cmd): Add user_selected_thread_frame
suppression for stack-select-frame, thread-select command.
* mi/mi-interp.c (struct
mi_suppress_notification)<user_selected_inf_thread_frame>: Init.
(mi_memory_changed): mi_user_selected_inf_thread_frame): New
* function.
(_initialize_mi_interp): Add user_selected_inf_thread_frame observer.
* mi/mi-main.c (mi_cmd_thread_select): Print thread selection response.
(mi_execute_command): Don't report thread change on
thread or inferior cli commands, notify if the thread changes.
* mi/mi-main.h (struct
mi_suppress_notification)<user_selected_inf_thread_frame>: New field.
* stack.c: include "observer.h".
(print_stack_frame_to_uiout): New function.
(select_frame_command): Notify user_selected_inf_thread_frame.
(frame_command): Use print_selected_thread_frame if there's no
change or notify if there is one.
(up_command): Notify user_selected_inf_thread_frame.
(down_command): Likewise.
(_initialize_stack): Suppress event in cli for command
select-frame.
* thread.c (thread_command): Print if thread has not changed.
(do_captured_thread_select): Let mi_cmd_thread_select print.
(print_selected_thread_frame): New function.
* tui/tui-interp.c (tui_on_user_selected_inf_thread_frame):
New function.
(_initialize_tui_interp): Add user_selected_inf_thread_frame observer.
gdb/doc/ChangeLog:
PR gdb/20487
* observer.texi (user_selected_inf_thread_frame): New function.
gdb/testsuite/ChangeLog:
PR gdb/20487
* gdb.mi/thread-selected-sync.c: New file.
* gdb.mi/thread-selected-sync.exp: Likewise.
* gdb.mi/mi-pthreads.exp(check_mi_thread_command_set): Adapt
=thread-select-event check.
---
gdb/NEWS | 4 +
gdb/cli/cli-decode.c | 31 +-
gdb/cli/cli-decode.h | 6 +
gdb/cli/cli-interp.c | 36 +
gdb/command.h | 16 +
gdb/defs.h | 15 +
gdb/doc/observer.texi | 4 +
gdb/frame.h | 8 +
gdb/gdbthread.h | 4 +
gdb/inferior.c | 40 +-
gdb/inferior.h | 3 +
gdb/mi/mi-cmds.c | 6 +-
gdb/mi/mi-interp.c | 57 ++
gdb/mi/mi-main.c | 45 +-
gdb/mi/mi-main.h | 2 +
gdb/stack.c | 39 +-
gdb/testsuite/gdb.mi/mi-pthreads.exp | 2 +-
gdb/testsuite/gdb.mi/thread-selected-sync.c | 64 ++
gdb/testsuite/gdb.mi/thread-selected-sync.exp | 1021 +++++++++++++++++++++++++
gdb/thread.c | 80 +-
gdb/tui/tui-interp.c | 31 +
21 files changed, 1451 insertions(+), 63 deletions(-)
create mode 100644 gdb/testsuite/gdb.mi/thread-selected-sync.c
create mode 100644 gdb/testsuite/gdb.mi/thread-selected-sync.exp
diff --git a/gdb/NEWS b/gdb/NEWS
index 6f5feb1..b427a7e 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -11,6 +11,10 @@
*** Changes in GDB 7.12
+* MI =thread-selected event now includes the frame field. For example:
+
+ =thread-selected,id="3",frame={level="0",addr="0x00000000004007c0"}
+
* GDB and GDBserver now build with a C++ compiler by default.
The --enable-build-with-cxx configure option is now enabled by
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 0d2b137..bd52dd2 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -253,6 +253,7 @@ add_cmd (const char *name, enum command_class theclass, cmd_cfunc_ftype *fun,
c->user_commands = NULL;
c->cmd_pointer = NULL;
c->alias_chain = NULL;
+ c->suppress_notification = NULL;
return c;
}
@@ -883,6 +884,21 @@ add_com_alias (const char *name, const char *oldname, enum command_class theclas
{
return add_alias_cmd (name, oldname, theclass, abbrev_flag, &cmdlist);
}
+
+/* Add an element with a suppress notification to the list of commands. */
+
+struct cmd_list_element *
+add_com_suppress_notification (const char *name, enum command_class theclass,
+ cmd_cfunc_ftype *fun, const char *doc,
+ int *suppress_notification)
+{
+ struct cmd_list_element *element;
+
+ element = add_cmd (name, theclass, fun, doc, &cmdlist);
+ element->suppress_notification = suppress_notification;
+
+ return element;
+}
/* Recursively walk the commandlist structures, and print out the
documentation of commands that match our regex in either their
@@ -1884,8 +1900,21 @@ cmd_func_p (struct cmd_list_element *cmd)
void
cmd_func (struct cmd_list_element *cmd, char *args, int from_tty)
{
+ struct cleanup *cleanup = NULL;
+
if (cmd_func_p (cmd))
- (*cmd->func) (cmd, args, from_tty);
+ {
+ if (cmd->suppress_notification != NULL)
+ {
+ cleanup = make_cleanup_restore_integer (cmd->suppress_notification);
+ *cmd->suppress_notification = 1;
+ }
+
+ (*cmd->func) (cmd, args, from_tty);
+
+ if (cleanup != NULL)
+ do_cleanups(cleanup);
+ }
else
error (_("Invalid command"));
}
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index 4ea8063..4ef2e1b 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -218,6 +218,12 @@ struct cmd_list_element
/* Link pointer for aliases on an alias list. */
struct cmd_list_element *alias_chain;
+
+ /* If non-null, the pointer to a field in 'struct
+ cli_suppress_notification', which will be set to true in cmd_func
+ when this command is being executed. It will be set back to false
+ when the command has been executed. */
+ int *suppress_notification;
};
extern void help_cmd_list (struct cmd_list_element *, enum command_class,
diff --git a/gdb/cli/cli-interp.c b/gdb/cli/cli-interp.c
index 5d67ba4..8f1a034 100644
--- a/gdb/cli/cli-interp.c
+++ b/gdb/cli/cli-interp.c
@@ -37,6 +37,12 @@ struct cli_interp
struct ui_out *cli_uiout;
};
+/* Suppress notification struct. */
+struct cli_suppress_notification cli_suppress_notification =
+ {
+ 0 /* user_selected_inf_thread_frame */
+ };
+
/* Returns the INTERP's data cast as cli_interp if INTERP is a CLI,
and returns NULL otherwise. */
@@ -229,6 +235,34 @@ cli_on_command_error (void)
display_gdb_prompt (NULL);
}
+/* Observer for the user_selected_inf_thread_frame notification. */
+
+static void
+cli_on_user_selected_inf_thread_frame (user_selected_what selection)
+{
+ struct switch_thru_all_uis state;
+ struct thread_info *tp = find_thread_ptid (inferior_ptid);
+
+ /* This event is suppressed. */
+ if (cli_suppress_notification.user_selected_inf_thread_frame)
+ return;
+
+ SWITCH_THRU_ALL_UIS (state)
+ {
+ struct cli_interp *cli = as_cli_interp (top_level_interpreter ());
+
+ if (cli == NULL)
+ continue;
+
+ if (selection & USER_SELECTED_INFERIOR)
+ print_selected_inferior (cli->cli_uiout);
+
+ if (tp != NULL
+ && ((selection & (USER_SELECTED_THREAD | USER_SELECTED_FRAME))))
+ print_selected_thread_frame (cli->cli_uiout, selection);
+ }
+}
+
/* pre_command_loop implementation. */
void
@@ -393,4 +427,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_inf_thread_frame
+ (cli_on_user_selected_inf_thread_frame);
}
diff --git a/gdb/command.h b/gdb/command.h
index ab62601..a36f05c 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -115,6 +115,17 @@ struct cmd_list_element;
typedef void cmd_cfunc_ftype (char *args, int from_tty);
+/* This structure specifies notifications to be suppressed by a cli
+ command interpreter. */
+
+struct cli_suppress_notification
+{
+ /* Inferior, thread, frame selected notification suppressed? */
+ int user_selected_inf_thread_frame;
+};
+
+extern struct cli_suppress_notification cli_suppress_notification;
+
/* Forward-declarations of the entry-points of cli/cli-decode.c. */
/* API to the manipulation of command lists. */
@@ -218,6 +229,11 @@ extern struct cmd_list_element *add_com (const char *, enum command_class,
extern struct cmd_list_element *add_com_alias (const char *, const char *,
enum command_class, int);
+extern struct cmd_list_element *add_com_suppress_notification
+ (const char *name, enum command_class theclass,
+ cmd_cfunc_ftype *fun, const char *doc,
+ int *supress_notification);
+
extern struct cmd_list_element *add_info (const char *,
cmd_cfunc_ftype *fun,
const char *);
diff --git a/gdb/defs.h b/gdb/defs.h
index fee5f41..cb180a9 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -750,6 +750,21 @@ enum block_enum
FIRST_LOCAL_BLOCK = 2
};
+/* User selection used in observer.h and multiple print functions. */
+
+enum user_selected_what_flag
+ {
+ /* Inferior selected. */
+ USER_SELECTED_INFERIOR = 1 << 1,
+
+ /* Thread selected. */
+ USER_SELECTED_THREAD = 1 << 2,
+
+ /* Frame selected. */
+ USER_SELECTED_FRAME = 1 << 3
+ };
+DEF_ENUM_FLAGS_TYPE (enum user_selected_what_flag, user_selected_what);
+
#include "utils.h"
#endif /* #ifndef DEFS_H */
diff --git a/gdb/doc/observer.texi b/gdb/doc/observer.texi
index fc7aac4..4f8f0bb 100644
--- a/gdb/doc/observer.texi
+++ b/gdb/doc/observer.texi
@@ -307,3 +307,7 @@ This observer is used for internal testing. Do not use.
See testsuite/gdb.gdb/observer.exp.
@end deftypefun
+@deftypefun void user_selected_inf_thread_frame (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 or frame has changed.
+@end deftypefun
diff --git a/gdb/frame.h b/gdb/frame.h
index 5f21bb8..de13e7d 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -704,6 +704,14 @@ extern CORE_ADDR get_pc_function_start (CORE_ADDR);
extern struct frame_info *find_relative_frame (struct frame_info *, int *);
+/* Wrapper over print_stack_frame modifying current_uiout with UIOUT for
+ the function call. */
+
+extern void print_stack_frame_to_uiout (struct ui_out *uiout,
+ struct frame_info *, int print_level,
+ enum print_what print_what,
+ int set_current_sal);
+
extern void print_stack_frame (struct frame_info *, int print_level,
enum print_what print_what,
int set_current_sal);
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index af2dc86..8f37fbb 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -630,6 +630,10 @@ extern void validate_registers_access (void);
true iff we ever detected multiple threads. */
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);
+
extern struct thread_info *thread_list;
#endif /* GDBTHREAD_H */
diff --git a/gdb/inferior.c b/gdb/inferior.c
index 47d91c7..60b3109 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -548,6 +548,24 @@ inferior_pid_to_str (int pid)
return _("<null>");
}
+/* See inferior.h. */
+
+void
+print_selected_inferior (struct ui_out *uiout)
+{
+ char buf[PATH_MAX+256];
+ struct inferior *inf = current_inferior ();
+
+ xsnprintf (buf, sizeof (buf),
+ _("[Switching to inferior %d [%s] (%s)]\n"),
+ inf->num,
+ inferior_pid_to_str (inf->pid),
+ (inf->pspace->pspace_exec_filename != NULL
+ ? inf->pspace->pspace_exec_filename
+ : _("<noexec>")));
+ ui_out_text (uiout, buf);
+}
+
/* Prints the list of inferiors and their details on UIOUT. This is a
version of 'info_inferior_command' suitable for use from MI.
@@ -726,13 +744,6 @@ inferior_command (char *args, int from_tty)
if (inf == NULL)
error (_("Inferior ID %d not known."), num);
- printf_filtered (_("[Switching to inferior %d [%s] (%s)]\n"),
- inf->num,
- inferior_pid_to_str (inf->pid),
- (inf->pspace->pspace_exec_filename != NULL
- ? inf->pspace->pspace_exec_filename
- : _("<noexec>")));
-
if (inf->pid != 0)
{
if (inf->pid != ptid_get_pid (inferior_ptid))
@@ -746,9 +757,10 @@ inferior_command (char *args, int from_tty)
switch_to_thread (tp->ptid);
}
- printf_filtered (_("[Switching to thread %s (%s)] "),
- print_thread_id (inferior_thread ()),
- target_pid_to_str (inferior_ptid));
+ observer_notify_user_selected_inf_thread_frame
+ (USER_SELECTED_INFERIOR
+ | USER_SELECTED_THREAD
+ | USER_SELECTED_FRAME);
}
else
{
@@ -758,14 +770,8 @@ inferior_command (char *args, int from_tty)
set_current_inferior (inf);
switch_to_thread (null_ptid);
set_current_program_space (inf->pspace);
- }
- if (inf->pid != 0 && is_running (inferior_ptid))
- ui_out_text (current_uiout, "(running)\n");
- else if (inf->pid != 0)
- {
- ui_out_text (current_uiout, "\n");
- print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+ observer_notify_user_selected_inf_thread_frame (USER_SELECTED_INFERIOR);
}
}
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 571d26a..54c6f65 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -523,4 +523,7 @@ 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);
+
#endif /* !defined (INFERIOR_H) */
diff --git a/gdb/mi/mi-cmds.c b/gdb/mi/mi-cmds.c
index 4779832..967b921 100644
--- a/gdb/mi/mi-cmds.c
+++ b/gdb/mi/mi-cmds.c
@@ -137,7 +137,8 @@ static struct mi_cmd mi_cmds[] =
DEF_MI_CMD_MI ("stack-list-frames", mi_cmd_stack_list_frames),
DEF_MI_CMD_MI ("stack-list-locals", mi_cmd_stack_list_locals),
DEF_MI_CMD_MI ("stack-list-variables", mi_cmd_stack_list_variables),
- DEF_MI_CMD_MI ("stack-select-frame", mi_cmd_stack_select_frame),
+ DEF_MI_CMD_MI_1 ("stack-select-frame", mi_cmd_stack_select_frame,
+ &mi_suppress_notification.user_selected_inf_thread_frame),
DEF_MI_CMD_MI ("symbol-list-lines", mi_cmd_symbol_list_lines),
DEF_MI_CMD_CLI ("target-attach", "attach", 1),
DEF_MI_CMD_MI ("target-detach", mi_cmd_target_detach),
@@ -149,7 +150,8 @@ static struct mi_cmd mi_cmds[] =
DEF_MI_CMD_CLI ("target-select", "target", 1),
DEF_MI_CMD_MI ("thread-info", mi_cmd_thread_info),
DEF_MI_CMD_MI ("thread-list-ids", mi_cmd_thread_list_ids),
- DEF_MI_CMD_MI ("thread-select", mi_cmd_thread_select),
+ DEF_MI_CMD_MI_1 ("thread-select", mi_cmd_thread_select,
+ &mi_suppress_notification.user_selected_inf_thread_frame),
DEF_MI_CMD_MI ("trace-define-variable", mi_cmd_trace_define_variable),
DEF_MI_CMD_MI_1 ("trace-find", mi_cmd_trace_find,
&mi_suppress_notification.traceframe),
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index e3c7dbd..df67c9a 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -769,6 +769,7 @@ struct mi_suppress_notification mi_suppress_notification =
0,
0,
0,
+ 0,
};
/* Emit notification on changing a traceframe. */
@@ -1334,6 +1335,60 @@ mi_memory_changed (struct inferior *inferior, CORE_ADDR memaddr,
}
}
+/* Emit an event about the user selection of inf,thread or frame. */
+
+static void
+mi_user_selected_inf_thread_frame (user_selected_what selection)
+{
+ struct switch_thru_all_uis state;
+ struct thread_info *tp = find_thread_ptid (inferior_ptid);
+
+ /* Don't send an event if we're responding to an MI command. */
+ if (mi_suppress_notification.user_selected_inf_thread_frame)
+ return;
+
+ SWITCH_THRU_ALL_UIS (state)
+ {
+ struct mi_interp *mi = as_mi_interp (top_level_interpreter ());
+ struct ui_out *mi_uiout;
+ struct cleanup *old_chain;
+
+ if (mi == NULL)
+ continue;
+
+ mi_uiout = interp_ui_out (top_level_interpreter ());
+
+ ui_out_redirect (mi_uiout, mi->event_channel);
+
+ old_chain = make_cleanup_restore_target_terminal ();
+ target_terminal_ours_for_output ();
+
+ if (selection & USER_SELECTED_INFERIOR)
+ print_selected_inferior (mi->cli_uiout);
+
+ if (tp != NULL
+ && ((selection & (USER_SELECTED_THREAD | USER_SELECTED_FRAME))))
+ {
+ print_selected_thread_frame (mi->cli_uiout, selection);
+
+ fprintf_unfiltered (mi->event_channel,
+ "thread-selected,id=\"%d\"",
+ tp->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);
+ }
+ }
+
+ ui_out_redirect (mi_uiout, NULL);
+ gdb_flush (mi->event_channel);
+ do_cleanups (old_chain);
+ }
+}
+
static int
report_initial_inferior (struct inferior *inf, void *closure)
{
@@ -1466,4 +1521,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_inf_thread_frame
+ (mi_user_selected_inf_thread_frame);
}
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 1913157..ebd5ee8 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -53,6 +53,7 @@
#include "linespec.h"
#include "extension.h"
#include "gdbcmd.h"
+#include "observer.h"
#include <ctype.h>
#include "gdb_sys_time.h"
@@ -575,6 +576,9 @@ mi_cmd_thread_select (char *command, char **argv, int argc)
make_cleanup (xfree, mi_error_message);
error ("%s", mi_error_message);
}
+
+ print_selected_thread_frame (interp_ui_out (top_level_interpreter ()),
+ USER_SELECTED_THREAD | USER_SELECTED_FRAME);
}
void
@@ -2102,6 +2106,7 @@ mi_execute_command (const char *cmd, int from_tty)
{
char *token;
struct mi_parse *command = NULL;
+ struct cleanup *cleanup = NULL;
/* This is to handle EOF (^D). We just quit gdb. */
/* FIXME: we should call some API function here. */
@@ -2161,10 +2166,15 @@ mi_execute_command (const char *cmd, int from_tty)
/* Don't try report anything if there are no threads --
the program is dead. */
&& thread_count () != 0
- /* -thread-select explicitly changes thread. If frontend uses that
- internally, we don't want to emit =thread-selected, since
- =thread-selected is supposed to indicate user's intentions. */
- && strcmp (command->command, "thread-select") != 0)
+ /* For the cli commands thread and inferior, the event is already sent
+ by the command, don't send it again. */
+ && ((command->op == CLI_COMMAND
+ && strncmp (command->command, "thread", 6) != 0
+ && strncmp (command->command, "inferior", 8) != 0)
+ || (command->op == MI_COMMAND && command->argc <= 1)
+ || (command->op == MI_COMMAND && command->argc > 1
+ && strncmp (command->argv[1], "thread", 6) != 0
+ && strncmp (command->argv[1], "inferior", 8) != 0)))
{
struct mi_interp *mi
= (struct mi_interp *) top_level_interpreter_data ();
@@ -2185,18 +2195,21 @@ mi_execute_command (const char *cmd, int from_tty)
if (report_change)
{
- struct thread_info *ti = inferior_thread ();
- struct cleanup *old_chain;
-
- old_chain = make_cleanup_restore_target_terminal ();
- target_terminal_ours_for_output ();
-
- fprintf_unfiltered (mi->event_channel,
- "thread-selected,id=\"%d\"",
- ti->global_num);
- gdb_flush (mi->event_channel);
-
- do_cleanups (old_chain);
+ /* Make sure we still keep event suppression. This is
+ handled in mi_cmd_execute so at this point this has been
+ reset. We still need it here however. */
+ if (command->cmd->suppress_notification != NULL)
+ {
+ cleanup = make_cleanup_restore_integer
+ (command->cmd->suppress_notification);
+ *command->cmd->suppress_notification = 1;
+ }
+
+ observer_notify_user_selected_inf_thread_frame
+ (USER_SELECTED_THREAD | USER_SELECTED_FRAME);
+
+ if (cleanup != NULL)
+ do_cleanups (cleanup);
}
}
diff --git a/gdb/mi/mi-main.h b/gdb/mi/mi-main.h
index 18000cf..f5b6bcf 100644
--- a/gdb/mi/mi-main.h
+++ b/gdb/mi/mi-main.h
@@ -49,6 +49,8 @@ struct mi_suppress_notification
int traceframe;
/* Memory changed notification suppressed? */
int memory;
+ /* Inferior, thread, frame selected notification suppressed? */
+ int user_selected_inf_thread_frame;
};
extern struct mi_suppress_notification mi_suppress_notification;
diff --git a/gdb/stack.c b/gdb/stack.c
index 417e887..80ce00b 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -51,6 +51,7 @@
#include "safe-ctype.h"
#include "symfile.h"
#include "extension.h"
+#include "observer.h"
/* The possible choices of "set print frame-arguments", and the value
of this setting. */
@@ -141,6 +142,22 @@ frame_show_address (struct frame_info *frame,
return get_frame_pc (frame) != sal.pc;
}
+/* See frame.h */
+
+void
+print_stack_frame_to_uiout (struct ui_out *uiout, struct frame_info *frame,
+ int print_level, enum print_what print_what,
+ int set_current_sal)
+{
+ struct ui_out *saved_uiout;
+ saved_uiout = current_uiout;
+ current_uiout = uiout;
+
+ print_stack_frame (frame, print_level, print_what, set_current_sal);
+
+ current_uiout = saved_uiout;
+}
+
/* Show or print a stack frame FRAME briefly. The output is formatted
according to PRINT_LEVEL and PRINT_WHAT printing the frame's
relative level, function name, argument list, and file name and
@@ -2302,7 +2319,11 @@ find_relative_frame (struct frame_info *frame, int *level_offset_ptr)
void
select_frame_command (char *level_exp, int from_tty)
{
+ struct frame_info *prev_frame = get_selected_frame_if_set ();
+
select_frame (parse_frame_specification (level_exp, NULL));
+ if (get_selected_frame_if_set () != prev_frame)
+ observer_notify_user_selected_inf_thread_frame (USER_SELECTED_FRAME);
}
/* The "frame" command. With no argument, print the selected frame
@@ -2312,8 +2333,13 @@ select_frame_command (char *level_exp, int from_tty)
static void
frame_command (char *level_exp, int from_tty)
{
- select_frame_command (level_exp, from_tty);
- print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+ struct frame_info *prev_frame = get_selected_frame_if_set ();
+
+ select_frame (parse_frame_specification (level_exp, NULL));
+ if (get_selected_frame_if_set () != prev_frame)
+ observer_notify_user_selected_inf_thread_frame (USER_SELECTED_FRAME);
+ else
+ print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
}
/* Select the frame up one or COUNT_EXP stack levels from the
@@ -2344,7 +2370,7 @@ static void
up_command (char *count_exp, int from_tty)
{
up_silently_base (count_exp);
- print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+ observer_notify_user_selected_inf_thread_frame (USER_SELECTED_FRAME);
}
/* Select the frame down one or COUNT_EXP stack levels from the previously
@@ -2383,7 +2409,7 @@ static void
down_command (char *count_exp, int from_tty)
{
down_silently_base (count_exp);
- print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+ observer_notify_user_selected_inf_thread_frame (USER_SELECTED_FRAME);
}
@@ -2616,10 +2642,11 @@ a command file or a user-defined command."));
add_com_alias ("f", "frame", class_stack, 1);
- add_com ("select-frame", class_stack, select_frame_command, _("\
+ add_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\
Select a stack frame without printing anything.\n\
An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame.\n"));
+It can be a stack frame number or the address of the frame.\n"),
+ &cli_suppress_notification.user_selected_inf_thread_frame);
add_com ("backtrace", class_stack, backtrace_command, _("\
Print backtrace of all stack frames, or innermost COUNT frames.\n\
diff --git a/gdb/testsuite/gdb.mi/mi-pthreads.exp b/gdb/testsuite/gdb.mi/mi-pthreads.exp
index 88a600a..a49856e 100644
--- a/gdb/testsuite/gdb.mi/mi-pthreads.exp
+++ b/gdb/testsuite/gdb.mi/mi-pthreads.exp
@@ -53,7 +53,7 @@ proc check_mi_thread_command_set {} {
foreach thread $thread_list {
mi_gdb_test "-interpreter-exec console \"thread $thread\"" \
- ".*\\^done\r\n=thread-selected,id=\"$thread\"" \
+ ".*=thread-selected,id=\"$thread\".*\[r\n\]+\\^done\[\r\n\]+" \
"check =thread-selected: thread $thread"
}
}
diff --git a/gdb/testsuite/gdb.mi/thread-selected-sync.c b/gdb/testsuite/gdb.mi/thread-selected-sync.c
new file mode 100644
index 0000000..4113d26
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/thread-selected-sync.c
@@ -0,0 +1,64 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2016 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+/* Note that this test is not expected to exit cleanly. All threads will
+ block at the barrier and they won't be waken up. */
+
+#include <pthread.h>
+#include <unistd.h>
+
+#define NUM_THREADS 2
+
+pthread_barrier_t barrier;
+
+void
+child_sub_function ()
+{
+ int test = 0;
+ test++; /* set break here */
+ pthread_barrier_wait (&barrier);
+ pthread_exit (NULL);
+}
+
+void *
+child_function (void *args)
+{
+ child_sub_function (); /* caller */
+}
+
+pthread_t child_thread[NUM_THREADS];
+
+int
+main (void)
+{
+ int i = 0;
+ pthread_barrier_init (&barrier, NULL, NUM_THREADS + 1);
+
+ for (i = 0; i < NUM_THREADS; i++)
+ {
+ pthread_create (&child_thread[i], NULL, child_function, NULL);
+ }
+
+ for (i = 0; i < NUM_THREADS; i++)
+ {
+ pthread_join (child_thread[i], NULL);
+ }
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.mi/thread-selected-sync.exp b/gdb/testsuite/gdb.mi/thread-selected-sync.exp
new file mode 100644
index 0000000..c36d018
--- /dev/null
+++ b/gdb/testsuite/gdb.mi/thread-selected-sync.exp
@@ -0,0 +1,1021 @@
+# Copyright 2016 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This test checks that thread, select-frame, frame or inferior selection
+# events are properly sent to all uis.
+#
+# This test considers the case where console and mi are two different uis
+# and mi is created with the new-ui command.
+#
+# It also considers the case were the console commands are sent directly in
+# the mi channel as described in PR 20487.
+#
+# It does so by starting 2 inferiors with 3 threads each.
+# Thread 1 is the main thread starting the others.
+# Thread 2 is stopped in non-stop mode.
+# Thread 3 is the thread used for testing, in non-stop mode this thread is running.
+
+load_lib mi-support.exp
+
+standard_testfile
+
+# Multiple inferiors are needed, therefore only native gdb and extended
+# gdbserver modes are supported.
+if [use_gdb_stub] {
+ untested ${testfile}.exp
+ return
+}
+
+set compile_options "debug pthreads"
+if {[build_executable $testfile.exp $testfile ${srcfile} ${compile_options}] == -1} {
+ untested "failed to compile $testfile"
+ return -1
+}
+
+set bp_lineno [gdb_get_line_number "set break here"]
+set caller_lineno [gdb_get_line_number "caller"]
+
+# Make a regular expression for cli.
+
+proc make_cli_re {mode inf thread frame lineend} {
+ global srcfile bp_lineno caller_lineno
+
+ set inf_re "\\\[Switching to inferior $inf.*\\\]"
+ set all_stop_thread_re "\\\[Switching to thread $thread.*\\\]"
+ set non_stop_thread_re "$all_stop_thread_re\\\(running\\\)"
+ set frame_re(0) "#0.*child_sub_function.*$srcfile:$bp_lineno\[\r\n\]+.*set break here \\\*/"
+ set frame_re(1) "#1.*child_function \\\(args=0x0\\\) at .*$srcfile:$caller_lineno\[\r\n\]+$caller_lineno.*caller \\\*/"
+ # Special frame for main threads.
+ set frame_re(2) "#0.*"
+ set end "\[\r\n\]"
+
+ set cli_re ""
+
+ if {$inf != -1} {
+ append cli_re $inf_re
+ }
+
+ if {$thread != "-1"} {
+ if {$inf != -1} {
+ append cli_re "\[\r\n\]+"
+ }
+ set thread_re ""
+ if {$mode == "all-stop"} {
+ set thread_re $all_stop_thread_re
+ } elseif {$mode == "non-stop"} {
+ set thread_re $non_stop_thread_re
+ }
+ append cli_re $thread_re
+ }
+
+ if {$mode == "all-stop" && $frame != -1} {
+ if {$thread != -1} {
+ append cli_re "\[\r\n\]+"
+ }
+ append cli_re $frame_re($frame)
+ }
+
+ if {$lineend == 1} {
+ append cli_re $end
+ }
+
+ return $cli_re
+}
+
+# Make a regular expression for mi.
+
+proc make_mi_re {mode thread event frame lineend} {
+ global srcfile bp_lineno caller_lineno hex
+
+ set mi_re ""
+ set thread_event_re "=thread-selected,id=\"$thread\""
+ set thread_answer_re ".*\\^done,new-thread-id=\"$thread\""
+ set frame_re(0) ",frame=\{level=\"0\",addr=\"$hex\",func=\"child_sub_function\",args=\\\[\\\],file=\".*$srcfile\",.*line=\"$bp_lineno\"\}"
+ set frame_re(1) ",frame=\{level=\"1\",addr=\"$hex\",func=\"child_function\",args=\\\[\{name=\"args\",value=\"0x0\"\}\\\],file=\".*$srcfile\",.*line=\"$caller_lineno\"\}"
+ #Special frame for main thread.
+ set frame_re(2) ",frame=\{level=\"0\",addr=\"$hex\",func=\".*\",args=\\\[\\\],from=\".*\"\}"
+ set end "\[\r\n\]"
+
+ if {$thread != "-1"} {
+ if {$event == 1} {
+ append mi_re $thread_event_re
+ } elseif {$event == 0} {
+ append mi_re $thread_answer_re
+ }
+ }
+
+ if {$mode == "all-stop" && $frame != -1} {
+ append mi_re $frame_re($frame)
+ }
+
+ if {$lineend == 1} {
+ append mi_re $end
+ }
+
+ return $mi_re
+}
+
+# Make a regular expression for cli in mi.
+
+proc make_cli_in_mi_re {command mode inf cli_thread mi_thread event frame lineend} {
+ global srcfile bp_lineno caller_lineno
+
+ set cli_in_mi_re ".*$command.*\[\r\n\]+"
+ set frame_re(0) "~\"#0.*child_sub_function.*$srcfile:$bp_lineno\\\\n.*set break here \\\*/\\\\n\"\[\r\n\]+"
+ set frame_re(1) "~\"#1.*child_function \\\(args=0x0\\\) at .*$srcfile:$caller_lineno\\\\n\"\[\r\n\]+~\"$caller_lineno.*caller \\\*/\\\\n\"\[\r\n\]+"
+ set frame_re(2) "~\"#0.*\\\\n\"\[\r\n\]+"
+
+ if {$inf != -1} {
+ append cli_in_mi_re "~\""
+ append cli_in_mi_re [make_cli_re $mode $inf "-1" -1 0]
+ append cli_in_mi_re "\\\\n\"\[\r\n\]+"
+ }
+
+ if {$cli_thread != "-1"} {
+ append cli_in_mi_re "~\""
+ append cli_in_mi_re [make_cli_re $mode -1 $cli_thread -1 0]
+ append cli_in_mi_re "\\\\n\"\[\r\n\]+"
+ }
+
+ if {$mode == "all-stop"} {
+ append cli_in_mi_re $frame_re($frame)
+ }
+
+ if {$event != -1} {
+ append cli_in_mi_re [make_mi_re $mode $mi_thread $event $frame $lineend]
+ }
+ append cli_in_mi_re "\[\r\n\]+\\^done"
+
+ return $cli_in_mi_re
+}
+
+# Continue to the breakpoint indicating the start position of the threads.
+
+proc test_continue_to_start {mode inf} {
+ global gdb_prompt mi_spawn_id
+
+ if {$mode == "all-stop"} {
+ for {set i 1} { $i <= 2 } { incr i } {
+ gdb_continue_to_breakpoint "barrier breakpoint"
+ }
+ } else {
+ set test "continue&"
+
+ gdb_test_multiple $test $test {
+ -re "Continuing\\.\[\r\n\]+$gdb_prompt " {
+ pass $test
+ }
+ }
+
+ # Wait until we've hit the breakpoint for the 2 threads.
+ for {set i 1} { $i <= 2 } { incr i } {
+ set test "thread $i started"
+ gdb_test_multiple "" $test {
+ -re "hit Breakpoint" {
+ # The prompt was already matched in the "continue &"
+ # test above. We're now consuming asynchronous output
+ # that comes after the prompt.
+ pass $test
+ }
+ }
+ }
+ # Switch to the test thread.
+ if {$inf == 1} {
+ gdb_test "thread 3" [make_cli_re "all-stop" -1 "3" 0 -1] "Switch to test thread"
+ } else {
+ gdb_test "thread 2.3" [make_cli_re "all-stop" -1 "2\\.3" 0 -1] "Switch to test thread"
+ }
+
+ gdb_test "continue&" "Continuing\\."
+ }
+}
+
+# Test context setup.
+
+proc test_setup {mode} {
+ global srcfile srcdir subdir testfile
+ global gdb_main_spawn_id mi_spawn_id
+ global decimal binfile bp_lineno
+ global GDBFLAGS
+
+ mi_gdb_exit
+
+ set saved_gdbflags $GDBFLAGS
+
+ if {$mode == "non-stop"} {
+ set GDBFLAGS [concat $GDBFLAGS " -ex \"set non-stop 1\""]
+ }
+
+ if {[mi_gdb_start "separate-mi-tty"] != 0} {
+ return
+ }
+
+ mi_delete_breakpoints
+ mi_gdb_reinitialize_dir $srcdir/$subdir
+ mi_gdb_load $binfile
+
+ if {[mi_runto main] < 0} {
+ fail "Can't run to main"
+ return
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+
+ gdb_test "break $srcfile:$bp_lineno" \
+ "Breakpoint $decimal .*$srcfile, line $bp_lineno\\\." \
+ "set breakpoint"
+
+ test_continue_to_start $mode 1
+
+ # Add a second inferior to test inferior selection.
+ gdb_test "add-inferior" "Added inferior 2" "Add inferior 2"
+ gdb_test "inferior 2" [make_cli_re $mode 2 "-1" -1 -1]
+ gdb_load ${binfile}
+ gdb_test "start" "Temporary breakpoint.*Starting program.*"
+ test_continue_to_start $mode 2
+ gdb_test "inferior 1" [make_cli_re $mode 1 "1\\.1" 2 -1]
+ }
+
+ set GDBFLAGS $saved_gdbflags
+}
+
+# Test selecting an inferior from cli.
+
+proc test_cli_select_inferior {mode} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ set mi_re [make_mi_re $mode "4" 1 2 1]
+ set cli_re [make_cli_re $mode 2 "2\\.1" 2 0]
+
+ with_spawn_id $gdb_main_spawn_id {
+ gdb_test "inferior 2" $cli_re "cli select inferior"
+ }
+
+ with_spawn_id $mi_spawn_id {
+ set test "mi select inferior"
+ gdb_test_multiple "" $test {
+ -re $mi_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test thread selection from cli.
+
+proc test_cli_select_thread {mode} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ set mi_re [make_mi_re $mode "3" 1 0 1]
+ set cli_re [make_cli_re $mode -1 "1\\.3" 0 0]
+
+ with_spawn_id $gdb_main_spawn_id {
+ gdb_test "thread 1.3" $cli_re "cli select thread"
+ }
+
+ with_spawn_id $mi_spawn_id {
+ set test "mi select thread"
+ gdb_test_multiple "" $test {
+ -re $mi_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test selecting the same thread twice from the cli.
+
+proc test_cli_select_thread_twice {mode} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ set mi_re [make_mi_re $mode "3" 1 0 1]
+ set cli_re [make_cli_re $mode -1 "1\\.3" 0 0]
+
+ with_spawn_id $gdb_main_spawn_id {
+ gdb_test "thread 1.3" $cli_re "cli select thread twice, first call"
+ }
+
+ with_spawn_id $mi_spawn_id {
+ set test "mi select thread twice, first call"
+ gdb_test_multiple "" $test {
+ -re $mi_re {
+ pass $test
+ }
+ }
+ }
+
+ # No event for that one.
+ set mi_re ""
+ set cli_re [make_cli_re $mode -1 "1\\.3" 0 0]
+
+ with_spawn_id $gdb_main_spawn_id {
+ gdb_test "thread 1.3" $cli_re "cli select thread twice, second call"
+ }
+
+ with_spawn_id $mi_spawn_id {
+ set test "mi select thread twice, second call"
+ gdb_test_multiple "" $test {
+ -re $mi_re {
+ pass $test
+ }
+ }
+ }
+
+}
+
+# Test frame selection from cli.
+
+proc test_cli_select_frame {mode frame} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ if {$mode == "all-stop"} {
+ set mi_re [make_mi_re $mode "3" 1 $frame 1]
+ set cli_re [make_cli_re $mode -1 "-1" $frame 0]
+ } elseif {$mode == "non-stop"} {
+ set cli_re "Selected thread is running\\."
+ # No output
+ set mi_re ""
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ gdb_test "frame $frame" $cli_re "cli select frame"
+ }
+
+ with_spawn_id $mi_spawn_id {
+ set test "mi select frame"
+ gdb_test_multiple "" $test {
+ -re $mi_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test frame selection from cli with the select-frame command.
+
+proc test_cli_select_select_frame {mode frame} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ if {$mode == "all-stop"} {
+ set cli_re ""
+ set mi_re [make_mi_re $mode "3" 1 $frame 1]
+ } elseif {$mode == "non-stop"} {
+ set cli_re "Selected thread is running\\."
+ # No output
+ set mi_re ""
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ if {$cli_re != ""} {
+ gdb_test "select-frame $frame" $cli_re "cli select frame with select-frame"
+ } else {
+ gdb_test_no_output "select-frame $frame" $cli_re "cli select frame with select-frame"
+ }
+ }
+
+ with_spawn_id $mi_spawn_id {
+ set test "mi select frame with select-frame"
+ gdb_test_multiple "" $test {
+ -re $mi_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test doing and up and then down command from cli.
+
+proc test_cli_up_down {mode} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ if {$mode == "all-stop"} {
+ set cli_up_re [make_cli_re $mode -1 "-1" 1 0]
+ set mi_up_re [make_mi_re $mode "3" 1 1 1]
+ set cli_down_re [make_cli_re $mode -1 "-1" 0 0]
+ set mi_down_re [make_mi_re $mode "3" 1 0 1]
+ } elseif {$mode == "non-stop"} {
+ set cli_up_re "No stack\\."
+ set mi_up_re ""
+ set cli_down_re "No stack\\."
+ set mi_down_re ""
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ gdb_test "up" $cli_up_re "cli up"
+ }
+
+ with_spawn_id $mi_spawn_id {
+ set test "mi up"
+ gdb_test_multiple "" $test {
+ -re $mi_up_re {
+ pass $test
+ }
+ }
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ gdb_test "down" $cli_down_re "cli down"
+ }
+
+ with_spawn_id $mi_spawn_id {
+ set test "mi down"
+ gdb_test_multiple "" $test {
+ -re $mi_down_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test same frame selection from cli.
+
+proc test_cli_select_frame_twice {mode frame} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ if {$mode == "all-stop"} {
+ set mi_re [make_mi_re $mode "3" 1 $frame 1]
+ set cli_re [make_cli_re $mode -1 "-1" $frame 0]
+ } elseif {$mode == "non-stop"} {
+ set cli_re "Selected thread is running\\."
+ # No output
+ set mi_re ""
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ gdb_test "frame $frame" $cli_re "cli select frame twice, first call"
+ }
+
+ with_spawn_id $mi_spawn_id {
+ set test "mi select frame twice, first call"
+ gdb_test_multiple "" $test {
+ -re $mi_re {
+ pass $test
+ }
+ }
+ }
+
+ if {$mode == "all-stop"} {
+ #No output thread has not changed.
+ set mi_re ""
+ set cli_re [make_cli_re $mode -1 "-1" $frame 0]
+ } elseif {$mode == "non-stop"} {
+ set cli_re "Selected thread is running\\."
+ # No output
+ set mi_re ""
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ gdb_test "frame $frame" $cli_re "cli select frame twice, second call"
+ }
+
+ with_spawn_id $mi_spawn_id {
+ set test "mi select frame, second call"
+ gdb_test_multiple "" $test {
+ -re $mi_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test thread command without any arguments fromt the cli.
+
+proc test_cli_select_thread_no_args {mode} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ set cli_re "\\\[Current thread is 1\\.3.*\\\]"
+ set mi_re ""
+
+ with_spawn_id $gdb_main_spawn_id {
+ gdb_test "thread" $cli_re "cli thread no args"
+ }
+
+ with_spawn_id $mi_spawn_id {
+ set test "mi thread no args"
+ gdb_test_multiple "" $test {
+ -re $mi_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test frame command without any arguments from the cli.
+
+proc test_cli_select_frame_no_args {mode} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ if {$mode == "all-stop"} {
+ set mi_re ""
+ set cli_re [make_cli_re $mode -1 "-1" 0 0]
+ } elseif {$mode == "non-stop"} {
+ set cli_re "No stack\\."
+ # No output
+ set mi_re ""
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ gdb_test "frame" $cli_re "cli frame no args"
+ }
+
+ with_spawn_id $mi_spawn_id {
+ set test "mi frame no args"
+ gdb_test_multiple "" $test {
+ -re $mi_re {
+ pass $test
+ }
+ }
+ }
+}
+
+
+# Test selecting a thread from mi with a thread group. This test verifies
+# that even if the thread GDB would switch to is the same has the
+# thread-group selected thread, that an event is still sent to cli.
+# In this case this is thread 1.2
+
+proc test_mi_select_thread_with_thread_group {mode} {
+
+ # This only applies to non-stop mode.
+ if {$mode == "all-stop"} {
+ return;
+ }
+ global gdb_main_spawn_id mi_spawn_id
+
+ set mi_re [make_mi_re "all-stop" "2" 0 0 0]
+ set cli_re [make_cli_re "all-stop" -1 "1\\.2" 0 1]
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test "-thread-select --thread-group i1 2" $mi_re "mi select thread with thread group"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select thread with thread group"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test selecting a thread from mi.
+
+proc test_mi_select_thread {mode} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ set mi_re [make_mi_re $mode "3" 0 0 0]
+ set cli_re [make_cli_re $mode -1 "1\\.3" 0 1]
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test "-thread-select 3" $mi_re "mi select thread"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select thread"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test selecting the same thread from mi.
+
+proc test_mi_select_thread_twice {mode} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ set mi_re [make_mi_re $mode "3" 0 0 0]
+ set cli_re [make_cli_re $mode -1 "1\\.3" 0 1]
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test "-thread-select 3" $mi_re "mi select thread twice, first call"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select thread twice, frist call"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+
+ set mi_re [make_mi_re $mode "3" 0 0 0]
+ # No event here.
+ set cli_re ""
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test "-thread-select 3" $mi_re "mi select thread twice, second call"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select thread twice, second call"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test selecting a frame from mi.
+
+proc test_mi_select_frame {mode frame} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ if {$mode == "all-stop"} {
+ set cli_re [make_cli_re $mode -1 "-1" $frame 1]
+ set mi_re ".*\\^done"
+ } elseif {$mode == "non-stop"} {
+ # No output
+ set cli_re ""
+ set mi_re ".*\\^error,msg=\"Selected thread is running\\.\""
+ }
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test "-stack-select-frame $frame" $mi_re "mi select frame"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select frame"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test selecting the same frame from mi.
+
+proc test_mi_select_frame_twice {mode frame} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ if {$mode == "all-stop"} {
+ set cli_re [make_cli_re $mode -1 "-1" $frame 1]
+ set mi_re ".*\\^done"
+ } elseif {$mode == "non-stop"} {
+ # No output
+ set cli_re ""
+ set mi_re ".*\\^error,msg=\"Selected thread is running\\.\""
+ }
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test "-stack-select-frame $frame" $mi_re "mi select frame twice, frist call"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select frame twice, frist call"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+
+ if {$mode == "all-stop"} {
+ set cli_re ""
+ set mi_re ".*\\^done"
+ } elseif {$mode == "non-stop"} {
+ # No output
+ set cli_re ""
+ set mi_re ".*\\^error,msg=\"Selected thread is running\\.\""
+ }
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test "-stack-select-frame $frame" $mi_re "mi select frame twice, second call"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select frame twice, second call"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+
+}
+
+# Test selecting the inferior using a cli command in the mi channel.
+
+proc test_cli_in_mi_select_inferior {mode exec_mode} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ if {$exec_mode == "interpreter-exec"} {
+ set command "-interpreter-exec console \"inferior 2\""
+ } else {
+ set command "inferior 2"
+ }
+
+ set mi_re [make_cli_in_mi_re [string_to_regexp $command] $mode 2 "2\\.1" "4" 1 2 0]
+ set cli_re [make_cli_re $mode 2 "2\\.1" 2 1]
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test $command $mi_re "mi select inferior"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select inferior"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test selecting the thread using a cli command in the mi channel.
+
+proc test_cli_in_mi_select_thread {mode exec_mode} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ if {$exec_mode == "interpreter-exec"} {
+ set command "-interpreter-exec console \"thread 1.3\""
+ } else {
+ set command "thread 1.3"
+ }
+
+ set mi_re [make_cli_in_mi_re [string_to_regexp $command] $mode -1 "1\\.3" "3" 1 0 0]
+ set cli_re [make_cli_re $mode -1 "1\\.3" 0 1]
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test $command $mi_re "mi select thread"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select thread"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test selecting the frame using a cli command in the mi channel.
+
+proc test_cli_in_mi_select_frame {mode exec_mode frame} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ if {$exec_mode == "interpreter-exec"} {
+ set command "-interpreter-exec console \"frame $frame\""
+ } else {
+ set command "frame $frame"
+ }
+
+ if {$mode == "all-stop"} {
+ set cli_re [make_cli_re $mode -1 "-1" $frame 1]
+ set mi_re [make_cli_in_mi_re [string_to_regexp $command] $mode -1 "-1" 3 1 $frame 0]
+ } elseif {$mode == "non-stop"} {
+ set cli_re ""
+ set mi_re ".*\\^error,msg=\"Selected thread is running\\.\""
+ }
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test $command $mi_re "mi select frame"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select frame"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test selecting the same thread using a cli command in the mi channel.
+
+proc test_cli_in_mi_select_thread_twice {mode exec_mode} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ if {$exec_mode == "interpreter-exec"} {
+ set command "-interpreter-exec console \"thread 1.3\""
+ } else {
+ set command "thread 1.3"
+ }
+
+ set mi_re [make_cli_in_mi_re [string_to_regexp $command] $mode -1 "1\\.3" "3" 1 0 0]
+ set cli_re [make_cli_re $mode -1 "1\\.3" 0 1]
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test $command $mi_re "mi select thread twice, first call"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select thread twice, first call"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+
+ set mi_re [make_cli_in_mi_re [string_to_regexp $command] $mode -1 "1\\.3" "3" -1 0 0]
+ set cli_re ""
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test $command $mi_re "mi select thread twice, second call"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select thread twice, second call"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+
+}
+
+# Test selecting the same frame using a cli command in the mi channel.
+
+proc test_cli_in_mi_select_frame_twice {mode exec_mode frame} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ if {$exec_mode == "interpreter-exec"} {
+ set command "-interpreter-exec console \"frame $frame\""
+ } else {
+ set command "frame $frame"
+ }
+
+ if {$mode == "all-stop"} {
+ set cli_re [make_cli_re $mode -1 "-1" $frame 1]
+ set mi_re [make_cli_in_mi_re [string_to_regexp $command] $mode -1 "-1" 3 1 $frame 0]
+ } elseif {$mode == "non-stop"} {
+ set cli_re ""
+ set mi_re ".*\\^error,msg=\"Selected thread is running\\.\""
+ }
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test $command $mi_re "mi select frame twice, first call"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select frame twice, first call"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+
+ if {$mode == "all-stop"} {
+ set cli_re ""
+ set mi_re [make_cli_in_mi_re [string_to_regexp $command] $mode -1 "-1" 3 -1 $frame 0]
+ } elseif {$mode == "non-stop"} {
+ set cli_re ""
+ set mi_re ".*\\^error,msg=\"Selected thread is running\\.\""
+ }
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test $command $mi_re "mi select frame twice, second call"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select frame twice, second call"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+
+}
+
+# Test printing the current thread using a cli command in the mi channel .
+
+proc test_cli_in_mi_select_thread_no_args {mode exec_mode} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ if {$exec_mode == "interpreter-exec"} {
+ set command "-interpreter-exec console \"thread\""
+ } else {
+ set command "thread"
+ }
+
+ set mi_re ".*$command.*\[\r\n\]+"
+ append mi_re "~\"\\\[Current thread is 1\\.3.*\\\]\\\\n\""
+ append mi_re "\[\r\n\]+\\^done"
+
+ set cli_re ""
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test $command $mi_re "mi select thread no args"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select thread no args"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test printing the current frame using a cli command in the mi channel .
+
+proc test_cli_in_mi_select_frame_no_args {mode exec_mode frame} {
+ global gdb_main_spawn_id mi_spawn_id
+
+ if {$exec_mode == "interpreter-exec"} {
+ set command "-interpreter-exec console \"frame\""
+ } else {
+ set command "frame"
+ }
+
+ if {$mode == "all-stop"} {
+ set cli_re ""
+ set mi_re [make_cli_in_mi_re "frame" $mode -1 "-1" -1 -1 $frame 0]
+ } elseif {$mode == "non-stop"} {
+ set cli_re ""
+ set mi_re ".*\\^error,msg=\"No stack\\.\""
+ }
+
+ with_spawn_id $mi_spawn_id {
+ mi_gdb_test $command $mi_re "mi select frame"
+ }
+
+ with_spawn_id $gdb_main_spawn_id {
+ set test "cli select frame"
+ gdb_test_multiple "" $test {
+ -re $cli_re {
+ pass $test
+ }
+ }
+ }
+}
+
+# Test all-stop and non-stop mode.
+
+foreach mode {"all-stop" "non-stop"} {
+ with_test_prefix $mode {
+ test_setup $mode
+ with_test_prefix "from cli" {
+ test_cli_select_inferior $mode
+ test_cli_select_thread $mode
+ test_cli_select_frame $mode 1
+ # Reset the frame for up/down test.
+ test_cli_select_frame $mode 0
+ test_cli_select_select_frame $mode 1
+ test_cli_select_select_frame $mode 0
+ test_cli_up_down $mode
+ # Reset thread/frame.
+ test_cli_select_inferior $mode
+ test_cli_select_thread_twice $mode
+ test_cli_select_frame_twice $mode 1
+ # Reset frame to 0.
+ test_cli_select_frame $mode 0
+ test_cli_select_frame_no_args $mode
+ test_cli_select_thread_no_args $mode
+ }
+ with_test_prefix "from mi" {
+ # Reset thread/frame.
+ test_cli_select_inferior $mode
+ test_mi_select_thread_with_thread_group $mode
+ test_mi_select_thread $mode
+ test_mi_select_frame $mode 1
+ test_mi_select_frame $mode 0
+ # Reset thread/frame.
+ test_cli_select_inferior $mode
+ test_mi_select_thread_twice $mode
+ test_mi_select_frame_twice $mode 1
+ }
+
+ # Test with a direct command from cli in mi like "thread 1"
+ # Or test with the interpreter-exec like -interpreter-exec "thread 1"
+ foreach exec_mode {"from cli in mi" "interpreter-exec"} {
+ with_test_prefix $exec_mode {
+ test_cli_in_mi_select_inferior $mode $exec_mode
+ test_cli_in_mi_select_thread $mode $exec_mode
+ test_cli_in_mi_select_frame $mode $exec_mode 1
+ test_cli_in_mi_select_frame $mode $exec_mode 0
+ # Reset thread/frame.
+ test_cli_in_mi_select_inferior $mode $exec_mode
+ test_cli_in_mi_select_thread_twice $mode $exec_mode
+ test_cli_in_mi_select_frame_twice $mode $exec_mode 1
+ test_cli_in_mi_select_frame $mode $exec_mode 0
+ test_cli_in_mi_select_thread_no_args $mode $exec_mode
+ test_cli_in_mi_select_frame_no_args $mode $exec_mode 0
+ }
+ }
+ }
+}
diff --git a/gdb/thread.c b/gdb/thread.c
index ab98777..f3c4879 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -1923,6 +1923,9 @@ thread_apply_command (char *tidlist, int from_tty)
void
thread_command (char *tidstr, int from_tty)
{
+ ptid_t previous_ptid = inferior_ptid;
+ enum gdb_rc result;
+
if (!tidstr)
{
if (ptid_equal (inferior_ptid, null_ptid))
@@ -1946,7 +1949,23 @@ thread_command (char *tidstr, int from_tty)
return;
}
- gdb_thread_select (current_uiout, tidstr, NULL);
+ 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_inf_thread_frame (USER_SELECTED_THREAD
+ | USER_SELECTED_FRAME);
+ }
}
/* Implementation of `thread name'. */
@@ -2058,32 +2077,53 @@ do_captured_thread_select (struct ui_out *uiout, void *tidstr_v)
annotate_thread_changed ();
- if (ui_out_is_mi_like_p (uiout))
- ui_out_field_int (uiout, "new-thread-id", inferior_thread ()->global_num);
- else
+ /* Since the current thread may have changed, see if there is any
+ exited thread we can now delete. */
+ prune_threads ();
+
+ return GDB_RC_OK;
+}
+
+/* Print thread and frame switch command response. */
+
+void
+print_selected_thread_frame (struct ui_out *uiout,
+ user_selected_what selection)
+{
+ struct thread_info *tp = inferior_thread ();
+ struct inferior *inf = current_inferior ();
+
+ if (selection & USER_SELECTED_THREAD)
{
- ui_out_text (uiout, "[Switching to thread ");
- ui_out_field_string (uiout, "new-thread-id", print_thread_id (tp));
- ui_out_text (uiout, " (");
- ui_out_text (uiout, target_pid_to_str (inferior_ptid));
- ui_out_text (uiout, ")]");
+ if (ui_out_is_mi_like_p (uiout))
+ {
+ ui_out_field_int (uiout, "new-thread-id",
+ inferior_thread ()->global_num);
+ }
+ else
+ {
+ ui_out_text (uiout, "[Switching to thread ");
+ ui_out_field_string (uiout, "new-thread-id", print_thread_id (tp));
+ ui_out_text (uiout, " (");
+ ui_out_text (uiout, target_pid_to_str (inferior_ptid));
+ ui_out_text (uiout, ")]");
+ }
}
- /* Note that we can't reach this with an exited thread, due to the
- thread_alive check above. */
if (tp->state == THREAD_RUNNING)
- ui_out_text (uiout, "(running)\n");
- else
{
- ui_out_text (uiout, "\n");
- print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+ if (selection & USER_SELECTED_THREAD)
+ ui_out_text (uiout, "(running)\n");
}
+ else if (selection & USER_SELECTED_FRAME)
+ {
+ if (selection & USER_SELECTED_THREAD)
+ ui_out_text (uiout, "\n");
- /* Since the current thread may have changed, see if there is any
- exited thread we can now delete. */
- prune_threads ();
-
- return GDB_RC_OK;
+ if (has_stack_frames ())
+ print_stack_frame_to_uiout (uiout, get_selected_frame (NULL),
+ 1, SRC_AND_LOC, 1);
+ }
}
enum gdb_rc
diff --git a/gdb/tui/tui-interp.c b/gdb/tui/tui-interp.c
index 3856382..22a5dab 100644
--- a/gdb/tui/tui-interp.c
+++ b/gdb/tui/tui-interp.c
@@ -206,6 +206,35 @@ tui_on_command_error (void)
display_gdb_prompt (NULL);
}
+/* Observer for the user_selected_thread_frame notification. */
+
+static void
+tui_on_user_selected_inf_thread_frame (user_selected_what selection)
+{
+ struct switch_thru_all_uis state;
+ struct thread_info *tp = find_thread_ptid (inferior_ptid);
+
+ /* This event is suppressed. */
+ if (cli_suppress_notification.user_selected_inf_thread_frame)
+ return;
+
+ SWITCH_THRU_ALL_UIS (state)
+ {
+ struct interp *tui = as_tui_interp (top_level_interpreter ());
+
+ if (tui == NULL)
+ continue;
+
+ if (selection & USER_SELECTED_INFERIOR)
+ print_selected_inferior (tui_ui_out (tui));
+
+ if (tp != NULL
+ && ((selection & (USER_SELECTED_THREAD | USER_SELECTED_FRAME))))
+ print_selected_thread_frame (tui_ui_out (tui), selection);
+
+ }
+}
+
/* These implement the TUI interpreter. */
static void *
@@ -323,4 +352,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_inf_thread_frame
+ (tui_on_user_selected_inf_thread_frame);
}
--
2.9.2