[PATCH v3] [PR python/29603] Disable out-of-scope watchpoints

Johnson Sun j3.soon777@gmail.com
Thu Oct 20 18:11:02 GMT 2022


Currently, when a local software watchpoint goes out of scope, GDB sets the
watchpoint's disposition to `delete at next stop' and then normal stops
(i.e., stop and wait for the next GDB command). When GDB normal stops, it
automatically deletes the breakpoints with their disposition set to
`delete at next stop'.

Suppose a Python script decides not to normal stop when a local software
watchpoint goes out of scope, the watchpoint will not be automatically
deleted even when its disposition is set to `delete at next stop'.

Since GDB single-steps the program and tests the watched expression after each
instruction, not deleting the watchpoint causes the watchpoint to be hit many
more times than it should, as reported in PR python/29603.

This was happening because the watchpoint is not deleted or disabled when
going out of scope.

This commit fixes this issue by disabling the watchpoint when going out of
scope. It also adds a test to ensure this feature isn't regressed in the
future.

Two other solutions seem to solve this issue, but are in fact inappropriate:
1. Automatically delete breakpoints on all kinds of stops
   (in `fetch_inferior_event'). This solution is very slow since the deletion
   requires O(N) time for N breakpoints.
2. Disable the watchpoint after the watchpoint's disposition is set to
   `delete at next stop' (in `watchpoint_del_at_next_stop'). This solution
   modifies a non-extension-related code path, and isn't preferred since this
   issue cannot occur without extension languages. (gdb scripts always normal
   stop before continuing execution)

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29603
---
 gdb/breakpoint.c                           |  2 +
 gdb/testsuite/gdb.python/py-watchpoint.c   | 27 ++++++++++++
 gdb/testsuite/gdb.python/py-watchpoint.exp | 48 ++++++++++++++++++++++
 gdb/testsuite/gdb.python/py-watchpoint.py  | 30 ++++++++++++++
 4 files changed, 107 insertions(+)
 create mode 100644 gdb/testsuite/gdb.python/py-watchpoint.c
 create mode 100644 gdb/testsuite/gdb.python/py-watchpoint.exp
 create mode 100644 gdb/testsuite/gdb.python/py-watchpoint.py

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index bff3bac7d1a..15f4ae2131c 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5340,6 +5340,8 @@ bpstat_check_breakpoint_conditions (bpstat *bs, thread_info *thread)
   /* Evaluate extension language breakpoints that have a "stop" method
      implemented.  */
   bs->stop = breakpoint_ext_lang_cond_says_stop (b);
+  if (b->disposition == disp_del_at_next_stop)
+    disable_breakpoint(b);
 
   if (is_watchpoint (b))
     {
diff --git a/gdb/testsuite/gdb.python/py-watchpoint.c b/gdb/testsuite/gdb.python/py-watchpoint.c
new file mode 100644
index 00000000000..4e1760bb05b
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-watchpoint.c
@@ -0,0 +1,27 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2022 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 <stdio.h>
+
+int
+main (void)
+{
+  int i;
+  for (i = 0; i < 3; i++)
+    printf ("%d", i);
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.python/py-watchpoint.exp b/gdb/testsuite/gdb.python/py-watchpoint.exp
new file mode 100644
index 00000000000..ac58d75c523
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-watchpoint.exp
@@ -0,0 +1,48 @@
+# Copyright (C) 2022 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 Watchpoints are deleted after use.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile]} {
+    return -1
+}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+if ![runto_main] then {
+    return 0
+}
+
+# For remote host testing
+set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+
+gdb_test_no_output "set can-use-hw-watchpoints 0" "Don't use hardware watchpoints"
+gdb_test "python print(len(gdb.breakpoints()))" "1" "check default BP count"
+gdb_test "source $pyfile" ".*Python script imported.*" \
+    "import python scripts"
+gdb_test "python print(len(gdb.breakpoints()))" "2" "check modified BP count"
+gdb_test_sequence "continue" "run until program stops" {
+    "Watchpoint Hit: ."
+    "\[\r\n\]+Watchpoint . deleted because the program has left the block in"
+    "\[\r\n\]+which its expression is valid\."
+    "Watchpoint Hit: ."
+    "\[\r\n\]+012\\[Inferior 1 \\(process .*\\) exited normally\\]"
+}
+gdb_test "python print(len(gdb.breakpoints()))" "1" "check BP count"
diff --git a/gdb/testsuite/gdb.python/py-watchpoint.py b/gdb/testsuite/gdb.python/py-watchpoint.py
new file mode 100644
index 00000000000..ce5dee118ad
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-watchpoint.py
@@ -0,0 +1,30 @@
+# Copyright (C) 2022 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/>.
+
+
+class MyBreakpoint(gdb.Breakpoint):
+    def __init__(self, *args, **kwargs):
+        gdb.Breakpoint.__init__(self, *args, **kwargs)
+        self.i = 0
+
+    def stop(self):
+        self.i += 1
+        print("Watchpoint Hit:", self.i)
+        return False
+
+
+MyBreakpoint("i", gdb.BP_WATCHPOINT)
+
+print("Python script imported")
-- 
2.34.1

base-commit: c99b2113a478a075e8d8f7c3848a92f1ce73f847


More information about the Gdb-patches mailing list