forked from Imagelibrary/binutils-gdb
gdb/aarch64: restore in-order watchpoint matching
At Red Hat we have an out of tree AArch64 watchpoint test which broke
after this commit:
commit cf16ab724a
Date: Tue Mar 12 17:08:18 2024 +0100
[gdb/tdep] Fix gdb.base/watch-bitfields.exp on aarch64
The problem with AArch64 hardware watchpoints is that they (as I
understand it) are restricted to a minimum of 8 bytes. This means
that, if the thing you are watching is less than 8-bytes, then there
is always scope for invalid watchpoint triggers caused by activity in
the part of the 8-bytes that are not being watched.
Or, as is the case in this RH test, multiple watchpoint are created
within an 8-byte region, and GDB can miss-identify which watchpoint
actually triggered.
Prior to the above commit the RH test was passing. However, the test
was relying on, in the case of ambiguity, GDB selecting the first
created watchpoint. That behaviour changed with the above commit.
Now GDB favours reporting non write breakpoints, and will only report
a write breakpoint if no non-write breakpoint exists in the same
region.
I originally posted a patch to try and tweak the existing logic to
restore enough of the original behaviour that the RH test would pass,
this can be found here (2 iterations):
https://inbox.sourceware.org/gdb-patches/65e746b6394f04faa027e778f733eda95d20f368.1753115072.git.aburgess@redhat.com
https://inbox.sourceware.org/gdb-patches/638cbe9b738c0c529f6370f90ba4a395711f63ae.1753971315.git.aburgess@redhat.com
Neither of these really resolved the problem, they fixed some cases,
but broke others.
Ultimately, the problem on AArch64 is that for a single watchpoint
trap, there could be multiple watchpoints that are potentially
responsible. The existing API defined by the target_ops methods
stopped_by_watchpoint() and stopped_data_address() only allow for two
possible options:
1. If stopped_by_watchpoint() is true then stopped_data_address()
can return true and a single address which identifies all
watchpoints at that single address, or
2. If stopped_by_watchpoint() is true then stopped_data_address()
can return false, in which case GDB will check all write
watchpoints to see if any have changed, if they have, then GDB
tells the user that that was the triggering watchpoint.
If we are in a situation where we have to choose between multiple
write and read watchpoints then the current API doesn't allow the
architecture specific code to tell GDB core about this case.
In this commit I propose that we change the target_ops API,
specifically, the method:
bool target_ops::stopped_data_address (CORE_ADDR *);
will change to:
std::vector<CORE_ADDR> target_ops::stopped_data_addresses ();
The architecture specific code can now return a set of watchpoint
addresses, allowing GDB to identify a set of watchpoints that might
have triggered. GDB core can then select the most likely watchpoint,
and present that to the user.
As with the old API, target_ops::stopped_data_addresses should only be
called when target_ops::stopped_by_watchpoint is true, in which case
it's return values can be interpreted like this:
a. An empty vector; this replaces the old case where false was
returned. GDB should check all the write watchpoints and select
the one that changed as the responsible watchpoint.
b. A single entry vector; all targets except AArch64 currently
return at most a single entry vector. The single address
indicates the watchpoint(s) that triggered.
c. A multi-entry vector; currently AArch64 only. These addresses
indicate the set of watchpoints that might have triggered. GDB
will check the write watchpoints to see which (if any) changed,
and if no write watchpoints changed, GDB will present the first
access watchpoint.
In the future, we might want to improve the handling of (c) so that
GDB tells the user that multiple access watchpoints might have
triggered, and then list all of them. This might clear up some
confusion. But I think that can be done in the future (I don't have
an immediate plan to work on this). I think this change is already a
good improvement.
The changes for this are pretty extensive, but here's a basic summary:
* Within gdb/ changing the API name from stopped_data_address to
stopped_data_addresses throughout. Comments are updated too where
needed.
* For targets other than AArch64, the existing code is retained with
as few changes as possible, we only allow for a single address to
be returned, the address is now wrapped in a vector. Where we
used to return false, we now return the empty vector.
* For AArch64, the return a vector logic is pushed through to
gdb/nat/aarch64-hw-point.{c,h}, and aarch64_stopped_data_address
changes to aarch64_stopped_data_addresses, and is updated to
return a vector of addresses.
* In infrun.c there's some updates to some debug output.
* In breakpoint.c the interesting changes are in
watchpoints_triggered. The existing code has three cases to
handle:
(i) target_stopped_by_watchpoint returns false. This case is
unchanged.
(ii) target_stopped_data_address returns false. This case is now
calling target_stopped_data_addresses, and checks for the
empty vector, but otherwise is unchanged.
(iii) target_stopped_data_address returns true, and a single
address. This code calls target_stopped_data_addresses, and
now handles the possibility of a vector containing multiple
entries. We need to first loop over every watchpoint
setting its triggered status to 'no', then we check every
address in the vector setting matching watchpoint's
triggered status to 'yes'. But the actual logic for if a
watchpoint matches an address or not is unchanged.
The important thing to notice here is that in case (iii), before
this patch, GDB could already set _multiple_ watchpoints to
triggered. For example, setting a read and write watchpoint on
the same address would result in multiple watchpoints being marked
as triggered. This patch just extends this so that multiple
watchpoints, at multiple addresses, can now be marked as
triggered.
* In remote.c there is an interesting change. We need to allow
gdbserver to pass the multiple addresses back to GDB. To achieve
this, I now allow multiple 'watch', 'rwatch', and 'awatch' tokens
in a 'T' stop reply packet. This change is largely backward
compatible. For old versions of GDB, GDB will just use the last
such token as the watchpoint stop address. For new GDBs, all of
the addresses are collected and returned from the
target_ops::stopped_data_addresses call. If a new GDB connects to
an old gdbserver then it'll only get a single watchpoint address
in the 'T' packet, but that's no worse than we are now, and will
not cause a GDB crash, GDB will just end up checking a restricted
set of watchpoints (which is where we are right now).
* In gdbserver/ the changes are pretty similar. The API is renamed
from ::stopped_data_address to ::stopped_data_addresses, and
::low_stopped_data_address to ::low_stopped_data_addresses.
* For all targets except AArch64, the existing code is retained, we
just wrap the single address into a vector.
* For AArch64, we call aarch64_stopped_data_addresses, which returns
the required vector.
For testing, I've built GDB on GNU/Linux for i386, x86-64, PPC64le,
ARM, and AArch64. That still leaves a lot of targets possibly
impacted by this change as untested. Which is a risk. I certainly
wouldn't want to push this patch until after GDB 17 branches so we
have time to find and fix any regressions that are introduced.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33240
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33252
This commit is contained in:
committed by
Andrew Burgess
parent
97b6ffe44b
commit
21c90ca166
6
gdb/NEWS
6
gdb/NEWS
@@ -209,6 +209,12 @@ vFile:stat
|
|||||||
lstat rather than stat. This has now been corrected. The
|
lstat rather than stat. This has now been corrected. The
|
||||||
documentation has also been clarified.
|
documentation has also been clarified.
|
||||||
|
|
||||||
|
T
|
||||||
|
The signal stop packet can now include multiple 'watch', 'rwatch',
|
||||||
|
and 'awatch' stop reason entries. GDB will select between all of
|
||||||
|
the possible watchpoint addresses that are return when presenting
|
||||||
|
the stop to the user.
|
||||||
|
|
||||||
* MI changes
|
* MI changes
|
||||||
|
|
||||||
** The =library-unloaded event now includes the 'ranges' field, which
|
** The =library-unloaded event now includes the 'ranges' field, which
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ struct aarch64_fbsd_nat_target final : public fbsd_nat_target
|
|||||||
#ifdef HAVE_DBREG
|
#ifdef HAVE_DBREG
|
||||||
/* Hardware breakpoints and watchpoints. */
|
/* Hardware breakpoints and watchpoints. */
|
||||||
bool stopped_by_watchpoint () override;
|
bool stopped_by_watchpoint () override;
|
||||||
bool stopped_data_address (CORE_ADDR *) override;
|
std::vector<CORE_ADDR> stopped_data_addresses () override;
|
||||||
bool stopped_by_hw_breakpoint () override;
|
bool stopped_by_hw_breakpoint () override;
|
||||||
bool supports_stopped_by_hw_breakpoint () override;
|
bool supports_stopped_by_hw_breakpoint () override;
|
||||||
|
|
||||||
@@ -134,28 +134,28 @@ bool aarch64_fbsd_nat_target::debug_regs_probed;
|
|||||||
|
|
||||||
static std::unordered_set<lwpid_t> aarch64_debug_pending_threads;
|
static std::unordered_set<lwpid_t> aarch64_debug_pending_threads;
|
||||||
|
|
||||||
/* Implement the "stopped_data_address" target_ops method. */
|
/* Implement the "stopped_data_addresses" target_ops method. */
|
||||||
|
|
||||||
bool
|
std::vector<CORE_ADDR>
|
||||||
aarch64_fbsd_nat_target::stopped_data_address (CORE_ADDR *addr_p)
|
aarch64_fbsd_nat_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
siginfo_t siginfo;
|
siginfo_t siginfo;
|
||||||
struct aarch64_debug_reg_state *state;
|
struct aarch64_debug_reg_state *state;
|
||||||
|
|
||||||
if (!fbsd_nat_get_siginfo (inferior_ptid, &siginfo))
|
if (!fbsd_nat_get_siginfo (inferior_ptid, &siginfo))
|
||||||
return false;
|
return {};
|
||||||
|
|
||||||
/* This must be a hardware breakpoint. */
|
/* This must be a hardware breakpoint. */
|
||||||
if (siginfo.si_signo != SIGTRAP
|
if (siginfo.si_signo != SIGTRAP
|
||||||
|| siginfo.si_code != TRAP_TRACE
|
|| siginfo.si_code != TRAP_TRACE
|
||||||
|| siginfo.si_trapno != EXCP_WATCHPT_EL0)
|
|| siginfo.si_trapno != EXCP_WATCHPT_EL0)
|
||||||
return false;
|
return {};
|
||||||
|
|
||||||
const CORE_ADDR addr_trap = (CORE_ADDR) siginfo.si_addr;
|
const CORE_ADDR addr_trap = (CORE_ADDR) siginfo.si_addr;
|
||||||
|
|
||||||
/* Check if the address matches any watched address. */
|
/* Check if the address matches any watched address. */
|
||||||
state = aarch64_get_debug_reg_state (inferior_ptid.pid ());
|
state = aarch64_get_debug_reg_state (inferior_ptid.pid ());
|
||||||
return aarch64_stopped_data_address (state, addr_trap, addr_p);
|
return aarch64_stopped_data_addresses (state, addr_trap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implement the "stopped_by_watchpoint" target_ops method. */
|
/* Implement the "stopped_by_watchpoint" target_ops method. */
|
||||||
@@ -163,7 +163,7 @@ aarch64_fbsd_nat_target::stopped_data_address (CORE_ADDR *addr_p)
|
|||||||
bool
|
bool
|
||||||
aarch64_fbsd_nat_target::stopped_by_watchpoint ()
|
aarch64_fbsd_nat_target::stopped_by_watchpoint ()
|
||||||
{
|
{
|
||||||
return stopped_data_address (nullptr);
|
return !stopped_data_addresses ().empty ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implement the "stopped_by_hw_breakpoint" target_ops method. */
|
/* Implement the "stopped_by_hw_breakpoint" target_ops method. */
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ public:
|
|||||||
|
|
||||||
/* Add our hardware breakpoint and watchpoint implementation. */
|
/* Add our hardware breakpoint and watchpoint implementation. */
|
||||||
bool stopped_by_watchpoint () override;
|
bool stopped_by_watchpoint () override;
|
||||||
bool stopped_data_address (CORE_ADDR *) override;
|
std::vector<CORE_ADDR> stopped_data_addresses () override;
|
||||||
|
|
||||||
int can_do_single_step () override;
|
int can_do_single_step () override;
|
||||||
|
|
||||||
@@ -922,21 +922,21 @@ aarch64_linux_nat_target::low_siginfo_fixup (siginfo_t *native, gdb_byte *inf,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implement the "stopped_data_address" target_ops method. */
|
/* Implement the "stopped_data_addresses" target_ops method. */
|
||||||
|
|
||||||
bool
|
std::vector<CORE_ADDR>
|
||||||
aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
|
aarch64_linux_nat_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
siginfo_t siginfo;
|
siginfo_t siginfo;
|
||||||
struct aarch64_debug_reg_state *state;
|
struct aarch64_debug_reg_state *state;
|
||||||
|
|
||||||
if (!linux_nat_get_siginfo (inferior_ptid, &siginfo))
|
if (!linux_nat_get_siginfo (inferior_ptid, &siginfo))
|
||||||
return false;
|
return {};
|
||||||
|
|
||||||
/* This must be a hardware breakpoint. */
|
/* This must be a hardware breakpoint. */
|
||||||
if (siginfo.si_signo != SIGTRAP
|
if (siginfo.si_signo != SIGTRAP
|
||||||
|| (siginfo.si_code & 0xffff) != TRAP_HWBKPT)
|
|| (siginfo.si_code & 0xffff) != TRAP_HWBKPT)
|
||||||
return false;
|
return {};
|
||||||
|
|
||||||
/* Make sure to ignore the top byte, otherwise we may not recognize a
|
/* Make sure to ignore the top byte, otherwise we may not recognize a
|
||||||
hardware watchpoint hit. The stopped data addresses coming from the
|
hardware watchpoint hit. The stopped data addresses coming from the
|
||||||
@@ -947,7 +947,7 @@ aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
|
|||||||
|
|
||||||
/* Check if the address matches any watched address. */
|
/* Check if the address matches any watched address. */
|
||||||
state = aarch64_get_debug_reg_state (inferior_ptid.pid ());
|
state = aarch64_get_debug_reg_state (inferior_ptid.pid ());
|
||||||
return aarch64_stopped_data_address (state, addr_trap, addr_p);
|
return aarch64_stopped_data_addresses (state, addr_trap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implement the "stopped_by_watchpoint" target_ops method. */
|
/* Implement the "stopped_by_watchpoint" target_ops method. */
|
||||||
@@ -955,7 +955,7 @@ aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
|
|||||||
bool
|
bool
|
||||||
aarch64_linux_nat_target::stopped_by_watchpoint ()
|
aarch64_linux_nat_target::stopped_by_watchpoint ()
|
||||||
{
|
{
|
||||||
return stopped_data_address (nullptr);
|
return !stopped_data_addresses ().empty ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implement the "can_do_single_step" target_ops method. */
|
/* Implement the "can_do_single_step" target_ops method. */
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ public:
|
|||||||
struct expression *) override;
|
struct expression *) override;
|
||||||
bool stopped_by_watchpoint () override;
|
bool stopped_by_watchpoint () override;
|
||||||
|
|
||||||
bool stopped_data_address (CORE_ADDR *) override;
|
std::vector<CORE_ADDR> stopped_data_addresses () override;
|
||||||
|
|
||||||
bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int) override;
|
bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int) override;
|
||||||
|
|
||||||
@@ -1169,41 +1169,37 @@ arm_linux_nat_target::remove_watchpoint (CORE_ADDR addr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* What was the data address the target was stopped on accessing. */
|
/* What was the data address the target was stopped on accessing. */
|
||||||
bool
|
std::vector<CORE_ADDR>
|
||||||
arm_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
|
arm_linux_nat_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
siginfo_t siginfo;
|
siginfo_t siginfo;
|
||||||
int slot;
|
|
||||||
|
|
||||||
if (!linux_nat_get_siginfo (inferior_ptid, &siginfo))
|
if (!linux_nat_get_siginfo (inferior_ptid, &siginfo))
|
||||||
return false;
|
return {};
|
||||||
|
|
||||||
/* This must be a hardware breakpoint. */
|
/* This must be a hardware breakpoint. */
|
||||||
if (siginfo.si_signo != SIGTRAP
|
if (siginfo.si_signo != SIGTRAP
|
||||||
|| (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
|
|| (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
|
||||||
return false;
|
return {};
|
||||||
|
|
||||||
/* We must be able to set hardware watchpoints. */
|
/* We must be able to set hardware watchpoints. */
|
||||||
if (arm_linux_get_hw_watchpoint_count () == 0)
|
if (arm_linux_get_hw_watchpoint_count () == 0)
|
||||||
return 0;
|
return {};
|
||||||
|
|
||||||
slot = siginfo.si_errno;
|
int slot = siginfo.si_errno;
|
||||||
|
|
||||||
/* If we are in a positive slot then we're looking at a breakpoint and not
|
/* If we are in a positive slot then we're looking at a breakpoint and not
|
||||||
a watchpoint. */
|
a watchpoint. */
|
||||||
if (slot >= 0)
|
if (slot >= 0)
|
||||||
return false;
|
return {};
|
||||||
|
|
||||||
*addr_p = (CORE_ADDR) (uintptr_t) siginfo.si_addr;
|
return { (CORE_ADDR) (uintptr_t) siginfo.si_addr };
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Has the target been stopped by hitting a watchpoint? */
|
/* Has the target been stopped by hitting a watchpoint? */
|
||||||
bool
|
bool
|
||||||
arm_linux_nat_target::stopped_by_watchpoint ()
|
arm_linux_nat_target::stopped_by_watchpoint ()
|
||||||
{
|
{
|
||||||
CORE_ADDR addr;
|
return !stopped_data_addresses ().empty ();
|
||||||
return stopped_data_address (&addr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|||||||
@@ -2140,7 +2140,7 @@ add_dummy_location (struct breakpoint *b,
|
|||||||
The following constraints influence the location where we can reset
|
The following constraints influence the location where we can reset
|
||||||
hardware watchpoints:
|
hardware watchpoints:
|
||||||
|
|
||||||
* target_stopped_by_watchpoint and target_stopped_data_address are
|
* target_stopped_by_watchpoint and target_stopped_data_addresses are
|
||||||
called several times when GDB stops.
|
called several times when GDB stops.
|
||||||
|
|
||||||
[linux]
|
[linux]
|
||||||
@@ -5243,10 +5243,7 @@ bpstat::bpstat ()
|
|||||||
int
|
int
|
||||||
watchpoints_triggered (const target_waitstatus &ws)
|
watchpoints_triggered (const target_waitstatus &ws)
|
||||||
{
|
{
|
||||||
bool stopped_by_watchpoint = target_stopped_by_watchpoint ();
|
if (!target_stopped_by_watchpoint ())
|
||||||
CORE_ADDR addr;
|
|
||||||
|
|
||||||
if (!stopped_by_watchpoint)
|
|
||||||
{
|
{
|
||||||
/* We were not stopped by a watchpoint. Mark all watchpoints
|
/* We were not stopped by a watchpoint. Mark all watchpoints
|
||||||
as not triggered. */
|
as not triggered. */
|
||||||
@@ -5261,7 +5258,9 @@ watchpoints_triggered (const target_waitstatus &ws)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!target_stopped_data_address (current_inferior ()->top_target (), &addr))
|
std::vector<CORE_ADDR> addr_list
|
||||||
|
= target_stopped_data_addresses (current_inferior ()->top_target ());
|
||||||
|
if (addr_list.empty ())
|
||||||
{
|
{
|
||||||
/* We were stopped by a watchpoint, but we don't know where.
|
/* We were stopped by a watchpoint, but we don't know where.
|
||||||
Mark all watchpoints as unknown. */
|
Mark all watchpoints as unknown. */
|
||||||
@@ -5279,13 +5278,20 @@ watchpoints_triggered (const target_waitstatus &ws)
|
|||||||
/* The target could report the data address. Mark watchpoints
|
/* The target could report the data address. Mark watchpoints
|
||||||
affected by this data address as triggered, and all others as not
|
affected by this data address as triggered, and all others as not
|
||||||
triggered. */
|
triggered. */
|
||||||
|
for (breakpoint &b : all_breakpoints ())
|
||||||
|
if (is_hardware_watchpoint (&b))
|
||||||
|
{
|
||||||
|
watchpoint &w = gdb::checked_static_cast<watchpoint &> (b);
|
||||||
|
w.watchpoint_triggered = watch_triggered_no;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const CORE_ADDR addr : addr_list)
|
||||||
|
{
|
||||||
for (breakpoint &b : all_breakpoints ())
|
for (breakpoint &b : all_breakpoints ())
|
||||||
if (is_hardware_watchpoint (&b))
|
if (is_hardware_watchpoint (&b))
|
||||||
{
|
{
|
||||||
watchpoint &w = gdb::checked_static_cast<watchpoint &> (b);
|
watchpoint &w = gdb::checked_static_cast<watchpoint &> (b);
|
||||||
|
|
||||||
w.watchpoint_triggered = watch_triggered_no;
|
|
||||||
for (bp_location &loc : b.locations ())
|
for (bp_location &loc : b.locations ())
|
||||||
{
|
{
|
||||||
if (is_masked_watchpoint (&b))
|
if (is_masked_watchpoint (&b))
|
||||||
@@ -5309,6 +5315,7 @@ watchpoints_triggered (const target_waitstatus &ws)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -5405,7 +5412,7 @@ watchpoint_check (bpstat *bs)
|
|||||||
|
|
||||||
if (is_masked_watchpoint (b))
|
if (is_masked_watchpoint (b))
|
||||||
/* Since we don't know the exact trigger address (from
|
/* Since we don't know the exact trigger address (from
|
||||||
stopped_data_address), just tell the user we've triggered
|
stopped_data_addresses), just tell the user we've triggered
|
||||||
a mask watchpoint. */
|
a mask watchpoint. */
|
||||||
return WP_VALUE_CHANGED;
|
return WP_VALUE_CHANGED;
|
||||||
|
|
||||||
|
|||||||
@@ -43909,8 +43909,15 @@ The currently defined stop reasons are:
|
|||||||
@item watch
|
@item watch
|
||||||
@itemx rwatch
|
@itemx rwatch
|
||||||
@itemx awatch
|
@itemx awatch
|
||||||
The packet indicates a watchpoint hit, and @var{r} is the data address, in
|
The packet indicates a watchpoint hit, and @var{r} is the data
|
||||||
hex.
|
address, in hex.
|
||||||
|
|
||||||
|
Some targets, for example AArch64, are unable to accurately report the
|
||||||
|
address which triggered a watchpoint trap. As a consequence, multiple
|
||||||
|
watched addresses could explain a single watchpoint trap. In these
|
||||||
|
cases, multiple instances of these stop reasons can appear in a single
|
||||||
|
stop packet. @value{GDBN} will select between the multiple reported
|
||||||
|
stop addresses when displaying the stop to the user.
|
||||||
|
|
||||||
@item syscall_entry
|
@item syscall_entry
|
||||||
@itemx syscall_return
|
@itemx syscall_return
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public:
|
|||||||
|
|
||||||
int can_use_hw_breakpoint (enum bptype, int, int) override;
|
int can_use_hw_breakpoint (enum bptype, int, int) override;
|
||||||
bool stopped_by_watchpoint () override;
|
bool stopped_by_watchpoint () override;
|
||||||
bool stopped_data_address (CORE_ADDR *) override;
|
std::vector<CORE_ADDR> stopped_data_addresses () override;
|
||||||
int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type,
|
int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type,
|
||||||
struct expression *) override;
|
struct expression *) override;
|
||||||
int remove_watchpoint (CORE_ADDR, int, enum target_hw_bp_type,
|
int remove_watchpoint (CORE_ADDR, int, enum target_hw_bp_type,
|
||||||
@@ -686,34 +686,32 @@ ia64_linux_nat_target::low_new_thread (struct lwp_info *lp)
|
|||||||
enable_watchpoints_in_psr (lp->ptid);
|
enable_watchpoints_in_psr (lp->ptid);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
std::vector<CORE_ADDR>
|
||||||
ia64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
|
ia64_linux_nat_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
CORE_ADDR psr;
|
CORE_ADDR psr;
|
||||||
siginfo_t siginfo;
|
siginfo_t siginfo;
|
||||||
regcache *regcache = get_thread_regcache (inferior_thread ());
|
regcache *regcache = get_thread_regcache (inferior_thread ());
|
||||||
|
|
||||||
if (!linux_nat_get_siginfo (inferior_ptid, &siginfo))
|
if (!linux_nat_get_siginfo (inferior_ptid, &siginfo))
|
||||||
return false;
|
return {};
|
||||||
|
|
||||||
if (siginfo.si_signo != SIGTRAP
|
if (siginfo.si_signo != SIGTRAP
|
||||||
|| (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
|
|| (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
|
||||||
return false;
|
return {};
|
||||||
|
|
||||||
regcache_cooked_read_unsigned (regcache, IA64_PSR_REGNUM, &psr);
|
regcache_cooked_read_unsigned (regcache, IA64_PSR_REGNUM, &psr);
|
||||||
psr |= IA64_PSR_DD; /* Set the dd bit - this will disable the watchpoint
|
psr |= IA64_PSR_DD; /* Set the dd bit - this will disable the watchpoint
|
||||||
for the next instruction. */
|
for the next instruction. */
|
||||||
regcache_cooked_write_unsigned (regcache, IA64_PSR_REGNUM, psr);
|
regcache_cooked_write_unsigned (regcache, IA64_PSR_REGNUM, psr);
|
||||||
|
|
||||||
*addr_p = (CORE_ADDR) siginfo.si_addr;
|
return { (CORE_ADDR) siginfo.si_addr };
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ia64_linux_nat_target::stopped_by_watchpoint ()
|
ia64_linux_nat_target::stopped_by_watchpoint ()
|
||||||
{
|
{
|
||||||
CORE_ADDR addr;
|
return !stopped_data_addresses ().empty ();
|
||||||
return stopped_data_address (&addr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|||||||
26
gdb/infrun.c
26
gdb/infrun.c
@@ -6889,16 +6889,26 @@ handle_signal_stop (struct execution_control_state *ecs)
|
|||||||
("stop_pc=%s", paddress (reg_gdbarch, ecs->event_thread->stop_pc ()));
|
("stop_pc=%s", paddress (reg_gdbarch, ecs->event_thread->stop_pc ()));
|
||||||
if (target_stopped_by_watchpoint ())
|
if (target_stopped_by_watchpoint ())
|
||||||
{
|
{
|
||||||
CORE_ADDR addr;
|
auto inf_target = current_inferior ()->top_target ();
|
||||||
|
std::vector<CORE_ADDR> addr_list
|
||||||
|
= target_stopped_data_addresses (inf_target);
|
||||||
|
|
||||||
infrun_debug_printf ("stopped by watchpoint");
|
std::string addr_str;
|
||||||
|
if (addr_list.empty ())
|
||||||
if (target_stopped_data_address (current_inferior ()->top_target (),
|
addr_str = "(no data addressses available)";
|
||||||
&addr))
|
|
||||||
infrun_debug_printf ("stopped data address=%s",
|
|
||||||
paddress (reg_gdbarch, addr));
|
|
||||||
else
|
else
|
||||||
infrun_debug_printf ("(no data address available)");
|
{
|
||||||
|
for (const CORE_ADDR addr : addr_list)
|
||||||
|
{
|
||||||
|
if (addr_str.length () > 0)
|
||||||
|
addr_str += ", ";
|
||||||
|
|
||||||
|
addr_str += paddress (reg_gdbarch, addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
infrun_debug_printf ("stopped by watchpoint, data addresses = %s",
|
||||||
|
addr_str.c_str ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2515,16 +2515,17 @@ linux_nat_target::stopped_by_watchpoint ()
|
|||||||
return lp->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
|
return lp->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
std::vector<CORE_ADDR>
|
||||||
linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
|
linux_nat_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
struct lwp_info *lp = find_lwp_pid (inferior_ptid);
|
struct lwp_info *lp = find_lwp_pid (inferior_ptid);
|
||||||
|
|
||||||
gdb_assert (lp != NULL);
|
gdb_assert (lp != NULL);
|
||||||
|
|
||||||
*addr_p = lp->stopped_data_address;
|
if (lp->stopped_data_address_p)
|
||||||
|
return { lp->stopped_data_address };
|
||||||
|
|
||||||
return lp->stopped_data_address_p;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Commonly any breakpoint / watchpoint generate only SIGTRAP. */
|
/* Commonly any breakpoint / watchpoint generate only SIGTRAP. */
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public:
|
|||||||
|
|
||||||
bool stopped_by_watchpoint () override;
|
bool stopped_by_watchpoint () override;
|
||||||
|
|
||||||
bool stopped_data_address (CORE_ADDR *) override;
|
std::vector<CORE_ADDR> stopped_data_addresses () override;
|
||||||
|
|
||||||
bool stopped_by_sw_breakpoint () override;
|
bool stopped_by_sw_breakpoint () override;
|
||||||
bool supports_stopped_by_sw_breakpoint () override;
|
bool supports_stopped_by_sw_breakpoint () override;
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public:
|
|||||||
|
|
||||||
/* Add our hardware breakpoint and watchpoint implementation. */
|
/* Add our hardware breakpoint and watchpoint implementation. */
|
||||||
bool stopped_by_watchpoint () override;
|
bool stopped_by_watchpoint () override;
|
||||||
bool stopped_data_address (CORE_ADDR *) override;
|
std::vector<CORE_ADDR> stopped_data_addresses () override;
|
||||||
|
|
||||||
int insert_hw_breakpoint (struct gdbarch *gdbarch,
|
int insert_hw_breakpoint (struct gdbarch *gdbarch,
|
||||||
struct bp_target_info *bp_tgt) override;
|
struct bp_target_info *bp_tgt) override;
|
||||||
@@ -590,26 +590,30 @@ loongarch_linux_nat_target::watchpoint_addr_within_range (CORE_ADDR addr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Implement the "stopped_data_address" target_ops method. */
|
/* Implement the "stopped_data_addresses" target_ops method. */
|
||||||
|
|
||||||
bool
|
std::vector<CORE_ADDR>
|
||||||
loongarch_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
|
loongarch_linux_nat_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
siginfo_t siginfo;
|
siginfo_t siginfo;
|
||||||
struct loongarch_debug_reg_state *state;
|
struct loongarch_debug_reg_state *state;
|
||||||
|
|
||||||
if (!linux_nat_get_siginfo (inferior_ptid, &siginfo))
|
if (!linux_nat_get_siginfo (inferior_ptid, &siginfo))
|
||||||
return false;
|
return {};
|
||||||
|
|
||||||
/* This must be a hardware breakpoint. */
|
/* This must be a hardware breakpoint. */
|
||||||
if (siginfo.si_signo != SIGTRAP || (siginfo.si_code & 0xffff) != TRAP_HWBKPT)
|
if (siginfo.si_signo != SIGTRAP || (siginfo.si_code & 0xffff) != TRAP_HWBKPT)
|
||||||
return false;
|
return {};
|
||||||
|
|
||||||
/* Check if the address matches any watched address. */
|
/* Check if the address matches any watched address. */
|
||||||
state = loongarch_get_debug_reg_state (inferior_ptid.pid ());
|
state = loongarch_get_debug_reg_state (inferior_ptid.pid ());
|
||||||
|
|
||||||
return
|
CORE_ADDR addr;
|
||||||
loongarch_stopped_data_address (state, (CORE_ADDR) siginfo.si_addr, addr_p);
|
if (loongarch_stopped_data_address (state, (CORE_ADDR) siginfo.si_addr,
|
||||||
|
&addr))
|
||||||
|
return { addr };
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implement the "stopped_by_watchpoint" target_ops method. */
|
/* Implement the "stopped_by_watchpoint" target_ops method. */
|
||||||
@@ -617,9 +621,7 @@ loongarch_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
|
|||||||
bool
|
bool
|
||||||
loongarch_linux_nat_target::stopped_by_watchpoint ()
|
loongarch_linux_nat_target::stopped_by_watchpoint ()
|
||||||
{
|
{
|
||||||
CORE_ADDR addr;
|
return !stopped_data_addresses ().empty ();
|
||||||
|
|
||||||
return stopped_data_address (&addr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Insert a hardware-assisted breakpoint at BP_TGT->reqstd_address.
|
/* Insert a hardware-assisted breakpoint at BP_TGT->reqstd_address.
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public:
|
|||||||
|
|
||||||
bool stopped_by_watchpoint () override;
|
bool stopped_by_watchpoint () override;
|
||||||
|
|
||||||
bool stopped_data_address (CORE_ADDR *) override;
|
std::vector<CORE_ADDR> stopped_data_addresses () override;
|
||||||
|
|
||||||
int region_ok_for_hw_watchpoint (CORE_ADDR, int) override;
|
int region_ok_for_hw_watchpoint (CORE_ADDR, int) override;
|
||||||
|
|
||||||
@@ -598,16 +598,17 @@ mips_linux_nat_target::stopped_by_watchpoint ()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Target to_stopped_data_address implementation. Set the address
|
/* Target stopped_data_addresses implementation. Return a vector
|
||||||
where the watch triggered (if known). Return 1 if the address was
|
containing the address(es) of the watchpoint(s) that triggered, if
|
||||||
known. */
|
known. Return an empty vector if it is unknown which watchpoint(s)
|
||||||
|
triggered. */
|
||||||
|
|
||||||
bool
|
std::vector<CORE_ADDR>
|
||||||
mips_linux_nat_target::stopped_data_address (CORE_ADDR *paddr)
|
mips_linux_nat_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
/* On mips we don't know the low order 3 bits of the data address,
|
/* On mips we don't know the low order 3 bits of the data address,
|
||||||
so we must return false. */
|
so we must return an empty vector. */
|
||||||
return false;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Target to_region_ok_for_hw_watchpoint implementation. Return 1 if
|
/* Target to_region_ok_for_hw_watchpoint implementation. Return 1 if
|
||||||
|
|||||||
@@ -215,14 +215,14 @@ aarch64_point_is_aligned (ptid_t ptid, int is_watchpoint, CORE_ADDR addr,
|
|||||||
|
|
||||||
Another limitation is that because the watched region is enlarged,
|
Another limitation is that because the watched region is enlarged,
|
||||||
the watchpoint fault address discovered by
|
the watchpoint fault address discovered by
|
||||||
aarch64_stopped_data_address may be outside of the original watched
|
aarch64_stopped_data_addresses may be outside of the original watched
|
||||||
region, especially when the triggering instruction is accessing a
|
region, especially when the triggering instruction is accessing a
|
||||||
larger region. When the fault address is not within any known
|
larger region. When the fault address is not within any known
|
||||||
range, watchpoints_triggered in gdb will get confused, as the
|
range, watchpoints_triggered in gdb will get confused, as the
|
||||||
higher-level watchpoint management is only aware of original
|
higher-level watchpoint management is only aware of original
|
||||||
watched regions, and will think that some unknown watchpoint has
|
watched regions, and will think that some unknown watchpoint has
|
||||||
been triggered. To prevent such a case,
|
been triggered. To prevent such a case,
|
||||||
aarch64_stopped_data_address implementations in gdb and gdbserver
|
aarch64_stopped_data_addresses implementations in gdb and gdbserver
|
||||||
try to match the trapped address with a watched region, and return
|
try to match the trapped address with a watched region, and return
|
||||||
an address within the latter. */
|
an address within the latter. */
|
||||||
|
|
||||||
@@ -646,14 +646,31 @@ aarch64_region_ok_for_watchpoint (CORE_ADDR addr, int len)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]] static void
|
||||||
|
apb_debug (const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (getenv ("APB_DEBUG") == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_start (ap, fmt);
|
||||||
|
vfprintf (stderr, fmt, ap);
|
||||||
|
va_end (ap);
|
||||||
|
}
|
||||||
|
|
||||||
/* See nat/aarch64-hw-point.h. */
|
/* See nat/aarch64-hw-point.h. */
|
||||||
|
|
||||||
bool
|
std::vector<CORE_ADDR>
|
||||||
aarch64_stopped_data_address (const struct aarch64_debug_reg_state *state,
|
aarch64_stopped_data_addresses (const struct aarch64_debug_reg_state *state,
|
||||||
CORE_ADDR addr_trap, CORE_ADDR *addr_p)
|
CORE_ADDR addr_trap)
|
||||||
{
|
{
|
||||||
bool found = false;
|
apb_debug ("APB: ---------- Enter: aarch64_stopped_data_addresses ----------\n");
|
||||||
for (int phase = 0; phase <= 1; ++phase)
|
apb_debug ("APB: addr_trap = %s\n", core_addr_to_string_nz (addr_trap));
|
||||||
|
|
||||||
|
/* ... */
|
||||||
|
std::vector<CORE_ADDR> matching_addresses;
|
||||||
|
|
||||||
for (int i = aarch64_num_wp_regs - 1; i >= 0; --i)
|
for (int i = aarch64_num_wp_regs - 1; i >= 0; --i)
|
||||||
{
|
{
|
||||||
if (!(state->dr_ref_count_wp[i]
|
if (!(state->dr_ref_count_wp[i]
|
||||||
@@ -671,19 +688,6 @@ aarch64_stopped_data_address (const struct aarch64_debug_reg_state *state,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (phase == 0)
|
|
||||||
{
|
|
||||||
/* Phase 0: No hw_write. */
|
|
||||||
if (type == hw_write)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Phase 1: Only hw_write. */
|
|
||||||
if (type != hw_write)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsigned int offset
|
const unsigned int offset
|
||||||
= aarch64_watchpoint_offset (state->dr_ctrl_wp[i]);
|
= aarch64_watchpoint_offset (state->dr_ctrl_wp[i]);
|
||||||
const unsigned int len
|
const unsigned int len
|
||||||
@@ -716,6 +720,17 @@ aarch64_stopped_data_address (const struct aarch64_debug_reg_state *state,
|
|||||||
const CORE_ADDR max_access_size = type == hw_write ? 16 : 8;
|
const CORE_ADDR max_access_size = type == hw_write ? 16 : 8;
|
||||||
const CORE_ADDR addr_watch_base = addr_watch_aligned -
|
const CORE_ADDR addr_watch_base = addr_watch_aligned -
|
||||||
(max_access_size - AARCH64_HWP_MAX_LEN_PER_REG);
|
(max_access_size - AARCH64_HWP_MAX_LEN_PER_REG);
|
||||||
|
|
||||||
|
|
||||||
|
apb_debug ("APB: WP %d, %s..%s, addr_watch_aligned = %s, addr_watch = %s, addr_orig = %s, len = %d, offset = %d\n",
|
||||||
|
i,
|
||||||
|
core_addr_to_string_nz (addr_watch_base),
|
||||||
|
core_addr_to_string_nz (addr_watch + len),
|
||||||
|
core_addr_to_string_nz (addr_watch_aligned), core_addr_to_string_nz (addr_watch),
|
||||||
|
core_addr_to_string_nz (addr_orig), len, offset);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (!(addr_trap >= addr_watch_base
|
if (!(addr_trap >= addr_watch_base
|
||||||
&& addr_trap < addr_watch + len))
|
&& addr_trap < addr_watch + len))
|
||||||
{
|
{
|
||||||
@@ -723,40 +738,18 @@ aarch64_stopped_data_address (const struct aarch64_debug_reg_state *state,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* To match a watchpoint known to GDB core, we must never
|
apb_debug ("APB: Match for %d, type: %s, range: %s..%s\n",
|
||||||
report *ADDR_P outside of any ADDR_WATCH..ADDR_WATCH+LEN
|
i,
|
||||||
range. ADDR_WATCH <= ADDR_TRAP < ADDR_ORIG is a false
|
(type == hw_write ? "write" : "access"),
|
||||||
positive on kernels older than 4.10. See PR
|
core_addr_to_string_nz (addr_watch_base),
|
||||||
external/20207. */
|
core_addr_to_string_nz (addr_watch + len));
|
||||||
if (addr_p != nullptr)
|
|
||||||
*addr_p = addr_orig;
|
|
||||||
|
|
||||||
if (phase == 0)
|
matching_addresses.push_back (addr_orig);
|
||||||
{
|
|
||||||
/* Phase 0: Return first match. */
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Phase 1. */
|
apb_debug ("APB: matching addresses:");
|
||||||
if (addr_p == nullptr)
|
for (CORE_ADDR &a : matching_addresses)
|
||||||
{
|
apb_debug (" %s", core_addr_to_string_nz (a));
|
||||||
/* First match, and we don't need to report an address. No need
|
apb_debug ("\n");
|
||||||
to look for other matches. */
|
return matching_addresses;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
/* First match, and we need to report an address. Look for other
|
|
||||||
matches. */
|
|
||||||
found = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* More than one match, and we need to return an address. No need to
|
|
||||||
look for further matches. */
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return found;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,13 +110,18 @@ unsigned int aarch64_watchpoint_offset (unsigned int ctrl);
|
|||||||
unsigned int aarch64_watchpoint_length (unsigned int ctrl);
|
unsigned int aarch64_watchpoint_length (unsigned int ctrl);
|
||||||
enum target_hw_bp_type aarch64_watchpoint_type (unsigned int ctrl);
|
enum target_hw_bp_type aarch64_watchpoint_type (unsigned int ctrl);
|
||||||
|
|
||||||
/* Helper for the "stopped_data_address" target method. Returns TRUE
|
/* Helper for the "stopped_data_addresses" target method. Returns a vector
|
||||||
if a hardware watchpoint trap at ADDR_TRAP matches a set
|
containing the addresses of all hardware watchpoints that could account
|
||||||
watchpoint. The address of the matched watchpoint is returned in
|
for a watchpoint trap at ADDR_TRAP. Return an empty vector if no
|
||||||
*ADDR_P. */
|
suitable watchpoint addresses can be identified.
|
||||||
|
|
||||||
bool aarch64_stopped_data_address (const struct aarch64_debug_reg_state *state,
|
It is possible that multiple watchpoints could account for a trap at
|
||||||
CORE_ADDR addr_trap, CORE_ADDR *addr_p);
|
ADDR_TRAP, in which case all possible addresses are returned, and GDB
|
||||||
|
core is responsible for selecting a suitable watchpoint, or otherwise
|
||||||
|
letting the user know that there is some ambiguity. */
|
||||||
|
|
||||||
|
extern std::vector<CORE_ADDR> aarch64_stopped_data_addresses
|
||||||
|
(const struct aarch64_debug_reg_state *state, CORE_ADDR addr_trap);
|
||||||
|
|
||||||
int aarch64_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
|
int aarch64_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
|
||||||
int len, int is_insert, ptid_t ptid,
|
int len, int is_insert, ptid_t ptid,
|
||||||
|
|||||||
@@ -655,7 +655,7 @@ x86_dr_stopped_data_address (struct x86_debug_reg_state *state,
|
|||||||
|
|
||||||
/* This second condition makes sure DRi is set up for a data
|
/* This second condition makes sure DRi is set up for a data
|
||||||
watchpoint, not a hardware breakpoint. The reason is that
|
watchpoint, not a hardware breakpoint. The reason is that
|
||||||
GDB doesn't call the target_stopped_data_address method
|
GDB doesn't call the target_stopped_data_addresses method
|
||||||
except for data watchpoints. In other words, I'm being
|
except for data watchpoints. In other words, I'm being
|
||||||
paranoiac. */
|
paranoiac. */
|
||||||
if (X86_DR_GET_RW_LEN (control, i) != 0)
|
if (X86_DR_GET_RW_LEN (control, i) != 0)
|
||||||
|
|||||||
24
gdb/procfs.c
24
gdb/procfs.c
@@ -159,7 +159,7 @@ public:
|
|||||||
int region_ok_for_hw_watchpoint (CORE_ADDR, int) override;
|
int region_ok_for_hw_watchpoint (CORE_ADDR, int) override;
|
||||||
|
|
||||||
int can_use_hw_breakpoint (enum bptype, int, int) override;
|
int can_use_hw_breakpoint (enum bptype, int, int) override;
|
||||||
bool stopped_data_address (CORE_ADDR *) override;
|
std::vector<CORE_ADDR> stopped_data_addresses () override;
|
||||||
|
|
||||||
void procfs_init_inferior (int pid);
|
void procfs_init_inferior (int pid);
|
||||||
};
|
};
|
||||||
@@ -3045,19 +3045,19 @@ procfs_target::stopped_by_watchpoint ()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns 1 if the OS knows the position of the triggered watchpoint,
|
/* Returns a vector containing the position of the triggered watchpoint.
|
||||||
and sets *ADDR to that address. Returns 0 if OS cannot report that
|
Returns the empty vector if OS cannot report that address. This
|
||||||
address. This function is only called if
|
function is only called if procfs_stopped_by_watchpoint returned 1, thus
|
||||||
procfs_stopped_by_watchpoint returned 1, thus no further checks are
|
no further checks are done. */
|
||||||
done. The function also assumes that ADDR is not NULL. */
|
|
||||||
|
|
||||||
bool
|
std::vector<CORE_ADDR>
|
||||||
procfs_target::stopped_data_address (CORE_ADDR *addr)
|
procfs_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
procinfo *pi;
|
procinfo *pi = find_procinfo_or_die (inferior_ptid.pid (), 0);
|
||||||
|
CORE_ADDR addr;
|
||||||
pi = find_procinfo_or_die (inferior_ptid.pid (), 0);
|
if (proc_watchpoint_address (pi, &addr))
|
||||||
return proc_watchpoint_address (pi, addr);
|
return { addr };
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ struct ravenscar_thread_target final : public target_ops
|
|||||||
|
|
||||||
bool stopped_by_watchpoint () override;
|
bool stopped_by_watchpoint () override;
|
||||||
|
|
||||||
bool stopped_data_address (CORE_ADDR *) override;
|
std::vector<CORE_ADDR> stopped_data_addresses () override;
|
||||||
|
|
||||||
enum target_xfer_status xfer_partial (enum target_object object,
|
enum target_xfer_status xfer_partial (enum target_object object,
|
||||||
const char *annex,
|
const char *annex,
|
||||||
@@ -818,14 +818,14 @@ ravenscar_thread_target::stopped_by_watchpoint ()
|
|||||||
return beneath ()->stopped_by_watchpoint ();
|
return beneath ()->stopped_by_watchpoint ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implement the to_stopped_data_address target_ops "method". */
|
/* Implement the to_stopped_data_addresses target_ops "method". */
|
||||||
|
|
||||||
bool
|
std::vector<CORE_ADDR>
|
||||||
ravenscar_thread_target::stopped_data_address (CORE_ADDR *addr_p)
|
ravenscar_thread_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
scoped_restore_current_thread saver;
|
scoped_restore_current_thread saver;
|
||||||
set_base_thread_from_ravenscar_task (inferior_ptid);
|
set_base_thread_from_ravenscar_task (inferior_ptid);
|
||||||
return beneath ()->stopped_data_address (addr_p);
|
return beneath ()->stopped_data_addresses ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ public:
|
|||||||
void async (bool) override;
|
void async (bool) override;
|
||||||
ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
|
ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
|
||||||
bool stopped_by_watchpoint () override;
|
bool stopped_by_watchpoint () override;
|
||||||
bool stopped_data_address (CORE_ADDR *) override;
|
std::vector<CORE_ADDR> stopped_data_addresses () override;
|
||||||
|
|
||||||
bool stopped_by_sw_breakpoint () override;
|
bool stopped_by_sw_breakpoint () override;
|
||||||
bool supports_stopped_by_sw_breakpoint () override;
|
bool supports_stopped_by_sw_breakpoint () override;
|
||||||
@@ -1501,13 +1501,13 @@ record_full_base_target::stopped_by_watchpoint ()
|
|||||||
return beneath ()->stopped_by_watchpoint ();
|
return beneath ()->stopped_by_watchpoint ();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
std::vector<CORE_ADDR>
|
||||||
record_full_base_target::stopped_data_address (CORE_ADDR *addr_p)
|
record_full_base_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
if (RECORD_FULL_IS_REPLAY)
|
if (RECORD_FULL_IS_REPLAY)
|
||||||
return false;
|
return {};
|
||||||
else
|
else
|
||||||
return this->beneath ()->stopped_data_address (addr_p);
|
return this->beneath ()->stopped_data_addresses ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The stopped_by_sw_breakpoint method of target record-full. */
|
/* The stopped_by_sw_breakpoint method of target record-full. */
|
||||||
|
|||||||
34
gdb/remote.c
34
gdb/remote.c
@@ -893,7 +893,7 @@ public:
|
|||||||
|
|
||||||
bool stopped_by_watchpoint () override;
|
bool stopped_by_watchpoint () override;
|
||||||
|
|
||||||
bool stopped_data_address (CORE_ADDR *) override;
|
std::vector<CORE_ADDR> stopped_data_addresses () override;
|
||||||
|
|
||||||
bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int) override;
|
bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int) override;
|
||||||
|
|
||||||
@@ -1479,7 +1479,7 @@ struct stop_reply : public notif_event
|
|||||||
|
|
||||||
enum target_stop_reason stop_reason;
|
enum target_stop_reason stop_reason;
|
||||||
|
|
||||||
CORE_ADDR watch_data_address;
|
std::vector<CORE_ADDR> watch_data_address;
|
||||||
|
|
||||||
int core;
|
int core;
|
||||||
};
|
};
|
||||||
@@ -1637,9 +1637,13 @@ struct remote_thread_info : public private_thread_info
|
|||||||
/* Whether the target stopped for a breakpoint/watchpoint. */
|
/* Whether the target stopped for a breakpoint/watchpoint. */
|
||||||
enum target_stop_reason stop_reason = TARGET_STOPPED_BY_NO_REASON;
|
enum target_stop_reason stop_reason = TARGET_STOPPED_BY_NO_REASON;
|
||||||
|
|
||||||
/* This is set to the data address of the access causing the target
|
/* This is set to all the watchpoint addresses of the access causing the
|
||||||
to stop for a watchpoint. */
|
target to stop for a watchpoint. For some targets (e.g. AArch64)
|
||||||
CORE_ADDR watch_data_address = 0;
|
targets cannot watch small (e.g. single byte) regions, so multiple
|
||||||
|
watchpoints could account for a stop. All possible watchpoint
|
||||||
|
addresses are reported back to GDB, and GDB must select between
|
||||||
|
them. */
|
||||||
|
std::vector<CORE_ADDR> watch_data_address;
|
||||||
|
|
||||||
/* Get the thread's resume state. */
|
/* Get the thread's resume state. */
|
||||||
enum resume_state get_resume_state () const
|
enum resume_state get_resume_state () const
|
||||||
@@ -6881,7 +6885,7 @@ resume_clear_thread_private_info (struct thread_info *thread)
|
|||||||
remote_thread_info *priv = get_remote_thread_info (thread);
|
remote_thread_info *priv = get_remote_thread_info (thread);
|
||||||
|
|
||||||
priv->stop_reason = TARGET_STOPPED_BY_NO_REASON;
|
priv->stop_reason = TARGET_STOPPED_BY_NO_REASON;
|
||||||
priv->watch_data_address = 0;
|
priv->watch_data_address.clear ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7497,7 +7501,7 @@ remote_target::remote_stop_ns (ptid_t ptid)
|
|||||||
sr->ws.set_stopped (GDB_SIGNAL_0);
|
sr->ws.set_stopped (GDB_SIGNAL_0);
|
||||||
sr->arch = tp->inf->arch ();
|
sr->arch = tp->inf->arch ();
|
||||||
sr->stop_reason = TARGET_STOPPED_BY_NO_REASON;
|
sr->stop_reason = TARGET_STOPPED_BY_NO_REASON;
|
||||||
sr->watch_data_address = 0;
|
sr->watch_data_address.clear ();
|
||||||
sr->core = 0;
|
sr->core = 0;
|
||||||
this->push_stop_reply (std::move (sr));
|
this->push_stop_reply (std::move (sr));
|
||||||
|
|
||||||
@@ -8083,7 +8087,7 @@ Packet: '%s'\n"),
|
|||||||
{
|
{
|
||||||
event->stop_reason = TARGET_STOPPED_BY_WATCHPOINT;
|
event->stop_reason = TARGET_STOPPED_BY_WATCHPOINT;
|
||||||
p = unpack_varlen_hex (++p1, &addr);
|
p = unpack_varlen_hex (++p1, &addr);
|
||||||
event->watch_data_address = (CORE_ADDR) addr;
|
event->watch_data_address.push_back ((CORE_ADDR) addr);
|
||||||
}
|
}
|
||||||
else if (strprefix (p, p1, "swbreak"))
|
else if (strprefix (p, p1, "swbreak"))
|
||||||
{
|
{
|
||||||
@@ -11401,20 +11405,16 @@ remote_target::stopped_by_watchpoint ()
|
|||||||
== TARGET_STOPPED_BY_WATCHPOINT));
|
== TARGET_STOPPED_BY_WATCHPOINT));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
std::vector<CORE_ADDR>
|
||||||
remote_target::stopped_data_address (CORE_ADDR *addr_p)
|
remote_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
struct thread_info *thread = inferior_thread ();
|
struct thread_info *thread = inferior_thread ();
|
||||||
|
|
||||||
if (thread->priv != NULL
|
if (thread->priv != NULL
|
||||||
&& (get_remote_thread_info (thread)->stop_reason
|
&& (get_remote_thread_info (thread)->stop_reason == TARGET_STOPPED_BY_WATCHPOINT))
|
||||||
== TARGET_STOPPED_BY_WATCHPOINT))
|
return get_remote_thread_info (thread)->watch_data_address;
|
||||||
{
|
|
||||||
*addr_p = get_remote_thread_info (thread)->watch_data_address;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -186,6 +186,10 @@ static std::string
|
|||||||
target_debug_print_std_vector_mem_region (const std::vector<mem_region> &vec)
|
target_debug_print_std_vector_mem_region (const std::vector<mem_region> &vec)
|
||||||
{ return host_address_to_string (vec.data ()); }
|
{ return host_address_to_string (vec.data ()); }
|
||||||
|
|
||||||
|
static std::string
|
||||||
|
target_debug_print_std_vector_CORE_ADDR (const std::vector<CORE_ADDR> &vec)
|
||||||
|
{ return host_address_to_string (vec.data ()); }
|
||||||
|
|
||||||
static std::string
|
static std::string
|
||||||
target_debug_print_std_vector_static_tracepoint_marker
|
target_debug_print_std_vector_static_tracepoint_marker
|
||||||
(const std::vector<static_tracepoint_marker> &vec)
|
(const std::vector<static_tracepoint_marker> &vec)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ struct dummy_target : public target_ops
|
|||||||
int remove_mask_watchpoint (CORE_ADDR arg0, CORE_ADDR arg1, enum target_hw_bp_type arg2) override;
|
int remove_mask_watchpoint (CORE_ADDR arg0, CORE_ADDR arg1, enum target_hw_bp_type arg2) override;
|
||||||
bool stopped_by_watchpoint () override;
|
bool stopped_by_watchpoint () override;
|
||||||
bool have_steppable_watchpoint () override;
|
bool have_steppable_watchpoint () override;
|
||||||
bool stopped_data_address (CORE_ADDR *arg0) override;
|
std::vector<CORE_ADDR> stopped_data_addresses () override;
|
||||||
bool watchpoint_addr_within_range (CORE_ADDR arg0, CORE_ADDR arg1, int arg2) override;
|
bool watchpoint_addr_within_range (CORE_ADDR arg0, CORE_ADDR arg1, int arg2) override;
|
||||||
int region_ok_for_hw_watchpoint (CORE_ADDR arg0, int arg1) override;
|
int region_ok_for_hw_watchpoint (CORE_ADDR arg0, int arg1) override;
|
||||||
bool can_accel_watchpoint_condition (CORE_ADDR arg0, int arg1, int arg2, struct expression *arg3) override;
|
bool can_accel_watchpoint_condition (CORE_ADDR arg0, int arg1, int arg2, struct expression *arg3) override;
|
||||||
@@ -237,7 +237,7 @@ struct debug_target : public target_ops
|
|||||||
int remove_mask_watchpoint (CORE_ADDR arg0, CORE_ADDR arg1, enum target_hw_bp_type arg2) override;
|
int remove_mask_watchpoint (CORE_ADDR arg0, CORE_ADDR arg1, enum target_hw_bp_type arg2) override;
|
||||||
bool stopped_by_watchpoint () override;
|
bool stopped_by_watchpoint () override;
|
||||||
bool have_steppable_watchpoint () override;
|
bool have_steppable_watchpoint () override;
|
||||||
bool stopped_data_address (CORE_ADDR *arg0) override;
|
std::vector<CORE_ADDR> stopped_data_addresses () override;
|
||||||
bool watchpoint_addr_within_range (CORE_ADDR arg0, CORE_ADDR arg1, int arg2) override;
|
bool watchpoint_addr_within_range (CORE_ADDR arg0, CORE_ADDR arg1, int arg2) override;
|
||||||
int region_ok_for_hw_watchpoint (CORE_ADDR arg0, int arg1) override;
|
int region_ok_for_hw_watchpoint (CORE_ADDR arg0, int arg1) override;
|
||||||
bool can_accel_watchpoint_condition (CORE_ADDR arg0, int arg1, int arg2, struct expression *arg3) override;
|
bool can_accel_watchpoint_condition (CORE_ADDR arg0, int arg1, int arg2, struct expression *arg3) override;
|
||||||
@@ -1020,28 +1020,27 @@ debug_target::have_steppable_watchpoint ()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
std::vector<CORE_ADDR>
|
||||||
target_ops::stopped_data_address (CORE_ADDR *arg0)
|
target_ops::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
return this->beneath ()->stopped_data_address (arg0);
|
return this->beneath ()->stopped_data_addresses ();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
std::vector<CORE_ADDR>
|
||||||
dummy_target::stopped_data_address (CORE_ADDR *arg0)
|
dummy_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
return false;
|
return std::vector<CORE_ADDR> ();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
std::vector<CORE_ADDR>
|
||||||
debug_target::stopped_data_address (CORE_ADDR *arg0)
|
debug_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
target_debug_printf_nofunc ("-> %s->stopped_data_address (...)", this->beneath ()->shortname ());
|
target_debug_printf_nofunc ("-> %s->stopped_data_addresses (...)", this->beneath ()->shortname ());
|
||||||
bool result
|
std::vector<CORE_ADDR> result
|
||||||
= this->beneath ()->stopped_data_address (arg0);
|
= this->beneath ()->stopped_data_addresses ();
|
||||||
target_debug_printf_nofunc ("<- %s->stopped_data_address (%s) = %s",
|
target_debug_printf_nofunc ("<- %s->stopped_data_addresses () = %s",
|
||||||
this->beneath ()->shortname (),
|
this->beneath ()->shortname (),
|
||||||
target_debug_print_CORE_ADDR_p (arg0).c_str (),
|
target_debug_print_std_vector_CORE_ADDR (result).c_str ());
|
||||||
target_debug_print_bool (result).c_str ());
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
25
gdb/target.h
25
gdb/target.h
@@ -601,8 +601,8 @@ struct target_ops
|
|||||||
TARGET_DEFAULT_RETURN (false);
|
TARGET_DEFAULT_RETURN (false);
|
||||||
virtual bool have_steppable_watchpoint ()
|
virtual bool have_steppable_watchpoint ()
|
||||||
TARGET_DEFAULT_RETURN (false);
|
TARGET_DEFAULT_RETURN (false);
|
||||||
virtual bool stopped_data_address (CORE_ADDR *)
|
virtual std::vector<CORE_ADDR> stopped_data_addresses ()
|
||||||
TARGET_DEFAULT_RETURN (false);
|
TARGET_DEFAULT_RETURN (std::vector<CORE_ADDR> ());
|
||||||
virtual bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int)
|
virtual bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int)
|
||||||
TARGET_DEFAULT_FUNC (default_watchpoint_addr_within_range);
|
TARGET_DEFAULT_FUNC (default_watchpoint_addr_within_range);
|
||||||
|
|
||||||
@@ -2169,11 +2169,22 @@ extern int target_remove_hw_breakpoint (gdbarch *gdbarch,
|
|||||||
|
|
||||||
extern int target_ranged_break_num_registers (void);
|
extern int target_ranged_break_num_registers (void);
|
||||||
|
|
||||||
/* Return non-zero if target knows the data address which triggered this
|
/* Return a vector containing the data addresses which triggered this
|
||||||
target_stopped_by_watchpoint, in such case place it to *ADDR_P. Only the
|
target_stopped_by_watchpoint if the addresses are known. If the
|
||||||
INFERIOR_PTID task is being queried. */
|
addresses are not known then an empty vector is returned. Only the
|
||||||
#define target_stopped_data_address(target, addr_p) \
|
INFERIOR_PTID task is being queried.
|
||||||
(target)->stopped_data_address (addr_p)
|
|
||||||
|
Some target, for example AArch64, can only watch ranges of memory,
|
||||||
|
e.g. 8 or 16 bytes. As a result, many watchpoints could fall within any
|
||||||
|
single region. In such a case, this method will return the address of
|
||||||
|
all possible watchpoints, and it is up to GDB core to select a suitable
|
||||||
|
watchpoint to display to the user, for example, by checking the value of
|
||||||
|
write watchpoints. Or GDB core could tell the user that it is unable to
|
||||||
|
disambiguate between multiple read watchpoints (though this isn't
|
||||||
|
currently done). */
|
||||||
|
|
||||||
|
#define target_stopped_data_addresses(target) \
|
||||||
|
(target)->stopped_data_addresses ()
|
||||||
|
|
||||||
/* Return non-zero if ADDR is within the range of a watchpoint spanning
|
/* Return non-zero if ADDR is within the range of a watchpoint spanning
|
||||||
LENGTH bytes beginning at START. */
|
LENGTH bytes beginning at START. */
|
||||||
|
|||||||
72
gdb/testsuite/gdb.base/watchpoint-adjacent.c
Normal file
72
gdb/testsuite/gdb.base/watchpoint-adjacent.c
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/* This testcase is part of GDB, the GNU debugger.
|
||||||
|
|
||||||
|
Copyright 2025 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 <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
typedef unsigned long long type_ll;
|
||||||
|
|
||||||
|
#ifndef VAR_TYPE
|
||||||
|
# error "VAR_TYPE not defined"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Place A and B within this wrapper struct. FIRST ensures that A is
|
||||||
|
(usually) going to start at an 8-byte boundary. The goal here is
|
||||||
|
that, when VAR_TYPE is less than 8 bytes, both A and B are placed
|
||||||
|
within the same 8-byte region, and that the region starts at an
|
||||||
|
8-byte boundary. */
|
||||||
|
|
||||||
|
struct wrapper
|
||||||
|
{
|
||||||
|
unsigned long long first;
|
||||||
|
|
||||||
|
VAR_TYPE a, b;
|
||||||
|
};
|
||||||
|
|
||||||
|
volatile struct wrapper obj;
|
||||||
|
|
||||||
|
/* Write to obj.a and obj.b, but don't read these fields. */
|
||||||
|
void
|
||||||
|
writer (void)
|
||||||
|
{
|
||||||
|
obj.a = 1;
|
||||||
|
obj.b = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read from obj.a and obj.b, but don't write to these fields. */
|
||||||
|
int
|
||||||
|
reader (void)
|
||||||
|
{
|
||||||
|
int v = obj.b - obj.a;
|
||||||
|
v--;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
/* Ensure that obj.a, obj.b, and obj.c were placed as we needed. */
|
||||||
|
assert ((((uintptr_t) &obj.a) & 0x7) == 0);
|
||||||
|
assert ((((uintptr_t) &obj.a) + sizeof (obj.a)) == (((uintptr_t) &obj.b)));
|
||||||
|
assert (sizeof (obj.a) == sizeof (obj.b));
|
||||||
|
|
||||||
|
writer ();
|
||||||
|
|
||||||
|
int val = reader (); /* Break for read test. */
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
182
gdb/testsuite/gdb.base/watchpoint-adjacent.exp
Normal file
182
gdb/testsuite/gdb.base/watchpoint-adjacent.exp
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
# Copyright 2025 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/>.
|
||||||
|
|
||||||
|
# The inferior has two adjacent variables. We a 'watch' on one field,
|
||||||
|
# and an 'rwatch' on the other. Running the inferior writes to both
|
||||||
|
# fields. Check GDB reports the expected 'watch' watchpoint.
|
||||||
|
#
|
||||||
|
# Multiple inferiors are compiled, using a variety of types for the
|
||||||
|
# two fields.
|
||||||
|
|
||||||
|
require allow_hw_watchpoint_multi_tests
|
||||||
|
|
||||||
|
standard_testfile
|
||||||
|
|
||||||
|
# When printing a value, for some variable types, GDB will add a
|
||||||
|
# suffix containing an alternative representation of the value. For
|
||||||
|
# example, characters will be printed as decimal, and then as the
|
||||||
|
# character.
|
||||||
|
#
|
||||||
|
# Return a regexp to match the suffix for a variable of VAR_TYPE.
|
||||||
|
# This doesn't match the specific value contents, it will match all
|
||||||
|
# possible suffix values for something of VAR_TYPE.
|
||||||
|
proc get_value_suffix { var_type } {
|
||||||
|
if { $var_type eq "char" } {
|
||||||
|
set suffix " '\[^'\]+'"
|
||||||
|
} else {
|
||||||
|
set suffix ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return $suffix
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start BINFILE, then set a watch and rwatch watchpoint on WATCH_VAR
|
||||||
|
# and RWATCH_VAR respectively. Continue the inferior and expect to
|
||||||
|
# see GDB stop due to WATCH_VAR being written too.
|
||||||
|
proc run_write_test { binfile var_type watch_var rwatch_var } {
|
||||||
|
clean_restart $binfile
|
||||||
|
|
||||||
|
if { ![runto_main] } {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_breakpoints
|
||||||
|
|
||||||
|
gdb_test_no_output "set breakpoint always-inserted on"
|
||||||
|
|
||||||
|
gdb_test "watch obj.$watch_var" \
|
||||||
|
"Hardware watchpoint $::decimal: obj.$watch_var"
|
||||||
|
set wp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*"]
|
||||||
|
gdb_test "rwatch obj.$rwatch_var" \
|
||||||
|
"Hardware read watchpoint $::decimal: obj.$rwatch_var"
|
||||||
|
|
||||||
|
if { $watch_var eq "a" } {
|
||||||
|
set new_val 1
|
||||||
|
} else {
|
||||||
|
set new_val 2
|
||||||
|
}
|
||||||
|
|
||||||
|
set suffix [get_value_suffix $var_type]
|
||||||
|
|
||||||
|
gdb_test "continue" \
|
||||||
|
[multi_line \
|
||||||
|
"Hardware watchpoint $wp_num: obj.$watch_var" \
|
||||||
|
"" \
|
||||||
|
"Old value = 0${suffix}" \
|
||||||
|
"New value = ${new_val}${suffix}" \
|
||||||
|
".*"]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start BINFILE, continue until the call to the `reader` function in
|
||||||
|
# the inferior. Then create an 'rwatch' watchpoint on RWATCH var,
|
||||||
|
# which will be either 'a' or 'b'. Next create 'watch' watchpoints on
|
||||||
|
# both the 'a' and 'b' variables, watching for writes.
|
||||||
|
#
|
||||||
|
# Continue the inferior, both 'a' and 'b' are read, and GDB should stop
|
||||||
|
# and let us know that we stopped at the 'rwatch' watchpoint.
|
||||||
|
#
|
||||||
|
# On some architectures, for some variable sizes, the hardware cannot
|
||||||
|
# figure out which watchpoint triggered as the hardware can only watch
|
||||||
|
# (for example) 8-byte memory blocks. In this case GDB just reports
|
||||||
|
# the first watchpoint (in creation order) within the block. For this
|
||||||
|
# reason the test creates the 'rwatch' watchpoint first.
|
||||||
|
proc run_read_test { binfile var_type rwatch_var rwatch_first watch_vars } {
|
||||||
|
clean_restart $binfile
|
||||||
|
|
||||||
|
if { ![runto_main] } {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gdb_breakpoint [gdb_get_line_number "Break for read test"]
|
||||||
|
gdb_continue_to_breakpoint "prepare for read test"
|
||||||
|
delete_breakpoints
|
||||||
|
|
||||||
|
gdb_test_no_output "set breakpoint always-inserted on"
|
||||||
|
|
||||||
|
if { $rwatch_first } {
|
||||||
|
gdb_test "rwatch obj.${rwatch_var}" \
|
||||||
|
"Hardware read watchpoint $::decimal: obj.$rwatch_var"
|
||||||
|
set wp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*"]
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach v $watch_vars {
|
||||||
|
gdb_test "watch obj.$v" \
|
||||||
|
"Hardware watchpoint $::decimal: obj.$v"
|
||||||
|
}
|
||||||
|
|
||||||
|
if { !$rwatch_first } {
|
||||||
|
gdb_test "rwatch obj.${rwatch_var}" \
|
||||||
|
"Hardware read watchpoint $::decimal: obj.$rwatch_var"
|
||||||
|
set wp_num [get_integer_valueof "\$bpnum" "*UNKNOWN*"]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if { $rwatch_var eq "a" } {
|
||||||
|
set val 1
|
||||||
|
} else {
|
||||||
|
set val 2
|
||||||
|
}
|
||||||
|
|
||||||
|
set suffix [get_value_suffix $var_type]
|
||||||
|
|
||||||
|
gdb_test "continue" \
|
||||||
|
[multi_line \
|
||||||
|
"Hardware read watchpoint ${wp_num}: obj.$rwatch_var" \
|
||||||
|
"" \
|
||||||
|
"Value = ${val}${suffix}" \
|
||||||
|
".*"]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build a binary using VAR_TYPE as the test variable type. Then Call
|
||||||
|
# run_test twice.
|
||||||
|
proc build_and_run_test { var_type } {
|
||||||
|
set filename ${::testfile}-${var_type}
|
||||||
|
set binfile [standard_output_file $filename]
|
||||||
|
|
||||||
|
set flags [list debug additional_flags=-DVAR_TYPE=${var_type}]
|
||||||
|
if {[build_executable "failed to build" $filename $::srcfile $flags]} {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
set test_list [list \
|
||||||
|
{ a {a b} } \
|
||||||
|
{ b {a b} } \
|
||||||
|
{ a {b} } \
|
||||||
|
{ b {a} }]
|
||||||
|
foreach_with_prefix test $test_list {
|
||||||
|
set rwatch_var [lindex $test 0]
|
||||||
|
set watch_vars [lindex $test 1]
|
||||||
|
|
||||||
|
foreach_with_prefix rwatch_first { true false } {
|
||||||
|
run_read_test $binfile $var_type $rwatch_var $rwatch_first $watch_vars
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach test { {a b} {b a} } {
|
||||||
|
set watch_var [lindex $test 0]
|
||||||
|
set rwatch_var [lindex $test 1]
|
||||||
|
|
||||||
|
with_test_prefix "watch: ${watch_var}, rwatch: ${rwatch_var}" {
|
||||||
|
run_write_test $binfile $var_type $watch_var $rwatch_var
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run the test with a series of different types.
|
||||||
|
foreach_with_prefix var_type { type_ll int short char float double } {
|
||||||
|
build_and_run_test $var_type
|
||||||
|
}
|
||||||
@@ -52,14 +52,14 @@ struct x86_linux_nat_target : public x86_nat_target<linux_nat_target>
|
|||||||
bool stopped_by_watchpoint () override
|
bool stopped_by_watchpoint () override
|
||||||
{ return linux_nat_target::stopped_by_watchpoint (); }
|
{ return linux_nat_target::stopped_by_watchpoint (); }
|
||||||
|
|
||||||
bool stopped_data_address (CORE_ADDR *addr_p) override
|
std::vector<CORE_ADDR> stopped_data_addresses () override
|
||||||
{ return linux_nat_target::stopped_data_address (addr_p); }
|
{ return linux_nat_target::stopped_data_addresses (); }
|
||||||
|
|
||||||
bool low_stopped_by_watchpoint () override
|
bool low_stopped_by_watchpoint () override
|
||||||
{ return x86_nat_target::stopped_by_watchpoint (); }
|
{ return x86_nat_target::stopped_by_watchpoint (); }
|
||||||
|
|
||||||
bool low_stopped_data_address (CORE_ADDR *addr_p) override
|
bool low_stopped_data_address (CORE_ADDR *addr_p) override
|
||||||
{ return x86_nat_target::stopped_data_address (addr_p); }
|
{ return x86_stopped_data_address (addr_p); }
|
||||||
|
|
||||||
void low_new_fork (struct lwp_info *parent, pid_t child_pid) override;
|
void low_new_fork (struct lwp_info *parent, pid_t child_pid) override;
|
||||||
|
|
||||||
|
|||||||
@@ -104,8 +104,14 @@ struct x86_nat_target : public BaseTarget
|
|||||||
bool stopped_by_watchpoint () override
|
bool stopped_by_watchpoint () override
|
||||||
{ return x86_stopped_by_watchpoint (); }
|
{ return x86_stopped_by_watchpoint (); }
|
||||||
|
|
||||||
bool stopped_data_address (CORE_ADDR *addr_p) override
|
std::vector<CORE_ADDR> stopped_data_addresses () override
|
||||||
{ return x86_stopped_data_address (addr_p); }
|
{
|
||||||
|
CORE_ADDR addr;
|
||||||
|
if (x86_stopped_data_address (&addr))
|
||||||
|
return { addr };
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
/* A target must provide an implementation of the
|
/* A target must provide an implementation of the
|
||||||
"supports_stopped_by_hw_breakpoint" target method before this
|
"supports_stopped_by_hw_breakpoint" target method before this
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ protected:
|
|||||||
|
|
||||||
bool low_stopped_by_watchpoint () override;
|
bool low_stopped_by_watchpoint () override;
|
||||||
|
|
||||||
CORE_ADDR low_stopped_data_address () override;
|
std::vector<CORE_ADDR> low_stopped_data_addresses () override;
|
||||||
|
|
||||||
bool low_siginfo_fixup (siginfo_t *native, gdb_byte *inf,
|
bool low_siginfo_fixup (siginfo_t *native, gdb_byte *inf,
|
||||||
int direction) override;
|
int direction) override;
|
||||||
@@ -548,10 +548,10 @@ aarch64_remove_non_address_bits (CORE_ADDR pointer)
|
|||||||
return aarch64_remove_top_bits (pointer, mask);
|
return aarch64_remove_top_bits (pointer, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implementation of linux target ops method "low_stopped_data_address". */
|
/* Implementation of linux target ops method "low_stopped_data_addresses". */
|
||||||
|
|
||||||
CORE_ADDR
|
std::vector<CORE_ADDR>
|
||||||
aarch64_target::low_stopped_data_address ()
|
aarch64_target::low_stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
siginfo_t siginfo;
|
siginfo_t siginfo;
|
||||||
struct aarch64_debug_reg_state *state;
|
struct aarch64_debug_reg_state *state;
|
||||||
@@ -559,12 +559,12 @@ aarch64_target::low_stopped_data_address ()
|
|||||||
|
|
||||||
/* Get the siginfo. */
|
/* Get the siginfo. */
|
||||||
if (ptrace (PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0)
|
if (ptrace (PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0)
|
||||||
return (CORE_ADDR) 0;
|
return {};
|
||||||
|
|
||||||
/* Need to be a hardware breakpoint/watchpoint trap. */
|
/* Need to be a hardware breakpoint/watchpoint trap. */
|
||||||
if (siginfo.si_signo != SIGTRAP
|
if (siginfo.si_signo != SIGTRAP
|
||||||
|| (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
|
|| (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
|
||||||
return (CORE_ADDR) 0;
|
return {};
|
||||||
|
|
||||||
/* Make sure to ignore the top byte, otherwise we may not recognize a
|
/* Make sure to ignore the top byte, otherwise we may not recognize a
|
||||||
hardware watchpoint hit. The stopped data addresses coming from the
|
hardware watchpoint hit. The stopped data addresses coming from the
|
||||||
@@ -574,11 +574,7 @@ aarch64_target::low_stopped_data_address ()
|
|||||||
|
|
||||||
/* Check if the address matches any watched address. */
|
/* Check if the address matches any watched address. */
|
||||||
state = aarch64_get_debug_reg_state (current_thread->id.pid ());
|
state = aarch64_get_debug_reg_state (current_thread->id.pid ());
|
||||||
CORE_ADDR result;
|
return aarch64_stopped_data_addresses (state, addr_trap);
|
||||||
if (aarch64_stopped_data_address (state, addr_trap, &result))
|
|
||||||
return result;
|
|
||||||
|
|
||||||
return (CORE_ADDR) 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implementation of linux target ops method "low_stopped_by_watchpoint". */
|
/* Implementation of linux target ops method "low_stopped_by_watchpoint". */
|
||||||
@@ -586,7 +582,7 @@ aarch64_target::low_stopped_data_address ()
|
|||||||
bool
|
bool
|
||||||
aarch64_target::low_stopped_by_watchpoint ()
|
aarch64_target::low_stopped_by_watchpoint ()
|
||||||
{
|
{
|
||||||
return (low_stopped_data_address () != 0);
|
return !low_stopped_data_addresses ().empty ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fetch the thread-local storage pointer for libthread_db. */
|
/* Fetch the thread-local storage pointer for libthread_db. */
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ protected:
|
|||||||
|
|
||||||
bool low_stopped_by_watchpoint () override;
|
bool low_stopped_by_watchpoint () override;
|
||||||
|
|
||||||
CORE_ADDR low_stopped_data_address () override;
|
std::vector<CORE_ADDR> low_stopped_data_addresses () override;
|
||||||
|
|
||||||
arch_process_info *low_new_process () override;
|
arch_process_info *low_new_process () override;
|
||||||
|
|
||||||
@@ -729,11 +729,11 @@ arm_target::low_stopped_by_watchpoint ()
|
|||||||
|
|
||||||
/* Return data address that triggered watchpoint. Called only if
|
/* Return data address that triggered watchpoint. Called only if
|
||||||
low_stopped_by_watchpoint returned true. */
|
low_stopped_by_watchpoint returned true. */
|
||||||
CORE_ADDR
|
std::vector<CORE_ADDR>
|
||||||
arm_target::low_stopped_data_address ()
|
arm_target::low_stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
struct lwp_info *lwp = get_thread_lwp (current_thread);
|
struct lwp_info *lwp = get_thread_lwp (current_thread);
|
||||||
return lwp->arch_private->stopped_data_address;
|
return { lwp->arch_private->stopped_data_address };
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called when a new process is created. */
|
/* Called when a new process is created. */
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ protected:
|
|||||||
|
|
||||||
bool low_stopped_by_watchpoint () override;
|
bool low_stopped_by_watchpoint () override;
|
||||||
|
|
||||||
CORE_ADDR low_stopped_data_address () override;
|
std::vector<CORE_ADDR> low_stopped_data_addresses () override;
|
||||||
|
|
||||||
arch_process_info *low_new_process () override;
|
arch_process_info *low_new_process () override;
|
||||||
|
|
||||||
@@ -555,10 +555,10 @@ loongarch_target::low_remove_point (raw_bkpt_type type, CORE_ADDR addr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Implementation of linux target ops method "low_stopped_data_address". */
|
/* Implementation of linux target ops method "low_stopped_data_addresses". */
|
||||||
|
|
||||||
CORE_ADDR
|
std::vector<CORE_ADDR>
|
||||||
loongarch_target::low_stopped_data_address ()
|
loongarch_target::low_stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
siginfo_t siginfo;
|
siginfo_t siginfo;
|
||||||
struct loongarch_debug_reg_state *state;
|
struct loongarch_debug_reg_state *state;
|
||||||
@@ -566,20 +566,20 @@ loongarch_target::low_stopped_data_address ()
|
|||||||
|
|
||||||
/* Get the siginfo. */
|
/* Get the siginfo. */
|
||||||
if (ptrace (PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0)
|
if (ptrace (PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0)
|
||||||
return (CORE_ADDR) 0;
|
return {};
|
||||||
|
|
||||||
/* Need to be a hardware breakpoint/watchpoint trap. */
|
/* Need to be a hardware breakpoint/watchpoint trap. */
|
||||||
if (siginfo.si_signo != SIGTRAP
|
if (siginfo.si_signo != SIGTRAP
|
||||||
|| (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
|
|| (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
|
||||||
return (CORE_ADDR) 0;
|
return {};
|
||||||
|
|
||||||
/* Check if the address matches any watched address. */
|
/* Check if the address matches any watched address. */
|
||||||
state = loongarch_get_debug_reg_state (current_thread->id.pid ());
|
state = loongarch_get_debug_reg_state (current_thread->id.pid ());
|
||||||
CORE_ADDR result;
|
CORE_ADDR result;
|
||||||
if (loongarch_stopped_data_address (state, (CORE_ADDR) siginfo.si_addr, &result))
|
if (loongarch_stopped_data_address (state, (CORE_ADDR) siginfo.si_addr, &result))
|
||||||
return result;
|
return { result };
|
||||||
|
|
||||||
return (CORE_ADDR) 0;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implementation of linux target ops method "low_stopped_by_watchpoint". */
|
/* Implementation of linux target ops method "low_stopped_by_watchpoint". */
|
||||||
@@ -587,7 +587,7 @@ loongarch_target::low_stopped_data_address ()
|
|||||||
bool
|
bool
|
||||||
loongarch_target::low_stopped_by_watchpoint ()
|
loongarch_target::low_stopped_by_watchpoint ()
|
||||||
{
|
{
|
||||||
return (low_stopped_data_address () != 0);
|
return !low_stopped_data_addresses ().empty ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implementation of linux target ops method "low_new_process". */
|
/* Implementation of linux target ops method "low_new_process". */
|
||||||
|
|||||||
@@ -2194,7 +2194,7 @@ linux_process_target::check_stopped_by_watchpoint (lwp_info *child)
|
|||||||
if (low_stopped_by_watchpoint ())
|
if (low_stopped_by_watchpoint ())
|
||||||
{
|
{
|
||||||
child->stop_reason = TARGET_STOPPED_BY_WATCHPOINT;
|
child->stop_reason = TARGET_STOPPED_BY_WATCHPOINT;
|
||||||
child->stopped_data_address = low_stopped_data_address ();
|
child->stopped_data_addresses = low_stopped_data_addresses ();
|
||||||
}
|
}
|
||||||
|
|
||||||
return child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
|
return child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
|
||||||
@@ -2206,10 +2206,10 @@ linux_process_target::low_stopped_by_watchpoint ()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CORE_ADDR
|
std::vector<CORE_ADDR>
|
||||||
linux_process_target::low_stopped_data_address ()
|
linux_process_target::low_stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
return 0;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return the ptrace options that we want to try to enable. */
|
/* Return the ptrace options that we want to try to enable. */
|
||||||
@@ -5641,12 +5641,12 @@ linux_process_target::stopped_by_watchpoint ()
|
|||||||
return lwp->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
|
return lwp->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
CORE_ADDR
|
std::vector<CORE_ADDR>
|
||||||
linux_process_target::stopped_data_address ()
|
linux_process_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
struct lwp_info *lwp = get_thread_lwp (current_thread);
|
struct lwp_info *lwp = get_thread_lwp (current_thread);
|
||||||
|
|
||||||
return lwp->stopped_data_address;
|
return lwp->stopped_data_addresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is only used for targets that define PT_TEXT_ADDR,
|
/* This is only used for targets that define PT_TEXT_ADDR,
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ public:
|
|||||||
|
|
||||||
bool stopped_by_watchpoint () override;
|
bool stopped_by_watchpoint () override;
|
||||||
|
|
||||||
CORE_ADDR stopped_data_address () override;
|
std::vector<CORE_ADDR> stopped_data_addresses () override;
|
||||||
|
|
||||||
bool supports_read_offsets () override;
|
bool supports_read_offsets () override;
|
||||||
|
|
||||||
@@ -652,7 +652,7 @@ protected:
|
|||||||
|
|
||||||
virtual bool low_stopped_by_watchpoint ();
|
virtual bool low_stopped_by_watchpoint ();
|
||||||
|
|
||||||
virtual CORE_ADDR low_stopped_data_address ();
|
virtual std::vector<CORE_ADDR> low_stopped_data_addresses ();
|
||||||
|
|
||||||
/* Hooks to reformat register data for PEEKUSR/POKEUSR (in particular
|
/* Hooks to reformat register data for PEEKUSR/POKEUSR (in particular
|
||||||
for registers smaller than an xfer unit). */
|
for registers smaller than an xfer unit). */
|
||||||
@@ -858,10 +858,9 @@ struct lwp_info
|
|||||||
enum target_stop_reason stop_reason = TARGET_STOPPED_BY_NO_REASON;
|
enum target_stop_reason stop_reason = TARGET_STOPPED_BY_NO_REASON;
|
||||||
|
|
||||||
/* On architectures where it is possible to know the data address of
|
/* On architectures where it is possible to know the data address of
|
||||||
a triggered watchpoint, STOPPED_DATA_ADDRESS is non-zero, and
|
a triggered watchpoint, STOPPED_DATA_ADDRESS is the list of such
|
||||||
contains such data address. Only valid if STOPPED_BY_WATCHPOINT
|
data addresses. Only valid if STOPPED_BY_WATCHPOINT is true. */
|
||||||
is true. */
|
std::vector<CORE_ADDR> stopped_data_addresses;
|
||||||
CORE_ADDR stopped_data_address = 0;
|
|
||||||
|
|
||||||
/* If this is non-zero, it is a breakpoint to be reinserted at our next
|
/* If this is non-zero, it is a breakpoint to be reinserted at our next
|
||||||
stop (SIGTRAP stops only). */
|
stop (SIGTRAP stops only). */
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ protected:
|
|||||||
|
|
||||||
bool low_stopped_by_watchpoint () override;
|
bool low_stopped_by_watchpoint () override;
|
||||||
|
|
||||||
CORE_ADDR low_stopped_data_address () override;
|
std::vector<CORE_ADDR> low_stopped_data_addresses () override;
|
||||||
|
|
||||||
void low_collect_ptrace_register (regcache *regcache, int regno,
|
void low_collect_ptrace_register (regcache *regcache, int regno,
|
||||||
char *buf) override;
|
char *buf) override;
|
||||||
@@ -658,10 +658,10 @@ mips_target::low_stopped_by_watchpoint ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* This is the implementation of linux target ops method
|
/* This is the implementation of linux target ops method
|
||||||
low_stopped_data_address. */
|
low_stopped_data_addresses. */
|
||||||
|
|
||||||
CORE_ADDR
|
std::vector<CORE_ADDR>
|
||||||
mips_target::low_stopped_data_address ()
|
mips_target::low_stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
struct process_info *proc = current_process ();
|
struct process_info *proc = current_process ();
|
||||||
struct arch_process_info *priv = proc->priv->arch_private;
|
struct arch_process_info *priv = proc->priv->arch_private;
|
||||||
@@ -679,7 +679,7 @@ mips_target::low_stopped_data_address ()
|
|||||||
&priv->watch_readback,
|
&priv->watch_readback,
|
||||||
&priv->watch_readback_valid,
|
&priv->watch_readback_valid,
|
||||||
0))
|
0))
|
||||||
return 0;
|
return {};
|
||||||
|
|
||||||
num_valid = mips_linux_watch_get_num_valid (&priv->watch_readback);
|
num_valid = mips_linux_watch_get_num_valid (&priv->watch_readback);
|
||||||
|
|
||||||
@@ -711,12 +711,12 @@ mips_target::low_stopped_data_address ()
|
|||||||
}
|
}
|
||||||
/* Check for overlap of even a single byte. */
|
/* Check for overlap of even a single byte. */
|
||||||
if (last_byte >= t_low && addr <= t_low + t_hi)
|
if (last_byte >= t_low && addr <= t_low + t_hi)
|
||||||
return addr;
|
return { addr };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Shouldn't happen. */
|
/* Shouldn't happen. */
|
||||||
return 0;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fetch the thread-local storage pointer for libthread_db. */
|
/* Fetch the thread-local storage pointer for libthread_db. */
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ protected:
|
|||||||
|
|
||||||
bool low_stopped_by_watchpoint () override;
|
bool low_stopped_by_watchpoint () override;
|
||||||
|
|
||||||
CORE_ADDR low_stopped_data_address () override;
|
std::vector<CORE_ADDR> low_stopped_data_addresses () override;
|
||||||
|
|
||||||
/* collect_ptrace_register/supply_ptrace_register are not needed in the
|
/* collect_ptrace_register/supply_ptrace_register are not needed in the
|
||||||
native i386 case (no registers smaller than an xfer unit), and are not
|
native i386 case (no registers smaller than an xfer unit), and are not
|
||||||
@@ -712,15 +712,15 @@ x86_target::low_stopped_by_watchpoint ()
|
|||||||
return x86_dr_stopped_by_watchpoint (&proc->priv->arch_private->debug_reg_state);
|
return x86_dr_stopped_by_watchpoint (&proc->priv->arch_private->debug_reg_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
CORE_ADDR
|
std::vector<CORE_ADDR>
|
||||||
x86_target::low_stopped_data_address ()
|
x86_target::low_stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
struct process_info *proc = current_process ();
|
struct process_info *proc = current_process ();
|
||||||
CORE_ADDR addr;
|
CORE_ADDR addr;
|
||||||
if (x86_dr_stopped_data_address (&proc->priv->arch_private->debug_reg_state,
|
if (x86_dr_stopped_data_address (&proc->priv->arch_private->debug_reg_state,
|
||||||
&addr))
|
&addr))
|
||||||
return addr;
|
return { addr };
|
||||||
return 0;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called when a new process is created. */
|
/* Called when a new process is created. */
|
||||||
|
|||||||
@@ -1182,22 +1182,22 @@ prepare_resume_reply (char *buf, ptid_t ptid, const target_waitstatus &status)
|
|||||||
|
|
||||||
if (the_target->stopped_by_watchpoint ())
|
if (the_target->stopped_by_watchpoint ())
|
||||||
{
|
{
|
||||||
CORE_ADDR addr;
|
std::vector<CORE_ADDR> addr_vec = the_target->stopped_data_addresses ();
|
||||||
int i;
|
|
||||||
|
|
||||||
|
for (const CORE_ADDR addr : addr_vec)
|
||||||
|
{
|
||||||
memcpy (buf, "watch:", 6);
|
memcpy (buf, "watch:", 6);
|
||||||
buf += 6;
|
buf += 6;
|
||||||
|
|
||||||
addr = the_target->stopped_data_address ();
|
|
||||||
|
|
||||||
/* Convert each byte of the address into two hexadecimal
|
/* Convert each byte of the address into two hexadecimal
|
||||||
chars. Note that we take sizeof (void *) instead of
|
chars. Note that we take sizeof (void *) instead of
|
||||||
sizeof (addr); this is to avoid sending a 64-bit
|
sizeof (addr); this is to avoid sending a 64-bit
|
||||||
address to a 32-bit GDB. */
|
address to a 32-bit GDB. */
|
||||||
for (i = sizeof (void *) * 2; i > 0; i--)
|
for (int i = sizeof (void *) * 2; i > 0; i--)
|
||||||
*buf++ = tohex ((addr >> (i - 1) * 4) & 0xf);
|
*buf++ = tohex ((addr >> (i - 1) * 4) & 0xf);
|
||||||
*buf++ = ';';
|
*buf++ = ';';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (cs.swbreak_feature && target_stopped_by_sw_breakpoint ())
|
else if (cs.swbreak_feature && target_stopped_by_sw_breakpoint ())
|
||||||
{
|
{
|
||||||
sprintf (buf, "swbreak:;");
|
sprintf (buf, "swbreak:;");
|
||||||
|
|||||||
@@ -409,10 +409,10 @@ process_stratum_target::stopped_by_watchpoint ()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CORE_ADDR
|
std::vector<CORE_ADDR>
|
||||||
process_stratum_target::stopped_data_address ()
|
process_stratum_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
return 0;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|||||||
@@ -218,9 +218,9 @@ public:
|
|||||||
otherwise. */
|
otherwise. */
|
||||||
virtual bool stopped_by_watchpoint ();
|
virtual bool stopped_by_watchpoint ();
|
||||||
|
|
||||||
/* Returns the address associated with the watchpoint that hit, if any;
|
/* Returns the list of addresses associated with the watchpoint(s)
|
||||||
returns 0 otherwise. */
|
that were hit, if any; returns an empty vector otherwise. */
|
||||||
virtual CORE_ADDR stopped_data_address ();
|
virtual std::vector<CORE_ADDR> stopped_data_addresses ();
|
||||||
|
|
||||||
/* Return true if the read_offsets target op is supported. */
|
/* Return true if the read_offsets target op is supported. */
|
||||||
virtual bool supports_read_offsets ();
|
virtual bool supports_read_offsets ();
|
||||||
|
|||||||
@@ -240,13 +240,13 @@ win32_process_target::stopped_by_watchpoint ()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CORE_ADDR
|
std::vector<CORE_ADDR>
|
||||||
win32_process_target::stopped_data_address ()
|
win32_process_target::stopped_data_addresses ()
|
||||||
{
|
{
|
||||||
if (the_low_target.stopped_data_address != NULL)
|
if (the_low_target.stopped_data_address != NULL)
|
||||||
return the_low_target.stopped_data_address ();
|
return { the_low_target.stopped_data_address () };
|
||||||
else
|
else
|
||||||
return 0;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ public:
|
|||||||
|
|
||||||
bool stopped_by_watchpoint () override;
|
bool stopped_by_watchpoint () override;
|
||||||
|
|
||||||
CORE_ADDR stopped_data_address () override;
|
std::vector<CORE_ADDR> stopped_data_addresses () override;
|
||||||
|
|
||||||
bool supports_qxfer_siginfo () override;
|
bool supports_qxfer_siginfo () override;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user