/* Low-level child interface to ttrace.
- Copyright 2004 Free Software Foundation, Inc.
+ Copyright 2004, 2005 Free Software Foundation, Inc.
This file is part of GDB.
}
\f
+/* When tracking a vfork(2), we cannot detach from the parent until
+ after the child has called exec(3) or has exited. If we are still
+ attached to the parent, this variable will be set to the process ID
+ of the parent. Otherwise it will be set to zero. */
+static pid_t inf_ttrace_vfork_ppid = -1;
+
+static int
+inf_ttrace_follow_fork (struct target_ops *ops, int follow_child)
+{
+ pid_t pid, fpid;
+ lwpid_t lwpid, flwpid;
+ ttstate_t tts;
+
+ /* FIXME: kettenis/20050720: This stuff should really be passed as
+ an argument by our caller. */
+ {
+ ptid_t ptid;
+ struct target_waitstatus status;
+
+ get_last_target_status (&ptid, &status);
+ gdb_assert (status.kind == TARGET_WAITKIND_FORKED
+ || status.kind == TARGET_WAITKIND_VFORKED);
+
+ pid = ptid_get_pid (ptid);
+ lwpid = ptid_get_lwp (ptid);
+ }
+
+ /* Get all important details that core GDB doesn't (and shouldn't)
+ know about. */
+ if (ttrace (TT_LWP_GET_STATE, pid, lwpid,
+ (uintptr_t)&tts, sizeof tts, 0) == -1)
+ perror_with_name (("ttrace"));
+
+ gdb_assert (tts.tts_event == TTEVT_FORK || tts.tts_event == TTEVT_VFORK);
+
+ if (tts.tts_u.tts_fork.tts_isparent)
+ {
+ pid = tts.tts_pid;
+ lwpid = tts.tts_lwpid;
+ fpid = tts.tts_u.tts_fork.tts_fpid;
+ flwpid = tts.tts_u.tts_fork.tts_flwpid;
+ }
+ else
+ {
+ pid = tts.tts_u.tts_fork.tts_fpid;
+ lwpid = tts.tts_u.tts_fork.tts_flwpid;
+ fpid = tts.tts_pid;
+ flwpid = tts.tts_lwpid;
+ }
+
+ if (follow_child)
+ {
+ inferior_ptid = ptid_build (fpid, flwpid, 0);
+ detach_breakpoints (pid);
+
+ target_terminal_ours ();
+ fprintf_unfiltered (gdb_stdlog, _("\
+Attaching after fork to child process %ld.\n"), (long)fpid);
+ }
+ else
+ {
+ inferior_ptid = ptid_build (pid, lwpid, 0);
+ detach_breakpoints (fpid);
+
+ target_terminal_ours ();
+ fprintf_unfiltered (gdb_stdlog, _("\
+Detaching after fork from child process %ld.\n"), (long)fpid);
+ }
+
+ if (tts.tts_event == TTEVT_VFORK)
+ {
+ gdb_assert (!tts.tts_u.tts_fork.tts_isparent);
+
+ if (follow_child)
+ {
+ /* We can't detach from the parent yet. */
+ inf_ttrace_vfork_ppid = pid;
+
+ reattach_breakpoints (fpid);
+ }
+ else
+ {
+ if (ttrace (TT_PROC_DETACH, fpid, 0, 0, 0, 0) == -1)
+ perror_with_name (("ttrace"));
+
+ /* Wait till we get the TTEVT_VFORK event in the parent.
+ This indicates that the child has called exec(3) or has
+ exited and that the parent is ready to be traced again. */
+ if (ttrace_wait (pid, lwpid, TTRACE_WAITOK, &tts, sizeof tts) == -1)
+ perror_with_name (("ttrace_wait"));
+ gdb_assert (tts.tts_event == TTEVT_VFORK);
+ gdb_assert (tts.tts_u.tts_fork.tts_isparent);
+
+ reattach_breakpoints (pid);
+ }
+ }
+ else
+ {
+ gdb_assert (tts.tts_u.tts_fork.tts_isparent);
+
+ if (follow_child)
+ {
+ if (ttrace (TT_PROC_DETACH, pid, 0, 0, 0, 0) == -1)
+ perror_with_name (("ttrace"));
+ }
+ else
+ {
+ if (ttrace (TT_PROC_DETACH, fpid, 0, 0, 0, 0) == -1)
+ perror_with_name (("ttrace"));
+ }
+ }
+
+ if (follow_child)
+ {
+ /* The child will start out single-threaded. */
+ inf_ttrace_num_lwps = 0;
+ inf_ttrace_num_lwps_in_syscall = 0;
+
+ /* Reset breakpoints in the child as appropriate. */
+ follow_inferior_reset_breakpoints ();
+ }
+
+ return 0;
+}
+\f
+
/* File descriptors for pipes used as semaphores during initial
startup of an inferior. */
static int inf_ttrace_pfd1[2];
inf_ttrace_prepare (void)
{
if (pipe (inf_ttrace_pfd1) == -1)
- perror_with_name ("pipe");
+ perror_with_name (("pipe"));
if (pipe (inf_ttrace_pfd2) == -1)
{
close (inf_ttrace_pfd1[0]);
close (inf_ttrace_pfd2[0]);
- perror_with_name ("pipe");
+ perror_with_name (("pipe"));
}
}
/* Set the initial event mask. */
memset (&tte, 0, sizeof (tte));
- tte.tte_events |= TTEVT_EXEC | TTEVT_EXIT;
+ tte.tte_events |= TTEVT_EXEC | TTEVT_EXIT | TTEVT_FORK | TTEVT_VFORK;
tte.tte_events |= TTEVT_LWP_CREATE | TTEVT_LWP_EXIT | TTEVT_LWP_TERMINATE;
#ifdef TTEVT_BPT_SSTEP
tte.tte_events |= TTEVT_BPT_SSTEP;
#endif
- tte.tte_opts = TTEO_NOSTRCCHLD;
+ tte.tte_opts |= TTEO_PROC_INHERIT;
if (ttrace (TT_PROC_SET_EVENT_MASK, pid, 0,
(uintptr_t)&tte, sizeof tte, 0) == -1)
perror_with_name (("ttrace"));
gdb_assert (inf_ttrace_num_lwps_in_syscall == 0);
gdb_assert (inf_ttrace_page_dict.count == 0);
gdb_assert (inf_ttrace_reenable_page_protections == 0);
+ gdb_assert (inf_ttrace_vfork_ppid == -1);
fork_inferior (exec_file, allargs, env, inf_ttrace_me, inf_ttrace_him,
inf_ttrace_prepare, NULL);
if (ttrace (TT_PROC_EXIT, pid, 0, 0, 0, 0) == -1)
perror_with_name (("ttrace"));
/* ??? Is it necessary to call ttrace_wait() here? */
+
+ if (inf_ttrace_vfork_ppid != -1)
+ {
+ if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1)
+ perror_with_name (("ttrace"));
+ inf_ttrace_vfork_ppid = -1;
+ }
+
target_mourn_inferior ();
}
ttevent_t tte;
if (!args)
- error_no_arg ("process-id to attach");
+ error_no_arg (_("process-id to attach"));
dummy = args;
pid = strtol (args, &dummy, 0);
exec_file = (char *) get_exec_file (0);
if (exec_file)
- printf_unfiltered ("Attaching to program: %s, %s\n", exec_file,
+ printf_unfiltered (_("Attaching to program: %s, %s\n"), exec_file,
target_pid_to_str (pid_to_ptid (pid)));
else
- printf_unfiltered ("Attaching to %s\n",
+ printf_unfiltered (_("Attaching to %s\n"),
target_pid_to_str (pid_to_ptid (pid)));
gdb_flush (gdb_stdout);
gdb_assert (inf_ttrace_num_lwps == 0);
gdb_assert (inf_ttrace_num_lwps_in_syscall == 0);
+ gdb_assert (inf_ttrace_vfork_ppid == -1);
if (ttrace (TT_PROC_ATTACH, pid, 0, TT_KILL_ON_EXIT, TT_VERSION, 0) == -1)
perror_with_name (("ttrace"));
/* Set the initial event mask. */
memset (&tte, 0, sizeof (tte));
- tte.tte_events |= TTEVT_EXEC | TTEVT_EXIT;
+ tte.tte_events |= TTEVT_EXEC | TTEVT_EXIT | TTEVT_FORK | TTEVT_VFORK;
tte.tte_events |= TTEVT_LWP_CREATE | TTEVT_LWP_EXIT | TTEVT_LWP_TERMINATE;
#ifdef TTEVT_BPT_SSTEP
tte.tte_events |= TTEVT_BPT_SSTEP;
#endif
- tte.tte_opts = TTEO_NOSTRCCHLD;
+ tte.tte_opts |= TTEO_PROC_INHERIT;
if (ttrace (TT_PROC_SET_EVENT_MASK, pid, 0,
(uintptr_t)&tte, sizeof tte, 0) == -1)
perror_with_name (("ttrace"));
static void
inf_ttrace_detach (char *args, int from_tty)
{
- int sig = 0;
pid_t pid = ptid_get_pid (inferior_ptid);
+ int sig = 0;
if (from_tty)
{
char *exec_file = get_exec_file (0);
if (exec_file == 0)
exec_file = "";
- printf_unfiltered ("Detaching from program: %s, %s\n", exec_file,
+ printf_unfiltered (_("Detaching from program: %s, %s\n"), exec_file,
target_pid_to_str (pid_to_ptid (pid)));
gdb_flush (gdb_stdout);
}
if (ttrace (TT_PROC_DETACH, pid, 0, 0, sig, 0) == -1)
perror_with_name (("ttrace"));
+ if (inf_ttrace_vfork_ppid != -1)
+ {
+ if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1)
+ perror_with_name (("ttrace"));
+ inf_ttrace_vfork_ppid = -1;
+ }
+
inf_ttrace_num_lwps = 0;
inf_ttrace_num_lwps_in_syscall = 0;
ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
if (pid == -1)
- pid = 0;
+ pid = lwpid = 0;
- gdb_assert (lwpid == 0 || pid != 0);
+ gdb_assert (pid != 0 || lwpid == 0);
do
{
set_sigio_trap ();
if (ttrace_wait (pid, lwpid, TTRACE_WAITOK, &tts, sizeof tts) == -1)
- perror_with_name ("ttrace_wait");
+ perror_with_name (("ttrace_wait"));
+
+ if (tts.tts_event == TTEVT_VFORK && tts.tts_u.tts_fork.tts_isparent)
+ {
+ if (inf_ttrace_vfork_ppid != -1)
+ {
+ gdb_assert (inf_ttrace_vfork_ppid == tts.tts_pid);
+
+ if (ttrace (TT_PROC_DETACH, tts.tts_pid, 0, 0, 0, 0) == -1)
+ perror_with_name (("ttrace"));
+ inf_ttrace_vfork_ppid = -1;
+ }
+
+ tts.tts_event = TTEVT_NONE;
+ }
clear_sigio_trap ();
clear_sigint_trap ();
#endif
case TTEVT_EXEC:
- /* Make it look like a breakpoint. */
- ourstatus->kind = TARGET_WAITKIND_STOPPED;
- ourstatus->value.sig = TARGET_SIGNAL_TRAP;
+ ourstatus->kind = TARGET_WAITKIND_EXECD;
+ ourstatus->value.execd_pathname =
+ xmalloc (tts.tts_u.tts_exec.tts_pathlen + 1);
+ if (ttrace (TT_PROC_GET_PATHNAME, tts.tts_pid, 0,
+ (uintptr_t)ourstatus->value.execd_pathname,
+ tts.tts_u.tts_exec.tts_pathlen, 0) == -1)
+ perror_with_name (("ttrace"));
+ ourstatus->value.execd_pathname[tts.tts_u.tts_exec.tts_pathlen] = 0;
break;
case TTEVT_EXIT:
inf_ttrace_num_lwps = 0;
break;
+ case TTEVT_FORK:
+ ourstatus->kind = TARGET_WAITKIND_FORKED;
+ ourstatus->value.related_pid = tts.tts_u.tts_fork.tts_fpid;
+
+ /* Make sure the other end of the fork is stopped too. */
+ if (ttrace_wait (tts.tts_u.tts_fork.tts_fpid,
+ tts.tts_u.tts_fork.tts_flwpid,
+ TTRACE_WAITOK, &tts, sizeof tts) == -1)
+ perror_with_name (("ttrace_wait"));
+
+ gdb_assert (tts.tts_event == TTEVT_FORK);
+ if (tts.tts_u.tts_fork.tts_isparent)
+ {
+ ptid = ptid_build (tts.tts_pid, tts.tts_lwpid, 0);
+ ourstatus->value.related_pid = tts.tts_u.tts_fork.tts_fpid;
+ }
+ break;
+
+ case TTEVT_VFORK:
+ gdb_assert (!tts.tts_u.tts_fork.tts_isparent);
+
+ ourstatus->kind = TARGET_WAITKIND_VFORKED;
+ ourstatus->value.related_pid = tts.tts_u.tts_fork.tts_fpid;
+
+ /* HACK: To avoid touching the parent during the vfork, switch
+ away from it. */
+ inferior_ptid = ptid;
+ break;
+
case TTEVT_LWP_CREATE:
lwpid = tts.tts_u.tts_thread.tts_target_lwpid;
ptid = ptid_build (tts.tts_pid, lwpid, 0);
add_thread (ptid_build (tts.tts_pid, tts.tts_lwpid, 0));
inf_ttrace_num_lwps++;
}
- printf_filtered ("[New %s]\n", target_pid_to_str (ptid));
+ printf_filtered (_("[New %s]\n"), target_pid_to_str (ptid));
add_thread (ptid);
inf_ttrace_num_lwps++;
ptid = ptid_build (tts.tts_pid, tts.tts_lwpid, 0);
break;
case TTEVT_LWP_EXIT:
- printf_filtered("[%s exited]\n", target_pid_to_str (ptid));
+ printf_filtered(_("[%s exited]\n"), target_pid_to_str (ptid));
delete_thread (ptid);
inf_ttrace_num_lwps--;
/* If we don't return -1 here, core GDB will re-add the thread. */
case TTEVT_LWP_TERMINATE:
lwpid = tts.tts_u.tts_thread.tts_target_lwpid;
ptid = ptid_build (tts.tts_pid, lwpid, 0);
- printf_filtered("[%s has been terminated]\n", target_pid_to_str (ptid));
+ printf_filtered(_("[%s has been terminated]\n"), target_pid_to_str (ptid));
delete_thread (ptid);
inf_ttrace_num_lwps--;
ptid = ptid_build (tts.tts_pid, tts.tts_lwpid, 0);
static LONGEST
inf_ttrace_xfer_partial (struct target_ops *ops, enum target_object object,
- const char *annex, void *readbuf,
- const void *writebuf, ULONGEST offset, LONGEST len)
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
{
switch (object)
{
static void
inf_ttrace_files_info (struct target_ops *ignore)
{
- printf_unfiltered ("\tUsing the running image of %s %s.\n",
+ printf_unfiltered (_("\tUsing the running image of %s %s.\n"),
attach_flag ? "attached" : "child",
target_pid_to_str (inferior_ptid));
}
{
pid_t pid = ptid_get_pid (ptid);
lwpid_t lwpid = ptid_get_lwp (ptid);
- static char buf[80];
+ static char buf[128];
- sprintf (buf, "process %ld, lwp %ld", (long)pid, (long)lwpid);
+ xsnprintf (buf, sizeof buf, "process %ld, lwp %ld",
+ (long)pid, (long)lwpid);
return buf;
}
t->to_files_info = inf_ttrace_files_info;
t->to_thread_alive = inf_ttrace_thread_alive;
t->to_pid_to_str = inf_ttrace_pid_to_str;
+ t->to_follow_fork = inf_ttrace_follow_fork;
t->to_can_use_hw_breakpoint = inf_ttrace_can_use_hw_breakpoint;
t->to_region_size_ok_for_hw_watchpoint =
inf_ttrace_region_size_ok_for_hw_watchpoint;