[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 (&current_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