From 94696ad31c3fac4a3bc17391e42362d83be1fb56 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Mon, 14 Jul 2014 19:55:31 +0100 Subject: [PATCH] Canceling pagination caused by execution command from command line aborts readline/gdb This fixes: $ ./gdb program -ex "set height 2" -ex "start" ... Reading symbols from /home/pedro/gdb/tests/threads...done. ---Type to continue, or q to quit---^CQuit << ctrl-c triggers a Quit *type something* readline: readline_callback_read_char() called with no handler! Aborted $ Usually, if an error propagates all the way to the top level, we'll re-enable stdin, in case the command that was running was a synchronous command. That's done in the event loop's actual loop (event-loop.c:start_event_loop). However, if a foreground execution command is run before the event loop starts and throws, nothing is presently reenabling stdin, which leaves sync_execution set. When we do start the event loop, because sync_execution is still (mistakenly) set, display_gdb_prompt removes the readline input callback, even though stdin is registered in the event loop. Any input from here on results in readline aborting. Such commands are run through catch_command_errors, catch_command_errors_const, so add the tweak there. gdb/ 2014-07-14 Pedro Alves PR gdb/17072 * main.c: Include event-top.h. (handle_command_errors): New function. (catch_command_errors, catch_command_errors_const): Use it. gdb/testsuite/ 2014-07-14 Pedro Alves PR gdb/17072 * gdb.base/paginate-execution-startup.c: New file. * gdb.base/paginate-execution-startup.exp: New file. * lib/gdb.exp (pagination_prompt): New global. (default_gdb_spawn): New procedure, factored out from default_gdb_spawn. (default_gdb_start): Adjust to call default_gdb_spawn. (gdb_spawn): New procedure. --- gdb/ChangeLog | 7 + gdb/main.c | 30 +++- gdb/testsuite/ChangeLog | 11 ++ .../gdb.base/paginate-execution-startup.c | 32 ++++ .../gdb.base/paginate-execution-startup.exp | 189 +++++++++++++++++++++ gdb/testsuite/lib/gdb.exp | 61 +++++-- 6 files changed, 310 insertions(+), 20 deletions(-) create mode 100644 gdb/testsuite/gdb.base/paginate-execution-startup.c create mode 100644 gdb/testsuite/gdb.base/paginate-execution-startup.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 7a5abe0..df5a9df 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,12 @@ 2014-07-14 Pedro Alves + PR gdb/17072 + * main.c: Include event-top.h. + (handle_command_errors): New function. + (catch_command_errors, catch_command_errors_const): Use it. + +2014-07-14 Pedro Alves + * exceptions.c (catch_command_errors, catch_command_errors_const): Moved to main.c. * exceptions.h (catch_command_errors_ftype) diff --git a/gdb/main.c b/gdb/main.c index 1d77bd3..b51ff89 100644 --- a/gdb/main.c +++ b/gdb/main.c @@ -46,6 +46,7 @@ #include "filenames.h" #include "filestuff.h" #include +#include "event-top.h" /* The selected interpreter. This will be used as a set command variable, so it should always be malloc'ed - since @@ -337,6 +338,25 @@ captured_command_loop (void *data) return 1; } +/* Handle command errors thrown from within + catch_command_errors/catch_command_errors_const. */ + +static int +handle_command_errors (volatile struct gdb_exception e) +{ + if (e.reason < 0) + { + exception_print (gdb_stderr, e); + + /* If any exception escaped to here, we better enable stdin. + Otherwise, any command that calls async_disable_stdin, and + then throws, will leave stdin inoperable. */ + async_enable_stdin (); + return 0; + } + return 1; +} + /* Type of the command callback passed to catch_command_errors. */ typedef void (catch_command_errors_ftype) (char *, int); @@ -353,10 +373,7 @@ catch_command_errors (catch_command_errors_ftype *command, { command (arg, from_tty); } - exception_print (gdb_stderr, e); - if (e.reason < 0) - return 0; - return 1; + return handle_command_errors (e); } /* Type of the command callback passed to catch_command_errors_const. */ @@ -375,10 +392,7 @@ catch_command_errors_const (catch_command_errors_const_ftype *command, { command (arg, from_tty); } - exception_print (gdb_stderr, e); - if (e.reason < 0) - return 0; - return 1; + return handle_command_errors (e); } /* Arguments of --command option and its counterpart. */ diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index f504db6..c0207c4 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,5 +1,16 @@ 2014-07-14 Pedro Alves + PR gdb/17072 + * gdb.base/paginate-execution-startup.c: New file. + * gdb.base/paginate-execution-startup.exp: New file. + * lib/gdb.exp (pagination_prompt): New global. + (default_gdb_spawn): New procedure, factored out from + default_gdb_spawn. + (default_gdb_start): Adjust to call default_gdb_spawn. + (gdb_spawn): New procedure. + +2014-07-14 Pedro Alves + * lib/gdb.exp (gdb_assert): New procedure. * gdb.trace/backtrace.exp (gdb_backtrace_tdp_4): Use it. diff --git a/gdb/testsuite/gdb.base/paginate-execution-startup.c b/gdb/testsuite/gdb.base/paginate-execution-startup.c new file mode 100644 index 0000000..e741785 --- /dev/null +++ b/gdb/testsuite/gdb.base/paginate-execution-startup.c @@ -0,0 +1,32 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2014 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 . */ + +#include + +static void +after_sleep (void) +{ + return; /* after sleep */ +} + +int +main (void) +{ + sleep (3); + after_sleep (); + return 0; +} diff --git a/gdb/testsuite/gdb.base/paginate-execution-startup.exp b/gdb/testsuite/gdb.base/paginate-execution-startup.exp new file mode 100644 index 0000000..dc713ec --- /dev/null +++ b/gdb/testsuite/gdb.base/paginate-execution-startup.exp @@ -0,0 +1,189 @@ +# Copyright (C) 2014 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 . + +# A collection of tests related to pagination resulting from running +# execution commands directly from the command line, with "-ex". + +standard_testfile + +if {[build_executable "failed to prepare" $testfile $srcfile debug] == -1} { + return -1 +} + +set file_arg $binfile +if [is_remote host] { + set file_arg [remote_download host $file_arg] +} + +global GDBFLAGS +set saved_gdbflags $GDBFLAGS + +# Returns true if the board can 'gdb -ex "start"', false otherwise. + +proc probe_can_run_cmdline {} { + global srcfile file_arg + global saved_gdbflags GDBFLAGS + global gdb_prompt + + set GDBFLAGS $saved_gdbflags + append GDBFLAGS " -ex \"set height 0\"" + append GDBFLAGS " -ex \"start\"" + append GDBFLAGS " --args \"$file_arg\"" + + with_test_prefix "probe support" { + set test "run to main" + + gdb_exit + set res [gdb_spawn] + if { $res != 0} { + fail $test + return -1 + } + + set res -1 + gdb_test_multiple "" $test { + -re "main .* at .*$srcfile.*$gdb_prompt $" { + set res 1 + pass $test + } + -re "Don't know how to run.*$gdb_prompt $" { + set res 0 + unsupported $test + } + } + + # In case the board file wants to send further commands. + gdb_test_no_output "set height unlimited" + return $res + } +} + +# Check that we handle pagination correctly when it triggers due to an +# execution command entered directly on the command line. + +proc test_fg_execution_pagination_return {} { + global file_arg + global saved_gdbflags GDBFLAGS + global gdb_prompt pagination_prompt + + set GDBFLAGS $saved_gdbflags + append GDBFLAGS " -ex \"set height 2\"" + append GDBFLAGS " -ex \"start\"" + append GDBFLAGS " --args \"$file_arg\"" + + with_test_prefix "return" { + set test "run to pagination" + + gdb_exit + set res [gdb_spawn] + if { $res != 0} { + fail $test + return $res + } + gdb_test_multiple "" $test { + -re "$pagination_prompt$" { + pass $test + } + -re "$gdb_prompt $" { + fail $test + } + } + + send_gdb "\n" + + set saw_pagination_prompt 0 + set test "send \\n to GDB" + gdb_test_multiple "" $test { + -re "$pagination_prompt$" { + set saw_pagination_prompt 1 + send_gdb "\n" + exp_continue + } + -notransfer -re "" { + # Otherwise gdb_test_multiple considers this an error. + exp_continue + } + -re "$gdb_prompt $" { + gdb_assert $saw_pagination_prompt $test + } + } + + gdb_test "p 1" " = 1" "GDB accepts further input" + + # In case the board file wants to send further commands. + gdb_test_no_output "set height unlimited" + } +} + +# Check that we handle canceling pagination correctly when it triggers +# due to an execution command entered directly on the command line. + +proc test_fg_execution_pagination_cancel { how } { + global file_arg + global saved_gdbflags GDBFLAGS + global gdb_prompt pagination_prompt + + set GDBFLAGS $saved_gdbflags + + append GDBFLAGS " -ex \"set height 2\"" + append GDBFLAGS " -ex \"start\"" + append GDBFLAGS " --args \"$file_arg\"" + + with_test_prefix "cancel with $how" { + set test "run to pagination" + + gdb_exit + set res [gdb_spawn] + if { $res != 0} { + fail $test + return $res + } + gdb_test_multiple "" $test { + -re "$pagination_prompt$" { + pass $test + } + -notransfer -re "" { + # Otherwise gdb_test_multiple considers this an error. + exp_continue + } + } + + set test "cancel pagination" + if { $how == "ctrl-c" } { + send_gdb "\003" + } else { + send_gdb "q\n" + + } + gdb_test_multiple "" $test { + -re "Quit\r\n$gdb_prompt $" { + pass $test + } + } + + gdb_test "p 1" " = 1" "GDB accepts further input" + + # In case the board file wants to send further commands. + gdb_test_no_output "set height unlimited" + } +} + +if {[probe_can_run_cmdline] > 0} { + test_fg_execution_pagination_return + test_fg_execution_pagination_cancel "ctrl-c" + test_fg_execution_pagination_cancel "quit" +} + +set GDBFLAGS $saved_gdbflags diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index 50f2481..7a00efb 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -70,6 +70,9 @@ if ![info exists gdb_prompt] then { set gdb_prompt "\[(\]gdb\[)\]" } +# A regexp that matches the pagination prompt. +set pagination_prompt "---Type to continue, or q to quit---" + # The variable fullname_syntax_POSIX is a regexp which matches a POSIX # absolute path ie. /foo/ set fullname_syntax_POSIX {/[^\n]*/} @@ -1426,19 +1429,12 @@ proc gdb_file_cmd { arg } { } } -# -# start gdb -- start gdb running, default procedure -# -# When running over NFS, particularly if running many simultaneous -# tests on different hosts all using the same server, things can -# get really slow. Give gdb at least 3 minutes to start up. -# -proc default_gdb_start { } { - global verbose use_gdb_stub +# Default gdb_spawn procedure. + +proc default_gdb_spawn { } { + global use_gdb_stub global GDB global INTERNAL_GDBFLAGS GDBFLAGS - global gdb_prompt - global timeout global gdb_spawn_id gdb_stop_suppressing_tests @@ -1469,21 +1465,45 @@ proc default_gdb_start { } { perror "Spawning $GDB failed." return 1 } + set gdb_spawn_id -1 + return 0 +} + +# Default gdb_start procedure. + +proc default_gdb_start { } { + global gdb_prompt + global gdb_spawn_id + + if [info exists gdb_spawn_id] { + return 0 + } + + set res [gdb_spawn] + if { $res != 0} { + return $res + } + + # When running over NFS, particularly if running many simultaneous + # tests on different hosts all using the same server, things can + # get really slow. Give gdb at least 3 minutes to start up. gdb_expect 360 { -re "\[\r\n\]$gdb_prompt $" { verbose "GDB initialized." } -re "$gdb_prompt $" { perror "GDB never initialized." + unset gdb_spawn_id return -1 } timeout { perror "(timeout) GDB never initialized after 10 seconds." remote_close host + unset gdb_spawn_id return -1 } } - set gdb_spawn_id -1 + # force the height to "unlimited", so no pagers get used send_gdb "set height 0\n" @@ -3286,6 +3306,23 @@ proc gdb_clear_suppressed { } { set suppress_flag 0 } +# Spawn the gdb process. +# +# This doesn't expect any output or do any other initialization, +# leaving those to the caller. +# +# Overridable function -- you can override this function in your +# baseboard file. + +proc gdb_spawn { } { + default_gdb_spawn +} + +# Start gdb running, wait for prompt, and disable the pagers. + +# Overridable function -- you can override this function in your +# baseboard file. + proc gdb_start { } { default_gdb_start } -- 2.7.4