This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[Patch] MIPS: Preliminary linux hardware watch register support.
- From: David Daney <ddaney at avtrex dot com>
- To: gdb-patches at sourceware dot org
- Date: Wed, 27 Aug 2008 22:39:33 -0700
- Subject: [Patch] MIPS: Preliminary linux hardware watch register support.
This is a slight modification of the patch I posted last April. It is
close to the minimal amount of code needed to verify that the Linux
kernel patch (still under development) is working, and will need more
work before it would be considered for committing.
The main change from the previous version is that
mips_linux_stopped_data_address() now always returns false as we
cannot accurately determine the location.
Why post it again you ask? To have something to point people at.
Any comments or criticisms (about the patch) are welcome. Note that I
am still aware of Daniel's earlier suggestions, and hope to act on
them soon.
2008-08-27 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.
diff -rcp ../gdb-clean/gdb-6.8/gdb/config/mips/linux.mh gdb/config/mips/linux.mh
*** ../gdb-clean/gdb-6.8/gdb/config/mips/linux.mh 2007-10-15 12:19:17.000000000 -0700
--- gdb/config/mips/linux.mh 2008-04-15 14:25:17.000000000 -0700
***************
*** 1,5 ****
# Host: Linux/MIPS
! NAT_FILE= config/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
--- 1,5 ----
# Host: Linux/MIPS
! 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 -rcp ../gdb-clean/gdb-6.8/gdb/infrun.c gdb/infrun.c
*** ../gdb-clean/gdb-6.8/gdb/infrun.c 2008-01-29 14:47:19.000000000 -0800
--- gdb/infrun.c 2008-04-21 11:01:07.000000000 -0700
*************** handle_inferior_event (struct execution_
*** 1830,1835 ****
--- 1830,1836 ----
&& (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");
*************** handle_inferior_event (struct execution_
*** 1857,1863 ****
if (!HAVE_STEPPABLE_WATCHPOINT)
remove_breakpoints ();
registers_changed ();
! target_resume (ecs->ptid, 1, TARGET_SIGNAL_0); /* Single step */
ecs->waiton_ptid = ecs->ptid;
if (HAVE_STEPPABLE_WATCHPOINT)
ecs->infwait_state = infwait_step_watch_state;
--- 1858,1880 ----
if (!HAVE_STEPPABLE_WATCHPOINT)
remove_breakpoints ();
registers_changed ();
!
! 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 -rcp ../gdb-clean/gdb-6.8/gdb/mips-linux-nat.c gdb/mips-linux-nat.c
*** ../gdb-clean/gdb-6.8/gdb/mips-linux-nat.c 2008-01-01 14:53:12.000000000 -0800
--- gdb/mips-linux-nat.c 2008-08-27 22:23:34.000000000 -0700
***************
*** 19,24 ****
--- 19,26 ----
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,49 ****
--- 46,54 ----
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. */
*************** mips_linux_read_description (struct targ
*** 355,366 ****
return tdesc_mips64_linux;
}
void _initialize_mips_linux_nat (void);
void
_initialize_mips_linux_nat (void)
{
! struct target_ops *t = linux_trad_target (mips_linux_register_u_offset);
super_fetch_registers = t->to_fetch_registers;
super_store_registers = t->to_store_registers;
--- 360,584 ----
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_by_watchpoint (void)
+ {
+ 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 < watch_readback.mips32.num_valid; i++)
+ {
+ if (watch_readback.mips32.watchhi[i] & 3)
+ {
+ return 1;
+ }
+ }
+ return 0;
+ }
+ int mips_linux_stopped_data_address (CORE_ADDR *paddr)
+ {
+ /* On mips we don't know the low order 3 bits of the data address,
+ so we must return false. */
+ return 0;
+ }
+
+ 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;
!
! 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;