[PATCH] gdb/infrun: disable pagination in fetch_inferior_event
Aktemur, Tankut Baris
tankut.baris.aktemur@intel.com
Wed Oct 28 17:21:40 GMT 2020
Kindly pinging.
Thanks.
-Baris
On Wednesday, October 14, 2020 12:24 PM, Aktemur, Tankut Baris wrote:
> Having pagination enabled when handling an inferior event gives the
> user an option to quit, which causes early exit in GDB's flow and may
> lead to half-baked state. For instance, here is a case where we quit
> in the middle of handling an inferior exit:
>
> $ gdb ./a.out
> Reading symbols from ./a.out...
> (gdb) set height 2
> (gdb) run
> Starting program: ./a.out
> --Type <RET> for more, q to quit, c to continue without paging--q
> Quit
> Couldn't get registers: No such process.
> (gdb) set height unlimited
> Couldn't get registers: No such process.
> (gdb) info threads
> Id Target Id Frame
> * 1 process 27098 Couldn't get registers: No such process.
> Couldn't get registers: No such process.
> (gdb)
>
> Or suppose having a multi-threaded program like below:
>
> static void *
> fun (void *dummy)
> {
> int a = 1; /* break-here */
> return NULL;
> }
>
> int
> main (void)
> {
> pthread_t thread;
> pthread_create (&thread, NULL, fun, NULL);
> pthread_join (thread, NULL);
>
> return 0;
> }
>
> If we define a breakpoint at line "break-here", we expect only Thread
> 2 to hit it.
>
> $ gdb ./a.out
> Reading symbols from ./a.out...
> (gdb) break 7
> Breakpoint 1 at 0x1182: file mt.c, line 7.
> (gdb) set height 2
> (gdb) run
> Starting program: ./a.out
> [Thread debugging using libthread_db enabled]
> Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
> [New Thread 0x7ffff77c4700 (LWP 23048)]
> --Type <RET> for more, q to quit, c to continue without paging--q
> Quit
> (gdb) set height unlimited
> (gdb) info thread
> Id Target Id Frame
> * 1 Thread 0x7ffff7fe3740 (LWP 23044) "a.out" 0x00007ffff7bbed2d in ...
> 2 Thread 0x7ffff77c4700 (LWP 23048) "a.out" fun (dummy=0x0) at mt.c:7
> (gdb)
>
> The prompt for continuation was triggered because Thread 2 hit the
> breakpoint. (If we had hit 'c', we were going to see that stop event,
> but we didn't.) The context did not switch to Thread 2. GDB also did
> not execute several other things it would normally do in
> infrun.c:normal_stop after outputting "[Switching to Thread ...]" (but
> it seems harmless in this case). If we 'continue' at this state, both
> threads run until termination, and we don't see the breakpoint hit
> event ever.
>
> Here is another related and more complicated scenario that leads to a
> GDB crash. Create two inferiors, one sitting on top of a native
> target, and the other on a remote target, so that we have a
> multi-target setting, like so:
>
> (gdb) i inferiors
> Num Description Connection Executable
> 1 process 13786 1 (native) a.out
> * 2 process 13806 2 (remote ...) target:a.out
>
> Next, resume both inferiors to run until termination:
>
> (gdb) set schedule-multiple on
> (gdb) set height 2
> (gdb) continue
> Continuing.
> --Type <RET> for more, q to quit, c to continue without paging--[Inferior 2 (process
> 13806) exited normally]
>
> terminate called after throwing an instance of 'gdb_exception_error'
> Aborted
>
> Here, GDB first received a termination event from Inferior 1. GDB
> attempted to print this event, triggering a "prompt for continue", and
> GDB started polling for events, hoping to get an input from the user.
> However, the exit event from Inferior 2 was received instead. So, GDB
> started processing an exit event while being in the middle of
> processing another exit event. It was not ready for this situation
> and eventually crashed.
>
> To address these cases, temporarily disable pagination in
> fetch_inferior_event. This doesn't affect commands like 'info
> threads', 'backtrace', or 'thread apply'.
>
> Regression-tested on X86_64 Linux.
>
> gdb/ChangeLog:
> 2020-10-14 Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
>
> * infrun.c (fetch_inferior_event): Temporarily disable pagination.
>
> gdb/testsuite/ChangeLog:
> 2020-10-14 Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
>
> * gdb.base/paginate-after-ctrl-c-running.exp: Update with no pagination
> behavior.
> * gdb.base/paginate-bg-execution.exp: Ditto.
> * gdb.base/paginate-inferior-exit.exp: Ditto.
> * gdb.base/double-prompt-target-event-error.c: Remove.
> * gdb.base/double-prompt-target-event-error.exp: Remove.
> ---
> gdb/infrun.c | 6 +
> .../double-prompt-target-event-error.c | 25 ----
> .../double-prompt-target-event-error.exp | 122 ------------------
> .../paginate-after-ctrl-c-running.exp | 25 ++--
> .../gdb.base/paginate-bg-execution.exp | 77 ++---------
> .../gdb.base/paginate-inferior-exit.exp | 36 +-----
> 6 files changed, 27 insertions(+), 264 deletions(-)
> delete mode 100644 gdb/testsuite/gdb.base/double-prompt-target-event-error.c
> delete mode 100644 gdb/testsuite/gdb.base/double-prompt-target-event-error.exp
>
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index d552fb3adb2..3e087710f63 100644
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -3866,6 +3866,12 @@ fetch_inferior_event ()
> the main console. */
> scoped_restore save_ui = make_scoped_restore (¤t_ui, main_ui);
>
> + /* Temporarily disable pagination. Otherwise, the user would be
> + given an option to press 'q' to quit, which would cause an early
> + exit and could leave GDB in a half-baked state. */
> + scoped_restore save_pagination
> + = make_scoped_restore (&pagination_enabled, false);
> +
> /* End up with readline processing input, if necessary. */
> {
> SCOPE_EXIT { reinstall_readline_callback_handler_cleanup (); };
> diff --git a/gdb/testsuite/gdb.base/double-prompt-target-event-error.c
> b/gdb/testsuite/gdb.base/double-prompt-target-event-error.c
> deleted file mode 100644
> index 469f91dd1c1..00000000000
> --- a/gdb/testsuite/gdb.base/double-prompt-target-event-error.c
> +++ /dev/null
> @@ -1,25 +0,0 @@
> -/* This testcase is part of GDB, the GNU debugger.
> -
> - Copyright 2014-2020 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/>. */
> -
> -#include <unistd.h>
> -
> -int
> -main (void)
> -{
> - sleep (3);
> - return 0; /* after sleep */
> -}
> diff --git a/gdb/testsuite/gdb.base/double-prompt-target-event-error.exp
> b/gdb/testsuite/gdb.base/double-prompt-target-event-error.exp
> deleted file mode 100644
> index 459d1866f32..00000000000
> --- a/gdb/testsuite/gdb.base/double-prompt-target-event-error.exp
> +++ /dev/null
> @@ -1,122 +0,0 @@
> -# Copyright (C) 2014-2020 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/>.
> -
> -if [target_info exists gdb,nointerrupts] {
> - verbose "Skipping double-prompt-target-event-error.exp because of nointerrupts."
> - return
> -}
> -
> -standard_testfile
> -
> -if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug] == -1} {
> - return -1
> -}
> -
> -# Test throwing an error while GDB is handling a target event. We use
> -# a ctrl-c/quit in a pagination prompt to emulate an error. COMMAND
> -# is either "continue" or "wrapcont". The latter is a continue issued
> -# from a user-defined command. That exercises the case of the
> -# interpreter forced sync, which was the case that originally had a
> -# bug.
> -
> -proc cancel_pagination_in_target_event { command } {
> - global binfile srcfile
> - global gdb_prompt pagination_prompt
> -
> - set testline [gdb_get_line_number "after sleep"]
> -
> - with_test_prefix "ctrlc target event: $command" {
> - clean_restart $binfile
> -
> - if ![runto_main] then {
> - fail "can't run to main"
> - return 0
> - }
> -
> - gdb_test "b $srcfile:$testline" \
> - "Breakpoint .*$srcfile, line $testline.*" \
> - "set breakpoint"
> -
> - gdb_test_no_output "set height 2"
> -
> - if { $command == "wrapcont" } {
> - gdb_test_multiple "define wrapcont" "define user command: wrapcont" {
> - -re "Type commands for definition of \"wrapcont\".\r\nEnd with a line saying
> just \"end\".\r\n>$" {
> - # Note that "Continuing." is ommitted when
> - # "continue" is issued from a user-defined
> - # command. Issue it ourselves.
> - gdb_test "echo Continuing\.\ncontinue\nend" "" \
> - "define user command: wrapcont"
> - }
> - }
> - }
> -
> - # Wait for pagination prompt after the "Continuing" line,
> - # indicating the program was running and then stopped.
> - set saw_continuing 0
> - set test "continue to pagination"
> - gdb_test_multiple "$command" $test {
> - -re "$pagination_prompt$" {
> - if {$saw_continuing} {
> - pass $test
> - } else {
> - send_gdb "\n"
> - exp_continue
> - }
> - }
> - -re "Continuing" {
> - set saw_continuing 1
> - exp_continue
> - }
> - }
> -
> - # We're now stopped in a pagination query while handling a
> - # target event (printing where the program stopped). Quitting
> - # the pagination should result in only one prompt being
> - # output.
> - send_gdb "\003p 1\n"
> -
> - # Note gdb_test_multiple has a default match for the prompt,
> - # which issues a FAIL. Consume the first prompt.
> - set test "first prompt"
> - gdb_test_multiple "" $test {
> - -re "$gdb_prompt" {
> - pass "first prompt"
> - }
> - }
> -
> - # We should only see one prompt more, and it should be
> - # preceeded by print's output.
> - set test "no double prompt"
> - gdb_test_multiple "" $test {
> - -re "$gdb_prompt.*$gdb_prompt $" {
> - # The bug is present, and expect managed to read
> - # enough characters into the buffer to fill it with
> - # both prompts.
> - fail $test
> - }
> - -re " = 1\r\n$gdb_prompt $" {
> - pass $test
> - }
> - }
> -
> - # In case the board file wants to send further commands.
> - gdb_test_no_output "set height unlimited"
> - }
> -}
> -
> -foreach variant { "continue" "wrapcont" } {
> - cancel_pagination_in_target_event $variant
> -}
> diff --git a/gdb/testsuite/gdb.base/paginate-after-ctrl-c-running.exp
> b/gdb/testsuite/gdb.base/paginate-after-ctrl-c-running.exp
> index 09f6fbfeed1..f904912bce3 100644
> --- a/gdb/testsuite/gdb.base/paginate-after-ctrl-c-running.exp
> +++ b/gdb/testsuite/gdb.base/paginate-after-ctrl-c-running.exp
> @@ -24,12 +24,12 @@ if {[build_executable "failed to prepare" $testfile $srcfile debug] == -
> 1} {
> return -1
> }
>
> -# Send a ctrl-c while the target is running and the next output causes
> -# a pagination prompt.
> +# Send a ctrl-c while the target is running and check that the output
> +# does not cause a pagination prompt.
>
> -proc test_ctrlc_while_target_running_paginates {} {
> +proc test_ctrlc_while_target_running_does_not_paginate {} {
> global binfile srcfile
> - global gdb_prompt pagination_prompt
> + global gdb_prompt
>
> set testline [gdb_get_line_number "after sleep"]
>
> @@ -62,18 +62,11 @@ proc test_ctrlc_while_target_running_paginates {} {
> send_gdb "\003"
>
> # GDB now intercepts the SIGINT and tries to let the user know
> - # about the spurious signal, but that paginates. Make sure
> - # the user can respond to the pagination query.
> - set test "got prompt"
> - set saw_pagination_prompt 0
> - gdb_test_multiple "" $test {
> - -re "$pagination_prompt$" {
> - set saw_pagination_prompt 1
> - send_gdb "\n"
> - exp_continue
> - }
> + # about the spurious signal. Make sure that this does not
> + # trigger pagination.
> + gdb_test_multiple "" "no pagination" {
> -re "$gdb_prompt $" {
> - gdb_assert $saw_pagination_prompt $test
> + pass $gdb_test_name
> }
> }
>
> @@ -85,4 +78,4 @@ proc test_ctrlc_while_target_running_paginates {} {
> }
> }
>
> -test_ctrlc_while_target_running_paginates
> +test_ctrlc_while_target_running_does_not_paginate
> diff --git a/gdb/testsuite/gdb.base/paginate-bg-execution.exp
> b/gdb/testsuite/gdb.base/paginate-bg-execution.exp
> index 541362f6e49..388dd52c9c7 100644
> --- a/gdb/testsuite/gdb.base/paginate-bg-execution.exp
> +++ b/gdb/testsuite/gdb.base/paginate-bg-execution.exp
> @@ -13,8 +13,8 @@
> # You should have received a copy of the GNU General Public License
> # along with this program. If not, see <http://www.gnu.org/licenses/>.
>
> -# A collection of tests related to running execution commands directly
> -# from the command line, with "-ex".
> +# Test that a breakpoint hit event coming from background execution
> +# does not trigger pagination.
>
> standard_testfile
>
> @@ -22,12 +22,12 @@ if {[build_executable "failed to prepare" $testfile $srcfile debug] == -
> 1} {
> return -1
> }
>
> -# Check that we handle pagination correctly when it triggers due to an
> -# background execution command entered directly on the command line.
> +# Check that we handle a stop event coming from a background execution
> +# command without getting caught in pagination.
>
> proc test_bg_execution_pagination_return {} {
> global binfile
> - global pagination_prompt
> + global decimal
>
> with_test_prefix "paginate" {
> clean_restart $binfile
> @@ -43,66 +43,9 @@ proc test_bg_execution_pagination_return {} {
>
> gdb_test "continue&" "Continuing\."
>
> - set test "pagination handled, breakpoint hit"
> - set saw_pagination_prompt 0
> - gdb_test_multiple "" $test {
> - -re "$pagination_prompt$" {
> - set saw_pagination_prompt 1
> - send_gdb "\n"
> - exp_continue
> - }
> - -re "after sleep\[^\r\n\]+\r\n$" {
> - gdb_assert $saw_pagination_prompt $test
> - }
> - }
> -
> - # GDB used to crash here.
> - 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 a background execution command entered directly on the
> -# command line.
> -
> -proc test_bg_execution_pagination_cancel { how } {
> - global binfile
> - global gdb_prompt pagination_prompt
> -
> - with_test_prefix "cancel with $how" {
> - clean_restart $binfile
> -
> - if ![runto_main] then {
> - fail "can't run to main"
> - return 0
> - }
> -
> - gdb_test "b after_sleep"
> -
> - gdb_test_no_output "set height 2"
> -
> - gdb_test "continue&" "Continuing\."
> -
> - set test "continue& paginates"
> - gdb_test_multiple "" $test {
> - -re "$pagination_prompt$" {
> - pass $test
> - }
> - }
> -
> - 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_multiple "" "no pagination, breakpoint hit" {
> + -re "Breakpoint $decimal, after_sleep\[^\r\n\]+\r\n\[^\r\n\]+\r\n" {
> + pass $gdb_test_name
> }
> }
>
> @@ -114,7 +57,3 @@ proc test_bg_execution_pagination_cancel { how } {
> }
>
> test_bg_execution_pagination_return
> -if ![target_info exists gdb,nointerrupts] {
> - test_bg_execution_pagination_cancel "ctrl-c"
> -}
> -test_bg_execution_pagination_cancel "quit"
> diff --git a/gdb/testsuite/gdb.base/paginate-inferior-exit.exp
> b/gdb/testsuite/gdb.base/paginate-inferior-exit.exp
> index 8e2d15cbabc..09527635916 100644
> --- a/gdb/testsuite/gdb.base/paginate-inferior-exit.exp
> +++ b/gdb/testsuite/gdb.base/paginate-inferior-exit.exp
> @@ -13,8 +13,7 @@
> # You should have received a copy of the GNU General Public License
> # along with this program. If not, see <http://www.gnu.org/licenses/>.
>
> -# A collection of tests related to running execution commands directly
> -# from the command line, with "-ex".
> +# Test that an inferior exit event does not trigger pagination.
>
> standard_testfile
>
> @@ -40,36 +39,9 @@ proc test_paginate_inferior_exited {} {
> # Force pagination.
> gdb_test_no_output "set height 2"
>
> - set test "continue to pagination"
> -
> - # Wait for the "Starting program" line, indicating the program
> - # is running.
> - set saw_starting 0
> - gdb_test_multiple "continue" $test {
> - -re "$pagination_prompt" {
> - if {$saw_starting} {
> - pass $test
> - } else {
> - send_gdb "\n"
> - exp_continue
> - }
> - }
> - -re "Continuing" {
> - set saw_starting 1
> - exp_continue
> - }
> - }
> -
> - # We're now stopped in a pagination output while handling a
> - # target event, trying to print about the program exiting.
> - set test "inferior exits normally"
> -
> - send_gdb "\n"
> - gdb_test_multiple "" $test {
> - -re "$inferior_exited_re normally.*$gdb_prompt $" {
> - pass $test
> - }
> - }
> + # The program continues until termination, without pagination
> + # being triggered.
> + gdb_continue_to_end
>
> gdb_test "p 1" " = 1" "GDB accepts further input"
>
> --
> 2.17.1
Intel Deutschland GmbH
Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Christin Eisenschmid, Gary Kershaw
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
More information about the Gdb-patches
mailing list