From patchwork Mon Dec 9 11:17:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 102661 Return-Path: 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 0ED5D3858416 for ; Mon, 9 Dec 2024 11:20:46 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0ED5D3858416 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=dTD+DUT2 X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id ECE61385841F for ; Mon, 9 Dec 2024 11:18:14 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org ECE61385841F Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org ECE61385841F Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1733743095; cv=none; b=JCgNRWy7XuTLH8DeXbz37Kv/ot5XFB0HLlah9H1SYsYYI8ilU7Bzy9v+LGchcbvLgnc9KNtk40pVJVrJ/0jAJ9nzrCRAOOgN5WLaBkooh5g36sq2P9Oapf/3hNq4DjAuZr88u4Mf0MTveta5Z/6tPjStNizp9YMX/UfEmVeXlIM= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1733743095; c=relaxed/simple; bh=8tL919slJniD0tN2/QrO8ChBCGxm/Eu4iWpShO5xdDs=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=mX6hxAaWEqAxpvHuglAGIiBX3bl4YPAGW2rLh6gxWjsXleP+BsM2mSd39USpwlDV9afD+DyfoaZh0iji+LW3Eo7UlSpaSamZyVfPjhfS335aRJRXG77IUTdnvTQD2MbRG4BEEI9B2+/5zz5t3GFJLlyC9dC85xgtIAwPhHhEdcY= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org ECE61385841F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1733743094; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=RpevVn+wcHw7SMhZvUWufVAjhkSRdpunUYenm/ecDN0=; b=dTD+DUT2pNZVq+0emC2Xi2Ml6MqsLY7o27rMPE3HY4fBXwpXoBSBXqBqRp+WXEZhiTLpIl a2lb1+nQz9BXnxsuHEh/5t5mrJkqa9IUxo3WdbRXTdie+TiVV7JasNE77NVqdP0nZdvT9U DLUN90KlFa9s7o5GOLgPdnlY7iLJ9Y4= Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-623-q0BZsjvNOPKhUTwL9M0XRw-1; Mon, 09 Dec 2024 06:18:13 -0500 X-MC-Unique: q0BZsjvNOPKhUTwL9M0XRw-1 X-Mimecast-MFC-AGG-ID: q0BZsjvNOPKhUTwL9M0XRw Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-434fff37885so60845e9.3 for ; Mon, 09 Dec 2024 03:18:13 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1733743092; x=1734347892; 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=RpevVn+wcHw7SMhZvUWufVAjhkSRdpunUYenm/ecDN0=; b=kFkaJw4bmzuLUbWe9fQyUupMixSTRf7RylhBZIaa5dHgbEzEoGGbe2XHLsCt7mcb/K 1bGXu/+13N/UC5f875Il60CsClpxa3tFDNemL1dfkg7nOYvCrMIOyHgjK3Y3pckoUrqL 73LfVYYuCfea3tYZjpaPedJqm2vG2LhlXmqCUHP2jdT9FBgw8uQZtE5fYCvHxlH43KTg GUaJ0MYYRjte2thX9oGMot+g2wQbG5+MXHoufmGZ3jLClVURb2IRz6w7/Q9czc6/onz+ 8EgvQFZRuF3YA1b5AgHEOBYi456B0AYBEitQSC0e7D0gUy36wqKM2pJnVzw4IwqCMh3g 8ODQ== X-Gm-Message-State: AOJu0Ywo180eyFm89mG8idiJwNTJMTH7zJP4MaQU/QheuwP7HJBCHidF 7KXHjFsb5iXNQI+sGlv4tmp/2S4jUR0/4K8WV1txtqB6vfAYMIUBCJZcZ+u+Nsgl9ImI176tDAn o1cKy6buiV1KmJEwo5DUK8Temd4gp37ES9UQ+EIBbbOXhFtatsKglA+9H/W4FY6P6GoNTNXqx4M V4cdzC1+9oJpWvL8EAvsNkLg6YU1sX0yyezNACoq/pZ3Q= X-Gm-Gg: ASbGncuUjq7FJyKCgh/lL8gjzxDSodfaSN/Serq66J0qMiEWqxj8YbLpwx0rHCNSLxU IODKcUbhda6i/1sRCjfSmUqZPFDCnOWlvnePD+kUrZI0tPWda9bVG9fwOWYt0ejAktpJcmJhE4s zWb3enLkVba1OeytISJEMgS11dhyOEmszuqNntGymxXWlUD7Px7L63NBFhmt/91GuYv0r43B043 GfMfVy3l2nkYhNmTKkaqEjBvurXGPn55yiY1kRyCAmh+b+Jlu5yfOGXgBXmC/KhTKHWMZbF5zK4 Ng== X-Received: by 2002:a7b:cb8d:0:b0:434:f335:855 with SMTP id 5b1f17b1804b1-434f3350d44mr28789955e9.28.1733743091097; Mon, 09 Dec 2024 03:18:11 -0800 (PST) X-Google-Smtp-Source: AGHT+IFDDVz6dNjxlaNHXUELgIvnliJUK89OZqAF4SveXUxbM/TUsMBA9tZdiuBKDkhqdasumXy6kA== X-Received: by 2002:a7b:cb8d:0:b0:434:f335:855 with SMTP id 5b1f17b1804b1-434f3350d44mr28789555e9.28.1733743090324; Mon, 09 Dec 2024 03:18:10 -0800 (PST) Received: from localhost (197.209.200.146.dyn.plus.net. [146.200.209.197]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-434eba653a0sm79134735e9.22.2024.12.09.03.18.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 09 Dec 2024 03:18:09 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Hannes Domani Subject: [PATCHv5 2/7] gdb: fixes for code_breakpoint::disabled_by_cond logic Date: Mon, 9 Dec 2024 11:17:59 +0000 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: cPHpDT9zzEDZze1K9h5ojhhXpl4sM7pmk9yQs3khVik_1733743092 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, 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.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~patchwork=sourceware.org@sourceware.org I spotted that the code_breakpoint::disabled_by_cond flag doesn't work how I'd expect it too. The flag appears to be "sticky" in some situations; once a code_breakpoint::disabled_by_cond flag is marked true, then, in some cases the flag wont automatically become false again, even when you'd think it should. The problem is in update_breakpoint_locations. In this function, which is called as a worker of code_breakpoint::re_set, GDB computes a new set of locations for a breakpoint, the new locations are then installed into the breakpoint. However, before installing the new locations GDB attempts to copy the bp_location::enabled and bp_location::disabled_by_cond flag from the old locations into the new locations. The reason for copying the ::enabled flag makes sense. This flag is controlled by the user. When we create the new locations if GDB can see that a new location is equivalent to one of the old locations, and if the old location was disabled by the user, then the new location should also be disabled. However, I think the logic behind copying the ::disabled_by_cond flag is wrong. The disabled_by_cond flag is controlled by GDB and should toggle automatically. If the condition string can be parsed then the flag should be false (b/p enabled), if the condition string can't be parsed then the flag should be true (b/p disabled). As we always parse the condition string in update_breakpoint_locations before we try to copy the ::enabled flag value then the ::disabled_by_cond flag should already be correct, there's no need to copy over the ::disabled_by_cond value from the old location. As a concrete example, consider a b/p placed within the main executable, but with a condition that depends on a variable within a shared library. When the b/p is initially created the b/p will be disabled as the condition string will be invalid (the shared library variable isn't available yet). When the inferior starts the shared library is loaded and the condition variable becomes available to GDB. When the shared library is loaded breakpoint_re_set is called which (eventually) calls update_breakpoint_locations. A new location is computed for the breakpoint and the condition string is parsed. As the shared library variable is now know the expression parses correctly and ::disabled_by_cond is left false for the new location. But currently GDB spots that the new location is at the same address as the old location and copies disabled_by_cond over from the old location, which marks the b/p location as disabled. This is not what I would expect. The solution is simple, don't copy over disabled_by_cond. While writing a test I found another problem though. The disabled_by_cond flag doesn't get set true when it should! This is the exact opposite of the above. The problem here is in solib_add which is (despite the name) called whenever the shared library set changes, including when a shared library is unloaded. Imagine an executable that uses dlopen/dlclose to load a shared library. Given an example of a b/p in the main executable that has a condition that uses a variable from our shared library, a library which might be unloaded with dlclose. My expectation is that, when the library is unloaded, GDB will automatically mark the breakpoint as disabled_by_cond, however, this was not happening. The problem is that in solib_add we only call breakpoint_re_set when shared libraries are added, not when shared libraries are removed. The solution I think is to just call breakpoint_re_set in both cases, now the disabled_by_cond flag is updated as I'd expect. Unfortunately, making this change causes a regression when running: make check-gdb \ TESTS="gdb.trace/change-loc.exp" \ RUNTESTFLAGS="--target_board=native-gdbserver" This test unloads a shared library and expects breakpoints within the shared library to enter the PENDING state (because the bp_location's shlib_disabled flag will be set). However, the new call to breakpoint_re_set means that this is no longer the case. The breakpoint_re_set call means that update_breakpoint_locations is called, which then checks if all locations for a breakpoint are pending or not. In this test not all locations are pending, and so GDB recalculates the locations of each breakpoint, this means that pending locations are discarded. There is a but report PR gdb/32404 which mentions the problems with shlib_disabled pending breakpoints, and how they are prone to being randomly deleted if the user can cause GDB to trigger a call to breakpoint_re_set. This patch just adds another call to breakpoint_re_set, which triggers this bug in this one test case. For now I have marked this test as KFAIL. I do plan to try and address the pending (shlib_disabled) breakpoint problem in the future, but I'm not sure when that will be right now. There are, of course, tests to cover all these cases. During review I was pointed at bug PR gdb/32079 as something that this commit might fix, or help in fixing. And this commit is part of the fix for that bug, but is not the complete solution. However, the remaining parts of the fix for that bug are not really related to the content of this commit. The problem in PR gdb/32079 is that the inferior maps multiple copies of libc in different linker namespaces using dlmopen (actually libc is loaded as a consequence of loading some other library into a different namespace, but that's just a detail). The user then uses a 'next' command to move the inferior forward. GDB sets up internal breakpoints on the longjmp symbols, of which there are multiple copies (there is a copy in every loaded libc). However, the 'next' command is, in the problem case, stepping over a dlclose call which unloads one of the loaded libc libraries. In current HEAD GDB in solib_add we fail to call breakpoint_re_set() when the library is unloaded; breakpoint_re_set() would delete and then recreate the longjmp breakpoints. As breakpoint_re_set() is not called GDB thinks that the the longjmp breakpoint in the now unloaded libc still exists, and is still inserted. When the inferior stops after the 'next' GDB tries to delete and remove the longjmp breakpoint which fails as the libc in which the breakpoint was inserted is no longer mapped in. When the user tries to 'next' again GDB tries to re-insert the still existing longjmp breakpoint which again fails as the memory in which the b/p should be inserted is no longer part of the inferior memory space. This commit helps a little. Now when the libc library is unmapped GDB does call breakpoint_re_set(). This deletes the longjmp breakpoints including the one in the unmapped library, then, when we try to recreate the longjmp breakpoints (at the end of breakpoint_re_set) we don't create a b/p in the now unmapped copy of libc. However GDB does still think that the deleted breakpoint is inserted. The breakpoint location remains in GDB's data structures until the next time the inferior stops, at which point GDB tries to remove the breakpoint .... and fails. However, as the b/p is now deleted, when the user tries to 'next' GDB no longer tries to re-insert the b/p, and so one of the problems reported in PR gdb/32079 is resolved. I'll fix the remaining issues from PR gdb/32079 in a later commit in this series. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32079 Tested-By: Hannes Domani --- gdb/breakpoint.c | 5 +- gdb/solib.c | 2 +- .../gdb.base/bp-disabled-by-cond-lib.c | 24 ++ gdb/testsuite/gdb.base/bp-disabled-by-cond.c | 64 ++++++ .../gdb.base/bp-disabled-by-cond.exp | 206 ++++++++++++++++++ gdb/testsuite/gdb.base/bp-disabled-by-cond.py | 21 ++ gdb/testsuite/gdb.trace/change-loc.exp | 2 + 7 files changed, 319 insertions(+), 5 deletions(-) create mode 100644 gdb/testsuite/gdb.base/bp-disabled-by-cond-lib.c create mode 100644 gdb/testsuite/gdb.base/bp-disabled-by-cond.c create mode 100644 gdb/testsuite/gdb.base/bp-disabled-by-cond.exp create mode 100644 gdb/testsuite/gdb.base/bp-disabled-by-cond.py diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 8dafb0a0c75..bfff3bd8ee1 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -13017,8 +13017,7 @@ update_breakpoint_locations (code_breakpoint *b, for (const bp_location &e : existing_locations) { - if (e.function_name == nullptr - || (e.enabled && !e.disabled_by_cond)) + if (e.function_name == nullptr || e.enabled) continue; if (have_ambiguous_names) @@ -13035,7 +13034,6 @@ update_breakpoint_locations (code_breakpoint *b, if (breakpoint_locations_match (&e, &l, true)) { l.enabled = e.enabled; - l.disabled_by_cond = e.disabled_by_cond; break; } } @@ -13048,7 +13046,6 @@ update_breakpoint_locations (code_breakpoint *b, l.function_name.get ()) == 0) { l.enabled = e.enabled; - l.disabled_by_cond = e.disabled_by_cond; break; } } diff --git a/gdb/solib.c b/gdb/solib.c index fdefdf0b142..0c63d31f645 100644 --- a/gdb/solib.c +++ b/gdb/solib.c @@ -973,7 +973,7 @@ solib_add (const char *pattern, int from_tty, int readsyms) } } - if (loaded_any_symbols) + if (loaded_any_symbols || !current_program_space->deleted_solibs.empty ()) breakpoint_re_set (); if (from_tty && pattern && !any_matches) diff --git a/gdb/testsuite/gdb.base/bp-disabled-by-cond-lib.c b/gdb/testsuite/gdb.base/bp-disabled-by-cond-lib.c new file mode 100644 index 00000000000..ab0cb9dcb16 --- /dev/null +++ b/gdb/testsuite/gdb.base/bp-disabled-by-cond-lib.c @@ -0,0 +1,24 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024 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 . */ + +volatile int lib_global = 0; + +void +lib_func (void) +{ + /* Nothing. */ +} diff --git a/gdb/testsuite/gdb.base/bp-disabled-by-cond.c b/gdb/testsuite/gdb.base/bp-disabled-by-cond.c new file mode 100644 index 00000000000..cf61c2821df --- /dev/null +++ b/gdb/testsuite/gdb.base/bp-disabled-by-cond.c @@ -0,0 +1,64 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024 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 . */ + +#include +#include + +#ifdef __WIN32__ +#include +#define dlopen(name, mode) LoadLibrary (TEXT (name)) +#ifdef _WIN32_WCE +# define dlsym(handle, func) GetProcAddress (handle, TEXT (func)) +#else +# define dlsym(handle, func) GetProcAddress (handle, func) +#endif +#define dlclose(handle) FreeLibrary (handle) +#else +#include +#endif + +void +breakpt () +{ + /* Nothing. */ +} + +int +main (void) +{ + int res; + void *handle; + void (*func) (void); + + handle = dlopen (SHLIB_NAME, RTLD_LAZY); + assert (handle != NULL); + + func = (void (*)(void)) dlsym (handle, "lib_func"); + assert (func != NULL); + + breakpt (); /* Conditional BP location. */ + breakpt (); /* Other BP location. */ + + func (); + + res = dlclose (handle); + assert (res == 0); + + breakpt (); /* BP before exit. */ + + return 0; +} diff --git a/gdb/testsuite/gdb.base/bp-disabled-by-cond.exp b/gdb/testsuite/gdb.base/bp-disabled-by-cond.exp new file mode 100644 index 00000000000..6104787ed3a --- /dev/null +++ b/gdb/testsuite/gdb.base/bp-disabled-by-cond.exp @@ -0,0 +1,206 @@ +# Copyright 2024 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 . + +# Breakpoint locations can be marked as "disabled due to their +# condition". This test sets up a breakpoint which depends on reading +# a variable in a shared library. We then check that the b/p is +# correctly disabled before the shared library is loaded, becomes +# enabled once the shared library is loaded. And becomes disabled +# again when the shared libraries are unloaded. + +standard_testfile .c -lib.c + +set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] + +# Build the library and copy it to the target. +set libname ${testfile}-lib +set libfile [standard_output_file $libname] +if { [build_executable "build shlib" $libfile $srcfile2 {debug shlib}] == -1} { + return +} +set libfile_on_target [gdb_download_shlib $libfile] + +# Build the executable. +set opts [list debug shlib_load additional_flags=-DSHLIB_NAME=\"${libname}\"] +if { [build_executable "build exec" $binfile $srcfile $opts] == -1} { + return +} + +set other_bp_line [gdb_get_line_number "Other BP location" $srcfile] +set cond_bp_line [gdb_get_line_number "Conditional BP location" $srcfile] +set exit_bp_line [gdb_get_line_number "BP before exit" $srcfile] + +# Setup a conditional b/p, the condition of which depends on reading a +# variable from a shared library. The b/p will initially be created +# disabled (due to the condition). +# +# Continue the inferior, when the shared library is loaded GDB should +# make the b/p enabled. +# +# Restart the inferior, which should unload the shared library, GDB +# should mark the b/p as disabled due to its condition again. +proc run_test { hit_cond } { + clean_restart $::binfile + + if {![runto_main]} { + return + } + + # Setup breakpoints. + gdb_breakpoint $::srcfile:$::other_bp_line + set other_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ + "get number of other b/p"] + + gdb_breakpoint $::srcfile:$::exit_bp_line + set exit_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ + "get number of exit b/p"] + + gdb_breakpoint $::srcfile:$::cond_bp_line + set cond_bp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ + "get number of conditional b/p"] + + if { $hit_cond } { + set lib_global_val 0 + } else { + set lib_global_val 1 + } + + # Set the condition. Use 'force' as we're referencing a variable in + # the shared library, which hasn't been loaded yet. The breakpoint + # will immediately be marked as disabled_by_cond. + gdb_test "condition -force $cond_bp_num lib_global == $lib_global_val" \ + [multi_line \ + "warning: failed to validate condition at location $cond_bp_num\\.1, disabling:" \ + " No symbol \"lib_global\" in current context\\."] \ + "set b/p condition, it will be disabled" + + # Source Python script if supported. + if { [allow_python_tests] } { + gdb_test_no_output "source $::pyfile" "import python scripts" + gdb_test "python print(bp_modified_list)" "\\\[\\\]" \ + "check b/p modified observer has not yet triggered" + } + + # Check the b/p is indeed marked as disabled (based on its condition). + gdb_test "info breakpoint $cond_bp_num" \ + [multi_line \ + "\r\n$cond_bp_num\\.1\\s+N\\*\\s+$::hex in main at \[^\r\n\]+" \ + "\\(\\*\\): Breakpoint condition is invalid at this location\\."] \ + "conditional breakpoint is disabled based on condition" + + if { $hit_cond } { + # Continue the inferior. The shared library is loaded and the + # breakpoint condition should become valid. We should then stop at + # the conditional breakpoint. + gdb_test "continue" \ + [multi_line \ + "Breakpoint $cond_bp_num, main \\(\\) at \[^\r\n\]+:$::cond_bp_line" \ + "$::cond_bp_line\\s+breakpt \\(\\);\\s+/\\* Conditional BP location\\. \\*/"] \ + "continue until conditional b/p is hit" + } else { + # Continue the inferior. The shared library is loaded and the + # breakpoint condition should become valid. As the condition + # is going to be false GDB will stop at the other line. + gdb_test "continue" \ + [multi_line \ + "Breakpoint $other_bp_num, main \\(\\) at \[^\r\n\]+:$::other_bp_line" \ + "$::other_bp_line\\s+breakpt \\(\\);\\s+/\\* Other BP location\\. \\*/"] \ + "continue until conditional b/p is hit" + } + + if { [allow_python_tests] } { + # We're going to look at the list of b/p that have been + # modified since we loaded the Python script. The first b/p + # modified will be the conditional b/p, this occurs when the + # b/p condition became valid. + # + # The second b/p will be whichever b/p we hit (the hit count + # increased). So figure out which b/p we are going to hit. + if { $hit_cond } { + set second_bp_num $cond_bp_num + } else { + set second_bp_num $other_bp_num + } + + # Now check the list of modified b/p. + gdb_test "python print(bp_modified_list)" \ + "\\\[$cond_bp_num, $second_bp_num\\\]" \ + "check b/p modified observer was triggered" + } + + if {[gdb_protocol_is_remote]} { + set evals_re "(?: \\(\[^) \]+ evals\\))?" + } else { + set evals_re "" + } + + # Check the b/p is no longer marked as disabled. The output is + # basically the same here whether the b/p was hit or not. It's + # just the hit counter line that we need to append or not. + set re_list \ + [list \ + "$cond_bp_num\\s+breakpoint\\s+keep\\s+y\\s+$::hex in main at \[^\r\n\]+:$::cond_bp_line" \ + "\\s+stop only if lib_global == $lib_global_val$evals_re"] + if { $hit_cond } { + lappend re_list "\\s+breakpoint already hit 1 time" + } + set re [multi_line {*}$re_list] + gdb_test "info breakpoint $cond_bp_num" $re \ + "conditional breakpoint is now enabled" + + if { $hit_cond } { + gdb_test "continue" \ + [multi_line \ + "Breakpoint $other_bp_num, main \\(\\) at \[^\r\n\]+:$::other_bp_line" \ + "$::other_bp_line\\s+breakpt \\(\\);\\s+/\\* Other BP location\\. \\*/"] \ + "continue to other b/p" + } + + if {[allow_python_tests]} { + # Clear out the list of modified b/p. This makes the results + # (see below) clearer. + gdb_test_no_output "python bp_modified_list=\[\]" \ + "clear bp_modified_list" + } + + gdb_test "continue" \ + [multi_line \ + "Breakpoint $exit_bp_num, main \\(\\) at \[^\r\n\]+:$::exit_bp_line" \ + "$::exit_bp_line\\s+breakpt \\(\\);\\s+/\\* BP before exit\\. \\*/"] \ + "continue b/p before exit" + + # Check the b/p is once again marked as disabled based on its + # condition. + gdb_test "info breakpoint $cond_bp_num" \ + [multi_line \ + "\r\n$cond_bp_num\\.1\\s+N\\*\\s+$::hex in main at \[^\r\n\]+" \ + "\\(\\*\\): Breakpoint condition is invalid at this location\\."] \ + "conditional breakpoint is again disabled based on condition" + + if { [allow_python_tests] } { + # The condition breakpoint will have been modified (moved to + # the disabled state) when GDB unloaded the shared libraries. + # And the b/p in main will have been modified in that it's hit + # count will have gone up. + gdb_test "python print(bp_modified_list)" \ + "\\\[$cond_bp_num, $exit_bp_num\\\]" \ + "check b/p modified observer was triggered during restart" + } +} + +# The tests. +foreach_with_prefix hit_cond { true false } { + run_test $hit_cond +} diff --git a/gdb/testsuite/gdb.base/bp-disabled-by-cond.py b/gdb/testsuite/gdb.base/bp-disabled-by-cond.py new file mode 100644 index 00000000000..87b374c201e --- /dev/null +++ b/gdb/testsuite/gdb.base/bp-disabled-by-cond.py @@ -0,0 +1,21 @@ +# Copyright (C) 2024 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 . + +bp_modified_list = [] + +def bp_modified(bp): + bp_modified_list.append (bp.number) + +gdb.events.breakpoint_modified.connect(bp_modified) diff --git a/gdb/testsuite/gdb.trace/change-loc.exp b/gdb/testsuite/gdb.trace/change-loc.exp index 1316d92b116..a01cb712887 100644 --- a/gdb/testsuite/gdb.trace/change-loc.exp +++ b/gdb/testsuite/gdb.trace/change-loc.exp @@ -149,6 +149,7 @@ proc tracepoint_change_loc_1 { trace_type } { (4\.\[1-3].* in func4.*\tinstalled on target.*){2}" \ "tracepoint with two locations - installed (unload)" + setup_kfail gdb/32404 "*-*-*" gdb_test "info trace" \ "Num Type\[ \]+Disp Enb Address\[ \]+What.* \[0-9\]+\[\t \]+\(|fast \)tracepoint\[ \]+keep y.*\.* @@ -263,6 +264,7 @@ proc tracepoint_change_loc_2 { trace_type } { (1\.\[1-3].* in func4.*\tinstalled on target.*){2}" \ "tracepoint with two locations - installed (unload)" + setup_kfail gdb/32404 "*-*-*" gdb_test "info trace" \ "Num Type\[ \]+Disp Enb Address\[ \]+What.* \[0-9\]+\[\t \]+\(|fast \)tracepoint\[ \]+keep y.*\.*