#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 */
}
/* 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;
-static ptid_t previous_inferior_ptid;
+/* 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 ());
+}
+
+/* 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.
static bool
follow_fork_inferior (bool follow_child, bool detach_fork)
{
+ INFRUN_SCOPED_DEBUG_ENTER_EXIT;
+
+ infrun_debug_printf ("follow_child = %d, detach_fork = %d",
+ follow_child, detach_fork);
+
target_waitkind fork_kind = inferior_thread ()->pending_follow.kind ();
gdb_assert (fork_kind == TARGET_WAITKIND_FORKED
|| fork_kind == TARGET_WAITKIND_VFORKED);
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->set_arch (parent_inf->arch ());
+ child_inf->tdesc_info = parent_inf->tdesc_info;
child_inf->symfile_flags = SYMFILE_NO_READ;
parent_inf->thread_waiting_for_vfork_done
= detach_fork ? inferior_thread () : nullptr;
parent_inf->pspace->breakpoints_not_allowed = detach_fork;
+
+ infrun_debug_printf
+ ("parent_inf->thread_waiting_for_vfork_done == %s",
+ (parent_inf->thread_waiting_for_vfork_done == nullptr
+ ? "nullptr"
+ : (parent_inf->thread_waiting_for_vfork_done
+ ->ptid.to_string ().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->set_arch (parent_inf->arch ());
+ child_inf->tdesc_info = parent_inf->tdesc_info;
if (has_vforked)
{
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 (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)
+ if (!follow_child && !sched_multi)
maybe_restore.emplace ();
switch_to_thread (*child_inf->threads ().begin ());
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. */
static bool
follow_fork ()
{
+ INFRUN_SCOPED_DEBUG_ENTER_EXIT;
+
bool follow_child = (follow_fork_mode_string == follow_fork_mode_child);
bool should_resume = true;
if (!non_stop)
{
- process_stratum_target *wait_target;
- ptid_t wait_ptid;
- struct target_waitstatus wait_status;
+ thread_info *cur_thr = inferior_thread ();
- /* Get the last target status returned by target_wait(). */
- get_last_target_status (&wait_target, &wait_ptid, &wait_status);
-
- /* If not stopped at a fork event, then there's nothing else to
- do. */
- if (wait_status.kind () != TARGET_WAITKIND_FORKED
- && wait_status.kind () != TARGET_WAITKIND_VFORKED)
- return 1;
+ 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;
+ }
}
}
}
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;
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
static void
handle_vfork_child_exec_or_exit (int exec)
{
+ INFRUN_SCOPED_DEBUG_ENTER_EXIT;
+
struct inferior *inf = current_inferior ();
if (inf->vfork_parent)
static void
handle_vfork_done (thread_info *event_thread)
{
+ INFRUN_SCOPED_DEBUG_ENTER_EXIT;
+
/* 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.
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.
previous incarnation of this process. */
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 (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 ();
}
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
thread. */
clear_step_over_info ();
- inf->thread_waiting_for_vfork_done = nullptr;
+ follow_inf->thread_waiting_for_vfork_done = nullptr;
}
/* If ON, and the architecture supports it, GDB will use displaced
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
struct target_waitstatus ws;
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;
: 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
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). */
- if (sched_multi)
+ thread_info *thr = find_thread_waiting_for_vfork_done ();
+ if (thr != nullptr)
{
- for (inferior *inf : all_non_exited_inferiors ())
- if (inf->thread_waiting_for_vfork_done != nullptr)
- return inf->thread_waiting_for_vfork_done->ptid;
+ /* 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;
}
- else if (current_inferior ()->thread_waiting_for_vfork_done != nullptr)
- return current_inferior ()->thread_waiting_for_vfork_done->ptid;
return user_visible_resume_ptid (user_step);
}
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;
}
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.stop_step = 0;
+ tp->control.proceed_to_finish = 0;
+
tp->control.stepping_command = 0;
/* Discard any remaining commands or status from previous stop. */
bpstat_clear (&tp->control.stop_bpstat);
}
+/* Notify the current interpreter and observers that the target is about to
+ proceed. */
+
+static void
+notify_about_to_proceed ()
+{
+ top_level_interpreter ()->on_about_to_proceed ();
+ gdb::observers::about_to_proceed.notify ();
+}
+
void
clear_proceed_status (int step)
{
inferior->control.stop_soon = NO_STOP_QUIETLY;
}
- gdb::observers::about_to_proceed.notify ();
+ notify_about_to_proceed ();
}
/* Returns true if TP is still stopped at a breakpoint that needs
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
}
}
+/* Helper function for `proceed`. Check if thread TP is suitable for
+ resuming, and, if it is, switch to the thread and call
+ `keep_going_pass_signal`. If TP is not suitable for resuming then this
+ function will just return without switching threads. */
+
+static void
+proceed_resume_thread_checked (thread_info *tp)
+{
+ if (!tp->inf->has_execution ())
+ {
+ infrun_debug_printf ("[%s] target has no execution",
+ tp->ptid.to_string ().c_str ());
+ return;
+ }
+
+ if (tp->resumed ())
+ {
+ infrun_debug_printf ("[%s] resumed",
+ tp->ptid.to_string ().c_str ());
+ gdb_assert (tp->executing () || tp->has_pending_waitstatus ());
+ return;
+ }
+
+ if (thread_is_in_step_over_chain (tp))
+ {
+ infrun_debug_printf ("[%s] needs step-over",
+ tp->ptid.to_string ().c_str ());
+ return;
+ }
+
+ /* When handling a vfork GDB removes all breakpoints from the program
+ space in which the vfork is being handled. If we are following the
+ parent then GDB will set the thread_waiting_for_vfork_done member of
+ the parent inferior. In this case we should take care to only resume
+ the vfork parent thread, the kernel will hold this thread suspended
+ until the vfork child has exited or execd, at which point the parent
+ will be resumed and a VFORK_DONE event sent to GDB. */
+ if (tp->inf->thread_waiting_for_vfork_done != nullptr)
+ {
+ if (target_is_non_stop_p ())
+ {
+ /* For non-stop targets, regardless of whether GDB is using
+ all-stop or non-stop mode, threads are controlled
+ individually.
+
+ When a thread is handling a vfork, breakpoints are removed
+ from the inferior (well, program space in fact), so it is
+ critical that we don't try to resume any thread other than the
+ vfork parent. */
+ if (tp != tp->inf->thread_waiting_for_vfork_done)
+ {
+ infrun_debug_printf ("[%s] thread %s of this inferior is "
+ "waiting for vfork-done",
+ tp->ptid.to_string ().c_str (),
+ tp->inf->thread_waiting_for_vfork_done
+ ->ptid.to_string ().c_str ());
+ return;
+ }
+ }
+ else
+ {
+ /* For all-stop targets, when we attempt to resume the inferior,
+ we will only resume the vfork parent thread, this is handled
+ in internal_resume_ptid.
+
+ Additionally, we will always be called with the vfork parent
+ thread as the current thread (TP) thanks to follow_fork, as
+ such the following assertion should hold.
+
+ Beyond this there is nothing more that needs to be done
+ here. */
+ gdb_assert (tp == tp->inf->thread_waiting_for_vfork_done);
+ }
+ }
+
+ /* When handling a vfork GDB removes all breakpoints from the program
+ space in which the vfork is being handled. If we are following the
+ child then GDB will set vfork_child member of the vfork parent
+ inferior. Once the child has either exited or execd then GDB will
+ detach from the parent process. Until that point GDB should not
+ resume any thread in the parent process. */
+ if (tp->inf->vfork_child != nullptr)
+ {
+ infrun_debug_printf ("[%s] thread is part of a vfork parent, child is %d",
+ tp->ptid.to_string ().c_str (),
+ tp->inf->vfork_child->pid);
+ return;
+ }
+
+ infrun_debug_printf ("resuming %s",
+ tp->ptid.to_string ().c_str ());
+
+ execution_control_state ecs (tp);
+ switch_to_thread (tp);
+ keep_going_pass_signal (&ecs);
+ if (!ecs.wait_some_more)
+ error (_("Command aborted."));
+}
+
/* Basic routine for continuing the program in various fashions.
ADDR is the address to resume at, or -1 for resume where stopped.
struct gdbarch *gdbarch;
CORE_ADDR pc;
- /* If we're stopped at a fork/vfork, follow the branch set by the
- "set follow-fork-mode" command; otherwise, we'll just proceed
+ /* If we're stopped at a fork/vfork, switch to either the parent or child
+ thread as defined by the "set follow-fork-mode" command, or, if both
+ the parent and child are controlled by GDB, and schedule-multiple is
+ on, follow the child. If none of the above apply then we just proceed
resuming the current thread. */
if (!follow_fork ())
{
}
/* 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 ();
thread_info *cur_thr = inferior_thread ();
+ infrun_debug_printf ("cur_thr = %s", cur_thr->ptid.to_string ().c_str ());
+
/* Fill in with reasonable starting values. */
init_thread_stepping_state (cur_thr);
if (!cur_thr->control.in_infcall)
set_running (resume_target, resume_ptid, true);
- infrun_debug_printf ("addr=%s, signal=%s", paddress (gdbarch, addr),
- gdb_signal_to_symbol_string (siggnal));
+ infrun_debug_printf ("addr=%s, signal=%s, resume_ptid=%s",
+ paddress (gdbarch, addr),
+ gdb_signal_to_symbol_string (siggnal),
+ resume_ptid.to_string ().c_str ());
annotate_starting ();
resume_ptid))
{
switch_to_thread_no_regs (tp);
-
- if (!tp->inf->has_execution ())
- {
- infrun_debug_printf ("[%s] target has no execution",
- tp->ptid.to_string ().c_str ());
- continue;
- }
-
- if (tp->resumed ())
- {
- infrun_debug_printf ("[%s] resumed",
- tp->ptid.to_string ().c_str ());
- gdb_assert (tp->executing () || tp->has_pending_waitstatus ());
- continue;
- }
-
- if (thread_is_in_step_over_chain (tp))
- {
- infrun_debug_printf ("[%s] needs step-over",
- tp->ptid.to_string ().c_str ());
- 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 ());
-
- execution_control_state ecs (tp);
- switch_to_thread (tp);
- keep_going_pass_signal (&ecs);
- if (!ecs.wait_some_more)
- error (_("Command aborted."));
+ proceed_resume_thread_checked (tp);
}
}
- 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. */
- execution_control_state ecs (cur_thr);
- switch_to_thread (cur_thr);
- keep_going_pass_signal (&ecs);
- if (!ecs.wait_some_more)
- error (_("Command aborted."));
- }
+ else
+ proceed_resume_thread_checked (cur_thr);
disable_commit_resumed.reset_and_commit ();
}
nullify_last_target_wait_ptid ();
- previous_inferior_ptid = inferior_ptid;
+ update_previous_thread ();
}
\f
}
}
-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. */
ptid.to_string ().c_str ());
/* We have a specific thread to check. */
- tp = find_thread_ptid (inf, ptid);
+ tp = inf->find_thread (ptid);
gdb_assert (tp != nullptr);
if (!tp->has_pending_waitstatus ())
tp = nullptr;
&& !gdb_in_secondary_prompt_p (ui))
{
target_terminal::ours ();
- gdb::observers::sync_execution_done.notify ();
+ 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
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 (); };
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);
auto defer_delete_threads
= make_scope_exit (delete_just_stopped_threads_infrun_breakpoints);
+ int stop_id = get_stop_id ();
+
/* Now figure out what to do with the result of the result. */
handle_inferior_event (&ecs);
clean_up_just_stopped_threads_fsms (&ecs);
- if (thr != nullptr && thr->thread_fsm () != nullptr)
+ if (stop_id != get_stop_id ())
+ {
+ /* If the stop-id has changed then a stop has already been
+ presented to the user in handle_inferior_event, this is
+ likely a failed inferior call. As the stop has already
+ been announced then we should not notify again.
+
+ Also, if the prompt state is not PROMPT_NEEDED then GDB
+ will not be ready for user input after this function. */
+ should_notify_stop = false;
+ gdb_assert (current_ui->prompt_state == PROMPT_NEEDED);
+ }
+ else if (thr != nullptr && thr->thread_fsm () != nullptr)
should_notify_stop
= thr->thread_fsm ()->should_notify_stop ();
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);
}
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);
+ thread_info *t = event.target->find_thread (event.ptid);
if (t == nullptr)
t = add_thread (event.target, event.ptid);
/* 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. */
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 == nullptr)
ecs->event_thread = add_thread (ecs->target, ecs->ptid);
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
/* 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
{
- struct gdbarch *gdbarch = current_inferior ()->gdbarch;
+ struct gdbarch *gdbarch = current_inferior ()->arch ();
if (gdbarch_gdb_signal_to_target_p (gdbarch))
{
"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);
/* 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. */
if (should_resume)
{
/* Never call switch_back_to_stepped_thread if we are waiting for
- vfork-done (waiting for an external vfork child to exec or
+ 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 (handle_stop_requested (ecs))
return;
- gdb::observers::no_history.notify ();
+ interps_notify_no_history ();
stop_waiting (ecs);
return;
}
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;
}
+/* 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
{
/* 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 ();
}
case BPSTAT_WHAT_STEP_RESUME:
infrun_debug_printf ("BPSTAT_WHAT_STEP_RESUME");
- delete_step_resume_breakpoint (ecs->event_thread);
- fill_in_stop_func (gdbarch, ecs);
- if (execution_direction == EXEC_REVERSE)
+ delete_step_resume_breakpoint (ecs->event_thread);
+ if (ecs->event_thread->control.proceed_to_finish
+ && execution_direction == EXEC_REVERSE)
{
struct thread_info *tp = ecs->event_thread;
- /* We are finishing a function in reverse or stepping over a function
- call in reverse, and just hit the step-resume breakpoint at the
- start address of the function, and we're almost there -- just need
- to back up to the function call. */
-
- stop_pc_sal = find_pc_line (ecs->event_thread->stop_pc (), 0);
- /* When setting a step range, need to call set_step_info
- to setup the current_line/symtab fields as well. */
- set_step_info (tp, frame, stop_pc_sal);
-
- /* Return using a step range so we will keep stepping back to the
- first instruction in the source code line. */
- tp->control.step_range_start = ecs->stop_func_start;
- tp->control.step_range_end = ecs->stop_func_start;
+ /* We are finishing a function in reverse, and just hit the
+ step-resume breakpoint at the start address of the
+ function, and we're almost there -- just need to back up
+ by one more single-step, which should take us back to the
+ function call. */
+ tp->control.step_range_start = tp->control.step_range_end = 1;
+ keep_going (ecs);
+ return;
+ }
+ fill_in_stop_func (gdbarch, ecs);
+ if (ecs->event_thread->stop_pc () == ecs->stop_func_start
+ && execution_direction == EXEC_REVERSE)
+ {
+ /* We are stepping over a function call in reverse, and just
+ hit the step-resume breakpoint at the start address of
+ the function. Go back to single-stepping, which should
+ take us back to the function call. */
+ ecs->event_thread->stepping_over_breakpoint = 1;
keep_going (ecs);
return;
}
= ecs->event_thread->control.step_resume_breakpoint;
if (sr_bp != nullptr
- && sr_bp->loc->permanent
+ && 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 (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 single-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
return;
}
else if (get_frame_id (get_current_frame ())
- == ecs->event_thread->control.step_frame_id)
+ == ecs->event_thread->control.step_frame_id)
{
/* We are not at the start of a statement, and we have not changed
frame.
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 = 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)
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
try
{
const struct block *b;
- struct block_iterator iter;
- struct symbol *sym;
int argno = 0;
/* The exception breakpoint is a thread-specific breakpoint on
handler. */
b = func->value_block ();
- ALL_BLOCK_SYMBOLS (b, iter, sym)
+ for (struct symbol *sym : block_iterator_range (b))
{
if (!sym->is_argument ())
continue;
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)
{
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 ();
- gdb_printf (_("[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)
{
execute_cmd_pre_hook (stop_command);
}
- catch (const gdb_exception &ex)
+ catch (const gdb_exception_error &ex)
{
exception_fprintf (gdb_stderr, ex,
"Error while running hook_stop:\n");
/* 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 (nullptr, 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 ())
target_read (current_inferior ()->top_target (),
TARGET_OBJECT_SIGNAL_INFO,
nullptr,
- value_contents_all_raw (v).data (),
- value_offset (v),
- value_type (v)->length ());
+ v->contents_all_raw ().data (),
+ v->offset (),
+ v->type ()->length ());
- if (transferred != value_type (v)->length ())
+ if (transferred != v->type ()->length ())
error (_("Unable to read siginfo"));
}
transferred = target_write (current_inferior ()->top_target (),
TARGET_OBJECT_SIGNAL_INFO,
nullptr,
- value_contents_all_raw (fromval).data (),
- value_offset (v),
- value_type (fromval)->length ());
+ fromval->contents_all_raw ().data (),
+ v->offset (),
+ fromval->type ()->length ());
- if (transferred != value_type (fromval)->length ())
+ 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, nullptr);
+ 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
static void
infrun_thread_ptid_changed ()
{
- gdbarch *arch = current_inferior ()->gdbarch;
+ gdbarch *arch = current_inferior ()->arch ();
/* The thread which inferior_ptid represents changes ptid. */
{
"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");