summaryrefslogtreecommitdiff
path: root/gdb/infcall.c
diff options
context:
space:
mode:
authorAndrew Burgess <aburgess@redhat.com>2022-10-14 13:40:20 +0100
committerAndrew Burgess <aburgess@redhat.com>2023-04-03 14:46:32 +0100
commit1bdcdb41926e18c3a0b14728b05f68f6f5cd2b8a (patch)
tree39e702d1129c99592751aaf3b6e47679be561c4d /gdb/infcall.c
parent3812b38d8de5804ad3eadd6c7a5d532402ddabab (diff)
downloadbinutils-gdb-1bdcdb41926e18c3a0b14728b05f68f6f5cd2b8a.tar.gz
gdb: avoid repeated signal reporting during failed conditional breakpoint
Consider the following case: (gdb) list some_func 1 int 2 some_func () 3 { 4 int *p = 0; 5 return *p; 6 } 7 8 void 9 foo () 10 { (gdb) break foo if (some_func ()) Breakpoint 1 at 0x40111e: file bpcond.c, line 11. (gdb) r Starting program: /tmp/bpcond Program received signal SIGSEGV, Segmentation fault. 0x0000000000401116 in some_func () at bpcond.c:5 5 return *p; Error in testing breakpoint condition: The program being debugged was signaled while in a function called from GDB. GDB remains in the frame where the signal was received. To change this behavior use "set unwindonsignal on". Evaluation of the expression containing the function (some_func) will be abandoned. When the function is done executing, GDB will silently stop. Program received signal SIGSEGV, Segmentation fault. Breakpoint 1, 0x0000000000401116 in some_func () at bpcond.c:5 5 return *p; (gdb) Notice that this line: Program received signal SIGSEGV, Segmentation fault. Appears twice in the output. The first time is followed by the current location. The second time is a little odd, why do we print that? Printing that line is controlled, in part, by a global variable, stopped_by_random_signal. This variable is reset to zero in handle_signal_stop, and is set if/when GDB figures out that the inferior stopped due to some random signal. The problem is, in our case, GDB first stops at the breakpoint for foo, and enters handle_signal_stop and the stopped_by_random_signal global is reset to 0. Later within handle_signal_stop GDB calls bpstat_stop_status, it is within this function (via bpstat_check_breakpoint_conditions) that the breakpoint condition is checked, and, we end up calling the inferior function (some_func in our example above). In our case above the thread performing the inferior function call segfaults in some_func. GDB catches the SIGSEGV and handles the stop, this causes us to reenter handle_signal_stop. The global variable stopped_by_random_signal is updated, this time it is set to true because the thread stopped due to SIGSEGV. As a result of this we print the first instance of the line (as seen above in the example). Finally we unwind GDB's call stack, the inferior function call is complete, and we return to the original handle_signal_stop. However, the stopped_by_random_signal global is still carrying the value as computed for the inferior function call's stop, which is why we now print a second instance of the line, as seen in the example. To prevent this, I propose adding a scoped_restore before we start an inferior function call. This will save and restore the global stopped_by_random_signal value. With this done, the output from our example is now this: (gdb) list some_func 1 int 2 some_func () 3 { 4 int *p = 0; 5 return *p; 6 } 7 8 void 9 foo () 10 { (gdb) break foo if (some_func ()) Breakpoint 1 at 0x40111e: file bpcond.c, line 11. (gdb) r Starting program: /tmp/bpcond Program received signal SIGSEGV, Segmentation fault. 0x0000000000401116 in some_func () at bpcond.c:5 5 return *p; Error in testing condition for breakpoint 1: The program being debugged stopped while in a function called from GDB. Evaluation of the expression containing the function (some_func) will be abandoned. When the function is done executing, GDB will silently stop. Breakpoint 1, 0x0000000000401116 in some_func () at bpcond.c:5 5 return *p; (gdb) We now only see the 'Program received signal SIGSEGV, ...' line once, which I think makes more sense. Finally, I'm aware that the last few lines, that report the stop as being at 'Breakpoint 1', when this is not where the thread is actually located anymore, is not great. I'll address that in the next commit.
Diffstat (limited to 'gdb/infcall.c')
-rw-r--r--gdb/infcall.c9
1 files changed, 9 insertions, 0 deletions
diff --git a/gdb/infcall.c b/gdb/infcall.c
index 9ed17bf4f8b..e6cc6ed1a21 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -1293,6 +1293,15 @@ call_function_by_hand_dummy (struct value *function,
/* Register a clean-up for unwind_on_terminating_exception_breakpoint. */
SCOPE_EXIT { delete_std_terminate_breakpoint (); };
+ /* The stopped_by_random_signal variable is global. If we are here
+ as part of a breakpoint condition check then the global will have
+ already been setup as part of the original breakpoint stop. By
+ making the inferior call the global will be changed when GDB
+ handles the stop after the inferior call. Avoid confusion by
+ restoring the current value after the inferior call. */
+ scoped_restore restore_stopped_by_random_signal
+ = make_scoped_restore (&stopped_by_random_signal, 0);
+
/* - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP - SNIP -
If you're looking to implement asynchronous dummy-frames, then
just below is the place to chop this function in two.. */