This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 1/4] GDB: inferior standard I/O redirection


This introduces a set of commands:

 * set inferior-stdin (aliased as stdin)
 * set inferior-stdout (aliased as stdout)
 * set inferior-stderr (aliased as stderr)

Those commands complement the "set inferior-tty" command, also
allowing the inferior standard I/O to be defined before the inferior
itself is started.

This is useful in a number of situations, including when shell based
redirection (the only current way to do it) is not desirable.

gdb/doc/ChangeLog:
2015-10-05  Cleber Rosa  <crosa@redhat.com>

    * gdb.texinfo (info): Added section on commands stdin, stdout and
    stderr

gdb/ChangeLog:
2015-10-05  Cleber Rosa  <crosa@redhat.com>

    * NEWS: New commands in gdb: set inferior-stdin (aliased as
    stdin), set inferior-stdout (aliased as stdout) and set
    inferior-stderr (aliased as stderr).
    * fork-child.c (set_std_io_helper): New utility function.
    * infcmd.c: Added inferior_io_std_scratch array.
    (set_inferior_io_helper): New utility function.
    (set_inferior_io_stdin): New function.
    (get_inferior_io_stdin): Likewise.
    (set_inferior_io_stdin_command): Likewise.
    (show_inferior_io_stdin_command): Likewise.
    (set_inferior_io_stdout): Likewise.
    (get_inferior_io_stdout): Likewise.
    (set_inferior_io_stdout_command): Likewise.
    (show_inferior_io_stdout_command): Likewise.
    (set_inferior_io_stderr): Likewise.
    (get_inferior_io_stderr): Likewise.
    (set_inferior_io_stderr_command): Likewise.
    (show_inferior_io_stderr_command): Likewise.
    (get_inferior_args): Added inferior-stdin/stdin,
    inferior-stdout/stdout and inferior-stderr/stderr commands.
    * inferior.c (free_inferior): Free inferior standard_io.
    * inferior.h (set_inferior_io_stdin): New declaration.
    (get_inferior_io_stdin): Likewise.
    (set_inferior_io_stdout): Likewise.
    (get_inferior_io_stdout): Likewise.
    (set_inferior_io_stderr): Likewise.
    (get_inferior_io_stderr): Likewise.
    (struct inferior): <inferior_io>: New field.

gdb/testsuite/ChangeLog:
2015-10-05  Cleber Rosa  <crosa@redhat.com>

    * gdb.base/default.exp: New tests for stdin, stdout and stderr
    commands.
---
 gdb/NEWS                           |  15 ++++
 gdb/doc/gdb.texinfo                |  97 +++++++++++++++++++-----
 gdb/fork-child.c                   |  81 ++++++++++++++++++++
 gdb/infcmd.c                       | 146 +++++++++++++++++++++++++++++++++++++
 gdb/inferior.c                     |   3 +
 gdb/inferior.h                     |  11 +++
 gdb/testsuite/gdb.base/default.exp |   6 ++
 7 files changed, 341 insertions(+), 18 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 2e38d9a..07bd4db 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -42,6 +42,21 @@ set remote multiprocess-extensions-packet
 show remote multiprocess-extensions-packet
   Set/show the use of the remote protocol multiprocess extensions.
 
+set inferior-stdin
+stdin
+show inferior-stdin
+  Set/show the standard input for the program being debugged.
+
+set inferior-stdout
+stdout
+show inferior-stdout
+  Set/show the standard output for the program being debugged.
+
+set inferior-stderr
+stderr
+show inferior-stderr
+  Set/show the standard error for the program being debugged.
+
 * The "disassemble" command accepts a new modifier: /s.
   It prints mixed source+disassembly like /m with two differences:
   - disassembled instructions are now printed in program order, and
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 57e47b8..39d5a1c 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -2443,6 +2443,12 @@ current working directory of the debuggee.
 @cindex redirection
 @cindex i/o
 @cindex terminal
+@value{GDBN} supports different ways of redirecting your program's
+input and output, including setting a specific terminal (a
+@code{tty}), individually setting standard input, output and error
+(@code{stdin}, @code{stdout} and @code{stderr}) or using shell based
+redirection (@code{run > output}).
+
 By default, the program you run under @value{GDBN} does input and output to
 the same terminal that @value{GDBN} uses.  @value{GDBN} switches the terminal
 to its own terminal modes to interact with you, but it records the terminal
@@ -2456,19 +2462,9 @@ Displays information recorded by @value{GDBN} about the terminal modes your
 program is using.
 @end table
 
-You can redirect your program's input and/or output using shell
-redirection with the @code{run} command.  For example,
-
-@smallexample
-run > outfile
-@end smallexample
-
-@noindent
-starts your program, diverting its output to the file @file{outfile}.
-
 @kindex tty
 @cindex controlling terminal
-Another way to specify where your program should do input and output is
+One way to specify where your program should do input and output is
 with the @code{tty} command.  This command accepts a file name as
 argument, and causes this file to be the default for future @code{run}
 commands.  It also resets the controlling terminal for the child
@@ -2483,14 +2479,17 @@ directs that processes started with subsequent @code{run} commands
 default to do input and output on the terminal @file{/dev/ttyb} and have
 that as their controlling terminal.
 
-An explicit redirection in @code{run} overrides the @code{tty} command's
-effect on the input/output device, but not its effect on the controlling
-terminal.
+An explicit redirection using either
+@code{stdin}/@code{stdout}/@code{stderr} or @code{run} overrides the
+@code{tty} command's effect on the input/output device, but not its
+effect on the controlling terminal.
 
-When you use the @code{tty} command or redirect input in the @code{run}
-command, only the input @emph{for your program} is affected.  The input
-for @value{GDBN} still comes from your terminal.  @code{tty} is an alias
-for @code{set inferior-tty}.
+When you use the @code{tty} command or redirect input using either
+@code{stdin}/@code{stdout}/@code{stderr} or the @code{run} command,
+only the input @emph{for your program} is affected.  The input for
+@value{GDBN} still comes from your terminal and from the original
+standard I/O channels.  @code{tty} is an alias for @code{set
+inferior-tty}.
 
 @cindex inferior tty
 @cindex set inferior controlling terminal
@@ -2508,6 +2507,68 @@ Set the tty for the program being debugged to /dev/ttyb.
 Show the current tty for the program being debugged.
 @end table
 
+@cindex inferior standard i/o
+@cindex set inferior standard i/o
+As mentioned before, you can also redirect your program's standard
+input, output and error individually with the @code{stdin},
+@code{stdout} and @code{stderr} commands.  For example,
+
+@smallexample
+stdin /tmp/answers
+stdout /tmp/output
+stderr /dev/null
+@end smallexample
+
+@noindent
+will set your program's standard input (file descriptor number 0) to
+the @file{/tmp/answers}, the standard output (file description number
+1) to the @file{/tmp/output} and will silence the standard error (file
+descriptor number 2) by redirecting it to @file{/dev/null}.
+
+The @code{stdin}, @code{stdout} and @code{stderr} are actually aliases
+to the commands @code{set inferior-stdin}, @code{set inferior-stdout}
+and @code{set inferior-stderr}.
+
+@table @code
+@item set inferior-stdin /tmp/answers
+@kindex set inferior-stdin
+@kindex stdin
+Set the standard input for the program being debugged to /tmp/answers.
+
+@item show inferior-stdin
+@kindex show inferior-stdin
+Show the current standard input redirection for the program being debugged.
+
+@item set inferior-stdout /tmp/output
+@kindex set inferior-stdout
+@kindex stdout
+Set the standard output for the program being debugged to /tmp/output.
+
+@item show inferior-stdout
+@kindex show inferior-stdout
+Show the current standard output redirection for the program being debugged.
+
+@item set inferior-stderr /dev/null
+@kindex set inferior-stderr
+@kindex stderr
+Set the standard error for the program being debugged to /dev/null
+
+@item show inferior-error
+@kindex show inferior-error
+Show the current standard error redirection for the program being debugged.
+@end table
+
+@cindex inferior i/o shell based redirection
+Yet another way of doing redirection is by using the shell
+capabilities with the @code{run} command.  For example,
+
+@smallexample
+run > outfile
+@end smallexample
+
+@noindent
+starts your program, diverting its output to the file @file{outfile}.
+
 @node Attach
 @section Debugging an Already-running Process
 @kindex attach
diff --git a/gdb/fork-child.c b/gdb/fork-child.c
index 959f578..1b1bd47 100644
--- a/gdb/fork-child.c
+++ b/gdb/fork-child.c
@@ -108,6 +108,64 @@ escape_bang_in_quoted_argument (const char *shell_file)
   return 0;
 }
 
+/* Helper function that attempts to open a file and if successful
+   redirects another file descriptor to the one just opened.  If
+   file_name is not given, this returns -1 as a simple way to flag
+   a skip (user has not asked for a redirection).  Seems OK since
+   this is a helper function.  */
+
+static int
+set_std_io_helper (const char *file_name, int std_io_fd, int flags, mode_t mode)
+{
+  int fd;
+
+  if (file_name == NULL)
+    return -1;
+
+  fd = open (file_name, flags, mode);
+  if (fd < 0)
+    return 0;
+
+  if (dup2 (fd, std_io_fd) == -1) {
+    close (fd);
+    return 0;
+  }
+
+  close (fd);
+  return 1;
+}
+
+/* Attempts to redirect stdin, stdout and stderr. If redirection was
+   not requested by the user, it will be skipped in the helper function.
+   In case of errors, each requested and failed redirection error will
+   be passed on for individual error reporting (no details such as
+   errno/perror though).  */
+
+#define SET_STDIO_ERROR_STDIN  0x01
+#define SET_STDIO_ERROR_STDOUT 0x02
+#define SET_STDIO_ERROR_STDERR 0x04
+
+static int
+set_std_io (void)
+{
+  int result = 0;
+  char *file_name = NULL;
+
+  if (set_std_io_helper (get_inferior_io_stdin(), 0,
+                         O_RDONLY, S_IRUSR | S_IWUSR) == 0)
+    result |= SET_STDIO_ERROR_STDIN;
+
+  if (set_std_io_helper (get_inferior_io_stdout(), 1,
+                         O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR) == 0)
+    result |= SET_STDIO_ERROR_STDOUT;
+
+  if (set_std_io_helper (get_inferior_io_stderr(), 2,
+                         O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR) == 0)
+    result |= SET_STDIO_ERROR_STDERR;
+
+  return result;
+}
+
 /* Start an inferior Unix child process and sets inferior_ptid to its
    pid.  EXEC_FILE is the file to run.  ALLARGS is a string containing
    the arguments to the program.  ENV is the environment vector to
@@ -141,6 +199,7 @@ fork_inferior (char *exec_file_arg, char *allargs, char **env,
   struct inferior *inf;
   int i;
   int save_errno;
+  int io_redir_errors = 0;
 
   /* If no exec file handed to us, get it from the exec-file command
      -- with a good, common error message if none is specified.  */
@@ -358,6 +417,28 @@ fork_inferior (char *exec_file_arg, char *allargs, char **env,
          path to find $SHELL.  Rich Pixley says so, and I agree.  */
       environ = env;
 
+      /* Sets the inferior process standard I/O (input, output, error)
+	 redirection if set with the "set inferior std{in,out,err}"
+	 commands.  */
+      gdb_flush (gdb_stdout);
+      gdb_flush (gdb_stderr);
+      io_redir_errors = set_std_io();
+      if (io_redir_errors & SET_STDIO_ERROR_STDIN)
+        fprintf_unfiltered (gdb_stderr,
+                            "Could not set %s as stdin for %s\n",
+                            get_inferior_io_stdin(),
+                            exec_file);
+      if (io_redir_errors & SET_STDIO_ERROR_STDOUT)
+        fprintf_unfiltered (gdb_stderr,
+                            "Could not set %s as stdout for %s\n",
+                            get_inferior_io_stdout(),
+                            exec_file);
+      if (io_redir_errors & SET_STDIO_ERROR_STDERR)
+        fprintf_unfiltered (gdb_stderr,
+                            "Could not set %s as stderr for %s\n",
+                            get_inferior_io_stderr(),
+                            exec_file);
+
       if (exec_fun != NULL)
         (*exec_fun) (argv[0], argv, env);
       else
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 4713490..9941a98 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -115,6 +115,11 @@ static char *inferior_args_scratch;
 
 static char *inferior_io_terminal_scratch;
 
+/* Scratch area where 'set inferior-{stdin,stdout,stderr}' will store
+   user provided value.  */
+
+static char *inferior_io_std_scratch[3] = { NULL, NULL, NULL };
+
 /* Pid of our debugged inferior, or 0 if no inferior now.
    Since various parts of infrun.c test this to see whether there is a program
    being debugged it should be nonzero (currently 3 is used) for remote
@@ -182,6 +187,117 @@ show_inferior_tty_command (struct ui_file *file, int from_tty,
 		      "is \"%s\".\n"), inferior_io_terminal);
 }
 
+static void
+set_inferior_io_helper (const char *file_name, int stdfd)
+{
+  gdb_assert (0 <= stdfd && stdfd <= 2);
+  xfree (current_inferior ()->standard_io[stdfd]);
+  current_inferior ()->standard_io[stdfd] =
+    file_name != NULL ? xstrdup (file_name) : NULL;
+}
+
+void
+set_inferior_io_stdin (const char *file_name)
+{
+  set_inferior_io_helper(file_name, 0);
+}
+
+const char *
+get_inferior_io_stdin (void)
+{
+  return current_inferior ()->standard_io[0];
+}
+
+static void
+set_inferior_stdin_command (char *args, int from_tty,
+			    struct cmd_list_element *c)
+{
+  set_inferior_io_stdin (inferior_io_std_scratch[0]);
+}
+
+static void
+show_inferior_stdin_command (struct ui_file *file, int from_tty,
+			     struct cmd_list_element *c, const char *value)
+{
+  /* Note that we ignore the passed-in value in favor of computing it
+     directly.  */
+  const char *inferior_io_stdin = get_inferior_io_stdin ();
+
+  if (inferior_io_stdin == NULL)
+    inferior_io_stdin = "";
+  fprintf_filtered (gdb_stdout,
+		    _("Standard input for future runs of program being "
+		      "debugged is \"%s\".\n"), inferior_io_stdin);
+}
+
+void
+set_inferior_io_stdout (const char *file_name)
+{
+  set_inferior_io_helper(file_name, 1);
+}
+
+const char *
+get_inferior_io_stdout (void)
+{
+  return current_inferior ()->standard_io[1];
+}
+
+static void
+set_inferior_stdout_command (char *args, int from_tty,
+			     struct cmd_list_element *c)
+{
+  set_inferior_io_stdout (inferior_io_std_scratch[1]);
+}
+
+static void
+show_inferior_stdout_command (struct ui_file *file, int from_tty,
+			      struct cmd_list_element *c, const char *value)
+{
+  /* Note that we ignore the passed-in value in favor of computing it
+     directly.  */
+  const char *inferior_io_stdout = get_inferior_io_stdout ();
+
+  if (inferior_io_stdout == NULL)
+    inferior_io_stdout = "";
+  fprintf_filtered (gdb_stdout,
+		    _("Standard output for future runs of program being "
+		      "debugged is \"%s\".\n"), inferior_io_stdout);
+}
+
+void
+set_inferior_io_stderr (const char *file_name)
+{
+  set_inferior_io_helper(file_name, 2);
+}
+
+const char *
+get_inferior_io_stderr (void)
+{
+  return current_inferior ()->standard_io[2];
+}
+
+static void
+set_inferior_stderr_command (char *args, int from_tty,
+			     struct cmd_list_element *c)
+{
+  set_inferior_io_stderr (inferior_io_std_scratch[2]);
+}
+
+static void
+show_inferior_stderr_command (struct ui_file *file, int from_tty,
+			      struct cmd_list_element *c, const char *value)
+{
+  /* Note that we ignore the passed-in value in favor of computing it
+     directly.  */
+  const char *inferior_io_stderr = get_inferior_io_stderr ();
+
+  if (inferior_io_stderr == NULL)
+    inferior_io_stderr = "";
+  fprintf_filtered (gdb_stderr,
+		    _("Standard error for future runs of program being "
+		      "debugged is \"%s\".\n"), inferior_io_stderr);
+}
+
 char *
 get_inferior_args (void)
 {
@@ -3132,6 +3248,36 @@ Usage: set inferior-tty /dev/pts/1"),
 			    &setlist, &showlist);
   add_com_alias ("tty", "set inferior-tty", class_alias, 0);
 
+  add_setshow_filename_cmd ("inferior-stdin", class_run,
+			    &inferior_io_std_scratch[0], _("\
+Set standard input for future runs of program being debugged."), _("\
+Show standard input for future runs of program being debugged."), _("\
+Usage: set inferior-input /tmp/redirected.stdin"),
+			    set_inferior_stdin_command,
+			    show_inferior_stdin_command,
+			    &setlist, &showlist);
+  add_com_alias ("stdin", "set inferior-stdin", class_alias, 0);
+
+  add_setshow_filename_cmd ("inferior-stdout", class_run,
+			    &inferior_io_std_scratch[1], _("\
+Set standard output for future runs of program being debugged."), _("\
+Show standard output for future runs of program being debugged."), _("\
+Usage: set inferior-stdout /tmp/redirected.stdout"),
+			    set_inferior_stdout_command,
+			    show_inferior_stdout_command,
+			    &setlist, &showlist);
+  add_com_alias ("stdout", "set inferior-stdout", class_alias, 0);
+
+  add_setshow_filename_cmd ("inferior-stderr", class_run,
+			    &inferior_io_std_scratch[2], _("\
+Set standard error for future runs of program being debugged."), _("\
+Show standard error for future runs of program being debugged."), _("\
+Usage: set inferior-stderr /tmp/redirected.stderr"),
+			    set_inferior_stderr_command,
+			    show_inferior_stderr_command,
+			    &setlist, &showlist);
+  add_com_alias ("stderr", "set inferior-stderr", class_alias, 0);
+
   cmd_name = "args";
   add_setshow_string_noescape_cmd (cmd_name, class_run,
 				   &inferior_args_scratch, _("\
diff --git a/gdb/inferior.c b/gdb/inferior.c
index 04e9a28..6c6b5aa 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -99,6 +99,9 @@ free_inferior (struct inferior *inf)
   inferior_free_data (inf);
   xfree (inf->args);
   xfree (inf->terminal);
+  xfree (inf->standard_io[0]);
+  xfree (inf->standard_io[1]);
+  xfree (inf->standard_io[2]);
   free_environ (inf->environment);
   target_desc_info_free (inf->tdesc_info);
   xfree (inf->priv);
diff --git a/gdb/inferior.h b/gdb/inferior.h
index e09cb00..ed42bbd 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -77,6 +77,14 @@ extern void clear_sigint_trap (void);
 extern void set_inferior_io_terminal (const char *terminal_name);
 extern const char *get_inferior_io_terminal (void);
 
+/* Set/get file name for standard input/output/error.  */
+extern void set_inferior_io_stdin (const char *file_name);
+extern const char *get_inferior_io_stdin (void);
+extern void set_inferior_io_stdout (const char *file_name);
+extern const char *get_inferior_io_stdout (void);
+extern void set_inferior_io_stderr (const char *file_name);
+extern const char *get_inferior_io_stderr (void);
+
 /* Collected pid, tid, etc. of the debugged inferior.  When there's
    no inferior, ptid_get_pid (inferior_ptid) will be 0.  */
 
@@ -329,6 +337,9 @@ struct inferior
   /* The name of terminal device to use for I/O.  */
   char *terminal;
 
+  /* The names of files to use as standard input/output/error */
+  char *standard_io[3];
+
   /* Environment to use for running inferior,
      in format described in environ.h.  */
   struct gdb_environ *environment;
diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp
index 4395c98..2ae4f90 100644
--- a/gdb/testsuite/gdb.base/default.exp
+++ b/gdb/testsuite/gdb.base/default.exp
@@ -798,6 +798,12 @@ gdb_test "thread find" "Command requires an argument." "thread find"
 gdb_test "thread name" "No thread selected" "thread name"
 #test tty
 gdb_test "tty" "Argument required .filename to set it to\..*" "tty"
+#test stdin
+gdb_test "stdin" "Argument required .filename to set it to\..*" "stdin"
+#test stdout
+gdb_test "stdout" "Argument required .filename to set it to\..*" "stdout"
+#test stderr
+gdb_test "stderr" "Argument required .filename to set it to\..*" "stderr"
 #test until "u" abbreviation
 gdb_test "u" "The program is not being run." "until \"u\" abbreviation"
 #test until
-- 
2.4.3


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]