diff --git a/gdb/infcall.c b/gdb/infcall.c index 41ed3ed73a1..0f9ad34bbb4 100644 --- a/gdb/infcall.c +++ b/gdb/infcall.c @@ -563,11 +563,20 @@ call_thread_fsm::should_stop (struct thread_info *thread) registers are restored to what they were before the call.. */ return_value = get_call_return_value (&return_meta_info); + } - /* Break out of wait_sync_command_done. This is similar to the - async_enable_stdin call in normal_stop (which we don't call), - however, in this case we only change the WAITING_UI. This is - enough for wait_sync_command_done. */ + /* We are always going to stop this thread, but we might not be planning + to call call normal_stop, which is only done if should_notify_stop + returns true. + + As normal_stop is responsible for calling async_enable_stdin, which + would break us out of wait_sync_command_done, then, if we don't plan + to call normal_stop, we should call async_enable_stdin here instead. + + Unlike normal_stop, we only call async_enable_stdin on WAITING_UI, but + that is sufficient for wait_sync_command_done. */ + if (!this->should_notify_stop ()) + { scoped_restore save_ui = make_scoped_restore (¤t_ui, waiting_ui); gdb_assert (current_ui->prompt_state == PROMPT_BLOCKED); async_enable_stdin (); @@ -581,10 +590,28 @@ call_thread_fsm::should_stop (struct thread_info *thread) bool call_thread_fsm::should_notify_stop () { + INFCALL_SCOPED_DEBUG_ENTER_EXIT; + if (finished_p ()) { /* Infcall succeeded. Be silent and proceed with evaluating the expression. */ + infcall_debug_printf ("inferior call has finished, don't notify"); + return false; + } + + infcall_debug_printf ("inferior call didn't complete fully"); + + if (stopped_by_random_signal && unwind_on_signal_p) + { + infcall_debug_printf ("unwind-on-signal is on, don't notify"); + return false; + } + + if (stop_stack_dummy == STOP_STD_TERMINATE + && unwind_on_terminating_exception_p) + { + infcall_debug_printf ("unwind-on-terminating-exception is on, don't notify"); return false; } @@ -1512,6 +1539,11 @@ When the function is done executing, GDB will silently stop."), { /* The user wants the context restored. */ + /* Capture details of the signal so we can include them in + the error message. Calling dummy_frame_pop will restore + the previous stop signal details. */ + gdb_signal stop_signal = call_thread->stop_signal (); + /* We must get back to the frame we were before the dummy call. */ dummy_frame_pop (dummy_id, call_thread.get ()); @@ -1523,11 +1555,13 @@ When the function is done executing, GDB will silently stop."), /* FIXME: Insert a bunch of wrap_here; name can be very long if it's a C++ name with arguments and stuff. */ error (_("\ -The program being debugged was signaled while in a function called from GDB.\n\ -GDB has restored the context to what it was before the call.\n\ -To change this behavior use \"set unwindonsignal off\".\n\ -Evaluation of the expression containing the function\n\ -(%s) will be abandoned."), +The program being debugged received signal %s, %s\n\ +while in a function called from GDB. GDB has restored the context\n\ +to what it was before the call. To change this behavior use\n\ +\"set unwindonsignal off\". Evaluation of the expression containing\n\ +the function (%s) will be abandoned."), + gdb_signal_to_name (stop_signal), + gdb_signal_to_string (stop_signal), name.c_str ()); } else diff --git a/gdb/testsuite/gdb.base/unwindonsignal.exp b/gdb/testsuite/gdb.base/unwindonsignal.exp index 625b0c4db12..5c2243236ba 100644 --- a/gdb/testsuite/gdb.base/unwindonsignal.exp +++ b/gdb/testsuite/gdb.base/unwindonsignal.exp @@ -45,7 +45,12 @@ gdb_test "show unwindonsignal" \ # Call function (causing the program to get a signal), and see if gdb handles # it properly. if {[gdb_test "call gen_signal ()" \ - "\[\r\n\]*The program being debugged was signaled.*" \ + [multi_line \ + "The program being debugged received signal SIGABRT, Aborted" \ + "while in a function called from GDB\\. GDB has restored the context" \ + "to what it was before the call\\. To change this behavior use" \ + "\"set unwindonsignal off\"\\. Evaluation of the expression containing" \ + "the function \\(gen_signal\\) will be abandoned\\."] \ "unwindonsignal, inferior function call signaled"] != 0} { return 0 } diff --git a/gdb/testsuite/gdb.compile/compile-cplus.exp b/gdb/testsuite/gdb.compile/compile-cplus.exp index c8a40ada7f3..48fb75c3d78 100644 --- a/gdb/testsuite/gdb.compile/compile-cplus.exp +++ b/gdb/testsuite/gdb.compile/compile-cplus.exp @@ -133,7 +133,12 @@ gdb_test "info sym $infcall_pc" "\r\nNo symbol matches .*" "info sym not found" gdb_test_no_output "set unwindonsignal on" gdb_test "compile code *(volatile int *) 0 = 0;" \ - "The program being debugged was signaled while in a function called from GDB\\.\r\nGDB has restored the context to what it was before the call\\.\r\n.*" \ + [multi_line \ + "The program being debugged received signal SIGSEGV, Segmentation fault" \ + "while in a function called from GDB\\. GDB has restored the context" \ + "to what it was before the call\\. To change this behavior use" \ + "\"set unwindonsignal off\"\\. Evaluation of the expression containing" \ + "the function \\(_gdb_expr\\(__gdb_regs\\*\\)\\) will be abandoned\\."] \ "compile code segfault second" # C++ Specific tests. @@ -304,7 +309,12 @@ gdb_test "p globalvar" " = -76" "expect -76" gdb_test_no_output "set debug compile on" gdb_test "compile code static const int readonly = 1; *(int *) &readonly = 2;" \ - "The program being debugged was signaled while in a function called from GDB\\.\r\nGDB has restored the context to what it was before the call\\.\r\n.*" + [multi_line \ + "The program being debugged received signal SIGSEGV, Segmentation fault" \ + "while in a function called from GDB\\. GDB has restored the context" \ + "to what it was before the call\\. To change this behavior use" \ + "\"set unwindonsignal off\"\\. Evaluation of the expression containing" \ + "the function \\(_gdb_expr\\(__gdb_regs\\*\\)\\) will be abandoned\\."] gdb_test_no_output "set debug compile off" diff --git a/gdb/testsuite/gdb.compile/compile.exp b/gdb/testsuite/gdb.compile/compile.exp index 38818d2d255..f2ab4fafa93 100644 --- a/gdb/testsuite/gdb.compile/compile.exp +++ b/gdb/testsuite/gdb.compile/compile.exp @@ -159,7 +159,12 @@ gdb_test "info sym $infcall_pc" "\r\nNo symbol matches .*" "info sym not found" gdb_test_no_output "set unwindonsignal on" gdb_test "compile code *(volatile int *) 0 = 0;" \ - "The program being debugged was signaled while in a function called from GDB\\.\r\nGDB has restored the context to what it was before the call\\.\r\n.*" \ + [multi_line \ + "The program being debugged received signal SIGSEGV, Segmentation fault" \ + "while in a function called from GDB\\. GDB has restored the context" \ + "to what it was before the call\\. To change this behavior use" \ + "\"set unwindonsignal off\"\\. Evaluation of the expression containing" \ + "the function \\(_gdb_expr\\) will be abandoned\\."] \ "compile code segfault second" gdb_breakpoint [gdb_get_line_number "break-here"] @@ -312,7 +317,12 @@ gdb_test "p globalvar" " = -76" "expect -76" gdb_test_no_output "set debug compile on" gdb_test "compile code static const int readonly = 1; *(int *) &readonly = 2;" \ - "The program being debugged was signaled while in a function called from GDB\\.\r\nGDB has restored the context to what it was before the call\\.\r\n.*" + [multi_line \ + "The program being debugged received signal SIGSEGV, Segmentation fault" \ + "while in a function called from GDB\\. GDB has restored the context" \ + "to what it was before the call\\. To change this behavior use" \ + "\"set unwindonsignal off\"\\. Evaluation of the expression containing" \ + "the function \\(_gdb_expr\\) will be abandoned\\."] gdb_test_no_output "set debug compile off" diff --git a/gdb/testsuite/gdb.cp/gdb2495.exp b/gdb/testsuite/gdb.cp/gdb2495.exp index e3c0cca3175..93b046ad3a6 100644 --- a/gdb/testsuite/gdb.cp/gdb2495.exp +++ b/gdb/testsuite/gdb.cp/gdb2495.exp @@ -108,7 +108,12 @@ gdb_test "show unwindonsignal" \ # Check to see if new behaviour interferes with # normal signal handling in inferior function calls. gdb_test "p exceptions.raise_signal(1)" \ - "To change this behavior use \"set unwindonsignal off\".*" \ + [multi_line \ + "The program being debugged received signal SIGABRT, Aborted" \ + "while in a function called from GDB\\. GDB has restored the context" \ + "to what it was before the call\\. To change this behavior use" \ + "\"set unwindonsignal off\"\\. Evaluation of the expression containing" \ + "the function \\(SimpleException::raise_signal\\(int\\)\\) will be abandoned\\."]\ "check for unwindonsignal off message" # And reverse - turn off again. diff --git a/gdb/testsuite/gdb.mi/mi-condbreak-fail.exp b/gdb/testsuite/gdb.mi/mi-condbreak-fail.exp index 34be4b907d3..3ccca4c2e9b 100644 --- a/gdb/testsuite/gdb.mi/mi-condbreak-fail.exp +++ b/gdb/testsuite/gdb.mi/mi-condbreak-fail.exp @@ -14,7 +14,8 @@ # along with this program. If not, see . # Check that when GDB fails to evaluate the condition of a conditional -# breakpoint we only get one *stopped notification. +# breakpoint we only get one *stopped notification. In this test case +# the breakpoint condition fails due to receiving a signal (SIGSEGV). load_lib mi-support.exp set MIFLAGS "-i=mi" @@ -25,43 +26,84 @@ if [build_executable ${testfile}.exp ${binfile} ${srcfile}] { return -1 } -if {[mi_clean_restart $binfile]} { - return +# Create a breakpoint with a condition that invokes an inferior +# function call, that will segfault. Run until GDB hits the +# breakpoint and check how GDB reports the failed condition check. +# +# UNWIND_ON_SIGNAL is either 'on' or 'off'. This is used to configure +# GDB's 'set unwindonsignal' setting. + +proc run_test { unwind_on_signal } { + + if {[mi_clean_restart $::binfile]} { + return + } + + if {[mi_runto_main] == -1} { + return + } + + mi_gdb_test "-gdb-set unwindonsignal ${unwind_on_signal}" {\^done} \ + "set unwind-on-signal" + + # Create the conditional breakpoint. + set bp_location [gdb_get_line_number "Set breakpoint here"] + mi_create_breakpoint "-c \"cond_fail ()\" $::srcfile:$bp_location" \ + "insert conditional breakpoint" \ + -func foo -file ".*$::srcfile" -line "$bp_location" \ + -cond "cond_fail \\(\\)" + + # Number of the previous breakpoint. + set bpnum [mi_get_valueof "/d" "\$bpnum" "INVALID" \ + "get number for breakpoint"] + + # The line where we expect the inferior to crash. + set crash_linenum [gdb_get_line_number "Crash here"] + + # Run the inferior and wait for it to stop. + mi_send_resuming_command "exec-continue" "continue the inferior" + + if {$unwind_on_signal} { + mi_gdb_test "" \ + [multi_line \ + "&\"Error in testing condition for breakpoint $bpnum:\\\\n\"" \ + "&\"The program being debugged received signal SIGSEGV, Segmentation fault\\\\n\"" \ + "&\"while in a function called from GDB\\. GDB has restored the context\\\\n\"" \ + "&\"to what it was before the call\\. To change this behavior use\\\\n\"" \ + "&\"\\\\\"set unwindonsignal off\\\\\"\\. Evaluation of the expression containing\\\\n\"" \ + "&\"the function \\(cond_fail\\) will be abandoned\\.\\\\n\"" \ + "=breakpoint-modified,bkpt={number=\"$bpnum\",type=\"breakpoint\",\[^\r\n\]+times=\"1\",\[^\r\n\]+}" \ + "~\"\\\\n\"" \ + "~\"Breakpoint $bpnum, foo \\(\\) at \[^\r\n\]+/${::srcfile}:${bp_location}\\\\n\"" \ + "~\"${bp_location}\\\\t\[^\r\n\]+Set breakpoint here\\.\[^\r\n\]+\\\\n\"" \ + "\\*stopped,reason=\"breakpoint-hit\",disp=\"keep\",bkptno=\"$bpnum\",frame=\\{addr=\"$::hex\",func=\"foo\"\\,args=\\\[\\\],file=\"\[^\r\n\]+\",fullname=\"\[^\r\n\]+\",line=\"$bp_location\",\[^\r\n\]+}\[^\r\n\]+"] \ + "wait for stop" + + mi_info_frame "check the current frame" \ + -level 0 -func foo -line $bp_location + } else { + mi_gdb_test "" \ + [multi_line \ + "~\"\\\\nProgram\"" \ + "~\" received signal SIGSEGV, Segmentation fault\\.\\\\n\"" \ + "~\"$::hex in cond_fail \\(\\) at \[^\r\n\]+\"" \ + "~\"${crash_linenum}\\\\t\\s+return \\*p;\[^\r\n\]+\\\\n\"" \ + "\\*stopped,reason=\"signal-received\",signal-name=\"SIGSEGV\"\[^\r\n\]+frame=\\{addr=\"$::hex\",func=\"cond_fail\",args=\\\[\\\],file=\"\[^\r\n\]+\",fullname=\"\[^\r\n\]+\",line=\"$crash_linenum\",\[^\r\n\]+\\}\[^\r\n\]+" \ + "&\"Error in testing condition for breakpoint $bpnum:\\\\n\"" \ + "&\"The program being debugged was signaled while in a function called from GDB\\.\\\\n\"" \ + "&\"GDB remains in the frame where the signal was received\\.\\\\n\"" \ + "&\"To change this behavior use \\\\\"set unwindonsignal on\\\\\"\\.\\\\n\"" \ + "&\"Evaluation of the expression containing the function\\\\n\"" \ + "&\"\\(cond_fail\\) will be abandoned\\.\\\\n\"" \ + "&\"When the function is done executing, GDB will silently stop\\.\\\\n\"" \ + "=breakpoint-modified,bkpt={number=\"$bpnum\",type=\"breakpoint\",\[^\r\n\]+times=\"1\",\[^\r\n\]+}"] \ + "wait for stop" + + mi_info_frame "check the current frame" \ + -level 0 -func cond_fail -line $crash_linenum + } } -if {[mi_runto_main] == -1} { - return +foreach_with_prefix unwind_on_signal { off on } { + run_test $unwind_on_signal } - -# Create the conditional breakpoint. -set bp_location [gdb_get_line_number "Set breakpoint here"] -mi_create_breakpoint "-c \"cond_fail ()\" $srcfile:$bp_location" \ - "insert conditional breakpoint" \ - -func foo -file ".*$srcfile" -line "$bp_location" \ - -cond "cond_fail \\(\\)" - -# Number of the previous breakpoint. -set bpnum [mi_get_valueof "/d" "\$bpnum" "INVALID" \ - "get number for breakpoint"] - -# The line where we expect the inferior to crash. -set crash_linenum [gdb_get_line_number "Crash here"] - -# Run the inferior and wait for it to stop. -mi_send_resuming_command "exec-continue" "continue the inferior" -mi_gdb_test "" \ - [multi_line \ - "~\"\\\\nProgram\"" \ - "~\" received signal SIGSEGV, Segmentation fault\\.\\\\n\"" \ - "~\"$hex in cond_fail \\(\\) at \[^\r\n\]+\"" \ - "~\"${crash_linenum}\\\\t\\s+return \\*p;\[^\r\n\]+\\\\n\"" \ - "\\*stopped,reason=\"signal-received\",signal-name=\"SIGSEGV\"\[^\r\n\]+" \ - "&\"Error in testing condition for breakpoint $bpnum:\\\\n\"" \ - "&\"The program being debugged was signaled while in a function called from GDB\\.\\\\n\"" \ - "&\"GDB remains in the frame where the signal was received\\.\\\\n\"" \ - "&\"To change this behavior use \\\\\"set unwindonsignal on\\\\\"\\.\\\\n\"" \ - "&\"Evaluation of the expression containing the function\\\\n\"" \ - "&\"\\(cond_fail\\) will be abandoned\\.\\\\n\"" \ - "&\"When the function is done executing, GDB will silently stop\\.\\\\n\"" \ - "=breakpoint-modified,bkpt={number=\"$bpnum\",type=\"breakpoint\",\[^\r\n\]+times=\"1\",\[^\r\n\]+}"] \ - "wait for stop" diff --git a/gdb/testsuite/gdb.mi/mi-condbreak-throw.cc b/gdb/testsuite/gdb.mi/mi-condbreak-throw.cc new file mode 100644 index 00000000000..4ce8e72a3c9 --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi-condbreak-throw.cc @@ -0,0 +1,38 @@ +/* Copyright 2023 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 . */ + +volatile int global_counter = 0; + +int +cond_throw () +{ + throw 3; +} + +int +foo () +{ + global_counter += 1; /* Set breakpoint here. */ + return 0; +} + +int +main () +{ + int res = foo (); + return res; +} diff --git a/gdb/testsuite/gdb.mi/mi-condbreak-throw.exp b/gdb/testsuite/gdb.mi/mi-condbreak-throw.exp new file mode 100644 index 00000000000..4151fa18395 --- /dev/null +++ b/gdb/testsuite/gdb.mi/mi-condbreak-throw.exp @@ -0,0 +1,122 @@ +# Copyright (C) 2023 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 . + +# Check that when GDB fails to evaluate the condition of a conditional +# breakpoint we only get one *stopped notification. In this test case +# the breakpoint condition fails due to throwing an uncaught C++ +# excpetion. + +require allow_cplus_tests + +load_lib mi-support.exp +set MIFLAGS "-i=mi" + +standard_testfile .cc + +if [build_executable ${testfile}.exp ${binfile} ${srcfile} {debug c++}] { + return -1 +} + +# Create a breakpoint with a condition that invokes an inferior +# function call, that will segfault. Run until GDB hits the +# breakpoint and check how GDB reports the failed condition check. +# +# UNWIND_ON_EXCEPTION is either 'on' or 'off'. This is used to configure +# GDB's 'set unwind-on-terminating-exception' setting. + +proc run_test { unwind_on_exception } { + + if {[mi_clean_restart $::binfile]} { + return + } + + if {[mi_runto_main] == -1} { + return + } + + mi_gdb_test "-gdb-set unwind-on-terminating-exception ${unwind_on_exception}" {\^done} \ + "set unwind-on-terminating-exception" + + # Create the conditional breakpoint. + set bp_location [gdb_get_line_number "Set breakpoint here"] + mi_create_breakpoint "-c \"cond_throw ()\" $::srcfile:$bp_location" \ + "insert conditional breakpoint" \ + -func "foo\\(\\)" -file ".*$::srcfile" -line "$bp_location" \ + -cond "cond_throw \\(\\)" + + # Number of the previous breakpoint. + set bpnum [mi_get_valueof "/d" "\$bpnum" "INVALID" \ + "get number for breakpoint"] + + # The line where we expect the inferior to crash. + set crash_linenum 0 + #[gdb_get_line_number "Crash here"] + + # Run the inferior and wait for it to stop. + mi_send_resuming_command "exec-continue" "continue the inferior" + + if {$unwind_on_exception} { + mi_gdb_test "" \ + [multi_line \ + "&\"Error in testing condition for breakpoint $bpnum:\\\\n\"" \ + "&\"The program being debugged entered a std::terminate call, most likely\\\\n\"" \ + "&\"caused by an unhandled C\\+\\+ exception. GDB blocked this call in order\\\\n\"" \ + "&\"to prevent the program from being terminated, and has restored the\\\\n\"" \ + "&\"context to its original state before the call.\\\\n\"" \ + "&\"To change this behaviour use \\\\\"set unwind-on-terminating-exception off\\\\\"\\.\\\\n\"" \ + "&\"Evaluation of the expression containing the function \\(cond_throw\\(\\)\\)\\\\n\"" \ + "&\"will be abandoned.\\\\n\"" \ + "=breakpoint-modified,bkpt={number=\"$bpnum\",type=\"breakpoint\",\[^\r\n\]+times=\"1\",\[^\r\n\]+}" \ + "~\"\\\\n\"" \ + "~\"Breakpoint $bpnum, foo \\(\\) at \[^\r\n\]+/${::srcfile}:${bp_location}\\\\n\"" \ + "~\"${bp_location}\\\\t\[^\r\n\]+Set breakpoint here\\.\[^\r\n\]+\\\\n\"" \ + "\\*stopped,reason=\"breakpoint-hit\",disp=\"keep\",bkptno=\"$bpnum\",frame=\\{addr=\"$::hex\",func=\"foo\"\\,args=\\\[\\\],file=\"\[^\r\n\]+\",fullname=\"\[^\r\n\]+\",line=\"$bp_location\",\[^\r\n\]+}\[^\r\n\]+"] \ + "wait for stop" + + mi_info_frame "check the current frame" \ + -level 0 -func foo -line $bp_location + } else { + # This pattern matches multiple lines being sent to MI's + # stdout stream (that is wrapped in ~"..."). Depending on + # where exactly the thread stops, and which debug info is + # available, the following stop will produce different numbers + # of lines. + set out_ln_re "(?:\r\n\[~&\]\"\[^\r\n\]+\")*" + + mi_gdb_test "" \ + [multi_line \ + "terminate called after throwing an instance of 'int'" \ + "~\"\\\\nProgram\"" \ + "~\" received signal SIGABRT, Aborted\\.\\\\n\"${out_ln_re}" \ + "\\*stopped,reason=\"signal-received\",signal-name=\"SIGABRT\"\[^\r\n\]+frame=\\{addr=\"$::hex\",\[^\r\n\]+\\}\[^\r\n\]+" \ + "&\"Error in testing condition for breakpoint $bpnum:\\\\n\"" \ + "&\"The program being debugged was signaled while in a function called from GDB\\.\\\\n\"" \ + "&\"GDB remains in the frame where the signal was received\\.\\\\n\"" \ + "&\"To change this behavior use \\\\\"set unwindonsignal on\\\\\"\\.\\\\n\"" \ + "&\"Evaluation of the expression containing the function\\\\n\"" \ + "&\"\\(cond_throw\\(\\)\\) will be abandoned\\.\\\\n\"" \ + "&\"When the function is done executing, GDB will silently stop\\.\\\\n\"" \ + "=breakpoint-modified,bkpt={number=\"$bpnum\",type=\"breakpoint\",\[^\r\n\]+times=\"1\",\[^\r\n\]+}"] \ + "wait for stop" + + # Don't try to check the current frame here, the inferior will + # be stopped somewhere in the C++ runtime at the point where + # it is determined that the exception has not been handled. + } +} + +foreach_with_prefix unwind_on_exception { off } { + run_test $unwind_on_exception +}