/* Target-struct-independent code to start (run) and stop an inferior
process.
- Copyright (C) 1986-2022 Free Software Foundation, Inc.
+ Copyright (C) 1986-2023 Free Software Foundation, Inc.
This file is part of GDB.
#include "annotate.h"
#include "symfile.h"
#include "top.h"
+#include "ui.h"
#include "inf-loop.h"
#include "regcache.h"
#include "value.h"
#include "test-target.h"
#include "gdbsupport/common-debug.h"
#include "gdbsupport/buildargv.h"
+#include "extension.h"
+#include "disasm.h"
+#include "interps.h"
/* Prototypes for local functions */
static bool currently_stepping (struct thread_info *tp);
-static void insert_hp_step_resume_breakpoint_at_frame (struct frame_info *);
+static void insert_hp_step_resume_breakpoint_at_frame (frame_info_ptr);
-static void insert_step_resume_breakpoint_at_caller (struct frame_info *);
+static void insert_step_resume_breakpoint_at_caller (frame_info_ptr);
static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR);
static void wait_for_inferior (inferior *inf);
+static void restart_threads (struct thread_info *event_thread,
+ inferior *inf = nullptr);
+
+static bool start_step_over (void);
+
+static bool step_over_info_valid_p (void);
+
/* Asynchronous signal handler registered as event loop source for
when we have pending events ready to be passed to the core. */
static struct async_event_handler *infrun_async_inferior_event_token;
show_step_stop_if_no_debug (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
- fprintf_filtered (file, _("Mode of the step operation is %s.\n"), value);
+ gdb_printf (file, _("Mode of the step operation is %s.\n"), value);
}
/* proceed and normal_stop use this to notify the user when the
- inferior stopped in a different thread than it had been running
- in. */
+ inferior stopped in a different thread than it had been running in.
+ It can also be used to find for which thread normal_stop last
+ reported a stop. */
+static thread_info_ref previous_thread;
+
+/* See infrun.h. */
+
+void
+update_previous_thread ()
+{
+ if (inferior_ptid == null_ptid)
+ previous_thread = nullptr;
+ else
+ previous_thread = thread_info_ref::new_reference (inferior_thread ());
+}
-static ptid_t previous_inferior_ptid;
+/* See infrun.h. */
+
+thread_info *
+get_previous_thread ()
+{
+ return previous_thread.get ();
+}
/* If set (default for legacy reasons), when following a fork, GDB
will detach from one of the fork branches, child or parent.
show_debug_infrun (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
- fprintf_filtered (file, _("Inferior debugging is %s.\n"), value);
+ gdb_printf (file, _("Inferior debugging is %s.\n"), value);
}
/* Support for disabling address space randomization. */
struct cmd_list_element *c, const char *value)
{
if (target_supports_disable_randomization ())
- fprintf_filtered (file,
- _("Disabling randomization of debuggee's "
- "virtual address space is %s.\n"),
- value);
+ gdb_printf (file,
+ _("Disabling randomization of debuggee's "
+ "virtual address space is %s.\n"),
+ value);
else
- fputs_filtered (_("Disabling randomization of debuggee's "
- "virtual address space is unsupported on\n"
- "this platform.\n"), file);
+ gdb_puts (_("Disabling randomization of debuggee's "
+ "virtual address space is unsupported on\n"
+ "this platform.\n"), file);
}
static void
show_non_stop (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
- fprintf_filtered (file,
- _("Controlling the inferior in non-stop mode is %s.\n"),
- value);
+ gdb_printf (file,
+ _("Controlling the inferior in non-stop mode is %s.\n"),
+ value);
}
/* "Observer mode" is somewhat like a more extreme version of
going out we leave it that way. */
if (observer_mode)
{
- pagination_enabled = 0;
+ pagination_enabled = false;
non_stop = non_stop_1 = true;
}
if (from_tty)
- printf_filtered (_("Observer mode is now %s.\n"),
- (observer_mode ? "on" : "off"));
+ gdb_printf (_("Observer mode is now %s.\n"),
+ (observer_mode ? "on" : "off"));
}
static void
show_observer_mode (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
- fprintf_filtered (file, _("Observer mode is %s.\n"), value);
+ gdb_printf (file, _("Observer mode is %s.\n"), value);
}
/* This updates the value of observer mode based on changes in
/* Let the user know if things change. */
if (newval != observer_mode)
- printf_filtered (_("Observer mode is now %s.\n"),
- (newval ? "on" : "off"));
+ gdb_printf (_("Observer mode is now %s.\n"),
+ (newval ? "on" : "off"));
observer_mode = observer_mode_1 = newval;
}
show_stop_on_solib_events (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
- fprintf_filtered (file, _("Stopping for shared library events is %s.\n"),
- value);
+ gdb_printf (file, _("Stopping for shared library events is %s.\n"),
+ value);
}
/* True after stop if current stack frame should be printed. */
static bool stop_print_frame;
/* This is a cached copy of the target/ptid/waitstatus of the last
- event returned by target_wait()/deprecated_target_wait_hook().
+ event returned by target_wait().
This information is returned by get_last_target_status(). */
static process_stratum_target *target_last_proc_target;
static ptid_t target_last_wait_ptid;
static const char *const follow_fork_mode_kind_names[] = {
follow_fork_mode_child,
follow_fork_mode_parent,
- NULL
+ nullptr
};
static const char *follow_fork_mode_string = follow_fork_mode_parent;
show_follow_fork_mode_string (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
- fprintf_filtered (file,
- _("Debugger response to a program "
- "call of fork or vfork is \"%s\".\n"),
- value);
+ gdb_printf (file,
+ _("Debugger response to a program "
+ "call of fork or vfork is \"%s\".\n"),
+ value);
}
\f
the parent stays blocked. If we're telling the parent to run
in the foreground, the user will not be able to ctrl-c to get
back the terminal, effectively hanging the debug session. */
- fprintf_filtered (gdb_stderr, _("\
+ gdb_printf (gdb_stderr, _("\
Can not resume the parent process over vfork in the foreground while\n\
holding the child stopped. Try \"set detach-on-fork\" or \
\"set schedule-multiple\".\n"));
inferior *parent_inf = current_inferior ();
inferior *child_inf = nullptr;
+ gdb_assert (parent_inf->thread_waiting_for_vfork_done == nullptr);
+
if (!follow_child)
{
/* Detach new forked process? */
ptid_t process_ptid = ptid_t (child_ptid.pid ());
target_terminal::ours_for_output ();
- printf_filtered (_("[Detaching after %s from child %s]\n"),
- has_vforked ? "vfork" : "fork",
- target_pid_to_str (process_ptid).c_str ());
+ gdb_printf (_("[Detaching after %s from child %s]\n"),
+ has_vforked ? "vfork" : "fork",
+ target_pid_to_str (process_ptid).c_str ());
}
}
else
child_inf->attach_flag = parent_inf->attach_flag;
copy_terminal_info (child_inf, parent_inf);
child_inf->gdbarch = parent_inf->gdbarch;
- copy_inferior_target_desc_info (child_inf, parent_inf);
+ child_inf->tdesc_info = parent_inf->tdesc_info;
child_inf->symfile_flags = SYMFILE_NO_READ;
with the shared region. Keep track of the
parent. */
child_inf->vfork_parent = parent_inf;
- child_inf->pending_detach = 0;
+ child_inf->pending_detach = false;
parent_inf->vfork_child = child_inf;
- parent_inf->pending_detach = 0;
+ parent_inf->pending_detach = false;
}
else
{
- child_inf->aspace = new_address_space ();
+ child_inf->aspace = new address_space ();
child_inf->pspace = new program_space (child_inf->aspace);
- child_inf->removable = 1;
+ child_inf->removable = true;
clone_program_space (child_inf->pspace, parent_inf->pspace);
}
}
insert breakpoints, so that we can debug it. A
subsequent child exec or exit is enough to know when does
the child stops using the parent's address space. */
- parent_inf->waiting_for_vfork_done = detach_fork;
+ parent_inf->thread_waiting_for_vfork_done
+ = detach_fork ? inferior_thread () : nullptr;
parent_inf->pspace->breakpoints_not_allowed = detach_fork;
}
}
std::string child_pid = target_pid_to_str (child_ptid);
target_terminal::ours_for_output ();
- printf_filtered (_("[Attaching after %s %s to child %s]\n"),
- parent_pid.c_str (),
- has_vforked ? "vfork" : "fork",
- child_pid.c_str ());
+ gdb_printf (_("[Attaching after %s %s to child %s]\n"),
+ parent_pid.c_str (),
+ has_vforked ? "vfork" : "fork",
+ child_pid.c_str ());
}
/* Add the new inferior first, so that the target_detach below
child_inf->attach_flag = parent_inf->attach_flag;
copy_terminal_info (child_inf, parent_inf);
child_inf->gdbarch = parent_inf->gdbarch;
- copy_inferior_target_desc_info (child_inf, parent_inf);
+ child_inf->tdesc_info = parent_inf->tdesc_info;
if (has_vforked)
{
child_inf->aspace = parent_inf->aspace;
child_inf->pspace = parent_inf->pspace;
- parent_inf->aspace = new_address_space ();
+ parent_inf->aspace = new address_space ();
parent_inf->pspace = new program_space (parent_inf->aspace);
clone_program_space (parent_inf->pspace, child_inf->pspace);
}
else
{
- child_inf->aspace = new_address_space ();
+ child_inf->aspace = new address_space ();
child_inf->pspace = new program_space (child_inf->aspace);
- child_inf->removable = 1;
+ child_inf->removable = true;
child_inf->symfile_flags = SYMFILE_NO_READ;
clone_program_space (child_inf->pspace, parent_inf->pspace);
}
target_follow_fork (child_inf, child_ptid, fork_kind, follow_child,
detach_fork);
+ gdb::observers::inferior_forked.notify (parent_inf, child_inf, fork_kind);
+
/* target_follow_fork must leave the parent as the current inferior. If we
want to follow the child, we make it the current one below. */
gdb_assert (current_inferior () == parent_inf);
The former case will have pending_follow cleared, the later will have
pending_follow set. */
- thread_info *parent_thread = find_thread_ptid (parent_inf, parent_ptid);
+ thread_info *parent_thread = parent_inf->find_thread (parent_ptid);
gdb_assert (parent_thread != nullptr);
parent_thread->pending_follow.set_spurious ();
if (has_vforked)
{
- gdb_assert (child_inf->vfork_parent == NULL);
- gdb_assert (parent_inf->vfork_child == NULL);
+ gdb_assert (child_inf->vfork_parent == nullptr);
+ gdb_assert (parent_inf->vfork_child == nullptr);
child_inf->vfork_parent = parent_inf;
- child_inf->pending_detach = 0;
+ child_inf->pending_detach = false;
parent_inf->vfork_child = child_inf;
parent_inf->pending_detach = detach_fork;
- parent_inf->waiting_for_vfork_done = 0;
}
else if (detach_fork)
{
ptid_t process_ptid = ptid_t (parent_ptid.pid ());
target_terminal::ours_for_output ();
- printf_filtered (_("[Detaching after fork from "
- "parent %s]\n"),
- target_pid_to_str (process_ptid).c_str ());
+ gdb_printf (_("[Detaching after fork from "
+ "parent %s]\n"),
+ target_pid_to_str (process_ptid).c_str ());
}
target_detach (parent_inf, 0);
if (child_inf != nullptr)
{
/* If FOLLOW_CHILD, we leave CHILD_INF as the current inferior
- (do not restore the parent as the current inferior). */
+ (do not restore the parent as the current inferior). */
gdb::optional<scoped_restore_current_thread> maybe_restore;
if (!follow_child)
return false;
}
+/* Set the last target status as TP having stopped. */
+
+static void
+set_last_target_status_stopped (thread_info *tp)
+{
+ set_last_target_status (tp->inf->process_target (), tp->ptid,
+ target_waitstatus {}.set_stopped (GDB_SIGNAL_0));
+}
+
/* Tell the target to follow the fork we're stopped at. Returns true
if the inferior should be resumed; false, if the target for some
reason decided it's best not to resume. */
followed fork child thread should have a copy of most of the
parent thread structure's run control related fields, not just these.
Initialized to avoid "may be used uninitialized" warnings from gcc. */
- struct breakpoint *step_resume_breakpoint = NULL;
- struct breakpoint *exception_resume_breakpoint = NULL;
+ struct breakpoint *step_resume_breakpoint = nullptr;
+ struct breakpoint *exception_resume_breakpoint = nullptr;
CORE_ADDR step_range_start = 0;
CORE_ADDR step_range_end = 0;
int current_line = 0;
- symtab *current_symtab = NULL;
+ symtab *current_symtab = nullptr;
struct frame_id step_frame_id = { 0 };
if (!non_stop)
{
- process_stratum_target *wait_target;
- ptid_t wait_ptid;
- struct target_waitstatus wait_status;
-
- /* Get the last target status returned by target_wait(). */
- get_last_target_status (&wait_target, &wait_ptid, &wait_status);
+ thread_info *cur_thr = inferior_thread ();
- /* If not stopped at a fork event, then there's nothing else to
- do. */
- if (wait_status.kind () != TARGET_WAITKIND_FORKED
- && wait_status.kind () != TARGET_WAITKIND_VFORKED)
- return 1;
+ ptid_t resume_ptid
+ = user_visible_resume_ptid (cur_thr->control.stepping_command);
+ process_stratum_target *resume_target
+ = user_visible_resume_target (resume_ptid);
- /* Check if we switched over from WAIT_PTID, since the event was
- reported. */
- if (wait_ptid != minus_one_ptid
- && (current_inferior ()->process_target () != wait_target
- || inferior_ptid != wait_ptid))
+ /* Check if there's a thread that we're about to resume, other
+ than the current, with an unfollowed fork/vfork. If so,
+ switch back to it, to tell the target to follow it (in either
+ direction). We'll afterwards refuse to resume, and inform
+ the user what happened. */
+ for (thread_info *tp : all_non_exited_threads (resume_target,
+ resume_ptid))
{
- /* We did. Switch back to WAIT_PTID thread, to tell the
- target to follow it (in either direction). We'll
- afterwards refuse to resume, and inform the user what
- happened. */
- thread_info *wait_thread = find_thread_ptid (wait_target, wait_ptid);
- switch_to_thread (wait_thread);
- should_resume = false;
+ if (tp == cur_thr)
+ continue;
+
+ /* follow_fork_inferior clears tp->pending_follow, and below
+ we'll need the value after the follow_fork_inferior
+ call. */
+ target_waitkind kind = tp->pending_follow.kind ();
+
+ if (kind != TARGET_WAITKIND_SPURIOUS)
+ {
+ infrun_debug_printf ("need to follow-fork [%s] first",
+ tp->ptid.to_string ().c_str ());
+
+ switch_to_thread (tp);
+
+ /* Set up inferior(s) as specified by the caller, and
+ tell the target to do whatever is necessary to follow
+ either parent or child. */
+ if (follow_child)
+ {
+ /* The thread that started the execution command
+ won't exist in the child. Abort the command and
+ immediately stop in this thread, in the child,
+ inside fork. */
+ should_resume = false;
+ }
+ else
+ {
+ /* Following the parent, so let the thread fork its
+ child freely, it won't influence the current
+ execution command. */
+ if (follow_fork_inferior (follow_child, detach_fork))
+ {
+ /* Target refused to follow, or there's some
+ other reason we shouldn't resume. */
+ switch_to_thread (cur_thr);
+ set_last_target_status_stopped (cur_thr);
+ return false;
+ }
+
+ /* If we're following a vfork, when we need to leave
+ the just-forked thread as selected, as we need to
+ solo-resume it to collect the VFORK_DONE event.
+ If we're following a fork, however, switch back
+ to the original thread that we continue stepping
+ it, etc. */
+ if (kind != TARGET_WAITKIND_VFORKED)
+ {
+ gdb_assert (kind == TARGET_WAITKIND_FORKED);
+ switch_to_thread (cur_thr);
+ }
+ }
+
+ break;
+ }
}
}
parent = inferior_ptid;
child = tp->pending_follow.child_ptid ();
+ /* If handling a vfork, stop all the inferior's threads, they will be
+ restarted when the vfork shared region is complete. */
+ if (tp->pending_follow.kind () == TARGET_WAITKIND_VFORKED
+ && target_is_non_stop_p ())
+ stop_all_threads ("handling vfork", tp->inf);
+
process_stratum_target *parent_targ = tp->inf->process_target ();
/* Set up inferior(s) as specified by the caller, and tell the
target to do whatever is necessary to follow either parent
}
else
{
- /* This makes sure we don't try to apply the "Switched
- over from WAIT_PID" logic above. */
- nullify_last_target_wait_ptid ();
-
/* If we followed the child, switch to it... */
if (follow_child)
{
- thread_info *child_thr = find_thread_ptid (parent_targ, child);
- switch_to_thread (child_thr);
+ tp = parent_targ->find_thread (child);
+ switch_to_thread (tp);
/* ... and preserve the stepping state, in case the
user was stepping over the fork call. */
if (should_resume)
{
- tp = inferior_thread ();
tp->control.step_resume_breakpoint
= step_resume_breakpoint;
tp->control.step_range_start = step_range_start;
/* Nothing to follow. */
break;
default:
- internal_error (__FILE__, __LINE__,
- "Unexpected pending_follow.kind %d\n",
+ internal_error ("Unexpected pending_follow.kind %d\n",
tp->pending_follow.kind ());
break;
}
+ if (!should_resume)
+ set_last_target_status_stopped (tp);
return should_resume;
}
if (tp->control.step_resume_breakpoint)
{
breakpoint_re_set_thread (tp->control.step_resume_breakpoint);
- tp->control.step_resume_breakpoint->loc->enabled = 1;
+ tp->control.step_resume_breakpoint->first_loc ().enabled = 1;
}
/* Treat exception_resume breakpoints like step_resume breakpoints. */
if (tp->control.exception_resume_breakpoint)
{
breakpoint_re_set_thread (tp->control.exception_resume_breakpoint);
- tp->control.exception_resume_breakpoint->loc->enabled = 1;
+ tp->control.exception_resume_breakpoint->first_loc ().enabled = 1;
}
/* Reinsert all breakpoints in the child. The user may have set
/* This exec or exit marks the end of the shared memory region
between the parent and the child. Break the bonds. */
inferior *vfork_parent = inf->vfork_parent;
- inf->vfork_parent->vfork_child = NULL;
- inf->vfork_parent = NULL;
+ inf->vfork_parent->vfork_child = nullptr;
+ inf->vfork_parent = nullptr;
/* If the user wanted to detach from the parent, now is the
time. */
/* follow-fork child, detach-on-fork on. */
- vfork_parent->pending_detach = 0;
+ vfork_parent->pending_detach = false;
scoped_restore_current_pspace_and_thread restore_thread;
pspace = inf->pspace;
aspace = inf->aspace;
- inf->aspace = NULL;
- inf->pspace = NULL;
+ inf->aspace = nullptr;
+ inf->pspace = nullptr;
if (print_inferior_events)
{
if (exec)
{
- printf_filtered (_("[Detaching vfork parent %s "
- "after child exec]\n"), pidstr.c_str ());
+ gdb_printf (_("[Detaching vfork parent %s "
+ "after child exec]\n"), pidstr.c_str ());
}
else
{
- printf_filtered (_("[Detaching vfork parent %s "
- "after child exit]\n"), pidstr.c_str ());
+ gdb_printf (_("[Detaching vfork parent %s "
+ "after child exit]\n"), pidstr.c_str ());
}
}
child a new address space. */
inf->pspace = new program_space (maybe_new_address_space ());
inf->aspace = inf->pspace->aspace;
- inf->removable = 1;
+ inf->removable = true;
set_current_program_space (inf->pspace);
resume_parent = vfork_parent;
inf->pspace = new program_space (maybe_new_address_space ());
inf->aspace = inf->pspace->aspace;
set_current_program_space (inf->pspace);
- inf->removable = 1;
+ inf->removable = true;
inf->symfile_flags = SYMFILE_NO_READ;
clone_program_space (inf->pspace, vfork_parent->pspace);
}
}
+/* Handle TARGET_WAITKIND_VFORK_DONE. */
+
+static void
+handle_vfork_done (thread_info *event_thread)
+{
+ /* We only care about this event if inferior::thread_waiting_for_vfork_done is
+ set, that is if we are waiting for a vfork child not under our control
+ (because we detached it) to exec or exit.
+
+ If an inferior has vforked and we are debugging the child, we don't use
+ the vfork-done event to get notified about the end of the shared address
+ space window. We rely instead on the child's exec or exit event, and the
+ inferior::vfork_{parent,child} fields are used instead. See
+ handle_vfork_child_exec_or_exit for that. */
+ if (event_thread->inf->thread_waiting_for_vfork_done == nullptr)
+ {
+ infrun_debug_printf ("not waiting for a vfork-done event");
+ return;
+ }
+
+ INFRUN_SCOPED_DEBUG_ENTER_EXIT;
+
+ /* We stopped all threads (other than the vforking thread) of the inferior in
+ follow_fork and kept them stopped until now. It should therefore not be
+ possible for another thread to have reported a vfork during that window.
+ If THREAD_WAITING_FOR_VFORK_DONE is set, it has to be the same thread whose
+ vfork-done we are handling right now. */
+ gdb_assert (event_thread->inf->thread_waiting_for_vfork_done == event_thread);
+
+ event_thread->inf->thread_waiting_for_vfork_done = nullptr;
+ event_thread->inf->pspace->breakpoints_not_allowed = 0;
+
+ /* On non-stop targets, we stopped all the inferior's threads in follow_fork,
+ resume them now. On all-stop targets, everything that needs to be resumed
+ will be when we resume the event thread. */
+ if (target_is_non_stop_p ())
+ {
+ /* restart_threads and start_step_over may change the current thread, make
+ sure we leave the event thread as the current thread. */
+ scoped_restore_current_thread restore_thread;
+
+ insert_breakpoints ();
+ start_step_over ();
+
+ if (!step_over_info_valid_p ())
+ restart_threads (event_thread, event_thread->inf);
+ }
+}
+
/* Enum strings for "set|show follow-exec-mode". */
static const char follow_exec_mode_new[] = "new";
{
follow_exec_mode_new,
follow_exec_mode_same,
- NULL,
+ nullptr,
};
static const char *follow_exec_mode_string = follow_exec_mode_same;
show_follow_exec_mode_string (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
- fprintf_filtered (file, _("Follow exec mode is \"%s\".\n"), value);
+ gdb_printf (file, _("Follow exec mode is \"%s\".\n"), value);
}
/* EXEC_FILE_TARGET is assumed to be non-NULL. */
breakpoint or similar, it's gone now. We cannot truly
step-to-next statement through an exec(). */
thread_info *th = inferior_thread ();
- th->control.step_resume_breakpoint = NULL;
- th->control.exception_resume_breakpoint = NULL;
- th->control.single_step_breakpoints = NULL;
+ th->control.step_resume_breakpoint = nullptr;
+ th->control.exception_resume_breakpoint = nullptr;
+ th->control.single_step_breakpoints = nullptr;
th->control.step_range_start = 0;
th->control.step_range_end = 0;
/* What is this a.out's name? */
process_ptid = ptid_t (pid);
- printf_unfiltered (_("%s is executing new program: %s\n"),
- target_pid_to_str (process_ptid).c_str (),
- exec_file_target);
+ gdb_printf (_("%s is executing new program: %s\n"),
+ target_pid_to_str (process_ptid).c_str (),
+ exec_file_target);
/* We've followed the inferior through an exec. Therefore, the
inferior has essentially been killed & reborn. */
breakpoint_init_inferior (inf_execd);
gdb::unique_xmalloc_ptr<char> exec_file_host
- = exec_file_find (exec_file_target, NULL);
+ = exec_file_find (exec_file_target, nullptr);
/* If we were unable to map the executable target pathname onto a host
pathname, tell the user that. Otherwise GDB's subsequent behavior
is confusing. Maybe it would even be better to stop at this point
so that the user can specify a file manually before continuing. */
- if (exec_file_host == NULL)
+ if (exec_file_host == nullptr)
warning (_("Could not load symbols for executable %s.\n"
"Do you need \"set sysroot\"?"),
exec_file_target);
/* Also, loading a symbol file below may trigger symbol lookups, and
we don't want those to be satisfied by the libraries of the
previous incarnation of this process. */
- no_shared_libraries (NULL, 0);
+ no_shared_libraries (nullptr, 0);
- struct inferior *inf = current_inferior ();
+ inferior *execing_inferior = current_inferior ();
+ inferior *following_inferior;
if (follow_exec_mode_string == follow_exec_mode_new)
{
inferior's pid. Having two inferiors with the same pid would confuse
find_inferior_p(t)id. Transfer the terminal state and info from the
old to the new inferior. */
- inferior *new_inferior = add_inferior_with_spaces ();
+ following_inferior = add_inferior_with_spaces ();
- swap_terminal_info (new_inferior, inf);
- exit_inferior_silent (inf);
+ swap_terminal_info (following_inferior, execing_inferior);
+ exit_inferior_silent (execing_inferior);
- new_inferior->pid = pid;
- target_follow_exec (new_inferior, ptid, exec_file_target);
-
- /* We continue with the new inferior. */
- inf = new_inferior;
+ following_inferior->pid = pid;
}
else
{
+ /* follow-exec-mode is "same", we continue execution in the execing
+ inferior. */
+ following_inferior = execing_inferior;
+
/* The old description may no longer be fit for the new image.
E.g, a 64-bit process exec'ed a 32-bit process. Clear the
old description; we'll read a new one below. No need to do
around (its description is later cleared/refetched on
restart). */
target_clear_description ();
- target_follow_exec (inf, ptid, exec_file_target);
}
- gdb_assert (current_inferior () == inf);
- gdb_assert (current_program_space == inf->pspace);
+ target_follow_exec (following_inferior, ptid, exec_file_target);
+
+ gdb_assert (current_inferior () == following_inferior);
+ gdb_assert (current_program_space == following_inferior->pspace);
/* Attempt to open the exec file. SYMFILE_DEFER_BP_RESET is used
because the proper displacement for a PIE (Position Independent
Executable) main symbol file will only be computed by
solib_create_inferior_hook below. breakpoint_re_set would fail
to insert the breakpoints with the zero displacement. */
- try_open_exec_file (exec_file_host.get (), inf, SYMFILE_DEFER_BP_RESET);
+ try_open_exec_file (exec_file_host.get (), following_inferior,
+ SYMFILE_DEFER_BP_RESET);
/* If the target can specify a description, read it. Must do this
after flipping to the new executable (because the target supplied
registers. */
target_find_description ();
- gdb::observers::inferior_execd.notify (inf);
+ gdb::observers::inferior_execd.notify (execing_inferior, following_inferior);
breakpoint_re_set ();
clear_step_over_info (void)
{
infrun_debug_printf ("clearing step over info");
- step_over_info.aspace = NULL;
+ step_over_info.aspace = nullptr;
step_over_info.address = 0;
step_over_info.nonsteppable_watchpoint_p = 0;
step_over_info.thread = -1;
stepping_past_instruction_at (struct address_space *aspace,
CORE_ADDR address)
{
- return (step_over_info.aspace != NULL
+ return (step_over_info.aspace != nullptr
&& breakpoint_address_match (aspace, address,
step_over_info.aspace,
step_over_info.address));
static bool
step_over_info_valid_p (void)
{
- return (step_over_info.aspace != NULL
+ return (step_over_info.aspace != nullptr
|| stepping_past_nonsteppable_watchpoint ());
}
static bool
displaced_step_in_progress_thread (thread_info *thread)
{
- gdb_assert (thread != NULL);
+ gdb_assert (thread != nullptr);
return thread->displaced_step_state.in_progress ();
}
infrun_inferior_exit (struct inferior *inf)
{
inf->displaced_step_state.reset ();
+ inf->thread_waiting_for_vfork_done = nullptr;
}
static void
-infrun_inferior_execd (inferior *inf)
+infrun_inferior_execd (inferior *exec_inf, inferior *follow_inf)
{
/* If some threads where was doing a displaced step in this inferior at the
moment of the exec, they no longer exist. Even if the exec'ing thread
doing a displaced step, we don't want to to any fixup nor restore displaced
stepping buffer bytes. */
- inf->displaced_step_state.reset ();
+ follow_inf->displaced_step_state.reset ();
- for (thread_info *thread : inf->threads ())
+ for (thread_info *thread : follow_inf->threads ())
thread->displaced_step_state.reset ();
/* Since an in-line step is done with everything else stopped, if there was
one in progress at the time of the exec, it must have been the exec'ing
thread. */
clear_step_over_info ();
+
+ follow_inf->thread_waiting_for_vfork_done = nullptr;
}
/* If ON, and the architecture supports it, GDB will use displaced
const char *value)
{
if (can_use_displaced_stepping == AUTO_BOOLEAN_AUTO)
- fprintf_filtered (file,
- _("Debugger's willingness to use displaced stepping "
- "to step over breakpoints is %s (currently %s).\n"),
- value, target_is_non_stop_p () ? "on" : "off");
+ gdb_printf (file,
+ _("Debugger's willingness to use displaced stepping "
+ "to step over breakpoints is %s (currently %s).\n"),
+ value, target_is_non_stop_p () ? "on" : "off");
else
- fprintf_filtered (file,
- _("Debugger's willingness to use displaced stepping "
- "to step over breakpoints is %s.\n"), value);
+ gdb_printf (file,
+ _("Debugger's willingness to use displaced stepping "
+ "to step over breakpoints is %s.\n"), value);
}
/* Return true if the gdbarch implements the required methods to use
using displaced_step_reset_cleanup = FORWARD_SCOPE_EXIT (displaced_step_reset);
-/* See infrun.h. */
-
-std::string
-displaced_step_dump_bytes (const gdb_byte *buf, size_t len)
-{
- std::string ret;
-
- for (size_t i = 0; i < len; i++)
- {
- if (i == 0)
- ret += string_printf ("%02x", buf[i]);
- else
- ret += string_printf (" %02x", buf[i]);
- }
-
- return ret;
-}
-
/* Prepare to single-step, using displaced stepping.
Note that we cannot use displaced stepping when we have a signal to
CORE_ADDR original_pc = regcache_read_pc (regcache);
CORE_ADDR displaced_pc;
+ /* Display the instruction we are going to displaced step. */
+ if (debug_displaced)
+ {
+ string_file tmp_stream;
+ int dislen = gdb_print_insn (gdbarch, original_pc, &tmp_stream,
+ nullptr);
+
+ if (dislen > 0)
+ {
+ gdb::byte_vector insn_buf (dislen);
+ read_memory (original_pc, insn_buf.data (), insn_buf.size ());
+
+ std::string insn_bytes = bytes_to_string (insn_buf);
+
+ displaced_debug_printf ("original insn %s: %s \t %s",
+ paddress (gdbarch, original_pc),
+ insn_bytes.c_str (),
+ tmp_stream.string ().c_str ());
+ }
+ else
+ displaced_debug_printf ("original insn %s: invalid length: %d",
+ paddress (gdbarch, original_pc), dislen);
+ }
+
displaced_step_prepare_status status
= gdbarch_displaced_step_prepare (gdbarch, tp, displaced_pc);
paddress (gdbarch, original_pc),
paddress (gdbarch, displaced_pc));
+ /* Display the new displaced instruction(s). */
+ if (debug_displaced)
+ {
+ string_file tmp_stream;
+ CORE_ADDR addr = displaced_pc;
+
+ /* If displaced stepping is going to use h/w single step then we know
+ that the replacement instruction can only be a single instruction,
+ in that case set the end address at the next byte.
+
+ Otherwise the displaced stepping copy instruction routine could
+ have generated multiple instructions, and all we know is that they
+ must fit within the LEN bytes of the buffer. */
+ CORE_ADDR end
+ = addr + (gdbarch_displaced_step_hw_singlestep (gdbarch)
+ ? 1 : gdbarch_displaced_step_buffer_length (gdbarch));
+
+ while (addr < end)
+ {
+ int dislen = gdb_print_insn (gdbarch, addr, &tmp_stream, nullptr);
+ if (dislen <= 0)
+ {
+ displaced_debug_printf
+ ("replacement insn %s: invalid length: %d",
+ paddress (gdbarch, addr), dislen);
+ break;
+ }
+
+ gdb::byte_vector insn_buf (dislen);
+ read_memory (addr, insn_buf.data (), insn_buf.size ());
+
+ std::string insn_bytes = bytes_to_string (insn_buf);
+ std::string insn_str = tmp_stream.release ();
+ displaced_debug_printf ("replacement insn %s: %s \t %s",
+ paddress (gdbarch, addr),
+ insn_bytes.c_str (),
+ insn_str.c_str ());
+ addr += dislen;
+ }
+ }
+
return DISPLACED_STEP_PREPARE_STATUS_OK;
}
DISPLACED_STEP_FINISH_STATUS_OK as well. */
static displaced_step_finish_status
-displaced_step_finish (thread_info *event_thread, enum gdb_signal signal)
+displaced_step_finish (thread_info *event_thread,
+ const target_waitstatus &event_status)
{
displaced_step_thread_state *displaced = &event_thread->displaced_step_state;
/* Do the fixup, and release the resources acquired to do the displaced
step. */
return gdbarch_displaced_step_finish (displaced->get_original_gdbarch (),
- event_thread, signal);
+ event_thread, event_status);
}
/* Data to be passed around while handling an event. This data is
discarded between events. */
struct execution_control_state
{
- execution_control_state ()
+ explicit execution_control_state (thread_info *thr = nullptr)
+ : ptid (thr == nullptr ? null_ptid : thr->ptid),
+ event_thread (thr)
{
- this->reset ();
}
- void reset ()
- {
- this->target = nullptr;
- this->ptid = null_ptid;
- this->event_thread = nullptr;
- ws = target_waitstatus ();
- stop_func_filled_in = 0;
- stop_func_start = 0;
- stop_func_end = 0;
- stop_func_name = nullptr;
- wait_some_more = 0;
- hit_singlestep_breakpoint = 0;
- }
-
- process_stratum_target *target;
+ process_stratum_target *target = nullptr;
ptid_t ptid;
/* The thread that got the event, if this was a thread event; NULL
otherwise. */
struct thread_info *event_thread;
struct target_waitstatus ws;
- int stop_func_filled_in;
- CORE_ADDR stop_func_start;
- CORE_ADDR stop_func_end;
- const char *stop_func_name;
- int wait_some_more;
+ int stop_func_filled_in = 0;
+ CORE_ADDR stop_func_alt_start = 0;
+ CORE_ADDR stop_func_start = 0;
+ CORE_ADDR stop_func_end = 0;
+ const char *stop_func_name = nullptr;
+ int wait_some_more = 0;
/* True if the event thread hit the single-step breakpoint of
another thread. Thus the event doesn't cause a stop, the thread
needs to be single-stepped past the single-step breakpoint before
we can switch back to the original stepping thread. */
- int hit_singlestep_breakpoint;
+ int hit_singlestep_breakpoint = 0;
};
-/* Clear ECS and set it to point at TP. */
-
-static void
-reset_ecs (struct execution_control_state *ecs, struct thread_info *tp)
-{
- ecs->reset ();
- ecs->event_thread = tp;
- ecs->ptid = tp->ptid;
-}
-
static void keep_going_pass_signal (struct execution_control_state *ecs);
static void prepare_to_wait (struct execution_control_state *ecs);
static bool keep_going_stepped_thread (struct thread_info *tp);
for (thread_info *tp : range)
{
- struct execution_control_state ecss;
- struct execution_control_state *ecs = &ecss;
step_over_what step_what;
int must_be_in_line;
continue;
}
+ if (tp->inf->thread_waiting_for_vfork_done != nullptr)
+ {
+ /* When we stop all threads, handling a vfork, any thread in the step
+ over chain remains there. A user could also try to continue a
+ thread stopped at a breakpoint while another thread is waiting for
+ a vfork-done event. In any case, we don't want to start a step
+ over right now. */
+ continue;
+ }
+
/* Remove thread from the THREADS_TO_STEP chain. If anything goes wrong
while we try to prepare the displaced step, we don't add it back to
the global step over chain. This is to avoid a thread staying in the
|| tp->resumed ()
|| tp->executing ())
{
- internal_error (__FILE__, __LINE__,
- "[%s] has inconsistent state: "
+ internal_error ("[%s] has inconsistent state: "
"trap_expected=%d, resumed=%d, executing=%d\n",
tp->ptid.to_string ().c_str (),
tp->control.trap_expected,
continue;
switch_to_thread (tp);
- reset_ecs (ecs, tp);
- keep_going_pass_signal (ecs);
+ execution_control_state ecs (tp);
+ keep_going_pass_signal (&ecs);
- if (!ecs->wait_some_more)
+ if (!ecs.wait_some_more)
error (_("Command aborted."));
/* If the thread's step over could not be initiated because no buffers
schedlock_on,
schedlock_step,
schedlock_replay,
- NULL
+ nullptr
};
static const char *scheduler_mode = schedlock_replay;
static void
show_scheduler_mode (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
- fprintf_filtered (file,
- _("Mode for locking scheduler "
- "during execution is \"%s\".\n"),
- value);
+ gdb_printf (file,
+ _("Mode for locking scheduler "
+ "during execution is \"%s\".\n"),
+ value);
}
static void
user_visible_resume_target (ptid_t resume_ptid)
{
return (resume_ptid == minus_one_ptid && sched_multi
- ? NULL
+ ? nullptr
: current_inferior ()->process_target ());
}
+/* Find a thread from the inferiors that we'll resume that is waiting
+ for a vfork-done event. */
+
+static thread_info *
+find_thread_waiting_for_vfork_done ()
+{
+ gdb_assert (!target_is_non_stop_p ());
+
+ if (sched_multi)
+ {
+ for (inferior *inf : all_non_exited_inferiors ())
+ if (inf->thread_waiting_for_vfork_done != nullptr)
+ return inf->thread_waiting_for_vfork_done;
+ }
+ else
+ {
+ inferior *cur_inf = current_inferior ();
+ if (cur_inf->thread_waiting_for_vfork_done != nullptr)
+ return cur_inf->thread_waiting_for_vfork_done;
+ }
+ return nullptr;
+}
+
/* Return a ptid representing the set of threads that we will resume,
in the perspective of the target, assuming run control handling
does not require leaving some threads stopped (e.g., stepping past
return a wildcard ptid. */
if (target_is_non_stop_p ())
return inferior_ptid;
- else
- return user_visible_resume_ptid (user_step);
+
+ /* The rest of the function assumes non-stop==off and
+ target-non-stop==off.
+
+ If a thread is waiting for a vfork-done event, it means breakpoints are out
+ for this inferior (well, program space in fact). We don't want to resume
+ any thread other than the one waiting for vfork done, otherwise these other
+ threads could miss breakpoints. So if a thread in the resumption set is
+ waiting for a vfork-done event, resume only that thread.
+
+ The resumption set width depends on whether schedule-multiple is on or off.
+
+ Note that if the target_resume interface was more flexible, we could be
+ smarter here when schedule-multiple is on. For example, imagine 3
+ inferiors with 2 threads each (1.1, 1.2, 2.1, 2.2, 3.1 and 3.2). Threads
+ 2.1 and 3.2 are both waiting for a vfork-done event. Then we could ask the
+ target(s) to resume:
+
+ - All threads of inferior 1
+ - Thread 2.1
+ - Thread 3.2
+
+ Since we don't have that flexibility (we can only pass one ptid), just
+ resume the first thread waiting for a vfork-done event we find (e.g. thread
+ 2.1). */
+ thread_info *thr = find_thread_waiting_for_vfork_done ();
+ if (thr != nullptr)
+ {
+ /* If we have a thread that is waiting for a vfork-done event,
+ then we should have switched to it earlier. Calling
+ target_resume with thread scope is only possible when the
+ current thread matches the thread scope. */
+ gdb_assert (thr->ptid == inferior_ptid);
+ gdb_assert (thr->inf->process_target ()
+ == inferior_thread ()->inf->process_target ());
+ return thr->ptid;
+ }
+
+ return user_visible_resume_ptid (user_step);
}
/* Wrapper for target_resume, that handles infrun-specific
else
target_pass_signals (signal_pass);
- target_resume (resume_ptid, step, sig);
+ infrun_debug_printf ("resume_ptid=%s, step=%d, sig=%s",
+ resume_ptid.to_string ().c_str (),
+ step, gdb_signal_to_symbol_string (sig));
- if (target_can_async_p ())
- target_async (1);
+ target_resume (resume_ptid, step, sig);
}
/* Resume the inferior. SIG is the signal to give the inferior
if (target_can_async_p ())
{
- target_async (1);
+ target_async (true);
/* Tell the event loop we have an event to process. */
mark_async_event_handler (infrun_async_inferior_event_token);
}
/* Depends on stepped_breakpoint. */
step = currently_stepping (tp);
- if (current_inferior ()->waiting_for_vfork_done)
+ if (current_inferior ()->thread_waiting_for_vfork_done != nullptr)
{
/* Don't try to single-step a vfork parent that is waiting for
the child to get out of the shared memory region (by exec'ing
clear_step_over_info ();
tp->control.trap_expected = 0;
- if (tp->control.step_resume_breakpoint == NULL)
+ if (tp->control.step_resume_breakpoint == nullptr)
{
/* 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);
+ gdb_assert (tp->control.step_resume_breakpoint->first_loc ()
+ .permanent);
tp->step_after_step_resume_breakpoint = step;
}
&& use_displaced_stepping (tp)
&& !step_over_info_valid_p ()
&& sig == GDB_SIGNAL_0
- && !current_inferior ()->waiting_for_vfork_done)
+ && current_inferior ()->thread_waiting_for_vfork_done == nullptr)
{
displaced_step_prepare_status prepare_status
= displaced_step_prepare (tp);
/* Fallback to stepping over the breakpoint in-line. */
if (target_is_non_stop_p ())
- stop_all_threads ();
+ stop_all_threads ("displaced stepping falling back on inline stepping");
set_step_over_info (regcache->aspace (),
regcache_read_pc (regcache), 0, tp->global_num);
a step-resume breakpoint set on the earlier handler. We cannot
set another step-resume breakpoint; just continue on until the
original breakpoint is hit. */
- if (tp->control.step_resume_breakpoint == NULL)
+ if (tp->control.step_resume_breakpoint == nullptr)
{
insert_hp_step_resume_breakpoint_at_frame (get_current_frame ());
tp->step_after_step_resume_breakpoint = 1;
step = false;
}
- if (debug_displaced
- && tp->control.trap_expected
- && use_displaced_stepping (tp)
- && !step_over_info_valid_p ())
- {
- struct regcache *resume_regcache = get_thread_regcache (tp);
- struct gdbarch *resume_gdbarch = resume_regcache->arch ();
- CORE_ADDR actual_pc = regcache_read_pc (resume_regcache);
- gdb_byte buf[4];
-
- read_memory (actual_pc, buf, sizeof (buf));
- displaced_debug_printf ("run %s: %s",
- paddress (resume_gdbarch, actual_pc),
- displaced_step_dump_bytes
- (buf, sizeof (buf)).c_str ());
- }
-
if (tp->control.may_range_step)
{
/* If we're resuming a thread with the PC out of the step
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->control.step_start_function = nullptr;
tp->stop_requested = 0;
tp->control.stop_step = 0;
if (m_prev_enable_commit_resumed)
{
/* This is the outermost instance, re-enable
- COMMIT_RESUMED_STATE on the targets where it's possible. */
+ COMMIT_RESUMED_STATE on the targets where it's possible. */
maybe_set_commit_resumed_all_targets ();
}
else
struct regcache *regcache;
struct gdbarch *gdbarch;
CORE_ADDR pc;
- struct execution_control_state ecss;
- struct execution_control_state *ecs = &ecss;
/* If we're stopped at a fork/vfork, follow the branch set by the
"set follow-fork-mode" command; otherwise, we'll just proceed
}
/* We'll update this if & when we switch to a new thread. */
- previous_inferior_ptid = inferior_ptid;
+ update_previous_thread ();
regcache = get_current_regcache ();
gdbarch = regcache->arch ();
continue;
}
+ /* If a thread of that inferior is waiting for a vfork-done
+ (for a detached vfork child to exec or exit), breakpoints are
+ removed. We must not resume any thread of that inferior, other
+ than the one waiting for the vfork-done. */
+ if (tp->inf->thread_waiting_for_vfork_done != nullptr
+ && tp != tp->inf->thread_waiting_for_vfork_done)
+ {
+ infrun_debug_printf ("[%s] another thread of this inferior is "
+ "waiting for vfork-done",
+ tp->ptid.to_string ().c_str ());
+ continue;
+ }
+
infrun_debug_printf ("resuming %s",
tp->ptid.to_string ().c_str ());
- reset_ecs (ecs, tp);
+ execution_control_state ecs (tp);
switch_to_thread (tp);
- keep_going_pass_signal (ecs);
- if (!ecs->wait_some_more)
+ keep_going_pass_signal (&ecs);
+ if (!ecs.wait_some_more)
error (_("Command aborted."));
}
}
- else if (!cur_thr->resumed () && !thread_is_in_step_over_chain (cur_thr))
+ else if (!cur_thr->resumed ()
+ && !thread_is_in_step_over_chain (cur_thr)
+ /* In non-stop, forbid resuming a thread if some other thread of
+ that inferior is waiting for a vfork-done event (this means
+ breakpoints are out for this inferior). */
+ && !(non_stop
+ && cur_thr->inf->thread_waiting_for_vfork_done != nullptr))
{
/* The thread wasn't started, and isn't queued, run it now. */
- reset_ecs (ecs, cur_thr);
+ execution_control_state ecs (cur_thr);
switch_to_thread (cur_thr);
- keep_going_pass_signal (ecs);
- if (!ecs->wait_some_more)
+ keep_going_pass_signal (&ecs);
+ if (!ecs.wait_some_more)
error (_("Command aborted."));
}
nullify_last_target_wait_ptid ();
- previous_inferior_ptid = inferior_ptid;
+ update_previous_thread ();
}
\f
struct execution_control_state *ecs);
static void handle_signal_stop (struct execution_control_state *ecs);
static void check_exception_resume (struct execution_control_state *,
- struct frame_info *);
+ frame_info_ptr);
static void end_stepping_range (struct execution_control_state *ecs);
static void stop_waiting (struct execution_control_state *ecs);
}
}
-static void
-infrun_thread_thread_exit (struct thread_info *tp, int silent)
-{
- if (target_last_proc_target == tp->inf->process_target ()
- && target_last_wait_ptid == tp->ptid)
- nullify_last_target_wait_ptid ();
-}
-
/* Delete the step resume, single-step and longjmp/exception resume
breakpoints of TP. */
do_target_wait_1 (inferior *inf, ptid_t ptid,
target_waitstatus *status, target_wait_flags options)
{
- ptid_t event_ptid;
struct thread_info *tp;
/* We know that we are looking for an event in the target of inferior
ptid.to_string ().c_str ());
/* We have a specific thread to check. */
- tp = find_thread_ptid (inf, ptid);
- gdb_assert (tp != NULL);
+ tp = inf->find_thread (ptid);
+ gdb_assert (tp != nullptr);
if (!tp->has_pending_waitstatus ())
- tp = NULL;
+ tp = nullptr;
}
- if (tp != NULL
+ if (tp != nullptr
&& (tp->stop_reason () == TARGET_STOPPED_BY_SW_BREAKPOINT
|| tp->stop_reason () == TARGET_STOPPED_BY_HW_BREAKPOINT))
{
}
}
- if (tp != NULL)
+ if (tp != nullptr)
{
infrun_debug_printf ("Using pending wait status %s for %s.",
tp->pending_waitstatus ().to_string ().c_str (),
if (!target_can_async_p ())
options &= ~TARGET_WNOHANG;
- if (deprecated_target_wait_hook)
- event_ptid = deprecated_target_wait_hook (ptid, status, options);
- else
- event_ptid = target_wait (ptid, status, options);
-
- return event_ptid;
+ return target_wait (ptid, status, options);
}
/* Wrapper for target_wait that first checks whether threads have
};
static bool handle_one (const wait_one_event &event);
-static void restart_threads (struct thread_info *event_thread);
/* Prepare and stabilize the inferior for detaching it. E.g.,
detaching while a thread is displaced stepping is a recipe for
}
}
+/* If all-stop, but there exists a non-stop target, stop all threads
+ now that we're presenting the stop to the user. */
+
+static void
+stop_all_threads_if_all_stop_mode ()
+{
+ if (!non_stop && exists_non_stop_target ())
+ stop_all_threads ("presenting stop to user in all-stop");
+}
+
/* Wait for control to return from inferior to debugger.
If inferior gets a signal, we may decide to start it up again
while (1)
{
- struct execution_control_state ecss;
- struct execution_control_state *ecs = &ecss;
+ execution_control_state ecs;
overlay_cache_invalid = 1;
don't get any event. */
target_dcache_invalidate ();
- ecs->ptid = do_target_wait_1 (inf, minus_one_ptid, &ecs->ws, 0);
- ecs->target = inf->process_target ();
+ ecs.ptid = do_target_wait_1 (inf, minus_one_ptid, &ecs.ws, 0);
+ ecs.target = inf->process_target ();
if (debug_infrun)
- print_target_wait_results (minus_one_ptid, ecs->ptid, ecs->ws);
+ print_target_wait_results (minus_one_ptid, ecs.ptid, ecs.ws);
/* Now figure out what to do with the result of the result. */
- handle_inferior_event (ecs);
+ handle_inferior_event (&ecs);
- if (!ecs->wait_some_more)
+ if (!ecs.wait_some_more)
break;
}
+ stop_all_threads_if_all_stop_mode ();
+
/* No error, don't finish the state yet. */
finish_state.release ();
}
static void
clean_up_just_stopped_threads_fsms (struct execution_control_state *ecs)
{
+ /* The first clean_up call below assumes the event thread is the current
+ one. */
+ if (ecs->event_thread != nullptr)
+ gdb_assert (ecs->event_thread == inferior_thread ());
+
if (ecs->event_thread != nullptr
&& ecs->event_thread->thread_fsm () != nullptr)
ecs->event_thread->thread_fsm ()->clean_up (ecs->event_thread);
if (!non_stop)
{
+ scoped_restore_current_thread restore_thread;
+
for (thread_info *thr : all_non_exited_threads ())
{
if (thr->thread_fsm () == nullptr)
switch_to_thread (thr);
thr->thread_fsm ()->clean_up (thr);
}
-
- if (ecs->event_thread != nullptr)
- switch_to_thread (ecs->event_thread);
}
}
&& !gdb_in_secondary_prompt_p (ui))
{
target_terminal::ours ();
- gdb::observers::sync_execution_done.notify ();
- ui_register_input_event_handler (ui);
+ top_level_interpreter ()->on_sync_execution_done ();
+ ui->register_file_handler ();
}
}
}
}
+/* A quit_handler callback installed while we're handling inferior
+ events. */
+
+static void
+infrun_quit_handler ()
+{
+ if (target_terminal::is_ours ())
+ {
+ /* Do nothing.
+
+ default_quit_handler would throw a quit in this case, but if
+ we're handling an event while we have the terminal, it means
+ the target is running a background execution command, and
+ thus when users press Ctrl-C, they're wanting to interrupt
+ whatever command they were executing in the command line.
+ E.g.:
+
+ (gdb) c&
+ (gdb) foo bar whatever<ctrl-c>
+
+ That Ctrl-C should clear the input line, not interrupt event
+ handling if it happens that the user types Ctrl-C at just the
+ "wrong" time!
+
+ It's as-if background event handling was handled by a
+ separate background thread.
+
+ To be clear, the Ctrl-C is not lost -- it will be processed
+ by the next QUIT call once we're out of fetch_inferior_event
+ again. */
+ }
+ else
+ {
+ if (check_quit_flag ())
+ target_pass_ctrlc ();
+ }
+}
+
/* 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
{
INFRUN_SCOPED_DEBUG_ENTER_EXIT;
- struct execution_control_state ecss;
- struct execution_control_state *ecs = &ecss;
+ execution_control_state ecs;
int cmd_done = 0;
/* Events are always processed with the main UI as current UI. This
scoped_restore save_pagination
= make_scoped_restore (&pagination_enabled, false);
+ /* Install a quit handler that does nothing if we have the terminal
+ (meaning the target is running a background execution command),
+ so that Ctrl-C never interrupts GDB before the event is fully
+ handled. */
+ scoped_restore restore_quit_handler
+ = make_scoped_restore (&quit_handler, infrun_quit_handler);
+
+ /* Make sure a SIGINT does not interrupt an extension language while
+ we're handling an event. That could interrupt a Python unwinder
+ or a Python observer or some such. A Ctrl-C should either be
+ forwarded to the inferior if the inferior has the terminal, or,
+ if GDB has the terminal, should interrupt the command the user is
+ typing in the CLI. */
+ scoped_disable_cooperative_sigint_handling restore_coop_sigint;
+
/* End up with readline processing input, if necessary. */
{
SCOPE_EXIT { reinstall_readline_callback_handler_cleanup (); };
the event. */
scoped_disable_commit_resumed disable_commit_resumed ("handling event");
- if (!do_target_wait (ecs, TARGET_WNOHANG))
+ if (!do_target_wait (&ecs, TARGET_WNOHANG))
{
infrun_debug_printf ("do_target_wait returned no event");
disable_commit_resumed.reset_and_commit ();
return;
}
- gdb_assert (ecs->ws.kind () != TARGET_WAITKIND_IGNORE);
+ gdb_assert (ecs.ws.kind () != TARGET_WAITKIND_IGNORE);
- /* Switch to the target that generated the event, so we can do
- target calls. */
- switch_to_target_no_thread (ecs->target);
+ /* Switch to the inferior that generated the event, so we can do
+ target calls. If the event was not associated to a ptid, */
+ if (ecs.ptid != null_ptid
+ && ecs.ptid != minus_one_ptid)
+ switch_to_inferior_no_thread (find_inferior_ptid (ecs.target, ecs.ptid));
+ else
+ switch_to_target_no_thread (ecs.target);
if (debug_infrun)
- print_target_wait_results (minus_one_ptid, ecs->ptid, ecs->ws);
+ print_target_wait_results (minus_one_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. */
- ptid_t finish_ptid = !target_is_non_stop_p () ? minus_one_ptid : ecs->ptid;
- scoped_finish_thread_state finish_state (ecs->target, finish_ptid);
+ ptid_t finish_ptid = !target_is_non_stop_p () ? minus_one_ptid : ecs.ptid;
+ scoped_finish_thread_state finish_state (ecs.target, finish_ptid);
/* Get executed before scoped_restore_current_thread above to apply
still for the thread which has thrown the exception. */
= make_scope_exit (delete_just_stopped_threads_infrun_breakpoints);
/* Now figure out what to do with the result of the result. */
- handle_inferior_event (ecs);
+ handle_inferior_event (&ecs);
- if (!ecs->wait_some_more)
+ if (!ecs.wait_some_more)
{
- struct inferior *inf = find_inferior_ptid (ecs->target, ecs->ptid);
+ struct inferior *inf = find_inferior_ptid (ecs.target, ecs.ptid);
bool should_stop = true;
- struct thread_info *thr = ecs->event_thread;
+ struct thread_info *thr = ecs.event_thread;
delete_just_stopped_threads_infrun_breakpoints ();
if (!should_stop)
{
- keep_going (ecs);
+ keep_going (&ecs);
}
else
{
bool should_notify_stop = true;
- int proceeded = 0;
+ bool proceeded = false;
+
+ stop_all_threads_if_all_stop_mode ();
- clean_up_just_stopped_threads_fsms (ecs);
+ clean_up_just_stopped_threads_fsms (&ecs);
if (thr != nullptr && thr->thread_fsm () != nullptr)
should_notify_stop
if (should_notify_stop)
{
/* We may not find an inferior if this was a process exit. */
- if (inf == NULL || inf->control.stop_soon == NO_STOP_QUIETLY)
+ if (inf == nullptr || inf->control.stop_soon == NO_STOP_QUIETLY)
proceeded = normal_stop ();
}
selected.". */
if (!non_stop
&& cmd_done
- && ecs->ws.kind () != TARGET_WAITKIND_NO_RESUMED)
+ && ecs.ws.kind () != TARGET_WAITKIND_NO_RESUMED)
restore_thread.dont_restore ();
}
}
reinstalled here. */
}
+ /* Handling this event might have caused some inferiors to become prunable.
+ For example, the exit of an inferior that was automatically added. Try
+ to get rid of them. Keeping those around slows down things linearly.
+
+ Note that this never removes the current inferior. Therefore, call this
+ after RESTORE_THREAD went out of scope, in case the event inferior (which was
+ temporarily made the current inferior) is meant to be deleted.
+
+ Call this before all_uis_check_sync_execution_done, so that notifications about
+ removed inferiors appear before the prompt. */
+ prune_inferiors ();
+
/* If a UI was in sync execution mode, and now isn't, restore its
prompt (a synchronous execution command has finished, and we're
ready for input). */
&& exec_done_display_p
&& (inferior_ptid == null_ptid
|| inferior_thread ()->state != THREAD_RUNNING))
- printf_unfiltered (_("completed.\n"));
+ gdb_printf (_("completed.\n"));
}
/* See infrun.h. */
void
-set_step_info (thread_info *tp, struct frame_info *frame,
+set_step_info (thread_info *tp, frame_info_ptr frame,
struct symtab_and_line sal)
{
/* This can be removed once this function no longer implicitly relies on the
tp->current_symtab = sal.symtab;
tp->current_line = sal.line;
+
+ infrun_debug_printf
+ ("symtab = %s, line = %d, step_frame_id = %s, step_stack_frame_id = %s",
+ tp->current_symtab != nullptr ? tp->current_symtab->filename : "<null>",
+ tp->current_line,
+ tp->control.step_frame_id.to_string ().c_str (),
+ tp->control.step_stack_frame_id.to_string ().c_str ());
}
/* Clear context switchable stepping state. */
}
static bool
-stepped_in_from (struct frame_info *frame, struct frame_id step_frame_id)
+stepped_in_from (frame_info_ptr frame, struct frame_id step_frame_id)
{
for (frame = get_prev_frame (frame);
- frame != NULL;
+ frame != nullptr;
frame = get_prev_frame (frame))
{
- if (frame_id_eq (get_frame_id (frame), step_frame_id))
+ if (get_frame_id (frame) == step_frame_id)
return true;
if (get_frame_type (frame) != INLINE_FRAME)
static bool
inline_frame_is_marked_for_skip (bool prev_frame, struct thread_info *tp)
{
- struct frame_info *frame = get_current_frame ();
+ frame_info_ptr frame = get_current_frame ();
if (prev_frame)
frame = get_prev_frame (frame);
- for (; frame != NULL; frame = get_prev_frame (frame))
+ for (; frame != nullptr; frame = get_prev_frame (frame))
{
- const char *fn = NULL;
+ const char *fn = nullptr;
symtab_and_line sal;
struct symbol *sym;
- if (frame_id_eq (get_frame_id (frame), tp->control.step_frame_id))
+ if (get_frame_id (frame) == tp->control.step_frame_id)
break;
if (get_frame_type (frame) != INLINE_FRAME)
break;
sal = find_frame_sal (frame);
sym = get_frame_function (frame);
- if (sym != NULL)
+ if (sym != nullptr)
fn = sym->print_name ();
if (sal.line != 0
infrun_debug_printf ("syscall number=%d", syscall_number);
ecs->event_thread->control.stop_bpstat
- = bpstat_stop_status (regcache->aspace (),
- ecs->event_thread->stop_pc (),
- ecs->event_thread, ecs->ws);
+ = bpstat_stop_status_nowatch (regcache->aspace (),
+ ecs->event_thread->stop_pc (),
+ ecs->event_thread, ecs->ws);
if (handle_stop_requested (ecs))
return false;
stop_func_start is NOT advanced when in a range of a
non-contiguous block that does not contain the entry pc. */
if (block != nullptr
- && ecs->stop_func_start <= BLOCK_ENTRY_PC (block)
- && BLOCK_ENTRY_PC (block) < ecs->stop_func_end)
+ && ecs->stop_func_start <= block->entry_pc ()
+ && block->entry_pc () < ecs->stop_func_end)
{
ecs->stop_func_start
+= gdbarch_deprecated_function_start_offset (gdbarch);
+ /* PowerPC functions have a Local Entry Point (LEP) and a Global
+ Entry Point (GEP). There is only one Entry Point (GEP = LEP) for
+ other architectures. */
+ ecs->stop_func_alt_start = ecs->stop_func_start;
+
if (gdbarch_skip_entrypoint_p (gdbarch))
ecs->stop_func_start
= gdbarch_skip_entrypoint (gdbarch, ecs->stop_func_start);
{
struct inferior *inf = find_inferior_ptid (ecs->target, ecs->ptid);
- gdb_assert (inf != NULL);
+ gdb_assert (inf != nullptr);
return inf->control.stop_soon;
}
don't get any event. */
target_dcache_invalidate ();
- if (deprecated_target_wait_hook)
- event_ptid = deprecated_target_wait_hook (minus_one_ptid, ws, TARGET_WNOHANG);
- else
- event_ptid = target_wait (minus_one_ptid, ws, TARGET_WNOHANG);
+ event_ptid = target_wait (minus_one_ptid, ws, TARGET_WNOHANG);
if (debug_infrun)
print_target_wait_results (minus_one_ptid, event_ptid, *ws);
for (inferior *inf : all_inferiors ())
{
process_stratum_target *target = inf->process_target ();
- if (target == NULL
+ if (target == nullptr
|| !target->is_async_p ()
|| !target->threads_executing)
continue;
{
/* If nothing is resumed, remove the target from the
event loop. */
- target_async (0);
+ target_async (false);
}
else if (event.ws.kind () != TARGET_WAITKIND_IGNORE)
return event;
for (inferior *inf : all_inferiors ())
{
process_stratum_target *target = inf->process_target ();
- if (target == NULL
+ if (target == nullptr
|| !target->is_async_p ()
|| !target->threads_executing)
continue;
/* No waitable targets left. All must be stopped. */
target_waitstatus ws;
ws.set_no_resumed ();
- return {NULL, minus_one_ptid, std::move (ws)};
+ return {nullptr, minus_one_ptid, std::move (ws)};
}
QUIT;
- int numfds = interruptible_select (nfds, &readfds, 0, NULL, 0);
+ int numfds = interruptible_select (nfds, &readfds, 0, nullptr, 0);
if (numfds < 0)
{
if (errno == EINTR)
}
else
{
- t = find_thread_ptid (event.target, event.ptid);
+ t = event.target->find_thread (event.ptid);
/* Check if this is the first time we see this thread.
Don't bother adding if it individually exited. */
if (t == nullptr
}
else
{
- thread_info *t = find_thread_ptid (event.target, event.ptid);
- if (t == NULL)
+ thread_info *t = event.target->find_thread (event.ptid);
+ if (t == nullptr)
t = add_thread (event.target, event.ptid);
t->stop_requested = 0;
/* This may be the first time we see the inferior report
a stop. */
- inferior *inf = find_inferior_ptid (event.target, event.ptid);
- if (inf->needs_setup)
+ if (t->inf->needs_setup)
{
switch_to_thread_no_regs (t);
setup_inferior (0);
/* We caught the event that we intended to catch, so
there's no event to save as pending. */
- if (displaced_step_finish (t, GDB_SIGNAL_0)
+ if (displaced_step_finish (t, event.ws)
== DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED)
{
/* Add it back to the step-over queue. */
}
else
{
- enum gdb_signal sig;
struct regcache *regcache;
infrun_debug_printf
/* Record for later. */
save_waitstatus (t, event.ws);
- sig = (event.ws.kind () == TARGET_WAITKIND_STOPPED
- ? event.ws.sig () : GDB_SIGNAL_0);
-
- if (displaced_step_finish (t, sig)
+ if (displaced_step_finish (t, event.ws)
== DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED)
{
/* Add it back to the step-over queue. */
/* See infrun.h. */
void
-stop_all_threads (void)
+stop_all_threads (const char *reason, inferior *inf)
{
/* We may need multiple passes to discover all threads. */
int pass;
gdb_assert (exists_non_stop_target ());
- infrun_debug_printf ("starting");
+ INFRUN_SCOPED_DEBUG_START_END ("reason=%s, inf=%d", reason,
+ inf != nullptr ? inf->num : -1);
+
+ infrun_debug_show_threads ("non-exited threads",
+ all_non_exited_threads ());
scoped_restore_current_thread restore_thread;
- /* Enable thread events of all targets. */
+ /* Enable thread events on relevant targets. */
for (auto *target : all_non_exited_process_targets ())
{
+ if (inf != nullptr && inf->process_target () != target)
+ continue;
+
switch_to_target_no_thread (target);
target_thread_events (true);
}
SCOPE_EXIT
{
- /* Disable thread events of all targets. */
+ /* Disable thread events on relevant targets. */
for (auto *target : all_non_exited_process_targets ())
{
+ if (inf != nullptr && inf->process_target () != target)
+ continue;
+
switch_to_target_no_thread (target);
target_thread_events (false);
}
for (auto *target : all_non_exited_process_targets ())
{
+ if (inf != nullptr && inf->process_target () != target)
+ continue;
+
switch_to_target_no_thread (target);
update_thread_list ();
}
to tell the target to stop. */
for (thread_info *t : all_non_exited_threads ())
{
+ if (inf != nullptr && t->inf != inf)
+ continue;
+
/* For a single-target setting with an all-stop target,
we would not even arrive here. For a multi-target
setting, until GDB is able to handle a mixture of
inferior *curr_inf = current_inferior ();
scoped_restore_current_thread restore_thread;
-
- for (auto *target : all_non_exited_process_targets ())
- {
- switch_to_target_no_thread (target);
- update_thread_list ();
- }
+ update_thread_list ();
/* If:
if (ecs->ws.kind () != TARGET_WAITKIND_EXITED
&& ecs->ws.kind () != TARGET_WAITKIND_SIGNALLED)
{
- ecs->event_thread = find_thread_ptid (ecs->target, ecs->ptid);
+ ecs->event_thread = ecs->target->find_thread (ecs->ptid);
/* If it's a new thread, add it to the thread database. */
- if (ecs->event_thread == NULL)
+ if (ecs->event_thread == nullptr)
ecs->event_thread = add_thread (ecs->target, ecs->ptid);
/* Disable range stepping. If the next step request could use a
ecs->event_thread->set_stop_pc (regcache_read_pc (regcache));
ecs->event_thread->control.stop_bpstat
- = bpstat_stop_status (regcache->aspace (),
- ecs->event_thread->stop_pc (),
- ecs->event_thread, ecs->ws);
+ = bpstat_stop_status_nowatch (regcache->aspace (),
+ ecs->event_thread->stop_pc (),
+ ecs->event_thread, ecs->ws);
if (handle_stop_requested (ecs))
return;
return;
}
- internal_error (__FILE__, __LINE__,
- _("unhandled stop_soon: %d"), (int) stop_soon);
+ internal_error (_("unhandled stop_soon: %d"), (int) stop_soon);
}
case TARGET_WAITKIND_SPURIOUS:
need to have access to the just-exited thread. That is the
case of GNU/Linux's "checkpoint" support, for example.
Call the switch_to_xxx routine as appropriate. */
- thread_info *thr = find_thread_ptid (ecs->target, ecs->ptid);
+ thread_info *thr = ecs->target->find_thread (ecs->ptid);
if (thr != nullptr)
switch_to_thread (thr);
else
(LONGEST) ecs->ws.exit_status ());
/* Also record this in the inferior itself. */
- current_inferior ()->has_exit_code = 1;
+ current_inferior ()->has_exit_code = true;
current_inferior ()->exit_code = (LONGEST) ecs->ws.exit_status ();
/* Support the --return-child-result option. */
return_child_result_value = ecs->ws.exit_status ();
- gdb::observers::exited.notify (ecs->ws.exit_status ());
+ interps_notify_exited (ecs->ws.exit_status ());
}
else
{
"signal number.");
}
- gdb::observers::signal_exited.notify (ecs->ws.sig ());
+ interps_notify_signal_exited (ecs->ws.sig ());
}
gdb_flush (gdb_stdout);
has been done. Perform cleanup for parent process here. Note
that this operation also cleans up the child process for vfork,
because their pages are shared. */
- displaced_step_finish (ecs->event_thread, GDB_SIGNAL_TRAP);
+ displaced_step_finish (ecs->event_thread, ecs->ws);
/* Start a new step-over in another thread if there's one
that needs it. */
start_step_over ();
list yet at this point. */
child_regcache
- = get_thread_arch_aspace_regcache (parent_inf->process_target (),
+ = get_thread_arch_aspace_regcache (parent_inf,
ecs->ws.child_ptid (),
gdbarch,
parent_inf->aspace);
(regcache_read_pc (get_thread_regcache (ecs->event_thread)));
ecs->event_thread->control.stop_bpstat
- = bpstat_stop_status (get_current_regcache ()->aspace (),
- ecs->event_thread->stop_pc (),
- ecs->event_thread, ecs->ws);
+ = bpstat_stop_status_nowatch (get_current_regcache ()->aspace (),
+ ecs->event_thread->stop_pc (),
+ ecs->event_thread, ecs->ws);
if (handle_stop_requested (ecs))
return;
/* Note that one of these may be an invalid pointer,
depending on detach_fork. */
thread_info *parent = ecs->event_thread;
- thread_info *child = find_thread_ptid (targ, ecs->ws.child_ptid ());
+ thread_info *child = targ->find_thread (ecs->ws.child_ptid ());
/* At this point, the parent is marked running, and the
child is marked stopped. */
ecs->ptid = inferior_ptid;
if (should_resume)
- keep_going (ecs);
+ {
+ /* Never call switch_back_to_stepped_thread if we are waiting for
+ vfork-done (waiting for an external vfork child to exec or
+ exit). We will resume only the vforking thread for the purpose
+ of collecting the vfork-done event, and we will restart any
+ step once the critical shared address space window is done. */
+ if ((!follow_child
+ && detach_fork
+ && parent->inf->thread_waiting_for_vfork_done != nullptr)
+ || !switch_back_to_stepped_thread (ecs))
+ keep_going (ecs);
+ }
else
stop_waiting (ecs);
return;
context_switch (ecs);
- current_inferior ()->waiting_for_vfork_done = 0;
- current_inferior ()->pspace->breakpoints_not_allowed = 0;
+ handle_vfork_done (ecs->event_thread);
+ gdb_assert (inferior_thread () == ecs->event_thread);
if (handle_stop_requested (ecs))
return;
- /* This also takes care of reinserting breakpoints in the
- previously locked inferior. */
- keep_going (ecs);
+ if (!switch_back_to_stepped_thread (ecs))
+ {
+ gdb_assert (inferior_thread () == ecs->event_thread);
+ /* This also takes care of reinserting breakpoints in the
+ previously locked inferior. */
+ keep_going (ecs);
+ }
return;
case TARGET_WAITKIND_EXECD:
(regcache_read_pc (get_thread_regcache (ecs->event_thread)));
ecs->event_thread->control.stop_bpstat
- = bpstat_stop_status (get_current_regcache ()->aspace (),
- ecs->event_thread->stop_pc (),
- ecs->event_thread, ecs->ws);
+ = bpstat_stop_status_nowatch (get_current_regcache ()->aspace (),
+ ecs->event_thread->stop_pc (),
+ ecs->event_thread, ecs->ws);
if (handle_stop_requested (ecs))
return;
if (handle_stop_requested (ecs))
return;
- gdb::observers::no_history.notify ();
+ interps_notify_no_history ();
stop_waiting (ecs);
return;
}
}
/* Restart threads back to what they were trying to do back when we
- paused them for an in-line step-over. The EVENT_THREAD thread is
- ignored. */
+ paused them (because of an in-line step-over or vfork, for example).
+ The EVENT_THREAD thread is ignored (not restarted).
+
+ If INF is non-nullptr, only resume threads from INF. */
static void
-restart_threads (struct thread_info *event_thread)
+restart_threads (struct thread_info *event_thread, inferior *inf)
{
+ INFRUN_SCOPED_DEBUG_START_END ("event_thread=%s, inf=%d",
+ event_thread->ptid.to_string ().c_str (),
+ inf != nullptr ? inf->num : -1);
+
+ gdb_assert (!step_over_info_valid_p ());
+
/* In case the instruction just stepped spawned a new thread. */
update_thread_list ();
for (thread_info *tp : all_non_exited_threads ())
{
+ if (inf != nullptr && tp->inf != inf)
+ continue;
+
if (tp->inf->detaching)
{
infrun_debug_printf ("restart threads: [%s] inferior detaching",
above. */
if (thread_still_needs_step_over (tp))
{
- internal_error (__FILE__, __LINE__,
- "thread [%s] needs a step-over, but not in "
+ internal_error ("thread [%s] needs a step-over, but not in "
"step-over queue\n",
tp->ptid.to_string ().c_str ());
}
}
else
{
- struct execution_control_state ecss;
- struct execution_control_state *ecs = &ecss;
-
infrun_debug_printf ("restart threads: [%s] continuing",
tp->ptid.to_string ().c_str ());
- reset_ecs (ecs, tp);
+ execution_control_state ecs (tp);
switch_to_thread (tp);
- keep_going_pass_signal (ecs);
+ keep_going_pass_signal (&ecs);
}
}
}
static int
finish_step_over (struct execution_control_state *ecs)
{
- displaced_step_finish (ecs->event_thread, ecs->event_thread->stop_signal ());
+ displaced_step_finish (ecs->event_thread, ecs->ws);
bool had_step_over_info = step_over_info_valid_p ();
return 0;
pending = iterate_over_threads (resumed_thread_with_pending_status,
- NULL);
- if (pending != NULL)
+ nullptr);
+ if (pending != nullptr)
{
struct thread_info *tp = ecs->event_thread;
struct regcache *regcache;
return 0;
}
+/* See infrun.h. */
+
+void
+notify_signal_received (gdb_signal sig)
+{
+ interps_notify_signal_received (sig);
+ gdb::observers::signal_received.notify (sig);
+}
+
+/* See infrun.h. */
+
+void
+notify_normal_stop (bpstat *bs, int print_frame)
+{
+ interps_notify_normal_stop (bs, print_frame);
+ gdb::observers::normal_stop.notify (bs, print_frame);
+}
+
+/* See infrun.h. */
+
+void notify_user_selected_context_changed (user_selected_what selection)
+{
+ interps_notify_user_selected_context_changed (selection);
+ gdb::observers::user_selected_context_changed.notify (selection);
+}
+
/* Come here when the program has stopped with a signal. */
static void
handle_signal_stop (struct execution_control_state *ecs)
{
- struct frame_info *frame;
+ frame_info_ptr frame;
struct gdbarch *gdbarch;
int stopped_by_watchpoint;
enum stop_kind stop_soon;
{
/* The signal table tells us to print about this signal. */
target_terminal::ours_for_output ();
- gdb::observers::signal_received.notify (ecs->event_thread->stop_signal ());
+ notify_signal_received (ecs->event_thread->stop_signal ());
target_terminal::inferior ();
}
if (ecs->event_thread->prev_pc == ecs->event_thread->stop_pc ()
&& ecs->event_thread->control.trap_expected
- && ecs->event_thread->control.step_resume_breakpoint == NULL)
+ && ecs->event_thread->control.step_resume_breakpoint == nullptr)
{
/* We were just starting a new sequence, attempting to
single-step off of a breakpoint and expecting a SIGTRAP.
&& (pc_in_thread_step_range (ecs->event_thread->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)
+ && (get_stack_frame_id (frame)
+ == ecs->event_thread->control.step_stack_frame_id)
+ && ecs->event_thread->control.step_resume_breakpoint == nullptr)
{
/* The inferior is about to take a signal that will take it
out of the single step range. Set a breakpoint at the
process_event_stop_test (struct execution_control_state *ecs)
{
struct symtab_and_line stop_pc_sal;
- struct frame_info *frame;
+ frame_info_ptr frame;
struct gdbarch *gdbarch;
CORE_ADDR jmp_buf_pc;
struct bpstat_what what;
case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME:
{
- struct frame_info *init_frame;
+ frame_info_ptr init_frame;
/* There are several cases to consider.
infrun_debug_printf ("BPSTAT_WHAT_CLEAR_LONGJMP_RESUME");
gdb_assert (ecs->event_thread->control.exception_resume_breakpoint
- != NULL);
+ != nullptr);
delete_exception_resume_breakpoint (ecs->event_thread);
if (what.is_longjmp)
{
struct frame_id current_id
= get_frame_id (get_current_frame ());
- if (frame_id_eq (current_id,
- ecs->event_thread->initiating_frame))
+ if (current_id == ecs->event_thread->initiating_frame)
{
/* Case 2. Fall through. */
}
struct breakpoint *sr_bp
= ecs->event_thread->control.step_resume_breakpoint;
- if (sr_bp != NULL
- && sr_bp->loc->permanent
+ if (sr_bp != nullptr
+ && sr_bp->first_loc ().permanent
&& sr_bp->type == bp_hp_step_resume
- && sr_bp->loc->address == ecs->event_thread->prev_pc)
+ && sr_bp->first_loc ().address == ecs->event_thread->prev_pc)
{
infrun_debug_printf ("stepped permanent breakpoint, stopped in handler");
delete_step_resume_breakpoint (ecs->event_thread);
if (pc_in_thread_step_range (ecs->event_thread->stop_pc (),
ecs->event_thread)
&& (execution_direction != EXEC_REVERSE
- || frame_id_eq (get_frame_id (frame),
- ecs->event_thread->control.step_frame_id)))
+ || get_frame_id (frame) == ecs->event_thread->control.step_frame_id))
{
infrun_debug_printf
("stepping inside range [%s-%s]",
if (execution_direction != EXEC_REVERSE
&& ecs->event_thread->control.step_over_calls == STEP_OVER_UNDEBUGGABLE
- && in_solib_dynsym_resolve_code (ecs->event_thread->stop_pc ()))
+ && in_solib_dynsym_resolve_code (ecs->event_thread->stop_pc ())
+ && (ecs->event_thread->control.step_start_function == nullptr
+ || !in_solib_dynsym_resolve_code (
+ ecs->event_thread->control.step_start_function->value_block ()
+ ->entry_pc ())))
{
CORE_ADDR pc_after_resolver =
gdbarch_skip_solib_resolver (gdbarch, ecs->event_thread->stop_pc ());
previous frame's ID is sufficient - but it is a common case and
cheaper than checking the previous frame's ID.
- NOTE: frame_id_eq will never report two invalid frame IDs as
+ NOTE: frame_id::operator== will never report two invalid frame IDs as
being equal, so to get into this block, both the current and
previous frame must have valid frame IDs. */
/* The outer_frame_id check is a heuristic to detect stepping
"outermost" function. This could be fixed by marking
outermost frames as !stack_p,code_p,special_p. Then the
initial outermost frame, before sp was valid, would
- have code_addr == &_start. See the comment in frame_id_eq
+ have code_addr == &_start. See the comment in frame_id::operator==
for more. */
- if (!frame_id_eq (get_stack_frame_id (frame),
- ecs->event_thread->control.step_stack_frame_id)
- && (frame_id_eq (frame_unwind_caller_id (get_current_frame ()),
- ecs->event_thread->control.step_stack_frame_id)
- && (!frame_id_eq (ecs->event_thread->control.step_stack_frame_id,
- outer_frame_id)
+ if ((get_stack_frame_id (frame)
+ != ecs->event_thread->control.step_stack_frame_id)
+ && ((frame_unwind_caller_id (get_current_frame ())
+ == ecs->event_thread->control.step_stack_frame_id)
+ && ((ecs->event_thread->control.step_stack_frame_id
+ != outer_frame_id)
|| (ecs->event_thread->control.step_start_function
!= find_pc_function (ecs->event_thread->stop_pc ())))))
{
sr_sal.pc = ecs->stop_func_start;
sr_sal.pspace = get_frame_program_space (frame);
insert_step_resume_breakpoint_at_sal (gdbarch,
- sr_sal, null_frame_id);
+ sr_sal, get_stack_frame_id (frame));
}
}
else
the trampoline processing logic, however, there are some trampolines
that have no names, so we should do trampoline handling first. */
if (ecs->event_thread->control.step_over_calls == STEP_OVER_UNDEBUGGABLE
- && ecs->stop_func_name == NULL
+ && ecs->stop_func_name == nullptr
&& stop_pc_sal.line == 0)
{
infrun_debug_printf ("stepped into undebuggable function");
}
}
+ if (execution_direction == EXEC_REVERSE
+ && ecs->event_thread->control.proceed_to_finish
+ && ecs->event_thread->stop_pc () >= ecs->stop_func_alt_start
+ && ecs->event_thread->stop_pc () < ecs->stop_func_start)
+ {
+ /* We are executing the reverse-finish command.
+ If the system supports multiple entry points and we are finishing a
+ function in reverse. If we are between the entry points singe-step
+ back to the alternate entry point. If we are at the alternate entry
+ point -- just need to back up by one more single-step, which
+ should take us back to the function call. */
+ ecs->event_thread->control.step_range_start
+ = ecs->event_thread->control.step_range_end = 1;
+ keep_going (ecs);
+ return;
+
+ }
+
if (ecs->event_thread->control.step_range_end == 1)
{
/* It is stepi or nexti. We always want to stop stepping after
frame machinery detected some skipped call sites, we have entered
a new inline function. */
- if (frame_id_eq (get_frame_id (get_current_frame ()),
- ecs->event_thread->control.step_frame_id)
+ if ((get_frame_id (get_current_frame ())
+ == ecs->event_thread->control.step_frame_id)
&& inline_skipped_frames (ecs->event_thread))
{
infrun_debug_printf ("stepped into inlined function");
through a more inlined call beyond its call site. */
if (get_frame_type (get_current_frame ()) == INLINE_FRAME
- && !frame_id_eq (get_frame_id (get_current_frame ()),
- ecs->event_thread->control.step_frame_id)
+ && (get_frame_id (get_current_frame ())
+ != ecs->event_thread->control.step_frame_id)
&& stepped_in_from (get_current_frame (),
ecs->event_thread->control.step_frame_id))
{
end_stepping_range (ecs);
return;
}
- else if (frame_id_eq (get_frame_id (get_current_frame ()),
- ecs->event_thread->control.step_frame_id))
+ else if (get_frame_id (get_current_frame ())
+ == ecs->event_thread->control.step_frame_id)
{
/* We are not at the start of a statement, and we have not changed
frame.
ecs->event_thread->control.step_range_start = stop_pc_sal.pc;
ecs->event_thread->control.step_range_end = stop_pc_sal.end;
ecs->event_thread->control.may_range_step = 1;
+ infrun_debug_printf
+ ("updated step range, start = %s, end = %s, may_range_step = %d",
+ paddress (gdbarch, ecs->event_thread->control.step_range_start),
+ paddress (gdbarch, ecs->event_thread->control.step_range_end),
+ ecs->event_thread->control.may_range_step);
if (refresh_step_info)
set_step_info (ecs->event_thread, frame, stop_pc_sal);
if (thr->state != THREAD_RUNNING)
continue;
- execution_control_state ecs;
- reset_ecs (&ecs, thr);
+ execution_control_state ecs (thr);
switch_to_thread (thr);
keep_going (&ecs);
return;
static bool
keep_going_stepped_thread (struct thread_info *tp)
{
- struct frame_info *frame;
- struct execution_control_state ecss;
- struct execution_control_state *ecs = &ecss;
+ frame_info_ptr frame;
/* If the stepping thread exited, then don't try to switch back and
resume it, which could fail in several different ways depending
infrun_debug_printf ("resuming previously stepped thread");
- reset_ecs (ecs, tp);
+ execution_control_state ecs (tp);
switch_to_thread (tp);
tp->set_stop_pc (regcache_read_pc (get_thread_regcache (tp)));
{
infrun_debug_printf ("expected thread still hasn't advanced");
- keep_going_pass_signal (ecs);
+ keep_going_pass_signal (&ecs);
}
return true;
currently_stepping (struct thread_info *tp)
{
return ((tp->control.step_range_end
- && tp->control.step_resume_breakpoint == NULL)
+ && tp->control.step_resume_breakpoint == nullptr)
|| tp->control.trap_expected
|| tp->stepped_breakpoint
|| bpstat_should_step ());
compunit_symtab *cust
= find_pc_compunit_symtab (ecs->event_thread->stop_pc ());
- if (cust != NULL && compunit_language (cust) != language_asm)
+ if (cust != nullptr && cust->language () != language_asm)
ecs->stop_func_start
= gdbarch_skip_prologue_noexcept (gdbarch, ecs->stop_func_start);
fill_in_stop_func (gdbarch, ecs);
cust = find_pc_compunit_symtab (ecs->event_thread->stop_pc ());
- if (cust != NULL && compunit_language (cust) != language_asm)
+ if (cust != nullptr && cust->language () != language_asm)
ecs->stop_func_start
= gdbarch_skip_prologue_noexcept (gdbarch, ecs->stop_func_start);
/* There should never be more than one step-resume or longjmp-resume
breakpoint per thread, so we should never be setting a new
step_resume_breakpoint when one is already active. */
- gdb_assert (inferior_thread ()->control.step_resume_breakpoint == NULL);
+ gdb_assert (inferior_thread ()->control.step_resume_breakpoint == nullptr);
gdb_assert (sr_type == bp_step_resume || sr_type == bp_hp_step_resume);
infrun_debug_printf ("inserting step-resume breakpoint at %s",
RETURN_FRAME.pc. */
static void
-insert_hp_step_resume_breakpoint_at_frame (struct frame_info *return_frame)
+insert_hp_step_resume_breakpoint_at_frame (frame_info_ptr return_frame)
{
- gdb_assert (return_frame != NULL);
+ gdb_assert (return_frame != nullptr);
struct gdbarch *gdbarch = get_frame_arch (return_frame);
of frame_unwind_caller_id for an example). */
static void
-insert_step_resume_breakpoint_at_caller (struct frame_info *next_frame)
+insert_step_resume_breakpoint_at_caller (frame_info_ptr next_frame)
{
/* We shouldn't have gotten here if we don't know where the call site
is. */
/* There should never be more than one longjmp-resume breakpoint per
thread, so we should never be setting a new
longjmp_resume_breakpoint when one is already active. */
- gdb_assert (inferior_thread ()->control.exception_resume_breakpoint == NULL);
+ gdb_assert (inferior_thread ()->control.exception_resume_breakpoint == nullptr);
infrun_debug_printf ("inserting longjmp-resume breakpoint at %s",
paddress (gdbarch, pc));
static void
insert_exception_resume_breakpoint (struct thread_info *tp,
const struct block *b,
- struct frame_info *frame,
+ frame_info_ptr frame,
struct symbol *sym)
{
try
b, VAR_DOMAIN);
value = read_var_value (vsym.symbol, vsym.block, frame);
/* If the value was optimized out, revert to the old behavior. */
- if (! value_optimized_out (value))
+ if (! value->optimized_out ())
{
handler = value_as_address (value);
infrun_debug_printf ("exception resume at %lx",
(unsigned long) handler);
+ /* set_momentary_breakpoint_at_pc creates a thread-specific
+ breakpoint for the current inferior thread. */
+ gdb_assert (tp == inferior_thread ());
bp = set_momentary_breakpoint_at_pc (get_frame_arch (frame),
handler,
bp_exception_resume).release ();
/* set_momentary_breakpoint_at_pc invalidates FRAME. */
- frame = NULL;
+ frame = nullptr;
- bp->thread = tp->global_num;
- inferior_thread ()->control.exception_resume_breakpoint = bp;
+ tp->control.exception_resume_breakpoint = bp;
}
}
catch (const gdb_exception_error &e)
static void
insert_exception_resume_from_probe (struct thread_info *tp,
const struct bound_probe *probe,
- struct frame_info *frame)
+ frame_info_ptr frame)
{
struct value *arg_value;
CORE_ADDR handler;
infrun_debug_printf ("exception resume at %s",
paddress (probe->objfile->arch (), handler));
+ /* set_momentary_breakpoint_at_pc creates a thread-specific breakpoint
+ for the current inferior thread. */
+ gdb_assert (tp == inferior_thread ());
bp = set_momentary_breakpoint_at_pc (get_frame_arch (frame),
handler, bp_exception_resume).release ();
- bp->thread = tp->global_num;
- inferior_thread ()->control.exception_resume_breakpoint = bp;
+ tp->control.exception_resume_breakpoint = bp;
}
/* This is called when an exception has been intercepted. Check to
static void
check_exception_resume (struct execution_control_state *ecs,
- struct frame_info *frame)
+ frame_info_ptr frame)
{
struct bound_probe probe;
struct symbol *func;
try
{
const struct block *b;
- struct block_iterator iter;
- struct symbol *sym;
int argno = 0;
/* The exception breakpoint is a thread-specific breakpoint on
cases such as throwing an exception from inside a signal
handler. */
- b = SYMBOL_BLOCK_VALUE (func);
- ALL_BLOCK_SYMBOLS (b, iter, sym)
+ b = func->value_block ();
+ for (struct symbol *sym : block_iterator_range (b))
{
if (!sym->is_argument ())
continue;
/* Let callers know we don't want to wait for the inferior anymore. */
ecs->wait_some_more = 0;
-
- /* If all-stop, but there exists a non-stop target, stop all
- threads now that we're presenting the stop to the user. */
- if (!non_stop && exists_non_stop_target ())
- stop_all_threads ();
}
/* Like keep_going, but passes the signal to the inferior, even if the
ecs->event_thread->global_num);
}
else if (remove_wps)
- set_step_over_info (NULL, 0, remove_wps, -1);
+ set_step_over_info (nullptr, 0, remove_wps, -1);
/* If we now need to do an in-line step-over, we need to stop
all other threads. Note this must be done before
we're about to step over, otherwise other threads could miss
it. */
if (step_over_info_valid_p () && target_is_non_stop_p ())
- stop_all_threads ();
+ stop_all_threads ("starting in-line step-over");
/* Stop stepping if inserting breakpoints fails. */
try
the interpreters, through observers. Interpreters then call these
with whatever uiout is right. */
-void
-print_end_stepping_range_reason (struct ui_out *uiout)
-{
- /* For CLI-like interpreters, print nothing. */
-
- if (uiout->is_mi_like_p ())
- {
- uiout->field_string ("reason",
- async_reason_lookup (EXEC_ASYNC_END_STEPPING_RANGE));
- }
-}
-
void
print_signal_exited_reason (struct ui_out *uiout, enum gdb_signal siggnal)
{
{
struct thread_info *thr = inferior_thread ();
+ infrun_debug_printf ("signal = %s", gdb_signal_to_string (siggnal));
+
annotate_signal ();
if (uiout->is_mi_like_p ())
uiout->field_string ("thread-id", print_thread_id (thr));
const char *name = thread_name (thr);
- if (name != NULL)
+ if (name != nullptr)
{
uiout->text (" \"");
uiout->field_string ("name", name);
void
print_no_history_reason (struct ui_out *uiout)
{
- uiout->text ("\nNo more reverse-execution history.\n");
+ if (uiout->is_mi_like_p ())
+ uiout->field_string ("reason", async_reason_lookup (EXEC_ASYNC_NO_HISTORY));
+ else
+ uiout->text ("\nNo more reverse-execution history.\n");
}
/* Print current location without a level number, if we have changed
should) carry around the function and does (or should) use
that when doing a frame comparison. */
if (tp->control.stop_step
- && frame_id_eq (tp->control.step_frame_id,
- get_frame_id (get_current_frame ()))
+ && (tp->control.step_frame_id
+ == get_frame_id (get_current_frame ()))
&& (tp->control.step_start_function
== find_pc_function (tp->stop_pc ())))
{
do_frame_printing = 0;
break;
default:
- internal_error (__FILE__, __LINE__, _("Unknown value."));
+ internal_error (_("Unknown value."));
}
/* The behavior of this routine with respect to the source
LOCATION: Print only location
SRC_AND_LOC: Print location and source line. */
if (do_frame_printing)
- print_stack_frame (get_selected_frame (NULL), 0, source_flag, 1);
+ print_stack_frame (get_selected_frame (nullptr), 0, source_flag, 1);
}
/* See infrun.h. */
if (remove_breakpoints ())
{
target_terminal::ours_for_output ();
- printf_filtered (_("Cannot remove breakpoints because "
- "program is no longer writable.\nFurther "
- "execution is probably impossible.\n"));
+ gdb_printf (_("Cannot remove breakpoints because "
+ "program is no longer writable.\nFurther "
+ "execution is probably impossible.\n"));
}
}
}
return true;
if (inf_num != current_inferior ()->num)
return true;
- if (thread != NULL && thread->state != THREAD_STOPPED)
+ if (thread != nullptr && thread->state != THREAD_STOPPED)
return true;
if (get_stop_id () != stop_id)
return true;
/* See infrun.h. */
-int
-normal_stop (void)
+bool
+normal_stop ()
{
struct target_waitstatus last;
update_thread_list ();
if (last.kind () == TARGET_WAITKIND_STOPPED && stopped_by_random_signal)
- gdb::observers::signal_received.notify (inferior_thread ()->stop_signal ());
+ notify_signal_received (inferior_thread ()->stop_signal ());
/* As with the notification of thread events, we want to delay
notifying the user that we've switched thread context until
the current thread back to the thread the user had selected right
after this event is handled, so we're not really switching, only
informing of a stop. */
- if (!non_stop
- && previous_inferior_ptid != inferior_ptid
- && target_has_execution ()
- && last.kind () != TARGET_WAITKIND_SIGNALLED
- && last.kind () != TARGET_WAITKIND_EXITED
- && last.kind () != TARGET_WAITKIND_NO_RESUMED)
+ if (!non_stop)
{
- SWITCH_THRU_ALL_UIS ()
+ if ((last.kind () != TARGET_WAITKIND_SIGNALLED
+ && last.kind () != TARGET_WAITKIND_EXITED
+ && last.kind () != TARGET_WAITKIND_NO_RESUMED)
+ && target_has_execution ()
+ && previous_thread != inferior_thread ())
{
- target_terminal::ours_for_output ();
- printf_filtered (_("[Switching to %s]\n"),
- target_pid_to_str (inferior_ptid).c_str ());
- annotate_thread_changed ();
+ SWITCH_THRU_ALL_UIS ()
+ {
+ target_terminal::ours_for_output ();
+ gdb_printf (_("[Switching to %s]\n"),
+ target_pid_to_str (inferior_ptid).c_str ());
+ annotate_thread_changed ();
+ }
}
- previous_inferior_ptid = inferior_ptid;
+
+ update_previous_thread ();
}
if (last.kind () == TARGET_WAITKIND_NO_RESUMED)
if (current_ui->prompt_state == PROMPT_BLOCKED)
{
target_terminal::ours_for_output ();
- printf_filtered (_("No unwaited-for children left.\n"));
+ gdb_printf (_("No unwaited-for children left.\n"));
}
}
/* Pop the empty frame that contains the stack dummy. This
also restores inferior state prior to the call (struct
infcall_suspend_state). */
- struct frame_info *frame = get_current_frame ();
+ frame_info_ptr frame = get_current_frame ();
gdb_assert (get_frame_type (frame) == DUMMY_FRAME);
frame_pop (frame);
/* Look up the hook_stop and run it (CLI internally handles problem
of stop_command's pre-hook not existing). */
- if (stop_command != NULL)
- {
- stop_context saved_context;
-
- try
- {
- execute_cmd_pre_hook (stop_command);
- }
- catch (const gdb_exception &ex)
- {
- exception_fprintf (gdb_stderr, ex,
- "Error while running hook_stop:\n");
- }
+ stop_context saved_context;
- /* If the stop hook resumes the target, then there's no point in
- trying to notify about the previous stop; its context is
- gone. Likewise if the command switches thread or inferior --
- the observers would print a stop for the wrong
- thread/inferior. */
- if (saved_context.changed ())
- return 1;
+ try
+ {
+ execute_cmd_pre_hook (stop_command);
+ }
+ catch (const gdb_exception_error &ex)
+ {
+ exception_fprintf (gdb_stderr, ex,
+ "Error while running hook_stop:\n");
}
+ /* If the stop hook resumes the target, then there's no point in
+ trying to notify about the previous stop; its context is
+ gone. Likewise if the command switches thread or inferior --
+ the observers would print a stop for the wrong
+ thread/inferior. */
+ if (saved_context.changed ())
+ return true;
+
/* Notify observers about the stop. This is where the interpreters
print the stop event. */
- if (inferior_ptid != null_ptid)
- gdb::observers::normal_stop.notify (inferior_thread ()->control.stop_bpstat,
- stop_print_frame);
- else
- gdb::observers::normal_stop.notify (NULL, stop_print_frame);
-
+ notify_normal_stop ((inferior_ptid != null_ptid
+ ? inferior_thread ()->control.stop_bpstat
+ : nullptr),
+ stop_print_frame);
annotate_stopped ();
if (target_has_execution ())
breakpoint_auto_delete (inferior_thread ()->control.stop_bpstat);
}
- /* Try to get rid of automatically added inferiors that are no
- longer needed. Keeping those around slows down things linearly.
- Note that this never removes the current inferior. */
- prune_inferiors ();
-
- return 0;
+ return false;
}
\f
int
static void
sig_print_header (void)
{
- printf_filtered (_("Signal Stop\tPrint\tPass "
- "to program\tDescription\n"));
+ gdb_printf (_("Signal Stop\tPrint\tPass "
+ "to program\tDescription\n"));
}
static void
if (name_padding <= 0)
name_padding = 0;
- printf_filtered ("%s", name);
- printf_filtered ("%*.*s ", name_padding, name_padding, " ");
- printf_filtered ("%s\t", signal_stop[oursig] ? "Yes" : "No");
- printf_filtered ("%s\t", signal_print[oursig] ? "Yes" : "No");
- printf_filtered ("%s\t\t", signal_program[oursig] ? "Yes" : "No");
- printf_filtered ("%s\n", gdb_signal_to_string (oursig));
+ gdb_printf ("%s", name);
+ gdb_printf ("%*.*s ", name_padding, name_padding, " ");
+ gdb_printf ("%s\t", signal_stop[oursig] ? "Yes" : "No");
+ gdb_printf ("%s\t", signal_print[oursig] ? "Yes" : "No");
+ gdb_printf ("%s\t\t", signal_program[oursig] ? "Yes" : "No");
+ gdb_printf ("%s\n", gdb_signal_to_string (oursig));
}
/* Specify how various signals in the inferior should be handled. */
enum gdb_signal oursig;
int allsigs;
- if (args == NULL)
+ if (args == nullptr)
{
error_no_arg (_("signal to handle"));
}
sigs[signum] = 1;
}
else
- printf_unfiltered (_("Not confirmed, unchanged.\n"));
+ gdb_printf (_("Not confirmed, unchanged.\n"));
}
break;
case GDB_SIGNAL_0:
"noignore",
"noprint",
"nopass",
- NULL,
+ nullptr,
};
signal_completer (ignore, tracker, text, word);
return;
}
- printf_filtered ("\n");
+ gdb_printf ("\n");
/* These ugly casts brought to you by the native VAX compiler. */
for (oursig = GDB_SIGNAL_FIRST;
(int) oursig < (int) GDB_SIGNAL_LAST;
sig_print_info (oursig);
}
- printf_filtered (_("\nUse the \"handle\" command "
- "to change these tables.\n"));
+ gdb_printf (_("\nUse the \"handle\" command "
+ "to change these tables.\n"));
}
/* The $_siginfo convenience variable is a bit special. We don't know
transferred =
target_read (current_inferior ()->top_target (),
TARGET_OBJECT_SIGNAL_INFO,
- NULL,
- value_contents_all_raw (v).data (),
- value_offset (v),
- TYPE_LENGTH (value_type (v)));
+ nullptr,
+ v->contents_all_raw ().data (),
+ v->offset (),
+ v->type ()->length ());
- if (transferred != TYPE_LENGTH (value_type (v)))
+ if (transferred != v->type ()->length ())
error (_("Unable to read siginfo"));
}
transferred = target_write (current_inferior ()->top_target (),
TARGET_OBJECT_SIGNAL_INFO,
- NULL,
- value_contents_all_raw (fromval).data (),
- value_offset (v),
- TYPE_LENGTH (value_type (fromval)));
+ nullptr,
+ fromval->contents_all_raw ().data (),
+ v->offset (),
+ fromval->type ()->length ());
- if (transferred != TYPE_LENGTH (value_type (fromval)))
+ if (transferred != fromval->type ()->length ())
error (_("Unable to write siginfo"));
}
{
struct type *type = gdbarch_get_siginfo_type (gdbarch);
- return allocate_computed_value (type, &siginfo_value_funcs, NULL);
+ return value::allocate_computed (type, &siginfo_value_funcs, nullptr);
}
- return allocate_value (builtin_type (gdbarch)->builtin_void);
+ return value::allocate (builtin_type (gdbarch)->builtin_void);
}
\f
if (gdbarch_get_siginfo_type_p (gdbarch))
{
struct type *type = gdbarch_get_siginfo_type (gdbarch);
- size_t len = TYPE_LENGTH (type);
+ size_t len = type->length ();
siginfo_data.reset ((gdb_byte *) xmalloc (len));
if (target_read (current_inferior ()->top_target (),
- TARGET_OBJECT_SIGNAL_INFO, NULL,
+ TARGET_OBJECT_SIGNAL_INFO, nullptr,
siginfo_data.get (), 0, len) != len)
{
/* Errors ignored. */
/* Errors ignored. */
target_write (current_inferior ()->top_target (),
- TARGET_OBJECT_SIGNAL_INFO, NULL,
- m_siginfo_data.get (), 0, TYPE_LENGTH (type));
+ TARGET_OBJECT_SIGNAL_INFO, nullptr,
+ m_siginfo_data.get (), 0, type->length ());
}
/* The inferior can be gone if the user types "print exit(0)"
struct gdbarch *m_siginfo_gdbarch = nullptr;
/* The inferior format depends on SIGINFO_GDBARCH and it has a length of
- TYPE_LENGTH (gdbarch_get_siginfo_type ()). For different gdbarch the
+ gdbarch_get_siginfo_type ()->length (). For different gdbarch the
content would be invalid. */
gdb::unique_xmalloc_ptr<gdb_byte> m_siginfo_data;
};
inf_status->thread_control = tp->control;
inf_status->inferior_control = inf->control;
- tp->control.step_resume_breakpoint = NULL;
- tp->control.exception_resume_breakpoint = NULL;
+ tp->control.step_resume_breakpoint = nullptr;
+ tp->control.exception_resume_breakpoint = nullptr;
/* Save original bpstat chain to INF_STATUS; replace it in TP with copy of
chain. If caller's caller is walking the chain, they'll be happier if we
static const char *const exec_direction_names[] = {
exec_forward,
exec_reverse,
- NULL
+ nullptr
};
static void
{
switch (execution_direction) {
case EXEC_FORWARD:
- fprintf_filtered (out, _("Forward.\n"));
+ gdb_printf (out, _("Forward.\n"));
break;
case EXEC_REVERSE:
- fprintf_filtered (out, _("Reverse.\n"));
+ gdb_printf (out, _("Reverse.\n"));
break;
default:
- internal_error (__FILE__, __LINE__,
- _("bogus execution_direction value: %d"),
+ internal_error (_("bogus execution_direction value: %d"),
(int) execution_direction);
}
}
show_schedule_multiple (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
- fprintf_filtered (file, _("Resuming the execution of threads "
- "of all processes is %s.\n"), value);
+ gdb_printf (file, _("Resuming the execution of threads "
+ "of all processes is %s.\n"), value);
}
/* Implementation of `siginfo' variable. */
static const struct internalvar_funcs siginfo_funcs =
{
siginfo_make_value,
- NULL,
- NULL
+ nullptr,
};
/* Callback for infrun's target events source. This is marked when a
/* Register extra event sources in the event loop. */
infrun_async_inferior_event_token
- = create_async_event_handler (infrun_async_inferior_event_handler, NULL,
+ = create_async_event_handler (infrun_async_inferior_event_handler, nullptr,
"infrun");
cmd_list_element *info_signals_cmd
all signals cumulatively specified."));
set_cmd_completer (c, handle_completer);
- if (!dbx_commands)
- stop_command = add_cmd ("stop", class_obscure,
- not_just_help_class_command, _("\
+ stop_command = add_cmd ("stop", class_obscure,
+ not_just_help_class_command, _("\
There is no `stop' command, but you can set a hook on `stop'.\n\
This allows you to set a list of commands to be run each time execution\n\
of the program stops."), &cmdlist);
_("Set inferior debugging."),
_("Show inferior debugging."),
_("When non-zero, inferior specific debugging is enabled."),
- NULL, show_debug_infrun, &setdebuglist, &showdebuglist);
+ nullptr, show_debug_infrun, &setdebuglist, &showdebuglist);
add_setshow_boolean_cmd ("non-stop", no_class,
&non_stop_1, _("\
child - the new process is debugged after a fork\n\
The unfollowed process will continue to run.\n\
By default, the debugger will follow the parent process."),
- NULL,
+ nullptr,
show_follow_fork_mode_string,
&setlist, &showlist);
the executable the process was running after the exec call.\n\
\n\
By default, the debugger will use the same inferior."),
- NULL,
+ nullptr,
show_follow_exec_mode_string,
&setlist, &showlist);
commands only resume the threads of the current process. The set of\n\
threads that are resumed is further refined by the scheduler-locking\n\
mode (see help set scheduler-locking)."),
- NULL,
+ nullptr,
show_schedule_multiple,
&setlist, &showlist);
When set, doing a step over a function without debug line information\n\
will stop at the first instruction of that function. Otherwise, the\n\
function is skipped and the step command stops at a different source line."),
- NULL,
+ nullptr,
show_step_stop_if_no_debug,
&setlist, &showlist);
architecture. If auto (which is the default), gdb will use displaced stepping\n\
if the target architecture supports it and non-stop mode is active, but will not\n\
use it in all-stop mode (see help set non-stop)."),
- NULL,
+ nullptr,
show_can_use_displaced_stepping,
&setlist, &showlist);
Set whether gdb will detach the child of a fork."), _("\
Show whether gdb will detach the child of a fork."), _("\
Tells gdb whether to detach the child of a fork."),
- NULL, NULL, &setlist, &showlist);
+ nullptr, nullptr, &setlist, &showlist);
/* Set/show disable address space randomization mode. */
"infrun");
gdb::observers::thread_stop_requested.attach (infrun_thread_stop_requested,
"infrun");
- gdb::observers::thread_exit.attach (infrun_thread_thread_exit, "infrun");
gdb::observers::inferior_exit.attach (infrun_inferior_exit, "infrun");
gdb::observers::inferior_execd.attach (infrun_inferior_execd, "infrun");
value with a void typed value, and when we get here, gdbarch
isn't initialized yet. At this point, we're quite sure there
isn't another convenience variable of the same name. */
- create_internalvar_type_lazy ("_siginfo", &siginfo_funcs, NULL);
+ create_internalvar_type_lazy ("_siginfo", &siginfo_funcs, nullptr);
add_setshow_boolean_cmd ("observer", no_class,
&observer_mode_1, _("\