This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH 1/4] GDB: inferior standard I/O redirection
- From: Cleber Rosa <crosa at redhat dot com>
- To: gdb-patches at sourceware dot org
- Cc: areis at redhat dot com, palves at redhat dot com, Cleber Rosa <crosa at redhat dot com>
- Date: Mon, 5 Oct 2015 08:46:54 -0300
- Subject: [PATCH 1/4] GDB: inferior standard I/O redirection
- Authentication-results: sourceware.org; auth=none
- References: <1444045617-14526-1-git-send-email-crosa at redhat dot com>
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