This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: RFC: Hardware watchpoint register support for mips-linux.


David Daney wrote:
> This patch adds support for hardware watchpoint registers present in
> some mips processors.  I consider it a very rough first version of the
> patch as there are several things missing before it would be fully
> functional.  However it is sufficient to demonstrate that the kernel
> support is working.
>
> Notes about the patch:
>
> * Corresponding Linux kernel support is needed.  I will post the
> corresponding patches to linux-mips.org shortly.
> * The patch is against gdb 6.8.
> * I need to add the thread creation hook so new threads install the
> watch registers.
> * Logic to cover large regions of memory is missing.
> * The mips specific code should probably be moved to more generic place
> with only linux specific implementations in mips-linux-nat.c as is done
> with i386.
> * Hardware breakpoints are not yet supported.  Since the smallest region
> covers two instructions, I was not sure how to implement it.
> * Probably there are other issues I have not addressed yet.
>
> Comments are welcome.
>
> 2008-04-21  David Daney  <ddaney@avtrex.com>
>
>     * config/mips/linux.mh (NAT_FILE): Change to nm-linux.h.
>     * config/mips/nm-linux.h: New file.
>     * infrun.c (handle_inferior_event): Use software single step when
>     target requires it.
>     * mips-linux-nat.c:  Include command.h and gdbcmd.h.
>     (maint_show_dr): New variable.
>     (PTRACE_GET_WATCH_REGS): New macro.
>     (PTRACE_SET_WATCH_REGS): Same.
>     (enum pt_watch_style): Define.
>     (struct mips32_watch_regs): Define.
>     (struct pt_watch_regs): Define.
>     (MAX_DEBUG_REGISTER): New macro.
>     (watch_mirror): New variable.
>     (mips_show_dr, mips_linux_can_use_hardware_watchpoint,
>     mips_linux_stopped_data_address, mips_linux_stopped_by_watchpoint,
>     mips_linux_region_ok_for_watchpoint, watchlo_val,
>     write_watchpoint_regs, mips_linux_insert_watchpoint,
>     mips_linux_remove_watchpoint): New functions.
>     (_initialize_mips_linux_nat): Register show-debug-regs maintenance
>     command.
>   
I changed the layout of the structures used by ptrace to set the watch
registers in the new version of the kernel patch (posted soon to
linux-mips.org), so here is the corresponding gdb patch.

It is identical to the previous version except for the structure
layout.  My plan is to improve the gdb patch once the Linux kernel patch
gains acceptance.

The change log is the same as the previous version.

David Daney
diff --exclude='*~' -rNup gdb-clean/gdb-6.8/gdb/config/mips/linux.mh gdb-6.8/gdb/config/mips/linux.mh
--- gdb-clean/gdb-6.8/gdb/config/mips/linux.mh	2007-10-15 12:19:17.000000000 -0700
+++ gdb-6.8/gdb/config/mips/linux.mh	2008-04-15 14:25:17.000000000 -0700
@@ -1,5 +1,5 @@
 # Host: Linux/MIPS
-NAT_FILE= config/nm-linux.h
+NAT_FILE= nm-linux.h
 NATDEPFILES= inf-ptrace.o fork-child.o mips-linux-nat.o \
 	linux-thread-db.o proc-service.o gcore.o \
 	linux-nat.o linux-fork.o
diff --exclude='*~' -rNup gdb-clean/gdb-6.8/gdb/config/mips/nm-linux.h gdb-6.8/gdb/config/mips/nm-linux.h
--- gdb-clean/gdb-6.8/gdb/config/mips/nm-linux.h	1969-12-31 16:00:00.000000000 -0800
+++ gdb-6.8/gdb/config/mips/nm-linux.h	2008-04-17 11:35:42.000000000 -0700
@@ -0,0 +1,60 @@
+/* Native support for GNU/Linux mips.
+   Copyright 2008 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef NM_LINUX_H
+#define NM_LINUX_H
+
+#include "config/nm-linux.h"
+
+/* GNU/Linux supports the mips hardware debugging registers.  */
+#define MIPS_USE_GENERIC_WATCHPOINTS
+
+int mips_linux_can_use_hardware_watchpoint (int type, int cnt,
+                                            int ot);
+
+#define TARGET_CAN_USE_HARDWARE_WATCHPOINT(type, cnt, ot) \
+  mips_linux_can_use_hardware_watchpoint (type, cnt, ot)
+
+int mips_linux_region_ok_for_watchpoint (CORE_ADDR addr, int len);
+
+#define TARGET_REGION_OK_FOR_HW_WATCHPOINT(addr, len) \
+  mips_linux_region_ok_for_watchpoint (addr, len)
+
+int mips_linux_stopped_by_watchpoint (void);
+
+#define STOPPED_BY_WATCHPOINT(W) \
+  mips_linux_stopped_by_watchpoint ()
+
+struct target_ops;
+
+int mips_linux_stopped_data_address (CORE_ADDR *paddr);
+
+#define target_stopped_data_address(target, x) \
+  mips_linux_stopped_data_address (x)
+
+int mips_linux_insert_watchpoint (CORE_ADDR addr, int len, int type);
+
+#define target_insert_watchpoint(addr, len, type) \
+  mips_linux_insert_watchpoint (addr, len, type)
+
+int mips_linux_remove_watchpoint (CORE_ADDR addr, int len, int type);
+
+#define target_remove_watchpoint(addr, len, type) \
+  mips_linux_remove_watchpoint (addr, len, type)
+
+#endif /* NM_LINUX_H */
diff --exclude='*~' -rNup gdb-clean/gdb-6.8/gdb/infrun.c gdb-6.8/gdb/infrun.c
--- gdb-clean/gdb-6.8/gdb/infrun.c	2008-01-29 14:47:19.000000000 -0800
+++ gdb-6.8/gdb/infrun.c	2008-04-21 11:01:07.000000000 -0700
@@ -1830,6 +1830,7 @@ handle_inferior_event (struct execution_
       && (HAVE_STEPPABLE_WATCHPOINT
 	  || gdbarch_have_nonsteppable_watchpoint (current_gdbarch)))
     {
+      int step_over_watchpoint = 1;
       if (debug_infrun)
 	fprintf_unfiltered (gdb_stdlog, "infrun: STOPPED_BY_WATCHPOINT\n");
 
@@ -1857,7 +1858,23 @@ handle_inferior_event (struct execution_
       if (!HAVE_STEPPABLE_WATCHPOINT)
 	remove_breakpoints ();
       registers_changed ();
-      target_resume (ecs->ptid, 1, TARGET_SIGNAL_0);	/* Single step */
+
+      if (gdbarch_software_single_step_p (current_gdbarch))
+	{
+	  /* Do it the hard way, w/temp breakpoints */
+	  if (gdbarch_software_single_step (current_gdbarch, get_current_frame ()))
+	    {
+	      /* ...and don't ask hardware to do it.  */
+	      step_over_watchpoint = 0;
+	      /* and do not pull these breakpoints until after a `wait' in
+		 `wait_for_inferior' */
+	      singlestep_breakpoints_inserted_p = 1;
+	      singlestep_ptid = inferior_ptid;
+	      singlestep_pc = read_pc ();
+	    }
+	}
+
+      target_resume (ecs->ptid, step_over_watchpoint, TARGET_SIGNAL_0);	/* Single step */
       ecs->waiton_ptid = ecs->ptid;
       if (HAVE_STEPPABLE_WATCHPOINT)
 	ecs->infwait_state = infwait_step_watch_state;
diff --exclude='*~' -rNup gdb-clean/gdb-6.8/gdb/mips-linux-nat.c gdb-6.8/gdb/mips-linux-nat.c
--- gdb-clean/gdb-6.8/gdb/mips-linux-nat.c	2008-01-01 14:53:12.000000000 -0800
+++ gdb-6.8/gdb/mips-linux-nat.c	2008-04-28 16:18:53.000000000 -0700
@@ -19,6 +19,8 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "command.h"
+#include "gdbcmd.h"
 #include "inferior.h"
 #include "mips-tdep.h"
 #include "target.h"
@@ -44,6 +46,9 @@
    we'll clear this and use PTRACE_PEEKUSER instead.  */
 static int have_ptrace_regsets = 1;
 
+/* Whether or not to print the mirrored debug registers.  */
+static int maint_show_dr;
+
 /* Saved function pointers to fetch and store a single register using
    PTRACE_PEEKUSER and PTRACE_POKEUSER.  */
 
@@ -355,12 +360,225 @@ mips_linux_read_description (struct targ
     return tdesc_mips64_linux;
 }
 
+#ifndef PTRACE_GET_WATCH_REGS
+#  define PTRACE_GET_WATCH_REGS	0xd0
+#endif
+
+#ifndef PTRACE_SET_WATCH_REGS
+#  define PTRACE_SET_WATCH_REGS	0xd1
+#endif
+
+enum pt_watch_style {
+	pt_watch_style_mips32,
+	pt_watch_style_mips64
+};
+struct mips32_watch_regs {
+	unsigned long watchlo[8];
+	unsigned int watchhi[8];
+	unsigned int num_valid;
+	unsigned int reg_mask;
+	unsigned int irw_mask;
+};
+
+struct pt_watch_regs {
+	enum pt_watch_style style;
+	union {
+		struct mips32_watch_regs mips32;
+	};
+};
+
+/* A value of zero in a register indicates that it is available. */
+#define MAX_DEBUG_REGISTER 4
+static struct pt_watch_regs watch_mirror;
+
+static void
+mips_show_dr (const char *func, CORE_ADDR addr,
+	      int len, enum target_hw_bp_type type)
+{
+  int i;
+
+  puts_unfiltered (func);
+  if (addr || len)
+    printf_unfiltered (" (addr=%lx, len=%d, type=%s)",
+		       /* This code is for mips, so casting CORE_ADDR
+			  to unsigned long should be okay.  */
+		       (unsigned long)addr, len,
+		       type == hw_write ? "data-write"
+		       : (type == hw_read ? "data-read"
+			  : (type == hw_access ? "data-read/write"
+			     : (type == hw_execute ? "instruction-execute"
+				/* FIXME: if/when I/O read/write
+				   watchpoints are supported, add them
+				   here.  */
+				: "??unknown??"))));
+  puts_unfiltered (":\n");
+
+  for (i = 0; i < MAX_DEBUG_REGISTER; i++)
+    {
+      printf_unfiltered ("\
+\tDR%d: lo=0x%s, hi=0x%s\n",
+			 i, paddr(watch_mirror.mips32.watchlo[i]),
+			 paddr(watch_mirror.mips32.watchhi[i]));
+    }
+}
+
+int mips_linux_can_use_hardware_watchpoint (int type, int cnt,
+                                            int ot)
+{
+  switch (type)
+    {
+    case bp_hardware_watchpoint:
+    case bp_read_watchpoint:
+    case bp_access_watchpoint:
+      return 1;
+    default:
+      return 0;
+    }
+}
+
+int mips_linux_stopped_data_address (CORE_ADDR *paddr)
+{
+  int i;
+  struct pt_watch_regs watch_readback;
+  int tid = ptid_get_lwp (inferior_ptid);
+
+  if (ptrace (PTRACE_GET_WATCH_REGS, tid, &watch_readback) == -1)
+    {
+      perror_with_name (_("Couldn't read debug register"));
+      return 0;
+    }
+  for (i = 0; i < MAX_DEBUG_REGISTER; i++)
+    {
+      if (watch_readback.mips32.watchhi[i] & 3)
+	{
+	  *paddr = watch_readback.mips32.watchlo[i] & ~7;
+	  return 1;
+	}
+    }
+  return 0;
+}
+
+int mips_linux_stopped_by_watchpoint (void)
+{
+  CORE_ADDR dummy;
+  return mips_linux_stopped_data_address (&dummy);
+}
+
+int mips_linux_region_ok_for_watchpoint (CORE_ADDR addr, int len)
+{
+  return 1;
+}
+
+static unsigned long watchlo_val(CORE_ADDR addr, int type)
+{
+  unsigned long irw;
+
+  switch (type)
+    {
+    case hw_write:
+      irw = 1;
+      break;
+    case hw_read:
+      irw = 2;
+      break;
+    case hw_access:
+      irw = 3;
+      break;
+    default:
+      return 0;
+    }
+
+  return irw | (addr & ~7UL);
+}
+
+static int write_watchpoint_regs(void)
+{
+  struct lwp_info *lp;
+  ptid_t ptid;
+  int tid;
+
+  ALL_LWPS (lp, ptid)
+    {
+      tid = ptid_get_lwp (ptid);
+      if (ptrace (PTRACE_SET_WATCH_REGS, tid, &watch_mirror) == -1)
+	{
+	  perror_with_name (_("Couldn't write debug register"));
+	  return -1;
+	}
+    }
+  return 0;
+}
+
+
+int mips_linux_insert_watchpoint (CORE_ADDR addr, int len, int type)
+{
+  int i;
+  int retval = -1;
+  unsigned long lo_val;
+  struct lwp_info *lp;
+  ptid_t ptid;
+
+  lo_val = watchlo_val(addr, type);
+
+  for (i = 0; i < MAX_DEBUG_REGISTER; i++)
+    {
+      if (watch_mirror.mips32.watchlo[i] == 0)
+	{
+	  watch_mirror.mips32.watchlo[i] = lo_val;
+	  watch_mirror.mips32.watchhi[i] = 0;
+	  retval = write_watchpoint_regs();
+	  break;
+	}
+    }
+
+  if (maint_show_dr)
+    mips_show_dr ("insert_watchpoint", addr, len, type);
+
+  return retval;
+}
+
+int mips_linux_remove_watchpoint (CORE_ADDR addr, int len, int type)
+{
+  int i;
+  int retval = -1;
+  unsigned long lo_val;
+
+  lo_val = watchlo_val(addr, type);
+
+  for (i = 0; i < MAX_DEBUG_REGISTER; i++)
+    {
+      if (watch_mirror.mips32.watchlo[i] == lo_val)
+	{
+            watch_mirror.mips32.watchlo[i] = 0;
+            retval = write_watchpoint_regs();
+        }
+    }
+
+  if (maint_show_dr)
+    mips_show_dr ("remove_watchpoint", addr, len, type);
+
+  return retval;
+}
+
+
 void _initialize_mips_linux_nat (void);
 
 void
 _initialize_mips_linux_nat (void)
 {
-  struct target_ops *t = linux_trad_target (mips_linux_register_u_offset);
+  struct target_ops *t;
+
+  deprecated_add_set_cmd ("show-debug-regs", class_maintenance,
+			  var_boolean, (char *) &maint_show_dr, _("\
+Set whether to show variables that mirror the mips debug registers.\n\
+Use \"on\" to enable, \"off\" to disable.\n\
+If enabled, the debug registers values are shown when GDB inserts\n\
+or removes a hardware breakpoint or watchpoint, and when the inferior\n\
+triggers a breakpoint or watchpoint."),
+			  &maintenancelist);
+
+
+  t = linux_trad_target (mips_linux_register_u_offset);
 
   super_fetch_registers = t->to_fetch_registers;
   super_store_registers = t->to_store_registers;

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]