public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: John Baldwin <jhb@FreeBSD.org>
To: gdb-patches@sourceware.org
Subject: [PATCH v2 12/12] Add support for hardware breakpoints/watchpoints on FreeBSD/Aarch64.
Date: Wed, 16 Mar 2022 13:19:23 -0700	[thread overview]
Message-ID: <20220316201923.89694-13-jhb@FreeBSD.org> (raw)
In-Reply-To: <20220316201923.89694-1-jhb@FreeBSD.org>

This shares aarch64-nat.c and nat/aarch64-hw-point.c with the Linux
native target.  Since FreeBSD writes all of the debug registers in one
ptrace op, use an unordered_set<> to track the "dirty" state for
threads rather than bitmasks of modified registers.
---
 gdb/NEWS               |   2 +
 gdb/aarch64-fbsd-nat.c | 260 ++++++++++++++++++++++++++++++++++++++++-
 gdb/configure.nat      |   3 +-
 3 files changed, 263 insertions(+), 2 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index dc2cac1871b..4d05feb4429 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,8 @@
 
 *** Changes since GDB 11
 
+* GDB now supports hardware watchpoints on FreeBSD/Aarch64.
+
 * Improved C++ template support
 
   GDB now treats functions/types involving C++ templates like it does function
diff --git a/gdb/aarch64-fbsd-nat.c b/gdb/aarch64-fbsd-nat.c
index e6ca1196139..99e2bf35276 100644
--- a/gdb/aarch64-fbsd-nat.c
+++ b/gdb/aarch64-fbsd-nat.c
@@ -18,24 +18,60 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "arch-utils.h"
+#include "inferior.h"
 #include "regcache.h"
 #include "target.h"
+#include "nat/aarch64-hw-point.h"
 
-#include <sys/types.h>
+#include <sys/param.h>
 #include <sys/ptrace.h>
+#include <machine/armreg.h>
 #include <machine/reg.h>
 
 #include "fbsd-nat.h"
 #include "aarch64-fbsd-tdep.h"
+#include "aarch64-nat.h"
 #include "inf-ptrace.h"
 
+#if __FreeBSD_version >= 1400005
+#define	HAVE_DBREG
+
+#include <unordered_set>
+#endif
+
+#ifdef HAVE_DBREG
+struct aarch64_fbsd_nat_target final
+  : public aarch64_nat_target<fbsd_nat_target>
+#else
 struct aarch64_fbsd_nat_target final : public fbsd_nat_target
+#endif
 {
   void fetch_registers (struct regcache *, int) override;
   void store_registers (struct regcache *, int) override;
+
+#ifdef HAVE_DBREG
+  /* Hardware breakpoints and watchpoints.  */
+  bool stopped_by_watchpoint () override;
+  bool stopped_data_address (CORE_ADDR *) override;
+  bool stopped_by_hw_breakpoint () override;
+  bool supports_stopped_by_hw_breakpoint () override;
+
+  void post_startup_inferior (ptid_t) override;
+  void post_attach (int pid) override;
+
+  void low_new_fork (ptid_t parent, pid_t child) override;
+  void low_delete_thread (thread_info *) override;
+  void low_prepare_to_resume (thread_info *) override;
+
+private:
+  void probe_debug_regs (int pid);
+  static bool debug_regs_probed;
+#endif
 };
 
 static aarch64_fbsd_nat_target the_aarch64_fbsd_nat_target;
+bool aarch64_fbsd_nat_target::debug_regs_probed;
 
 /* Fetch register REGNUM from the inferior.  If REGNUM is -1, do this
    for all registers.  */
@@ -63,9 +99,231 @@ aarch64_fbsd_nat_target::store_registers (struct regcache *regcache,
 				    PT_SETFPREGS, &aarch64_fbsd_fpregset);
 }
 
+#ifdef HAVE_DBREG
+/* Set of threads which need to update debug registers on next resume.  */
+
+static std::unordered_set<lwpid_t> aarch64_debug_pending_threads;
+
+/* Implement the "stopped_data_address" target_ops method.  */
+
+bool
+aarch64_fbsd_nat_target::stopped_data_address (CORE_ADDR *addr_p)
+{
+  siginfo_t siginfo;
+  struct aarch64_debug_reg_state *state;
+
+  if (!fbsd_nat_get_siginfo (inferior_ptid, &siginfo))
+    return false;
+
+  /* This must be a hardware breakpoint.  */
+  if (siginfo.si_signo != SIGTRAP
+      || siginfo.si_code != TRAP_TRACE
+      || siginfo.si_trapno != EXCP_WATCHPT_EL0)
+    return false;
+
+  const CORE_ADDR addr_trap = (CORE_ADDR) siginfo.si_addr;
+
+  /* Check if the address matches any watched address.  */
+  state = aarch64_get_debug_reg_state (inferior_ptid.pid ());
+  return aarch64_stopped_data_address (state, addr_trap, addr_p);
+}
+
+/* Implement the "stopped_by_watchpoint" target_ops method.  */
+
+bool
+aarch64_fbsd_nat_target::stopped_by_watchpoint ()
+{
+  CORE_ADDR addr;
+
+  return stopped_data_address (&addr);
+}
+
+/* Implement the "stopped_by_hw_breakpoint" target_ops method.  */
+
+bool
+aarch64_fbsd_nat_target::stopped_by_hw_breakpoint ()
+{
+  siginfo_t siginfo;
+  struct aarch64_debug_reg_state *state;
+
+  if (!fbsd_nat_get_siginfo (inferior_ptid, &siginfo))
+    return false;
+
+  /* This must be a hardware breakpoint.  */
+  if (siginfo.si_signo != SIGTRAP
+      || siginfo.si_code != TRAP_TRACE
+      || siginfo.si_trapno != EXCP_WATCHPT_EL0)
+    return false;
+
+  return !stopped_by_watchpoint();
+}
+
+/* Implement the "supports_stopped_by_hw_breakpoint" target_ops method.  */
+
+bool
+aarch64_fbsd_nat_target::supports_stopped_by_hw_breakpoint ()
+{
+  return true;
+}
+
+/* Fetch the hardware debug register capability information.  */
+
+void
+aarch64_fbsd_nat_target::probe_debug_regs (int pid)
+{
+  if (!debug_regs_probed)
+    {
+      struct dbreg reg;
+
+      debug_regs_probed = true;
+      aarch64_num_bp_regs = 0;
+      aarch64_num_wp_regs = 0;
+
+      if (ptrace(PT_GETDBREGS, pid, (PTRACE_TYPE_ARG3) &reg, 0) == 0)
+	{
+	  switch (reg.db_debug_ver)
+	    {
+	    case AARCH64_DEBUG_ARCH_V8:
+	    case AARCH64_DEBUG_ARCH_V8_1:
+	    case AARCH64_DEBUG_ARCH_V8_2:
+	    case AARCH64_DEBUG_ARCH_V8_4:
+	      break;
+	    default:
+	      return;
+	    }
+
+	  aarch64_num_bp_regs = reg.db_nbkpts;
+	  if (aarch64_num_bp_regs > AARCH64_HBP_MAX_NUM)
+	    {
+	      warning (_("Unexpected number of hardware breakpoint registers"
+			 " reported by ptrace, got %d, expected %d."),
+		       aarch64_num_bp_regs, AARCH64_HBP_MAX_NUM);
+	      aarch64_num_bp_regs = AARCH64_HBP_MAX_NUM;
+	    }
+	  aarch64_num_wp_regs = reg.db_nwtpts;
+	  if (aarch64_num_wp_regs > AARCH64_HWP_MAX_NUM)
+	    {
+	      warning (_("Unexpected number of hardware watchpoint registers"
+			 " reported by ptrace, got %d, expected %d."),
+		       aarch64_num_wp_regs, AARCH64_HWP_MAX_NUM);
+	      aarch64_num_wp_regs = AARCH64_HWP_MAX_NUM;
+	    }
+	}
+    }
+}
+
+/* Implement the virtual inf_ptrace_target::post_startup_inferior method.  */
+
+void
+aarch64_fbsd_nat_target::post_startup_inferior (ptid_t ptid)
+{
+  aarch64_remove_debug_reg_state (ptid.pid ());
+  probe_debug_regs (ptid.pid ());
+  fbsd_nat_target::post_startup_inferior (ptid);
+}
+
+/* Implement the "post_attach" target_ops method.  */
+
+void
+aarch64_fbsd_nat_target::post_attach (int pid)
+{
+  aarch64_remove_debug_reg_state (pid);
+  probe_debug_regs (pid);
+  fbsd_nat_target::post_attach (pid);
+}
+
+/* Implement the virtual fbsd_nat_target::low_new_fork method.  */
+
+void
+aarch64_fbsd_nat_target::low_new_fork (ptid_t parent, pid_t child)
+{
+  struct aarch64_debug_reg_state *parent_state, *child_state;
+
+  /* If there is no parent state, no watchpoints nor breakpoints have
+     been set, so there is nothing to do.  */
+  parent_state = aarch64_lookup_debug_reg_state (parent.pid ());
+  if (parent_state == nullptr)
+    return;
+
+  /* The kernel clears debug registers in the new child process after
+     fork, but GDB core assumes the child inherits the watchpoints/hw
+     breakpoints of the parent, and will remove them all from the
+     forked off process.  Copy the debug registers mirrors into the
+     new process so that all breakpoints and watchpoints can be
+     removed together.  */
+
+  child_state = aarch64_get_debug_reg_state (child);
+  *child_state = *parent_state;
+}
+
+/* Mark debug register state "dirty" for all threads belonging to the
+   current inferior.  */
+
+void
+aarch64_notify_debug_reg_change (ptid_t ptid,
+				 int is_watchpoint, unsigned int idx)
+{
+  for (thread_info *tp : current_inferior ()->non_exited_threads ())
+    {
+      if (tp->ptid.lwp_p ())
+	aarch64_debug_pending_threads.emplace (tp->ptid.lwp ());
+    }
+}
+
+/* Implement the virtual fbsd_nat_target::low_delete_thread method.  */
+
+void
+aarch64_fbsd_nat_target::low_delete_thread (thread_info *tp)
+{
+  gdb_assert(tp->ptid.lwp_p ());
+  aarch64_debug_pending_threads.erase (tp->ptid.lwp ());
+}
+
+/* Implement the virtual fbsd_nat_target::low_prepare_to_resume method.  */
+
+void
+aarch64_fbsd_nat_target::low_prepare_to_resume (thread_info *tp)
+{
+  gdb_assert(tp->ptid.lwp_p ());
+
+  if (aarch64_debug_pending_threads.erase (tp->ptid.lwp ()) == 0)
+    return;
+
+  struct aarch64_debug_reg_state *state =
+    aarch64_lookup_debug_reg_state (tp->ptid.pid ());
+  gdb_assert(state != nullptr);
+
+  struct dbreg reg;
+  memset (&reg, 0, sizeof(reg));
+  for (int i = 0; i < aarch64_num_bp_regs; i++)
+    {
+      reg.db_breakregs[i].dbr_addr = state->dr_addr_bp[i];
+      reg.db_breakregs[i].dbr_ctrl = state->dr_ctrl_bp[i];
+    }
+  for (int i = 0; i < aarch64_num_wp_regs; i++)
+    {
+      reg.db_watchregs[i].dbw_addr = state->dr_addr_wp[i];
+      reg.db_watchregs[i].dbw_ctrl = state->dr_ctrl_wp[i];
+    }
+  if (ptrace(PT_SETDBREGS, tp->ptid.lwp (), (PTRACE_TYPE_ARG3) &reg, 0) != 0)
+    error (_("Failed to set hardware debug registers"));
+}
+#else
+/* A stub that should never be called.  */
+void
+aarch64_notify_debug_reg_change (ptid_t ptid,
+				 int is_watchpoint, unsigned int idx)
+{
+  gdb_assert (true);
+}
+#endif
+
 void _initialize_aarch64_fbsd_nat ();
 void
 _initialize_aarch64_fbsd_nat ()
 {
+#ifdef HAVE_DBREG
+  aarch64_initialize_hw_point ();
+#endif
   add_inf_child_target (&the_aarch64_fbsd_nat_target);
 }
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 4f5850dd595..d219d6a960c 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -154,7 +154,8 @@ case ${gdb_host} in
 	case ${gdb_host_cpu} in
 	    aarch64)
 		# Host: FreeBSD/aarch64
-		NATDEPFILES="${NATDEPFILES} aarch64-fbsd-nat.o"
+		NATDEPFILES="${NATDEPFILES} aarch64-nat.o \
+		nat/aarch64-hw-point.o aarch64-fbsd-nat.o"
 		LOADLIBES=
 		;;
 	    arm)
-- 
2.34.1


  parent reply	other threads:[~2022-03-16 20:19 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-16 20:19 [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support John Baldwin
2022-03-16 20:19 ` [PATCH v2 01/12] Remove USE_SIGTRAP_SIGINFO condition for FreeBSD/x86 debug regs support John Baldwin
2022-03-16 20:19 ` [PATCH v2 02/12] x86-nat: Use an unordered_map to store per-pid debug reg state John Baldwin
2022-03-21 18:07   ` Pedro Alves
2022-03-16 20:19 ` [PATCH v2 03/12] x86-nat: Add x86_lookup_debug_reg_state John Baldwin
2022-03-21 18:10   ` Pedro Alves
2022-03-16 20:19 ` [PATCH v2 04/12] Add an x86_fbsd_nat_target mixin class for FreeBSD x86 native targets John Baldwin
2022-03-16 20:19 ` [PATCH v2 05/12] fbsd-nat: Add a low_new_fork virtual method John Baldwin
2022-03-16 20:19 ` [PATCH v2 06/12] x86-fbsd-nat: Copy debug register state on fork John Baldwin
2022-03-16 20:19 ` [PATCH v2 07/12] nat: Split out platform-independent aarch64 debug register support John Baldwin
2022-03-17 15:37   ` Luis Machado
2022-03-16 20:19 ` [PATCH v2 08/12] aarch64: Add an aarch64_nat_target mixin class John Baldwin
2022-03-17 15:35   ` Luis Machado
2022-03-16 20:19 ` [PATCH v2 09/12] fbsd-nat: Add helper routine to fetch siginfo_t for a ptid John Baldwin
2022-03-16 20:19 ` [PATCH v2 10/12] fbsd-nat: Add a low_delete_thread virtual method John Baldwin
2022-03-16 20:19 ` [PATCH v2 11/12] fbsd-nat: Add a low_prepare_to_resume " John Baldwin
2022-03-16 20:19 ` John Baldwin [this message]
2022-03-30 15:23 ` [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support Luis Machado
2022-03-30 15:31   ` Luis Machado

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220316201923.89694-13-jhb@FreeBSD.org \
    --to=jhb@freebsd.org \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).