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 :-)
This commit is contained in:
Joerg Wunsch 2000-08-17 16:27:26 +00:00
parent 8ba8e2ef62
commit 22c68d23f5
2 changed files with 256 additions and 0 deletions

View File

@ -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 */

View File

@ -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 */