aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.base/infcall-failure.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/testsuite/gdb.base/infcall-failure.c
parent3812b38d8de5804ad3eadd6c7a5d532402ddabab (diff)
downloadgdb-1bdcdb41926e18c3a0b14728b05f68f6f5cd2b8a.zip
gdb-1bdcdb41926e18c3a0b14728b05f68f6f5cd2b8a.tar.gz
gdb-1bdcdb41926e18c3a0b14728b05f68f6f5cd2b8a.tar.bz2
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/testsuite/gdb.base/infcall-failure.c')
-rw-r--r--gdb/testsuite/gdb.base/infcall-failure.c48
1 files changed, 48 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.base/infcall-failure.c b/gdb/testsuite/gdb.base/infcall-failure.c
new file mode 100644
index 0000000..fae012b
--- /dev/null
+++ b/gdb/testsuite/gdb.base/infcall-failure.c
@@ -0,0 +1,48 @@
+/* Copyright 2022-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/>. */
+
+/* A function that segfaults (assuming that reads of address zero are
+ prohibited), this is used from within a breakpoint condition. */
+int
+func_segfault ()
+{
+ volatile int *p = 0;
+ return *p; /* Segfault here. */
+}
+
+/* A function in which we will place a breakpoint. This function is itself
+ then used from within a breakpoint condition. */
+int
+func_bp ()
+{
+ int res = 0; /* Second breakpoint. */
+ return res;
+}
+
+int
+foo ()
+{
+ return 0; /* First breakpoint. */
+}
+
+int
+main ()
+{
+ int res = foo ();
+
+ return res;
+}