#include "defs.h"
#include "infrun.h"
-#include <string.h>
#include <ctype.h>
#include "symtab.h"
#include "frame.h"
#include "inferior.h"
-#include "exceptions.h"
#include "breakpoint.h"
#include "gdb_wait.h"
#include "gdbcore.h"
#include "main.h"
#include "dictionary.h"
#include "block.h"
-#include "gdb_assert.h"
#include "mi/mi-common.h"
#include "event-top.h"
#include "record.h"
#include "completer.h"
#include "target-descriptions.h"
#include "target-dcache.h"
+#include "terminal.h"
/* Prototypes for local functions */
static int follow_fork (void);
+static int follow_fork_inferior (int follow_child, int detach_fork);
+
+static void follow_inferior_reset_breakpoints (void);
+
static void set_schedlock_func (char *args, int from_tty,
struct cmd_list_element *c);
}
\f
+/* Handle changes to the inferior list based on the type of fork,
+ which process is being followed, and whether the other process
+ should be detached. On entry inferior_ptid must be the ptid of
+ the fork parent. At return inferior_ptid is the ptid of the
+ followed inferior. */
+
+static int
+follow_fork_inferior (int follow_child, int detach_fork)
+{
+ int has_vforked;
+ int parent_pid, child_pid;
+
+ has_vforked = (inferior_thread ()->pending_follow.kind
+ == TARGET_WAITKIND_VFORKED);
+ parent_pid = ptid_get_lwp (inferior_ptid);
+ if (parent_pid == 0)
+ parent_pid = ptid_get_pid (inferior_ptid);
+ child_pid
+ = ptid_get_pid (inferior_thread ()->pending_follow.value.related_pid);
+
+ if (has_vforked
+ && !non_stop /* Non-stop always resumes both branches. */
+ && (!target_is_async_p () || sync_execution)
+ && !(follow_child || detach_fork || sched_multi))
+ {
+ /* The parent stays blocked inside the vfork syscall until the
+ child execs or exits. If we don't let the child run, then
+ 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, _("\
+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"));
+ /* FIXME output string > 80 columns. */
+ return 1;
+ }
+
+ if (!follow_child)
+ {
+ /* Detach new forked process? */
+ if (detach_fork)
+ {
+ struct cleanup *old_chain;
+
+ /* Before detaching from the child, remove all breakpoints
+ from it. If we forked, then this has already been taken
+ care of by infrun.c. If we vforked however, any
+ breakpoint inserted in the parent is visible in the
+ child, even those added while stopped in a vfork
+ catchpoint. This will remove the breakpoints from the
+ parent also, but they'll be reinserted below. */
+ if (has_vforked)
+ {
+ /* Keep breakpoints list in sync. */
+ remove_breakpoints_pid (ptid_get_pid (inferior_ptid));
+ }
+
+ if (info_verbose || debug_infrun)
+ {
+ target_terminal_ours ();
+ fprintf_filtered (gdb_stdlog,
+ "Detaching after fork from "
+ "child process %d.\n",
+ child_pid);
+ }
+ }
+ else
+ {
+ struct inferior *parent_inf, *child_inf;
+ struct cleanup *old_chain;
+
+ /* Add process to GDB's tables. */
+ child_inf = add_inferior (child_pid);
+
+ parent_inf = current_inferior ();
+ 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);
+
+ old_chain = save_inferior_ptid ();
+ save_current_program_space ();
+
+ inferior_ptid = ptid_build (child_pid, child_pid, 0);
+ add_thread (inferior_ptid);
+ child_inf->symfile_flags = SYMFILE_NO_READ;
+
+ /* If this is a vfork child, then the address-space is
+ shared with the parent. */
+ if (has_vforked)
+ {
+ child_inf->pspace = parent_inf->pspace;
+ child_inf->aspace = parent_inf->aspace;
+
+ /* The parent will be frozen until the child is done
+ with the shared region. Keep track of the
+ parent. */
+ child_inf->vfork_parent = parent_inf;
+ child_inf->pending_detach = 0;
+ parent_inf->vfork_child = child_inf;
+ parent_inf->pending_detach = 0;
+ }
+ else
+ {
+ child_inf->aspace = new_address_space ();
+ child_inf->pspace = add_program_space (child_inf->aspace);
+ child_inf->removable = 1;
+ set_current_program_space (child_inf->pspace);
+ clone_program_space (child_inf->pspace, parent_inf->pspace);
+
+ /* Let the shared library layer (e.g., solib-svr4) learn
+ about this new process, relocate the cloned exec, pull
+ in shared libraries, and install the solib event
+ breakpoint. If a "cloned-VM" event was propagated
+ better throughout the core, this wouldn't be
+ required. */
+ solib_create_inferior_hook (0);
+ }
+
+ do_cleanups (old_chain);
+ }
+
+ if (has_vforked)
+ {
+ struct inferior *parent_inf;
+
+ parent_inf = current_inferior ();
+
+ /* If we detached from the child, then we have to be careful
+ to not insert breakpoints in the parent until the child
+ is done with the shared memory region. However, if we're
+ staying attached to the child, then we can and should
+ 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->pspace->breakpoints_not_allowed = detach_fork;
+ }
+ }
+ else
+ {
+ /* Follow the child. */
+ struct inferior *parent_inf, *child_inf;
+ struct program_space *parent_pspace;
+
+ if (info_verbose || debug_infrun)
+ {
+ target_terminal_ours ();
+ if (has_vforked)
+ fprintf_filtered (gdb_stdlog,
+ _("Attaching after process %d "
+ "vfork to child process %d.\n"),
+ parent_pid, child_pid);
+ else
+ fprintf_filtered (gdb_stdlog,
+ _("Attaching after process %d "
+ "fork to child process %d.\n"),
+ parent_pid, child_pid);
+ }
+
+ /* Add the new inferior first, so that the target_detach below
+ doesn't unpush the target. */
+
+ child_inf = add_inferior (child_pid);
+
+ parent_inf = current_inferior ();
+ 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);
+
+ parent_pspace = parent_inf->pspace;
+
+ /* If we're vforking, we want to hold on to the parent until the
+ child exits or execs. At child exec or exit time we can
+ remove the old breakpoints from the parent and detach or
+ resume debugging it. Otherwise, detach the parent now; we'll
+ want to reuse it's program/address spaces, but we can't set
+ them to the child before removing breakpoints from the
+ parent, otherwise, the breakpoints module could decide to
+ remove breakpoints from the wrong process (since they'd be
+ assigned to the same address space). */
+
+ if (has_vforked)
+ {
+ gdb_assert (child_inf->vfork_parent == NULL);
+ gdb_assert (parent_inf->vfork_child == NULL);
+ child_inf->vfork_parent = parent_inf;
+ child_inf->pending_detach = 0;
+ parent_inf->vfork_child = child_inf;
+ parent_inf->pending_detach = detach_fork;
+ parent_inf->waiting_for_vfork_done = 0;
+ }
+ else if (detach_fork)
+ target_detach (NULL, 0);
+
+ /* Note that the detach above makes PARENT_INF dangling. */
+
+ /* Add the child thread to the appropriate lists, and switch to
+ this new thread, before cloning the program space, and
+ informing the solib layer about this new process. */
+
+ inferior_ptid = ptid_build (child_pid, child_pid, 0);
+ add_thread (inferior_ptid);
+
+ /* If this is a vfork child, then the address-space is shared
+ with the parent. If we detached from the parent, then we can
+ reuse the parent's program/address spaces. */
+ if (has_vforked || detach_fork)
+ {
+ child_inf->pspace = parent_pspace;
+ child_inf->aspace = child_inf->pspace->aspace;
+ }
+ else
+ {
+ child_inf->aspace = new_address_space ();
+ child_inf->pspace = add_program_space (child_inf->aspace);
+ child_inf->removable = 1;
+ child_inf->symfile_flags = SYMFILE_NO_READ;
+ set_current_program_space (child_inf->pspace);
+ clone_program_space (child_inf->pspace, parent_pspace);
+
+ /* Let the shared library layer (e.g., solib-svr4) learn
+ about this new process, relocate the cloned exec, pull in
+ shared libraries, and install the solib event breakpoint.
+ If a "cloned-VM" event was propagated better throughout
+ the core, this wouldn't be required. */
+ solib_create_inferior_hook (0);
+ }
+ }
+
+ return target_follow_fork (follow_child, detach_fork);
+}
+
/* 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. */
parent = inferior_ptid;
child = tp->pending_follow.value.related_pid;
- /* Tell the target to do whatever is necessary to follow
- either parent or child. */
- if (target_follow_fork (follow_child, detach_fork))
+ /* 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_fork_inferior (follow_child, detach_fork))
{
/* Target refused to follow, or there's some other reason
we shouldn't resume. */
return should_resume;
}
-void
+static void
follow_inferior_reset_breakpoints (void)
{
struct thread_info *tp = inferior_thread ();
/* Was there a step_resume breakpoint? (There was if the user
did a "next" at the fork() call.) If so, explicitly reset its
- thread number.
+ thread number. Cloned step_resume breakpoints are disabled on
+ creation, so enable it here now that it is associated with the
+ correct thread.
step_resumes are a form of bp that are made to be per-thread.
Since we created the step_resume bp when the parent process
it is for, or it'll be ignored when it triggers. */
if (tp->control.step_resume_breakpoint)
- breakpoint_re_set_thread (tp->control.step_resume_breakpoint);
+ {
+ breakpoint_re_set_thread (tp->control.step_resume_breakpoint);
+ tp->control.step_resume_breakpoint->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);
+ {
+ breakpoint_re_set_thread (tp->control.exception_resume_breakpoint);
+ tp->control.exception_resume_breakpoint->loc->enabled = 1;
+ }
/* Reinsert all breakpoints in the child. The user may have set
breakpoints after catching the fork, in which case those
target_pid_to_str (thread->ptid));
switch_to_thread (thread->ptid);
- clear_proceed_status ();
+ clear_proceed_status (0);
proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT, 0);
}
step_over_info.address = 0;
}
-/* See inferior.h. */
+/* See infrun.h. */
int
stepping_past_instruction_at (struct address_space *aspace,
return hw_step;
}
-/* Return a ptid representing the set of threads that we will proceed,
- in the perspective of the user/frontend. We may actually resume
- fewer threads at first, e.g., if a thread is stopped at a
- breakpoint that needs stepping-off, but that should not be visible
- to the user/frontend, and neither should the frontend/user be
- allowed to proceed any of the threads that happen to be stopped for
- internal run control handling, if a previous command wanted them
- resumed. */
-
ptid_t
user_visible_resume_ptid (int step)
{
resume_ptid = inferior_ptid;
}
else if ((scheduler_mode == schedlock_on)
- || (scheduler_mode == schedlock_step
- && (step || singlestep_breakpoints_inserted_p)))
+ || (scheduler_mode == schedlock_step && step))
{
/* User-settable 'scheduler' mode requires solo thread resume. */
resume_ptid = inferior_ptid;
}
+ /* We may actually resume fewer threads at first, e.g., if a thread
+ is stopped at a breakpoint that needs stepping-off, but that
+ should not be visible to the user/frontend, and neither should
+ the frontend/user be allowed to proceed any of the threads that
+ happen to be stopped for internal run control handling, if a
+ previous command wanted them resumed. */
return resume_ptid;
}
"infrun: clear_proceed_status_thread (%s)\n",
target_pid_to_str (tp->ptid));
+ /* If this signal should not be seen by program, give it zero.
+ Used for debugging signals. */
+ if (!signal_pass_state (tp->suspend.stop_signal))
+ tp->suspend.stop_signal = GDB_SIGNAL_0;
+
tp->control.trap_expected = 0;
tp->control.step_range_start = 0;
tp->control.step_range_end = 0;
bpstat_clear (&tp->control.stop_bpstat);
}
-static int
-clear_proceed_status_callback (struct thread_info *tp, void *data)
-{
- if (is_exited (tp->ptid))
- return 0;
-
- clear_proceed_status_thread (tp);
- return 0;
-}
-
void
-clear_proceed_status (void)
+clear_proceed_status (int step)
{
if (!non_stop)
{
- /* In all-stop mode, delete the per-thread status of all
- threads, even if inferior_ptid is null_ptid, there may be
- threads on the list. E.g., we may be launching a new
- process, while selecting the executable. */
- iterate_over_threads (clear_proceed_status_callback, NULL);
+ struct thread_info *tp;
+ ptid_t resume_ptid;
+
+ resume_ptid = user_visible_resume_ptid (step);
+
+ /* In all-stop mode, delete the per-thread status of all threads
+ we're about to resume, implicitly and explicitly. */
+ ALL_NON_EXITED_THREADS (tp)
+ {
+ if (!ptid_match (tp->ptid, resume_ptid))
+ continue;
+ clear_proceed_status_thread (tp);
+ }
}
if (!ptid_equal (inferior_ptid, null_ptid))
return NULL;
}
- ALL_THREADS (tp)
+ ALL_NON_EXITED_THREADS (tp)
{
/* Ignore the EXCEPT thread. */
if (tp == except)
regcache_write_pc (regcache, addr);
}
+ if (siggnal != GDB_SIGNAL_DEFAULT)
+ tp->suspend.stop_signal = siggnal;
+
/* Record the interpreter that issued the execution command that
caused this thread to resume. If the top level interpreter is
MI/async, and the execution command was a CLI command
tp->control.trap_expected = tp->stepping_over_breakpoint;
- if (!non_stop)
- {
- /* Pass the last stop signal to the thread we're resuming,
- irrespective of whether the current thread is the thread that
- got the last event or not. This was historically GDB's
- behaviour before keeping a stop_signal per thread. */
-
- struct thread_info *last_thread;
- ptid_t last_ptid;
- struct target_waitstatus last_status;
-
- get_last_target_status (&last_ptid, &last_status);
- if (!ptid_equal (inferior_ptid, last_ptid)
- && !ptid_equal (last_ptid, null_ptid)
- && !ptid_equal (last_ptid, minus_one_ptid))
- {
- last_thread = find_thread_ptid (last_ptid);
- if (last_thread)
- {
- tp->suspend.stop_signal = last_thread->suspend.stop_signal;
- last_thread->suspend.stop_signal = GDB_SIGNAL_0;
- }
- }
- }
-
- if (siggnal != GDB_SIGNAL_DEFAULT)
- tp->suspend.stop_signal = siggnal;
- /* If this signal should not be seen by program,
- give it zero. Used for debugging signals. */
- else if (!signal_program[tp->suspend.stop_signal])
- tp->suspend.stop_signal = GDB_SIGNAL_0;
-
annotate_starting ();
/* Make sure that output from GDB appears before output from the
breakpoint_init_inferior (inf_starting);
- clear_proceed_status ();
+ clear_proceed_status (0);
target_last_wait_ptid = minus_one_ptid;
{
/* Loading of shared libraries might have changed breakpoint
addresses. Make sure new breakpoints are inserted. */
- if (stop_soon == NO_STOP_QUIETLY
- && !breakpoints_always_inserted_mode ())
+ if (stop_soon == NO_STOP_QUIETLY)
insert_breakpoints ();
resume (0, GDB_SIGNAL_0);
prepare_to_wait (ecs);
watchpoint expression. We do this by single-stepping the
target.
- It may not be necessary to disable the watchpoint to stop over
+ It may not be necessary to disable the watchpoint to step over
it. For example, the PA can (with some kernel cooperation)
single step over a watchpoint without disabling the watchpoint.
if (random_signal)
{
/* Signal not for debugging purposes. */
- int printed = 0;
struct inferior *inf = find_inferior_pid (ptid_get_pid (ecs->ptid));
enum gdb_signal stop_signal = ecs->event_thread->suspend.stop_signal;
stopped_by_random_signal = 1;
- if (signal_print[ecs->event_thread->suspend.stop_signal])
- {
- /* The signal table tells us to print about this signal. */
- printed = 1;
- target_terminal_ours_for_output ();
- observer_notify_signal_received (ecs->event_thread->suspend.stop_signal);
- }
/* Always stop on signals if we're either just gaining control
of the program, or the user explicitly requested this thread
to remain stopped. */
stop_waiting (ecs);
return;
}
- /* If not going to stop, give terminal back
- if we took it away. */
- else if (printed)
- target_terminal_inferior ();
+
+ /* Notify observers the signal has "handle print" set. Note we
+ returned early above if stopping; normal_stop handles the
+ printing in that case. */
+ if (signal_print[ecs->event_thread->suspend.stop_signal])
+ {
+ /* The signal table tells us to print about this signal. */
+ target_terminal_ours_for_output ();
+ observer_notify_signal_received (ecs->event_thread->suspend.stop_signal);
+ target_terminal_inferior ();
+ }
/* Clear the signal if it should not be passed. */
if (signal_program[ecs->event_thread->suspend.stop_signal] == 0)
if (what.is_longjmp)
{
- check_longjmp_breakpoint_for_call_dummy (ecs->event_thread->num);
+ check_longjmp_breakpoint_for_call_dummy (ecs->event_thread);
if (!frame_id_p (ecs->event_thread->initiating_frame))
{
what keep_going does as well, if we call it. */
ecs->event_thread->control.trap_expected = 0;
+ /* Likewise, clear the signal if it should not be passed. */
+ if (!signal_program[ecs->event_thread->suspend.stop_signal])
+ ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0;
+
/* If scheduler locking applies even if not stepping, there's no
need to walk over threads. Above we've checked whether the
current thread is stepping. If some other thread not the
step/next/etc. */
stepping_thread = NULL;
step_over = NULL;
- ALL_THREADS (tp)
+ ALL_NON_EXITED_THREADS (tp)
{
/* Ignore threads of processes we're not resuming. */
if (!sched_multi
static void
insert_exception_resume_breakpoint (struct thread_info *tp,
- struct block *b,
+ const struct block *b,
struct frame_info *frame,
struct symbol *sym)
{
TRY_CATCH (e, RETURN_MASK_ERROR)
{
- struct block *b;
+ const struct block *b;
struct block_iterator iter;
struct symbol *sym;
int argno = 0;
}
/* We are done with the step range of a step/next/si/ni command.
- Called once for each n of a "step n" operation. Notify observers
- if not in the middle of doing a "step N" operation for N > 1. */
+ Called once for each n of a "step n" operation. */
static void
end_stepping_range (struct execution_control_state *ecs)
{
ecs->event_thread->control.stop_step = 1;
- if (!ecs->event_thread->step_multi)
- observer_notify_end_stepping_range ();
stop_waiting (ecs);
}
&& last.kind != TARGET_WAITKIND_NO_RESUMED)
make_cleanup (finish_thread_state_cleanup, &inferior_ptid);
+ /* As we're presenting a stop, and potentially removing breakpoints,
+ update the thread list so we can tell whether there are threads
+ running on the target. With target remote, for example, we can
+ only learn about new threads when we explicitly update the thread
+ list. Do this before notifying the interpreters about signal
+ stops, end of stepping ranges, etc., so that the "new thread"
+ output is emitted before e.g., "Program received signal FOO",
+ instead of after. */
+ update_thread_list ();
+
+ if (last.kind == TARGET_WAITKIND_STOPPED && stopped_by_random_signal)
+ observer_notify_signal_received (inferior_thread ()->suspend.stop_signal);
+
/* As with the notification of thread events, we want to delay
notifying the user that we've switched thread context until
the inferior actually stops.
printf_filtered (_("No unwaited-for children left.\n"));
}
- if (!breakpoints_always_inserted_mode () && target_has_execution)
+ /* Note: this depends on the update_thread_list call above. */
+ if (!breakpoints_should_be_inserted_now () && target_has_execution)
{
if (remove_breakpoints ())
{
if (stopped_by_random_signal)
disable_current_display ();
- /* Don't print a message if in the middle of doing a "step n"
- operation for n > 1 */
+ /* Notify observers if we finished a "step"-like command, etc. */
if (target_has_execution
&& last.kind != TARGET_WAITKIND_SIGNALLED
&& last.kind != TARGET_WAITKIND_EXITED
- && inferior_thread ()->step_multi
&& inferior_thread ()->control.stop_step)
- goto done;
+ {
+ /* But not if in the middle of doing a "step n" operation for
+ n > 1 */
+ if (inferior_thread ()->step_multi)
+ goto done;
+
+ observer_notify_end_stepping_range ();
+ }
target_terminal_ours ();
async_enable_stdin ();
return make_cleanup (restore_inferior_ptid, saved_ptid_ptr);
}
-/* See inferior.h. */
+/* See infrun.h. */
void
clear_exit_convenience_vars (void)
signal_catch = (unsigned char *)
xmalloc (sizeof (signal_catch[0]) * numsigs);
signal_pass = (unsigned char *)
- xmalloc (sizeof (signal_program[0]) * numsigs);
+ xmalloc (sizeof (signal_pass[0]) * numsigs);
for (i = 0; i < numsigs; i++)
{
signal_stop[i] = 1;