gdb: MI stopped events when unwindonsignal is on
This recent commit: commitb1c0ab2080
Date: Wed Jul 12 21:56:50 2023 +0100 gdb: avoid double stop after failed breakpoint condition check Addressed a problem where two MI stopped events would be reported if a breakpoint condition failed due to a signal, this commit was a replacement for this commit: commit2e411b8c68
Date: Fri Oct 14 14:53:15 2022 +0100 gdb: don't always print breakpoint location after failed condition check which solved the two stop problem, but only for the CLI. Before both of these commits, if a b/p condition failed due to a signal then the user would see two stops reported, the first at the location where the signal occurred, and the second at the location of the breakpoint. By default GDB remains at the location where the signal occurred, so the second reported stop can be confusing, this is the problem that commit2e411b8c68
tried to solve (for the CLI) andb1c0ab2080
extended also address the issue for MI too. However, while working on another patch I realised that there was a problem with GDB after the above commits. Neither of the above commits considered 'set unwindonsignal on'. With this setting on, when an inferior function call fails with a signal GDB will unwind the stack back to the location where the inferior function call started. In the b/p case we're looking at, the stop should be reported at the location of the breakpoint, not at the location where the signal occurred, and this isn't what happens. This commit fixes this by ensuring that when unwindonsignal is 'on', GDB reports a single stop event at the location of the breakpoint, this fixes things for both CLI and MI. The function call_thread_fsm::should_notify_stop is called when the inferior function call completes and GDB is figuring out if the user should be notified about this stop event by calling normal_stop from fetch_inferior_event in infrun.c. If normal_stop is called, then this notification will be for the location where the inferior call stopped, which will be the location at which the signal occurred. Prior to this commit, the only time that normal_stop was not called, was if the inferior function call completed successfully, this was controlled by ::should_notify_stop, which only turns false when the inferior function call has completed successfully. In this commit I have extended the logic in ::should_notify_stop. Now there are three cases in which ::should_notify_stop will return false, and we will not announce the first stop (by calling normal_stop). These three reasons are: 1. If the inferior function call completes successfully, this is unchanged behaviour, 2. If the inferior function call stopped due to a signal and 'set unwindonsignal on' is in effect, and 3. If the inferior function call stopped due to an uncaught C++ exception, and 'set unwind-on-terminating-exception on' is in effect. However, if we don't call normal_stop then we need to call async_enable_stdin in call_thread_fsm::should_stop. Prior to this commit this was only done for the case where the inferior function call completed successfully. In this commit I now call ::should_notify_stop and use this to determine if we need to call async_enable_stdin. With this done we now call async_enable_stdin for each of the three cases listed above, which means that GDB will exit wait_sync_command_done correctly (see run_inferior_call in infcall.c). With these two changes the problem is mostly resolved. However, the solution isn't ideal, we've still lost some information. Here is how GDB 13.1 behaves, this is before commitsb1c0ab2080
and2e411b8c68
: $ gdb -q /tmp/mi-condbreak-fail \ -ex 'set unwindonsignal on' \ -ex 'break 30 if (cond_fail())' \ -ex 'run' Reading symbols from /tmp/mi-condbreak-fail... Breakpoint 1 at 0x40111e: file /tmp/mi-condbreak-fail.c, line 30. Starting program: /tmp/mi-condbreak-fail Program received signal SIGSEGV, Segmentation fault. 0x0000000000401116 in cond_fail () at /tmp/mi-condbreak-fail.c:24 24 return *p; /* Crash here. */ Error in testing breakpoint condition: The program being debugged was signaled 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 (cond_fail) will be abandoned. Breakpoint 1, foo () at /tmp/mi-condbreak-fail.c:30 30 global_counter += 1; /* Set breakpoint here. */ (gdb) In this state we see two stop notifications, the first is where the signal occurred, while the second is where the breakpoint is located. As GDB has unwound the stack (thanks to unwindonsignal) the second stop notification reflects where the inferior is actually located. Then after commitsb1c0ab2080
and2e411b8c68
the behaviour changed to this: $ gdb -q /tmp/mi-condbreak-fail \ -ex 'set unwindonsignal on' \ -ex 'break 30 if (cond_fail())' \ -ex 'run' Reading symbols from /tmp/mi-condbreak-fail... Breakpoint 1 at 0x40111e: file /tmp/mi-condbreak-fail.c, line 30. Starting program: /tmp/mi-condbreak-fail Program received signal SIGSEGV, Segmentation fault. 0x0000000000401116 in cond_fail () at /tmp/mi-condbreak-fail.c:24 24 return *p; /* Crash here. */ Error in testing condition for breakpoint 1: The program being debugged was signaled 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 (cond_fail) will be abandoned. (gdb) bt 1 #0 foo () at /tmp/mi-condbreak-fail.c:30 (More stack frames follow...) (gdb) This is the broken state. GDB is reports the SIGSEGV location, but not the unwound breakpoint location. The final 'bt 1' shows that the inferior is not located in cond_fail, which is the only location GDB reported, so this is clearly wrong. After implementing the fixes described above we now get this behaviour: $ gdb -q /tmp/mi-condbreak-fail \ -ex 'set unwindonsignal on' \ -ex 'break 30 if (cond_fail())' \ -ex 'run' Reading symbols from /tmp/mi-condbreak-fail... Breakpoint 1 at 0x40111e: file /tmp/mi-condbreak-fail.c, line 30. Starting program: /tmp/mi-condbreak-fail Error in testing breakpoint condition for breakpoint 1: The program being debugged was signaled 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 (cond_fail) will be abandoned. Breakpoint 1, foo () at /tmp/mi-condbreak-fail.c:30 30 global_counter += 1; /* Set breakpoint here. */ (gdb) This is better. GDB now reports a single stop at the location of the breakpoint, which is where the inferior is actually located. However, by removing the first stop notification we have lost some potentially useful information about which signal caused the inferior to stop. To address this I've reworked the message that is printed to include the signal information. GDB now reports this: $ gdb -q /tmp/mi-condbreak-fail \ -ex 'set unwindonsignal on' \ -ex 'break 30 if (cond_fail())' \ -ex 'run' Reading symbols from /tmp/mi-condbreak-fail... Breakpoint 1 at 0x40111e: file /tmp/mi-condbreak-fail.c, line 30. Starting program: /tmp/mi-condbreak-fail Error in testing condition for breakpoint 1: 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 (cond_fail) will be abandoned. Breakpoint 1, foo () at /tmp/mi-condbreak-fail.c:30 30 global_counter += 1; /* Set breakpoint here. */ (gdb) This is better, the user now sees a single stop notification at the correct location, and the error message describes which signal caused the inferior function call to stop. However, we have lost the information about where the signal occurred. I did consider trying to include this information in the error message, but, in the end, I opted not too. I wasn't sure it was worth the effort. If the user has selected to unwind on signal, then surely this implies they maybe aren't interested in debugging failed inferior calls, so, hopefully, just knowing the signal name will be enough. I figure we can always add this information in later if there's a demand for it.
This commit is contained in:
parent
696b09bb2a
commit
3ce8f906be
8 changed files with 319 additions and 53 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# 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"
|
||||
|
|
38
gdb/testsuite/gdb.mi/mi-condbreak-throw.cc
Normal file
38
gdb/testsuite/gdb.mi/mi-condbreak-throw.cc
Normal file
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||
|
||||
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;
|
||||
}
|
122
gdb/testsuite/gdb.mi/mi-condbreak-throw.exp
Normal file
122
gdb/testsuite/gdb.mi/mi-condbreak-throw.exp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# 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
|
||||
}
|
Loading…
Add table
Reference in a new issue