/* Fork a Unix child process, and set up to debug it, for GDB.
Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000,
- 2001, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+ 2001, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+ Free Software Foundation, Inc.
Contributed by Cygnus Support.
#include "defs.h"
#include "gdb_string.h"
-#include "frame.h" /* required by inferior.h */
#include "inferior.h"
+#include "terminal.h"
#include "target.h"
#include "gdb_wait.h"
#include "gdb_vfork.h"
static void
breakup_args (char *scratch, char **argv)
{
- char *cp = scratch;
+ char *cp = scratch, *tmp;
for (;;)
{
*argv++ = cp;
/* Scan for next arg separator. */
- cp = strchr (cp, ' ');
- if (cp == NULL)
- cp = strchr (cp, '\t');
- if (cp == NULL)
- cp = strchr (cp, '\n');
+ tmp = strchr (cp, ' ');
+ if (tmp == NULL)
+ tmp = strchr (cp, '\t');
+ if (tmp == NULL)
+ tmp = strchr (cp, '\n');
/* No separators => end of string => break. */
- if (cp == NULL)
+ if (tmp == NULL)
break;
+ cp = tmp;
/* Replace the separator with a terminator. */
*cp++ = '\0';
/* This function is NOT reentrant. Some of the variables have been
made static to ensure that they survive the vfork call. */
-void
+int
fork_inferior (char *exec_file_arg, char *allargs, char **env,
void (*traceme_fun) (void), void (*init_trace_fun) (int),
void (*pre_trace_fun) (void), char *shell_file_arg)
char *shell_command;
static char default_shell_file[] = SHELL_FILE;
int len;
- /* Set debug_fork then attach to the child while it sleeps, to debug. */
+ /* Set debug_fork then attach to the child while it sleeps, to debug. */
static int debug_fork = 0;
/* This is set to the result of setpgrp, which if vforked, will be visible
to you in the parent process. It's only used by humans for debugging. */
int shell = 0;
static char **argv;
const char *inferior_io_terminal = get_inferior_io_terminal ();
+ struct inferior *inf;
/* If no exec file handed to us, get it from the exec-file command
-- with a good, common error message if none is specified. */
assuming that every other character is a separate
argument. */
int argc = (strlen (allargs) + 1) / 2 + 2;
+
argv = (char **) xmalloc (argc * sizeof (*argv));
argv[0] = exec_file;
breakup_args (allargs, &argv[1]);
/* It is generally good practice to flush any possible pending stdio
output prior to doing a fork, to avoid the possibility of both
- the parent and child flushing the same data after the fork. */
+ the parent and child flushing the same data after the fork. */
gdb_flush (gdb_stdout);
gdb_flush (gdb_stderr);
in a separate process group. */
debug_setpgrp = gdb_setpgid ();
if (debug_setpgrp == -1)
- perror ("setpgrp failed in child");
+ perror (_("setpgrp failed in child"));
}
/* Ask the tty subsystem to switch to the one we specified
initialize_signals for how we get the right signal handlers
for the inferior. */
- /* "Trace me, Dr. Memory!" */
+ /* "Trace me, Dr. Memory!" */
(*traceme_fun) ();
/* The call above set this process (the "child") as debuggable
/* Restore our environment in case a vforked child clob'd it. */
environ = save_our_env;
- init_thread_list ();
+ if (!have_inferiors ())
+ init_thread_list ();
+
+ inf = current_inferior ();
+
+ inferior_appeared (inf, pid);
/* Needed for wait_for_inferior stuff below. */
inferior_ptid = pid_to_ptid (pid);
+ new_tty_postfork ();
+
+ /* We have something that executes now. We'll be running through
+ the shell at this point, but the pid shouldn't change. Targets
+ supporting MT should fill this task's ptid with more data as soon
+ as they can. */
+ add_thread_silent (inferior_ptid);
+
/* Now that we have a child process, make it our target, and
initialize anything target-vector-specific that needs
initializing. */
- (*init_trace_fun) (pid);
+ if (init_trace_fun)
+ (*init_trace_fun) (pid);
/* We are now in the child process of interest, having exec'd the
correct program, and are poised at the first instruction of the
new program. */
+ return pid;
}
/* Accept NTRAPS traps from the inferior. */
{
int pending_execs = ntraps;
int terminal_initted = 0;
+ ptid_t resume_ptid;
+
+ if (target_supports_multi_process ())
+ resume_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
+ else
+ resume_ptid = minus_one_ptid;
/* The process was started by the fork that created it, but it will
have stopped one instruction after execing the shell. Here we
if (exec_wrapper)
pending_execs++;
- clear_proceed_status ();
+ while (1)
+ {
+ enum target_signal resume_signal = TARGET_SIGNAL_0;
+ ptid_t event_ptid;
- init_wait_for_inferior ();
+ struct target_waitstatus ws;
+ memset (&ws, 0, sizeof (ws));
+ event_ptid = target_wait (resume_ptid, &ws, 0);
- inferior_ignoring_leading_exec_events =
- target_reported_exec_events_per_exec_call () - 1;
+ if (ws.kind == TARGET_WAITKIND_IGNORE)
+ /* The inferior didn't really stop, keep waiting. */
+ continue;
- while (1)
- {
- /* Make wait_for_inferior be quiet. */
- stop_soon = STOP_QUIETLY;
- wait_for_inferior (1);
- if (stop_signal != TARGET_SIGNAL_TRAP)
+ switch (ws.kind)
{
- /* Let shell child handle its own signals in its own way.
- FIXME: what if child has exited? Must exit loop
- somehow. */
- resume (0, stop_signal);
+ case TARGET_WAITKIND_SPURIOUS:
+ case TARGET_WAITKIND_LOADED:
+ case TARGET_WAITKIND_FORKED:
+ case TARGET_WAITKIND_VFORKED:
+ case TARGET_WAITKIND_SYSCALL_ENTRY:
+ case TARGET_WAITKIND_SYSCALL_RETURN:
+ /* Ignore gracefully during startup of the inferior. */
+ switch_to_thread (event_ptid);
+ break;
+
+ case TARGET_WAITKIND_SIGNALLED:
+ target_terminal_ours ();
+ target_mourn_inferior ();
+ error (_("During startup program terminated with signal %s, %s."),
+ target_signal_to_name (ws.value.sig),
+ target_signal_to_string (ws.value.sig));
+ return;
+
+ case TARGET_WAITKIND_EXITED:
+ target_terminal_ours ();
+ target_mourn_inferior ();
+ if (ws.value.integer)
+ error (_("During startup program exited with code %d."),
+ ws.value.integer);
+ else
+ error (_("During startup program exited normally."));
+ return;
+
+ case TARGET_WAITKIND_EXECD:
+ /* Handle EXEC signals as if they were SIGTRAP signals. */
+ xfree (ws.value.execd_pathname);
+ resume_signal = TARGET_SIGNAL_TRAP;
+ switch_to_thread (event_ptid);
+ break;
+
+ case TARGET_WAITKIND_STOPPED:
+ resume_signal = ws.value.sig;
+ switch_to_thread (event_ptid);
+ break;
+ }
+
+ if (resume_signal != TARGET_SIGNAL_TRAP)
+ {
+ /* Let shell child handle its own signals in its own way. */
+ target_resume (resume_ptid, 0, resume_signal);
}
else
{
if (--pending_execs == 0)
break;
- resume (0, TARGET_SIGNAL_0); /* Just make it go on. */
+ /* Just make it go on. */
+ target_resume (resume_ptid, 0, TARGET_SIGNAL_0);
}
}
- stop_soon = NO_STOP_QUIETLY;
+
+ /* Mark all threads non-executing. */
+ set_executing (resume_ptid, 0);
}
/* Implement the "unset exec-wrapper" command. */
exec_wrapper = NULL;
}
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_fork_child;
+
void
_initialize_fork_child (void)
{