This is the mail archive of the
gdb-patches@sourceware.cygnus.com
mailing list for the GDB project.
A patch for ia32 hardware watchpoint.
- To: gdb-patches at sourceware dot cygnus dot com
- Subject: A patch for ia32 hardware watchpoint.
- From: "H . J . Lu" <hjl at valinux dot com>
- Date: Tue, 7 Mar 2000 13:26:13 -0800
- Cc: GDB <gdb at sourceware dot cygnus dot com>
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,