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. * gdbarch.sh (single_step_through_delay): New directive. * gdbarch.h: Regenerated. * gdbarch.c: Ditto. * doc/observer.texi: Add two new observers for linux_new_thread and sigtrap. --- gdb-6.3/gdb/doc/observer.texi.fix Fri Jan 7 16:59:57 2005 +++ gdb-6.3/gdb/doc/observer.texi Fri Jan 7 17:04:07 2005 @@ -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/infrun.c.fix Fri Jan 7 16:55:54 2005 +++ gdb-6.3/gdb/infrun.c Fri Jan 7 17:04:37 2005 @@ -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; @@ -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/gdbarch.h.fix Fri Jan 7 17:08:19 2005 +++ gdb-6.3/gdb/gdbarch.h Fri Jan 7 17:09:12 2005 @@ -1218,6 +1218,15 @@ extern void set_gdbarch_software_single_ #define SOFTWARE_SINGLE_STEP(sig, insert_breakpoints_p) (gdbarch_software_single_step (current_gdbarch, sig, insert_breakpoints_p)) #endif +/* Return non-zero if the processor is executing a delay slot and a + * further single-step is needed before the instruction finishes. */ + +extern int gdbarch_single_step_through_delay_p (struct gdbarch *gdbarch); + +typedef int (gdbarch_single_step_through_delay_ftype) (struct gdbarch *gdbarch, struct frame_info *frame); +extern int gdbarch_single_step_through_delay (struct gdbarch *gdbarch, struct frame_info *frame); +extern void set_gdbarch_single_step_through_delay (struct gdbarch *gdbarch, gdbarch_single_step_through_delay_ftype *single_step_through_delay); + /* FIXME: cagney/2003-08-28: Need to find a better way of selecting the disassembler. Perhaps objdump can handle it? */ --- gdb-6.3/gdb/breakpoint.c.fix Fri Jan 7 16:58:12 2005 +++ gdb-6.3/gdb/breakpoint.c Fri Jan 7 17:04:07 2005 @@ -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 @@ -2122,8 +2197,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; @@ -2560,6 +2640,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 @@ -2690,82 +2818,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); @@ -2794,6 +2901,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: @@ -2809,7 +2925,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 @@ -4091,6 +4207,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 @@ -5449,169 +5566,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 @@ -5668,29 +5622,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/gdbarch.c.fix Fri Jan 7 17:09:18 2005 +++ gdb-6.3/gdb/gdbarch.c Fri Jan 7 17:36:36 2005 @@ -211,6 +211,7 @@ struct gdbarch gdbarch_addr_bits_remove_ftype *addr_bits_remove; gdbarch_smash_text_address_ftype *smash_text_address; gdbarch_software_single_step_ftype *software_single_step; + gdbarch_single_step_through_delay_ftype *single_step_through_delay; gdbarch_print_insn_ftype *print_insn; gdbarch_skip_trampoline_code_ftype *skip_trampoline_code; gdbarch_skip_solib_resolver_ftype *skip_solib_resolver; @@ -337,6 +338,7 @@ struct gdbarch startup_gdbarch = 0, /* addr_bits_remove */ 0, /* smash_text_address */ 0, /* software_single_step */ + 0, /* single_step_through_delay */ 0, /* print_insn */ 0, /* skip_trampoline_code */ generic_skip_solib_resolver, /* skip_solib_resolver */ @@ -591,6 +593,7 @@ verify_gdbarch (struct gdbarch *current_ /* Skip verify of addr_bits_remove, invalid_p == 0 */ /* Skip verify of smash_text_address, invalid_p == 0 */ /* Skip verify of software_single_step, has predicate */ + /* Skip verify of single_step_through_delay, has predicate */ if (current_gdbarch->print_insn == 0) fprintf_unfiltered (log, "\n\tprint_insn"); /* Skip verify of skip_trampoline_code, invalid_p == 0 */ @@ -1516,6 +1519,12 @@ gdbarch_dump (struct gdbarch *current_gd fprintf_unfiltered (file, "gdbarch_dump: short_bit = %s\n", paddr_d (current_gdbarch->short_bit)); + fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_single_step_through_delay_p() = %d\n", + gdbarch_single_step_through_delay_p (current_gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: single_step_through_delay = <0x%lx>\n", + (long) current_gdbarch->single_step_through_delay); #ifdef SKIP_PROLOGUE fprintf_unfiltered (file, "gdbarch_dump: %s # %s\n", @@ -3346,6 +3355,30 @@ set_gdbarch_software_single_step (struct } int +gdbarch_single_step_through_delay_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->single_step_through_delay != NULL; +} + +int +gdbarch_single_step_through_delay (struct gdbarch *gdbarch, struct frame_info *frame) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->single_step_through_delay != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_single_step_through_delay called\n"); + return gdbarch->single_step_through_delay (gdbarch, frame); +} + +void +set_gdbarch_single_step_through_delay (struct gdbarch *gdbarch, + gdbarch_single_step_through_delay_ftype single_step_through_delay) +{ + gdbarch->single_step_through_delay = single_step_through_delay; +} + +int gdbarch_print_insn (struct gdbarch *gdbarch, bfd_vma vma, struct disassemble_info *info) { gdb_assert (gdbarch != NULL); --- gdb-6.3/gdb/breakpoint.h.fix Fri Jan 7 16:58:19 2005 +++ gdb-6.3/gdb/breakpoint.h Fri Jan 7 17:04:07 2005 @@ -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/linux-nat.c.fix Fri Jan 7 16:58:27 2005 +++ gdb-6.3/gdb/linux-nat.c Fri Jan 7 17:04:07 2005 @@ -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; @@ -716,6 +765,9 @@ delete_lwp (ptid_t ptid) else lwp_list = lp->next; + if (lp->saved_trap_data) + xfree (lp->saved_trap_data); + xfree (lp); } @@ -1285,6 +1337,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 +2019,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) { --- gdb-6.3/gdb/linux-nat.h.fix Fri Jan 7 16:58:33 2005 +++ gdb-6.3/gdb/linux-nat.h Fri Jan 7 17:04:07 2005 @@ -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/Makefile.in.fix Fri Jan 7 16:58:42 2005 +++ gdb-6.3/gdb/Makefile.in Fri Jan 7 17:04:07 2005 @@ -2055,7 +2055,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) \ @@ -2437,7 +2438,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/thread-db.c.fix Fri Jan 7 16:58:49 2005 +++ gdb-6.3/gdb/thread-db.c Fri Jan 7 17:04:07 2005 @@ -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) @@ -903,7 +903,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) { @@ -950,7 +950,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); @@ -1175,7 +1176,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/i386-linux-nat.c.fix Fri Jan 7 16:59:02 2005 +++ gdb-6.3/gdb/i386-linux-nat.c Fri Jan 7 17:04:07 2005 @@ -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/ia64-linux-nat.c.fix Fri Jan 7 16:59:11 2005 +++ gdb-6.3/gdb/ia64-linux-nat.c Fri Jan 7 17:04:07 2005 @@ -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/amd64-linux-nat.c.fix Fri Jan 7 16:59:21 2005 +++ gdb-6.3/gdb/amd64-linux-nat.c Fri Jan 7 17:04:07 2005 @@ -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/s390-nat.c.fix Fri Jan 7 16:59:28 2005 +++ gdb-6.3/gdb/s390-nat.c Fri Jan 7 17:04:07 2005 @@ -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/gdbarch.sh.fix Fri Jan 7 17:30:46 2005 +++ gdb-6.3/gdb/gdbarch.sh Fri Jan 7 17:32:28 2005 @@ -611,6 +611,9 @@ f:=:CORE_ADDR:smash_text_address:CORE_AD # FIXME/cagney/2001-01-18: The logic is backwards. It should be asking if the target can # single step. If not, then implement single step using breakpoints. F:=:void:software_single_step:enum target_signal sig, int insert_breakpoints_p:sig, insert_breakpoints_p +# Return non-zero if the processor is executing a delay slot and a +# further single-step is needed before the instruction finishes. +M::int:single_step_through_delay:struct frame_info *frame:frame # FIXME: cagney/2003-08-28: Need to find a better way of selecting the # disassembler. Perhaps objdump can handle it? f:TARGET_PRINT_INSN:int:print_insn:bfd_vma vma, struct disassemble_info *info:vma, info::0: