2005-02-28 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. * testsuite/gdb.threads/watchthreads2.c: New test case. * testsuite/gdb.threads/watchthreads2.exp: Ditto. 2007-10-14 Jan Kratochvil Port to GDB-6.7. Index: gdb-6.7/gdb/config/i386/nm-linux64.h =================================================================== --- gdb-6.7.orig/gdb/config/i386/nm-linux64.h 2007-08-23 20:08:48.000000000 +0200 +++ gdb-6.7/gdb/config/i386/nm-linux64.h 2007-10-14 23:41:50.000000000 +0200 @@ -33,20 +33,58 @@ /* Provide access to the i386 hardware debugging registers. */ -extern void amd64_linux_dr_set_control (unsigned long control); +extern void amd64_linux_dr_set_control (ptid_t ptid, 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); +extern void amd64_linux_dr_set_addr (ptid_t ptid, 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); +extern void amd64_linux_dr_reset_addr (ptid_t ptid, 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); +extern unsigned long amd64_linux_dr_get_status (ptid_t ptid); #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-6.7/gdb/config/i386/nm-linux.h =================================================================== --- gdb-6.7.orig/gdb/config/i386/nm-linux.h 2007-08-23 20:08:48.000000000 +0200 +++ gdb-6.7/gdb/config/i386/nm-linux.h 2007-10-14 23:40:55.000000000 +0200 @@ -29,21 +29,60 @@ /* 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 Index: gdb-6.7/gdb/i386-nat.c =================================================================== --- gdb-6.7.orig/gdb/i386-nat.c 2007-08-23 20:08:34.000000000 +0200 +++ gdb-6.7/gdb/i386-nat.c 2007-10-14 23:39:04.000000000 +0200 @@ -19,6 +19,7 @@ #include "defs.h" #include "breakpoint.h" +#include "inferior.h" #include "command.h" #include "gdbcmd.h" Index: gdb-6.7/gdb/testsuite/gdb.threads/watchthreads2.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-6.7/gdb/testsuite/gdb.threads/watchthreads2.c 2007-10-14 23:39:04.000000000 +0200 @@ -0,0 +1,66 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + This file is copied from schedlock.c. */ + +#include +#include +#include +#include + +void *thread_function(void *arg); /* Pointer to function executed by each thread */ + +#define NUM 5 + +unsigned int args[NUM+1]; + +int main() { + int res; + pthread_t threads[NUM]; + void *thread_result; + long i; + + for (i = 0; i < NUM; i++) + { + args[i] = 1; /* Init value. */ + res = pthread_create(&threads[i], + NULL, + thread_function, + (void *) i); + } + + args[i] = 1; + thread_function ((void *) i); + + exit(EXIT_SUCCESS); +} + +void *thread_function(void *arg) { + int my_number = (long) arg; + int *myp = (int *) &args[my_number]; + + /* Don't run forever. Run just short of it :) */ + while (*myp > 0) + { + (*myp) ++; usleep (1); /* Loop increment. */ + } + + pthread_exit(NULL); +} + Index: gdb-6.7/gdb/testsuite/gdb.threads/watchthreads2.exp =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-6.7/gdb/testsuite/gdb.threads/watchthreads2.exp 2007-10-14 23:39:04.000000000 +0200 @@ -0,0 +1,133 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2005 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# Check that GDB can support multiple watchpoints across threads. + +if $tracelevel { + strace $tracelevel +} + +set prms_id 0 +set bug_id 0 + +# This test verifies that a watchpoint is detected in the proper thread +# so the test is only meaningful on a system with hardware watchpoints. +if [target_info exists gdb,no_hardware_watchpoints] { + return 0; +} + +set testfile "watchthreads2" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug "incdir=${objdir}"]] != "" } { + return -1 +} + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +gdb_test "set can-use-hw-watchpoints 1" "" "" + +# +# Run to `main' where we begin our tests. +# + +if ![runto_main] then { + gdb_suppress_tests +} + +set args_2 0 +set args_3 0 + +gdb_breakpoint "thread_function" +gdb_continue_to_breakpoint "thread_function" +gdb_test "disable 2" "" + +gdb_test_multiple "p args\[2\]" "get initial args2" { + -re "\\\$\[0-9\]* = (.*)$gdb_prompt $" { + set init_args_2 $expect_out(1,string) + pass "get initial args2" + } +} + +gdb_test_multiple "p args\[3\]" "get initial args3" { + -re "\\\$\[0-9\]* = (.*)$gdb_prompt $" { + set init_args_3 $expect_out(1,string) + pass "get initial args3" + } +} + +set args_2 $init_args_2 +set args_3 $init_args_3 + +# Watch values that will be modified by distinct threads. +gdb_test "watch args\[2\]" "Hardware watchpoint 3: args\\\[2\\\]" +gdb_test "watch args\[3\]" "Hardware watchpoint 4: args\\\[3\\\]" + +set init_line [expr [gdb_get_line_number "Init value"]+1] +set inc_line [gdb_get_line_number "Loop increment"] + +# Loop and continue to allow both watchpoints to be triggered. +for {set i 0} {$i < 30} {incr i} { + set test_flag 0 + gdb_test_multiple "continue" "threaded watch loop" { + -re "Hardware watchpoint 3: args\\\[2\\\].*Old value = 0.*New value = 1.*main \\\(\\\) at .*watchthreads2.c:$init_line.*$gdb_prompt $" + { set args_2 1; set test_flag 1 } + -re "Hardware watchpoint 4: args\\\[3\\\].*Old value = 0.*New value = 1.*main \\\(\\\) at .*watchthreads2.c:$init_line.*$gdb_prompt $" + { set args_3 1; set test_flag 1 } + -re "Hardware watchpoint 3: args\\\[2\\\].*Old value = $args_2.*New value = [expr $args_2+1].*in thread_function \\\(arg=0x2\\\) at .*watchthreads2.c:$inc_line.*$gdb_prompt $" + { set args_2 [expr $args_2+1]; set test_flag 1 } + -re "Hardware watchpoint 4: args\\\[3\\\].*Old value = $args_3.*New value = [expr $args_3+1].*in thread_function \\\(arg=0x3\\\) at .*watchthreads2.c:$inc_line.*$gdb_prompt $" + { set args_3 [expr $args_3+1]; set test_flag 1 } + } + # If we fail above, don't bother continuing loop + if { $test_flag == 0 } { + set i 30; + } +} + +# Print success message if loop succeeded. +if { $test_flag == 1 } { + pass "threaded watch loop" +} + +# Verify that we hit first watchpoint in child thread. +set message "watchpoint on args\[2\] hit in thread" +if { $args_2 > 1 } { + pass $message +} else { + fail $message +} + +# Verify that we hit second watchpoint in child thread. +set message "watchpoint on args\[3\] hit in thread" +if { $args_3 > 1 } { + pass $message +} else { + fail $message +} + +# Verify that all watchpoint hits are accounted for. +set message "combination of threaded watchpoints = 30 + initial values" +if { [expr $args_2+$args_3] == [expr [expr 30+$init_args_2]+$init_args_3] } { + pass $message +} else { + fail $message +} Index: gdb-6.7/gdb/i386-linux-nat.c =================================================================== --- gdb-6.7.orig/gdb/i386-linux-nat.c 2007-10-13 05:09:50.000000000 +0200 +++ gdb-6.7/gdb/i386-linux-nat.c 2007-10-14 23:39:04.000000000 +0200 @@ -22,6 +22,7 @@ #include "inferior.h" #include "gdbcore.h" #include "regcache.h" +#include "observer.h" #include "target.h" #include "linux-nat.h" @@ -580,14 +581,14 @@ i386_linux_store_inferior_registers (str /* 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 @@ -608,13 +609,13 @@ i386_linux_dr_get (int regnum) } 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, @@ -624,34 +625,158 @@ i386_linux_dr_set (int regnum, unsigned } 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). */ @@ -816,4 +941,6 @@ _initialize_i386_linux_nat (void) /* Register the target. */ linux_nat_add_target (t); + + observer_attach_linux_new_thread (i386_linux_new_thread); } Index: gdb-6.7/gdb/amd64-linux-nat.c =================================================================== --- gdb-6.7.orig/gdb/amd64-linux-nat.c 2007-10-13 05:09:50.000000000 +0200 +++ gdb-6.7/gdb/amd64-linux-nat.c 2007-10-14 23:39:04.000000000 +0200 @@ -23,6 +23,7 @@ #include "inferior.h" #include "gdbcore.h" #include "regcache.h" +#include "observer.h" #include "linux-nat.h" #include "amd64-linux-tdep.h" @@ -235,14 +236,14 @@ amd64_linux_store_inferior_registers (st 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 @@ -263,13 +264,13 @@ amd64_linux_dr_get (int regnum) } 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); @@ -278,34 +279,158 @@ amd64_linux_dr_set (int regnum, unsigned } 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. */ @@ -406,4 +531,6 @@ _initialize_amd64_linux_nat (void) /* Register the target. */ linux_nat_add_target (t); + + observer_attach_linux_new_thread (amd64_linux_new_thread); } Index: gdb-6.7/gdb/testsuite/gdb.threads/watchthreads.c =================================================================== --- gdb-6.7.orig/gdb/testsuite/gdb.threads/watchthreads.c 2007-08-23 20:08:50.000000000 +0200 +++ gdb-6.7/gdb/testsuite/gdb.threads/watchthreads.c 2007-10-14 23:39:04.000000000 +0200 @@ -56,7 +56,7 @@ void *thread_function(void *arg) { /* Don't run forever. Run just short of it :) */ while (*myp > 0) { - (*myp) ++; /* Loop increment. */ + (*myp) ++; usleep (1); /* Loop increment. */ } pthread_exit(NULL);