aboutsummaryrefslogtreecommitdiff
path: root/gdb/fork-child.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/fork-child.c')
-rw-r--r--gdb/fork-child.c355
1 files changed, 324 insertions, 31 deletions
diff --git a/gdb/fork-child.c b/gdb/fork-child.c
index 243bed4..e262f89 100644
--- a/gdb/fork-child.c
+++ b/gdb/fork-child.c
@@ -33,12 +33,67 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include <unistd.h>
#endif
-extern char **environ;
+#define DEBUGGING 0
+/* This just gets used as a default if we can't find SHELL */
#ifndef SHELL_FILE
#define SHELL_FILE "/bin/sh"
#endif
+extern char **environ;
+
+/* This function breaks up an argument string into an argument
+ * vector suitable for passing to execvp().
+ * E.g., on "run a b c d" this routine would get as input
+ * the string "a b c d", and as output it would fill in argv with
+ * the four arguments "a", "b", "c", "d".
+ */
+static void
+breakup_args (
+ scratch,
+ argv)
+char *scratch;
+char **argv;
+{
+ char *cp = scratch;
+
+#if DEBUGGING
+ printf("breakup_args: input = %s\n", scratch);
+#endif
+ for (;;) {
+
+ /* Scan past leading separators */
+ while (*cp == ' ' || *cp == '\t' || *cp == '\n') {
+ cp++;
+ }
+
+ /* Break if at end of string */
+ if (*cp == '\0') break;
+
+ /* Take an arg */
+ *argv++ = cp;
+
+ /* Scan for next arg separator */
+ cp = strchr (cp, ' ');
+ if (cp == NULL)
+ cp = strchr (cp, '\t');
+ if (cp == NULL)
+ cp = strchr (cp, '\n');
+
+ /* No separators => end of string => break */
+ if (cp == NULL)
+ break;
+
+ /* Replace the separator with a terminator */
+ *cp++ = '\0';
+ }
+
+ /* execv requires a null-terminated arg vector */
+ *argv = NULL;
+
+}
+
+
/* Start an inferior Unix child process and sets inferior_pid to its pid.
EXEC_FILE is the file to run.
ALLARGS is a string containing the arguments to the program.
@@ -47,12 +102,13 @@ extern char **environ;
void
fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun,
- shell_file)
+ pre_trace_fun, shell_file)
char *exec_file;
char *allargs;
char **env;
void (*traceme_fun) PARAMS ((void));
- int (*init_trace_fun) PARAMS ((int));
+ void (*init_trace_fun) PARAMS ((int));
+ void (*pre_trace_fun) PARAMS ((void));
char *shell_file;
{
int pid;
@@ -65,22 +121,31 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun,
to you in the parent process. It's only used by humans for debugging. */
static int debug_setpgrp = 657473;
char **save_our_env;
+ int shell = 0;
+ char **argv;
+ char *tryname;
/* If no exec file handed to us, get it from the exec-file command -- with
a good, common error message if none is specified. */
if (exec_file == 0)
exec_file = get_exec_file(1);
- /* The user might want tilde-expansion, and in general probably wants
- the program to behave the same way as if run from
- his/her favorite shell. So we let the shell run it for us.
- FIXME-maybe, we might want a "set shell" command so the user can change
- the shell from within GDB (if so, change callers which pass in a non-NULL
- shell_file too). */
- if (shell_file == NULL)
- shell_file = getenv ("SHELL");
- if (shell_file == NULL)
- shell_file = default_shell_file;
+ /* STARTUP_WITH_SHELL is defined in inferior.h.
+ * If 0, we'll just do a fork/exec, no shell, so don't
+ * bother figuring out what shell.
+ */
+ if (STARTUP_WITH_SHELL) {
+ /* Figure out what shell to start up the user program under. */
+ if (shell_file == NULL)
+ shell_file = getenv ("SHELL");
+ if (shell_file == NULL)
+ shell_file = default_shell_file;
+ shell = 1;
+ }
+
+#if DEBUGGING
+ printf("shell is %s\n", shell_file);
+#endif
/* Multiplying the length of exec_file by 4 is to account for the fact
that it may expand when quoted; it is a worst-case number based on
@@ -95,20 +160,40 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun,
shell_command = (char *) alloca (len);
shell_command[0] = '\0';
#endif
- strcat (shell_command, "exec ");
- /* Now add exec_file, quoting as necessary. */
- {
+ if (!shell) {
+ /* We're going to call execvp. Create argv */
+ /* Largest case: every other character is a separate arg */
+#if DEBUGGING
+ printf("allocating argv, length = %d\n",
+ (
+ (strlen (allargs) + 1) / (unsigned) 2
+ + 2
+ ) * sizeof (*argv)
+ );
+#endif
+ argv = (char **) xmalloc(((strlen (allargs) + 1) / (unsigned) 2 + 2) * sizeof (*argv));
+ argv[0] = exec_file;
+ breakup_args(allargs, &argv[1]);
+
+ } else {
+
+ /* We're going to call a shell */
+
+ /* Now add exec_file, quoting as necessary. */
+
char *p;
int need_to_quote;
+ strcat (shell_command, "exec ");
+
/* Quoting in this style is said to work with all shells. But csh
on IRIX 4.0.1 can't deal with it. So we only quote it if we need
to. */
p = exec_file;
while (1)
{
- switch (*p)
+ switch (*p)
{
case '\'':
case '"':
@@ -137,8 +222,8 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun,
end_scan:
if (need_to_quote)
{
- strcat (shell_command, "'");
- for (p = exec_file; *p != '\0'; ++p)
+ strcat (shell_command, "'");
+ for (p = exec_file; *p != '\0'; ++p)
{
if (*p == '\'')
strcat (shell_command, "'\\''");
@@ -149,10 +234,11 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun,
}
else
strcat (shell_command, exec_file);
- }
+
+ strcat (shell_command, " ");
+ strcat (shell_command, allargs);
- strcat (shell_command, " ");
- strcat (shell_command, allargs);
+ }
/* exec is said to fail if the executable is open. */
close_exec_file ();
@@ -174,6 +260,12 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun,
gdb_flush (gdb_stdout);
gdb_flush (gdb_stderr);
+ /* If there's any initialization of the target layers that must happen
+ to prepare to handle the child we're about fork, do it now...
+ */
+ if (pre_trace_fun != NULL)
+ (*pre_trace_fun) ();
+
#if defined(USG) && !defined(HAVE_VFORK)
pid = fork ();
#else
@@ -209,6 +301,14 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun,
/* "Trace me, Dr. Memory!" */
(*traceme_fun) ();
+ /* The call above set this process (the "child") as debuggable
+ * by the original gdb process (the "parent"). Since processes
+ * (unlike people) can have only one parent, if you are
+ * debugging gdb itself (and your debugger is thus _already_ the
+ * controller/parent for this child), code from here on out
+ * is undebuggable. Indeed, you probably got an error message
+ * saying "not parent". Sorry--you'll have to use print statements!
+ */
/* There is no execlpe call, so we have to set the environment
for our child in the global variable. If we've vforked, this
@@ -216,12 +316,65 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun,
in the parent. By the way, yes we do need to look down the
path to find $SHELL. Rich Pixley says so, and I agree. */
environ = env;
- execlp (shell_file, shell_file, "-c", shell_command, (char *)0);
- fprintf_unfiltered (gdb_stderr, "Cannot exec %s: %s.\n", shell_file,
- safe_strerror (errno));
- gdb_flush (gdb_stderr);
- _exit (0177);
+ /* If we decided above to start up with a shell,
+ * we exec the shell,
+ * "-c" says to interpret the next arg as a shell command
+ * to execute, and this command is "exec <target-program> <args>".
+ * "-f" means "fast startup" to the c-shell, which means
+ * don't do .cshrc file. Doing .cshrc may cause fork/exec
+ * events which will confuse debugger start-up code.
+ */
+ if (shell) {
+#if 0
+
+ /* HP change is problematic. The -f option has different meanings
+ for different shells. It is particularly inappropriate for
+ bourne shells. */
+ execlp (shell_file, shell_file, "-f", "-c", shell_command, (char *)0);
+#else
+ execlp (shell_file, shell_file, "-c", shell_command, (char *)0);
+#endif
+
+
+ /* If we get here, it's an error */
+ fprintf_unfiltered (gdb_stderr, "Cannot exec %s: %s.\n", shell_file,
+ safe_strerror (errno));
+ gdb_flush (gdb_stderr);
+ _exit (0177);
+ } else {
+ /* Otherwise, we directly exec the target program with execvp. */
+ int i;
+ char * errstring;
+#if DEBUGGING
+ printf("about to exec target, exec_file = %s\n", exec_file);
+ i = 0;
+ while (argv[i] != NULL) {
+ printf("strlen(argv[%d]) is %d\n", i, strlen(argv[i]));
+ printf("argv[%d] is %s\n", i, argv[i]);
+ i++;
+ }
+#endif
+ execvp (exec_file, argv);
+
+ /* If we get here, it's an error */
+ errstring = safe_strerror (errno);
+ fprintf_unfiltered (gdb_stderr, "Cannot exec %s ", exec_file);
+
+ i = 1;
+ while (argv[i] != NULL) {
+ if (i != 1)
+ fprintf_unfiltered (gdb_stderr, " ");
+ fprintf_unfiltered (gdb_stderr, "%s", argv[i]);
+ i++;
+ }
+ fprintf_unfiltered (gdb_stderr, ".\n");
+ /* This extra info seems to be useless
+ fprintf_unfiltered (gdb_stderr, "Got error %s.\n", errstring);
+ */
+ gdb_flush (gdb_stderr);
+ _exit (0177);
+ }
}
/* Restore our environment in case a vforked child clob'd it. */
@@ -234,8 +387,7 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun,
/* Now that we have a child process, make it our target, and
initialize anything target-vector-specific that needs initializing. */
- /* Note that pid may be modified by this function. */
- inferior_pid = pid = (*init_trace_fun)(pid);
+ (*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
@@ -251,6 +403,137 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun,
#endif
}
+/* An inferior Unix process CHILD_PID has been created by a call to
+ fork() (or variants like vfork). It is presently stopped, and waiting
+ to be resumed. clone_and_follow_inferior will fork the debugger,
+ and that clone will "follow" (attach to) CHILD_PID. The original copy
+ of the debugger will not touch CHILD_PID again.
+
+ Also, the original debugger will set FOLLOWED_CHILD FALSE, while the
+ clone will set it TRUE.
+ */
+void
+clone_and_follow_inferior (child_pid, followed_child)
+ int child_pid;
+ int *followed_child;
+{
+ extern int auto_solib_add;
+
+ int debugger_pid;
+ int status;
+ char pid_spelling [100]; /* Arbitrary but sufficient length. */
+
+ /* This semaphore is used to coordinate the two debuggers' handoff
+ of CHILD_PID. The original debugger will detach from CHILD_PID,
+ and then the clone debugger will attach to it. (It must be done
+ this way because on some targets, only one process at a time can
+ trace another. Thus, the original debugger must relinquish its
+ tracing rights before the clone can pick them up.)
+ */
+#define SEM_TALK (1)
+#define SEM_LISTEN (0)
+ int handoff_semaphore[2]; /* Original "talks" to [1], clone "listens" to [0] */
+ int talk_value = 99;
+ int listen_value;
+
+ /* Set debug_fork then attach to the child while it sleeps, to debug. */
+ static int debug_fork = 0;
+
+ /* 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. */
+
+ gdb_flush (gdb_stdout);
+ gdb_flush (gdb_stderr);
+
+ /* Open the semaphore pipes.
+ */
+ status = pipe (handoff_semaphore);
+ if (status < 0)
+ error ("error getting pipe for handoff semaphore");
+
+ /* Clone the debugger. */
+#if defined(USG) && !defined(HAVE_VFORK)
+ debugger_pid = fork ();
+#else
+ if (debug_fork)
+ debugger_pid = fork ();
+ else
+ debugger_pid = vfork ();
+#endif
+
+ if (debugger_pid < 0)
+ perror_with_name ("fork");
+
+ /* Are we the original debugger? If so, we must relinquish all claims
+ to CHILD_PID. */
+ if (debugger_pid != 0)
+ {
+ char signal_spelling[100]; /* Arbitrary but sufficient length */
+
+ /* Detach from CHILD_PID. Deliver a "stop" signal when we do, though,
+ so that it remains stopped until the clone debugger can attach
+ to it.
+ */
+ detach_breakpoints (child_pid);
+
+ sprintf (signal_spelling, "%d", target_signal_to_host (TARGET_SIGNAL_STOP));
+ target_require_detach (child_pid, signal_spelling, 1);
+
+ /* Notify the clone debugger that it should attach to CHILD_PID. */
+ write (handoff_semaphore[SEM_TALK], &talk_value, sizeof (talk_value));
+
+ *followed_child = 0;
+ }
+
+ /* We're the child. */
+ else
+ {
+ if (debug_fork)
+ sleep (debug_fork);
+
+ /* The child (i.e., the cloned debugger) must now attach to
+ CHILD_PID. inferior_pid is presently set to the parent process
+ of the fork, while CHILD_PID should be the child process of the
+ fork.
+
+ Wait until the original debugger relinquishes control of CHILD_PID,
+ though.
+ */
+ read (handoff_semaphore[SEM_LISTEN], &listen_value, sizeof (listen_value));
+
+ /* Note that we DON'T want to actually detach from inferior_pid,
+ because that would allow it to run free. The original
+ debugger wants to retain control of the process. So, we
+ just reset inferior_pid to CHILD_PID, and then ensure that all
+ breakpoints are really set in CHILD_PID.
+ */
+ target_mourn_inferior ();
+
+ /* Ask the tty subsystem to switch to the one we specified earlier
+ (or to share the current terminal, if none was specified). */
+
+ new_tty ();
+
+ dont_repeat ();
+ sprintf(pid_spelling, "%d", child_pid);
+ target_require_attach (pid_spelling, 1);
+
+ /* Perform any necessary cleanup, after attachment. (This form
+ of attaching can behave differently on some targets than the
+ standard method, where a process formerly not under debugger
+ control was suddenly attached to..)
+ */
+ target_post_follow_inferior_by_clone ();
+
+ *followed_child = 1;
+ }
+
+ /* Discard the handoff sempahore. */
+ (void) close (handoff_semaphore[SEM_LISTEN]);
+ (void) close (handoff_semaphore[SEM_TALK]);
+}
+
/* Accept NTRAPS traps from the inferior. */
void
@@ -270,6 +553,13 @@ startup_inferior (ntraps)
terminal_initted = 0;
+ if (STARTUP_WITH_SHELL)
+ inferior_ignoring_startup_exec_events = ntraps;
+ else
+ inferior_ignoring_startup_exec_events = 0;
+ inferior_ignoring_leading_exec_events =
+ target_reported_exec_events_per_exec_call () - 1;
+
#ifdef STARTUP_INFERIOR
STARTUP_INFERIOR (pending_execs);
#else
@@ -301,9 +591,12 @@ startup_inferior (ntraps)
terminal_initted = 1;
}
- if (0 == --pending_execs)
+
+ pending_execs = pending_execs - 1;
+ if (0 == pending_execs)
break;
- resume (0, TARGET_SIGNAL_0); /* Just make it go on */
+
+ resume (0, TARGET_SIGNAL_0); /* Just make it go on */
}
}
#endif /* STARTUP_INFERIOR */