X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Finfrun.c;h=7d870eb75ef861f7e028c4511b86378f4fb64ad7;hb=e09875d41026beb03eae1a65510ca40ed3a5d6c1;hp=3a67ac5400a64bfbebc54e2e9c8b6c17adf398a6;hpb=f00150c95d261f2fac4fbab21d60eceb2ccb7d3e;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/infrun.c b/gdb/infrun.c index 3a67ac5400..7d870eb75e 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -48,6 +48,7 @@ #include "gdb_assert.h" #include "mi/mi-common.h" #include "event-top.h" +#include "record.h" /* Prototypes for local functions */ @@ -82,6 +83,8 @@ static int prepare_to_proceed (int); void _initialize_infrun (void); +void nullify_last_target_wait_ptid (void); + /* When set, stop the 'step' command if we enter a function which has no line number information. The normal behavior is that we step over such function. */ @@ -254,23 +257,6 @@ void init_thread_stepping_state (struct thread_info *tss); void init_infwait_state (void); -/* This is used to remember when a fork, vfork or exec event - was caught by a catchpoint, and thus the event is to be - followed at the next resume of the inferior, and not - immediately. */ -static struct -{ - enum target_waitkind kind; - struct - { - ptid_t parent_pid; - ptid_t child_pid; - } - fork_event; - char *execd_pathname; -} -pending_follow; - static const char follow_fork_mode_child[] = "child"; static const char follow_fork_mode_parent[] = "parent"; @@ -291,12 +277,157 @@ Debugger response to a program call of fork or vfork is \"%s\".\n"), } +/* Tell the target to follow the fork we're stopped at. Returns true + if the inferior should be resumed; false, if the target for some + reason decided it's best not to resume. */ + static int follow_fork (void) { int follow_child = (follow_fork_mode_string == follow_fork_mode_child); + int should_resume = 1; + struct thread_info *tp; + + /* Copy user stepping state to the new inferior thread. FIXME: the + followed fork child thread should have a copy of most of the + parent thread structure's run control related fields, not just these. + Initialized to avoid "may be used uninitialized" warnings from gcc. */ + struct breakpoint *step_resume_breakpoint = NULL; + CORE_ADDR step_range_start = 0; + CORE_ADDR step_range_end = 0; + struct frame_id step_frame_id = { 0 }; + + if (!non_stop) + { + ptid_t wait_ptid; + struct target_waitstatus wait_status; + + /* Get the last target status returned by target_wait(). */ + get_last_target_status (&wait_ptid, &wait_status); + + /* If not stopped at a fork event, then there's nothing else to + do. */ + if (wait_status.kind != TARGET_WAITKIND_FORKED + && wait_status.kind != TARGET_WAITKIND_VFORKED) + return 1; + + /* Check if we switched over from WAIT_PTID, since the event was + reported. */ + if (!ptid_equal (wait_ptid, minus_one_ptid) + && !ptid_equal (inferior_ptid, wait_ptid)) + { + /* We did. Switch back to WAIT_PTID thread, to tell the + target to follow it (in either direction). We'll + afterwards refuse to resume, and inform the user what + happened. */ + switch_to_thread (wait_ptid); + should_resume = 0; + } + } + + tp = inferior_thread (); + + /* If there were any forks/vforks that were caught and are now to be + followed, then do so now. */ + switch (tp->pending_follow.kind) + { + case TARGET_WAITKIND_FORKED: + case TARGET_WAITKIND_VFORKED: + { + ptid_t parent, child; + + /* If the user did a next/step, etc, over a fork call, + preserve the stepping state in the fork child. */ + if (follow_child && should_resume) + { + step_resume_breakpoint + = clone_momentary_breakpoint (tp->step_resume_breakpoint); + step_range_start = tp->step_range_start; + step_range_end = tp->step_range_end; + step_frame_id = tp->step_frame_id; + + /* For now, delete the parent's sr breakpoint, otherwise, + parent/child sr breakpoints are considered duplicates, + and the child version will not be installed. Remove + this when the breakpoints module becomes aware of + inferiors and address spaces. */ + delete_step_resume_breakpoint (tp); + tp->step_range_start = 0; + tp->step_range_end = 0; + tp->step_frame_id = null_frame_id; + } + + parent = inferior_ptid; + child = tp->pending_follow.value.related_pid; + + /* Tell the target to do whatever is necessary to follow + either parent or child. */ + if (target_follow_fork (follow_child)) + { + /* Target refused to follow, or there's some other reason + we shouldn't resume. */ + should_resume = 0; + } + else + { + /* This pending follow fork event is now handled, one way + or another. The previous selected thread may be gone + from the lists by now, but if it is still around, need + to clear the pending follow request. */ + tp = find_thread_ptid (parent); + if (tp) + tp->pending_follow.kind = TARGET_WAITKIND_SPURIOUS; + + /* This makes sure we don't try to apply the "Switched + over from WAIT_PID" logic above. */ + nullify_last_target_wait_ptid (); + + /* If we followed the child, switch to it... */ + if (follow_child) + { + switch_to_thread (child); + + /* ... and preserve the stepping state, in case the + user was stepping over the fork call. */ + if (should_resume) + { + tp = inferior_thread (); + tp->step_resume_breakpoint = step_resume_breakpoint; + tp->step_range_start = step_range_start; + tp->step_range_end = step_range_end; + tp->step_frame_id = step_frame_id; + } + else + { + /* If we get here, it was because we're trying to + resume from a fork catchpoint, but, the user + has switched threads away from the thread that + forked. In that case, the resume command + issued is most likely not applicable to the + child, so just warn, and refuse to resume. */ + warning (_("\ +Not resuming: switched threads before following fork child.\n")); + } + + /* Reset breakpoints in the child as appropriate. */ + follow_inferior_reset_breakpoints (); + } + else + switch_to_thread (parent); + } + } + break; + case TARGET_WAITKIND_SPURIOUS: + /* Nothing to follow. */ + break; + default: + internal_error (__FILE__, __LINE__, + "Unexpected pending_follow.kind %d\n", + tp->pending_follow.kind); + break; + } - return target_follow_fork (follow_child); + return should_resume; } void @@ -603,7 +734,8 @@ use_displaced_stepping (struct gdbarch *gdbarch) return (((can_use_displaced_stepping == can_use_displaced_stepping_auto && non_stop) || can_use_displaced_stepping == can_use_displaced_stepping_on) - && gdbarch_displaced_step_copy_insn_p (gdbarch)); + && gdbarch_displaced_step_copy_insn_p (gdbarch) + && !RECORD_IS_USED); } /* Clean out any stray displaced stepping state. */ @@ -836,7 +968,7 @@ displaced_step_fixup (ptid_t event_ptid, enum target_signal signal) context_switch (ptid); - actual_pc = read_pc (); + actual_pc = regcache_read_pc (get_thread_regcache (ptid)); if (breakpoint_here_p (actual_pc)) { @@ -950,6 +1082,29 @@ set_schedlock_func (char *args, int from_tty, struct cmd_list_element *c) } } +/* Try to setup for software single stepping over the specified location. + Return 1 if target_resume() should use hardware single step. + + GDBARCH the current gdbarch. + PC the location to step over. */ + +static int +maybe_software_singlestep (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + int hw_step = 1; + + if (gdbarch_software_single_step_p (gdbarch) + && gdbarch_software_single_step (gdbarch, get_current_frame ())) + { + hw_step = 0; + /* Do not pull these breakpoints until after a `wait' in + `wait_for_inferior' */ + singlestep_breakpoints_inserted_p = 1; + singlestep_ptid = inferior_ptid; + singlestep_pc = pc; + } + return hw_step; +} /* Resume the inferior, but allow a QUIT. This is useful if the user wants to interrupt some lengthy single-stepping operation @@ -964,8 +1119,6 @@ resume (int step, enum target_signal sig) { int should_resume = 1; struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0); - - /* Note that these must be reset if we follow a fork below. */ struct regcache *regcache = get_current_regcache (); struct gdbarch *gdbarch = get_regcache_arch (regcache); struct thread_info *tp = inferior_thread (); @@ -1031,50 +1184,9 @@ a command like `return' or `jump' to continue execution.")); } } - if (step && gdbarch_software_single_step_p (gdbarch)) - { - /* Do it the hard way, w/temp breakpoints */ - if (gdbarch_software_single_step (gdbarch, get_current_frame ())) - { - /* ...and don't ask hardware to do it. */ - step = 0; - /* and do not pull these breakpoints until after a `wait' in - `wait_for_inferior' */ - singlestep_breakpoints_inserted_p = 1; - singlestep_ptid = inferior_ptid; - singlestep_pc = pc; - } - } - - /* If there were any forks/vforks/execs that were caught and are - now to be followed, then do so. */ - switch (pending_follow.kind) - { - case TARGET_WAITKIND_FORKED: - case TARGET_WAITKIND_VFORKED: - pending_follow.kind = TARGET_WAITKIND_SPURIOUS; - if (follow_fork ()) - should_resume = 0; - - /* Following a child fork will change our notion of current - thread. */ - tp = inferior_thread (); - regcache = get_current_regcache (); - gdbarch = get_regcache_arch (regcache); - pc = regcache_read_pc (regcache); - break; - - case TARGET_WAITKIND_EXECD: - /* follow_exec is called as soon as the exec event is seen. */ - pending_follow.kind = TARGET_WAITKIND_SPURIOUS; - break; - - default: - break; - } - - /* Install inferior's terminal modes. */ - target_terminal_inferior (); + /* Do we need to do it the hard way, w/temp breakpoints? */ + if (step) + step = maybe_software_singlestep (gdbarch, pc); if (should_resume) { @@ -1157,6 +1269,9 @@ a command like `return' or `jump' to continue execution.")); displaced_step_dump_bytes (gdb_stdlog, buf, sizeof (buf)); } + /* Install inferior's terminal modes. */ + target_terminal_inferior (); + /* Avoid confusing the next resume, if the next stop/resume happens to apply to another thread. */ tp->stop_signal = TARGET_SIGNAL_0; @@ -1230,7 +1345,8 @@ clear_proceed_status (void) } stop_after_trap = 0; - breakpoint_proceeded = 1; /* We're about to proceed... */ + + observer_notify_about_to_proceed (); if (stop_registers) { @@ -1297,12 +1413,26 @@ prepare_to_proceed (int step) void proceed (CORE_ADDR addr, enum target_signal siggnal, int step) { - struct regcache *regcache = get_current_regcache (); - struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct regcache *regcache; + struct gdbarch *gdbarch; struct thread_info *tp; - CORE_ADDR pc = regcache_read_pc (regcache); + CORE_ADDR pc; int oneproc = 0; + /* If we're stopped at a fork/vfork, follow the branch set by the + "set follow-fork-mode" command; otherwise, we'll just proceed + resuming the current thread. */ + if (!follow_fork ()) + { + /* The target for some reason decided not to resume. */ + normal_stop (); + return; + } + + regcache = get_current_regcache (); + gdbarch = get_regcache_arch (regcache); + pc = regcache_read_pc (regcache); + if (step > 0) step_start_function = find_pc_function (pc); if (step < 0) @@ -1398,7 +1528,7 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step) && !ptid_equal (last_ptid, null_ptid) && !ptid_equal (last_ptid, minus_one_ptid)) { - last_thread = find_thread_pid (last_ptid); + last_thread = find_thread_ptid (last_ptid); if (last_thread) { tp->stop_signal = last_thread->stop_signal; @@ -1509,9 +1639,6 @@ init_wait_for_inferior (void) breakpoint_init_inferior (inf_starting); - /* The first resume is not following a fork/vfork/exec. */ - pending_follow.kind = TARGET_WAITKIND_SPURIOUS; /* I.e., none. */ - clear_proceed_status (); stepping_past_singlestep_breakpoint = 0; @@ -1630,7 +1757,7 @@ infrun_thread_stop_requested_callback (struct thread_info *info, void *arg) have consistent output as if the stop event had been reported. */ ecs->ptid = info->ptid; - ecs->event_thread = find_thread_pid (info->ptid); + ecs->event_thread = find_thread_ptid (info->ptid); ecs->ws.kind = TARGET_WAITKIND_STOPPED; ecs->ws.value.sig = TARGET_SIGNAL_0; @@ -1660,7 +1787,7 @@ infrun_thread_stop_requested_callback (struct thread_info *info, void *arg) Cleanup local state that assumed the PTID was to be resumed, and report the stop to the frontend. */ -void +static void infrun_thread_stop_requested (ptid_t ptid) { struct displaced_step_request *it, *next, *prev = NULL; @@ -1690,6 +1817,13 @@ infrun_thread_stop_requested (ptid_t ptid) iterate_over_threads (infrun_thread_stop_requested_callback, &ptid); } +static void +infrun_thread_thread_exit (struct thread_info *tp, int silent) +{ + if (ptid_equal (target_last_wait_ptid, tp->ptid)) + nullify_last_target_wait_ptid (); +} + /* Callback for iterate_over_threads. */ static int @@ -1737,6 +1871,46 @@ delete_step_thread_step_resume_breakpoint_cleanup (void *arg) delete_step_thread_step_resume_breakpoint (); } +/* Pretty print the results of target_wait, for debugging purposes. */ + +static void +print_target_wait_results (ptid_t waiton_ptid, ptid_t result_ptid, + const struct target_waitstatus *ws) +{ + char *status_string = target_waitstatus_to_string (ws); + struct ui_file *tmp_stream = mem_fileopen (); + char *text; + long len; + + /* The text is split over several lines because it was getting too long. + Call fprintf_unfiltered (gdb_stdlog) once so that the text is still + output as a unit; we want only one timestamp printed if debug_timestamp + is set. */ + + fprintf_unfiltered (tmp_stream, + "infrun: target_wait (%d", PIDGET (waiton_ptid)); + if (PIDGET (waiton_ptid) != -1) + fprintf_unfiltered (tmp_stream, + " [%s]", target_pid_to_str (waiton_ptid)); + fprintf_unfiltered (tmp_stream, ", status) =\n"); + fprintf_unfiltered (tmp_stream, + "infrun: %d [%s],\n", + PIDGET (result_ptid), target_pid_to_str (result_ptid)); + fprintf_unfiltered (tmp_stream, + "infrun: %s\n", + status_string); + + text = ui_file_xstrdup (tmp_stream, &len); + + /* This uses %s in part to handle %'s in the text, but also to avoid + a gcc error: the format attribute requires a string literal. */ + fprintf_unfiltered (gdb_stdlog, "%s", text); + + xfree (status_string); + xfree (text); + ui_file_delete (tmp_stream); +} + /* Wait for control to return from inferior to debugger. If TREAT_EXEC_AS_SIGTRAP is non-zero, then handle EXEC signals @@ -1785,19 +1959,12 @@ wait_for_inferior (int treat_exec_as_sigtrap) struct cleanup *old_chain; if (deprecated_target_wait_hook) - ecs->ptid = deprecated_target_wait_hook (waiton_ptid, &ecs->ws); + ecs->ptid = deprecated_target_wait_hook (waiton_ptid, &ecs->ws, 0); else - ecs->ptid = target_wait (waiton_ptid, &ecs->ws); + ecs->ptid = target_wait (waiton_ptid, &ecs->ws, 0); if (debug_infrun) - { - char *status_string = target_waitstatus_to_string (&ecs->ws); - fprintf_unfiltered (gdb_stdlog, - "infrun: target_wait (%d, status) = %d, %s\n", - PIDGET (waiton_ptid), PIDGET (ecs->ptid), - status_string); - xfree (status_string); - } + print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws); if (treat_exec_as_sigtrap && ecs->ws.kind == TARGET_WAITKIND_EXECD) { @@ -1870,19 +2037,12 @@ fetch_inferior_event (void *client_data) if (deprecated_target_wait_hook) ecs->ptid = - deprecated_target_wait_hook (waiton_ptid, &ecs->ws); + deprecated_target_wait_hook (waiton_ptid, &ecs->ws, TARGET_WNOHANG); else - ecs->ptid = target_wait (waiton_ptid, &ecs->ws); + ecs->ptid = target_wait (waiton_ptid, &ecs->ws, TARGET_WNOHANG); if (debug_infrun) - { - char *status_string = target_waitstatus_to_string (&ecs->ws); - fprintf_unfiltered (gdb_stdlog, - "infrun: target_wait (%d, status) = %d, %s\n", - PIDGET (waiton_ptid), PIDGET (ecs->ptid), - status_string); - xfree (status_string); - } + print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws); if (non_stop && ecs->ws.kind != TARGET_WAITKIND_IGNORE @@ -2082,6 +2242,10 @@ adjust_pc_after_break (struct execution_control_state *ecs) if (software_breakpoint_inserted_here_p (breakpoint_pc) || (non_stop && moribund_breakpoint_here_p (breakpoint_pc))) { + struct cleanup *old_cleanups = NULL; + if (RECORD_IS_USED) + old_cleanups = record_gdb_operation_disable_set (); + /* When using hardware single-step, a SIGTRAP is reported for both a completed single-step and a software breakpoint. Need to differentiate between the two, as the latter needs adjusting @@ -2105,6 +2269,9 @@ adjust_pc_after_break (struct execution_control_state *ecs) || !currently_stepping (ecs->event_thread) || ecs->event_thread->prev_pc == breakpoint_pc) regcache_write_pc (regcache, breakpoint_pc); + + if (RECORD_IS_USED) + do_cleanups (old_cleanups); } } @@ -2170,7 +2337,7 @@ handle_inferior_event (struct execution_control_state *ecs) && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED && ecs->new_thread_event) add_thread (ecs->ptid); - ecs->event_thread = find_thread_pid (ecs->ptid); + ecs->event_thread = find_thread_ptid (ecs->ptid); /* Dependent on valid ECS->EVENT_THREAD. */ adjust_pc_after_break (ecs); @@ -2357,10 +2524,6 @@ handle_inferior_event (struct execution_control_state *ecs) case TARGET_WAITKIND_VFORKED: if (debug_infrun) fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_FORKED\n"); - pending_follow.kind = ecs->ws.kind; - - pending_follow.fork_event.parent_pid = ecs->ptid; - pending_follow.fork_event.child_pid = ecs->ws.value.related_pid; if (!ptid_equal (ecs->ptid, inferior_ptid)) { @@ -2368,7 +2531,33 @@ handle_inferior_event (struct execution_control_state *ecs) reinit_frame_cache (); } - stop_pc = read_pc (); + /* Immediately detach breakpoints from the child before there's + any chance of letting the user delete breakpoints from the + breakpoint lists. If we don't do this early, it's easy to + leave left over traps in the child, vis: "break foo; catch + fork; c; ; del; c; ". We only follow + the fork on the last `continue', and by that time the + breakpoint at "foo" is long gone from the breakpoint table. + If we vforked, then we don't need to unpatch here, since both + parent and child are sharing the same memory pages; we'll + need to unpatch at follow/detach time instead to be certain + that new breakpoints added between catchpoint hit time and + vfork follow are detached. */ + if (ecs->ws.kind != TARGET_WAITKIND_VFORKED) + { + int child_pid = ptid_get_pid (ecs->ws.value.related_pid); + + /* This won't actually modify the breakpoint list, but will + physically remove the breakpoints from the child. */ + detach_breakpoints (child_pid); + } + + /* In case the event is caught by a catchpoint, remember that + the event is to be followed at the next resume of the thread, + and not immediately. */ + ecs->event_thread->pending_follow = ecs->ws; + + stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid)); ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid); @@ -2377,8 +2566,19 @@ handle_inferior_event (struct execution_control_state *ecs) /* If no catchpoint triggered for this, then keep going. */ if (ecs->random_signal) { + int should_resume; + ecs->event_thread->stop_signal = TARGET_SIGNAL_0; - keep_going (ecs); + + should_resume = follow_fork (); + + ecs->event_thread = inferior_thread (); + ecs->ptid = inferior_ptid; + + if (should_resume) + keep_going (ecs); + else + stop_stepping (ecs); return; } ecs->event_thread->stop_signal = TARGET_SIGNAL_TRAP; @@ -2387,9 +2587,6 @@ handle_inferior_event (struct execution_control_state *ecs) case TARGET_WAITKIND_EXECD: if (debug_infrun) fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_EXECD\n"); - pending_follow.execd_pathname = - savestring (ecs->ws.value.execd_pathname, - strlen (ecs->ws.value.execd_pathname)); if (!ptid_equal (ecs->ptid, inferior_ptid)) { @@ -2397,17 +2594,21 @@ handle_inferior_event (struct execution_control_state *ecs) reinit_frame_cache (); } - stop_pc = read_pc (); + stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid)); /* This causes the eventpoints and symbol table to be reset. Must do this now, before trying to determine whether to stop. */ - follow_exec (inferior_ptid, pending_follow.execd_pathname); - xfree (pending_follow.execd_pathname); + follow_exec (inferior_ptid, ecs->ws.value.execd_pathname); ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid); ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat); + /* Note that this may be referenced from inside + bpstat_stop_status above, through inferior_has_execd. */ + xfree (ecs->ws.value.execd_pathname); + ecs->ws.value.execd_pathname = NULL; + /* If no catchpoint triggered for this, then keep going. */ if (ecs->random_signal) { @@ -2447,7 +2648,7 @@ handle_inferior_event (struct execution_control_state *ecs) case TARGET_WAITKIND_NO_HISTORY: /* Reverse execution: target ran out of history info. */ - stop_pc = read_pc (); + stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid)); print_stop_reason (NO_HISTORY, 0); stop_stepping (ecs); return; @@ -2511,7 +2712,7 @@ targets should add new threads to the thread list themselves in non-stop mode.") { fprintf_unfiltered (gdb_stdlog, "infrun: stop_pc = 0x%s\n", paddr_nz (stop_pc)); - if (STOPPED_BY_WATCHPOINT (&ecs->ws)) + if (target_stopped_by_watchpoint ()) { CORE_ADDR addr; fprintf_unfiltered (gdb_stdlog, "infrun: stopped by watchpoint\n"); @@ -2663,7 +2864,7 @@ targets should add new threads to the thread list themselves in non-stop mode.") stop_signal = ecs->event_thread->stop_signal; ecs->event_thread->stop_signal = TARGET_SIGNAL_0; ecs->ptid = singlestep_ptid; - ecs->event_thread = find_thread_pid (ecs->ptid); + ecs->event_thread = find_thread_ptid (ecs->ptid); ecs->event_thread->stop_signal = stop_signal; stop_pc = new_singlestep_pc; } @@ -2767,7 +2968,7 @@ targets should add new threads to the thread list themselves in non-stop mode.") /* If necessary, step over this watchpoint. We'll be back to display it in a moment. */ if (stopped_by_watchpoint - && (HAVE_STEPPABLE_WATCHPOINT + && (target_have_steppable_watchpoint || gdbarch_have_nonsteppable_watchpoint (current_gdbarch))) { /* At this point, we are stopped at an instruction which has @@ -2790,13 +2991,16 @@ targets should add new threads to the thread list themselves in non-stop mode.") the inferior over it. If we have non-steppable watchpoints, we must disable the current watchpoint; it's simplest to disable all watchpoints and breakpoints. */ - - if (!HAVE_STEPPABLE_WATCHPOINT) + int hw_step = 1; + + if (!target_have_steppable_watchpoint) remove_breakpoints (); + /* Single step */ + hw_step = maybe_software_singlestep (current_gdbarch, stop_pc); + target_resume (ecs->ptid, hw_step, TARGET_SIGNAL_0); registers_changed (); - target_resume (ecs->ptid, 1, TARGET_SIGNAL_0); /* Single step */ waiton_ptid = ecs->ptid; - if (HAVE_STEPPABLE_WATCHPOINT) + if (target_have_steppable_watchpoint) infwait_state = infwait_step_watch_state; else infwait_state = infwait_nonstep_watch_state; @@ -3023,7 +3227,7 @@ process_event_stop_test: if (signal_program[ecs->event_thread->stop_signal] == 0) ecs->event_thread->stop_signal = TARGET_SIGNAL_0; - if (ecs->event_thread->prev_pc == read_pc () + if (ecs->event_thread->prev_pc == stop_pc && ecs->event_thread->trap_expected && ecs->event_thread->step_resume_breakpoint == NULL) { @@ -3972,7 +4176,8 @@ static void keep_going (struct execution_control_state *ecs) { /* Save the pc before execution, to compare with pc after stop. */ - ecs->event_thread->prev_pc = read_pc (); /* Might have been DECR_AFTER_BREAK */ + ecs->event_thread->prev_pc + = regcache_read_pc (get_thread_regcache (ecs->ptid)); /* If we did not do break;, it means we should keep running the inferior and not return to debugger. */ @@ -4213,10 +4418,10 @@ normal_stop (void) if (target_has_execution) { if (!non_stop) - old_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid); + make_cleanup (finish_thread_state_cleanup, &minus_one_ptid); else if (last.kind != TARGET_WAITKIND_SIGNALLED && last.kind != TARGET_WAITKIND_EXITED) - old_chain = make_cleanup (finish_thread_state_cleanup, &inferior_ptid); + make_cleanup (finish_thread_state_cleanup, &inferior_ptid); } /* In non-stop mode, we don't want GDB to switch threads behind the @@ -4275,7 +4480,7 @@ Further execution is probably impossible.\n")); /* Set the current source location. This will also happen if we display the frame below, but the current SAL will be incorrect during a user hook-stop function. */ - if (target_has_stack && !stop_stack_dummy) + if (has_stack_frames () && !stop_stack_dummy) set_current_sal_from_frame (get_current_frame (), 1); /* Let the user/frontend see the threads as stopped. */ @@ -4287,7 +4492,7 @@ Further execution is probably impossible.\n")); catch_errors (hook_stop_stub, stop_command, "Error while running hook_stop:\n", RETURN_MASK_ALL); - if (!target_has_stack) + if (!has_stack_frames ()) goto done; if (last.kind == TARGET_WAITKIND_SIGNALLED @@ -4360,22 +4565,6 @@ Further execution is probably impossible.\n")); internal_error (__FILE__, __LINE__, _("Unknown value.")); } - if (ui_out_is_mi_like_p (uiout)) - { - - ui_out_field_int (uiout, "thread-id", - pid_to_thread_id (inferior_ptid)); - if (non_stop) - { - struct cleanup *back_to = make_cleanup_ui_out_list_begin_end - (uiout, "stopped-threads"); - ui_out_field_int (uiout, NULL, - pid_to_thread_id (inferior_ptid)); - do_cleanups (back_to); - } - else - ui_out_field_string (uiout, "stopped-threads", "all"); - } /* The behavior of this routine with respect to the source flag is: SRC_LINE: Print only source line @@ -4423,16 +4612,32 @@ Further execution is probably impossible.\n")); done: annotate_stopped (); - if (!suppress_stop_observer - && !(target_has_execution - && last.kind != TARGET_WAITKIND_SIGNALLED - && last.kind != TARGET_WAITKIND_EXITED - && inferior_thread ()->step_multi)) + + /* Suppress the stop observer if we're in the middle of: + + - a step n (n > 1), as there still more steps to be done. + + - a "finish" command, as the observer will be called in + finish_command_continuation, so it can include the inferior + function's return value. + + - calling an inferior function, as we pretend we inferior didn't + run at all. The return value of the call is handled by the + expression evaluator, through call_function_by_hand. */ + + if (!target_has_execution + || last.kind == TARGET_WAITKIND_SIGNALLED + || last.kind == TARGET_WAITKIND_EXITED + || (!inferior_thread ()->step_multi + && !(inferior_thread ()->stop_bpstat + && inferior_thread ()->proceed_to_finish) + && !inferior_thread ()->in_infcall)) { if (!ptid_equal (inferior_ptid, null_ptid)) - observer_notify_normal_stop (inferior_thread ()->stop_bpstat); + observer_notify_normal_stop (inferior_thread ()->stop_bpstat, + stop_print_frame); else - observer_notify_normal_stop (NULL); + observer_notify_normal_stop (NULL, stop_print_frame); } if (target_has_execution) @@ -4652,8 +4857,8 @@ handle_command (char *args, int from_tty) case TARGET_SIGNAL_INT: if (!allsigs && !sigs[signum]) { - if (query ("%s is used by the debugger.\n\ -Are you sure you want to change it? ", target_signal_to_name ((enum target_signal) signum))) + if (query (_("%s is used by the debugger.\n\ +Are you sure you want to change it? "), target_signal_to_name ((enum target_signal) signum))) { sigs[signum] = 1; } @@ -4804,6 +5009,87 @@ signals_info (char *signum_exp, int from_tty) printf_filtered (_("\nUse the \"handle\" command to change these tables.\n")); } + +/* The $_siginfo convenience variable is a bit special. We don't know + for sure the type of the value until we actually have a chance to + fetch the data. The type can change depending on gdbarch, so it it + also dependent on which thread you have selected. + + 1. making $_siginfo be an internalvar that creates a new value on + access. + + 2. making the value of $_siginfo be an lval_computed value. */ + +/* This function implements the lval_computed support for reading a + $_siginfo value. */ + +static void +siginfo_value_read (struct value *v) +{ + LONGEST transferred; + + transferred = + target_read (¤t_target, TARGET_OBJECT_SIGNAL_INFO, + NULL, + value_contents_all_raw (v), + value_offset (v), + TYPE_LENGTH (value_type (v))); + + if (transferred != TYPE_LENGTH (value_type (v))) + error (_("Unable to read siginfo")); +} + +/* This function implements the lval_computed support for writing a + $_siginfo value. */ + +static void +siginfo_value_write (struct value *v, struct value *fromval) +{ + LONGEST transferred; + + transferred = target_write (¤t_target, + TARGET_OBJECT_SIGNAL_INFO, + NULL, + value_contents_all_raw (fromval), + value_offset (v), + TYPE_LENGTH (value_type (fromval))); + + if (transferred != TYPE_LENGTH (value_type (fromval))) + error (_("Unable to write siginfo")); +} + +static struct lval_funcs siginfo_value_funcs = + { + siginfo_value_read, + siginfo_value_write + }; + +/* Return a new value with the correct type for the siginfo object of + the current thread. Return a void value if there's no object + available. */ + +static struct value * +siginfo_make_value (struct internalvar *var) +{ + struct type *type; + struct gdbarch *gdbarch; + + if (target_has_stack + && !ptid_equal (inferior_ptid, null_ptid)) + { + gdbarch = get_frame_arch (get_current_frame ()); + + if (gdbarch_get_siginfo_type_p (gdbarch)) + { + type = gdbarch_get_siginfo_type (gdbarch); + + return allocate_computed_value (type, &siginfo_value_funcs, NULL); + } + } + + return allocate_value (builtin_type_void); +} + /* Inferior thread state. These are details related to the inferior itself, and don't include @@ -4900,8 +5186,8 @@ struct inferior_status /* ID if the selected frame when the inferior function call was made. */ struct frame_id selected_frame_id; - int breakpoint_proceeded; int proceed_to_finish; + int in_infcall; }; /* Save all of the information associated with the inferior<==>gdb @@ -4930,8 +5216,8 @@ save_inferior_status (void) called. */ inf_status->stop_bpstat = tp->stop_bpstat; tp->stop_bpstat = bpstat_copy (tp->stop_bpstat); - inf_status->breakpoint_proceeded = breakpoint_proceeded; inf_status->proceed_to_finish = tp->proceed_to_finish; + inf_status->in_infcall = tp->in_infcall; inf_status->selected_frame_id = get_frame_id (get_selected_frame (NULL)); @@ -4980,8 +5266,8 @@ restore_inferior_status (struct inferior_status *inf_status) bpstat_clear (&tp->stop_bpstat); tp->stop_bpstat = inf_status->stop_bpstat; inf_status->stop_bpstat = NULL; - breakpoint_proceeded = inf_status->breakpoint_proceeded; tp->proceed_to_finish = inf_status->proceed_to_finish; + tp->in_infcall = inf_status->in_infcall; if (target_has_stack) { @@ -5467,4 +5753,11 @@ Options are 'forward' or 'reverse'."), observer_attach_thread_ptid_changed (infrun_thread_ptid_changed); observer_attach_thread_stop_requested (infrun_thread_stop_requested); + observer_attach_thread_exit (infrun_thread_thread_exit); + + /* Explicitly create without lookup, since that tries to create a + value with a void typed value, and when we get here, gdbarch + isn't initialized yet. At this point, we're quite sure there + isn't another convenience variable of the same name. */ + create_internalvar_type_lazy ("_siginfo", siginfo_make_value); }