From 36ea501317e53047d488c1d25b08c1e40f1554c1 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Dec 13 2004 21:48:04 +0000 Subject: Add threaded watchpoint support. --- diff --git a/gdb-6.3-threaded-watchpoints-20041213.patch b/gdb-6.3-threaded-watchpoints-20041213.patch new file mode 100644 index 0000000..0f5b346 --- /dev/null +++ b/gdb-6.3-threaded-watchpoints-20041213.patch @@ -0,0 +1,2159 @@ +2004-12-13 Jeff Johnston + + * linux-nat.c: Add latest vfork fixes. + (stop_wait_callback, linux-nat-wait): Notify observers of a sigtrap. + (delete_lwp): Free the saved_trap_data if present. + * linux-nat.h (struct lwp_info): Add saved_trap_data field. + (struct linux_watchpoint): New struct. + * thread-db.c: Add support to always keep lwp info in ptids. + (attach_thread): Notify observers of a linux + new thread. + (thread_db_wait): Call check_event if SIGILL occurs. + * infrun.c: Add debug statement support. + (handle_inferior_event): For platforms that + hit watchpoints prior to the data write, mark the watchpoints + so we know to check them after we step through the write. + k + * breakpoint.c (bpstat_stop_status): Fix up watchpoint code. + (insert_watchpoints_for_new_thread): New function. + (mark_triggered_watchpoints): Ditto. + * breakpoint.h (insert_watchpoints_for_new_thread): New prototype. + (mark_triggered_watchpoints): Ditto. + * i386-linux-nat.c (i386_linux_dr_get, i386_linux_dr_set): Use + TIDGET to get PTRACE lpw, otherwise fall back to PIDGET. + * amd64-linux-nat.c (amd64_linux_dr_get, amd64_linux_dr_set): Ditto. + * ia64-linux-nat.c: Add support for removing and inserting watchpoints + on all threads. + * s390-nat.c: Ditto. + * Makefile.in: Add observer.h and linux-nat.h to ia64-linux-nat.o + and s390-nat.o. + * doc/observer.texi: Add two new observers for linux_new_thread + and sigtrap. + +--- gdb-6.3/gdb/doc/observer.texi.fix Mon Dec 13 15:01:35 2004 ++++ gdb-6.3/gdb/doc/observer.texi Mon Dec 13 16:07:04 2004 +@@ -95,3 +95,14 @@ inferior, and before any information on + The specified shared library has been discovered to be unloaded. + @end deftypefun + ++@deftypefun void linux_new_thread (ptid_t @var{ptid}) ++A new linux thread described by @var{ptid} has been officially attached ++to by gdb. ++@end deftypefun ++ ++@deftypefun void sigtrap (void * @var{data}) ++A low-level SIGTRAP has been discovered. This notification can be used to save ++additional state necessary if the trap is deferred for later handling. ++@end deftypefun ++ ++ +--- gdb-6.3/gdb/ia64-linux-nat.c.fix Mon Dec 13 14:59:45 2004 ++++ gdb-6.3/gdb/ia64-linux-nat.c Mon Dec 13 15:10:01 2004 +@@ -39,6 +39,8 @@ + + #include + #include ++#include "observer.h" ++#include "linux-nat.h" + + /* Prototypes for supply_gregset etc. */ + #include "gregset.h" +@@ -559,8 +561,9 @@ is_power_of_2 (int val) + return onecount <= 1; + } + +-int +-ia64_linux_insert_watchpoint (ptid_t ptid, CORE_ADDR addr, int len, int rw) ++/* Internal routine to insert one watchpoint for a specified thread. */ ++static int ++ia64_linux_insert_one_watchpoint (ptid_t ptid, CORE_ADDR addr, int len, int rw) + { + int idx; + long dbr_addr, dbr_mask; +@@ -606,8 +609,38 @@ ia64_linux_insert_watchpoint (ptid_t pti + return 0; + } + ++/* Internal callback routine which can be used via iterate_over_lwps ++ to insert a specific watchpoint from all active threads. */ ++static int ++ia64_linux_insert_watchpoint_callback (struct lwp_info *lwp, void *data) ++{ ++ struct linux_watchpoint *args = (struct linux_watchpoint *)data; ++ ++ return ia64_linux_insert_one_watchpoint (lwp->ptid, args->addr, ++ args->len, args->type); ++} ++ ++/* Insert a watchpoint for all threads. */ + int +-ia64_linux_remove_watchpoint (ptid_t ptid, CORE_ADDR addr, int len) ++ia64_linux_insert_watchpoint (ptid_t ptid, CORE_ADDR addr, int len, int rw) ++{ ++ struct linux_watchpoint args; ++ ++ args.addr = addr; ++ args.len = len; ++ args.type = rw; ++ ++ /* For ia64, watchpoints must be inserted/removed on each thread so ++ we iterate over the lwp list. */ ++ if (iterate_over_lwps (&ia64_linux_insert_watchpoint_callback, &args)) ++ return -1; ++ ++ return 0; ++} ++ ++/* Internal routine to remove one watchpoint for a specified thread. */ ++static int ++ia64_linux_remove_one_watchpoint (ptid_t ptid, CORE_ADDR addr, int len) + { + int idx; + long dbr_addr, dbr_mask; +@@ -630,23 +663,74 @@ ia64_linux_remove_watchpoint (ptid_t pti + return -1; + } + ++/* Internal callback routine which can be used via iterate_over_lwps ++ to remove a specific watchpoint from all active threads. */ ++static int ++ia64_linux_remove_watchpoint_callback (struct lwp_info *lwp, void *data) ++{ ++ struct linux_watchpoint *args = (struct linux_watchpoint *)data; ++ ++ return ia64_linux_remove_one_watchpoint (lwp->ptid, args->addr, ++ args->len); ++} ++ ++/* Remove a watchpoint for all threads. */ ++int ++ia64_linux_remove_watchpoint (ptid_t ptid, CORE_ADDR addr, int len) ++{ ++ struct linux_watchpoint args; ++ ++ args.addr = addr; ++ args.len = len; ++ ++ /* For ia64, watchpoints must be inserted/removed on each thread so ++ we iterate over the lwp list. */ ++ if (iterate_over_lwps (&ia64_linux_remove_watchpoint_callback, &args)) ++ return -1; ++ ++ return 0; ++} ++ ++/* Callback to find lwp_info struct for a given lwp. */ ++static int ++find_lwp_info (struct lwp_info *lp, void *data) ++{ ++ int lwp = *(int *)data; ++ ++ if (lwp == TIDGET (lp->ptid)) ++ return 1; ++ ++ return 0; ++} ++ + int + ia64_linux_stopped_data_address (CORE_ADDR *addr_p) + { + CORE_ADDR psr; + int tid; + struct siginfo siginfo; ++ struct siginfo *siginfo_p; + ptid_t ptid = inferior_ptid; ++ struct lwp_info *lp; + + tid = TIDGET(ptid); + if (tid == 0) + tid = PIDGET (ptid); + + errno = 0; +- ptrace (PTRACE_GETSIGINFO, tid, (PTRACE_TYPE_ARG3) 0, &siginfo); ++ /* Check to see if we have already cached the siginfo for this ++ event. */ ++ lp = iterate_over_lwps (find_lwp_info, &tid); ++ if (lp && lp->saved_trap_data != NULL) ++ siginfo_p = (struct siginfo *)lp->saved_trap_data; ++ else ++ { ++ siginfo_p = &siginfo; ++ ptrace (PTRACE_GETSIGINFO, tid, (PTRACE_TYPE_ARG3) 0, siginfo_p); ++ } + +- if (errno != 0 || siginfo.si_signo != SIGTRAP || +- (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */) ++ if (errno != 0 || siginfo_p->si_signo != SIGTRAP || ++ (siginfo_p->si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */) + return 0; + + psr = read_register_pid (IA64_PSR_REGNUM, ptid); +@@ -654,7 +738,7 @@ ia64_linux_stopped_data_address (CORE_AD + for the next instruction */ + write_register_pid (IA64_PSR_REGNUM, psr, ptid); + +- *addr_p = (CORE_ADDR)siginfo.si_addr; ++ *addr_p = (CORE_ADDR)siginfo_p->si_addr; + return 1; + } + +@@ -674,3 +758,36 @@ ia64_linux_xfer_unwind_table (struct tar + { + return syscall (__NR_getunwind, readbuf, len); + } ++ ++/* Observer function for a new thread attach. We need to insert ++ existing watchpoints on the new thread. */ ++static void ++ia64_linux_new_thread (ptid_t ptid) ++{ ++ insert_watchpoints_for_new_thread (ptid, ++ &ia64_linux_insert_one_watchpoint); ++} ++ ++/* For ia64 linux, we must save the siginfo data as part of the state ++ of a queued SIGTRAP. This is because siginfo is used to determine ++ if a watchpoint has occurred and the information will be lost if ++ a SIGSTOP is issued to the thread. */ ++void ++ia64_linux_save_sigtrap_info (void *queue_data) ++{ ++ struct lwp_info *lp = (struct lwp_info *)queue_data; ++ ++ if (lp->saved_trap_data == NULL) ++ lp->saved_trap_data = xmalloc (sizeof(struct siginfo)); ++ ++ ptrace (PTRACE_GETSIGINFO, ptid_get_lwp (lp->ptid), (PTRACE_ARG3_TYPE) 0, ++ lp->saved_trap_data); ++} ++ ++void ++_initialize_ia64_linux_nat (void) ++{ ++ observer_attach_linux_new_thread (ia64_linux_new_thread); ++ observer_attach_sigtrap (ia64_linux_save_sigtrap_info); ++} ++ +--- gdb-6.3/gdb/s390-nat.c.fix Mon Dec 13 15:00:02 2004 ++++ gdb-6.3/gdb/s390-nat.c Mon Dec 13 15:31:42 2004 +@@ -27,6 +27,8 @@ + #include "inferior.h" + + #include "s390-tdep.h" ++#include "linux-nat.h" ++#include "observer.h" + + #include + #include +@@ -112,14 +114,14 @@ fill_fpregset (fpregset_t *regp, int reg + ((char *)regp) + regmap_fpregset[i]); + } + +-/* Find the TID for the current inferior thread to use with ptrace. */ ++/* Find the TID for use with ptrace. */ + static int +-s390_inferior_tid (void) ++s390_tid (ptid_t ptid) + { + /* GNU/Linux LWP ID's are process ID's. */ +- int tid = TIDGET (inferior_ptid); ++ int tid = TIDGET (ptid); + if (tid == 0) +- tid = PIDGET (inferior_ptid); /* Not a threaded program. */ ++ tid = PIDGET (ptid); /* Not a threaded program. */ + + return tid; + } +@@ -203,7 +205,7 @@ store_fpregs (int tid, int regnum) + void + fetch_inferior_registers (int regnum) + { +- int tid = s390_inferior_tid (); ++ int tid = s390_tid (inferior_ptid); + + if (regnum == -1 + || (regnum < S390_NUM_REGS && regmap_gregset[regnum] != -1)) +@@ -219,7 +221,7 @@ fetch_inferior_registers (int regnum) + void + store_inferior_registers (int regnum) + { +- int tid = s390_inferior_tid (); ++ int tid = s390_tid (inferior_ptid); + + if (regnum == -1 + || (regnum < S390_NUM_REGS && regmap_gregset[regnum] != -1)) +@@ -261,7 +263,7 @@ s390_stopped_by_watchpoint (void) + parea.len = sizeof (per_lowcore); + parea.process_addr = (addr_t) & per_lowcore; + parea.kernel_addr = offsetof (struct user_regs_struct, per_info.lowcore); +- if (ptrace (PTRACE_PEEKUSR_AREA, s390_inferior_tid (), &parea) < 0) ++ if (ptrace (PTRACE_PEEKUSR_AREA, s390_tid (inferior_ptid), &parea) < 0) + perror_with_name ("Couldn't retrieve watchpoint status"); + + return per_lowcore.perc_storage_alteration == 1 +@@ -269,9 +271,9 @@ s390_stopped_by_watchpoint (void) + } + + static void +-s390_fix_watch_points (void) ++s390_fix_watch_points (ptid_t ptid) + { +- int tid = s390_inferior_tid (); ++ int tid = s390_tid (ptid); + + per_struct per_info; + ptrace_area parea; +@@ -308,6 +310,16 @@ s390_fix_watch_points (void) + perror_with_name ("Couldn't modify watchpoint status"); + } + ++/* Callback routine to use with iterate_over_lwps to insert a specified ++ watchpoint on all threads. */ ++static int ++s390_insert_watchpoint_callback (struct lwp_info *lwp, void *data) ++{ ++ s390_fix_watch_points (lwp->ptid); ++ return 0; ++} ++ ++/* Insert a specified watchpoint on all threads. */ + int + s390_insert_watchpoint (CORE_ADDR addr, int len) + { +@@ -321,10 +333,24 @@ s390_insert_watchpoint (CORE_ADDR addr, + area->next = watch_base; + watch_base = area; + +- s390_fix_watch_points (); ++ /* For the S390, a watchpoint must be inserted/removed for each ++ thread so we iterate over the list of existing lwps. */ ++ if (iterate_over_lwps (&s390_insert_watchpoint_callback, NULL)) ++ return -1; ++ + return 0; + } + ++/* Callback routine to use with iterate_over_lwps to remove a specified ++ watchpoint from all threads. */ ++static int ++s390_remove_watchpoint_callback (struct lwp_info *lwp, void *data) ++{ ++ s390_fix_watch_points (lwp->ptid); ++ return 0; ++} ++ ++/* Remove a specified watchpoint from all threads. */ + int + s390_remove_watchpoint (CORE_ADDR addr, int len) + { +@@ -346,7 +372,11 @@ s390_remove_watchpoint (CORE_ADDR addr, + *parea = area->next; + xfree (area); + +- s390_fix_watch_points (); ++ /* For the S390, a watchpoint must be inserted/removed for each ++ thread so we iterate over the list of existing lwps. */ ++ if (iterate_over_lwps (&s390_remove_watchpoint_callback, NULL)) ++ return -1; ++ + return 0; + } + +@@ -357,3 +387,17 @@ kernel_u_size (void) + return sizeof (struct user); + } + ++/* New thread observer that inserts all existing watchpoints on the ++ new thread. */ ++static void ++s390_linux_new_thread (ptid_t ptid) ++{ ++ /* Add existing watchpoints to new thread. */ ++ s390_fix_watch_points (ptid); ++} ++ ++void ++_initialize_s390_nat (void) ++{ ++ observer_attach_linux_new_thread (s390_linux_new_thread); ++} +--- gdb-6.3/gdb/Makefile.in.fix Mon Dec 13 15:01:16 2004 ++++ gdb-6.3/gdb/Makefile.in Mon Dec 13 15:06:36 2004 +@@ -2083,7 +2083,8 @@ ia64-aix-nat.o: ia64-aix-nat.c $(defs_h) + $(objfiles_h) $(gdb_stat_h) + ia64-aix-tdep.o: ia64-aix-tdep.c $(defs_h) + ia64-linux-nat.o: ia64-linux-nat.c $(defs_h) $(gdb_string_h) $(inferior_h) \ +- $(target_h) $(gdbcore_h) $(regcache_h) $(gdb_wait_h) $(gregset_h) ++ $(target_h) $(gdbcore_h) $(regcache_h) $(gdb_wait_h) $(gregset_h) \ ++ $(observer_h) $(linux_nat_h) + ia64-linux-tdep.o: ia64-linux-tdep.c $(defs_h) $(ia64_tdep_h) \ + $(arch_utils_h) $(gdbcore_h) $(regcache_h) + ia64-tdep.o: ia64-tdep.c $(defs_h) $(inferior_h) $(gdbcore_h) \ +@@ -2465,7 +2466,7 @@ rs6000-tdep.o: rs6000-tdep.c $(defs_h) $ + $(ppc_tdep_h) $(gdb_assert_h) $(dis_asm_h) $(trad_frame_h) \ + $(frame_unwind_h) $(frame_base_h) + s390-nat.o: s390-nat.c $(defs_h) $(tm_h) $(regcache_h) $(inferior_h) \ +- $(s390_tdep_h) ++ $(s390_tdep_h) $(observer_h) $(linux_nat_h) + s390-tdep.o: s390-tdep.c $(defs_h) $(arch_utils_h) $(frame_h) $(inferior_h) \ + $(symtab_h) $(target_h) $(gdbcore_h) $(gdbcmd_h) $(objfiles_h) \ + $(tm_h) $(__bfd_bfd_h) $(floatformat_h) $(regcache_h) \ +--- gdb-6.3/gdb/breakpoint.c.fix Mon Dec 13 15:00:09 2004 ++++ gdb-6.3/gdb/breakpoint.c Mon Dec 13 15:22:24 2004 +@@ -86,11 +86,6 @@ static void watch_command (char *, int); + + static int can_use_hardware_watchpoint (struct value *); + +-extern void break_at_finish_command (char *, int); +-extern void break_at_finish_at_depth_command (char *, int); +- +-extern void tbreak_at_finish_command (char *, int); +- + static int break_command_1 (char *, int, int, struct breakpoint *); + + static void mention (struct breakpoint *); +@@ -181,11 +176,6 @@ static void create_fork_vfork_event_catc + char *cond_string, + enum bptype bp_kind); + +-static void break_at_finish_at_depth_command_1 (char *arg, +- int flag, int from_tty); +- +-static void break_at_finish_command_1 (char *arg, int flag, int from_tty); +- + static void stop_command (char *arg, int from_tty); + + static void stopin_command (char *arg, int from_tty); +@@ -748,6 +738,90 @@ insert_catchpoint (struct ui_out *uo, vo + return 0; + } + ++/* External function to insert all existing watchpoints on a newly ++ attached thread. IWPFN is a callback function to perform ++ the target insert watchpoint. This function is used to support ++ platforms where a watchpoint must be inserted/removed on each ++ individual thread (e.g. ia64-linux and s390-linux). For ++ ia64 and s390 linux, this function is called via a new thread ++ observer. */ ++int ++insert_watchpoints_for_new_thread (ptid_t new_thread, ++ insert_watchpoint_ftype *iwpfn) ++{ ++ struct bp_location *b; ++ int val = 0; ++ int return_val = 0; ++ struct ui_file *tmp_error_stream = mem_fileopen (); ++ make_cleanup_ui_file_delete (tmp_error_stream); ++ ++ /* Explicitly mark the warning -- this will only be printed if ++ there was an error. */ ++ fprintf_unfiltered (tmp_error_stream, "Warning:\n"); ++ ++ ALL_BP_LOCATIONS (b) ++ { ++ /* Skip disabled breakpoints. */ ++ if (!breakpoint_enabled (b->owner)) ++ continue; ++ ++ /* For every active watchpoint, we need to insert the watchpoint on ++ the new thread. */ ++ if (b->loc_type == bp_loc_hardware_watchpoint) ++ { ++ struct value *v = b->owner->val_chain; ++ ++ /* Look at each value on the value chain. */ ++ for (; v; v = v->next) ++ { ++ /* If it's a memory location, and GDB actually needed ++ its contents to evaluate the expression, then we ++ must watch it. */ ++ if (VALUE_LVAL (v) == lval_memory ++ && ! VALUE_LAZY (v)) ++ { ++ struct type *vtype = check_typedef (VALUE_TYPE (v)); ++ ++ /* We only watch structs and arrays if user asked ++ for it explicitly, never if they just happen to ++ appear in the middle of some value chain. */ ++ if (v == b->owner->val_chain ++ || (TYPE_CODE (vtype) != TYPE_CODE_STRUCT ++ && TYPE_CODE (vtype) != TYPE_CODE_ARRAY)) ++ { ++ CORE_ADDR addr; ++ int len, type; ++ ++ addr = VALUE_ADDRESS (v) + VALUE_OFFSET (v); ++ len = TYPE_LENGTH (VALUE_TYPE (v)); ++ type = hw_write; ++ if (b->owner->type == bp_read_watchpoint) ++ type = hw_read; ++ else if (b->owner->type == bp_access_watchpoint) ++ type = hw_access; ++ val = (*iwpfn) (new_thread, addr, len, type); ++ } ++ } ++ } ++ } ++ ++ if (val) ++ return_val = val; ++ } ++ ++ /* Failure to insert a watchpoint on any memory value in the ++ value chain brings us here. */ ++ if (return_val) ++ { ++ fprintf_unfiltered (tmp_error_stream, ++ "%s\n", ++ "Could not insert hardware watchpoints on new thread."); ++ target_terminal_ours_for_output (); ++ error_stream (tmp_error_stream); ++ } ++ return return_val; ++} ++ + /* Helper routine: free the value chain for a breakpoint (watchpoint). */ + + static void free_valchain (struct bp_location *b) +@@ -1179,6 +1253,7 @@ remove_breakpoints (void) + { + struct bp_location *b; + int val; ++ int return_val = 0; + + ALL_BP_LOCATIONS (b) + { +@@ -1186,10 +1261,10 @@ remove_breakpoints (void) + { + val = remove_breakpoint (b, mark_uninserted); + if (val != 0) +- return val; ++ return_val = val; + } + } +- return 0; ++ return return_val; + } + + int +@@ -2123,8 +2198,13 @@ print_it_typical (bpstat bs) + break; + + case bp_thread_event: +- /* Not sure how we will get here. +- GDB should not stop for these breakpoints. */ ++ /* We can only get here legitimately if something further on the bs ++ list has caused the stop status to be noisy. A valid example ++ of this is a new thread event and a software watchpoint have ++ both occurred. */ ++ if (bs->next) ++ return PRINT_UNKNOWN; ++ + printf_filtered ("Thread Event Breakpoint: gdb should not stop!\n"); + return PRINT_NOTHING; + break; +@@ -2561,6 +2641,54 @@ which its expression is valid.\n"); + } + } + ++/* Check watchpoints for a match with a stopped data address. ++ ++ STOPPED_DATA_ADDRESS is the address of a triggered watchpoint. ++ A match with an existing watchpoint will cause that watchpoint ++ to be marked as triggered. ++ ++ This function is only used for platforms where a watchpoint ++ triggers prior to the data being accessed. */ ++ ++void ++mark_triggered_watchpoints (CORE_ADDR stopped_data_address) ++{ ++ struct breakpoint *b, *temp; ++ CORE_ADDR addr = stopped_data_address; ++ struct value *v; ++ ++ ALL_BREAKPOINTS_SAFE (b, temp) ++ { ++ if (b->type == bp_hardware_watchpoint ++ || b->type == bp_read_watchpoint ++ || b->type == bp_access_watchpoint) ++ { ++ for (v = b->val_chain; v; v = v->next) ++ { ++ if (VALUE_LVAL (v) == lval_memory ++ && ! VALUE_LAZY (v)) ++ { ++ struct type *vtype = check_typedef (VALUE_TYPE (v)); ++ ++ if (v == b->val_chain ++ || (TYPE_CODE (vtype) != TYPE_CODE_STRUCT ++ && TYPE_CODE (vtype) != TYPE_CODE_ARRAY)) ++ { ++ CORE_ADDR vaddr; ++ ++ vaddr = VALUE_ADDRESS (v) + VALUE_OFFSET (v); ++ /* Exact match not required. Within range is ++ sufficient. */ ++ if (addr >= vaddr && ++ addr < vaddr + TYPE_LENGTH (VALUE_TYPE (v))) ++ b->watchpoint_triggered = 1; ++ } ++ } ++ } ++ } ++ } ++} ++ + /* Get a bpstat associated with having just stopped at address + BP_ADDR in thread PTID. STOPPED_BY_WATCHPOINT is 1 if the + target thinks we stopped due to a hardware watchpoint, 0 if we +@@ -2691,82 +2819,61 @@ bpstat_stop_status (CORE_ADDR bp_addr, p + bs->stop = 1; + bs->print = 1; + +- if (b->type == bp_watchpoint || +- b->type == bp_hardware_watchpoint) +- { +- char *message = xstrprintf ("Error evaluating expression for watchpoint %d\n", +- b->number); +- struct cleanup *cleanups = make_cleanup (xfree, message); +- int e = catch_errors (watchpoint_check, bs, message, +- RETURN_MASK_ALL); +- do_cleanups (cleanups); +- switch (e) +- { +- case WP_DELETED: +- /* We've already printed what needs to be printed. */ +- /* Actually this is superfluous, because by the time we +- call print_it_typical() the wp will be already deleted, +- and the function will return immediately. */ +- bs->print_it = print_it_done; +- /* Stop. */ +- break; +- case WP_VALUE_CHANGED: +- /* Stop. */ +- ++(b->hit_count); +- break; +- case WP_VALUE_NOT_CHANGED: +- /* Don't stop. */ +- bs->print_it = print_it_noop; +- bs->stop = 0; +- continue; +- default: +- /* Can't happen. */ +- /* FALLTHROUGH */ +- case 0: +- /* Error from catch_errors. */ +- printf_filtered ("Watchpoint %d deleted.\n", b->number); +- if (b->related_breakpoint) +- b->related_breakpoint->disposition = disp_del_at_next_stop; +- b->disposition = disp_del_at_next_stop; +- /* We've already printed what needs to be printed. */ +- bs->print_it = print_it_done; +- +- /* Stop. */ +- break; +- } +- } +- else if (b->type == bp_read_watchpoint || +- b->type == bp_access_watchpoint) ++ if (b->type == bp_watchpoint ++ || b->type == bp_read_watchpoint ++ || b->type == bp_access_watchpoint ++ || b->type == bp_hardware_watchpoint) + { + CORE_ADDR addr; + struct value *v; +- int found = 0; ++ int must_check_value = 0; + +- if (!target_stopped_data_address (¤t_target, &addr)) +- continue; +- for (v = b->val_chain; v; v = v->next) ++ if (b->type == bp_watchpoint ++ || b->watchpoint_triggered ++ || (b->type == bp_hardware_watchpoint ++ && !target_stopped_data_address_p (¤t_target))) + { +- if (VALUE_LVAL (v) == lval_memory +- && ! VALUE_LAZY (v)) ++ /* We either have a software watchpoint, a triggered watchpoint ++ which we have stepped over, or we cannot ascertain what data ++ address causes a write watchpoint. In all these ++ cases, we must check the watchpoint value. */ ++ b->watchpoint_triggered = 0; ++ must_check_value = 1; ++ } ++ else ++ { ++ /* At this point, we know target_stopped_data_address () works or ++ we have a read or access watchpoint and have no alternatives. */ ++ if (!target_stopped_data_address (¤t_target, &addr)) + { +- struct type *vtype = check_typedef (VALUE_TYPE (v)); +- +- if (v == b->val_chain +- || (TYPE_CODE (vtype) != TYPE_CODE_STRUCT +- && TYPE_CODE (vtype) != TYPE_CODE_ARRAY)) ++ bs->print_it = print_it_noop; ++ bs->stop = 0; ++ continue; ++ } ++ for (v = b->val_chain; v; v = v->next) ++ { ++ if (VALUE_LVAL (v) == lval_memory ++ && ! VALUE_LAZY (v)) + { +- CORE_ADDR vaddr; +- +- vaddr = VALUE_ADDRESS (v) + VALUE_OFFSET (v); +- /* Exact match not required. Within range is +- sufficient. */ +- if (addr >= vaddr && +- addr < vaddr + TYPE_LENGTH (VALUE_TYPE (v))) +- found = 1; ++ struct type *vtype = check_typedef (VALUE_TYPE (v)); ++ ++ if (v == b->val_chain ++ || (TYPE_CODE (vtype) != TYPE_CODE_STRUCT ++ && TYPE_CODE (vtype) != TYPE_CODE_ARRAY)) ++ { ++ CORE_ADDR vaddr; ++ ++ vaddr = VALUE_ADDRESS (v) + VALUE_OFFSET (v); ++ /* Exact match not required. Within range is ++ sufficient. */ ++ if (addr >= vaddr && ++ addr < vaddr + TYPE_LENGTH (VALUE_TYPE (v))) ++ must_check_value = 1; ++ } + } + } + } +- if (found) ++ if (must_check_value) + { + char *message = xstrprintf ("Error evaluating expression for watchpoint %d\n", + b->number); +@@ -2795,6 +2902,15 @@ bpstat_stop_status (CORE_ADDR bp_addr, p + break; + case WP_VALUE_NOT_CHANGED: + /* Stop. */ ++ if (b->type == bp_hardware_watchpoint ++ || b->type == bp_watchpoint) ++ { ++ /* Don't stop: write watchpoints shouldn't fire if ++ the value hasn't changed. */ ++ bs->print_it = print_it_noop; ++ bs->stop = 0; ++ continue; ++ } + ++(b->hit_count); + break; + default: +@@ -2810,7 +2926,7 @@ bpstat_stop_status (CORE_ADDR bp_addr, p + break; + } + } +- else /* found == 0 */ ++ else /* must_check_value == 0 */ + { + /* This is a case where some watchpoint(s) triggered, + but not at the address of this watchpoint (FOUND +@@ -4092,6 +4208,7 @@ set_raw_breakpoint (struct symtab_and_li + b->exec_pathname = NULL; + b->ops = NULL; + b->pending = 0; ++ b->watchpoint_triggered = 0; + + /* Add this breakpoint to the end of the chain + so that a list of breakpoints will come out in order +@@ -5450,169 +5567,6 @@ gdb_breakpoint (char *address, char *con + } + + +-static void +-break_at_finish_at_depth_command_1 (char *arg, int flag, int from_tty) +-{ +- struct frame_info *frame; +- CORE_ADDR low, high, selected_pc = 0; +- char *extra_args = NULL; +- char *level_arg; +- int extra_args_len = 0, if_arg = 0; +- +- if (!arg || +- (arg[0] == 'i' && arg[1] == 'f' && (arg[2] == ' ' || arg[2] == '\t'))) +- { +- +- if (default_breakpoint_valid) +- { +- if (deprecated_selected_frame) +- { +- selected_pc = get_frame_pc (deprecated_selected_frame); +- if (arg) +- if_arg = 1; +- } +- else +- error ("No selected frame."); +- } +- else +- error ("No default breakpoint address now."); +- } +- else +- { +- extra_args = strchr (arg, ' '); +- if (extra_args) +- { +- extra_args++; +- extra_args_len = strlen (extra_args); +- level_arg = (char *) xmalloc (extra_args - arg); +- strncpy (level_arg, arg, extra_args - arg - 1); +- level_arg[extra_args - arg - 1] = '\0'; +- } +- else +- { +- level_arg = (char *) xmalloc (strlen (arg) + 1); +- strcpy (level_arg, arg); +- } +- +- frame = parse_frame_specification (level_arg); +- if (frame) +- selected_pc = get_frame_pc (frame); +- else +- selected_pc = 0; +- } +- if (if_arg) +- { +- extra_args = arg; +- extra_args_len = strlen (arg); +- } +- +- if (selected_pc) +- { +- if (find_pc_partial_function (selected_pc, (char **) NULL, &low, &high)) +- { +- char *addr_string; +- if (extra_args_len) +- addr_string = xstrprintf ("*0x%s %s", paddr_nz (high), extra_args); +- else +- addr_string = xstrprintf ("*0x%s", paddr_nz (high)); +- break_command_1 (addr_string, flag, from_tty, NULL); +- xfree (addr_string); +- } +- else +- error ("No function contains the specified address"); +- } +- else +- error ("Unable to set breakpoint at procedure exit"); +-} +- +- +-static void +-break_at_finish_command_1 (char *arg, int flag, int from_tty) +-{ +- char *addr_string, *break_string, *beg_addr_string; +- CORE_ADDR low, high; +- struct symtabs_and_lines sals; +- struct symtab_and_line sal; +- struct cleanup *old_chain; +- char *extra_args = NULL; +- int extra_args_len = 0; +- int i, if_arg = 0; +- +- if (!arg || +- (arg[0] == 'i' && arg[1] == 'f' && (arg[2] == ' ' || arg[2] == '\t'))) +- { +- if (default_breakpoint_valid) +- { +- if (deprecated_selected_frame) +- { +- addr_string = xstrprintf ("*0x%s", +- paddr_nz (get_frame_pc (deprecated_selected_frame))); +- if (arg) +- if_arg = 1; +- } +- else +- error ("No selected frame."); +- } +- else +- error ("No default breakpoint address now."); +- } +- else +- { +- addr_string = (char *) xmalloc (strlen (arg) + 1); +- strcpy (addr_string, arg); +- } +- +- if (if_arg) +- { +- extra_args = arg; +- extra_args_len = strlen (arg); +- } +- else if (arg) +- { +- /* get the stuff after the function name or address */ +- extra_args = strchr (arg, ' '); +- if (extra_args) +- { +- extra_args++; +- extra_args_len = strlen (extra_args); +- } +- } +- +- sals.sals = NULL; +- sals.nelts = 0; +- +- beg_addr_string = addr_string; +- sals = decode_line_1 (&addr_string, 1, (struct symtab *) NULL, 0, +- (char ***) NULL, NULL); +- +- xfree (beg_addr_string); +- old_chain = make_cleanup (xfree, sals.sals); +- for (i = 0; (i < sals.nelts); i++) +- { +- sal = sals.sals[i]; +- if (find_pc_partial_function (sal.pc, (char **) NULL, &low, &high)) +- { +- break_string; +- if (extra_args_len) +- break_string = xstrprintf ("*0x%s %s", paddr_nz (high), +- extra_args); +- else +- break_string = xstrprintf ("*0x%s", paddr_nz (high)); +- break_command_1 (break_string, flag, from_tty, NULL); +- xfree (break_string); +- } +- else +- error ("No function contains the specified address"); +- } +- if (sals.nelts > 1) +- { +- warning ("Multiple breakpoints were set.\n"); +- warning ("Use the \"delete\" command to delete unwanted breakpoints."); +- } +- do_cleanups (old_chain); +-} +- +- + /* Helper function for break_command_1 and disassemble_command. */ + + void +@@ -5669,29 +5623,11 @@ break_command (char *arg, int from_tty) + } + + void +-break_at_finish_command (char *arg, int from_tty) +-{ +- break_at_finish_command_1 (arg, 0, from_tty); +-} +- +-void +-break_at_finish_at_depth_command (char *arg, int from_tty) +-{ +- break_at_finish_at_depth_command_1 (arg, 0, from_tty); +-} +- +-void + tbreak_command (char *arg, int from_tty) + { + break_command_1 (arg, BP_TEMPFLAG, from_tty, NULL); + } + +-void +-tbreak_at_finish_command (char *arg, int from_tty) +-{ +- break_at_finish_command_1 (arg, BP_TEMPFLAG, from_tty); +-} +- + static void + hbreak_command (char *arg, int from_tty) + { +--- gdb-6.3/gdb/amd64-linux-nat.c.fix Mon Dec 13 15:01:58 2004 ++++ gdb-6.3/gdb/amd64-linux-nat.c Mon Dec 13 15:10:16 2004 +@@ -233,10 +233,9 @@ amd64_linux_dr_get (int regnum) + int tid; + unsigned long value; + +- /* FIXME: kettenis/2001-01-29: It's not clear what we should do with +- multi-threaded processes here. For now, pretend there is just +- one thread. */ +- tid = PIDGET (inferior_ptid); ++ tid = TIDGET (inferior_ptid); ++ if (tid == 0) ++ tid = PIDGET (inferior_ptid); + + /* FIXME: kettenis/2001-03-27: Calling perror_with_name if the + ptrace call fails breaks debugging remote targets. The correct +@@ -261,10 +260,9 @@ amd64_linux_dr_set (int regnum, unsigned + { + int tid; + +- /* FIXME: kettenis/2001-01-29: It's not clear what we should do with +- multi-threaded processes here. For now, pretend there is just +- one thread. */ +- tid = PIDGET (inferior_ptid); ++ tid = TIDGET (inferior_ptid); ++ if (tid == 0) ++ tid = PIDGET (inferior_ptid); + + errno = 0; + ptrace (PT_WRITE_U, tid, offsetof (struct user, u_debugreg[regnum]), value); +--- gdb-6.3/gdb/breakpoint.h.fix Mon Dec 13 15:02:25 2004 ++++ gdb-6.3/gdb/breakpoint.h Mon Dec 13 15:10:24 2004 +@@ -397,6 +397,11 @@ struct breakpoint + + /* Is breakpoint pending on shlib loads? */ + int pending; ++ ++ /* Has a watchpoint been triggered? This is only used for ++ non-continuable watchpoints which trigger prior to the data ++ being modified. */ ++ int watchpoint_triggered; + }; + + /* The following stuff is an abstract data type "bpstat" ("breakpoint +@@ -663,6 +668,14 @@ extern void tbreak_command (char *, int) + + extern int insert_breakpoints (void); + ++/* The following provides a callback mechanism to insert watchpoints ++ for a new thread. This is needed, for example, on ia64 linux. */ ++typedef int (insert_watchpoint_ftype) (ptid_t, CORE_ADDR, int, int); ++extern int insert_watchpoints_for_new_thread (ptid_t ptid, ++ insert_watchpoint_ftype *fn); ++ ++extern void mark_triggered_watchpoints (CORE_ADDR); ++ + extern int remove_breakpoints (void); + + /* This function can be used to physically insert eventpoints from the +--- gdb-6.3/gdb/i386-linux-nat.c.fix Mon Dec 13 15:02:52 2004 ++++ gdb-6.3/gdb/i386-linux-nat.c Mon Dec 13 15:10:06 2004 +@@ -617,10 +617,9 @@ i386_linux_dr_get (int regnum) + int tid; + unsigned long value; + +- /* FIXME: kettenis/2001-01-29: It's not clear what we should do with +- multi-threaded processes here. For now, pretend there is just +- one thread. */ +- tid = PIDGET (inferior_ptid); ++ tid = TIDGET (inferior_ptid); ++ if (tid == 0) ++ tid = PIDGET (inferior_ptid); + + /* FIXME: kettenis/2001-03-27: Calling perror_with_name if the + ptrace call fails breaks debugging remote targets. The correct +@@ -645,10 +644,9 @@ i386_linux_dr_set (int regnum, unsigned + { + int tid; + +- /* FIXME: kettenis/2001-01-29: It's not clear what we should do with +- multi-threaded processes here. For now, pretend there is just +- one thread. */ +- tid = PIDGET (inferior_ptid); ++ tid = TIDGET (inferior_ptid); ++ if (tid == 0) ++ tid = PIDGET (inferior_ptid); + + errno = 0; + ptrace (PTRACE_POKEUSER, tid, +--- gdb-6.3/gdb/infrun.c.fix Mon Dec 13 15:03:13 2004 ++++ gdb-6.3/gdb/infrun.c Mon Dec 13 15:10:27 2004 +@@ -106,6 +106,8 @@ static ptid_t previous_inferior_ptid; + + static int may_follow_exec = MAY_FOLLOW_EXEC; + ++static int debug_infrun = 0; ++ + /* If the program uses ELF-style shared libraries, then calls to + functions in shared libraries go through stubs, which live in a + table called the PLT (Procedure Linkage Table). The first time the +@@ -161,16 +163,6 @@ static int may_follow_exec = MAY_FOLLOW_ + #define SOLIB_IN_DYNAMIC_LINKER(pid,pc) 0 + #endif + +-/* On some systems, the PC may be left pointing at an instruction that won't +- actually be executed. This is usually indicated by a bit in the PSW. If +- we find ourselves in such a state, then we step the target beyond the +- nullified instruction before returning control to the user so as to avoid +- confusion. */ +- +-#ifndef INSTRUCTION_NULLIFIED +-#define INSTRUCTION_NULLIFIED 0 +-#endif +- + /* We can't step off a permanent breakpoint in the ordinary way, because we + can't remove it. Instead, we have to advance the PC to the next + instruction. This macro should expand to a pointer to a function that +@@ -517,6 +509,9 @@ resume (int step, enum target_signal sig + struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0); + QUIT; + ++ if (debug_infrun) ++ printf_unfiltered ("infrun: resume (step=%d, signal=%d)\n", step, sig); ++ + /* FIXME: calling breakpoint_here_p (read_pc ()) three times! */ + + +@@ -714,24 +709,17 @@ proceed (CORE_ADDR addr, enum target_sig + + if (addr == (CORE_ADDR) -1) + { +- /* If there is a breakpoint at the address we will resume at, +- step one instruction before inserting breakpoints +- so that we do not stop right away (and report a second +- hit at this breakpoint). */ +- + if (read_pc () == stop_pc && breakpoint_here_p (read_pc ())) ++ /* There is a breakpoint at the address we will resume at, ++ step one instruction before inserting breakpoints so that ++ we do not stop right away (and report a second hit at this ++ breakpoint). */ + oneproc = 1; +- +-#ifndef STEP_SKIPS_DELAY +-#define STEP_SKIPS_DELAY(pc) (0) +-#define STEP_SKIPS_DELAY_P (0) +-#endif +- /* Check breakpoint_here_p first, because breakpoint_here_p is fast +- (it just checks internal GDB data structures) and STEP_SKIPS_DELAY +- is slow (it needs to read memory from the target). */ +- if (STEP_SKIPS_DELAY_P +- && breakpoint_here_p (read_pc () + 4) +- && STEP_SKIPS_DELAY (read_pc ())) ++ else if (gdbarch_single_step_through_delay_p (current_gdbarch) ++ && gdbarch_single_step_through_delay (current_gdbarch, ++ get_current_frame ())) ++ /* We stepped onto an instruction that needs to be stepped ++ again before re-inserting the breakpoint, do so. */ + oneproc = 1; + } + else +@@ -739,6 +727,10 @@ proceed (CORE_ADDR addr, enum target_sig + write_pc (addr); + } + ++ if (debug_infrun) ++ printf_unfiltered ("infrun: proceed (addr=0x%s, signal=%d, step=%d)\n", ++ paddr_nz (addr), siggnal, step); ++ + /* In a multi-threaded task we may select another thread + and then continue or step. + +@@ -878,7 +870,6 @@ enum infwait_states + { + infwait_normal_state, + infwait_thread_hop_state, +- infwait_nullified_state, + infwait_nonstep_watch_state + }; + +@@ -957,6 +948,9 @@ wait_for_inferior (void) + struct execution_control_state ecss; + struct execution_control_state *ecs; + ++ if (debug_infrun) ++ printf_unfiltered ("infrun: wait_for_inferior\n"); ++ + old_cleanups = make_cleanup (delete_step_resume_breakpoint, + &step_resume_breakpoint); + +@@ -1241,6 +1235,8 @@ handle_inferior_event (struct execution_ + switch (ecs->infwait_state) + { + case infwait_thread_hop_state: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: infwait_thread_hop_state\n"); + /* Cancel the waiton_ptid. */ + ecs->waiton_ptid = pid_to_ptid (-1); + /* See comments where a TARGET_WAITKIND_SYSCALL_RETURN event +@@ -1254,6 +1250,8 @@ handle_inferior_event (struct execution_ + break; + + case infwait_normal_state: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: infwait_normal_state\n"); + /* See comments where a TARGET_WAITKIND_SYSCALL_RETURN event + is serviced in this loop, below. */ + if (ecs->enable_hw_watchpoints_after_wait) +@@ -1264,11 +1262,9 @@ handle_inferior_event (struct execution_ + stepped_after_stopped_by_watchpoint = 0; + break; + +- case infwait_nullified_state: +- stepped_after_stopped_by_watchpoint = 0; +- break; +- + case infwait_nonstep_watch_state: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: infwait_nonstep_watch_state\n"); + insert_breakpoints (); + + /* FIXME-maybe: is this cleaner than setting a flag? Does it +@@ -1303,6 +1299,8 @@ handle_inferior_event (struct execution_ + switch (ecs->ws.kind) + { + case TARGET_WAITKIND_LOADED: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: TARGET_WAITKIND_LOADED\n"); + /* Ignore gracefully during startup of the inferior, as it + might be the shell which has just loaded some objects, + otherwise add the symbols for the newly loaded objects. */ +@@ -1347,11 +1345,15 @@ handle_inferior_event (struct execution_ + return; + + case TARGET_WAITKIND_SPURIOUS: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: TARGET_WAITKIND_SPURIOUS\n"); + resume (0, TARGET_SIGNAL_0); + prepare_to_wait (ecs); + return; + + case TARGET_WAITKIND_EXITED: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: TARGET_WAITKIND_EXITED\n"); + target_terminal_ours (); /* Must do this before mourn anyway */ + print_stop_reason (EXITED, ecs->ws.value.integer); + +@@ -1368,6 +1370,8 @@ handle_inferior_event (struct execution_ + return; + + case TARGET_WAITKIND_SIGNALLED: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: TARGET_WAITKIND_SIGNALLED\n"); + stop_print_frame = 0; + stop_signal = ecs->ws.value.sig; + target_terminal_ours (); /* Must do this before mourn anyway */ +@@ -1388,6 +1392,8 @@ handle_inferior_event (struct execution_ + the above cases end in a continue or goto. */ + case TARGET_WAITKIND_FORKED: + case TARGET_WAITKIND_VFORKED: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: TARGET_WAITKIND_FORKED\n"); + stop_signal = TARGET_SIGNAL_TRAP; + pending_follow.kind = ecs->ws.kind; + +@@ -1410,6 +1416,8 @@ handle_inferior_event (struct execution_ + goto process_event_stop_test; + + case TARGET_WAITKIND_EXECD: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: TARGET_WAITKIND_EXECED\n"); + stop_signal = TARGET_SIGNAL_TRAP; + + /* NOTE drow/2002-12-05: This code should be pushed down into the +@@ -1477,6 +1485,8 @@ handle_inferior_event (struct execution_ + Also, be careful not to try to gather much state about a thread + that's in a syscall. It's frequently a losing proposition. */ + case TARGET_WAITKIND_SYSCALL_ENTRY: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: TARGET_WAITKIND_SYSCALL_ENTRY\n"); + number_of_threads_in_syscalls++; + if (number_of_threads_in_syscalls == 1) + { +@@ -1501,6 +1511,8 @@ handle_inferior_event (struct execution_ + here, which will be serviced immediately after the target + is waited on. */ + case TARGET_WAITKIND_SYSCALL_RETURN: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: TARGET_WAITKIND_SYSCALL_RETURN\n"); + target_resume (ecs->ptid, 1, TARGET_SIGNAL_0); + + if (number_of_threads_in_syscalls > 0) +@@ -1513,6 +1525,8 @@ handle_inferior_event (struct execution_ + return; + + case TARGET_WAITKIND_STOPPED: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: TARGET_WAITKIND_STOPPED\n"); + stop_signal = ecs->ws.value.sig; + break; + +@@ -1526,6 +1540,8 @@ handle_inferior_event (struct execution_ + circumstance is any event which the lower level knows will be + reported multiple times without an intervening resume. */ + case TARGET_WAITKIND_IGNORE: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: TARGET_WAITKIND_IGNORE\n"); + prepare_to_wait (ecs); + return; + } +@@ -1546,6 +1562,9 @@ handle_inferior_event (struct execution_ + + stop_pc = read_pc_pid (ecs->ptid); + ++ if (debug_infrun) ++ printf_unfiltered ("infrun: stop_pc = 0x%s\n", paddr_nz (stop_pc)); ++ + if (stepping_past_singlestep_breakpoint) + { + gdb_assert (SOFTWARE_SINGLE_STEP_P () +@@ -1560,6 +1579,8 @@ handle_inferior_event (struct execution_ + we could tell, but we can't reliably. */ + if (stop_signal == TARGET_SIGNAL_TRAP) + { ++ if (debug_infrun) ++ printf_unfiltered ("infrun: stepping_past_singlestep_breakpoint\n"); + /* Pull the single step breakpoints out of the target. */ + SOFTWARE_SINGLE_STEP (0, 0); + singlestep_breakpoints_inserted_p = 0; +@@ -1616,6 +1637,9 @@ handle_inferior_event (struct execution_ + { + int remove_status; + ++ if (debug_infrun) ++ printf_unfiltered ("infrun: thread_hop_needed\n"); ++ + /* Saw a breakpoint, but it was hit by the wrong thread. + Just continue. */ + +@@ -1681,6 +1705,9 @@ handle_inferior_event (struct execution_ + so, then switch to that thread. */ + if (!ptid_equal (ecs->ptid, inferior_ptid)) + { ++ if (debug_infrun) ++ printf_unfiltered ("infrun: context switch\n"); ++ + context_switch (ecs); + + if (deprecated_context_hook) +@@ -1696,33 +1723,24 @@ handle_inferior_event (struct execution_ + singlestep_breakpoints_inserted_p = 0; + } + +- /* If PC is pointing at a nullified instruction, then step beyond +- it so that the user won't be confused when GDB appears to be ready +- to execute it. */ +- +- /* if (INSTRUCTION_NULLIFIED && currently_stepping (ecs)) */ +- if (INSTRUCTION_NULLIFIED) +- { +- registers_changed (); +- target_resume (ecs->ptid, 1, TARGET_SIGNAL_0); +- +- /* We may have received a signal that we want to pass to +- the inferior; therefore, we must not clobber the waitstatus +- in WS. */ +- +- ecs->infwait_state = infwait_nullified_state; +- ecs->waiton_ptid = ecs->ptid; +- ecs->wp = &(ecs->tmpstatus); +- prepare_to_wait (ecs); +- return; +- } +- + /* It may not be necessary to disable the watchpoint to stop over + it. For example, the PA can (with some kernel cooperation) + single step over a watchpoint without disabling the watchpoint. */ + if (HAVE_STEPPABLE_WATCHPOINT && STOPPED_BY_WATCHPOINT (ecs->ws)) + { +- resume (1, 0); ++ CORE_ADDR addr = 0; ++ ++ if (debug_infrun) ++ printf_unfiltered ("infrun: STOPPED_BY_WATCHPOINT\n"); ++ ++ target_stopped_data_address (¤t_target, &addr); ++ mark_triggered_watchpoints (addr); ++ registers_changed (); ++ target_resume (ecs->ptid, 1, TARGET_SIGNAL_0); /* Single step */ ++ ++ ecs->waiton_ptid = ecs->ptid; ++ ecs->wp = &(ecs->ws); ++ ecs->infwait_state = infwait_nonstep_watch_state; + prepare_to_wait (ecs); + return; + } +@@ -1732,6 +1750,8 @@ handle_inferior_event (struct execution_ + register or page protection watchpoint scheme need here? */ + if (HAVE_NONSTEPPABLE_WATCHPOINT && STOPPED_BY_WATCHPOINT (ecs->ws)) + { ++ CORE_ADDR addr = 0; ++ + /* At this point, we are stopped at an instruction which has + attempted to write to a piece of memory under control of + a watchpoint. The instruction hasn't actually executed +@@ -1739,15 +1759,15 @@ handle_inferior_event (struct execution_ + now, we would get the old value, and therefore no change + would seem to have occurred. + +- In order to make watchpoints work `right', we really need +- to complete the memory write, and then evaluate the +- watchpoint expression. The following code does that by +- removing the watchpoint (actually, all watchpoints and +- breakpoints), single-stepping the target, re-inserting +- watchpoints, and then falling through to let normal +- single-step processing handle proceed. Since this +- includes evaluating watchpoints, things will come to a +- stop in the correct manner. */ ++ In order to make watchpoints work `right', we mark the ++ triggered watchpoints so that after we single step, ++ we will check for a value change. */ ++ ++ if (debug_infrun) ++ printf_unfiltered ("infrun: STOPPED_BY_WATCHPOINT\n"); ++ ++ target_stopped_data_address (¤t_target, &addr); ++ mark_triggered_watchpoints (addr); + + remove_breakpoints (); + registers_changed (); +@@ -1781,6 +1801,41 @@ handle_inferior_event (struct execution_ + stopped_by_random_signal = 0; + breakpoints_failed = 0; + ++ if (stop_signal == TARGET_SIGNAL_TRAP ++ && trap_expected ++ && gdbarch_single_step_through_delay_p (current_gdbarch) ++ && currently_stepping (ecs)) ++ { ++ /* We're trying to step of a breakpoint. Turns out that we're ++ also on an instruction that needs to be stepped multiple ++ times before it's been fully executing. E.g., architectures ++ with a delay slot. It needs to be stepped twice, once for ++ the instruction and once for the delay slot. */ ++ int step_through_delay ++ = gdbarch_single_step_through_delay (current_gdbarch, ++ get_current_frame ()); ++ if (debug_infrun && step_through_delay) ++ printf_unfiltered ("infrun: step through delay\n"); ++ if (step_range_end == 0 && step_through_delay) ++ { ++ /* The user issued a continue when stopped at a breakpoint. ++ Set up for another trap and get out of here. */ ++ ecs->another_trap = 1; ++ keep_going (ecs); ++ return; ++ } ++ else if (step_through_delay) ++ { ++ /* The user issued a step when stopped at a breakpoint. ++ Maybe we should stop, maybe we should not - the delay ++ slot *might* correspond to a line of source. In any ++ case, don't decide that here, just set ecs->another_trap, ++ making sure we single-step again before breakpoints are ++ re-inserted. */ ++ ecs->another_trap = 1; ++ } ++ } ++ + /* Look at the cause of the stop, and decide what to do. + The alternatives are: + 1) break; to really stop and return to the debugger, +@@ -1809,6 +1864,8 @@ handle_inferior_event (struct execution_ + { + if (stop_signal == TARGET_SIGNAL_TRAP && stop_after_trap) + { ++ if (debug_infrun) ++ printf_unfiltered ("infrun: stopped\n"); + stop_print_frame = 0; + stop_stepping (ecs); + return; +@@ -1818,6 +1875,8 @@ handle_inferior_event (struct execution_ + shared libraries hook functions. */ + if (stop_soon == STOP_QUIETLY) + { ++ if (debug_infrun) ++ printf_unfiltered ("infrun: quietly stopped\n"); + stop_stepping (ecs); + return; + } +@@ -1837,7 +1896,11 @@ handle_inferior_event (struct execution_ + /* Don't even think about breakpoints if just proceeded over a + breakpoint. */ + if (stop_signal == TARGET_SIGNAL_TRAP && trap_expected) +- bpstat_clear (&stop_bpstat); ++ { ++ if (debug_infrun) ++ printf_unfiltered ("infrun: trap expected\n"); ++ bpstat_clear (&stop_bpstat); ++ } + else + { + /* See if there is a breakpoint at the current PC. */ +@@ -1898,6 +1961,9 @@ process_event_stop_test: + /* Signal not for debugging purposes. */ + int printed = 0; + ++ if (debug_infrun) ++ printf_unfiltered ("infrun: random signal %d\n", stop_signal); ++ + stopped_by_random_signal = 1; + + if (signal_print[stop_signal]) +@@ -1977,6 +2043,8 @@ process_event_stop_test: + /* If we hit the breakpoint at longjmp, disable it for the + duration of this command. Then, install a temporary + breakpoint at the target of the jmp_buf. */ ++ if (debug_infrun) ++ printf_unfiltered ("infrun: BPSTATE_WHAT_SET_LONGJMP_RESUME\n"); + disable_longjmp_breakpoint (); + remove_breakpoints (); + breakpoints_inserted = 0; +@@ -2000,6 +2068,8 @@ process_event_stop_test: + + case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME: + case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: BPSTATE_WHAT_CLEAR_LONGJMP_RESUME\n"); + remove_breakpoints (); + breakpoints_inserted = 0; + disable_longjmp_breakpoint (); +@@ -2009,6 +2079,8 @@ process_event_stop_test: + /* else fallthrough */ + + case BPSTAT_WHAT_SINGLE: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: BPSTATE_WHAT_SINGLE\n"); + if (breakpoints_inserted) + { + remove_breakpoints (); +@@ -2020,6 +2092,8 @@ process_event_stop_test: + break; + + case BPSTAT_WHAT_STOP_NOISY: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: BPSTATE_WHAT_STOP_NOISY\n"); + stop_print_frame = 1; + + /* We are about to nuke the step_resume_breakpointt via the +@@ -2029,6 +2103,8 @@ process_event_stop_test: + return; + + case BPSTAT_WHAT_STOP_SILENT: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: BPSTATE_WHAT_STOP_SILENT\n"); + stop_print_frame = 0; + + /* We are about to nuke the step_resume_breakpoin via the +@@ -2055,6 +2131,9 @@ process_event_stop_test: + step-resume bp, but it makes no effort to ensure that + the one deleted is the one currently stopped at. MVS */ + ++ if (debug_infrun) ++ printf_unfiltered ("infrun: BPSTATE_WHAT_STEP_RESUME\n"); ++ + if (step_resume_breakpoint == NULL) + { + step_resume_breakpoint = +@@ -2076,6 +2155,8 @@ process_event_stop_test: + break; + + case BPSTAT_WHAT_THROUGH_SIGTRAMP: ++ if (debug_infrun) ++ printf_unfiltered ("infrun: BPSTATE_WHAT_THROUGH_SIGTRAMP\n"); + /* If were waiting for a trap, hitting the step_resume_break + doesn't count as getting it. */ + if (trap_expected) +@@ -2086,6 +2167,8 @@ process_event_stop_test: + case BPSTAT_WHAT_CHECK_SHLIBS_RESUME_FROM_HOOK: + #ifdef SOLIB_ADD + { ++ if (debug_infrun) ++ printf_unfiltered ("infrun: BPSTATE_WHAT_CHECK_SHLIBS\n"); + /* Remove breakpoints, we eventually want to step over the + shlib event breakpoint, and SOLIB_ADD might adjust + breakpoint addresses via breakpoint_re_set. */ +@@ -2198,11 +2281,15 @@ process_event_stop_test: + /* Have we reached our destination? If not, keep going. */ + if (SOLIB_IN_DYNAMIC_LINKER (PIDGET (ecs->ptid), stop_pc)) + { ++ if (debug_infrun) ++ printf_unfiltered ("infrun: stepping in dynamic linker\n"); + ecs->another_trap = 1; + keep_going (ecs); + return; + } + #endif ++ if (debug_infrun) ++ printf_unfiltered ("infrun: step past dynamic linker\n"); + /* Else, stop and report the catchpoint(s) whose triggering + caused us to begin stepping. */ + ecs->stepping_through_solib_after_catch = 0; +@@ -2216,6 +2303,9 @@ process_event_stop_test: + + if (step_resume_breakpoint) + { ++ if (debug_infrun) ++ printf_unfiltered ("infrun: step-resume breakpoint\n"); ++ + /* Having a step-resume breakpoint overrides anything + else having to do with stepping commands until + that breakpoint is reached. */ +@@ -2225,6 +2315,8 @@ process_event_stop_test: + + if (step_range_end == 0) + { ++ if (debug_infrun) ++ printf_unfiltered ("infrun: no stepping, continue\n"); + /* Likewise if we aren't even stepping. */ + keep_going (ecs); + return; +@@ -2237,6 +2329,10 @@ process_event_stop_test: + within it! */ + if (stop_pc >= step_range_start && stop_pc < step_range_end) + { ++ if (debug_infrun) ++ printf_unfiltered ("infrun: stepping inside range [0x%s-0x%s]\n", ++ paddr_nz (step_range_start), ++ paddr_nz (step_range_end)); + keep_going (ecs); + return; + } +@@ -2253,6 +2349,9 @@ process_event_stop_test: + CORE_ADDR pc_after_resolver = + gdbarch_skip_solib_resolver (current_gdbarch, stop_pc); + ++ if (debug_infrun) ++ printf_unfiltered ("infrun: stepped into dynsym resolve code\n"); ++ + if (pc_after_resolver) + { + /* Set up a step-resume breakpoint at the address +@@ -2273,6 +2372,8 @@ process_event_stop_test: + || step_over_calls == STEP_OVER_ALL) + && get_frame_type (get_current_frame ()) == SIGTRAMP_FRAME) + { ++ if (debug_infrun) ++ printf_unfiltered ("infrun: stepped into signal trampoline\n"); + /* The inferior, while doing a "step" or "next", has ended up in + a signal trampoline (either by a signal being delivered or by + the signal handler returning). Just single-step until the +@@ -2287,6 +2388,9 @@ process_event_stop_test: + /* It's a subroutine call. */ + CORE_ADDR real_stop_pc; + ++ if (debug_infrun) ++ printf_unfiltered ("infrun: stepped into subroutine\n"); ++ + if ((step_over_calls == STEP_OVER_NONE) + || ((step_range_end == 1) + && in_prologue (prev_pc, ecs->stop_func_start))) +@@ -2303,27 +2407,6 @@ process_event_stop_test: + return; + } + +-#ifdef DEPRECATED_IGNORE_HELPER_CALL +- /* On MIPS16, a function that returns a floating point value may +- call a library helper function to copy the return value to a +- floating point register. The DEPRECATED_IGNORE_HELPER_CALL +- macro returns non-zero if we should ignore (i.e. step over) +- this function call. */ +- /* FIXME: cagney/2004-07-21: These custom ``ignore frame when +- stepping'' function attributes (SIGTRAMP_FRAME, +- DEPRECATED_IGNORE_HELPER_CALL, SKIP_TRAMPOLINE_CODE, +- skip_language_trampoline frame, et.al.) need to be replaced +- with generic attributes bound to the frame's function. */ +- if (DEPRECATED_IGNORE_HELPER_CALL (stop_pc)) +- { +- /* We're doing a "next", set a breakpoint at callee's return +- address (the address at which the caller will +- resume). */ +- insert_step_resume_breakpoint_at_frame (get_prev_frame (get_current_frame ())); +- keep_going (ecs); +- return; +- } +-#endif + if (step_over_calls == STEP_OVER_ALL) + { + /* We're doing a "next", set a breakpoint at callee's return +@@ -2398,6 +2481,9 @@ process_event_stop_test: + /* Determine where this trampoline returns. */ + CORE_ADDR real_stop_pc = SKIP_TRAMPOLINE_CODE (stop_pc); + ++ if (debug_infrun) ++ printf_unfiltered ("infrun: stepped into solib return tramp\n"); ++ + /* Only proceed through if we know where it's going. */ + if (real_stop_pc) + { +@@ -2426,6 +2512,9 @@ process_event_stop_test: + if (step_over_calls == STEP_OVER_UNDEBUGGABLE + && ecs->stop_func_name == NULL) + { ++ if (debug_infrun) ++ printf_unfiltered ("infrun: stepped into undebuggable function\n"); ++ + /* The inferior just stepped into, or returned to, an + undebuggable function (where there is no symbol, not even a + minimal symbol, corresponding to the address where the +@@ -2456,6 +2545,8 @@ process_event_stop_test: + { + /* It is stepi or nexti. We always want to stop stepping after + one instruction. */ ++ if (debug_infrun) ++ printf_unfiltered ("infrun: stepi/nexti\n"); + stop_step = 1; + print_stop_reason (END_STEPPING_RANGE, 0); + stop_stepping (ecs); +@@ -2470,6 +2561,8 @@ process_event_stop_test: + stepping (does this always happen right after one instruction, + when we do "s" in a function with no line numbers, + or can this happen as a result of a return or longjmp?). */ ++ if (debug_infrun) ++ printf_unfiltered ("infrun: no line number info\n"); + stop_step = 1; + print_stop_reason (END_STEPPING_RANGE, 0); + stop_stepping (ecs); +@@ -2484,6 +2577,8 @@ process_event_stop_test: + we don't stop if we step into the middle of a different line. + That is said to make things like for (;;) statements work + better. */ ++ if (debug_infrun) ++ printf_unfiltered ("infrun: stepped to a different line\n"); + stop_step = 1; + print_stop_reason (END_STEPPING_RANGE, 0); + stop_stepping (ecs); +@@ -2504,6 +2599,8 @@ process_event_stop_test: + This is particularly necessary for a one-line function, + in which after skipping the prologue we better stop even though + we will be in mid-line. */ ++ if (debug_infrun) ++ printf_unfiltered ("infrun: stepped to a different function\n"); + stop_step = 1; + print_stop_reason (END_STEPPING_RANGE, 0); + stop_stepping (ecs); +@@ -2540,6 +2637,8 @@ process_event_stop_test: + step_frame_id = current_frame; + } + ++ if (debug_infrun) ++ printf_unfiltered ("infrun: keep going\n"); + keep_going (ecs); + } + +@@ -2676,6 +2775,9 @@ insert_step_resume_breakpoint_at_frame ( + static void + stop_stepping (struct execution_control_state *ecs) + { ++ if (debug_infrun) ++ printf_unfiltered ("infrun: stop_stepping\n"); ++ + /* Let callers know we don't want to wait for the inferior anymore. */ + ecs->wait_some_more = 0; + } +@@ -2753,6 +2855,8 @@ keep_going (struct execution_control_sta + static void + prepare_to_wait (struct execution_control_state *ecs) + { ++ if (debug_infrun) ++ printf_unfiltered ("infrun: prepare_to_wait\n"); + if (ecs->infwait_state == infwait_normal_state) + { + overlay_cache_invalid = 1; +@@ -3014,7 +3118,7 @@ normal_stop (void) + LOCATION: Print only location + SRC_AND_LOC: Print location and source line */ + if (do_frame_printing) +- print_stack_frame (get_selected_frame (), 0, source_flag); ++ print_stack_frame (get_selected_frame (NULL), 0, source_flag); + + /* Display the auto-display expressions. */ + do_displays (); +@@ -3790,6 +3894,10 @@ Pass and Stop may be combined.", NULL)); + This allows you to set a list of commands to be run each time execution\n\ + of the program stops.", &cmdlist); + ++ add_set_cmd ("infrun", class_maintenance, var_zinteger, ++ &debug_infrun, "Set inferior debugging.\n\ ++When non-zero, inferior specific debugging is enabled.", &setdebuglist); ++ + numsigs = (int) TARGET_SIGNAL_LAST; + signal_stop = (unsigned char *) xmalloc (sizeof (signal_stop[0]) * numsigs); + signal_print = (unsigned char *) +--- gdb-6.3/gdb/linux-nat.h.fix Mon Dec 13 15:03:36 2004 ++++ gdb-6.3/gdb/linux-nat.h Mon Dec 13 15:10:41 2004 +@@ -61,6 +61,18 @@ struct lwp_info + + /* Next LWP in list. */ + struct lwp_info *next; ++ ++ /* Optional saved trap state for when a trap gets pushed back ++ due to multiple events occurring at the same time. */ ++ void *saved_trap_data; ++}; ++ ++/* Watchpoint description. */ ++struct linux_watchpoint ++{ ++ CORE_ADDR addr; ++ int len; ++ int type; + }; + + /* Read/write to target memory via the Linux kernel's "proc file +--- gdb-6.3/gdb/thread-db.c.fix Mon Dec 13 15:03:48 2004 ++++ gdb-6.3/gdb/thread-db.c Mon Dec 13 15:49:44 2004 +@@ -34,6 +34,7 @@ + #include "target.h" + #include "regcache.h" + #include "solib-svr4.h" ++#include "observer.h" + + #ifdef HAVE_GNU_LIBC_VERSION_H + #include +@@ -143,7 +144,6 @@ static void detach_thread (ptid_t ptid, + #define is_thread(ptid) (GET_THREAD (ptid) != 0) + + #define BUILD_LWP(lwp, pid) ptid_build (pid, lwp, 0) +-#define BUILD_THREAD(tid, pid) ptid_build (pid, 0, tid) + + + /* Use "struct private_thread_info" to cache thread state. This is +@@ -267,7 +267,7 @@ thread_get_info_callback (const td_thrha + thread_db_err_str (err)); + + /* Fill the cache. */ +- thread_ptid = BUILD_THREAD (ti.ti_tid, GET_PID (inferior_ptid)); ++ thread_ptid = ptid_build (GET_PID (inferior_ptid), ti.ti_lid, ti.ti_tid); + thread_info = find_thread_pid (thread_ptid); + + /* In the case of a zombie thread, don't continue. We don't want to +@@ -385,22 +385,14 @@ thread_from_lwp (ptid_t ptid) + + gdb_assert (thread_info && thread_info->private->ti_valid); + +- return BUILD_THREAD (thread_info->private->ti.ti_tid, GET_PID (ptid)); ++ return ptid_build (GET_PID (ptid), GET_LWP (ptid), ++ thread_info->private->ti.ti_tid); + } + + static ptid_t + lwp_from_thread (ptid_t ptid) + { +- struct thread_info *thread_info; +- ptid_t thread_ptid; +- +- if (!is_thread (ptid)) +- return ptid; +- +- thread_info = find_thread_pid (ptid); +- thread_db_get_info (thread_info); +- +- return BUILD_LWP (thread_info->private->ti.ti_lid, GET_PID (ptid)); ++ return BUILD_LWP (GET_LWP (ptid), GET_PID (ptid)); + } + + +@@ -722,6 +714,7 @@ attach_thread (ptid_t ptid, const td_thr + { + struct thread_info *tp; + td_err_e err; ++ ptid_t new_ptid; + + /* If we're being called after a TD_CREATE event, we may already + know about this thread. There are two ways this can happen. We +@@ -757,11 +750,18 @@ attach_thread (ptid_t ptid, const td_thr + if (ti_p->ti_state == TD_THR_UNKNOWN || ti_p->ti_state == TD_THR_ZOMBIE) + return; /* A zombie thread -- do not attach. */ + ++ new_ptid = BUILD_LWP (ti_p->ti_lid, GET_PID (ptid)); ++ + /* Under GNU/Linux, we have to attach to each and every thread. */ + #ifdef ATTACH_LWP +- ATTACH_LWP (BUILD_LWP (ti_p->ti_lid, GET_PID (ptid)), 0); ++ ATTACH_LWP (new_ptid, 0); + #endif + ++ /* Notify any observers of a new linux thread. This ++ would include any linux platforms that have to insert hardware ++ watchpoints on every thread. */ ++ observer_notify_linux_new_thread (new_ptid); ++ + /* Enable thread event reporting for this thread. */ + err = td_thr_event_enable_p (th_p, 1); + if (err != TD_OK) +@@ -887,7 +887,7 @@ check_event (ptid_t ptid) + if (err != TD_OK) + error ("Cannot get thread info: %s", thread_db_err_str (err)); + +- ptid = BUILD_THREAD (ti.ti_tid, GET_PID (ptid)); ++ ptid = ptid_build (GET_PID (ptid), ti.ti_lid, ti.ti_tid); + + switch (msg.event) + { +@@ -934,7 +934,8 @@ thread_db_wait (ptid_t ptid, struct targ + return pid_to_ptid (-1); + + if (ourstatus->kind == TARGET_WAITKIND_STOPPED +- && ourstatus->value.sig == TARGET_SIGNAL_TRAP) ++ && (ourstatus->value.sig == TARGET_SIGNAL_TRAP ++ || ourstatus->value.sig == TARGET_SIGNAL_ILL)) + /* Check for a thread event. */ + check_event (ptid); + +@@ -1159,7 +1160,7 @@ find_new_threads_callback (const td_thrh + if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE) + return 0; /* A zombie -- ignore. */ + +- ptid = BUILD_THREAD (ti.ti_tid, GET_PID (inferior_ptid)); ++ ptid = ptid_build (GET_PID (inferior_ptid), ti.ti_lid, ti.ti_tid); + + if (!in_thread_list (ptid)) + attach_thread (ptid, th_p, &ti, 1); +--- gdb-6.3/gdb/linux-nat.c.fix Mon Dec 13 15:42:30 2004 ++++ gdb-6.3/gdb/linux-nat.c Mon Dec 13 15:43:58 2004 +@@ -34,6 +34,7 @@ + #include "gdbthread.h" + #include "gdbcmd.h" + #include "regcache.h" ++#include "observer.h" + #include /* for MAXPATHLEN */ + #include /* for elf_gregset etc. */ + #include "elf-bfd.h" /* for elfcore_write_* */ +@@ -69,7 +70,7 @@ + #define PTRACE_EVENT_VFORK 2 + #define PTRACE_EVENT_CLONE 3 + #define PTRACE_EVENT_EXEC 4 +-#define PTRACE_EVENT_VFORKDONE 5 ++#define PTRACE_EVENT_VFORK_DONE 5 + #define PTRACE_EVENT_EXIT 6 + + #endif /* PTRACE_EVENT_FORK */ +@@ -147,21 +148,50 @@ linux_tracefork_child (void) + ptrace (PTRACE_TRACEME, 0, 0, 0); + kill (getpid (), SIGSTOP); + fork (); +- exit (0); ++ _exit (0); + } + +-/* Determine if PTRACE_O_TRACEFORK can be used to follow fork events. We ++/* Wrapper function for waitpid which handles EINTR. */ ++ ++static int ++my_waitpid (int pid, int *status, int flags) ++{ ++ int ret; ++ do ++ { ++ ret = waitpid (pid, status, flags); ++ } ++ while (ret == -1 && errno == EINTR); ++ ++ return ret; ++} ++ ++/* Determine if PTRACE_O_TRACEFORK can be used to follow fork events. ++ ++ First, we try to enable fork tracing on ORIGINAL_PID. If this fails, ++ we know that the feature is not available. This may change the tracing ++ options for ORIGINAL_PID, but we'll be setting them shortly anyway. ++ ++ However, if it succeeds, we don't know for sure that the feature is ++ available; old versions of PTRACE_SETOPTIONS ignored unknown options. We + create a child process, attach to it, use PTRACE_SETOPTIONS to enable +- fork tracing, and let it fork. If the process exits, we assume that +- we can't use TRACEFORK; if we get the fork notification, and we can +- extract the new child's PID, then we assume that we can. */ ++ fork tracing, and let it fork. If the process exits, we assume that we ++ can't use TRACEFORK; if we get the fork notification, and we can extract ++ the new child's PID, then we assume that we can. */ + + static void +-linux_test_for_tracefork (void) ++linux_test_for_tracefork (int original_pid) + { + int child_pid, ret, status; + long second_pid; + ++ linux_supports_tracefork_flag = 0; ++ linux_supports_tracevforkdone_flag = 0; ++ ++ ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACEFORK); ++ if (ret != 0) ++ return; ++ + child_pid = fork (); + if (child_pid == -1) + perror_with_name ("linux_test_for_tracefork: fork"); +@@ -169,7 +199,7 @@ linux_test_for_tracefork (void) + if (child_pid == 0) + linux_tracefork_child (); + +- ret = waitpid (child_pid, &status, 0); ++ ret = my_waitpid (child_pid, &status, 0); + if (ret == -1) + perror_with_name ("linux_test_for_tracefork: waitpid"); + else if (ret != child_pid) +@@ -177,13 +207,23 @@ linux_test_for_tracefork (void) + if (! WIFSTOPPED (status)) + error ("linux_test_for_tracefork: waitpid: unexpected status %d.", status); + +- linux_supports_tracefork_flag = 0; +- + ret = ptrace (PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_TRACEFORK); + if (ret != 0) + { +- ptrace (PTRACE_KILL, child_pid, 0, 0); +- waitpid (child_pid, &status, 0); ++ ret = ptrace (PTRACE_KILL, child_pid, 0, 0); ++ if (ret != 0) ++ { ++ warning ("linux_test_for_tracefork: failed to kill child"); ++ return; ++ } ++ ++ ret = my_waitpid (child_pid, &status, 0); ++ if (ret != child_pid) ++ warning ("linux_test_for_tracefork: failed to wait for killed child"); ++ else if (!WIFSIGNALED (status)) ++ warning ("linux_test_for_tracefork: unexpected wait status 0x%x from " ++ "killed child", status); ++ + return; + } + +@@ -192,8 +232,12 @@ linux_test_for_tracefork (void) + PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORKDONE); + linux_supports_tracevforkdone_flag = (ret == 0); + +- ptrace (PTRACE_CONT, child_pid, 0, 0); +- ret = waitpid (child_pid, &status, 0); ++ ret = ptrace (PTRACE_CONT, child_pid, 0, 0); ++ if (ret != 0) ++ warning ("linux_test_for_tracefork: failed to resume child"); ++ ++ ret = my_waitpid (child_pid, &status, 0); ++ + if (ret == child_pid && WIFSTOPPED (status) + && status >> 16 == PTRACE_EVENT_FORK) + { +@@ -204,34 +248,38 @@ linux_test_for_tracefork (void) + int second_status; + + linux_supports_tracefork_flag = 1; +- waitpid (second_pid, &second_status, 0); +- ptrace (PTRACE_DETACH, second_pid, 0, 0); ++ my_waitpid (second_pid, &second_status, 0); ++ ret = ptrace (PTRACE_KILL, second_pid, 0, 0); ++ if (ret != 0) ++ warning ("linux_test_for_tracefork: failed to kill second child"); + } + } ++ else ++ warning ("linux_test_for_tracefork: unexpected result from waitpid " ++ "(%d, status 0x%x)", ret, status); + +- if (WIFSTOPPED (status)) +- { +- ptrace (PTRACE_DETACH, child_pid, 0, 0); +- waitpid (child_pid, &status, 0); +- } ++ ret = ptrace (PTRACE_KILL, child_pid, 0, 0); ++ if (ret != 0) ++ warning ("linux_test_for_tracefork: failed to kill child"); ++ my_waitpid (child_pid, &status, 0); + } + + /* Return non-zero iff we have tracefork functionality available. + This function also sets linux_supports_tracefork_flag. */ + + static int +-linux_supports_tracefork (void) ++linux_supports_tracefork (int pid) + { + if (linux_supports_tracefork_flag == -1) +- linux_test_for_tracefork (); ++ linux_test_for_tracefork (pid); + return linux_supports_tracefork_flag; + } + + static int +-linux_supports_tracevforkdone (void) ++linux_supports_tracevforkdone (int pid) + { + if (linux_supports_tracefork_flag == -1) +- linux_test_for_tracefork (); ++ linux_test_for_tracefork (pid); + return linux_supports_tracevforkdone_flag; + } + +@@ -242,12 +290,12 @@ linux_enable_event_reporting (ptid_t pti + int pid = ptid_get_pid (ptid); + int options; + +- if (! linux_supports_tracefork ()) ++ if (! linux_supports_tracefork (pid)) + return; + + options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC + | PTRACE_O_TRACECLONE; +- if (linux_supports_tracevforkdone ()) ++ if (linux_supports_tracevforkdone (pid)) + options |= PTRACE_O_TRACEVFORKDONE; + + /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to support +@@ -308,13 +356,14 @@ child_follow_fork (int follow_child) + + if (has_vforked) + { +- if (linux_supports_tracevforkdone ()) ++ gdb_assert (linux_supports_tracefork_flag >= 0); ++ if (linux_supports_tracevforkdone (0)) + { + int status; + + ptrace (PTRACE_CONT, parent_pid, 0, 0); + waitpid (parent_pid, &status, __WALL); +- if ((status >> 16) != PTRACE_EVENT_VFORKDONE) ++ if ((status >> 16) != PTRACE_EVENT_VFORK_DONE) + warning ("Unexpected waitpid result %06x when waiting for " + "vfork-done", status); + } +@@ -476,7 +525,7 @@ linux_handle_extended_wait (int pid, int + int + child_insert_fork_catchpoint (int pid) + { +- if (! linux_supports_tracefork ()) ++ if (! linux_supports_tracefork (pid)) + error ("Your system does not support fork catchpoints."); + + return 0; +@@ -485,7 +534,7 @@ child_insert_fork_catchpoint (int pid) + int + child_insert_vfork_catchpoint (int pid) + { +- if (!linux_supports_tracefork ()) ++ if (!linux_supports_tracefork (pid)) + error ("Your system does not support vfork catchpoints."); + + return 0; +@@ -494,7 +543,7 @@ child_insert_vfork_catchpoint (int pid) + int + child_insert_exec_catchpoint (int pid) + { +- if (!linux_supports_tracefork ()) ++ if (!linux_supports_tracefork (pid)) + error ("Your system does not support exec catchpoints."); + + return 0; +@@ -1285,6 +1334,13 @@ stop_wait_callback (struct lwp_info *lp, + user will delete or disable the breakpoint, but the + thread will have already tripped on it. */ + ++ /* Notify any observers that we have a SIGTRAP. ++ This is needed on platforms that must save more state ++ than just the trap. For example, ia64 linux uses ++ siginfo to determine if a watchpoint has occurred and ++ this information gets trashed by a SIGSTOP. */ ++ observer_notify_sigtrap (lp); ++ + /* Now resume this LWP and get the SIGSTOP event. */ + errno = 0; + ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0); +@@ -1960,6 +2016,14 @@ retry: + } + } + ++ /* For platforms such as ia64, a hardware watchpoint is ++ determined by looking at special information available ++ at the time time of the trap (siginfo). This information ++ is not preserved if we choose to take an event on another ++ thread and later come back to this event, thus we must ++ notify an observer so the information can be stored. */ ++ observer_notify_sigtrap (lp); ++ + /* Handle GNU/Linux's extended waitstatus for trace events. */ + if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0) + { diff --git a/gdb.spec b/gdb.spec index 392db10..ace61fd 100644 --- a/gdb.spec +++ b/gdb.spec @@ -58,12 +58,6 @@ Patch50: gdb-6.1post-vsyscall-jul2004.patch #Broken: Patch51: gdb-6.1post-pie-jul2004.patch #Broken: Patch52: gdb-6.1post-test-pie-nov2004.patch -# Add threaded watchpoint support -#Broken: Patch74: gdb-6.1post-threaded-watchpoints-sep2004.patch -# Fix for thread_db_get_lwp -#Broken: Patch75: gdb-6.1post-thread-get-lwp-oct2004.patch -# Fix for S/390 watchpoints under threads. -#Broken: Patch76: gdb-6.1post-s390-watchpoints-oct2004.patch # Fix for caching thread lwps for linux Patch77: gdb-6.1post-lwp-cache-oct2004.patch # Fix for allowing macros to continue after backtrace errors @@ -134,6 +128,9 @@ Patch111: gdb-6.3-ppc64displaysymbol-20041124.patch # Fix stepping in threads Patch112: gdb-6.3-thread-step-20041207.patch +# Threaded watchpoint support +Patch113: gdb-6.3-threaded-watchpoints-20041213.patch + %ifarch ia64 BuildRequires: ncurses-devel glibc-devel gcc make gzip texinfo dejagnu libunwind >= 0.96-3 %else @@ -179,6 +176,7 @@ and printing their data. %patch110 -p1 %patch111 -p1 %patch112 -p1 +%patch113 -p1 %patch50 -p1