void _initialize_infrun (void);
-int inferior_ignoring_leading_exec_events = 0;
-
/* 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. */
static ptid_t previous_inferior_ptid;
-/* This is true for configurations that may follow through execl() and
- similar functions. At present this is only true for HP-UX native. */
-
-#ifndef MAY_FOLLOW_EXEC
-#define MAY_FOLLOW_EXEC (0)
-#endif
-
-static int may_follow_exec = MAY_FOLLOW_EXEC;
+int debug_displaced = 0;
+static void
+show_debug_displaced (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("Displace stepping debugging is %s.\n"), value);
+}
static int debug_infrun = 0;
static void
int saved_pid = pid;
struct target_ops *tgt;
- if (!may_follow_exec)
- return;
-
/* 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
momentary bp's, etc.
/* We've followed the inferior through an exec. Therefore, the
inferior has essentially been killed & reborn. */
- /* First collect the run target in effect. */
- tgt = find_run_target ();
- /* If we can't find one, things are in a very strange state... */
- if (tgt == NULL)
- error (_("Could find run target to save before following exec"));
-
gdb_flush (gdb_stdout);
- target_mourn_inferior ();
- inferior_ptid = pid_to_ptid (saved_pid);
+ generic_mourn_inferior ();
/* Because mourn_inferior resets inferior_ptid. */
- push_target (tgt);
+ inferior_ptid = pid_to_ptid (saved_pid);
+
+ if (gdb_sysroot && *gdb_sysroot)
+ {
+ char *name = alloca (strlen (gdb_sysroot)
+ + strlen (execd_pathname)
+ + 1);
+ strcpy (name, gdb_sysroot);
+ strcat (name, execd_pathname);
+ execd_pathname = name;
+ }
/* That a.out is now the one to use. */
exec_file_attach (execd_pathname, 0);
/* Reset the shared library package. This ensures that we get
a shlib event when the child reaches "_start", at which point
the dld will have had a chance to initialize the child. */
-#if defined(SOLIB_RESTART)
- SOLIB_RESTART ();
-#endif
+ no_shared_libraries (NULL, 0);
#ifdef SOLIB_CREATE_INFERIOR_HOOK
SOLIB_CREATE_INFERIOR_HOOK (PIDGET (inferior_ptid));
#else
stepping the thread user has selected. */
static ptid_t deferred_step_ptid;
\f
+/* Displaced stepping. */
+
+/* In non-stop debugging mode, we must take special care to manage
+ breakpoints properly; in particular, the traditional strategy for
+ stepping a thread past a breakpoint it has hit is unsuitable.
+ 'Displaced stepping' is a tactic for stepping one thread past a
+ breakpoint it has hit while ensuring that other threads running
+ concurrently will hit the breakpoint as they should.
+
+ The traditional way to step a thread T off a breakpoint in a
+ multi-threaded program in all-stop mode is as follows:
+
+ a0) Initially, all threads are stopped, and breakpoints are not
+ inserted.
+ a1) We single-step T, leaving breakpoints uninserted.
+ a2) We insert breakpoints, and resume all threads.
+
+ In non-stop debugging, however, this strategy is unsuitable: we
+ don't want to have to stop all threads in the system in order to
+ continue or step T past a breakpoint. Instead, we use displaced
+ stepping:
+
+ n0) Initially, T is stopped, other threads are running, and
+ breakpoints are inserted.
+ n1) We copy the instruction "under" the breakpoint to a separate
+ location, outside the main code stream, making any adjustments
+ to the instruction, register, and memory state as directed by
+ T's architecture.
+ n2) We single-step T over the instruction at its new location.
+ n3) We adjust the resulting register and memory state as directed
+ by T's architecture. This includes resetting T's PC to point
+ back into the main instruction stream.
+ n4) We resume T.
+
+ This approach depends on the following gdbarch methods:
+
+ - gdbarch_max_insn_length and gdbarch_displaced_step_location
+ indicate where to copy the instruction, and how much space must
+ be reserved there. We use these in step n1.
+
+ - gdbarch_displaced_step_copy_insn copies a instruction to a new
+ address, and makes any necessary adjustments to the instruction,
+ register contents, and memory. We use this in step n1.
+
+ - gdbarch_displaced_step_fixup adjusts registers and memory after
+ we have successfuly single-stepped the instruction, to yield the
+ same effect the instruction would have had if we had executed it
+ at its original address. We use this in step n3.
+
+ - gdbarch_displaced_step_free_closure provides cleanup.
+
+ The gdbarch_displaced_step_copy_insn and
+ gdbarch_displaced_step_fixup functions must be written so that
+ copying an instruction with gdbarch_displaced_step_copy_insn,
+ single-stepping across the copied instruction, and then applying
+ gdbarch_displaced_insn_fixup should have the same effects on the
+ thread's memory and registers as stepping the instruction in place
+ would have. Exactly which responsibilities fall to the copy and
+ which fall to the fixup is up to the author of those functions.
+
+ See the comments in gdbarch.sh for details.
+
+ Note that displaced stepping and software single-step cannot
+ currently be used in combination, although with some care I think
+ they could be made to. Software single-step works by placing
+ breakpoints on all possible subsequent instructions; if the
+ displaced instruction is a PC-relative jump, those breakpoints
+ could fall in very strange places --- on pages that aren't
+ executable, or at addresses that are not proper instruction
+ boundaries. (We do generally let other threads run while we wait
+ to hit the software single-step breakpoint, and they might
+ encounter such a corrupted instruction.) One way to work around
+ this would be to have gdbarch_displaced_step_copy_insn fully
+ simulate the effect of PC-relative instructions (and return NULL)
+ on architectures that use software single-stepping.
+
+ In non-stop mode, we can have independent and simultaneous step
+ requests, so more than one thread may need to simultaneously step
+ over a breakpoint. The current implementation assumes there is
+ only one scratch space per process. In this case, we have to
+ serialize access to the scratch space. If thread A wants to step
+ over a breakpoint, but we are currently waiting for some other
+ thread to complete a displaced step, we leave thread A stopped and
+ place it in the displaced_step_request_queue. Whenever a displaced
+ step finishes, we pick the next thread in the queue and start a new
+ displaced step operation on it. See displaced_step_prepare and
+ displaced_step_fixup for details. */
+
+/* If this is not null_ptid, this is the thread carrying out a
+ displaced single-step. This thread's state will require fixing up
+ once it has completed its step. */
+static ptid_t displaced_step_ptid;
+
+struct displaced_step_request
+{
+ ptid_t ptid;
+ struct displaced_step_request *next;
+};
+
+/* A queue of pending displaced stepping requests. */
+struct displaced_step_request *displaced_step_request_queue;
+
+/* The architecture the thread had when we stepped it. */
+static struct gdbarch *displaced_step_gdbarch;
+
+/* The closure provided gdbarch_displaced_step_copy_insn, to be used
+ for post-step cleanup. */
+static struct displaced_step_closure *displaced_step_closure;
+
+/* The address of the original instruction, and the copy we made. */
+static CORE_ADDR displaced_step_original, displaced_step_copy;
+
+/* Saved contents of copy area. */
+static gdb_byte *displaced_step_saved_copy;
+
+/* When this is non-zero, we are allowed to use displaced stepping, if
+ the architecture supports it. When this is zero, we use
+ traditional the hold-and-step approach. */
+int can_use_displaced_stepping = 1;
+static void
+show_can_use_displaced_stepping (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c,
+ const char *value)
+{
+ fprintf_filtered (file, _("\
+Debugger's willingness to use displaced stepping to step over "
+"breakpoints is %s.\n"), value);
+}
+
+/* Return non-zero if displaced stepping is enabled, and can be used
+ with GDBARCH. */
+static int
+use_displaced_stepping (struct gdbarch *gdbarch)
+{
+ return (can_use_displaced_stepping
+ && gdbarch_displaced_step_copy_insn_p (gdbarch));
+}
+
+/* Clean out any stray displaced stepping state. */
+static void
+displaced_step_clear (void)
+{
+ /* Indicate that there is no cleanup pending. */
+ displaced_step_ptid = null_ptid;
+
+ if (displaced_step_closure)
+ {
+ gdbarch_displaced_step_free_closure (displaced_step_gdbarch,
+ displaced_step_closure);
+ displaced_step_closure = NULL;
+ }
+}
+
+static void
+cleanup_displaced_step_closure (void *ptr)
+{
+ struct displaced_step_closure *closure = ptr;
+
+ gdbarch_displaced_step_free_closure (current_gdbarch, closure);
+}
+
+/* Dump LEN bytes at BUF in hex to FILE, followed by a newline. */
+void
+displaced_step_dump_bytes (struct ui_file *file,
+ const gdb_byte *buf,
+ size_t len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ fprintf_unfiltered (file, "%02x ", buf[i]);
+ fputs_unfiltered ("\n", file);
+}
+
+/* Prepare to single-step, using displaced stepping.
+
+ Note that we cannot use displaced stepping when we have a signal to
+ deliver. If we have a signal to deliver and an instruction to step
+ over, then after the step, there will be no indication from the
+ target whether the thread entered a signal handler or ignored the
+ signal and stepped over the instruction successfully --- both cases
+ result in a simple SIGTRAP. In the first case we mustn't do a
+ fixup, and in the second case we must --- but we can't tell which.
+ Comments in the code for 'random signals' in handle_inferior_event
+ explain how we handle this case instead.
+
+ Returns 1 if preparing was successful -- this thread is going to be
+ stepped now; or 0 if displaced stepping this thread got queued. */
+static int
+displaced_step_prepare (ptid_t ptid)
+{
+ struct cleanup *old_cleanups;
+ struct regcache *regcache = get_thread_regcache (ptid);
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ CORE_ADDR original, copy;
+ ULONGEST len;
+ struct displaced_step_closure *closure;
+
+ /* We should never reach this function if the architecture does not
+ support displaced stepping. */
+ gdb_assert (gdbarch_displaced_step_copy_insn_p (gdbarch));
+
+ /* For the first cut, we're displaced stepping one thread at a
+ time. */
+
+ if (!ptid_equal (displaced_step_ptid, null_ptid))
+ {
+ /* Already waiting for a displaced step to finish. Defer this
+ request and place in queue. */
+ struct displaced_step_request *req, *new_req;
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: defering step of %s\n",
+ target_pid_to_str (ptid));
+
+ new_req = xmalloc (sizeof (*new_req));
+ new_req->ptid = ptid;
+ new_req->next = NULL;
+
+ if (displaced_step_request_queue)
+ {
+ for (req = displaced_step_request_queue;
+ req && req->next;
+ req = req->next)
+ ;
+ req->next = new_req;
+ }
+ else
+ displaced_step_request_queue = new_req;
+
+ return 0;
+ }
+ else
+ {
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: stepping %s now\n",
+ target_pid_to_str (ptid));
+ }
+
+ displaced_step_clear ();
+
+ original = read_pc_pid (ptid);
+
+ copy = gdbarch_displaced_step_location (gdbarch);
+ len = gdbarch_max_insn_length (gdbarch);
+
+ /* Save the original contents of the copy area. */
+ displaced_step_saved_copy = xmalloc (len);
+ old_cleanups = make_cleanup (free_current_contents,
+ &displaced_step_saved_copy);
+ read_memory (copy, displaced_step_saved_copy, len);
+ if (debug_displaced)
+ {
+ fprintf_unfiltered (gdb_stdlog, "displaced: saved 0x%s: ",
+ paddr_nz (copy));
+ displaced_step_dump_bytes (gdb_stdlog, displaced_step_saved_copy, len);
+ };
+
+ closure = gdbarch_displaced_step_copy_insn (gdbarch,
+ original, copy, regcache);
+
+ /* We don't support the fully-simulated case at present. */
+ gdb_assert (closure);
+
+ make_cleanup (cleanup_displaced_step_closure, closure);
+
+ /* Resume execution at the copy. */
+ write_pc_pid (copy, ptid);
+
+ discard_cleanups (old_cleanups);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: displaced pc to 0x%s\n",
+ paddr_nz (copy));
+
+ /* Save the information we need to fix things up if the step
+ succeeds. */
+ displaced_step_ptid = ptid;
+ displaced_step_gdbarch = gdbarch;
+ displaced_step_closure = closure;
+ displaced_step_original = original;
+ displaced_step_copy = copy;
+ return 1;
+}
+
+static void
+displaced_step_clear_cleanup (void *ignore)
+{
+ displaced_step_clear ();
+}
+
+static void
+write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
+{
+ struct cleanup *ptid_cleanup = save_inferior_ptid ();
+ inferior_ptid = ptid;
+ write_memory (memaddr, myaddr, len);
+ do_cleanups (ptid_cleanup);
+}
+
+static void
+displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
+{
+ struct cleanup *old_cleanups;
+
+ /* Was this event for the pid we displaced? */
+ if (ptid_equal (displaced_step_ptid, null_ptid)
+ || ! ptid_equal (displaced_step_ptid, event_ptid))
+ return;
+
+ old_cleanups = make_cleanup (displaced_step_clear_cleanup, 0);
+
+ /* Restore the contents of the copy area. */
+ {
+ ULONGEST len = gdbarch_max_insn_length (displaced_step_gdbarch);
+ write_memory_ptid (displaced_step_ptid, displaced_step_copy,
+ displaced_step_saved_copy, len);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: restored 0x%s\n",
+ paddr_nz (displaced_step_copy));
+ }
+
+ /* Did the instruction complete successfully? */
+ if (signal == TARGET_SIGNAL_TRAP)
+ {
+ /* Fix up the resulting state. */
+ gdbarch_displaced_step_fixup (displaced_step_gdbarch,
+ displaced_step_closure,
+ displaced_step_original,
+ displaced_step_copy,
+ get_thread_regcache (displaced_step_ptid));
+ }
+ else
+ {
+ /* Since the instruction didn't complete, all we can do is
+ relocate the PC. */
+ CORE_ADDR pc = read_pc_pid (event_ptid);
+ pc = displaced_step_original + (pc - displaced_step_copy);
+ write_pc_pid (pc, event_ptid);
+ }
+
+ do_cleanups (old_cleanups);
+
+ /* Are there any pending displaced stepping requests? If so, run
+ one now. */
+ if (displaced_step_request_queue)
+ {
+ struct displaced_step_request *head;
+ ptid_t ptid;
+
+ head = displaced_step_request_queue;
+ ptid = head->ptid;
+ displaced_step_request_queue = head->next;
+ xfree (head);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: stepping queued %s now\n",
+ target_pid_to_str (ptid));
+
+
+ displaced_step_ptid = null_ptid;
+ displaced_step_prepare (ptid);
+ target_resume (ptid, 1, TARGET_SIGNAL_0);
+ }
+}
+
+\f
+/* Resuming. */
/* Things to clean up if we QUIT out of resume (). */
static void
{
int should_resume = 1;
struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0);
+ CORE_ADDR pc = read_pc ();
QUIT;
if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: resume (step=%d, signal=%d)\n",
- step, sig);
-
- /* FIXME: calling breakpoint_here_p (read_pc ()) three times! */
-
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: resume (step=%d, signal=%d), "
+ "stepping_over_breakpoint=%d\n",
+ step, sig, stepping_over_breakpoint);
/* Some targets (e.g. Solaris x86) have a kernel bug when stepping
over an instruction that causes a page fault without triggering
removed or inserted, as appropriate. The exception is if we're sitting
at a permanent breakpoint; we need to step over it, but permanent
breakpoints can't be removed. So we have to test for it here. */
- if (breakpoint_here_p (read_pc ()) == permanent_breakpoint_here)
+ if (breakpoint_here_p (pc) == permanent_breakpoint_here)
{
if (gdbarch_skip_permanent_breakpoint_p (current_gdbarch))
gdbarch_skip_permanent_breakpoint (current_gdbarch,
a command like `return' or `jump' to continue execution."));
}
+ /* If enabled, step over breakpoints by executing a copy of the
+ instruction at a different address.
+
+ We can't use displaced stepping when we have a signal to deliver;
+ the comments for displaced_step_prepare explain why. The
+ comments in the handle_inferior event for dealing with 'random
+ signals' explain what we do instead. */
+ if (use_displaced_stepping (current_gdbarch)
+ && stepping_over_breakpoint
+ && sig == TARGET_SIGNAL_0)
+ {
+ if (!displaced_step_prepare (inferior_ptid))
+ /* Got placed in displaced stepping queue. Will be resumed
+ later when all the currently queued displaced stepping
+ requests finish. */
+ return;
+ }
+
if (step && gdbarch_software_single_step_p (current_gdbarch))
{
/* Do it the hard way, w/temp breakpoints */
`wait_for_inferior' */
singlestep_breakpoints_inserted_p = 1;
singlestep_ptid = inferior_ptid;
- singlestep_pc = read_pc ();
+ singlestep_pc = pc;
}
}
}
if ((step || singlestep_breakpoints_inserted_p)
- && breakpoint_here_p (read_pc ())
- && !breakpoint_inserted_here_p (read_pc ()))
+ && stepping_over_breakpoint)
{
- /* We're stepping, have breakpoint at PC, and it's
- not inserted. Most likely, proceed has noticed that
- we have breakpoint and tries to single-step over it,
- so that it's not hit. In which case, we need to
- single-step only this thread, and keep others stopped,
- as they can miss this breakpoint if allowed to run.
-
- The current code either has all breakpoints inserted,
- or all removed, so if we let other threads run,
- we can actually miss any breakpoint, not the one at PC. */
+ /* We're allowing a thread to run past a breakpoint it has
+ hit, by single-stepping the thread with the breakpoint
+ removed. In which case, we need to single-step only this
+ thread, and keep others stopped, as they can miss this
+ breakpoint if allowed to run.
+
+ The current code actually removes all breakpoints when
+ doing this, not just the one being stepped over, so if we
+ let other threads run, we can actually miss any
+ breakpoint, not just the one at PC. */
resume_ptid = inferior_ptid;
}
/* 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 (read_pc ()))
+ if (step && breakpoint_inserted_here_p (pc))
step = 0;
}
+
+ if (debug_displaced
+ && use_displaced_stepping (current_gdbarch)
+ && stepping_over_breakpoint)
+ {
+ CORE_ADDR actual_pc = read_pc_pid (resume_ptid);
+ gdb_byte buf[4];
+
+ fprintf_unfiltered (gdb_stdlog, "displaced: run 0x%s: ",
+ paddr_nz (actual_pc));
+ read_memory (actual_pc, buf, sizeof (buf));
+ displaced_step_dump_bytes (gdb_stdlog, buf, sizeof (buf));
+ }
+
target_resume (resume_ptid, step, sig);
}
discard_cleanups (old_cleanups);
}
\f
+/* Proceeding. */
/* Clear out all variables saying what to do when inferior is continued.
First do this, then set the ones you want, then call `proceed'. */
oneproc = 1;
if (oneproc)
- /* We will get a trace trap after one instruction.
- Continue it automatically and insert breakpoints then. */
- stepping_over_breakpoint = 1;
- else
+ {
+ stepping_over_breakpoint = 1;
+ /* If displaced stepping is enabled, we can step over the
+ breakpoint without hitting it, so leave all breakpoints
+ inserted. Otherwise we need to disable all breakpoints, step
+ one instruction, and then re-add them when that step is
+ finished. */
+ if (!use_displaced_stepping (current_gdbarch))
+ remove_breakpoints ();
+ }
+
+ /* We can insert breakpoints if we're not trying to step over one,
+ or if we are stepping over one but we're using displaced stepping
+ to do so. */
+ if (! stepping_over_breakpoint || use_displaced_stepping (current_gdbarch))
insert_breakpoints ();
if (siggnal != TARGET_SIGNAL_DEFAULT)
deferred_step_ptid = null_ptid;
target_last_wait_ptid = minus_one_ptid;
+
+ displaced_step_clear ();
}
+
\f
/* 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
if (!async_ecs->wait_some_more)
{
- old_cleanups = make_exec_cleanup (delete_step_resume_breakpoint,
- &step_resume_breakpoint);
-
/* Fill in with reasonable starting values. */
init_execution_control_state (async_ecs);
if (!async_ecs->wait_some_more)
{
- /* Do only the cleanups that have been added by this
- function. Let the continuations for the commands do the rest,
- if there are any. */
- do_exec_cleanups (old_cleanups);
+ delete_step_resume_breakpoint (&step_resume_breakpoint);
+
normal_stop ();
if (step_multi && stop_step)
inferior_event_handler (INF_EXEC_CONTINUE, NULL);
established. */
if (stop_soon == NO_STOP_QUIETLY)
{
- /* Remove breakpoints, SOLIB_ADD might adjust
- breakpoint addresses via breakpoint_re_set. */
- remove_breakpoints ();
-
/* Check for any newly added shared libraries if we're
supposed to be adding them automatically. Switch
terminal for any messages produced by
/* NOTE drow/2007-05-11: This might be a good place to check
for "catch load". */
-
- /* Reinsert breakpoints and continue. */
- insert_breakpoints ();
}
/* If we are skipping through a shell, or through shared library
we're attaching or setting up a remote connection. */
if (stop_soon == STOP_QUIETLY || stop_soon == NO_STOP_QUIETLY)
{
+ /* Loading of shared libraries might have changed breakpoint
+ addresses. Make sure new breakpoints are inserted. */
+ if (!breakpoints_always_inserted_mode ())
+ insert_breakpoints ();
resume (0, TARGET_SIGNAL_0);
prepare_to_wait (ecs);
return;
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_EXECD\n");
stop_signal = TARGET_SIGNAL_TRAP;
- /* NOTE drow/2002-12-05: This code should be pushed down into the
- target_wait function. Until then following vfork on HP/UX 10.20
- is probably broken by this. Of course, it's broken anyway. */
- /* Is this a target which reports multiple exec events per actual
- call to exec()? (HP-UX using ptrace does, for example.) If so,
- ignore all but the last one. Just resume the exec'r, and wait
- for the next exec event. */
- if (inferior_ignoring_leading_exec_events)
- {
- inferior_ignoring_leading_exec_events--;
- target_resume (ecs->ptid, 0, TARGET_SIGNAL_0);
- prepare_to_wait (ecs);
- return;
- }
- inferior_ignoring_leading_exec_events =
- target_reported_exec_events_per_exec_call () - 1;
-
pending_follow.execd_pathname =
savestring (ecs->ws.value.execd_pathname,
strlen (ecs->ws.value.execd_pathname));
return;
}
+ /* Do we need to clean up the state of a thread that has completed a
+ displaced single-step? (Doing so usually affects the PC, so do
+ it here, before we set stop_pc.) */
+ displaced_step_fixup (ecs->ptid, stop_signal);
+
stop_pc = read_pc_pid (ecs->ptid);
if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: stop_pc = 0x%s\n", paddr_nz (stop_pc));
+ {
+ fprintf_unfiltered (gdb_stdlog, "infrun: stop_pc = 0x%s\n",
+ paddr_nz (stop_pc));
+ if (STOPPED_BY_WATCHPOINT (&ecs->ws))
+ {
+ CORE_ADDR addr;
+ fprintf_unfiltered (gdb_stdlog, "infrun: stopped by watchpoint\n");
+
+ if (target_stopped_data_address (¤t_target, &addr))
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stopped data address = 0x%s\n",
+ paddr_nz (addr));
+ else
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: (no data address available)\n");
+ }
+ }
if (stepping_past_singlestep_breakpoint)
{
if (thread_hop_needed)
{
- int remove_status;
+ int remove_status = 0;
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: thread_hop_needed\n");
singlestep_breakpoints_inserted_p = 0;
}
- remove_status = remove_breakpoints ();
+ /* If the arch can displace step, don't remove the
+ breakpoints. */
+ if (!use_displaced_stepping (current_gdbarch))
+ remove_status = remove_breakpoints ();
+
/* Did we fail to remove breakpoints? If so, try
to set the PC past the bp. (There's at least
one situation in which we can fail to remove
&& (HAVE_STEPPABLE_WATCHPOINT
|| gdbarch_have_nonsteppable_watchpoint (current_gdbarch)))
{
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: STOPPED_BY_WATCHPOINT\n");
-
/* At this point, we are stopped at an instruction which has
attempted to write to a piece of memory under control of
a watchpoint. The instruction hasn't actually executed
when we're trying to execute a breakpoint instruction on a
non-executable stack. This happens for call dummy breakpoints
for architectures like SPARC that place call dummies on the
- stack. */
+ stack.
+ If we're doing a displaced step past a breakpoint, then the
+ breakpoint is always inserted at the original instruction;
+ non-standard signals can't be explained by the breakpoint. */
if (stop_signal == TARGET_SIGNAL_TRAP
- || (breakpoint_inserted_here_p (stop_pc)
+ || (! stepping_over_breakpoint
+ && breakpoint_inserted_here_p (stop_pc)
&& (stop_signal == TARGET_SIGNAL_ILL
|| stop_signal == TARGET_SIGNAL_SEGV
|| stop_signal == TARGET_SIGNAL_EMT))
/* This originates from attach_command(). We need to overwrite
the stop_signal here, because some kernels don't ignore a
- SIGSTOP in a subsequent ptrace(PTRACE_SONT,SOGSTOP) call.
- See more comments in inferior.h. */
- if (stop_soon == STOP_QUIETLY_NO_SIGSTOP)
+ SIGSTOP in a subsequent ptrace(PTRACE_CONT,SIGSTOP) call.
+ See more comments in inferior.h. On the other hand, if we
+ get a non-SIGSTOP, report it to the user - assume the backend
+ will handle the SIGSTOP if it should show up later. */
+ if (stop_soon == STOP_QUIETLY_NO_SIGSTOP
+ && stop_signal == TARGET_SIGNAL_STOP)
{
stop_stepping (ecs);
- if (stop_signal == TARGET_SIGNAL_STOP)
- stop_signal = TARGET_SIGNAL_0;
+ stop_signal = TARGET_SIGNAL_0;
return;
}
target_terminal_ours_for_output ();
print_stop_reason (SIGNAL_RECEIVED, stop_signal);
}
- if (signal_stop[stop_signal])
+ if (signal_stop_state (stop_signal))
{
stop_stepping (ecs);
return;
stop_signal = TARGET_SIGNAL_0;
if (prev_pc == read_pc ()
- && breakpoint_here_p (read_pc ())
- && !breakpoint_inserted_here_p (read_pc ())
+ && stepping_over_breakpoint
&& step_resume_breakpoint == NULL)
{
/* We were just starting a new sequence, attempting to
single-step off of a breakpoint and expecting a SIGTRAP.
- Intead this signal arrives. This signal will take us out
+ Instead this signal arrives. This signal will take us out
of the stepping range so GDB needs to remember to, when
the signal handler returns, resume stepping off that
breakpoint. */
code paths as single-step - set a breakpoint at the
signal return address and then, once hit, step off that
breakpoint. */
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: signal arrived while stepping over "
+ "breakpoint\n");
insert_step_resume_breakpoint_at_frame (get_current_frame ());
ecs->step_after_step_resume_breakpoint = 1;
Note that this is only needed for a signal delivered
while in the single-step range. Nested signals aren't a
problem as they eventually all return. */
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: signal may take us out of "
+ "single-step range\n");
+
insert_step_resume_breakpoint_at_frame (get_current_frame ());
keep_going (ecs);
return;
return;
case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME:
- case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CLEAR_LONGJMP_RESUME\n");
disable_longjmp_breakpoint ();
ecs->handling_longjmp = 0; /* FIXME */
- if (what.main_action == BPSTAT_WHAT_CLEAR_LONGJMP_RESUME)
- break;
- /* else fallthrough */
+ break;
case BPSTAT_WHAT_SINGLE:
if (debug_infrun)
{
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CHECK_SHLIBS\n");
- /* Remove breakpoints, we eventually want to step over the
- shlib event breakpoint, and SOLIB_ADD might adjust
- breakpoint addresses via breakpoint_re_set. */
- remove_breakpoints ();
/* Check for any newly added shared libraries if we're
supposed to be adding them automatically. Switch
if (ecs->stepping_over_breakpoint)
{
- remove_breakpoints ();
+ if (! use_displaced_stepping (current_gdbarch))
+ /* Since we can't do a displaced step, we have to remove
+ the breakpoint while we step it. To keep things
+ simple, we remove them all. */
+ remove_breakpoints ();
}
else
{
gdbarch_decr_pc_after_break needs to just go away. */
deprecated_update_frame_pc_hack (get_current_frame (), read_pc ());
- if (target_has_execution)
+ if (!breakpoints_always_inserted_mode () && target_has_execution)
{
if (remove_breakpoints ())
{
}
}
- /* Delete the breakpoint we stopped at, if it wants to be deleted.
- Delete any breakpoint that is to be deleted at the next stop. */
-
- breakpoint_auto_delete (stop_bpstat);
-
/* If an auto-display called a function and that got a signal,
delete that auto-display to avoid an infinite recursion. */
bpstat_print() contains the logic deciding in detail
what to print, based on the event(s) that just occurred. */
- if (stop_print_frame)
+ /* If --batch-silent is enabled then there's no need to print the current
+ source location, and to try risks causing an error message about
+ missing source files. */
+ if (stop_print_frame && !batch_silent)
{
int bpstat_ret;
int source_flag;
done:
annotate_stopped ();
observer_notify_normal_stop (stop_bpstat);
+ /* Delete the breakpoint we stopped at, if it wants to be deleted.
+ Delete any breakpoint that is to be deleted at the next stop. */
+ breakpoint_auto_delete (stop_bpstat);
}
static int
int
signal_stop_state (int signo)
{
- return signal_stop[signo];
+ /* Always stop on signals if we're just gaining control of the
+ program. */
+ return signal_stop[signo] || stop_soon != NO_STOP_QUIETLY;
}
int
show_debug_infrun,
&setdebuglist, &showdebuglist);
+ add_setshow_boolean_cmd ("displaced", class_maintenance, &debug_displaced, _("\
+Set displaced stepping debugging."), _("\
+Show displaced stepping debugging."), _("\
+When non-zero, displaced stepping specific debugging is enabled."),
+ NULL,
+ show_debug_displaced,
+ &setdebuglist, &showdebuglist);
+
numsigs = (int) TARGET_SIGNAL_LAST;
signal_stop = (unsigned char *) xmalloc (sizeof (signal_stop[0]) * numsigs);
signal_print = (unsigned char *)
show_step_stop_if_no_debug,
&setlist, &showlist);
+ add_setshow_boolean_cmd ("can-use-displaced-stepping", class_maintenance,
+ &can_use_displaced_stepping, _("\
+Set debugger's willingness to use displaced stepping."), _("\
+Show debugger's willingness to use displaced stepping."), _("\
+If zero, gdb will not use to use displaced stepping to step over\n\
+breakpoints, even if such is supported by the target."),
+ NULL,
+ show_can_use_displaced_stepping,
+ &maintenance_set_cmdlist,
+ &maintenance_show_cmdlist);
+
+
/* ptid initializations */
null_ptid = ptid_build (0, 0, 0);
minus_one_ptid = ptid_build (-1, 0, 0);
inferior_ptid = null_ptid;
target_last_wait_ptid = minus_one_ptid;
+ displaced_step_ptid = null_ptid;
}