From patchwork Thu Mar 21 16:43:31 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 87457 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 62778385828C for ; Thu, 21 Mar 2024 16:44:18 +0000 (GMT) X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from mail-wm1-f54.google.com (mail-wm1-f54.google.com [209.85.128.54]) by sourceware.org (Postfix) with ESMTPS id BF53C3858419 for ; Thu, 21 Mar 2024 16:43:35 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org BF53C3858419 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=palves.net Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org BF53C3858419 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=209.85.128.54 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1711039419; cv=none; b=nrwo+AUb2eHPEvsmCnVM5t20/ZhRAfYcczMfZf4YDp8eWkMHEDle7VirX/gyxe7+rRuNf6JPYlfPNlFTsgUSx+bjOGrywv92u4KMunMX46seg553JSi8mDVa/xWuERTHgDOj9AHo8ITY1ZUj2YF2S45Is6+HXQjIkMt1DJg8WdQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1711039419; c=relaxed/simple; bh=vu0lI/PvEDbkfnFjl272CLLv5H19d78hfGgvqKG0iMk=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=RxdU1Tdx+5l75ll30XVUoBpm0sftSG0mB6aBCs+8AXp2ZM+ILdZ7MVNLUJhuWA1I0d7cfdXpclILAgYaPTx/mxK77iAf+E2eQ60JNxWnckJ/lvXM4QGDhcuipAyieNIPQiFt0guY4/iFuq1rSnRi5BVcLCivrIf01T1lecGojeU= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-wm1-f54.google.com with SMTP id 5b1f17b1804b1-414689ba80eso9800235e9.2 for ; Thu, 21 Mar 2024 09:43:35 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711039414; x=1711644214; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=nioGJusm1NPzH62aS9/UFIM98SXDY4Glhu5kfV3rx60=; b=etQ9KKrHnkXWQv8mUiX7eKAkXMmJXdYQzHvsWfZppzPjq8AeOjjkLwcWjr3J9PzfpF WZOatAlYplnkPjE1M8t7sPjEjVNpah6Yce3gfGGz9VZtQPfZtu5nD9Ll+w0V8eE8IS3m R6PpN8XIVJ9LGpPEhTXplaV3uTMZZTyYK1hzmPVjSxylP6uB4GnSFH1PiRHFDR5NRo8+ nlfaXR8Y8ESCJcqHSlDZ1hYEV4sGFCNR9xOXO2uwidntQ741Wh1WV14hOI9uaL/aY9+t qawrwRocn3mB5WSWiRfC4cenz9kz6ZC6iVNRksgWgIX2RIuhUZ4jO6WNNkw2ia9oTqSd QFAg== X-Gm-Message-State: AOJu0YzkYq679fDpDjzeXsgQ6oA3QP7ntoLvQezDyMtssutwbcUKp8Pt occN9if/CVDgxtCww8Rj4cAUItRU216bqeVFe8Y/w+5TfUWe0s0E1jLX9TZK X-Google-Smtp-Source: AGHT+IG4ltForyNcAJaUQ6vaP/slAXAB/JMJMVNpQawmf791yCOiAQQIYL92XY5GV6H+zGpK3/jbAw== X-Received: by 2002:a05:600c:5127:b0:414:6571:a941 with SMTP id o39-20020a05600c512700b004146571a941mr4570310wms.28.1711039413724; Thu, 21 Mar 2024 09:43:33 -0700 (PDT) Received: from localhost ([2001:8a0:f918:ab00:5ea7:1bb:7941:5784]) by smtp.gmail.com with UTF8SMTPSA id bd6-20020a05600c1f0600b0041330d49604sm6153330wmb.45.2024.03.21.09.43.33 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 21 Mar 2024 09:43:33 -0700 (PDT) From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH] Fix setting watchpoints when current thread is running (PR gdb/31521) Date: Thu, 21 Mar 2024 16:43:31 +0000 Message-ID: <20240321164331.967577-1-pedro@palves.net> X-Mailer: git-send-email 2.43.2 MIME-Version: 1.0 X-Spam-Status: No, score=-9.6 required=5.0 tests=BAYES_00, FREEMAIL_FORGED_FROMDOMAIN, FREEMAIL_FROM, GIT_PATCH_0, HEADER_FROM_DIFFERENT_DOMAINS, KAM_DMARC_STATUS, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, 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.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 Currently, when the current thread is running, you can print global variables. However, if you try to set a watchpoint on the same globals, GDB errors out, complaining that the selected thread is running. Like so: (gdb) c& Continuing. (gdb) p global $1 = 1098377287 (gdb) watch global Selected thread is running. This patch makes setting the watchpoint work. The problem is that update_watchpoint calls get_selected_frame unconditionally. We can skip it if the watchpoint expression is only watching globals. You'll now get: (gdb) c& Continuing. (gdb) [New Thread 0x7ffff7d6e640 (LWP 434993)] [New Thread 0x7ffff756d640 (LWP 434994)] p global $1 = 88168 (gdb) watch global Hardware watchpoint 2: global (gdb) [Switching to Thread 0x7ffff7d6e640 (LWP 434993)] Thread 2 "function0" hit Hardware watchpoint 2: global Old value = 185420 New value = 185423 int_return () at threads.c:39 39 } This adds a testcase that exercises both all-stop and non-stop, and also software and hardware watchpoints. It is kfailed for software watchpoints, as those require another fix not handled by this patch (the sw watchpoint doesn't fire because GDB doesn't force the running-free thread to switch to single-stepping). New NEWS blurb added. I don't think we need to change anything in the manual. https://sourceware.org/bugzilla/show_bug.cgi?id=31521 Change-Id: I68ca948541aea3edd4f70741f272f543187abe40 Reviewed-By: Eli Zaretskii --- gdb/NEWS | 5 + gdb/breakpoint.c | 11 +- gdb/testsuite/gdb.base/watchpoint-running.c | 44 ++++++ gdb/testsuite/gdb.base/watchpoint-running.exp | 137 ++++++++++++++++++ 4 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 gdb/testsuite/gdb.base/watchpoint-running.c create mode 100644 gdb/testsuite/gdb.base/watchpoint-running.exp base-commit: 9bec569fda7c76849cf3eb0e4a525f627d25f980 diff --git a/gdb/NEWS b/gdb/NEWS index d8ac0bb06a7..d3df0f96f58 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -28,6 +28,11 @@ maintenance info line-table if the line is considered the start of the epilgoue, and thus a point at which the frame can be considered destroyed. +watch +awatch +rwatch + GDB now lets you set a watchpoint even if the selected thread is running. + * New commands info missing-debug-handler diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 102bd7fad41..44418956c69 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -2154,7 +2154,7 @@ update_watchpoint (struct watchpoint *b, bool reparse) { std::vector val_chain; struct value *v, *result; - struct program_space *frame_pspace; + struct program_space *wp_pspace; fetch_subexp_value (b->exp.get (), b->exp->op.get (), &v, &result, &val_chain, false); @@ -2173,7 +2173,10 @@ update_watchpoint (struct watchpoint *b, bool reparse) b->val_valid = true; } - frame_pspace = get_frame_program_space (get_selected_frame (NULL)); + if (b->exp_valid_block == nullptr) + wp_pspace = current_program_space; + else + wp_pspace = get_frame_program_space (get_selected_frame (NULL)); /* Look at each value on the value chain. */ gdb_assert (!val_chain.empty ()); @@ -2233,7 +2236,7 @@ update_watchpoint (struct watchpoint *b, bool reparse) bp_location *loc = b->allocate_location (); loc->gdbarch = v->type ()->arch (); - loc->pspace = frame_pspace; + loc->pspace = wp_pspace; loc->address = gdbarch_remove_non_address_bits (loc->gdbarch, addr); b->add_location (*loc); @@ -2358,7 +2361,7 @@ update_watchpoint (struct watchpoint *b, bool reparse) bpstat_stop_status requires a location to be able to report stops, so make sure there's at least a dummy one. */ if (b->type == bp_watchpoint && !b->has_locations ()) - add_dummy_location (b, frame_pspace); + add_dummy_location (b, wp_pspace); } else if (!within_current_scope) { diff --git a/gdb/testsuite/gdb.base/watchpoint-running.c b/gdb/testsuite/gdb.base/watchpoint-running.c new file mode 100644 index 00000000000..7e6dc5f605b --- /dev/null +++ b/gdb/testsuite/gdb.base/watchpoint-running.c @@ -0,0 +1,44 @@ +/* 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 + +uint64_t global_var; + +/* How long to usleep, in us. The exp file assumes this is lower than + 1s. */ +#define MS_USLEEP 100000 + +/* How many increments per second MS_USLEEP corresponds to. */ +#define INCREMENTS_PER_SEC (1000000 / MS_USLEEP) + +int +main () +{ + while (1) + { + usleep (MS_USLEEP); + global_var++; + + /* Time out after 30s. */ + if (global_var >= (30 * INCREMENTS_PER_SEC)) + return 1; + } + + return 0; +} diff --git a/gdb/testsuite/gdb.base/watchpoint-running.exp b/gdb/testsuite/gdb.base/watchpoint-running.exp new file mode 100644 index 00000000000..bf5f563e737 --- /dev/null +++ b/gdb/testsuite/gdb.base/watchpoint-running.exp @@ -0,0 +1,137 @@ +# 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 . + +# This test verifies that you can set a watchpoint that watches global +# memory, when the selected thread is running. + +set allow_hw_watchpoint_tests_p [allow_hw_watchpoint_tests] + +standard_testfile + +if [build_executable "failed to prepare" $testfile $srcfile {debug}] { + return -1 +} + +# STOP_MODE is either "all-stop" or "non-stop". HW is true if we are +# testing hardware watchpoints, and false if we're testing software +# watchpoints. + +proc test {stop_mode hw} { + + save_vars { ::GDBFLAGS } { + if { $stop_mode == "non-stop" } { + append ::GDBFLAGS " -ex \"set non-stop on\"" + } elseif {[target_info gdb_protocol] == "remote" + || [target_info gdb_protocol]== "extended-remote"} { + # Communicating with the target while the inferior is + # running depends on target running in non-stop mode. + # Force it on for remote targets, until this is the + # default. + append ::GDBFLAGS " -ex \"maint set target-non-stop on\"" + } + clean_restart $::binfile + } + + gdb_test_no_output "set can-use-hw-watchpoints $hw" + + if {![runto_main]} { + return + } + + delete_breakpoints + + # Continue the program in the background. + set test "continue&" + gdb_test_multiple "continue&" $test { + -re "Continuing\\.\r\n$::gdb_prompt " { + pass $test + } + } + + set val1 "" + gdb_test_multiple "print global_var" "global_var once" { + -re -wrap " = ($::decimal)" { + set val1 $expect_out(1,string) + pass "$gdb_test_name" + } + } + + # Sleep for a bit, so the variable is sure to be incremented, if + # indeed we managed to get the target running. + sleep 1 + + set val2 "" + gdb_test_multiple "print global_var" "global_var twice" { + -re -wrap " = ($::decimal)" { + set val2 $expect_out(1,string) + pass "$gdb_test_name" + } + } + + # The variable should have incremented. (Note we didn't give it + # sufficient time to ever wrap around.) + gdb_assert {$val1 != $val2} "values are different" + + set wp_str [expr ($hw)?"Hardware watchpoint":"Watchpoint"] + + # Now set a watchpoint, while the inferior is running. Since + # we're watching a global, and we can read global memory while the + # target is running, this should be able to work. + gdb_test_multiple "watch global_var" "" { + -re "$wp_str $::decimal: global_var\r\n$::gdb_prompt " { + pass $gdb_test_name + } + } + + # Check that the watchpoint triggers. + + save_vars ::timeout { + if {!$hw} { + # This doesn't currently work with software watchpoints. + # GDB should transparently temporarily pause the inferior, + # to force it to single step, but it doesn't, so the + # thread continues running free. + setup_kfail gdb/31521 *-*-* + + # No point waiting too much for output we know isn't + # coming. + set ::timeout 1 + } + set re [multi_line \ + "$wp_str $::decimal: global_var" \ + "" \ + "Old value = $::decimal" \ + "New value = $::decimal"] + gdb_test_multiple "" "watchpoint hit" { + -re $re { + pass $gdb_test_name + } + } + } +} + +foreach hw {0 1} { + if {$hw && !$allow_hw_watchpoint_tests_p} { + continue + } + foreach stop_mode {all-stop non-stop} { + set wp_type [expr ($hw)?"hardware":"software"] + with_test_prefix "$stop_mode: $wp_type" { + test $stop_mode $hw + } + } +}