This is the mail archive of the gdb-patches@sourceware.cygnus.com 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]

A patch for ia32 hardware watchpoint.


This is a patch for

http://sourceware.cygnus.com/ml/gdb/2000-q1/msg00564.html

I only did it for Linux since it is not a perfect solution.

Thanks.


---
2000-03-07  H.J. Lu  <hjl@gnu.org>

	* breakpoint.c (insert_breakpoints): Also pass b->number to
	target_insert_watchpoint () if NEED_WATCHPOINT_NUMBER is
	defined. Don't return error if errno is EBUSY.
	(remove_breakpoint): Also pass b->number to
	target_remove_watchpoint () if NEED_WATCHPOINT_NUMBER is
	defined.
	(delete_breakpoint): Call target_delete_watchpoint () for
	watchpoint if NEED_WATCHPOINT_NUMBER is defined.

	* breakpoint.c (TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT):
	Modified to only take "struct value *".
	(TARGET_REGION_OK_FOR_HW_WATCHPOINT): Likewise.
	(can_use_hardware_watchpoint): Likewise.
	* config/i386/nm-go32.h (TARGET_REGION_OK_FOR_HW_WATCHPOINT):
	Likwise.
	* config/pa/nm-hppah.h (TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT):
	Likwise.

	* i386-linux-nat.c (i386_register_u_addr): Copied from
	i386v-nat.c.
	(kernel_u_size): Likewise.
	Copy hardware watchpoint runtines from i386v-nat.c and modify.

	* config/i386/linux.mh (NATDEPFILES): Remove i386v-nat.o.

	* config/i386/nm-linux.h (NEED_WATCHPOINT_NUMBER): Defined.
	(target_insert_watchpoint): Add breakpoint number.
	(target_remove_watchpoint): Likewise.
	(target_delete_watchpoint): New.

	* config/i386/tm-linux.h (TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT):
	Also return 1 for long long, double and long double.

Index: breakpoint.c
===================================================================
RCS file: /work/cvs/gnu/gdb/gdb/breakpoint.c,v
retrieving revision 1.1.1.8
diff -u -p -r1.1.1.8 breakpoint.c
--- breakpoint.c	2000/03/07 18:42:09	1.1.1.8
+++ breakpoint.c	2000/03/07 18:44:05
@@ -984,7 +984,12 @@ insert_breakpoints ()
 		    else if (b->type == bp_access_watchpoint)
 		      type = hw_access;
 
+#ifdef NEED_WATCHPOINT_NUMBER
+		    val = target_insert_watchpoint (b->number, addr,
+		    				    len, type);
+#else
 		    val = target_insert_watchpoint (addr, len, type);
+#endif
 		    if (val == -1)
 		      {
 			b->inserted = 0;
@@ -1000,8 +1005,11 @@ insert_breakpoints ()
 		remove_breakpoint (b, mark_uninserted);
 		warning ("Could not insert hardware watchpoint %d.",
 			 b->number);
-		val = -1;
-	      }               
+		/* Don't return an error if we fail to insert
+		   a hardware watchpoint due to the limited number
+		   of hardware watchpoints availabel.  */
+		val = (errno == EBUSY) ? 0 : -1;
+	      }
 	  }
 	else
 	  {
@@ -1337,7 +1345,12 @@ remove_breakpoint (b, is)
 	      else if (b->type == bp_access_watchpoint)
 		type = hw_access;
 
+#ifdef NEED_WATCHPOINT_NUMBER
+	      val = target_remove_watchpoint (b->number, addr, len,
+					      type);
+#else
 	      val = target_remove_watchpoint (addr, len, type);
+#endif
 	      if (val == -1)
 		b->inserted = 1;
 	      val = 0;
@@ -5500,13 +5513,13 @@ watch_command_1 (arg, accessflag, from_t
    in hardware return zero.  */
 
 #if !defined(TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT)
-#define TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT(BYTE_SIZE) \
-    ((BYTE_SIZE) <= (REGISTER_SIZE))
+#define TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT(VAL) \
+    (TYPE_LENGTH (VALUE_TYPE (VAL)) <= (REGISTER_SIZE))
 #endif
 
 #if !defined(TARGET_REGION_OK_FOR_HW_WATCHPOINT)
-#define TARGET_REGION_OK_FOR_HW_WATCHPOINT(ADDR,LEN) \
-     TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT(LEN)
+#define TARGET_REGION_OK_FOR_HW_WATCHPOINT(VAL) \
+     TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT(VAL)
 #endif
 
 static int
@@ -5551,10 +5564,7 @@ can_use_hardware_watchpoint (v)
 	    {
 	      /* Ahh, memory we actually used!  Check if we can cover
                  it with hardware watchpoints.  */
-	      CORE_ADDR vaddr = VALUE_ADDRESS (v) + VALUE_OFFSET (v);
-	      int       len   = TYPE_LENGTH (VALUE_TYPE (v));
-
-	      if (!TARGET_REGION_OK_FOR_HW_WATCHPOINT (vaddr, len))
+	      if (!TARGET_REGION_OK_FOR_HW_WATCHPOINT (v))
 		return 0;
 	      else
 		found_memory_cnt++;
@@ -6852,6 +6862,11 @@ delete_breakpoint (bpt)
 
   if (bpt->inserted)
     remove_breakpoint (bpt, mark_uninserted);
+
+#ifdef NEED_WATCHPOINT_NUMBER
+  if (bpt->type == bp_hardware_watchpoint)
+    target_delete_watchpoint (bpt->number);
+#endif
 
   if (breakpoint_chain == bpt)
     breakpoint_chain = bpt->next;
Index: i386-linux-nat.c
===================================================================
RCS file: /work/cvs/gnu/gdb/gdb/i386-linux-nat.c,v
retrieving revision 1.1.1.5
diff -u -p -r1.1.1.5 i386-linux-nat.c
--- i386-linux-nat.c	2000/03/07 18:42:13	1.1.1.5
+++ i386-linux-nat.c	2000/03/07 19:01:36
@@ -35,6 +35,21 @@
 #include <sys/reg.h>
 #endif
 
+/* FIXME: The following used to be just "#include <sys/debugreg.h>", but
+ * the the Linux 2.1.x kernel and glibc 2.0.x are not in sync; including
+ * <sys/debugreg.h> will result in an error.  With luck, these losers
+ * will get their act together and we can trash this hack in the near future.
+ * --jsm 1998-10-21
+ */
+
+#ifdef TARGET_HAS_HARDWARE_WATCHPOINTS
+#ifdef HAVE_ASM_DEBUGREG_H
+#include <asm/debugreg.h>
+#else
+#include <sys/debugreg.h>
+#endif
+#endif
+
 /* On Linux, threads are implemented as pseudo-processes, in which
    case we may be tracing more than one process at a time.  In that
    case, inferior_pid will contain the main process ID and the
@@ -1058,3 +1073,300 @@ _initialize_i386_linux_nat ()
 {
   add_core_fns (&linux_elf_core_fns);
 }
+
+/* blockend is the value of u.u_ar0, and points to the
+ * place where GS is stored
+ */
+
+int
+i386_register_u_addr (blockend, regnum)
+     int blockend;
+     int regnum;
+{
+  struct user u;
+  int fpstate;
+  int ubase;
+
+  ubase = blockend;
+  /* FIXME:  Should have better way to test floating point range */
+  if (regnum >= FP0_REGNUM && regnum <= (FP0_REGNUM + 7))
+    {
+#ifdef KSTKSZ			/* SCO, and others? */
+      ubase += 4 * (SS + 1) - KSTKSZ;
+      fpstate = ubase + ((char *) &u.u_fps.u_fpstate - (char *) &u);
+      return (fpstate + 0x1c + 10 * (regnum - FP0_REGNUM));
+#else
+      fpstate = ubase + ((char *) &u.i387.st_space - (char *) &u);
+      return (fpstate + 10 * (regnum - FP0_REGNUM));
+#endif
+    }
+  else
+    {
+      return (ubase + 4 * regmap[regnum]);
+    }
+
+}
+
+int
+kernel_u_size ()
+{
+  return (sizeof (struct user));
+}
+
+#ifdef TARGET_HAS_HARDWARE_WATCHPOINTS
+
+/* Record the value of the debug control register.  */
+static int debug_control_mirror;
+
+static struct
+{
+  /* Record which address associates with which register.  */
+  CORE_ADDR address;
+  /* Number assigned to distinguish watchpoints.  */
+  int number;
+} address_lookup [DR_LASTADDR - DR_FIRSTADDR + 1];
+
+static int
+i386_insert_aligned_watchpoint PARAMS ((int, int, CORE_ADDR, CORE_ADDR,
+					int, int));
+
+static int
+i386_insert_nonaligned_watchpoint PARAMS ((int, int, CORE_ADDR,
+					   CORE_ADDR, int, int));
+
+/* Insert a watchpoint.  */
+
+int
+i386_insert_watchpoint (pid, num, addr, len, rw)
+     int pid;
+     int num;
+     CORE_ADDR addr;
+     int len;
+     int rw;
+{
+  return i386_insert_aligned_watchpoint (pid, num, addr, addr, len, rw);
+}
+
+static int
+i386_insert_aligned_watchpoint (pid, num, waddr, addr, len, rw)
+     int pid;
+     int num;
+     CORE_ADDR waddr;
+     CORE_ADDR addr;
+     int len;
+     int rw;
+{
+  int i;
+  int read_write_bits, len_bits;
+  int free_debug_register;
+  int register_number;
+
+  switch (len)
+    {
+    case 1: case 2: case 4: case 8: case 10: case 12:
+      break;
+
+    default:
+      /* This is a kludge. x86 only has limited number of hardware
+	 waitpoint registers. If LEN is too big, we will check
+
+	1. If we have seen the same watch point number in
+	   address_lookup, we just return 0 to save the hardware
+	   waitpoint registers for others. We fake it to make gdb
+	   happy.
+	2. Otherwise, return -1 and set errno EBUSY.
+      */
+      for (i = 0; i < DR_LASTADDR - DR_FIRSTADDR + 1; i++)
+	if (address_lookup[i].number == num
+	    && address_lookup[i].address != 0)
+	  return 0;
+
+      errno = EBUSY;
+      return -1;
+      break;
+    }
+
+  /* Look for a free debug register.  */
+  for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
+    {
+      if (address_lookup[i - DR_FIRSTADDR].address == 0
+	  || address_lookup[i - DR_FIRSTADDR].address == addr)
+	break;
+    }
+
+  /* No more debug registers!  */
+  if (i > DR_LASTADDR)
+    {
+      errno = EBUSY;
+      return -1;
+    }
+
+  read_write_bits = (rw & 1) ? DR_RW_READ : DR_RW_WRITE;
+
+  if (len == 1)
+    len_bits = DR_LEN_1;
+  else if (len == 2)
+    {
+      if (addr % 2)
+	return i386_insert_nonaligned_watchpoint (pid, num, waddr,
+						  addr, len, rw);
+      len_bits = DR_LEN_2;
+    }
+
+  else if (len == 4)
+    {
+      if (addr % 4)
+	return i386_insert_nonaligned_watchpoint (pid, num, waddr,
+						  addr, len, rw);
+      len_bits = DR_LEN_4;
+    }
+  else if (len == 8 || len == 10 || len == 12)
+    {
+      /* It should only happen with long long, double or long double.
+	 All should be updated at the same time. We just watch the
+	 first 4 bytes. */
+      len = 4;
+      if (addr % 4)
+	return i386_insert_nonaligned_watchpoint (pid, num, waddr,
+						  addr, len, rw);
+      len_bits = DR_LEN_4;
+    }
+  else
+    return i386_insert_nonaligned_watchpoint (pid, num, waddr, addr,
+					      len, rw);
+  
+  free_debug_register = i;
+  register_number = free_debug_register - DR_FIRSTADDR;
+  debug_control_mirror |=
+    ((read_write_bits | len_bits)
+     << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * register_number));
+  debug_control_mirror |=
+    (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * register_number));
+  debug_control_mirror |= DR_LOCAL_SLOWDOWN;
+  debug_control_mirror &= ~DR_CONTROL_RESERVED;
+
+  ptrace (PT_WRITE_U, pid, offsetof (struct user, u_debugreg[DR_CONTROL]),
+	  debug_control_mirror);
+  ptrace (PT_WRITE_U, pid, offsetof (struct user, u_debugreg[free_debug_register]),
+	  addr);
+
+  /* Record where we came from.  */
+  address_lookup[register_number].address = addr;
+  address_lookup[register_number].number = num;
+  return 0;
+}
+
+static int
+i386_insert_nonaligned_watchpoint (pid, num, waddr, addr, len, rw)
+     int pid;
+     int num;
+     CORE_ADDR waddr;
+     CORE_ADDR addr;
+     int len;
+     int rw;
+{
+  int align;
+  int size;
+  int rv;
+
+  static int size_try_array[16] =
+  {
+    1, 1, 1, 1,			/* trying size one */
+    2, 1, 2, 1,			/* trying size two */
+    2, 1, 2, 1,			/* trying size three */
+    4, 1, 2, 1			/* trying size four */
+  };
+
+  rv = 0;
+  while (len > 0)
+    {
+      align = addr % 4;
+      /* Four is the maximum length for 386.  */
+      size = (len > 4) ? 3 : len - 1;
+      size = size_try_array[size * 4 + align];
+
+      rv = i386_insert_aligned_watchpoint (pid, num, waddr, addr,
+					   size, rw);
+      if (rv)
+	{
+	  i386_remove_watchpoint (pid, num, waddr, size);
+	  return rv;
+	}
+      addr += size;
+      len -= size;
+    }
+  return rv;
+}
+
+/* Remove a watchpoint.  */
+
+int
+i386_remove_watchpoint (pid, num, addr, len)
+     int pid;
+     int num;
+     CORE_ADDR addr;
+     int len;
+{
+  int i;
+  int register_number;
+
+  for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
+    {
+      register_number = i - DR_FIRSTADDR;
+      if (address_lookup[register_number].address == addr)
+	{
+	  debug_control_mirror &=
+	    ~(1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * register_number));
+	  address_lookup[register_number].address = 0;
+	  address_lookup[register_number].number = 0;
+	}
+    }
+  ptrace (PT_WRITE_U, pid, offsetof (struct user, u_debugreg[DR_CONTROL]),
+	  debug_control_mirror);
+  ptrace (PT_WRITE_U, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0);
+
+  return 0;
+}
+
+/* Delete a watchpoint.  */
+
+int
+i386_delete_watchpoint (pid, num)
+     int pid;
+     int num;
+{
+  int i;
+  int retval = -1;
+
+  for (i = 0; i < DR_LASTADDR - DR_FIRSTADDR + 1; i++)
+    if (address_lookup[i].number == num
+	&& address_lookup[i].address != 0)
+      {
+	i386_remove_watchpoint (pid, num, address_lookup[i].address, 0);
+	retval = 0;
+      }
+
+  return retval;
+}
+
+/* Check if stopped by a watchpoint.  */
+CORE_ADDR
+i386_stopped_by_watchpoint (pid)
+     int pid;
+{
+  int i;
+  int status;
+
+  status = ptrace (PT_READ_U, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0);
+  ptrace (PT_WRITE_U, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0);
+
+  for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
+    {
+      if (status & (1 << (i - DR_FIRSTADDR)))
+	return address_lookup[i - DR_FIRSTADDR].address;
+    }
+
+  return 0;
+}
+
+#endif /* TARGET_HAS_HARDWARE_WATCHPOINTS */
Index: config/i386/nm-go32.h
===================================================================
RCS file: /work/cvs/gnu/gdb/gdb/config/i386/nm-go32.h,v
retrieving revision 1.1.1.2
diff -u -p -r1.1.1.2 nm-go32.h
--- config/i386/nm-go32.h	2000/03/07 18:42:21	1.1.1.2
+++ config/i386/nm-go32.h	2000/03/07 18:53:48
@@ -44,10 +44,10 @@
 #define TARGET_CAN_USE_HARDWARE_WATCHPOINT(type, cnt, ot) 1
 
 /* Returns non-zero if we can use hardware watchpoints to watch a region
-   whose address is ADDR and whose length is LEN.  */
+   which represents VAL.  */
 
-#define TARGET_REGION_OK_FOR_HW_WATCHPOINT(addr,len) \
-	go32_region_ok_for_watchpoint(addr,len)
+#define TARGET_REGION_OK_FOR_HW_WATCHPOINT(val) \
+	go32_region_ok_for_watchpoint((VALUE_ADDRESS (val) + VALUE_OFFSET (val)),TYPE_LENGTH (VALUE_TYPE (val)))
 extern int go32_region_ok_for_watchpoint (CORE_ADDR, int);
 
 /* After a watchpoint trap, the PC points to the instruction after the
Index: config/i386/linux.mh
===================================================================
RCS file: /work/cvs/gnu/gdb/gdb/config/i386/linux.mh,v
retrieving revision 1.1.1.2
diff -u -p -r1.1.1.2 linux.mh
--- config/i386/linux.mh	2000/01/18 17:07:18	1.1.1.2
+++ config/i386/linux.mh	2000/01/18 17:57:36
@@ -1,10 +1,14 @@
 # Host: Intel 386 running GNU/Linux
 
+# We should use the one in glibc 2.
+REGEX=
+REGEX1=
+
 XM_FILE= xm-linux.h
 XDEPFILES= ser-tcp.o
 
 NAT_FILE= nm-linux.h
 NATDEPFILES= infptrace.o solib.o inftarg.o fork-child.o corelow.o \
-	core-aout.o i386v-nat.o i386-linux-nat.o linux-thread.o lin-thread.o
+	core-aout.o i386-linux-nat.o linux-thread.o lin-thread.o
 
 LOADLIBES = -ldl -rdynamic
Index: config/i386/nm-linux.h
===================================================================
RCS file: /work/cvs/gnu/gdb/gdb/config/i386/nm-linux.h,v
retrieving revision 1.1.1.4
diff -u -p -r1.1.1.4 nm-linux.h
--- config/i386/nm-linux.h	2000/03/07 18:42:21	1.1.1.4
+++ config/i386/nm-linux.h	2000/03/07 18:44:07
@@ -46,14 +46,19 @@ extern int kernel_u_size PARAMS ((void))
 #define STOPPED_BY_WATCHPOINT(W)  \
   i386_stopped_by_watchpoint (inferior_pid)
 
-/* Use these macros for watchpoint insertion/removal.  */
+#define NEED_WATCHPOINT_NUMBER
 
-#define target_insert_watchpoint(addr, len, type)  \
-  i386_insert_watchpoint (inferior_pid, addr, len, type)
+/* Use these macros for watchpoint insertion/removal/deletion.  */
 
-#define target_remove_watchpoint(addr, len, type)  \
-  i386_remove_watchpoint (inferior_pid, addr, len)
+#define target_insert_watchpoint(num, addr, len, type)  \
+  i386_insert_watchpoint (inferior_pid, num, addr, len, type)
 
+#define target_remove_watchpoint(num, addr, len, type)  \
+  i386_remove_watchpoint (inferior_pid, num, addr, len)
+
+#define target_delete_watchpoint(num) \
+  i386_delete_watchpoint (inferior_pid, num)
+
 /* We define this if link.h is available, because with ELF we use SVR4 style
    shared libraries. */
 
@@ -74,9 +79,11 @@ extern int kernel_u_size PARAMS ((void))
 
 extern CORE_ADDR
   i386_stopped_by_watchpoint PARAMS ((int));
+extern int
+  i386_insert_watchpoint PARAMS ((int pid, int num, CORE_ADDR addr, int len, int rw));
 extern int
-i386_insert_watchpoint PARAMS ((int pid, CORE_ADDR addr, int len, int rw));
+  i386_remove_watchpoint PARAMS ((int pid, int num, CORE_ADDR addr, int len));
 extern int
-i386_remove_watchpoint PARAMS ((int pid, CORE_ADDR addr, int len));
+  i386_delete_watchpoint PARAMS ((int pid, int num));
 
 #endif /* #ifndef NM_LINUX_H */
Index: config/i386/tm-linux.h
===================================================================
RCS file: /work/cvs/gnu/gdb/gdb/config/i386/tm-linux.h,v
retrieving revision 1.1.1.5
diff -u -p -r1.1.1.5 tm-linux.h
--- config/i386/tm-linux.h	2000/03/07 18:42:21	1.1.1.5
+++ config/i386/tm-linux.h	2000/03/07 18:54:47
@@ -166,4 +166,14 @@ extern CORE_ADDR i386_linux_skip_solib_r
    to be relocated. */
 #define SOFUN_ADDRESS_MAYBE_MISSING
 
+/* Does a value fit in a hardware debug register? Although a long long,
+   double or long double doesn't fit in a register, since x86 has to
+   update all bytes at once, it should be ok to just watch the first
+   few bytes. */
+#undef TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT
+#define TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT(v) \
+  (TYPE_LENGTH (VALUE_TYPE (v)) <= REGISTER_SIZE \
+   || TYPE_CODE (VALUE_TYPE (v)) == TYPE_CODE_INT \
+   || TYPE_CODE (VALUE_TYPE (v)) == TYPE_CODE_FLT)
+
 #endif /* #ifndef TM_LINUX_H */
Index: config/pa/nm-hppah.h
===================================================================
RCS file: /work/cvs/gnu/gdb/gdb/config/pa/nm-hppah.h,v
retrieving revision 1.1.1.3
diff -u -p -r1.1.1.3 nm-hppah.h
--- config/pa/nm-hppah.h	2000/01/18 17:07:28	1.1.1.3
+++ config/pa/nm-hppah.h	2000/01/18 17:08:57
@@ -142,7 +142,7 @@ extern int hppa_require_detach PARAMS ((
 /* The PA can also watch memory regions of arbitrary size, since we're using
    a page-protection scheme.  (On some targets, apparently watch registers
    are used, which can only accomodate regions of REGISTER_SIZE.) */
-#define TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT(byte_count) \
+#define TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT(val) \
         (1)
 
 /* However, some addresses may not be profitable to use hardware to watch,

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