diff options
author | joerg <joerg@FreeBSD.org> | 2000-08-17 16:27:26 +0000 |
---|---|---|
committer | joerg <joerg@FreeBSD.org> | 2000-08-17 16:27:26 +0000 |
commit | 362647b5673bc592adda4f718d5850ace6a777ba (patch) | |
tree | 821f20d23cc6257491b4ef06c1679682486285fb /gnu/usr.bin | |
parent | dded6137fc936ede7b68b40ecaf2dfa9e79a4463 (diff) | |
download | FreeBSD-src-362647b5673bc592adda4f718d5850ace6a777ba.zip FreeBSD-src-362647b5673bc592adda4f718d5850ace6a777ba.tar.gz |
Implement the GDB counterpart to use hardware watchpoints in connection
with Brian's kernel support for i386 debug registers. This makes
watchpoints actually usable for real-life problems. Note: you can
only set watchpoints on 1-, 2- or 4-byte locations, gdb automatically
falls back to [sloooow] software watchpoints when attempting to use
them on variables which don't fit into this category. To circumvent
this, one can use the following hack:
watch *(int *)0x<some address>
David O'Brien is IMHO considering to get this fully integrated into the
official GDB, but as long as we've got the i386/* files sitting around
in our private FreeBSD tree here, the feature can now be tested more
extensively, so i'm committing this for the time being.
This work has been done in order to debug a tix toolkit problem, thus
it has been sponsored by teh Deutsche Post AG.
Reviewed by: bsd (not the operating system, but Brian :-)
Diffstat (limited to 'gnu/usr.bin')
-rw-r--r-- | gnu/usr.bin/binutils/gdb/i386/freebsd-nat.c | 223 | ||||
-rw-r--r-- | gnu/usr.bin/binutils/gdb/i386/nm.h | 33 |
2 files changed, 256 insertions, 0 deletions
diff --git a/gnu/usr.bin/binutils/gdb/i386/freebsd-nat.c b/gnu/usr.bin/binutils/gdb/i386/freebsd-nat.c index 8dd81d1..75824d3 100644 --- a/gnu/usr.bin/binutils/gdb/i386/freebsd-nat.c +++ b/gnu/usr.bin/binutils/gdb/i386/freebsd-nat.c @@ -428,3 +428,226 @@ _initialize_core_aout () { add_core_fns (&aout_core_fns); } + +#ifdef PT_GETDBREGS + +/* + * 0: no trace output + * 1: trace watchpoint requests + * 2: trace `watchpoint hit?' tests, too + */ +#define WATCHPOINT_DEBUG 0 + +#include "breakpoint.h" + +int +can_watch(type, cnt, ot) + int type, cnt, ot; +{ + int rv; + static int cnt_watch, cnt_awatch; + + switch (type) + { + case bp_hardware_watchpoint: + cnt_watch = cnt; + break; + + case bp_access_watchpoint: + cnt_awatch = cnt; + break; + + default: + rv = 0; + goto overandout; + } + + rv = cnt_watch + cnt_awatch <= 4? 1: -1; + + overandout: +#if WATCHPOINT_DEBUG + printf_filtered("can_watch(%d, %d, %d) = %d (counts: w: %d, rw: %d)\n", + type, cnt, ot, rv, cnt_watch, cnt_awatch); +#endif + + return rv; +} + +int +stopped_by_watchpoint() +{ + struct dbreg dbr; + extern int inferior_pid; + + if (inferior_pid != 0 && core_bfd == NULL) + { + int pid = inferior_pid & ((1 << 17) - 1); /* XXX extract pid from tid */ + + if (ptrace(PT_GETDBREGS, pid, (caddr_t)&dbr, 0) == -1) + { + perror("ptrace(PT_GETDBREGS) failed"); + return 0; + } +#if WATCHPOINT_DEBUG > 1 + printf_filtered("stopped_by_watchpoint(): DR6 = %#x\n", dbr.dr6); +#endif + /* + * If a hardware watchpoint was hit, one of the lower 4 bits in + * DR6 is set (the actual bit indicates which of DR0...DR3 triggered + * the trap). + */ + return dbr.dr6 & 0x0f; + } + else + { + warning("Can't set a watchpoint on a core file."); + return 0; + } +} + +int +insert_watchpoint(addr, len, type) + int addr, len, type; +{ + struct dbreg dbr; + extern int inferior_pid; + + if (inferior_pid != 0 && core_bfd == NULL) + { + int pid = inferior_pid & ((1 << 17) - 1); /* XXX extract pid from tid */ + int i, mask; + unsigned int sbits; + + if (ptrace(PT_GETDBREGS, pid, (caddr_t)&dbr, 0) == -1) + { + perror("ptrace(PT_GETDBREGS) failed"); + return 0; + } + + for (i = 0, mask = 0x03; i < 4; i++, mask <<= 2) + if ((dbr.dr7 & mask) == 0) + break; + if (i >= 4) { + warning("no more hardware watchpoints available"); + return -1; + } + + /* paranoia */ + if (len > 4) + { + warning("watchpoint length %d unsupported, using lenght = 4", + len); + len = 4; + } + else if (len == 3) + { + warning("weird watchpoint length 3, using 2"); + len = 2; + } + else if (len == 0) + { + warning("weird watchpoint length 0, using 1"); + len = 1; + } + + switch (len) + { + case 1: sbits = 0; break; + case 2: sbits = 4; break; + case 4: sbits = 0x0c; break; + } + + /* + * The `type' value is 0 for `watch on write', 1 for `watch on + * read', 2 for `watch on both'. The i386 debug register + * breakpoint types are 0 for `execute' (not used in GDB), 1 for + * `write', and 4 for `read/write'. Plain `read' trapping is + * not supported on i386, value 3 is illegal. + */ + switch (type) + { + default: + warning("weird watchpoint type %d, using a write watchpoint"); + /* FALLTHROUGH */ + case 0: + sbits |= 1; + break; + + case 2: + sbits |= 3; + break; + } + sbits <<= 4 * i + 16; + sbits |= 1 << 2 * i; + + dbr.dr7 |= sbits; + *(&dbr.dr0 + i) = (unsigned int)addr; + +#if WATCHPOINT_DEBUG + printf_filtered("insert_watchpoint(), inserting DR7 = %#x, DR%d = %#x\n", + dbr.dr7, i, addr); +#endif + if (ptrace(PT_SETDBREGS, pid, (caddr_t)&dbr, 0) == -1) + { + perror("ptrace(PT_SETDBREGS) failed"); + return 0; + } + } + else + { + warning("Can't set a watchpoint on a core file."); + return 0; + } +} + +int +remove_watchpoint(addr, len, type) + int addr, len, type; +{ + struct dbreg dbr; + extern int inferior_pid; + + if (inferior_pid != 0 && core_bfd == NULL) + { + int pid = inferior_pid & ((1 << 17) - 1); /* XXX extract pid from tid */ + int i; + unsigned int sbits, *dbregp; + + if (ptrace(PT_GETDBREGS, pid, (caddr_t)&dbr, 0) == -1) + { + perror("ptrace(PT_GETDBREGS) failed"); + return 0; + } + + for (i = 0, dbregp = &dbr.dr0; i < 4; i++, dbregp++) + if (*dbregp == (unsigned int)addr) + break; + if (i >= 4) + { + warning("watchpoint for address %#x not found", addr); + return -1; + } + + *dbregp = 0; + sbits = 0xf << (4 * i + 16); + sbits |= 3 << 2 * i; + dbr.dr7 &= ~sbits; + +#if WATCHPOINT_DEBUG + printf_filtered("remove_watchpoint(): removing watchpoint for %#x, DR7 = %#x\n", + addr, dbr.dr7); +#endif + if (ptrace(PT_SETDBREGS, pid, (caddr_t)&dbr, 0) == -1) + { + perror("ptrace(PT_SETDBREGS) failed"); + return 0; + } + } + else + { + warning("Can't set a watchpoint on a core file."); + return 0; + } +} + +#endif /* PT_GETDBREGS */ diff --git a/gnu/usr.bin/binutils/gdb/i386/nm.h b/gnu/usr.bin/binutils/gdb/i386/nm.h index 786abc9..65f872b 100644 --- a/gnu/usr.bin/binutils/gdb/i386/nm.h +++ b/gnu/usr.bin/binutils/gdb/i386/nm.h @@ -1,3 +1,4 @@ +/* $FreeBSD$ */ /* Native-dependent definitions for Intel 386 running BSD Unix, for GDB. Copyright 1986, 1987, 1989, 1992, 1996 Free Software Foundation, Inc. @@ -132,4 +133,36 @@ extern int kernel_writablecore; if (!strcmp(STR, "kgdb")) \ kernel_debugging = 1; +#include <sys/types.h> +#include <sys/ptrace.h> + +#ifdef PT_GETDBREGS +#define TARGET_HAS_HARDWARE_WATCHPOINTS + +extern int can_watch PARAMS((int type, int cnt, int othertype)); +extern int stopped_by_watchpoint PARAMS((void)); +extern int insert_watchpoint PARAMS((int addr, int len, int type)); +extern int remove_watchpoint PARAMS((int addr, int len, int type)); + +#define TARGET_CAN_USE_HARDWARE_WATCHPOINT(type, cnt, ot) \ + can_watch(type, cnt, ot) + +/* After a watchpoint trap, the PC points to the instruction after + the one that caused the trap. Therefore we don't need to step over it. + But we do need to reset the status register to avoid another trap. */ +#define HAVE_CONTINUABLE_WATCHPOINT + +#define STOPPED_BY_WATCHPOINT(W) \ + stopped_by_watchpoint() + +/* Use these macros for watchpoint insertion/removal. */ + +#define target_insert_watchpoint(addr, len, type) \ + insert_watchpoint(addr, len, type) + +#define target_remove_watchpoint(addr, len, type) \ + remove_watchpoint(addr, len, type) + +#endif /* PT_GETDBREGS */ + #endif /* NM_FREEBSD_H */ |