diff options
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/ChangeLog | 24 | ||||
-rw-r--r-- | gdb/cli-out.c | 13 | ||||
-rw-r--r-- | gdb/gdbthread.h | 5 | ||||
-rw-r--r-- | gdb/infrun.c | 14 | ||||
-rw-r--r-- | gdb/interps.c | 36 | ||||
-rw-r--r-- | gdb/interps.h | 2 | ||||
-rw-r--r-- | gdb/mi/mi-interp.c | 77 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 13 | ||||
-rw-r--r-- | gdb/testsuite/gdb.mi/mi-cli.exp | 52 | ||||
-rw-r--r-- | gdb/testsuite/gdb.mi/mi-solib.exp | 11 | ||||
-rw-r--r-- | gdb/testsuite/lib/mi-support.exp | 24 |
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) # |