From patchwork Mon Oct 5 11:46:54 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cleber Rosa X-Patchwork-Id: 8923 Received: (qmail 96072 invoked by alias); 5 Oct 2015 11:47:15 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 95914 invoked by uid 89); 5 Oct 2015 11:47:13 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.7 required=5.0 tests=AWL, BAYES_00, SPF_HELO_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Mon, 05 Oct 2015 11:47:10 +0000 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (Postfix) with ESMTPS id 05772AED78 for ; Mon, 5 Oct 2015 11:47:09 +0000 (UTC) Received: from x220.tallawa.org (ovpn-113-80.phx2.redhat.com [10.3.113.80]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t95Bl8UE004694; Mon, 5 Oct 2015 07:47:08 -0400 From: Cleber Rosa To: gdb-patches@sourceware.org Cc: areis@redhat.com, palves@redhat.com, Cleber Rosa Subject: [PATCH 1/4] GDB: inferior standard I/O redirection Date: Mon, 5 Oct 2015 08:46:54 -0300 Message-Id: <1444045617-14526-2-git-send-email-crosa@redhat.com> In-Reply-To: <1444045617-14526-1-git-send-email-crosa@redhat.com> References: <1444045617-14526-1-git-send-email-crosa@redhat.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 * gdb.texinfo (info): Added section on commands stdin, stdout and stderr gdb/ChangeLog: 2015-10-05 Cleber Rosa * 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): : New field. gdb/testsuite/ChangeLog: 2015-10-05 Cleber Rosa * 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