aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
Diffstat (limited to 'gdb')
-rw-r--r--gdb/ChangeLog24
-rw-r--r--gdb/cli-out.c13
-rw-r--r--gdb/gdbthread.h5
-rw-r--r--gdb/infrun.c14
-rw-r--r--gdb/interps.c36
-rw-r--r--gdb/interps.h2
-rw-r--r--gdb/mi/mi-interp.c77
-rw-r--r--gdb/testsuite/ChangeLog13
-rw-r--r--gdb/testsuite/gdb.mi/mi-cli.exp52
-rw-r--r--gdb/testsuite/gdb.mi/mi-solib.exp11
-rw-r--r--gdb/testsuite/lib/mi-support.exp24
11 files changed, 262 insertions, 9 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index a70263d..f64a48b 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,29 @@
2014-05-21 Pedro Alves <palves@redhat.com>
+ PR gdb/13860
+ * gdbthread.h (struct thread_control_state): New field
+ `command_interp'.
+ * infrun.c (follow_fork): Copy the new thread control field to the
+ child fork thread.
+ (clear_proceed_status_thread): Clear the new thread control field.
+ (proceed): Set the new thread control field.
+ * interps.h (command_interp): Declare.
+ * interps.c (command_interpreter): New global.
+ (command_interp): New function.
+ (interp_exec): Set `command_interpreter' while here.
+ * cli-out.c (cli_uiout_dtor): New function.
+ (cli_ui_out_impl): Install it.
+ * mi/mi-interp.c: Include cli-out.h.
+ (mi_cmd_interpreter_exec): Add comment.
+ (restore_current_uiout_cleanup): New function.
+ (ui_out_free_cleanup): New function.
+ (mi_on_normal_stop): If finishing an execution command started by
+ a CLI command, or any kind of breakpoint-like event triggered,
+ print the stop event to the output (CLI) stream.
+ * mi/mi-out.c (mi_ui_out_impl): Install NULL `dtor' handler.
+
+2014-05-21 Pedro Alves <palves@redhat.com>
+
* cli/cli-cmds.c (list_command): Handle the first "list" after the
current source line having changed.
* frame.h (set_current_sal_from_frame): Remove 'center' parameter.
diff --git a/gdb/cli-out.c b/gdb/cli-out.c
index 5943fa7..eedbd2c 100644
--- a/gdb/cli-out.c
+++ b/gdb/cli-out.c
@@ -40,6 +40,17 @@ static void out_field_fmt (struct ui_out *uiout, int fldno,
const char *fldname,
const char *format,...) ATTRIBUTE_PRINTF (4, 5);
+/* The destructor. */
+
+static void
+cli_uiout_dtor (struct ui_out *ui_out)
+{
+ cli_out_data *data = ui_out_data (ui_out);
+
+ VEC_free (ui_filep, data->streams);
+ xfree (data);
+}
+
/* These are the CLI output functions */
/* Mark beginning of a table */
@@ -367,7 +378,7 @@ const struct ui_out_impl cli_ui_out_impl =
cli_wrap_hint,
cli_flush,
cli_redirect,
- 0,
+ cli_uiout_dtor,
0, /* Does not need MI hacks (i.e. needs CLI hacks). */
};
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 9b43a68..9f5dee6 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -122,6 +122,11 @@ struct thread_control_state
/* Chain containing status of breakpoint(s) the thread stopped
at. */
bpstat stop_bpstat;
+
+ /* The interpreter that issued the execution command. NULL if the
+ thread was resumed as a result of a command applied to some other
+ thread (e.g., "next" with scheduler-locking off). */
+ struct interp *command_interp;
};
/* Inferior thread specific part of `struct infcall_suspend_state'.
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 2ed82b6..9ec2363 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -428,6 +428,7 @@ follow_fork (void)
CORE_ADDR step_range_start = 0;
CORE_ADDR step_range_end = 0;
struct frame_id step_frame_id = { 0 };
+ struct interp *command_interp = NULL;
if (!non_stop)
{
@@ -479,6 +480,7 @@ follow_fork (void)
step_frame_id = tp->control.step_frame_id;
exception_resume_breakpoint
= clone_momentary_breakpoint (tp->control.exception_resume_breakpoint);
+ command_interp = tp->control.command_interp;
/* For now, delete the parent's sr breakpoint, otherwise,
parent/child sr breakpoints are considered duplicates,
@@ -490,6 +492,7 @@ follow_fork (void)
tp->control.step_range_end = 0;
tp->control.step_frame_id = null_frame_id;
delete_exception_resume_breakpoint (tp);
+ tp->control.command_interp = NULL;
}
parent = inferior_ptid;
@@ -534,6 +537,7 @@ follow_fork (void)
tp->control.step_frame_id = step_frame_id;
tp->control.exception_resume_breakpoint
= exception_resume_breakpoint;
+ tp->control.command_interp = command_interp;
}
else
{
@@ -2024,6 +2028,8 @@ clear_proceed_status_thread (struct thread_info *tp)
tp->control.proceed_to_finish = 0;
+ tp->control.command_interp = NULL;
+
/* Discard any remaining commands or status from previous stop. */
bpstat_clear (&tp->control.stop_bpstat);
}
@@ -2229,6 +2235,14 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step)
regcache_write_pc (regcache, addr);
}
+ /* Record the interpreter that issued the execution command that
+ caused this thread to resume. If the top level interpreter is
+ MI/async, and the execution command was a CLI command
+ (next/step/etc.), we'll want to print stop event output to the MI
+ console channel (the stepped-to line, etc.), as if the user
+ entered the execution command on a real GDB console. */
+ inferior_thread ()->control.command_interp = command_interp ();
+
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
"infrun: proceed (addr=%s, signal=%s, step=%d)\n",
diff --git a/gdb/interps.c b/gdb/interps.c
index e446747..f6b941c 100644
--- a/gdb/interps.c
+++ b/gdb/interps.c
@@ -318,6 +318,29 @@ current_interp_display_prompt_p (void)
data);
}
+/* The interpreter that is active while `interp_exec' is active, NULL
+ at all other times. */
+static struct interp *command_interpreter;
+
+/* The interpreter that was active when a command was executed.
+ Normally that'd always be CURRENT_INTERPRETER, except that MI's
+ -interpreter-exec command doesn't actually flip the current
+ interpreter when running its sub-command. The
+ `command_interpreter' global tracks when interp_exec is called
+ (IOW, when -interpreter-exec is called). If that is set, it is
+ INTERP in '-interpreter-exec INTERP "CMD"' or in 'interpreter-exec
+ INTERP "CMD". Otherwise, interp_exec isn't active, and so the
+ interpreter running the command is the current interpreter. */
+
+struct interp *
+command_interp (void)
+{
+ if (command_interpreter != NULL)
+ return command_interpreter;
+ else
+ return current_interpreter;
+}
+
/* Run the current command interpreter's main loop. */
void
current_interp_command_loop (void)
@@ -351,9 +374,20 @@ interp_set_quiet (struct interp *interp, int quiet)
struct gdb_exception
interp_exec (struct interp *interp, const char *command_str)
{
+ struct gdb_exception ex;
+ struct interp *save_command_interp;
+
gdb_assert (interp->procs->exec_proc != NULL);
- return interp->procs->exec_proc (interp->data, command_str);
+ /* See `command_interp' for why we do this. */
+ save_command_interp = command_interpreter;
+ command_interpreter = interp;
+
+ ex = interp->procs->exec_proc (interp->data, command_str);
+
+ command_interpreter = save_command_interp;
+
+ return ex;
}
/* A convenience routine that nulls out all the common command hooks.
diff --git a/gdb/interps.h b/gdb/interps.h
index 568b5df..13edf42 100644
--- a/gdb/interps.h
+++ b/gdb/interps.h
@@ -96,6 +96,8 @@ extern int current_interp_set_logging (int start_log, struct ui_file *out,
extern void *top_level_interpreter_data (void);
extern struct interp *top_level_interpreter (void);
+extern struct interp *command_interp (void);
+
/* True if the current interpreter is in async mode, false if in sync
mode. If in sync mode, running a synchronous execution command
(with execute_command, e.g, "next") will not return until the
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index e8de1ea..e1fbb5e 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -37,6 +37,7 @@
#include "gdb.h"
#include "objfiles.h"
#include "tracepoint.h"
+#include "cli-out.h"
/* These are the interpreter setup, etc. functions for the MI
interpreter. */
@@ -231,6 +232,10 @@ mi_cmd_interpreter_exec (char *command, char **argv, int argc)
error (_("-interpreter-exec: could not find interpreter \"%s\""),
argv[0]);
+ /* Note that unlike the CLI version of this command, we don't
+ actually set INTERP_TO_USE as the current interpreter, as we
+ still want gdb_stdout, etc. to point at MI streams. */
+
/* Insert the MI out hooks, making sure to also call the
interpreter's hooks if it has any. */
/* KRS: We shouldn't need this... Events should be installed and
@@ -415,6 +420,26 @@ mi_inferior_removed (struct inferior *inf)
gdb_flush (mi->event_channel);
}
+/* Cleanup that restores a previous current uiout. */
+
+static void
+restore_current_uiout_cleanup (void *arg)
+{
+ struct ui_out *saved_uiout = arg;
+
+ current_uiout = saved_uiout;
+}
+
+/* Cleanup that destroys the a ui_out object. */
+
+static void
+ui_out_free_cleanup (void *arg)
+{
+ struct ui_out *uiout = arg;
+
+ ui_out_destroy (uiout);
+}
+
static void
mi_on_normal_stop (struct bpstats *bs, int print_frame)
{
@@ -445,6 +470,58 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
current_uiout = saved_uiout;
}
+ /* Otherwise, frame information has already been printed by
+ normal_stop. */
+ else
+ {
+ /* Breakpoint hits should always be mirrored to the console.
+ Deciding what to mirror to the console wrt to breakpoints
+ and random stops gets messy real fast. E.g., say "s"
+ trips on a breakpoint. We'd clearly want to mirror the
+ event to the console in this case. But what about more
+ complicated cases like "s&; thread n; s&", and one of
+ those steps spawning a new thread, and that thread
+ hitting a breakpoint? It's impossible in general to
+ track whether the thread had any relation to the commands
+ that had been executed. So we just simplify and always
+ mirror breakpoints and random events to the console.
+
+ Also, CLI execution commands (-interpreter-exec console
+ "next", for example) in async mode have the opposite
+ issue as described in the "then" branch above --
+ normal_stop has already printed frame information to MI
+ uiout, but nothing has printed the same information to
+ the CLI channel. We should print the source line to the
+ console when stepping or other similar commands, iff the
+ step was started by a console command (but not if it was
+ started with -exec-step or similar). */
+ struct thread_info *tp = inferior_thread ();
+
+ if ((!tp->control.stop_step
+ && !tp->control.proceed_to_finish)
+ || (tp->control.command_interp != NULL
+ && tp->control.command_interp != top_level_interpreter ()))
+ {
+ struct mi_interp *mi = top_level_interpreter_data ();
+ struct target_waitstatus last;
+ ptid_t last_ptid;
+ struct ui_out *cli_uiout;
+ struct cleanup *old_chain;
+
+ /* Sets the current uiout to a new temporary CLI uiout
+ assigned to STREAM. */
+ cli_uiout = cli_out_new (mi->out);
+ old_chain = make_cleanup (ui_out_free_cleanup, cli_uiout);
+
+ make_cleanup (restore_current_uiout_cleanup, current_uiout);
+ current_uiout = cli_uiout;
+
+ get_last_target_status (&last_ptid, &last);
+ print_stop_event (&last);
+
+ do_cleanups (old_chain);
+ }
+ }
ui_out_field_int (mi_uiout, "thread-id",
pid_to_thread_id (inferior_ptid));
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index f545ec7..b25e478 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,18 @@
2014-05-21 Pedro Alves <palves@redhat.com>
+ PR gdb/13860
+ * gdb.mi/mi-cli.exp (line_callee4_next_step): New global.
+ (top level): Test that output related to execution commands is
+ sent to the console with CLI commands, but not with MI commands.
+ Test that breakpoint events are always mirrored to the console.
+ Also expect the new source line to be output after a "next" in
+ async mode too. Make it a pass/fail test.
+ * gdb.mi/mi-solib.exp: Test that the CLI solib event note is
+ output.
+ * lib/mi-support.exp (mi_gdb_expect_cli_output): New procedure.
+
+2014-05-21 Pedro Alves <palves@redhat.com>
+
* gdb.base/list.exp (build_pattern, test_list): New procedures.
Use them to test variations of "list" after reaching a breakpoint.
* gdb.mi/mi-cli.exp (line_main_callme_2): New global.
diff --git a/gdb/testsuite/gdb.mi/mi-cli.exp b/gdb/testsuite/gdb.mi/mi-cli.exp
index cb8d3af..81f8898 100644
--- a/gdb/testsuite/gdb.mi/mi-cli.exp
+++ b/gdb/testsuite/gdb.mi/mi-cli.exp
@@ -69,6 +69,7 @@ set line_main_callme_2 [expr $line_main_return + 1]
set line_callee4_head [gdb_get_line_number "callee4 ("]
set line_callee4_body [expr $line_callee4_head + 2]
set line_callee4_next [expr $line_callee4_body + 1]
+set line_callee4_next_step [expr $line_callee4_next + 3]
mi_gdb_test "-interpreter-exec console \"set args foobar\"" \
".*=cmd-param-changed,param=\"args\",value=\"foobar\".*\\^done" \
@@ -153,13 +154,44 @@ if {$async} {
mi_execute_to "interpreter-exec console step" $reason "callee4" "" ".*basics.c" $line_callee4_next \
"" "check *stopped from CLI command"
+mi_send_resuming_command "exec-step" "-exec-step to line \$line_callee4_next_step"
+
+# Test that the new current source line is _not_ output, given we
+# executed MI's -exec-next, not CLI's 'next' command.
+
+set output [mi_gdb_expect_cli_output "\\*stopped" "collect CLI output for -exec-step"]
+
+set test "-exec-step does not produce CLI step output"
+if {[regexp "A + B" "$output"]} {
+ fail $test
+} else {
+ pass $test
+}
+
+mi_expect_stop "end-stepping-range" "callee4" "" ".*basics.c" $line_callee4_next_step \
+ "" "check *stopped from CLI command 2"
+
mi_gdb_test "600-break-insert -t basics.c:$line_main_hello" \
{600\^done,bkpt=.number="3",type="breakpoint".*\}} \
"-break-insert -t basics.c:\$line_main_hello"
-mi_execute_to "exec-continue" "breakpoint-hit" "main" "" ".*basics.c" \
- $line_main_hello { "" "disp=\"del\"" } \
- "-exec-continue to line \$line_main_hello"
+# Test that breakpoint events are always mirrored to the CLI output
+# stream (both sync and async modes).
+mi_send_resuming_command "exec-continue" "-exec-continue to line \$line_main_hello"
+
+set output [mi_gdb_expect_cli_output "\\*stopped" "collect CLI output for breakpoint hit"]
+set test "breakpoint hit produces CLI output"
+set pattern "\\\\nTemporary breakpoint 3, main \\(\\) at \[^\n\]+basics.c:$line_main_hello\\\\n\[^\n\]+Hello"
+
+if {[regexp $pattern $output]} {
+ pass $test
+} else {
+ fail $test
+}
+
+# Test the MI output.
+mi_expect_stop "breakpoint-hit" "main" "" ".*basics.c" \
+ $line_main_hello { "" "disp=\"del\"" } "temporary breakpoint output hit in MI"
# Test that the token is output even for CLI commands
# Also test that *stopped includes frame information.
@@ -167,10 +199,16 @@ mi_gdb_test "34 next" \
".*34\\\^running.*\\*running,thread-id=\"all\"" \
"34 next: run"
-if {!$async} {
- gdb_expect {
- -re "~\[^\r\n\]+\r\n" {
- }
+# Test that the new current source line is output to the console
+# stream, given we executed the console 'next' command, not
+# -exec-next.
+set test "34 next: CLI output"
+gdb_expect {
+ -re "~\"$line_main_return\[^\r\n\]+\r\n" {
+ pass $test
+ }
+ timeout {
+ fail "$test (timeout)"
}
}
diff --git a/gdb/testsuite/gdb.mi/mi-solib.exp b/gdb/testsuite/gdb.mi/mi-solib.exp
index 608d2b7..a684a3d 100644
--- a/gdb/testsuite/gdb.mi/mi-solib.exp
+++ b/gdb/testsuite/gdb.mi/mi-solib.exp
@@ -60,4 +60,15 @@ mi_gdb_test "777-gdb-set stop-on-solib-events 1" "777\\^done" \
# commands still cause the correct MI output to be generated.
mi_run_with_cli
+# Also test that the CLI solib event note is output.
+set test "CLI prints solib event"
+gdb_expect {
+ -re "~\"Stopped due to shared library event \\(no libraries added or removed\\)\\\\n" {
+ pass "$test"
+ }
+ timeout {
+ fail "$test (timeout)"
+ }
+}
+
mi_expect_stop solib-event .* .* .* .* .* "check for solib event"
diff --git a/gdb/testsuite/lib/mi-support.exp b/gdb/testsuite/lib/mi-support.exp
index 6d011b9..09a514b 100644
--- a/gdb/testsuite/lib/mi-support.exp
+++ b/gdb/testsuite/lib/mi-support.exp
@@ -799,6 +799,30 @@ proc mi_gdb_test { args } {
return $result
}
+# Collect output sent to the console output stream until UNTIL is
+# seen. UNTIL is a regular expression. MESSAGE is the message to be
+# printed in case of timeout.
+
+proc mi_gdb_expect_cli_output {until message} {
+
+ set output ""
+ gdb_expect {
+ -re "~\"(\[^\r\n\]+)\"\r\n" {
+ append output $expect_out(1,string)
+ exp_continue
+ }
+ -notransfer -re "$until" {
+ # Done
+ }
+ timeout {
+ fail "$message (timeout)"
+ return ""
+ }
+ }
+
+ return $output
+}
+
#
# MI run command. (A modified version of gdb_run_cmd)
#