X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Finfrun.c;h=7870f703e6c76041d0c831b28545f3a5536e1513;hb=8550d3b32f4ac17bdb9e4768bb905da181b77ded;hp=d0431df7cb20aa1a77852f8f3ce8d5fb59455751;hpb=963f9c80cb3f12fb779cf3189072ac48946da96c;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/infrun.c b/gdb/infrun.c index d0431df7cb..7870f703e6 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -1,7 +1,7 @@ /* Target-struct-independent code to start (run) and stop an inferior process. - Copyright (C) 1986-2014 Free Software Foundation, Inc. + Copyright (C) 1986-2015 Free Software Foundation, Inc. This file is part of GDB. @@ -88,8 +88,6 @@ static void set_schedlock_func (char *args, int from_tty, static int currently_stepping (struct thread_info *tp); -static void xdb_handle_command (char *args, int from_tty); - void _initialize_infrun (void); void nullify_last_target_wait_ptid (void); @@ -100,6 +98,8 @@ static void insert_step_resume_breakpoint_at_caller (struct frame_info *); static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR); +static int maybe_software_singlestep (struct gdbarch *gdbarch, CORE_ADDR pc); + /* 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. */ @@ -326,10 +326,6 @@ update_signals_program_target (void) static struct cmd_list_element *stop_command; -/* Function inferior was in as of last step command. */ - -static struct symbol *step_start_function; - /* Nonzero if we want to give control to the user when we're notified of shared library events by the dynamic linker. */ int stop_on_solib_events; @@ -408,15 +404,12 @@ static int follow_fork_inferior (int follow_child, int detach_fork) { int has_vforked; - int parent_pid, child_pid; + ptid_t parent_ptid, child_ptid; has_vforked = (inferior_thread ()->pending_follow.kind == TARGET_WAITKIND_VFORKED); - parent_pid = ptid_get_lwp (inferior_ptid); - if (parent_pid == 0) - parent_pid = ptid_get_pid (inferior_ptid); - child_pid - = ptid_get_pid (inferior_thread ()->pending_follow.value.related_pid); + parent_ptid = inferior_ptid; + child_ptid = inferior_thread ()->pending_follow.value.related_pid; if (has_vforked && !non_stop /* Non-stop always resumes both branches. */ @@ -458,11 +451,11 @@ holding the child stopped. Try \"set detach-on-fork\" or \ if (info_verbose || debug_infrun) { - target_terminal_ours (); + target_terminal_ours_for_output (); fprintf_filtered (gdb_stdlog, - "Detaching after fork from " - "child process %d.\n", - child_pid); + _("Detaching after %s from child %s.\n"), + has_vforked ? "vfork" : "fork", + target_pid_to_str (child_ptid)); } } else @@ -471,7 +464,7 @@ holding the child stopped. Try \"set detach-on-fork\" or \ struct cleanup *old_chain; /* Add process to GDB's tables. */ - child_inf = add_inferior (child_pid); + child_inf = add_inferior (ptid_get_pid (child_ptid)); parent_inf = current_inferior (); child_inf->attach_flag = parent_inf->attach_flag; @@ -482,7 +475,7 @@ holding the child stopped. Try \"set detach-on-fork\" or \ old_chain = save_inferior_ptid (); save_current_program_space (); - inferior_ptid = ptid_build (child_pid, child_pid, 0); + inferior_ptid = child_ptid; add_thread (inferior_ptid); child_inf->symfile_flags = SYMFILE_NO_READ; @@ -546,23 +539,18 @@ holding the child stopped. Try \"set detach-on-fork\" or \ if (info_verbose || debug_infrun) { - target_terminal_ours (); - if (has_vforked) - fprintf_filtered (gdb_stdlog, - _("Attaching after process %d " - "vfork to child process %d.\n"), - parent_pid, child_pid); - else - fprintf_filtered (gdb_stdlog, - _("Attaching after process %d " - "fork to child process %d.\n"), - parent_pid, child_pid); + target_terminal_ours_for_output (); + fprintf_filtered (gdb_stdlog, + _("Attaching after %s %s to child %s.\n"), + target_pid_to_str (parent_ptid), + has_vforked ? "vfork" : "fork", + target_pid_to_str (child_ptid)); } /* Add the new inferior first, so that the target_detach below doesn't unpush the target. */ - child_inf = add_inferior (child_pid); + child_inf = add_inferior (ptid_get_pid (child_ptid)); parent_inf = current_inferior (); child_inf->attach_flag = parent_inf->attach_flag; @@ -593,7 +581,18 @@ holding the child stopped. Try \"set detach-on-fork\" or \ parent_inf->waiting_for_vfork_done = 0; } else if (detach_fork) - target_detach (NULL, 0); + { + if (info_verbose || debug_infrun) + { + target_terminal_ours_for_output (); + fprintf_filtered (gdb_stdlog, + _("Detaching after fork from " + "child %s.\n"), + target_pid_to_str (child_ptid)); + } + + target_detach (NULL, 0); + } /* Note that the detach above makes PARENT_INF dangling. */ @@ -601,7 +600,7 @@ holding the child stopped. Try \"set detach-on-fork\" or \ this new thread, before cloning the program space, and informing the solib layer about this new process. */ - inferior_ptid = ptid_build (child_pid, child_pid, 0); + inferior_ptid = child_ptid; add_thread (inferior_ptid); /* If this is a vfork child, then the address-space is shared @@ -860,7 +859,7 @@ proceed_after_vfork_done (struct thread_info *thread, switch_to_thread (thread->ptid); clear_proceed_status (0); - proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT, 0); + proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT); } return 0; @@ -927,18 +926,22 @@ handle_vfork_child_exec_or_exit (int exec) if (debug_infrun || info_verbose) { - target_terminal_ours (); + target_terminal_ours_for_output (); if (exec) - fprintf_filtered (gdb_stdlog, - "Detaching vfork parent process " - "%d after child exec.\n", - inf->vfork_parent->pid); + { + fprintf_filtered (gdb_stdlog, + _("Detaching vfork parent process " + "%d after child exec.\n"), + inf->vfork_parent->pid); + } else - fprintf_filtered (gdb_stdlog, - "Detaching vfork parent process " - "%d after child exit.\n", - inf->vfork_parent->pid); + { + fprintf_filtered (gdb_stdlog, + _("Detaching vfork parent process " + "%d after child exit.\n"), + inf->vfork_parent->pid); + } } target_detach (NULL, 0); @@ -1048,10 +1051,11 @@ show_follow_exec_mode_string (struct ui_file *file, int from_tty, /* EXECD_PATHNAME is assumed to be non-NULL. */ static void -follow_exec (ptid_t pid, char *execd_pathname) +follow_exec (ptid_t ptid, char *execd_pathname) { - struct thread_info *th = inferior_thread (); + struct thread_info *th, *tmp; struct inferior *inf = current_inferior (); + int pid = ptid_get_pid (ptid); /* This is an exec event that we actually wish to pay attention to. Refresh our symbol table to the newly exec'd program, remove any @@ -1076,23 +1080,47 @@ follow_exec (ptid_t pid, char *execd_pathname) mark_breakpoints_out (); - update_breakpoints_after_exec (); - - /* If there was one, it's gone now. We cannot truly step-to-next - statement through an exec(). */ + /* The target reports the exec event to the main thread, even if + some other thread does the exec, and even if the main thread was + stopped or already gone. We may still have non-leader threads of + the process on our list. E.g., on targets that don't have thread + exit events (like remote); or on native Linux in non-stop mode if + there were only two threads in the inferior and the non-leader + one is the one that execs (and nothing forces an update of the + thread list up to here). When debugging remotely, it's best to + avoid extra traffic, when possible, so avoid syncing the thread + list with the target, and instead go ahead and delete all threads + of the process but one that reported the event. Note this must + be done before calling update_breakpoints_after_exec, as + otherwise clearing the threads' resources would reference stale + thread breakpoints -- it may have been one of these threads that + stepped across the exec. We could just clear their stepping + states, but as long as we're iterating, might as well delete + them. Deleting them now rather than at the next user-visible + stop provides a nicer sequence of events for user and MI + notifications. */ + ALL_THREADS_SAFE (th, tmp) + if (ptid_get_pid (th->ptid) == pid && !ptid_equal (th->ptid, ptid)) + delete_thread (th->ptid); + + /* We also need to clear any left over stale state for the + leader/event thread. E.g., if there was any step-resume + breakpoint or similar, it's gone now. We cannot truly + step-to-next statement through an exec(). */ + th = inferior_thread (); th->control.step_resume_breakpoint = NULL; th->control.exception_resume_breakpoint = NULL; + th->control.single_step_breakpoints = NULL; th->control.step_range_start = 0; th->control.step_range_end = 0; - /* The target reports the exec event to the main thread, even if - some other thread does the exec, and even if the main thread was - already stopped --- if debugging in non-stop mode, it's possible - the user had the main thread held stopped in the previous image - --- release it now. This is the same behavior as step-over-exec - with scheduler-locking on in all-stop mode. */ + /* The user may have had the main thread held stopped in the + previous image (e.g., schedlock on, or non-stop). Release + it now. */ th->stop_requested = 0; + update_breakpoints_after_exec (); + /* What is this a.out's name? */ printf_unfiltered (_("%s is executing new program: %s\n"), target_pid_to_str (inferior_ptid), @@ -1195,17 +1223,6 @@ follow_exec (ptid_t pid, char *execd_pathname) matically get reset there in the new process.). */ } -/* Non-zero if we just simulating a single-step. This is needed - because we cannot remove the breakpoints in the inferior process - until after the `wait' in `wait_for_inferior'. */ -static int singlestep_breakpoints_inserted_p = 0; - -/* The thread we inserted single-step breakpoints for. */ -static ptid_t singlestep_ptid; - -/* PC when we started this single-step. */ -static CORE_ADDR singlestep_pc; - /* Info about an instruction that is being stepped over. */ struct step_over_info @@ -1448,6 +1465,20 @@ get_displaced_stepping_state (int pid) return NULL; } +/* Return true if process PID has a thread doing a displaced step. */ + +static int +displaced_step_in_progress (int pid) +{ + struct displaced_step_inferior_state *displaced; + + displaced = get_displaced_stepping_state (pid); + if (displaced != NULL && !ptid_equal (displaced->step_ptid, null_ptid)) + return 1; + + return 0; +} + /* Add a new displaced stepping state for process PID to the displaced stepping state list, or return a pointer to an already existing entry, if it already exists. Never returns NULL. */ @@ -1779,8 +1810,16 @@ displaced_step_fixup (ptid_t event_ptid, enum gdb_signal signal) displaced_step_restore (displaced, displaced->step_ptid); + /* Fixup may need to read memory/registers. Switch to the thread + that we're fixing up. Also, target_stopped_by_watchpoint checks + the current thread. */ + switch_to_thread (event_ptid); + /* Did the instruction complete successfully? */ - if (signal == GDB_SIGNAL_TRAP) + if (signal == GDB_SIGNAL_TRAP + && !(target_stopped_by_watchpoint () + && (gdbarch_have_nonsteppable_watchpoint (displaced->step_gdbarch) + || target_have_steppable_watchpoint))) { /* Fix up the resulting state. */ gdbarch_displaced_step_fixup (displaced->step_gdbarch, @@ -1826,6 +1865,7 @@ displaced_step_fixup (ptid_t event_ptid, enum gdb_signal signal) regcache = get_thread_regcache (ptid); actual_pc = regcache_read_pc (regcache); aspace = get_regcache_aspace (regcache); + gdbarch = get_regcache_arch (regcache); if (breakpoint_here_p (aspace, actual_pc)) { @@ -1836,8 +1876,6 @@ displaced_step_fixup (ptid_t event_ptid, enum gdb_signal signal) displaced_step_prepare (ptid); - gdbarch = get_regcache_arch (regcache); - if (debug_displaced) { CORE_ADDR actual_pc = regcache_read_pc (regcache); @@ -1870,6 +1908,9 @@ displaced_step_fixup (ptid_t event_ptid, enum gdb_signal signal) /* Go back to what we were trying to do. */ step = currently_stepping (tp); + if (step) + step = maybe_software_singlestep (gdbarch, actual_pc); + if (debug_displaced) fprintf_unfiltered (gdb_stdlog, "displaced: breakpoint is gone: %s, step(%d)\n", @@ -1895,9 +1936,6 @@ infrun_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid) if (ptid_equal (inferior_ptid, old_ptid)) inferior_ptid = new_ptid; - if (ptid_equal (singlestep_ptid, old_ptid)) - singlestep_ptid = new_ptid; - for (displaced = displaced_step_inferior_states; displaced; displaced = displaced->next) @@ -1918,6 +1956,9 @@ infrun_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid) static void resume_cleanups (void *ignore) { + if (!ptid_equal (inferior_ptid, null_ptid)) + delete_single_step_breakpoints (inferior_thread ()); + normal_stop (); } @@ -1972,28 +2013,17 @@ maybe_software_singlestep (struct gdbarch *gdbarch, CORE_ADDR pc) && 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; } +/* See infrun.h. */ + ptid_t user_visible_resume_ptid (int step) { - /* By default, resume all threads of all processes. */ - ptid_t resume_ptid = RESUME_ALL; - - /* Maybe resume only all threads of the current process. */ - if (!sched_multi && target_supports_multi_process ()) - { - resume_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid)); - } + ptid_t resume_ptid; - /* Maybe resume a single thread after all. */ if (non_stop) { /* With non-stop mode on, threads are always handled @@ -2003,29 +2033,77 @@ user_visible_resume_ptid (int step) else if ((scheduler_mode == schedlock_on) || (scheduler_mode == schedlock_step && step)) { - /* User-settable 'scheduler' mode requires solo thread resume. */ + /* User-settable 'scheduler' mode requires solo thread + resume. */ resume_ptid = inferior_ptid; } + else if (!sched_multi && target_supports_multi_process ()) + { + /* Resume all threads of the current process (and none of other + processes). */ + resume_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid)); + } + else + { + /* Resume all threads of all processes. */ + resume_ptid = RESUME_ALL; + } - /* We may actually resume fewer threads at first, e.g., if a thread - is stopped at a breakpoint that needs stepping-off, but that - should not be visible to the user/frontend, and neither should - the frontend/user be allowed to proceed any of the threads that - happen to be stopped for internal run control handling, if a - previous command wanted them resumed. */ return resume_ptid; } +/* Wrapper for target_resume, that handles infrun-specific + bookkeeping. */ + +static void +do_target_resume (ptid_t resume_ptid, int step, enum gdb_signal sig) +{ + struct thread_info *tp = inferior_thread (); + + /* 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->suspend.stop_signal = GDB_SIGNAL_0; + + /* Advise target which signals may be handled silently. + + If we have removed breakpoints because we are stepping over one + in-line (in any thread), we need to receive all signals to avoid + accidentally skipping a breakpoint during execution of a signal + handler. + + Likewise if we're displaced stepping, otherwise a trap for a + breakpoint in a signal handler might be confused with the + displaced step finishing. We don't make the displaced_step_fixup + step distinguish the cases instead, because: + + - a backtrace while stopped in the signal handler would show the + scratch pad as frame older than the signal handler, instead of + the real mainline code. + + - when the thread is later resumed, the signal handler would + return to the scratch pad area, which would no longer be + valid. */ + if (step_over_info_valid_p () + || displaced_step_in_progress (ptid_get_pid (tp->ptid))) + target_pass_signals (0, NULL); + else + target_pass_signals ((int) GDB_SIGNAL_LAST, signal_pass); + + target_resume (resume_ptid, step, sig); +} + /* Resume the inferior, but allow a QUIT. This is useful if the user wants to interrupt some lengthy single-stepping operation (for child processes, the SIGINT goes to the inferior, and so we get a SIGINT random_signal, but for remote debugging and perhaps other targets, that's not true). - STEP nonzero if we should step (zero to continue instead). SIG is the signal to give the inferior (zero for none). */ void -resume (int step, enum gdb_signal sig) +resume (enum gdb_signal sig) { struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0); struct regcache *regcache = get_current_regcache (); @@ -2034,16 +2112,23 @@ resume (int step, enum gdb_signal sig) CORE_ADDR pc = regcache_read_pc (regcache); struct address_space *aspace = get_regcache_aspace (regcache); ptid_t resume_ptid; - /* From here on, this represents the caller's step vs continue - request, while STEP represents what we'll actually request the - target to do. STEP can decay from a step to a continue, if e.g., - we need to implement single-stepping with breakpoints (software - single-step). When deciding whether "set scheduler-locking step" - applies, it's the callers intention that counts. */ - const int entry_step = step; + /* This represents the user's step vs continue request. When + deciding whether "set scheduler-locking step" applies, it's the + user's intention that counts. */ + const int user_step = tp->control.stepping_command; + /* This represents what we'll actually request the target to do. + This can decay from a step to a continue, if e.g., we need to + implement single-stepping with breakpoints (software + single-step). */ + int step; + + tp->stepped_breakpoint = 0; QUIT; + /* Depends on stepped_breakpoint. */ + step = currently_stepping (tp); + if (current_inferior ()->waiting_for_vfork_done) { /* Don't try to single-step a vfork parent that is waiting for @@ -2078,13 +2163,76 @@ resume (int step, enum gdb_signal sig) breakpoints can't be removed. So we have to test for it here. */ if (breakpoint_here_p (aspace, pc) == permanent_breakpoint_here) { - if (gdbarch_skip_permanent_breakpoint_p (gdbarch)) - gdbarch_skip_permanent_breakpoint (gdbarch, regcache); + if (sig != GDB_SIGNAL_0) + { + /* We have a signal to pass to the inferior. The resume + may, or may not take us to the signal handler. If this + is a step, we'll need to stop in the signal handler, if + there's one, (if the target supports stepping into + handlers), or in the next mainline instruction, if + there's no handler. If this is a continue, we need to be + sure to run the handler with all breakpoints inserted. + In all cases, set a breakpoint at the current address + (where the handler returns to), and once that breakpoint + is hit, resume skipping the permanent breakpoint. If + that breakpoint isn't hit, then we've stepped into the + signal handler (or hit some other event). We'll delete + the step-resume breakpoint then. */ + + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: resume: skipping permanent breakpoint, " + "deliver signal first\n"); + + clear_step_over_info (); + tp->control.trap_expected = 0; + + if (tp->control.step_resume_breakpoint == NULL) + { + /* Set a "high-priority" step-resume, as we don't want + user breakpoints at PC to trigger (again) when this + hits. */ + insert_hp_step_resume_breakpoint_at_frame (get_current_frame ()); + gdb_assert (tp->control.step_resume_breakpoint->loc->permanent); + + tp->step_after_step_resume_breakpoint = step; + } + + insert_breakpoints (); + } else - error (_("\ -The program is stopped at a permanent breakpoint, but GDB does not know\n\ -how to step past a permanent breakpoint on this architecture. Try using\n\ -a command like `return' or `jump' to continue execution.")); + { + /* There's no signal to pass, we can go ahead and skip the + permanent breakpoint manually. */ + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: resume: skipping permanent breakpoint\n"); + gdbarch_skip_permanent_breakpoint (gdbarch, regcache); + /* Update pc to reflect the new address from which we will + execute instructions. */ + pc = regcache_read_pc (regcache); + + if (step) + { + /* We've already advanced the PC, so the stepping part + is done. Now we need to arrange for a trap to be + reported to handle_inferior_event. Set a breakpoint + at the current PC, and run to it. Don't update + prev_pc, because if we end in + switch_back_to_stepped_thread, we want the "expected + thread advanced also" branch to be taken. IOW, we + don't want this thread to step further from PC + (overstep). */ + gdb_assert (!step_over_info_valid_p ()); + insert_single_step_breakpoint (gdbarch, aspace, pc); + insert_breakpoints (); + + resume_ptid = user_visible_resume_ptid (user_step); + do_target_resume (resume_ptid, 0, GDB_SIGNAL_0); + discard_cleanups (old_cleanups); + return; + } + } } /* If we have a breakpoint to step over, make sure to do a single @@ -2104,8 +2252,8 @@ a command like `return' or `jump' to continue execution.")); event, displaced stepping breaks the vfork child similarly as single step software breakpoint. */ if (use_displaced_stepping (gdbarch) - && (tp->control.trap_expected - || (step && gdbarch_software_single_step_p (gdbarch))) + && tp->control.trap_expected + && !step_over_info_valid_p () && sig == GDB_SIGNAL_0 && !current_inferior ()->waiting_for_vfork_done) { @@ -2122,7 +2270,7 @@ a command like `return' or `jump' to continue execution.")); Unless we're calling an inferior function, as in that case we pretend the inferior doesn't run at all. */ if (!tp->control.in_infcall) - set_running (user_visible_resume_ptid (entry_step), 1); + set_running (user_visible_resume_ptid (user_step), 1); discard_cleanups (old_cleanups); return; } @@ -2164,7 +2312,7 @@ a command like `return' or `jump' to continue execution.")); at the current address, deliver the signal without stepping, and once we arrive back at the step-resume breakpoint, actually step over the breakpoint we originally wanted to step over. */ - if (singlestep_breakpoints_inserted_p + if (thread_has_single_step_breakpoints_set (tp) && sig != GDB_SIGNAL_0 && step_over_info_valid_p ()) { @@ -2179,8 +2327,7 @@ a command like `return' or `jump' to continue execution.")); tp->step_after_step_resume_breakpoint = 1; } - remove_single_step_breakpoints (); - singlestep_breakpoints_inserted_p = 0; + delete_single_step_breakpoints (tp); clear_step_over_info (); tp->control.trap_expected = 0; @@ -2191,12 +2338,12 @@ a command like `return' or `jump' to continue execution.")); /* If STEP is set, it's a request to use hardware stepping facilities. But in that case, we should never use singlestep breakpoint. */ - gdb_assert (!(singlestep_breakpoints_inserted_p && step)); + gdb_assert (!(thread_has_single_step_breakpoints_set (tp) && step)); /* Decide the set of threads to ask the target to resume. Start by assuming everything will be resumed, than narrow the set by applying increasingly restricting conditions. */ - resume_ptid = user_visible_resume_ptid (entry_step); + resume_ptid = user_visible_resume_ptid (user_step); /* Even if RESUME_PTID is a wildcard, and we end up resuming less (e.g., we might need to step over a breakpoint), from the @@ -2207,7 +2354,7 @@ a command like `return' or `jump' to continue execution.")); set_running (resume_ptid, 1); /* Maybe resume a single thread after all. */ - if ((step || singlestep_breakpoints_inserted_p) + if ((step || thread_has_single_step_breakpoints_set (tp)) && tp->control.trap_expected) { /* We're allowing a thread to run past a breakpoint it has @@ -2218,20 +2365,44 @@ a command like `return' or `jump' to continue execution.")); resume_ptid = inferior_ptid; } - if (gdbarch_cannot_step_breakpoint (gdbarch)) - { + if (execution_direction != EXEC_REVERSE + && step && breakpoint_inserted_here_p (aspace, pc)) + { + /* The only case we currently need to step a breakpoint + instruction is when we have a signal to deliver. See + handle_signal_stop where we handle random signals that could + take out us out of the stepping range. Normally, in that + case we end up continuing (instead of stepping) over the + signal handler with a breakpoint at PC, but there are cases + where we should _always_ single-step, even if we have a + step-resume breakpoint, like when a software watchpoint is + set. Assuming single-stepping and delivering a signal at the + same time would takes us to the signal handler, then we could + have removed the breakpoint at PC to step over it. However, + some hardware step targets (like e.g., Mac OS) can't step + into signal handlers, and for those, we need to leave the + breakpoint at PC inserted, as otherwise if the handler + recurses and executes PC again, it'll miss the breakpoint. + So we leave the breakpoint inserted anyway, but we need to + record that we tried to step a breakpoint instruction, so + that adjust_pc_after_break doesn't end up confused. */ + gdb_assert (sig != GDB_SIGNAL_0); + + tp->stepped_breakpoint = 1; + /* Most targets can step a breakpoint instruction, thus executing it normally. But if this one cannot, just continue and we will hit it anyway. */ - if (step && breakpoint_inserted_here_p (aspace, pc)) + if (gdbarch_cannot_step_breakpoint (gdbarch)) step = 0; } if (debug_displaced && use_displaced_stepping (gdbarch) - && tp->control.trap_expected) + && tp->control.trap_expected + && !step_over_info_valid_p ()) { - struct regcache *resume_regcache = get_thread_regcache (resume_ptid); + struct regcache *resume_regcache = get_thread_regcache (tp->ptid); struct gdbarch *resume_gdbarch = get_regcache_arch (resume_regcache); CORE_ADDR actual_pc = regcache_read_pc (resume_regcache); gdb_byte buf[4]; @@ -2252,24 +2423,7 @@ a command like `return' or `jump' to continue execution.")); gdb_assert (pc_in_thread_step_range (pc, tp)); } - /* 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->suspend.stop_signal = GDB_SIGNAL_0; - - /* Advise target which signals may be handled silently. If we have - removed breakpoints because we are stepping over one (in any - thread), we need to receive all signals to avoid accidentally - skipping a breakpoint during execution of a signal handler. */ - if (step_over_info_valid_p ()) - target_pass_signals (0, NULL); - else - target_pass_signals ((int) GDB_SIGNAL_LAST, signal_pass); - - target_resume (resume_ptid, step, sig); - + do_target_resume (resume_ptid, step, sig); discard_cleanups (old_cleanups); } @@ -2298,6 +2452,7 @@ clear_proceed_status_thread (struct thread_info *tp) tp->control.step_frame_id = null_frame_id; tp->control.step_stack_frame_id = null_frame_id; tp->control.step_over_calls = STEP_OVER_UNDEBUGGABLE; + tp->control.step_start_function = NULL; tp->stop_requested = 0; tp->control.stop_step = 0; @@ -2305,6 +2460,7 @@ clear_proceed_status_thread (struct thread_info *tp) tp->control.proceed_to_finish = 0; tp->control.command_interp = NULL; + tp->control.stepping_command = 0; /* Discard any remaining commands or status from previous stop. */ bpstat_clear (&tp->control.stop_bpstat); @@ -2370,7 +2526,8 @@ thread_still_needs_step_over (struct thread_info *tp) struct regcache *regcache = get_thread_regcache (tp->ptid); if (breakpoint_here_p (get_regcache_aspace (regcache), - regcache_read_pc (regcache))) + regcache_read_pc (regcache)) + == ordinary_breakpoint_here) return 1; tp->stepping_over_breakpoint = 0; @@ -2383,21 +2540,19 @@ thread_still_needs_step_over (struct thread_info *tp) we're about to do a step/next-like command to a thread. */ static int -schedlock_applies (int step) +schedlock_applies (struct thread_info *tp) { return (scheduler_mode == schedlock_on || (scheduler_mode == schedlock_step - && step)); + && tp->control.stepping_command)); } /* Look a thread other than EXCEPT that has previously reported a breakpoint event, and thus needs a step-over in order to make - progress. Returns NULL is none is found. STEP indicates whether - we're about to step the current thread, in order to decide whether - "set scheduler-locking step" applies. */ + progress. Returns NULL is none is found. */ static struct thread_info * -find_thread_needs_step_over (int step, struct thread_info *except) +find_thread_needs_step_over (struct thread_info *except) { struct thread_info *tp, *current; @@ -2408,7 +2563,7 @@ find_thread_needs_step_over (int step, struct thread_info *except) /* If scheduler locking applies, we can avoid iterating over all threads. */ - if (schedlock_applies (step)) + if (schedlock_applies (except)) { if (except != current && thread_still_needs_step_over (current)) @@ -2447,7 +2602,7 @@ find_thread_needs_step_over (int step, struct thread_info *except) You should call clear_proceed_status before calling proceed. */ void -proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step) +proceed (CORE_ADDR addr, enum gdb_signal siggnal) { struct regcache *regcache; struct gdbarch *gdbarch; @@ -2476,17 +2631,13 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step) pc = regcache_read_pc (regcache); tp = inferior_thread (); - if (step > 0) - step_start_function = find_pc_function (pc); - if (step < 0) - stop_after_trap = 1; - /* Fill in with reasonable starting values. */ init_thread_stepping_state (tp); if (addr == (CORE_ADDR) -1) { - if (pc == stop_pc && breakpoint_here_p (aspace, pc) + if (pc == stop_pc + && breakpoint_here_p (aspace, pc) == ordinary_breakpoint_here && execution_direction != EXEC_REVERSE) /* There is a breakpoint at the address we will resume at, step one instruction before inserting breakpoints so that @@ -2522,9 +2673,9 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step) if (debug_infrun) fprintf_unfiltered (gdb_stdlog, - "infrun: proceed (addr=%s, signal=%s, step=%d)\n", + "infrun: proceed (addr=%s, signal=%s)\n", paddress (gdbarch, addr), - gdb_signal_to_symbol_string (siggnal), step); + gdb_signal_to_symbol_string (siggnal)); if (non_stop) /* In non-stop, each thread is handled individually. The context @@ -2544,7 +2695,7 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step) Look for a thread other than the current (TP) that reported a breakpoint hit and hasn't been resumed yet since. */ - step_over = find_thread_needs_step_over (step, tp); + step_over = find_thread_needs_step_over (tp); if (step_over != NULL) { if (debug_infrun) @@ -2553,7 +2704,7 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step) target_pid_to_str (step_over->ptid)); /* Store the prev_pc for the stepping thread too, needed by - switch_back_to_stepping thread. */ + switch_back_to_stepped_thread. */ tp->prev_pc = regcache_read_pc (get_current_regcache ()); switch_to_thread (step_over->ptid); tp = step_over; @@ -2610,8 +2761,7 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step) tp->prev_pc = regcache_read_pc (get_current_regcache ()); /* Resume inferior. */ - resume (tp->control.trap_expected || step || bpstat_should_step (), - tp->suspend.stop_signal); + resume (tp->suspend.stop_signal); /* Wait for it to stop (if not standalone) and in any case decode why it stopped, and act accordingly. */ @@ -2676,26 +2826,9 @@ init_wait_for_inferior (void) /* Discard any skipped inlined frames. */ clear_inline_frame_state (minus_one_ptid); - - singlestep_ptid = null_ptid; - singlestep_pc = 0; } -/* This enum encodes possible reasons for doing a target_wait, so that - wfi can call target_wait in one place. (Ultimately the call will be - moved out of the infinite loop entirely.) */ - -enum infwait_states -{ - infwait_normal_state, - infwait_step_watch_state, - infwait_nonstep_watch_state -}; - -/* Current inferior wait state. */ -static enum infwait_states infwait_state; - /* Data to be passed around while handling an event. This data is discarded between events. */ struct execution_control_state @@ -2846,54 +2979,71 @@ infrun_thread_thread_exit (struct thread_info *tp, int silent) nullify_last_target_wait_ptid (); } -/* Callback for iterate_over_threads. */ +/* Delete the step resume, single-step and longjmp/exception resume + breakpoints of TP. */ -static int -delete_step_resume_breakpoint_callback (struct thread_info *info, void *data) +static void +delete_thread_infrun_breakpoints (struct thread_info *tp) { - if (is_exited (info->ptid)) - return 0; - - delete_step_resume_breakpoint (info); - delete_exception_resume_breakpoint (info); - return 0; + delete_step_resume_breakpoint (tp); + delete_exception_resume_breakpoint (tp); + delete_single_step_breakpoints (tp); } -/* In all-stop, delete the step resume breakpoint of any thread that - had one. In non-stop, delete the step resume breakpoint of the - thread that just stopped. */ +/* If the target still has execution, call FUNC for each thread that + just stopped. In all-stop, that's all the non-exited threads; in + non-stop, that's the current thread, only. */ + +typedef void (*for_each_just_stopped_thread_callback_func) + (struct thread_info *tp); static void -delete_step_thread_step_resume_breakpoint (void) +for_each_just_stopped_thread (for_each_just_stopped_thread_callback_func func) { - if (!target_has_execution - || ptid_equal (inferior_ptid, null_ptid)) - /* If the inferior has exited, we have already deleted the step - resume breakpoints out of GDB's lists. */ + if (!target_has_execution || ptid_equal (inferior_ptid, null_ptid)) return; if (non_stop) { - /* If in non-stop mode, only delete the step-resume or - longjmp-resume breakpoint of the thread that just stopped - stepping. */ - struct thread_info *tp = inferior_thread (); - - delete_step_resume_breakpoint (tp); - delete_exception_resume_breakpoint (tp); + /* If in non-stop mode, only the current thread stopped. */ + func (inferior_thread ()); } else - /* In all-stop mode, delete all step-resume and longjmp-resume - breakpoints of any thread that had them. */ - iterate_over_threads (delete_step_resume_breakpoint_callback, NULL); + { + struct thread_info *tp; + + /* In all-stop mode, all threads have stopped. */ + ALL_NON_EXITED_THREADS (tp) + { + func (tp); + } + } +} + +/* Delete the step resume and longjmp/exception resume breakpoints of + the threads that just stopped. */ + +static void +delete_just_stopped_threads_infrun_breakpoints (void) +{ + for_each_just_stopped_thread (delete_thread_infrun_breakpoints); +} + +/* Delete the single-step breakpoints of the threads that just + stopped. */ + +static void +delete_just_stopped_threads_single_step_breakpoints (void) +{ + for_each_just_stopped_thread (delete_single_step_breakpoints); } /* A cleanup wrapper. */ static void -delete_step_thread_step_resume_breakpoint_cleanup (void *arg) +delete_just_stopped_threads_infrun_breakpoints_cleanup (void *arg) { - delete_step_thread_step_resume_breakpoint (); + delete_just_stopped_threads_infrun_breakpoints (); } /* Pretty print the results of target_wait, for debugging purposes. */ @@ -2912,14 +3062,19 @@ print_target_wait_results (ptid_t waiton_ptid, ptid_t result_ptid, is set. */ fprintf_unfiltered (tmp_stream, - "infrun: target_wait (%d", ptid_get_pid (waiton_ptid)); + "infrun: target_wait (%d.%ld.%ld", + ptid_get_pid (waiton_ptid), + ptid_get_lwp (waiton_ptid), + ptid_get_tid (waiton_ptid)); if (ptid_get_pid (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", + "infrun: %d.%ld.%ld [%s],\n", ptid_get_pid (result_ptid), + ptid_get_lwp (result_ptid), + ptid_get_tid (result_ptid), target_pid_to_str (result_ptid)); fprintf_unfiltered (tmp_stream, "infrun: %s\n", @@ -3023,19 +3178,25 @@ void wait_for_inferior (void) { struct cleanup *old_cleanups; + struct cleanup *thread_state_chain; if (debug_infrun) fprintf_unfiltered (gdb_stdlog, "infrun: wait_for_inferior ()\n"); - old_cleanups = - make_cleanup (delete_step_thread_step_resume_breakpoint_cleanup, NULL); + old_cleanups + = make_cleanup (delete_just_stopped_threads_infrun_breakpoints_cleanup, + NULL); + + /* If an error happens while handling the event, propagate GDB's + knowledge of the executing state to the frontend/user running + state. */ + thread_state_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid); while (1) { struct execution_control_state ecss; struct execution_control_state *ecs = &ecss; - struct cleanup *old_chain; ptid_t waiton_ptid = minus_one_ptid; memset (ecs, 0, sizeof (*ecs)); @@ -3056,24 +3217,46 @@ wait_for_inferior (void) if (debug_infrun) print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws); - /* If an error happens while handling the event, propagate GDB's - knowledge of the executing state to the frontend/user running - state. */ - old_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid); - /* Now figure out what to do with the result of the result. */ handle_inferior_event (ecs); - /* No error, don't finish the state yet. */ - discard_cleanups (old_chain); - if (!ecs->wait_some_more) break; } + /* No error, don't finish the state yet. */ + discard_cleanups (thread_state_chain); + do_cleanups (old_cleanups); } +/* Cleanup that reinstalls the readline callback handler, if the + target is running in the background. If while handling the target + event something triggered a secondary prompt, like e.g., a + pagination prompt, we'll have removed the callback handler (see + gdb_readline_wrapper_line). Need to do this as we go back to the + event loop, ready to process further input. Note this has no + effect if the handler hasn't actually been removed, because calling + rl_callback_handler_install resets the line buffer, thus losing + input. */ + +static void +reinstall_readline_callback_handler_cleanup (void *arg) +{ + if (!interpreter_async) + { + /* We're not going back to the top level event loop yet. Don't + install the readline callback, as it'd prep the terminal, + readline-style (raw, noecho) (e.g., --batch). We'll install + it the next time the prompt is displayed, when we're ready + for input. */ + return; + } + + if (async_command_editing_p && !sync_execution) + gdb_rl_callback_handler_reinstall (); +} + /* Asynchronous version of wait_for_inferior. It is called by the event loop whenever a change of state is detected on the file descriptor corresponding to the target. It can be called more than @@ -3096,6 +3279,9 @@ fetch_inferior_event (void *client_data) memset (ecs, 0, sizeof (*ecs)); + /* End up with readline processing input, if necessary. */ + make_cleanup (reinstall_readline_callback_handler_cleanup, NULL); + /* We're handling a live event, so make sure we're doing live debugging. If we're looking at traceframes while the target is running, we're going to need to get back to that mode after @@ -3144,14 +3330,16 @@ fetch_inferior_event (void *client_data) still for the thread which has thrown the exception. */ make_bpstat_clear_actions_cleanup (); + make_cleanup (delete_just_stopped_threads_infrun_breakpoints_cleanup, NULL); + /* Now figure out what to do with the result of the result. */ handle_inferior_event (ecs); if (!ecs->wait_some_more) { - struct inferior *inf = find_inferior_pid (ptid_get_pid (ecs->ptid)); + struct inferior *inf = find_inferior_ptid (ecs->ptid); - delete_step_thread_step_resume_breakpoint (); + delete_just_stopped_threads_infrun_breakpoints (); /* We may not find an inferior if this was a process exit. */ if (inf == NULL || inf->control.stop_soon == NO_STOP_QUIETLY) @@ -3209,6 +3397,7 @@ set_step_info (struct frame_info *frame, struct symtab_and_line sal) void init_thread_stepping_state (struct thread_info *tss) { + tss->stepped_breakpoint = 0; tss->stepping_over_breakpoint = 0; tss->stepping_over_watchpoint = 0; tss->step_after_step_resume_breakpoint = 0; @@ -3321,12 +3510,24 @@ adjust_pc_after_break (struct execution_control_state *ecs) if (execution_direction == EXEC_REVERSE) return; + /* If the target can tell whether the thread hit a SW breakpoint, + trust it. Targets that can tell also adjust the PC + themselves. */ + if (target_supports_stopped_by_sw_breakpoint ()) + return; + + /* Note that relying on whether a breakpoint is planted in memory to + determine this can fail. E.g,. the breakpoint could have been + removed since. Or the thread could have been told to step an + instruction the size of a breakpoint instruction, and only + _after_ was a breakpoint inserted at its address. */ + /* If this target does not decrement the PC after breakpoints, then we have nothing to do. */ regcache = get_thread_regcache (ecs->ptid); gdbarch = get_regcache_arch (regcache); - decr_pc = target_decr_pc_after_break (gdbarch); + decr_pc = gdbarch_decr_pc_after_break (gdbarch); if (decr_pc == 0) return; @@ -3336,6 +3537,11 @@ adjust_pc_after_break (struct execution_control_state *ecs) breakpoint would be. */ breakpoint_pc = regcache_read_pc (regcache) - decr_pc; + /* If the target can't tell whether a software breakpoint triggered, + fallback to figuring it out based on breakpoints we think were + inserted in the target, and on whether the thread was stepped or + continued. */ + /* Check whether there actually is a software breakpoint inserted at that location. @@ -3343,7 +3549,10 @@ adjust_pc_after_break (struct execution_control_state *ecs) removed a breakpoint, but stop events for that breakpoint were already queued and arrive later. To suppress those spurious SIGTRAPs, we keep a list of such breakpoint locations for a bit, - and retire them after a number of stop events are reported. */ + and retire them after a number of stop events are reported. Note + this is an heuristic and can thus get confused. The real fix is + to get the "stopped by SW BP and needs adjustment" info out of + the target/kernel (and thus never reach here; see above). */ if (software_breakpoint_inserted_here_p (aspace, breakpoint_pc) || (non_stop && moribund_breakpoint_here_p (aspace, breakpoint_pc))) { @@ -3359,7 +3568,6 @@ adjust_pc_after_break (struct execution_control_state *ecs) The SIGTRAP can be due to a completed hardware single-step only if - we didn't insert software single-step breakpoints - - the thread to be examined is still the current thread - this thread is currently being stepped If any of these events did not occur, we must have stopped due @@ -3370,10 +3578,10 @@ adjust_pc_after_break (struct execution_control_state *ecs) software breakpoint. In this case (prev_pc == breakpoint_pc), we also need to back up to the breakpoint address. */ - if (singlestep_breakpoints_inserted_p - || !ptid_equal (ecs->ptid, inferior_ptid) + if (thread_has_single_step_breakpoints_set (ecs->event_thread) || !currently_stepping (ecs->event_thread) - || ecs->event_thread->prev_pc == breakpoint_pc) + || (ecs->event_thread->stepped_breakpoint + && ecs->event_thread->prev_pc == breakpoint_pc)) regcache_write_pc (regcache, breakpoint_pc); do_cleanups (old_cleanups); @@ -3466,7 +3674,7 @@ fill_in_stop_func (struct gdbarch *gdbarch, static enum stop_kind get_inferior_stop_soon (ptid_t ptid) { - struct inferior *inf = find_inferior_pid (ptid_get_pid (ptid)); + struct inferior *inf = find_inferior_ptid (ptid); gdb_assert (inf != NULL); return inf->control.stop_soon; @@ -3659,7 +3867,7 @@ handle_inferior_event (struct execution_control_state *ecs) addresses. Make sure new breakpoints are inserted. */ if (stop_soon == NO_STOP_QUIETLY) insert_breakpoints (); - resume (0, GDB_SIGNAL_0); + resume (GDB_SIGNAL_0); prepare_to_wait (ecs); return; } @@ -3683,7 +3891,7 @@ handle_inferior_event (struct execution_control_state *ecs) fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SPURIOUS\n"); if (!ptid_equal (ecs->ptid, inferior_ptid)) context_switch (ecs->ptid); - resume (0, GDB_SIGNAL_0); + resume (GDB_SIGNAL_0); prepare_to_wait (ecs); return; @@ -3700,7 +3908,7 @@ handle_inferior_event (struct execution_control_state *ecs) } inferior_ptid = ecs->ptid; - set_current_inferior (find_inferior_pid (ptid_get_pid (ecs->ptid))); + set_current_inferior (find_inferior_ptid (ecs->ptid)); set_current_program_space (current_inferior ()->pspace); handle_vfork_child_exec_or_exit (0); target_terminal_ours (); /* Must do this before mourn anyway. */ @@ -3756,8 +3964,6 @@ Cannot fill $_exitsignal with the correct signal number.\n")); gdb_flush (gdb_stdout); target_mourn_inferior (); - singlestep_breakpoints_inserted_p = 0; - cancel_single_step_breakpoints (); stop_print_frame = 0; stop_waiting (ecs); return; @@ -3786,7 +3992,7 @@ Cannot fill $_exitsignal with the correct signal number.\n")); if (displaced && ptid_equal (displaced->step_ptid, ecs->ptid)) { struct inferior *parent_inf - = find_inferior_pid (ptid_get_pid (ecs->ptid)); + = find_inferior_ptid (ecs->ptid); struct regcache *child_regcache; CORE_ADDR parent_pc; @@ -3850,12 +4056,7 @@ Cannot fill $_exitsignal with the correct signal number.\n")); detach_breakpoints (ecs->ws.value.related_pid); } - if (singlestep_breakpoints_inserted_p) - { - /* Pull the single step breakpoints out of the target. */ - remove_single_step_breakpoints (); - singlestep_breakpoints_inserted_p = 0; - } + delete_just_stopped_threads_single_step_breakpoints (); /* In case the event is caught by a catchpoint, remember that the event is to be followed at the next resume of the thread, @@ -3942,9 +4143,6 @@ Cannot fill $_exitsignal with the correct signal number.\n")); if (!ptid_equal (ecs->ptid, inferior_ptid)) context_switch (ecs->ptid); - singlestep_breakpoints_inserted_p = 0; - cancel_single_step_breakpoints (); - stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid)); /* Do whatever is necessary to the parent branch of the vfork. */ @@ -4010,14 +4208,7 @@ Cannot fill $_exitsignal with the correct signal number.\n")); fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_NO_HISTORY\n"); /* Reverse execution: target ran out of history info. */ - /* Pull the single step breakpoints out of the target. */ - if (singlestep_breakpoints_inserted_p) - { - if (!ptid_equal (ecs->ptid, inferior_ptid)) - context_switch (ecs->ptid); - remove_single_step_breakpoints (); - singlestep_breakpoints_inserted_p = 0; - } + delete_just_stopped_threads_single_step_breakpoints (); stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid)); observer_notify_no_history (); stop_waiting (ecs); @@ -4156,38 +4347,46 @@ handle_signal_stop (struct execution_control_state *ecs) gdbarch = get_frame_arch (frame); /* Pull the single step breakpoints out of the target. */ - if (singlestep_breakpoints_inserted_p) + if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP) { + struct regcache *regcache; + struct address_space *aspace; + CORE_ADDR pc; + + regcache = get_thread_regcache (ecs->ptid); + aspace = get_regcache_aspace (regcache); + pc = regcache_read_pc (regcache); + /* However, before doing so, if this single-step breakpoint was actually for another thread, set this thread up for moving past it. */ - if (!ptid_equal (ecs->ptid, singlestep_ptid) - && ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP) + if (!thread_has_single_step_breakpoint_here (ecs->event_thread, + aspace, pc)) { - struct regcache *regcache; - struct address_space *aspace; - CORE_ADDR pc; - - regcache = get_thread_regcache (ecs->ptid); - aspace = get_regcache_aspace (regcache); - pc = regcache_read_pc (regcache); if (single_step_breakpoint_inserted_here_p (aspace, pc)) { if (debug_infrun) { fprintf_unfiltered (gdb_stdlog, - "infrun: [%s] hit step over single-step" - " breakpoint of [%s]\n", - target_pid_to_str (ecs->ptid), - target_pid_to_str (singlestep_ptid)); + "infrun: [%s] hit another thread's " + "single-step breakpoint\n", + target_pid_to_str (ecs->ptid)); } ecs->hit_singlestep_breakpoint = 1; } } - - remove_single_step_breakpoints (); - singlestep_breakpoints_inserted_p = 0; + else + { + if (debug_infrun) + { + fprintf_unfiltered (gdb_stdlog, + "infrun: [%s] hit its " + "single-step breakpoint\n", + target_pid_to_str (ecs->ptid)); + } + } } + delete_just_stopped_threads_single_step_breakpoints (); if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP && ecs->event_thread->control.trap_expected @@ -4368,6 +4567,54 @@ handle_signal_stop (struct execution_control_state *ecs) = !bpstat_explains_signal (ecs->event_thread->control.stop_bpstat, ecs->event_thread->suspend.stop_signal); + /* Maybe this was a trap for a software breakpoint that has since + been removed. */ + if (random_signal && target_stopped_by_sw_breakpoint ()) + { + if (program_breakpoint_here_p (gdbarch, stop_pc)) + { + struct regcache *regcache; + int decr_pc; + + /* Re-adjust PC to what the program would see if GDB was not + debugging it. */ + regcache = get_thread_regcache (ecs->event_thread->ptid); + decr_pc = gdbarch_decr_pc_after_break (gdbarch); + if (decr_pc != 0) + { + struct cleanup *old_cleanups = make_cleanup (null_cleanup, NULL); + + if (record_full_is_used ()) + record_full_gdb_operation_disable_set (); + + regcache_write_pc (regcache, stop_pc + decr_pc); + + do_cleanups (old_cleanups); + } + } + else + { + /* A delayed software breakpoint event. Ignore the trap. */ + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: delayed software breakpoint " + "trap, ignoring\n"); + random_signal = 0; + } + } + + /* Maybe this was a trap for a hardware breakpoint/watchpoint that + has since been removed. */ + if (random_signal && target_stopped_by_hw_breakpoint ()) + { + /* A delayed hardware breakpoint event. Ignore the trap. */ + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: delayed hardware breakpoint/watchpoint " + "trap, ignoring\n"); + random_signal = 0; + } + /* If not, perhaps stepping/nexting can. */ if (random_signal) random_signal = !(ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP @@ -4389,7 +4636,7 @@ handle_signal_stop (struct execution_control_state *ecs) if (random_signal) { /* Signal not for debugging purposes. */ - struct inferior *inf = find_inferior_pid (ptid_get_pid (ecs->ptid)); + struct inferior *inf = find_inferior_ptid (ecs->ptid); enum gdb_signal stop_signal = ecs->event_thread->suspend.stop_signal; if (debug_infrun) @@ -4456,9 +4703,9 @@ handle_signal_stop (struct execution_control_state *ecs) return; } - if (ecs->event_thread->control.step_range_end != 0 - && ecs->event_thread->suspend.stop_signal != GDB_SIGNAL_0 - && pc_in_thread_step_range (stop_pc, ecs->event_thread) + if (ecs->event_thread->suspend.stop_signal != GDB_SIGNAL_0 + && (pc_in_thread_step_range (stop_pc, ecs->event_thread) + || ecs->event_thread->control.step_range_end == 1) && frame_id_eq (get_stack_frame_id (frame), ecs->event_thread->control.step_stack_frame_id) && ecs->event_thread->control.step_resume_breakpoint == NULL) @@ -4478,6 +4725,7 @@ handle_signal_stop (struct execution_control_state *ecs) "single-step range\n"); insert_hp_step_resume_breakpoint_at_frame (frame); + ecs->event_thread->step_after_step_resume_breakpoint = 1; /* Reset trap_expected to ensure breakpoints are re-inserted. */ ecs->event_thread->control.trap_expected = 0; keep_going (ecs); @@ -4740,6 +4988,31 @@ process_event_stop_test (struct execution_control_state *ecs) break; } + /* If we stepped a permanent breakpoint and we had a high priority + step-resume breakpoint for the address we stepped, but we didn't + hit it, then we must have stepped into the signal handler. The + step-resume was only necessary to catch the case of _not_ + stepping into the handler, so delete it, and fall through to + checking whether the step finished. */ + if (ecs->event_thread->stepped_breakpoint) + { + struct breakpoint *sr_bp + = ecs->event_thread->control.step_resume_breakpoint; + + if (sr_bp != NULL + && sr_bp->loc->permanent + && sr_bp->type == bp_hp_step_resume + && sr_bp->loc->address == ecs->event_thread->prev_pc) + { + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: stepped permanent breakpoint, stopped in " + "handler\n"); + delete_step_resume_breakpoint (ecs->event_thread); + ecs->event_thread->step_after_step_resume_breakpoint = 0; + } + } + /* We come here if we hit a breakpoint but should not stop for it. Possibly we also were stepping and should stop for that. So fall through and test for stepping. But, if not stepping, do not @@ -4943,24 +5216,19 @@ process_event_stop_test (struct execution_control_state *ecs) ecs->event_thread->control.step_stack_frame_id) && (!frame_id_eq (ecs->event_thread->control.step_stack_frame_id, outer_frame_id) - || step_start_function != find_pc_function (stop_pc)))) + || (ecs->event_thread->control.step_start_function + != find_pc_function (stop_pc))))) { CORE_ADDR real_stop_pc; if (debug_infrun) fprintf_unfiltered (gdb_stdlog, "infrun: stepped into subroutine\n"); - if ((ecs->event_thread->control.step_over_calls == STEP_OVER_NONE) - || ((ecs->event_thread->control.step_range_end == 1) - && in_prologue (gdbarch, ecs->event_thread->prev_pc, - ecs->stop_func_start))) + if (ecs->event_thread->control.step_over_calls == STEP_OVER_NONE) { /* I presume that step_over_calls is only 0 when we're supposed to be stepping at the assembly language level ("stepi"). Just stop. */ - /* Also, maybe we just did a "nexti" inside a prolog, so we - thought it was a subroutine call but it was not. Stop as - well. FENN */ /* And this works the same backward as frontward. MVS */ end_stepping_range (ecs); return; @@ -5372,7 +5640,7 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs) current thread is stepping. If some other thread not the event thread is stepping, then it must be that scheduler locking is not in effect. */ - if (schedlock_applies (0)) + if (schedlock_applies (ecs->event_thread)) return 0; /* Look for the stepping/nexting thread, and check if any other @@ -5408,7 +5676,7 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs) stepping, then scheduler locking can't be in effect, otherwise we wouldn't have resumed the current event thread in the first place. */ - gdb_assert (!schedlock_applies (1)); + gdb_assert (!schedlock_applies (tp)); stepping_thread = tp; } @@ -5510,22 +5778,29 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs) breakpoint forward, one instruction at a time, overstepping. */ - if (gdbarch_software_single_step_p (gdbarch) - && stop_pc != tp->prev_pc) + if (stop_pc != tp->prev_pc) { + ptid_t resume_ptid; + if (debug_infrun) fprintf_unfiltered (gdb_stdlog, "infrun: expected thread advanced also\n"); + /* Clear the info of the previous step-over, as it's no + longer valid. It's what keep_going would do too, if + we called it. Must do this before trying to insert + the sss breakpoint, otherwise if we were previously + trying to step over this exact address in another + thread, the breakpoint ends up not installed. */ + clear_step_over_info (); + insert_single_step_breakpoint (get_frame_arch (frame), get_frame_address_space (frame), stop_pc); - singlestep_breakpoints_inserted_p = 1; - ecs->event_thread->control.trap_expected = 1; - singlestep_ptid = inferior_ptid; - singlestep_pc = stop_pc; - resume (0, GDB_SIGNAL_0); + resume_ptid = user_visible_resume_ptid (tp->control.stepping_command); + do_target_resume (resume_ptid, + currently_stepping (tp), GDB_SIGNAL_0); prepare_to_wait (ecs); } else @@ -5551,6 +5826,7 @@ currently_stepping (struct thread_info *tp) return ((tp->control.step_range_end && tp->control.step_resume_breakpoint == NULL) || tp->control.trap_expected + || tp->stepped_breakpoint || bpstat_should_step ()); } @@ -5562,13 +5838,13 @@ static void handle_step_into_function (struct gdbarch *gdbarch, struct execution_control_state *ecs) { - struct symtab *s; + struct compunit_symtab *cust; struct symtab_and_line stop_func_sal, sr_sal; fill_in_stop_func (gdbarch, ecs); - s = find_pc_symtab (stop_pc); - if (s && s->language != language_asm) + cust = find_pc_compunit_symtab (stop_pc); + if (cust != NULL && compunit_language (cust) != language_asm) ecs->stop_func_start = gdbarch_skip_prologue (gdbarch, ecs->stop_func_start); @@ -5641,13 +5917,13 @@ static void handle_step_into_function_backward (struct gdbarch *gdbarch, struct execution_control_state *ecs) { - struct symtab *s; + struct compunit_symtab *cust; struct symtab_and_line stop_func_sal; fill_in_stop_func (gdbarch, ecs); - s = find_pc_symtab (stop_pc); - if (s && s->language != language_asm) + cust = find_pc_compunit_symtab (stop_pc); + if (cust != NULL && compunit_language (cust) != language_asm) ecs->stop_func_start = gdbarch_skip_prologue (gdbarch, ecs->stop_func_start); @@ -5801,10 +6077,7 @@ insert_exception_resume_breakpoint (struct thread_info *tp, struct frame_info *frame, struct symbol *sym) { - volatile struct gdb_exception e; - - /* We want to ignore errors here. */ - TRY_CATCH (e, RETURN_MASK_ERROR) + TRY { struct symbol *vsym; struct value *value; @@ -5833,6 +6106,11 @@ insert_exception_resume_breakpoint (struct thread_info *tp, inferior_thread ()->control.exception_resume_breakpoint = bp; } } + CATCH (e, RETURN_MASK_ERROR) + { + /* We want to ignore errors here. */ + } + END_CATCH } /* A helper for check_exception_resume that sets an @@ -5873,7 +6151,6 @@ static void check_exception_resume (struct execution_control_state *ecs, struct frame_info *frame) { - volatile struct gdb_exception e; struct bound_probe probe; struct symbol *func; @@ -5892,7 +6169,7 @@ check_exception_resume (struct execution_control_state *ecs, if (!func) return; - TRY_CATCH (e, RETURN_MASK_ERROR) + TRY { const struct block *b; struct block_iterator iter; @@ -5929,6 +6206,10 @@ check_exception_resume (struct execution_control_state *ecs, } } } + CATCH (e, RETURN_MASK_ERROR) + { + } + END_CATCH } static void @@ -5966,12 +6247,10 @@ keep_going (struct execution_control_state *ecs) are supposed to pass through to the inferior. Simply continue. */ discard_cleanups (old_cleanups); - resume (currently_stepping (ecs->event_thread), - ecs->event_thread->suspend.stop_signal); + resume (ecs->event_thread->suspend.stop_signal); } else { - volatile struct gdb_exception e; struct regcache *regcache = get_current_regcache (); int remove_bp; int remove_wps; @@ -6000,7 +6279,12 @@ keep_going (struct execution_control_state *ecs) remove_wps = (ecs->event_thread->stepping_over_watchpoint && !target_have_steppable_watchpoint); - if (remove_bp && !use_displaced_stepping (get_regcache_arch (regcache))) + /* We can't use displaced stepping if we need to step past a + watchpoint. The instruction copied to the scratch pad would + still trigger the watchpoint. */ + if (remove_bp + && (remove_wps + || !use_displaced_stepping (get_regcache_arch (regcache)))) { set_step_over_info (get_regcache_aspace (regcache), regcache_read_pc (regcache), remove_wps); @@ -6011,16 +6295,18 @@ keep_going (struct execution_control_state *ecs) clear_step_over_info (); /* Stop stepping if inserting breakpoints fails. */ - TRY_CATCH (e, RETURN_MASK_ERROR) + TRY { insert_breakpoints (); } - if (e.reason < 0) + CATCH (e, RETURN_MASK_ERROR) { exception_print (gdb_stderr, e); stop_waiting (ecs); + discard_cleanups (old_cleanups); return; } + END_CATCH ecs->event_thread->control.trap_expected = (remove_bp || remove_wps); @@ -6039,8 +6325,7 @@ keep_going (struct execution_control_state *ecs) ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0; discard_cleanups (old_cleanups); - resume (currently_stepping (ecs->event_thread), - ecs->event_thread->suspend.stop_signal); + resume (ecs->event_thread->suspend.stop_signal); } prepare_to_wait (ecs); @@ -6212,7 +6497,7 @@ print_stop_event (struct target_waitstatus *ws) if (tp->control.stop_step && frame_id_eq (tp->control.step_frame_id, get_frame_id (get_current_frame ())) - && step_start_function == find_pc_function (stop_pc)) + && tp->control.step_start_function == find_pc_function (stop_pc)) { /* Finished step, just print source line. */ source_flag = SRC_LINE; @@ -6818,72 +7103,6 @@ handle_completer (struct cmd_list_element *ignore, return return_val; } -static void -xdb_handle_command (char *args, int from_tty) -{ - char **argv; - struct cleanup *old_chain; - - if (args == NULL) - error_no_arg (_("xdb command")); - - /* Break the command line up into args. */ - - argv = gdb_buildargv (args); - old_chain = make_cleanup_freeargv (argv); - if (argv[1] != (char *) NULL) - { - char *argBuf; - int bufLen; - - bufLen = strlen (argv[0]) + 20; - argBuf = (char *) xmalloc (bufLen); - if (argBuf) - { - int validFlag = 1; - enum gdb_signal oursig; - - oursig = gdb_signal_from_name (argv[0]); - memset (argBuf, 0, bufLen); - if (strcmp (argv[1], "Q") == 0) - sprintf (argBuf, "%s %s", argv[0], "noprint"); - else - { - if (strcmp (argv[1], "s") == 0) - { - if (!signal_stop[oursig]) - sprintf (argBuf, "%s %s", argv[0], "stop"); - else - sprintf (argBuf, "%s %s", argv[0], "nostop"); - } - else if (strcmp (argv[1], "i") == 0) - { - if (!signal_program[oursig]) - sprintf (argBuf, "%s %s", argv[0], "pass"); - else - sprintf (argBuf, "%s %s", argv[0], "nopass"); - } - else if (strcmp (argv[1], "r") == 0) - { - if (!signal_print[oursig]) - sprintf (argBuf, "%s %s", argv[0], "print"); - else - sprintf (argBuf, "%s %s", argv[0], "noprint"); - } - else - validFlag = 0; - } - if (validFlag) - handle_command (argBuf, from_tty); - else - printf_filtered (_("Invalid signal handling flag.\n")); - if (argBuf) - xfree (argBuf); - } - } - do_cleanups (old_chain); -} - enum gdb_signal gdb_signal_from_command (int num) { @@ -7466,29 +7685,6 @@ may be interspersed with actions, with the actions being performed for\n\ all signals cumulatively specified.")); set_cmd_completer (c, handle_completer); - if (xdb_commands) - { - add_com ("lz", class_info, signals_info, _("\ -What debugger does when program gets various signals.\n\ -Specify a signal as argument to print info on that signal only.")); - add_com ("z", class_run, xdb_handle_command, _("\ -Specify how to handle a signal.\n\ -Args are signals and actions to apply to those signals.\n\ -Symbolic signals (e.g. SIGSEGV) are recommended but numeric signals\n\ -from 1-15 are allowed for compatibility with old versions of GDB.\n\ -Numeric ranges may be specified with the form LOW-HIGH (e.g. 1-5).\n\ -The special arg \"all\" is recognized to mean all signals except those\n\ -used by the debugger, typically SIGTRAP and SIGINT.\n\ -Recognized actions include \"s\" (toggles between stop and nostop),\n\ -\"r\" (toggles between print and noprint), \"i\" (toggles between pass and \ -nopass), \"Q\" (noprint)\n\ -Stop means reenter debugger if this signal happens (implies print).\n\ -Print means print a message if this signal happens.\n\ -Pass means let program see this signal; otherwise program doesn't know.\n\ -Ignore is a synonym for nopass and noignore is a synonym for pass.\n\ -Pass and Stop may be combined.")); - } - if (!dbx_commands) stop_command = add_cmd ("stop", class_obscure, not_just_help_class_command, _("\ @@ -7645,9 +7841,8 @@ Set mode for locking scheduler during execution."), _("\ Show mode for locking scheduler during execution."), _("\ off == no locking (threads may preempt at any time)\n\ on == full locking (no thread except the current thread may run)\n\ -step == scheduler locked during every single-step operation.\n\ - In this mode, no other thread may run during a step command.\n\ - Other threads may run while stepping over a function call ('next')."), +step == scheduler locked during stepping commands (step, next, stepi, nexti).\n\ + In this mode, other threads may run during other commands."), set_schedlock_func, /* traps on target vector */ show_scheduler_mode, &setlist, &showlist);