Message ID | 20221020181101.193226-1-j3.soon777@gmail.com |
---|---|
State | New |
Headers |
Return-Path: <gdb-patches-bounces+patchwork=sourceware.org@sourceware.org> X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 5B0A8384D175 for <patchwork@sourceware.org>; Thu, 20 Oct 2022 18:12:16 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 5B0A8384D175 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1666289536; bh=flG0Nh1IdfPm5pBL5pYk1YIUAsVGk7WChNu+IkWscVA=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=knEQRRYXMKpq3yOAnuf68RHDQrS5CeD2c4ZeB2xCxgdSPs+6F5J9/HPQQcAGW3RqL onnxKGFV0F6EY4fi4WVF0bdc8gbpM6Km2KLBpEqZlHScJ6lz1SWWXveKLz8CCUXJWg RumA3f3YAWX/2LulVppl0+MPAD5fir9ls2te1A8k= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from mail-oa1-x2a.google.com (mail-oa1-x2a.google.com [IPv6:2001:4860:4864:20::2a]) by sourceware.org (Postfix) with ESMTPS id 4B369384D14D for <gdb-patches@sourceware.org>; Thu, 20 Oct 2022 18:11:51 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 4B369384D14D Received: by mail-oa1-x2a.google.com with SMTP id 586e51a60fabf-1321a1e94b3so586085fac.1 for <gdb-patches@sourceware.org>; Thu, 20 Oct 2022 11:11:51 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=flG0Nh1IdfPm5pBL5pYk1YIUAsVGk7WChNu+IkWscVA=; b=VthHKLc5mmKSjqEodYIqekXNZ6y8HAfJHZSLgxqWSv3J/YG6X3gpl2rNjg61u3uYHA Z5PHrv5CWG5dQe0uOm9Xz1RHnwlhfjg8UNZvjlBE/p/gn55i0m5uaCPkf6gQuWl3dVQg 7ZzkeByfBmrsLS+HcTkA6VL8owNo3ObNBWCx7tRJ156FGn/D+6tilesxdbavXQhZBmI4 tHWdtveLHjVV9ThX0gLaGInkF2jP/DDI7aFAWysvAmhChECsEc/gltQQGnjH9WPLG1N8 Ubo4ljd9N/L3qi1sKc7E8DVDCtThAt2x/bbWS36vbentV8iZwfbmkLHha0oMgOq6F8xS RGQQ== X-Gm-Message-State: ACrzQf0NahfCWN9orWd5TpujOoyk10bzl81ORasRcQiZyBUscDTRqFxN 4kYeWFagc7sTKzGphbL9ezNJKWoXo2d0IZnG X-Google-Smtp-Source: AMsMyM5cSczc/jrnt8G4cyNTgauQww9jM5ftaXhe15txxH9WfXGBaEKIn1h6rrtlXHJ50e81IaBHjA== X-Received: by 2002:a17:90b:4b48:b0:20a:8ea4:a18d with SMTP id mi8-20020a17090b4b4800b0020a8ea4a18dmr52952876pjb.75.1666289499968; Thu, 20 Oct 2022 11:11:39 -0700 (PDT) Received: from ubuntu-22.. (intel10.cs.nthu.edu.tw. [140.114.89.60]) by smtp.gmail.com with ESMTPSA id e13-20020a17090301cd00b00177c488fea5sm13375546plh.12.2022.10.20.11.11.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 20 Oct 2022 11:11:39 -0700 (PDT) To: Lancelot SIX <lsix@lancelotsix.com>, gdb-patches@sourceware.org Subject: [PATCH v3] [PR python/29603] Disable out-of-scope watchpoints Date: Fri, 21 Oct 2022 02:11:02 +0800 Message-Id: <20221020181101.193226-1-j3.soon777@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221020175707.193041-1-j3.soon777@gmail.com> References: <20221020175707.193041-1-j3.soon777@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-10.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list <gdb-patches.sourceware.org> List-Unsubscribe: <https://sourceware.org/mailman/options/gdb-patches>, <mailto:gdb-patches-request@sourceware.org?subject=unsubscribe> List-Archive: <https://sourceware.org/pipermail/gdb-patches/> List-Post: <mailto:gdb-patches@sourceware.org> List-Help: <mailto:gdb-patches-request@sourceware.org?subject=help> List-Subscribe: <https://sourceware.org/mailman/listinfo/gdb-patches>, <mailto:gdb-patches-request@sourceware.org?subject=subscribe> From: Johnson Sun via Gdb-patches <gdb-patches@sourceware.org> Reply-To: Johnson Sun <j3.soon777@gmail.com> Cc: j3.soon777@gmail.com Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" <gdb-patches-bounces+patchwork=sourceware.org@sourceware.org> |
Series |
[v3,PR,python/29603] Disable out-of-scope watchpoints
|
|
Commit Message
Johnson Sun
Oct. 20, 2022, 6:11 p.m. UTC
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
Comments
Ping for: <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. Johnson On 10/21/2022 2:11 AM, Johnson Sun wrote: > 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")
Ping for: <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. Johnson On 11/18/2022 8:17 PM, JohnsonSun wrote: > Ping for: > <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. > > Johnson > > On 10/21/2022 2:11 AM, Johnson Sun wrote: >> 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") >
Ping for: <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. Johnson On 11/25/2022 11:11 PM, Johnson Sun wrote: > Ping for: > <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. > > Johnson > > On 11/18/2022 8:17 PM, JohnsonSun wrote: >> Ping for: >> <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. >> >> Johnson >> >> On 10/21/2022 2:11 AM, Johnson Sun wrote: >>> 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") >>
Ping for: <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. Johnson On 12/5/2022 12:45 AM, Johnson Sun wrote: > Ping for: > <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. > > Johnson > > On 11/25/2022 11:11 PM, Johnson Sun wrote: >> Ping for: >> <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. >> >> Johnson >> >> On 11/18/2022 8:17 PM, JohnsonSun wrote: >>> Ping for: >>> <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. >>> >>> Johnson >>> >>> On 10/21/2022 2:11 AM, Johnson Sun wrote: >>>> 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") >>>
Ping for: <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. Johnson On 12/13/2022 5:44 AM, Johnson Sun wrote: > Ping for: > <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. > > Johnson > > On 12/5/2022 12:45 AM, Johnson Sun wrote: >> Ping for: >> <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. >> >> Johnson >> >> On 11/25/2022 11:11 PM, Johnson Sun wrote: >>> Ping for: >>> <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. >>> >>> Johnson >>> >>> On 11/18/2022 8:17 PM, JohnsonSun wrote: >>>> Ping for: >>>> <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. >>>> >>>> Johnson >>>> >>>> On 10/21/2022 2:11 AM, Johnson Sun wrote: >>>>> 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") >>>>
Ping for: <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. Johnson On 12/21/2022 6:08 AM, Johnson Sun wrote: > Ping for: > <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. > > Johnson > > On 12/13/2022 5:44 AM, Johnson Sun wrote: >> Ping for: >> <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. >> >> Johnson >> >> On 12/5/2022 12:45 AM, Johnson Sun wrote: >>> Ping for: >>> <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. >>> >>> Johnson >>> >>> On 11/25/2022 11:11 PM, Johnson Sun wrote: >>>> Ping for: >>>> <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. >>>> >>>> Johnson >>>> >>>> On 11/18/2022 8:17 PM, JohnsonSun wrote: >>>>> Ping for: >>>>> <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. >>>>> >>>>> Johnson >>>>> >>>>> On 10/21/2022 2:11 AM, Johnson Sun wrote: >>>>>> 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") >>>>>
Ping for: <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. Johnson On 12/28/2022 12:40 AM, JohnsonSun wrote: > Ping for: > <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. > > Johnson > > On 12/21/2022 6:08 AM, Johnson Sun wrote: >> Ping for: >> <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. >> >> Johnson >> >> On 12/13/2022 5:44 AM, Johnson Sun wrote: >>> Ping for: >>> <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. >>> >>> Johnson >>> >>> On 12/5/2022 12:45 AM, Johnson Sun wrote: >>>> Ping for: >>>> <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. >>>> >>>> Johnson >>>> >>>> On 11/25/2022 11:11 PM, Johnson Sun wrote: >>>>> Ping for: >>>>> <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. >>>>> >>>>> Johnson >>>>> >>>>> On 11/18/2022 8:17 PM, JohnsonSun wrote: >>>>>> Ping for: >>>>>> <https://sourceware.org/pipermail/gdb-patches/2022-October/192909.html>. >>>>>> >>>>>> Johnson >>>>>> >>>>>> On 10/21/2022 2:11 AM, Johnson Sun wrote: >>>>>>> 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") >>>>>> >
On 10/20/22 14:11, Johnson Sun via Gdb-patches wrote: > 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) I have a bit of trouble reviewing this, because I'm not too familiar with the lifecycle of watchpoints (I know the principle, but not the specifically where things happen in GDB). So it's difficult to tell whether this is right or if there's a better way to handle it. However, the supplied test does not pass for me: source /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.python/py-watchpoint/py-watchpoint.py Watchpoint 2: i Python script imported (gdb) PASS: gdb.python/py-watchpoint.exp: import python scripts python print(len(gdb.breakpoints())) 2 (gdb) PASS: gdb.python/py-watchpoint.exp: check modified BP count gdb_expect_list pattern: /Watchpoint Hit: ./ continue Continuing. Watchpoint Hit: 1 gdb_expect_list pattern: /[ ]+Watchpoint . deleted because the program has left the block in/ FAIL: gdb.python/py-watchpoint.exp: run until program stops (pattern 2) (timeout) gdb_expect_list pattern: /[ ]+which its expression is valid./ gdb_expect_list pattern: /Watchpoint Hit: ./ gdb_expect_list pattern: /[ ]+012\[Inferior 1 \(process .*\) exited normally\]/ gdb_expect_list pattern: // python print(len(gdb.breakpoints())) Watchpoint Hit: 2 Watchpoint Hit: 3 Watchpoint Hit: 4 Watchpoint 2 deleted because the program has left the block in which its expression is valid. Watchpoint Hit: 5 012[Inferior 1 (process 2381681) exited normally] (gdb) FAIL: gdb.python/py-watchpoint.exp: check BP count (the program exited) > > 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); Is there a reason to do it here in particular, and not, let's say as soon as we change the disposition to disp_del_at_next_stop? Other question: shouldn't marking the watchpoint as disp_del_at_next_stop make it so it won't be inserted next time we resume? After all should_be_inserted returns false for breakpoint locations that are disp_del_at_next_stop. Perhaps it's because since we don't do a "normal" stop, breakpoint locations stay inserted, and there's nothing that pulls this location out of the target? Therefore, maybe a solution, to keep things coherent, would be to pull the breakpoint locations out when marking the breakpoint as disp_del_at_next_stop? This way, we would respect the invariant that a breakpoint disp_del_at_next_stop can't be inserted (I don't know if this is really what is happening though, it's just speculation). And, in a broader scope, why wait until the next normal stop to delete the watchpoint? A "normal" stop being a stop that we present to the user (the normal_stop function). We currently call breakpoint_auto_delete in normal_stop, which is why we don't reach it when your Breakpoint.stop method returns False. At the end of, let's say, handle_signal_stop, could we go over the bpstat chain and delete any breakpoint we've just hit that is marked for deletion? Simon
Hi, Thanks for reviewing this! > the supplied test does not pass for me The current test doesn't seem to produce consistent results across different machines, I'll try to fix it in the next version. > Is there a reason to do it here in particular, and not, let's say as > soon as we change the disposition to disp_del_at_next_stop? I have implemented this in the v1 patch, I called `disable_breakpoint' as soon as we change the disposition to `disp_del_at_next_stop' (in `watchpoint_del_at_next_stop'). However, LancelotSIX <lsix@lancelotsix.com> mentioned that the fix is in a non-extension-related code path, and suggested disabling it in `bpstat_check_breakpoint_conditions' instead (the v3 patch). See: https://sourceware.org/pipermail/gdb-patches/2022-September/192120.html Both the v1 and v3 patch fixes the issue. I personally prefer the v1 patch. (i.e., modifying `watchpoint_del_at_next_stop') > shouldn't marking the watchpoint as > disp_del_at_next_stop make it so it won't be inserted next time we > resume? After all should_be_inserted returns false for breakpoint > locations that are disp_del_at_next_stop. Perhaps it's because since we > don't do a "normal" stop, breakpoint locations stay inserted, and > there's nothing that pulls this location out of the target? Therefore, > maybe a solution, to keep things coherent, would be to pull the > breakpoint locations out when marking the breakpoint as > disp_del_at_next_stop? This way, we would respect the invariant that a > breakpoint disp_del_at_next_stop can't be inserted (I don't know if this > is really what is happening though, it's just speculation). For software watchpoints, they cannot be pulled out of the target, since they may not be inserted into the target in the first place: /* Locations of type bp_loc_other and bp_loc_software_watchpoint are only maintained at GDB side, so there is no need to remove them. */ -- gdb/breakpoint.c:3840 Their expressions are re-evaluated by single-stepping through the program: Software watchpoints are very slow, since GDB needs to single-step the program being debugged and test the value of the watched expression(s) after each instruction. -- https://sourceware.org/gdb/wiki/Internals%20Watchpoints Therefore, we must either disable or delete the software watchpoint. We cannot pull it out of the target since it's only maintained on the GDB side. > And, in a broader scope, why wait until the next normal stop to delete > the watchpoint? A "normal" stop being a stop that we present to the > user (the normal_stop function). We currently call > breakpoint_auto_delete in normal_stop, which is why we don't reach it > when your Breakpoint.stop method returns False. At the end of, let's > say, handle_signal_stop, could we go over the bpstat chain and delete > any breakpoint we've just hit that is marked for deletion? I believe this choice is due to performance issues. In an early attempt, I tried to call `breakpoint_auto_delete' on all kinds of stops. However, this implementation is very slow when we have a large number of breakpoints, since we need to go over the entire bpstat chain on all stops. I recall that this implementation fails on certain testcases with a large number of breakpoints with many breakpoint stops. Furthermore, the reason for using the `disp_del_at_next_stop' state remains unclear, as mentioned in the comments: /* NOTE drow/2003-09-08: This state only exists for removing watchpoints. It's not clear that it's necessary... */ -- gdb/breakpoint.c:2914 By tracing the git history, the `disp_del_at_next_stop' state is introduced in commit c906108c21474dfb4ed285bcc0ac6fe02cd400cc, which doesn't provide any proper explanation of this state. I think the best way to deal with this is to avoid going over the entire bpstat chain when deleting breakpoints. Potentially by keeping track of a chain of breakpoints that should be deleted, and changing the bpstat chain to a doubly linked list for the ease of deletion. Such changes deserve a dedicated patch, though. To sum up, I prefer modifying `watchpoint_del_at_next_stop' (i.e., the v1 patch). If you also think it's appropriate, I'll fix the failing test and prepare the v4 patch accordingly. Johnson On 1/13/2023 11:40 PM, SimonMarchi wrote: > > On 10/20/22 14:11, Johnson Sun via Gdb-patches wrote: >> 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) > I have a bit of trouble reviewing this, because I'm not too familiar > with the lifecycle of watchpoints (I know the principle, but not the > specifically where things happen in GDB). So it's difficult to tell > whether this is right or if there's a better way to handle it. > > However, the supplied test does not pass for me: > > source /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.python/py-watchpoint/py-watchpoint.py > Watchpoint 2: i > Python script imported > (gdb) PASS: gdb.python/py-watchpoint.exp: import python scripts > python print(len(gdb.breakpoints())) > 2 > (gdb) PASS: gdb.python/py-watchpoint.exp: check modified BP count > gdb_expect_list pattern: /Watchpoint Hit: ./ > continue > Continuing. > Watchpoint Hit: 1 > gdb_expect_list pattern: /[ > ]+Watchpoint . deleted because the program has left the block in/ > FAIL: gdb.python/py-watchpoint.exp: run until program stops (pattern 2) (timeout) > gdb_expect_list pattern: /[ > ]+which its expression is valid./ > gdb_expect_list pattern: /Watchpoint Hit: ./ > gdb_expect_list pattern: /[ > ]+012\[Inferior 1 \(process .*\) exited normally\]/ > gdb_expect_list pattern: // > python print(len(gdb.breakpoints())) > Watchpoint Hit: 2 > Watchpoint Hit: 3 > Watchpoint Hit: 4 > > Watchpoint 2 deleted because the program has left the block in > which its expression is valid. > Watchpoint Hit: 5 > 012[Inferior 1 (process 2381681) exited normally] > (gdb) FAIL: gdb.python/py-watchpoint.exp: check BP count (the program exited) > >> 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); > Is there a reason to do it here in particular, and not, let's say as > soon as we change the disposition to disp_del_at_next_stop? > > Other question: shouldn't marking the watchpoint as > disp_del_at_next_stop make it so it won't be inserted next time we > resume? After all should_be_inserted returns false for breakpoint > locations that are disp_del_at_next_stop. Perhaps it's because since we > don't do a "normal" stop, breakpoint locations stay inserted, and > there's nothing that pulls this location out of the target? Therefore, > maybe a solution, to keep things coherent, would be to pull the > breakpoint locations out when marking the breakpoint as > disp_del_at_next_stop? This way, we would respect the invariant that a > breakpoint disp_del_at_next_stop can't be inserted (I don't know if this > is really what is happening though, it's just speculation). > > And, in a broader scope, why wait until the next normal stop to delete > the watchpoint? A "normal" stop being a stop that we present to the > user (the normal_stop function). We currently call > breakpoint_auto_delete in normal_stop, which is why we don't reach it > when your Breakpoint.stop method returns False. At the end of, let's > say, handle_signal_stop, could we go over the bpstat chain and delete > any breakpoint we've just hit that is marked for deletion? > > Simon >
Ping for: <https://sourceware.org/pipermail/gdb-patches/2023-January/196068.html>. I would like to ask for some feedback before submitting the v4 patch. Johnson On 1/23/2023 6:15 PM, Johnson Sun wrote: > Hi, > > Thanks for reviewing this! > > > the supplied test does not pass for me > > The current test doesn't seem to produce consistent results across > different > machines, I'll try to fix it in the next version. > > > Is there a reason to do it here in particular, and not, let's say as > > soon as we change the disposition to disp_del_at_next_stop? > > I have implemented this in the v1 patch, I called `disable_breakpoint' as > soon as we change the disposition to `disp_del_at_next_stop' > (in `watchpoint_del_at_next_stop'). However, > LancelotSIX <lsix@lancelotsix.com> mentioned that the fix is in a > non-extension-related code path, and suggested disabling it in > `bpstat_check_breakpoint_conditions' instead (the v3 patch). > See: > https://sourceware.org/pipermail/gdb-patches/2022-September/192120.html > > Both the v1 and v3 patch fixes the issue. I personally prefer the v1 > patch. > (i.e., modifying `watchpoint_del_at_next_stop') > > > shouldn't marking the watchpoint as > > disp_del_at_next_stop make it so it won't be inserted next time we > > resume? After all should_be_inserted returns false for breakpoint > > locations that are disp_del_at_next_stop. Perhaps it's because > since we > > don't do a "normal" stop, breakpoint locations stay inserted, and > > there's nothing that pulls this location out of the target? Therefore, > > maybe a solution, to keep things coherent, would be to pull the > > breakpoint locations out when marking the breakpoint as > > disp_del_at_next_stop? This way, we would respect the invariant that a > > breakpoint disp_del_at_next_stop can't be inserted (I don't know if > this > > is really what is happening though, it's just speculation). > > For software watchpoints, they cannot be pulled out of the target, since > they may not be inserted into the target in the first place: > > /* Locations of type bp_loc_other and > bp_loc_software_watchpoint are only maintained at GDB side, > so there is no need to remove them. */ > > -- gdb/breakpoint.c:3840 > > Their expressions are re-evaluated by single-stepping through the > program: > > Software watchpoints are very slow, since GDB needs to single-step > the program being debugged and test the value of the watched > expression(s) after each instruction. > > -- https://sourceware.org/gdb/wiki/Internals%20Watchpoints > > Therefore, we must either disable or delete the software watchpoint. > We cannot pull it out of the target since it's only maintained on the > GDB side. > > > And, in a broader scope, why wait until the next normal stop to delete > > the watchpoint? A "normal" stop being a stop that we present to the > > user (the normal_stop function). We currently call > > breakpoint_auto_delete in normal_stop, which is why we don't reach it > > when your Breakpoint.stop method returns False. At the end of, let's > > say, handle_signal_stop, could we go over the bpstat chain and delete > > any breakpoint we've just hit that is marked for deletion? > > I believe this choice is due to performance issues. > > In an early attempt, I tried to call `breakpoint_auto_delete' on all > kinds > of stops. However, this implementation is very slow when we have a large > number of breakpoints, since we need to go over the entire bpstat > chain on > all stops. I recall that this implementation fails on certain > testcases with > a large number of breakpoints with many breakpoint stops. > > Furthermore, the reason for using the `disp_del_at_next_stop' state > remains > unclear, as mentioned in the comments: > > /* NOTE drow/2003-09-08: This state only exists for removing > watchpoints. It's not clear that it's necessary... */ > > -- gdb/breakpoint.c:2914 > > By tracing the git history, the `disp_del_at_next_stop' state is > introduced > in commit c906108c21474dfb4ed285bcc0ac6fe02cd400cc, which doesn't provide > any proper explanation of this state. > > I think the best way to deal with this is to avoid going over the entire > bpstat chain when deleting breakpoints. Potentially by keeping track of > a chain of breakpoints that should be deleted, and changing the bpstat > chain > to a doubly linked list for the ease of deletion. Such changes deserve a > dedicated patch, though. > > To sum up, I prefer modifying `watchpoint_del_at_next_stop' (i.e., the > v1 patch). > If you also think it's appropriate, I'll fix the failing test and > prepare the > v4 patch accordingly. > > Johnson > > On 1/13/2023 11:40 PM, SimonMarchi wrote: >> >> On 10/20/22 14:11, Johnson Sun via Gdb-patches wrote: >>> 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) >> I have a bit of trouble reviewing this, because I'm not too familiar >> with the lifecycle of watchpoints (I know the principle, but not the >> specifically where things happen in GDB). So it's difficult to tell >> whether this is right or if there's a better way to handle it. >> >> However, the supplied test does not pass for me: >> >> source >> /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.python/py-watchpoint/py-watchpoint.py >> Watchpoint 2: i >> Python script imported >> (gdb) PASS: gdb.python/py-watchpoint.exp: import python scripts >> python print(len(gdb.breakpoints())) >> 2 >> (gdb) PASS: gdb.python/py-watchpoint.exp: check modified BP count >> gdb_expect_list pattern: /Watchpoint Hit: ./ >> continue >> Continuing. >> Watchpoint Hit: 1 >> gdb_expect_list pattern: /[ >> ]+Watchpoint . deleted because the program has left the block in/ >> FAIL: gdb.python/py-watchpoint.exp: run until program stops >> (pattern 2) (timeout) >> gdb_expect_list pattern: /[ >> ]+which its expression is valid./ >> gdb_expect_list pattern: /Watchpoint Hit: ./ >> gdb_expect_list pattern: /[ >> ]+012\[Inferior 1 \(process .*\) exited normally\]/ >> gdb_expect_list pattern: // >> python print(len(gdb.breakpoints())) >> Watchpoint Hit: 2 >> Watchpoint Hit: 3 >> Watchpoint Hit: 4 >> >> Watchpoint 2 deleted because the program has left the block in >> which its expression is valid. >> Watchpoint Hit: 5 >> 012[Inferior 1 (process 2381681) exited normally] >> (gdb) FAIL: gdb.python/py-watchpoint.exp: check BP count (the >> program exited) >> >>> 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); >> Is there a reason to do it here in particular, and not, let's say as >> soon as we change the disposition to disp_del_at_next_stop? >> >> Other question: shouldn't marking the watchpoint as >> disp_del_at_next_stop make it so it won't be inserted next time we >> resume? After all should_be_inserted returns false for breakpoint >> locations that are disp_del_at_next_stop. Perhaps it's because since we >> don't do a "normal" stop, breakpoint locations stay inserted, and >> there's nothing that pulls this location out of the target? Therefore, >> maybe a solution, to keep things coherent, would be to pull the >> breakpoint locations out when marking the breakpoint as >> disp_del_at_next_stop? This way, we would respect the invariant that a >> breakpoint disp_del_at_next_stop can't be inserted (I don't know if this >> is really what is happening though, it's just speculation). >> >> And, in a broader scope, why wait until the next normal stop to delete >> the watchpoint? A "normal" stop being a stop that we present to the >> user (the normal_stop function). We currently call >> breakpoint_auto_delete in normal_stop, which is why we don't reach it >> when your Breakpoint.stop method returns False. At the end of, let's >> say, handle_signal_stop, could we go over the bpstat chain and delete >> any breakpoint we've just hit that is marked for deletion? >> >> Simon >>
Request for comments: <https://sourceware.org/pipermail/gdb-patches/2023-January/196068.html>. Johnson On 2/19/2023 12:26 AM, JohnsonSun wrote: > Ping for: > <https://sourceware.org/pipermail/gdb-patches/2023-January/196068.html>. > > I would like to ask for some feedback before submitting the v4 patch. > > Johnson > > On 1/23/2023 6:15 PM, Johnson Sun wrote: >> Hi, >> >> Thanks for reviewing this! >> >> > the supplied test does not pass for me >> >> The current test doesn't seem to produce consistent results across >> different >> machines, I'll try to fix it in the next version. >> >> > Is there a reason to do it here in particular, and not, let's say as >> > soon as we change the disposition to disp_del_at_next_stop? >> >> I have implemented this in the v1 patch, I called >> `disable_breakpoint' as >> soon as we change the disposition to `disp_del_at_next_stop' >> (in `watchpoint_del_at_next_stop'). However, >> LancelotSIX <lsix@lancelotsix.com> mentioned that the fix is in a >> non-extension-related code path, and suggested disabling it in >> `bpstat_check_breakpoint_conditions' instead (the v3 patch). >> See: >> https://sourceware.org/pipermail/gdb-patches/2022-September/192120.html >> >> Both the v1 and v3 patch fixes the issue. I personally prefer the v1 >> patch. >> (i.e., modifying `watchpoint_del_at_next_stop') >> >> > shouldn't marking the watchpoint as >> > disp_del_at_next_stop make it so it won't be inserted next time we >> > resume? After all should_be_inserted returns false for breakpoint >> > locations that are disp_del_at_next_stop. Perhaps it's because >> since we >> > don't do a "normal" stop, breakpoint locations stay inserted, and >> > there's nothing that pulls this location out of the target? Therefore, >> > maybe a solution, to keep things coherent, would be to pull the >> > breakpoint locations out when marking the breakpoint as >> > disp_del_at_next_stop? This way, we would respect the invariant >> that a >> > breakpoint disp_del_at_next_stop can't be inserted (I don't know if >> this >> > is really what is happening though, it's just speculation). >> >> For software watchpoints, they cannot be pulled out of the target, since >> they may not be inserted into the target in the first place: >> >> /* Locations of type bp_loc_other and >> bp_loc_software_watchpoint are only maintained at GDB side, >> so there is no need to remove them. */ >> >> -- gdb/breakpoint.c:3840 >> >> Their expressions are re-evaluated by single-stepping through the >> program: >> >> Software watchpoints are very slow, since GDB needs to single-step >> the program being debugged and test the value of the watched >> expression(s) after each instruction. >> >> -- https://sourceware.org/gdb/wiki/Internals%20Watchpoints >> >> Therefore, we must either disable or delete the software watchpoint. >> We cannot pull it out of the target since it's only maintained on the >> GDB side. >> >> > And, in a broader scope, why wait until the next normal stop to delete >> > the watchpoint? A "normal" stop being a stop that we present to the >> > user (the normal_stop function). We currently call >> > breakpoint_auto_delete in normal_stop, which is why we don't reach it >> > when your Breakpoint.stop method returns False. At the end of, let's >> > say, handle_signal_stop, could we go over the bpstat chain and delete >> > any breakpoint we've just hit that is marked for deletion? >> >> I believe this choice is due to performance issues. >> >> In an early attempt, I tried to call `breakpoint_auto_delete' on all >> kinds >> of stops. However, this implementation is very slow when we have a large >> number of breakpoints, since we need to go over the entire bpstat >> chain on >> all stops. I recall that this implementation fails on certain >> testcases with >> a large number of breakpoints with many breakpoint stops. >> >> Furthermore, the reason for using the `disp_del_at_next_stop' state >> remains >> unclear, as mentioned in the comments: >> >> /* NOTE drow/2003-09-08: This state only exists for removing >> watchpoints. It's not clear that it's necessary... */ >> >> -- gdb/breakpoint.c:2914 >> >> By tracing the git history, the `disp_del_at_next_stop' state is >> introduced >> in commit c906108c21474dfb4ed285bcc0ac6fe02cd400cc, which doesn't >> provide >> any proper explanation of this state. >> >> I think the best way to deal with this is to avoid going over the entire >> bpstat chain when deleting breakpoints. Potentially by keeping track of >> a chain of breakpoints that should be deleted, and changing the >> bpstat chain >> to a doubly linked list for the ease of deletion. Such changes deserve a >> dedicated patch, though. >> >> To sum up, I prefer modifying `watchpoint_del_at_next_stop' (i.e., >> the v1 patch). >> If you also think it's appropriate, I'll fix the failing test and >> prepare the >> v4 patch accordingly. >> >> Johnson >> >> On 1/13/2023 11:40 PM, SimonMarchi wrote: >>> >>> On 10/20/22 14:11, Johnson Sun via Gdb-patches wrote: >>>> 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) >>> I have a bit of trouble reviewing this, because I'm not too familiar >>> with the lifecycle of watchpoints (I know the principle, but not the >>> specifically where things happen in GDB). So it's difficult to tell >>> whether this is right or if there's a better way to handle it. >>> >>> However, the supplied test does not pass for me: >>> >>> source >>> /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.python/py-watchpoint/py-watchpoint.py >>> Watchpoint 2: i >>> Python script imported >>> (gdb) PASS: gdb.python/py-watchpoint.exp: import python scripts >>> python print(len(gdb.breakpoints())) >>> 2 >>> (gdb) PASS: gdb.python/py-watchpoint.exp: check modified BP count >>> gdb_expect_list pattern: /Watchpoint Hit: ./ >>> continue >>> Continuing. >>> Watchpoint Hit: 1 >>> gdb_expect_list pattern: /[ >>> ]+Watchpoint . deleted because the program has left the block in/ >>> FAIL: gdb.python/py-watchpoint.exp: run until program stops >>> (pattern 2) (timeout) >>> gdb_expect_list pattern: /[ >>> ]+which its expression is valid./ >>> gdb_expect_list pattern: /Watchpoint Hit: ./ >>> gdb_expect_list pattern: /[ >>> ]+012\[Inferior 1 \(process .*\) exited normally\]/ >>> gdb_expect_list pattern: // >>> python print(len(gdb.breakpoints())) >>> Watchpoint Hit: 2 >>> Watchpoint Hit: 3 >>> Watchpoint Hit: 4 >>> >>> Watchpoint 2 deleted because the program has left the block in >>> which its expression is valid. >>> Watchpoint Hit: 5 >>> 012[Inferior 1 (process 2381681) exited normally] >>> (gdb) FAIL: gdb.python/py-watchpoint.exp: check BP count (the >>> program exited) >>> >>>> 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); >>> Is there a reason to do it here in particular, and not, let's say as >>> soon as we change the disposition to disp_del_at_next_stop? >>> >>> Other question: shouldn't marking the watchpoint as >>> disp_del_at_next_stop make it so it won't be inserted next time we >>> resume? After all should_be_inserted returns false for breakpoint >>> locations that are disp_del_at_next_stop. Perhaps it's because >>> since we >>> don't do a "normal" stop, breakpoint locations stay inserted, and >>> there's nothing that pulls this location out of the target? Therefore, >>> maybe a solution, to keep things coherent, would be to pull the >>> breakpoint locations out when marking the breakpoint as >>> disp_del_at_next_stop? This way, we would respect the invariant that a >>> breakpoint disp_del_at_next_stop can't be inserted (I don't know if >>> this >>> is really what is happening though, it's just speculation). >>> >>> And, in a broader scope, why wait until the next normal stop to delete >>> the watchpoint? A "normal" stop being a stop that we present to the >>> user (the normal_stop function). We currently call >>> breakpoint_auto_delete in normal_stop, which is why we don't reach it >>> when your Breakpoint.stop method returns False. At the end of, let's >>> say, handle_signal_stop, could we go over the bpstat chain and delete >>> any breakpoint we've just hit that is marked for deletion? >>> >>> Simon >>> >
Ping for comments: <https://sourceware.org/pipermail/gdb-patches/2023-January/196068.html>. Johnson On 2/26/2023 2:16 PM, Johnson Sun wrote: > Request for comments: > <https://sourceware.org/pipermail/gdb-patches/2023-January/196068.html>. > > Johnson > > On 2/19/2023 12:26 AM, JohnsonSun wrote: >> Ping for: >> <https://sourceware.org/pipermail/gdb-patches/2023-January/196068.html>. >> >> I would like to ask for some feedback before submitting the v4 patch. >> >> Johnson >> >> On 1/23/2023 6:15 PM, Johnson Sun wrote: >>> Hi, >>> >>> Thanks for reviewing this! >>> >>> > the supplied test does not pass for me >>> >>> The current test doesn't seem to produce consistent results across >>> different >>> machines, I'll try to fix it in the next version. >>> >>> > Is there a reason to do it here in particular, and not, let's say as >>> > soon as we change the disposition to disp_del_at_next_stop? >>> >>> I have implemented this in the v1 patch, I called >>> `disable_breakpoint' as >>> soon as we change the disposition to `disp_del_at_next_stop' >>> (in `watchpoint_del_at_next_stop'). However, >>> LancelotSIX <lsix@lancelotsix.com> mentioned that the fix is in a >>> non-extension-related code path, and suggested disabling it in >>> `bpstat_check_breakpoint_conditions' instead (the v3 patch). >>> See: >>> https://sourceware.org/pipermail/gdb-patches/2022-September/192120.html >>> >>> Both the v1 and v3 patch fixes the issue. I personally prefer the v1 >>> patch. >>> (i.e., modifying `watchpoint_del_at_next_stop') >>> >>> > shouldn't marking the watchpoint as >>> > disp_del_at_next_stop make it so it won't be inserted next time we >>> > resume? After all should_be_inserted returns false for breakpoint >>> > locations that are disp_del_at_next_stop. Perhaps it's because >>> since we >>> > don't do a "normal" stop, breakpoint locations stay inserted, and >>> > there's nothing that pulls this location out of the target? >>> Therefore, >>> > maybe a solution, to keep things coherent, would be to pull the >>> > breakpoint locations out when marking the breakpoint as >>> > disp_del_at_next_stop? This way, we would respect the invariant >>> that a >>> > breakpoint disp_del_at_next_stop can't be inserted (I don't know >>> if this >>> > is really what is happening though, it's just speculation). >>> >>> For software watchpoints, they cannot be pulled out of the target, >>> since >>> they may not be inserted into the target in the first place: >>> >>> /* Locations of type bp_loc_other and >>> bp_loc_software_watchpoint are only maintained at GDB side, >>> so there is no need to remove them. */ >>> >>> -- gdb/breakpoint.c:3840 >>> >>> Their expressions are re-evaluated by single-stepping through the >>> program: >>> >>> Software watchpoints are very slow, since GDB needs to single-step >>> the program being debugged and test the value of the watched >>> expression(s) after each instruction. >>> >>> -- https://sourceware.org/gdb/wiki/Internals%20Watchpoints >>> >>> Therefore, we must either disable or delete the software watchpoint. >>> We cannot pull it out of the target since it's only maintained on the >>> GDB side. >>> >>> > And, in a broader scope, why wait until the next normal stop to >>> delete >>> > the watchpoint? A "normal" stop being a stop that we present to the >>> > user (the normal_stop function). We currently call >>> > breakpoint_auto_delete in normal_stop, which is why we don't reach it >>> > when your Breakpoint.stop method returns False. At the end of, let's >>> > say, handle_signal_stop, could we go over the bpstat chain and delete >>> > any breakpoint we've just hit that is marked for deletion? >>> >>> I believe this choice is due to performance issues. >>> >>> In an early attempt, I tried to call `breakpoint_auto_delete' on all >>> kinds >>> of stops. However, this implementation is very slow when we have a >>> large >>> number of breakpoints, since we need to go over the entire bpstat >>> chain on >>> all stops. I recall that this implementation fails on certain >>> testcases with >>> a large number of breakpoints with many breakpoint stops. >>> >>> Furthermore, the reason for using the `disp_del_at_next_stop' state >>> remains >>> unclear, as mentioned in the comments: >>> >>> /* NOTE drow/2003-09-08: This state only exists for removing >>> watchpoints. It's not clear that it's necessary... */ >>> >>> -- gdb/breakpoint.c:2914 >>> >>> By tracing the git history, the `disp_del_at_next_stop' state is >>> introduced >>> in commit c906108c21474dfb4ed285bcc0ac6fe02cd400cc, which doesn't >>> provide >>> any proper explanation of this state. >>> >>> I think the best way to deal with this is to avoid going over the >>> entire >>> bpstat chain when deleting breakpoints. Potentially by keeping track of >>> a chain of breakpoints that should be deleted, and changing the >>> bpstat chain >>> to a doubly linked list for the ease of deletion. Such changes >>> deserve a >>> dedicated patch, though. >>> >>> To sum up, I prefer modifying `watchpoint_del_at_next_stop' (i.e., >>> the v1 patch). >>> If you also think it's appropriate, I'll fix the failing test and >>> prepare the >>> v4 patch accordingly. >>> >>> Johnson >>> >>> On 1/13/2023 11:40 PM, SimonMarchi wrote: >>>> >>>> On 10/20/22 14:11, Johnson Sun via Gdb-patches wrote: >>>>> 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) >>>> I have a bit of trouble reviewing this, because I'm not too familiar >>>> with the lifecycle of watchpoints (I know the principle, but not the >>>> specifically where things happen in GDB). So it's difficult to tell >>>> whether this is right or if there's a better way to handle it. >>>> >>>> However, the supplied test does not pass for me: >>>> >>>> source >>>> /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.python/py-watchpoint/py-watchpoint.py >>>> Watchpoint 2: i >>>> Python script imported >>>> (gdb) PASS: gdb.python/py-watchpoint.exp: import python scripts >>>> python print(len(gdb.breakpoints())) >>>> 2 >>>> (gdb) PASS: gdb.python/py-watchpoint.exp: check modified BP count >>>> gdb_expect_list pattern: /Watchpoint Hit: ./ >>>> continue >>>> Continuing. >>>> Watchpoint Hit: 1 >>>> gdb_expect_list pattern: /[ >>>> ]+Watchpoint . deleted because the program has left the block in/ >>>> FAIL: gdb.python/py-watchpoint.exp: run until program stops >>>> (pattern 2) (timeout) >>>> gdb_expect_list pattern: /[ >>>> ]+which its expression is valid./ >>>> gdb_expect_list pattern: /Watchpoint Hit: ./ >>>> gdb_expect_list pattern: /[ >>>> ]+012\[Inferior 1 \(process .*\) exited normally\]/ >>>> gdb_expect_list pattern: // >>>> python print(len(gdb.breakpoints())) >>>> Watchpoint Hit: 2 >>>> Watchpoint Hit: 3 >>>> Watchpoint Hit: 4 >>>> >>>> Watchpoint 2 deleted because the program has left the block in >>>> which its expression is valid. >>>> Watchpoint Hit: 5 >>>> 012[Inferior 1 (process 2381681) exited normally] >>>> (gdb) FAIL: gdb.python/py-watchpoint.exp: check BP count (the >>>> program exited) >>>> >>>>> 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); >>>> Is there a reason to do it here in particular, and not, let's say as >>>> soon as we change the disposition to disp_del_at_next_stop? >>>> >>>> Other question: shouldn't marking the watchpoint as >>>> disp_del_at_next_stop make it so it won't be inserted next time we >>>> resume? After all should_be_inserted returns false for breakpoint >>>> locations that are disp_del_at_next_stop. Perhaps it's because >>>> since we >>>> don't do a "normal" stop, breakpoint locations stay inserted, and >>>> there's nothing that pulls this location out of the target? Therefore, >>>> maybe a solution, to keep things coherent, would be to pull the >>>> breakpoint locations out when marking the breakpoint as >>>> disp_del_at_next_stop? This way, we would respect the invariant >>>> that a >>>> breakpoint disp_del_at_next_stop can't be inserted (I don't know if >>>> this >>>> is really what is happening though, it's just speculation). >>>> >>>> And, in a broader scope, why wait until the next normal stop to delete >>>> the watchpoint? A "normal" stop being a stop that we present to the >>>> user (the normal_stop function). We currently call >>>> breakpoint_auto_delete in normal_stop, which is why we don't reach it >>>> when your Breakpoint.stop method returns False. At the end of, let's >>>> say, handle_signal_stop, could we go over the bpstat chain and delete >>>> any breakpoint we've just hit that is marked for deletion? >>>> >>>> Simon >>>> >>
On 1/23/23 05:15, Johnson Sun via Gdb-patches wrote: > Hi, > > Thanks for reviewing this! Sorry for the delay. > >> the supplied test does not pass for me > > The current test doesn't seem to produce consistent results across different > machines, I'll try to fix it in the next version. > >> Is there a reason to do it here in particular, and not, let's say as >> soon as we change the disposition to disp_del_at_next_stop? > > I have implemented this in the v1 patch, I called `disable_breakpoint' as > soon as we change the disposition to `disp_del_at_next_stop' > (in `watchpoint_del_at_next_stop'). However, > LancelotSIX <lsix@lancelotsix.com> mentioned that the fix is in a > non-extension-related code path, and suggested disabling it in > `bpstat_check_breakpoint_conditions' instead (the v3 patch). > See: https://sourceware.org/pipermail/gdb-patches/2022-September/192120.html Ah, sorry for missing that. I now see that you also mentionned it in your commit message. > Both the v1 and v3 patch fixes the issue. I personally prefer the v1 patch. > (i.e., modifying `watchpoint_del_at_next_stop') > >> shouldn't marking the watchpoint as >> disp_del_at_next_stop make it so it won't be inserted next time we >> resume? After all should_be_inserted returns false for breakpoint >> locations that are disp_del_at_next_stop. Perhaps it's because since we >> don't do a "normal" stop, breakpoint locations stay inserted, and >> there's nothing that pulls this location out of the target? Therefore, >> maybe a solution, to keep things coherent, would be to pull the >> breakpoint locations out when marking the breakpoint as >> disp_del_at_next_stop? This way, we would respect the invariant that a >> breakpoint disp_del_at_next_stop can't be inserted (I don't know if this >> is really what is happening though, it's just speculation). > > For software watchpoints, they cannot be pulled out of the target, since > they may not be inserted into the target in the first place: > > /* Locations of type bp_loc_other and > bp_loc_software_watchpoint are only maintained at GDB side, > so there is no need to remove them. */ > > -- gdb/breakpoint.c:3840 > > Their expressions are re-evaluated by single-stepping through the program: > > Software watchpoints are very slow, since GDB needs to single-step > the program being debugged and test the value of the watched > expression(s) after each instruction. > > -- https://sourceware.org/gdb/wiki/Internals%20Watchpoints > > Therefore, we must either disable or delete the software watchpoint. > We cannot pull it out of the target since it's only maintained on the > GDB side. Ah ok, I had not understood that we are talking about software watchpoints. > >> And, in a broader scope, why wait until the next normal stop to delete >> the watchpoint? A "normal" stop being a stop that we present to the >> user (the normal_stop function). We currently call >> breakpoint_auto_delete in normal_stop, which is why we don't reach it >> when your Breakpoint.stop method returns False. At the end of, let's >> say, handle_signal_stop, could we go over the bpstat chain and delete >> any breakpoint we've just hit that is marked for deletion? > > I believe this choice is due to performance issues. > > In an early attempt, I tried to call `breakpoint_auto_delete' on all kinds > of stops. However, this implementation is very slow when we have a large > number of breakpoints, since we need to go over the entire bpstat chain on > all stops. I recall that this implementation fails on certain testcases with > a large number of breakpoints with many breakpoint stops. There's a difference between going over all breakpoints, and going over the bpstat chain. Unecessarily going over all breakpoints is not a good idea, indeed. But the bpstat chain only contains elements for breakpoints that were hit right now. In a pathological case, you could have a lot of elements in that chain, but I don't think it's the norm. The typical case is to have just one element, when hitting a single breakpoint. breakpoint_auto_delete does both. I was thinking that just looking at the bpstat chain, let's say at the end of handle_inferior_event, could be done for cheap. But in any case, that's a larger change, I think we should focus on your fix, and if somebody feels like going it, this can be another change later. > Furthermore, the reason for using the `disp_del_at_next_stop' state remains > unclear, as mentioned in the comments: > > /* NOTE drow/2003-09-08: This state only exists for removing > watchpoints. It's not clear that it's necessary... */ > > -- gdb/breakpoint.c:2914 > > By tracing the git history, the `disp_del_at_next_stop' state is introduced > in commit c906108c21474dfb4ed285bcc0ac6fe02cd400cc, which doesn't provide > any proper explanation of this state. > > I think the best way to deal with this is to avoid going over the entire > bpstat chain when deleting breakpoints. Potentially by keeping track of > a chain of breakpoints that should be deleted, and changing the bpstat chain > to a doubly linked list for the ease of deletion. Such changes deserve a > dedicated patch, though. Maybe the bpstat chain could use intrusive_list: https://gitlab.com/gnutools/binutils-gdb/-/blob/master/gdbsupport/intrusive_list.h > To sum up, I prefer modifying `watchpoint_del_at_next_stop' (i.e., the v1 patch). > If you also think it's appropriate, I'll fix the failing test and prepare the > v4 patch accordingly. I think I prefer the v1 too, it puts the action of disabling the watchpoint closer to where we decide to disable it, so it's easier to follow. I don't think it's a problem that the fix touches a non-extension code path. The change in v1 sounds logical to me: the intent of watchpoint_del_at_next_stop is to get rid of the watchpoint, make it so the user can't hit it anymore. It's just that for some reason (unknown to me at this point), we can't delete it right away. If disabling it is needed to ensure the watchpoint is not seen by the user in some circumstances, then so be it. I guess another solution would be to make watchpoint_check check b->disposition, and skip it if it's disp_del_at_next_stop. I don't think it's any better though. Simon
Hi Simon, Thanks for reviewing this! I revised the patch and will send version 4 shortly. Cheers, Johnson
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")