2007-08-13 Jeff Johnston * config/i386/nm-linux.h: Change dr register routines to accept a ptid_t first argument. Change all calling macros to default the inferior_ptid for the first argument. (i386_linux_insert_watchpoint): New prototype. (i386_linux_remove_watchpoint, i386_linux_insert_hw_breakpoint): Ditto. (i386_linux_remove_hw_breakpoint): Ditto. (target_insert_watchpoint, target_remove_watchpoint): Undef and override. (target_insert_hw_breakpoint, target_remove_hw_breakpoint): Ditto. * config/i386/nm-linux64.h: Ditto except add amd64 versions of the watchpoint/hw-breakpoint insert/remove routines. * i386-nat.c: Include "inferior.h" to define inferior_ptid. * i386-linux-nat.c: Change all dr get/set routines to accept ptid_t as first argument and to use this argument to determine the tid for PTRACE. (i386_linux_set_debug_regs_for_thread): New function. (i386_linux_sync_debug_registers_callback): Ditto. (i386_linux_sync_debug_registers_across_threads): Ditto. (i386_linux_insert_watchpoint, i386_linux_remove_watchpoint): Ditto. (i386_linux_hw_breakpoint, i386_linux_remove_hw_breakpoint): Ditto. (i386_linux_new_thread): Ditto. (_initialize_i386_linux_nat): Ditto. * amd64-linux-nat.c: Change all dr get/set routines to accept ptid_t as first argument and to use this argument to determine the tid for PTRACE. (amd64_linux_set_debug_regs_for_thread): New function. (amd64_linux_sync_debug_registers_callback): Ditto. (amd64_linux_sync_debug_registers_across_threads): Ditto. (amd64_linux_insert_watchpoint, amd64_linux_remove_watchpoint): Ditto. (amd64_linux_hw_breakpoint, amd64_linux_remove_hw_breakpoint): Ditto. (amd64_linux_new_thread): Ditto. (_initialize_amd64_linux_nat): Register linux new thread observer. Index: gdb/config/i386/nm-linux64.h =================================================================== --- gdb.orig/config/i386/nm-linux64.h 2007-08-13 06:22:00.000000000 -0700 +++ gdb/config/i386/nm-linux64.h 2007-08-13 06:22:05.000000000 -0700 @@ -37,18 +37,57 @@ extern void amd64_linux_dr_set_control (unsigned long control); #define I386_DR_LOW_SET_CONTROL(control) \ - amd64_linux_dr_set_control (control) + amd64_linux_dr_set_control (inferior_ptid, control) extern void amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr); #define I386_DR_LOW_SET_ADDR(regnum, addr) \ - amd64_linux_dr_set_addr (regnum, addr) + amd64_linux_dr_set_addr (inferior_ptid, regnum, addr) extern void amd64_linux_dr_reset_addr (int regnum); #define I386_DR_LOW_RESET_ADDR(regnum) \ - amd64_linux_dr_reset_addr (regnum) + amd64_linux_dr_reset_addr (inferior_ptid, regnum) extern unsigned long amd64_linux_dr_get_status (void); #define I386_DR_LOW_GET_STATUS() \ - amd64_linux_dr_get_status () + amd64_linux_dr_get_status (inferior_ptid, ) + +/* Watchpoints and hardware breakpoints. */ + +/* Insert a watchpoint to watch a memory region which starts at + * * address ADDR and whose length is LEN bytes. Watch memory accesses + * * of the type TYPE. Return 0 on success, -1 on failure. */ +extern int amd64_linux_insert_watchpoint (CORE_ADDR addr, int len, int type); + +/* Remove a watchpoint that watched the memory region which starts at + * * address ADDR, whose length is LEN bytes, and for accesses of the + * * type TYPE. Return 0 on success, -1 on failure. */ +extern int amd64_linux_remove_watchpoint (CORE_ADDR addr, int len, int type); + +/* Insert a hardware-assisted breakpoint at address ADDR. SHADOW is + * * unused. Return 0 on success, EBUSY on failure. */ +extern int amd64_linux_insert_hw_breakpoint (struct bp_target_info *bp_tgt); + +/* Remove a hardware-assisted breakpoint at address ADDR. SHADOW is + * * unused. Return 0 on success, -1 on failure. */ +extern int amd64_linux_remove_hw_breakpoint (struct bp_target_info *bp_tgt); + +/* Override basic amd64 macros for watchpoint and hardware breakpoint + * insertion/removal to support threads. */ +#undef target_insert_watchpoint +#define target_insert_watchpoint(addr, len, type) \ + amd64_linux_insert_watchpoint (addr, len, type) + +#undef target_remove_watchpoint +#define target_remove_watchpoint(addr, len, type) \ + amd64_linux_remove_watchpoint (addr, len, type) + +#undef target_insert_hw_breakpoint +#define target_insert_hw_breakpoint(bp_tgt) \ + amd64_linux_insert_hw_breakpoint (bp_tgt) + +#undef target_remove_hw_breakpoint +#define target_remove_hw_breakpoint(bp_tgt) \ + amd64_linux_remove_hw_breakpoint (bp_tgt) + #endif /* nm-linux64.h */ Index: gdb/config/i386/nm-linux.h =================================================================== --- gdb.orig/config/i386/nm-linux.h 2007-08-13 06:22:00.000000000 -0700 +++ gdb/config/i386/nm-linux.h 2007-08-13 06:22:05.000000000 -0700 @@ -31,23 +31,62 @@ /* Provide access to the i386 hardware debugging registers. */ -extern void i386_linux_dr_set_control (unsigned long control); +extern void i386_linux_dr_set_control (ptid_t ptid, unsigned long control); #define I386_DR_LOW_SET_CONTROL(control) \ - i386_linux_dr_set_control (control) + i386_linux_dr_set_control (inferior_ptid, control) -extern void i386_linux_dr_set_addr (int regnum, CORE_ADDR addr); +extern void i386_linux_dr_set_addr (ptid_t ptid, int regnum, CORE_ADDR addr); #define I386_DR_LOW_SET_ADDR(regnum, addr) \ - i386_linux_dr_set_addr (regnum, addr) + i386_linux_dr_set_addr (inferior_ptid, regnum, addr) -extern void i386_linux_dr_reset_addr (int regnum); +extern void i386_linux_dr_reset_addr (ptid_t ptid, int regnum); #define I386_DR_LOW_RESET_ADDR(regnum) \ - i386_linux_dr_reset_addr (regnum) + i386_linux_dr_reset_addr (inferior_ptid, regnum) -extern unsigned long i386_linux_dr_get_status (void); +extern unsigned long i386_linux_dr_get_status (ptid_t ptid, ); #define I386_DR_LOW_GET_STATUS() \ - i386_linux_dr_get_status () + i386_linux_dr_get_status (inferior_ptid, ) +/* Watchpoints and hardware breakpoints. */ + +/* Insert a watchpoint to watch a memory region which starts at + * address ADDR and whose length is LEN bytes. Watch memory accesses + * of the type TYPE. Return 0 on success, -1 on failure. */ +extern int i386_linux_insert_watchpoint (CORE_ADDR addr, int len, int type); + +/* Remove a watchpoint that watched the memory region which starts at + * address ADDR, whose length is LEN bytes, and for accesses of the + * type TYPE. Return 0 on success, -1 on failure. */ +extern int i386_linux_remove_watchpoint (CORE_ADDR addr, int len, int type); + +/* Insert a hardware-assisted breakpoint at address ADDR. SHADOW is + * unused. Return 0 on success, EBUSY on failure. */ +extern int i386_linux_insert_hw_breakpoint (struct bp_target_info *bp_tgt); + +/* Remove a hardware-assisted breakpoint at address ADDR. SHADOW is + * unused. Return 0 on success, -1 on failure. */ +extern int i386_linux_remove_hw_breakpoint (struct bp_target_info *bp_tgt); + +/* Override basic i386 macros for watchpoint and hardware breakpoint + insertion/removal to support threads. */ +#undef target_insert_watchpoint +#define target_insert_watchpoint(addr, len, type) \ + i386_linux_insert_watchpoint (addr, len, type) + +#undef target_remove_watchpoint +#define target_remove_watchpoint(addr, len, type) \ + i386_linux_remove_watchpoint (addr, len, type) + +#undef target_insert_hw_breakpoint +#define target_insert_hw_breakpoint(bp_tgt) \ + i386_linux_insert_hw_breakpoint (bp_tgt) + +#undef target_remove_hw_breakpoint +#define target_remove_hw_breakpoint(bp_tgt) \ + i386_linux_remove_hw_breakpoint (bp_tgt) + + #ifdef HAVE_PTRACE_GETFPXREGS /* Include register set support for the SSE registers. */ #define FILL_FPXREGSET Index: gdb/i386-nat.c =================================================================== --- gdb.orig/i386-nat.c 2007-08-13 06:22:00.000000000 -0700 +++ gdb/i386-nat.c 2007-08-13 06:22:05.000000000 -0700 @@ -21,6 +21,7 @@ #include "defs.h" #include "breakpoint.h" +#include "inferior.h" #include "command.h" #include "gdbcmd.h" Index: gdb/i386-linux-nat.c =================================================================== --- gdb.orig/i386-linux-nat.c 2007-08-13 06:22:00.000000000 -0700 +++ gdb/i386-linux-nat.c 2007-08-13 06:22:05.000000000 -0700 @@ -24,6 +24,7 @@ #include "inferior.h" #include "gdbcore.h" #include "regcache.h" +#include "observer.h" #include "target.h" #include "linux-nat.h" @@ -582,14 +583,14 @@ /* Support for debug registers. */ static unsigned long -i386_linux_dr_get (int regnum) +i386_linux_dr_get (ptid_t ptid, int regnum) { int tid; unsigned long value; - tid = TIDGET (inferior_ptid); + tid = TIDGET (ptid); if (tid == 0) - tid = PIDGET (inferior_ptid); + tid = PIDGET (ptid); /* FIXME: kettenis/2001-03-27: Calling perror_with_name if the ptrace call fails breaks debugging remote targets. The correct @@ -610,13 +611,13 @@ } static void -i386_linux_dr_set (int regnum, unsigned long value) +i386_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) { int tid; - tid = TIDGET (inferior_ptid); + tid = TIDGET (ptid); if (tid == 0) - tid = PIDGET (inferior_ptid); + tid = PIDGET (ptid); errno = 0; ptrace (PTRACE_POKEUSER, tid, @@ -626,34 +627,158 @@ } void -i386_linux_dr_set_control (unsigned long control) +i386_linux_dr_set_control (ptid_t ptid, unsigned long control) { - i386_linux_dr_set (DR_CONTROL, control); + i386_linux_dr_set (ptid, DR_CONTROL, control); } void -i386_linux_dr_set_addr (int regnum, CORE_ADDR addr) +i386_linux_dr_set_addr (ptid_t ptid, int regnum, CORE_ADDR addr) { gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); - i386_linux_dr_set (DR_FIRSTADDR + regnum, addr); + i386_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr); } void -i386_linux_dr_reset_addr (int regnum) +i386_linux_dr_reset_addr (ptid_t ptid, int regnum) { gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); - i386_linux_dr_set (DR_FIRSTADDR + regnum, 0L); + i386_linux_dr_set (ptid, DR_FIRSTADDR + regnum, 0L); } unsigned long -i386_linux_dr_get_status (void) +i386_linux_dr_get_status (ptid_t ptid) { - return i386_linux_dr_get (DR_STATUS); + return i386_linux_dr_get (ptid, DR_STATUS); } +/* Structure used to sync debug registers for all threads. */ +struct i386_debug_register_state +{ + int tid; + CORE_ADDR addr[DR_LASTADDR - DR_FIRSTADDR + 1]; + unsigned long control; +}; + +static void +i386_linux_set_debug_regs_for_thread (ptid_t ptid, + struct i386_debug_register_state *dbs) +{ + int i; + for (i = 0; i < (DR_LASTADDR - DR_FIRSTADDR) + 1; ++i) + i386_linux_dr_set_addr (ptid, i, dbs->addr[i]); + i386_linux_dr_set_control (ptid, dbs->control); +} + +/* Iterator function to support syncing debug registers across all threads. */ +static int +i386_linux_sync_debug_registers_callback (struct lwp_info *lwp, void *data) +{ + struct i386_debug_register_state *args = data; + int i, tid; + + tid = TIDGET (lwp->ptid); + if (tid == 0) + tid = PIDGET (lwp->ptid); + + if (tid != args->tid) + i386_linux_set_debug_regs_for_thread (lwp->ptid, args); + return 0; +} + +/* Sync the debug registers for all known threads to the current + thread that has just performed an operation. This is required + because the debug registers are thread-specific. We want + watchpoints and hardware breakpoints to be treated globally + across all threads. */ +static int +i386_linux_sync_debug_registers_across_threads (void) +{ + int i, tid; + struct i386_debug_register_state args; + + tid = TIDGET (inferior_ptid); + if (tid == 0) + tid = PIDGET (inferior_ptid); + + args.tid = tid; + for (i = 0; i < (DR_LASTADDR - DR_FIRSTADDR) + 1; ++i) + args.addr[i] = i386_linux_dr_get (inferior_ptid, DR_FIRSTADDR + i); + args.control = i386_linux_dr_get (inferior_ptid, DR_CONTROL); + + iterate_over_lwps (&i386_linux_sync_debug_registers_callback, &args); + + return 0; +} + +/* Insert a watchpoint to watch a memory region which starts at + address ADDR and whose length is LEN bytes. Watch memory accesses + of the type TYPE. Return 0 on success, -1 on failure. */ +int +i386_linux_insert_watchpoint (CORE_ADDR addr, int len, int type) +{ + int rc; + rc = i386_insert_watchpoint (addr, len, type); + if (!rc) + i386_linux_sync_debug_registers_across_threads (); + return rc; +} + +/* Remove a watchpoint that watched the memory region which starts at + address ADDR, whose length is LEN bytes, and for accesses of the + type TYPE. Return 0 on success, -1 on failure. */ +int +i386_linux_remove_watchpoint (CORE_ADDR addr, int len, int type) +{ + int rc; + rc = i386_remove_watchpoint (addr, len, type); + if (!rc) + i386_linux_sync_debug_registers_across_threads (); + return rc; +} + +/* Insert a hardware-assisted breakpoint at address ADDR. SHADOW is + unused. Return 0 on success, EBUSY on failure. */ +int +i386_linux_insert_hw_breakpoint (struct bp_target_info *bp_tgt) +{ + int rc; + rc = i386_insert_hw_breakpoint (bp_tgt); + if (!rc) + i386_linux_sync_debug_registers_across_threads (); + return rc; +} + +/* Remove a hardware-assisted breakpoint at address ADDR. SHADOW is + unused. Return 0 on success, -1 on failure. */ +int +i386_linux_remove_hw_breakpoint (struct bp_target_info *bp_tgt) +{ + int rc; + rc = i386_remove_hw_breakpoint (bp_tgt); + if (!rc) + i386_linux_sync_debug_registers_across_threads (); + return rc; +} + +/* Observer function for a new thread attach. We need to insert + existing watchpoints and hardware breakpoints on the new thread. */ +static void +i386_linux_new_thread (ptid_t ptid) +{ + int i; + struct i386_debug_register_state dbs; + + for (i = 0; i < (DR_LASTADDR - DR_FIRSTADDR) + 1; ++i) + dbs.addr[i] = i386_linux_dr_get (inferior_ptid, DR_FIRSTADDR + i); + dbs.control = i386_linux_dr_get (inferior_ptid, DR_CONTROL); + + i386_linux_set_debug_regs_for_thread (ptid, &dbs); +} + /* Called by libthread_db. Returns a pointer to the thread local storage (or its descriptor). */ @@ -818,4 +943,6 @@ /* Register the target. */ linux_nat_add_target (t); + + observer_attach_linux_new_thread (i386_linux_new_thread); } Index: gdb/amd64-linux-nat.c =================================================================== --- gdb.orig/amd64-linux-nat.c 2007-08-13 06:22:00.000000000 -0700 +++ gdb/amd64-linux-nat.c 2007-08-13 06:22:05.000000000 -0700 @@ -25,6 +25,7 @@ #include "inferior.h" #include "gdbcore.h" #include "regcache.h" +#include "observer.h" #include "linux-nat.h" #include "amd64-linux-tdep.h" @@ -237,14 +238,14 @@ static unsigned long -amd64_linux_dr_get (int regnum) +amd64_linux_dr_get (ptid_t ptid, int regnum) { int tid; unsigned long value; - tid = TIDGET (inferior_ptid); + tid = TIDGET (ptid); if (tid == 0) - tid = PIDGET (inferior_ptid); + tid = PIDGET (ptid); /* FIXME: kettenis/2001-03-27: Calling perror_with_name if the ptrace call fails breaks debugging remote targets. The correct @@ -265,13 +266,13 @@ } static void -amd64_linux_dr_set (int regnum, unsigned long value) +amd64_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) { int tid; - tid = TIDGET (inferior_ptid); + tid = TIDGET (ptid); if (tid == 0) - tid = PIDGET (inferior_ptid); + tid = PIDGET (ptid); errno = 0; ptrace (PT_WRITE_U, tid, offsetof (struct user, u_debugreg[regnum]), value); @@ -280,34 +281,158 @@ } void -amd64_linux_dr_set_control (unsigned long control) +amd64_linux_dr_set_control (ptid_t ptid, unsigned long control) { - amd64_linux_dr_set (DR_CONTROL, control); + amd64_linux_dr_set (ptid, DR_CONTROL, control); } void -amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr) +amd64_linux_dr_set_addr (ptid_t ptid, int regnum, CORE_ADDR addr) { gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); - amd64_linux_dr_set (DR_FIRSTADDR + regnum, addr); + amd64_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr); } void -amd64_linux_dr_reset_addr (int regnum) +amd64_linux_dr_reset_addr (ptid_t ptid, int regnum) { gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); - amd64_linux_dr_set (DR_FIRSTADDR + regnum, 0L); + amd64_linux_dr_set (ptid, DR_FIRSTADDR + regnum, 0L); } unsigned long -amd64_linux_dr_get_status (void) +amd64_linux_dr_get_status (ptid_t ptid) { - return amd64_linux_dr_get (DR_STATUS); + return amd64_linux_dr_get (ptid, DR_STATUS); } +/* Structure used to sync debug registers for all threads. */ +struct amd64_debug_register_state +{ + int tid; + CORE_ADDR addr[DR_LASTADDR - DR_FIRSTADDR + 1]; + unsigned long control; +}; + +static void +amd64_linux_set_debug_regs_for_thread (ptid_t ptid, + struct amd64_debug_register_state *dbs) +{ + int i; + for (i = 0; i < (DR_LASTADDR - DR_FIRSTADDR) + 1; ++i) + amd64_linux_dr_set_addr (ptid, i, dbs->addr[i]); + amd64_linux_dr_set_control (ptid, dbs->control); +} + +/* Iterator function to support syncing debug registers across all threads. */ +static int +amd64_linux_sync_debug_registers_callback (struct lwp_info *lwp, void *data) +{ + struct amd64_debug_register_state *args = data; + int i, tid; + + tid = TIDGET (lwp->ptid); + if (tid == 0) + tid = PIDGET (lwp->ptid); + + if (tid != args->tid) + amd64_linux_set_debug_regs_for_thread (lwp->ptid, args); + return 0; +} + +/* Sync the debug registers for all known threads to the current + thread that has just performed an operation. This is required + because the debug registers are thread-specific. We want + watchpoints and hardware breakpoints to be treated globally + across all threads. */ +static int +amd64_linux_sync_debug_registers_across_threads (void) +{ + int i, tid; + struct amd64_debug_register_state args; + + tid = TIDGET (inferior_ptid); + if (tid == 0) + tid = PIDGET (inferior_ptid); + + args.tid = tid; + for (i = 0; i < (DR_LASTADDR - DR_FIRSTADDR) + 1; ++i) + args.addr[i] = amd64_linux_dr_get (inferior_ptid, DR_FIRSTADDR + i); + args.control = amd64_linux_dr_get (inferior_ptid, DR_CONTROL); + + iterate_over_lwps (&amd64_linux_sync_debug_registers_callback, &args); + + return 0; +} + +/* Insert a watchpoint to watch a memory region which starts at + address ADDR and whose length is LEN bytes. Watch memory accesses + of the type TYPE. Return 0 on success, -1 on failure. */ +int +amd64_linux_insert_watchpoint (CORE_ADDR addr, int len, int type) +{ + int rc; + rc = i386_insert_watchpoint (addr, len, type); + if (!rc) + amd64_linux_sync_debug_registers_across_threads (); + return rc; +} + +/* Remove a watchpoint that watched the memory region which starts at + address ADDR, whose length is LEN bytes, and for accesses of the + type TYPE. Return 0 on success, -1 on failure. */ +int +amd64_linux_remove_watchpoint (CORE_ADDR addr, int len, int type) +{ + int rc; + rc = i386_remove_watchpoint (addr, len, type); + if (!rc) + amd64_linux_sync_debug_registers_across_threads (); + return rc; +} + +/* Insert a hardware-assisted breakpoint at address ADDR. SHADOW is + unused. Return 0 on success, EBUSY on failure. */ +int +amd64_linux_insert_hw_breakpoint (struct bp_target_info *bp_tgt) +{ + int rc; + rc = i386_insert_hw_breakpoint (bp_tgt); + if (!rc) + amd64_linux_sync_debug_registers_across_threads (); + return rc; +} + +/* Remove a hardware-assisted breakpoint at address ADDR. SHADOW is + unused. Return 0 on success, -1 on failure. */ +int +amd64_linux_remove_hw_breakpoint (struct bp_target_info *bp_tgt) +{ + int rc; + rc = i386_remove_hw_breakpoint (bp_tgt); + if (!rc) + amd64_linux_sync_debug_registers_across_threads (); + return rc; +} + +/* Observer function for a new thread attach. We need to insert + existing watchpoints and hardware breakpoints on the new thread. */ +static void +amd64_linux_new_thread (ptid_t ptid) +{ + int i; + struct amd64_debug_register_state dbs; + + for (i = 0; i < (DR_LASTADDR - DR_FIRSTADDR) + 1; ++i) + dbs.addr[i] = amd64_linux_dr_get (inferior_ptid, DR_FIRSTADDR + i); + dbs.control = amd64_linux_dr_get (inferior_ptid, DR_CONTROL); + + amd64_linux_set_debug_regs_for_thread (ptid, &dbs); +} + /* This function is called by libthread_db as part of its handling of a request for a thread's local storage address. */ @@ -408,4 +533,6 @@ /* Register the target. */ linux_nat_add_target (t); + + observer_attach_linux_new_thread (amd64_linux_new_thread); } Index: gdb/testsuite/gdb.threads/watchthreads.c =================================================================== --- gdb.orig/testsuite/gdb.threads/watchthreads.c 2007-08-13 06:22:00.000000000 -0700 +++ gdb/testsuite/gdb.threads/watchthreads.c 2007-08-13 06:22:05.000000000 -0700 @@ -58,7 +58,7 @@ /* Don't run forever. Run just short of it :) */ while (*myp > 0) { - (*myp) ++; /* Loop increment. */ + (*myp) ++; usleep (1); /* Loop increment. */ } pthread_exit(NULL);