From patchwork Wed May 6 19:51:06 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cleber Rosa X-Patchwork-Id: 6600 Received: (qmail 97965 invoked by alias); 6 May 2015 19:51:29 -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 97947 invoked by uid 89); 6 May 2015 19:51:28 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.2 required=5.0 tests=AWL, BAYES_00, KAM_LAZY_DOMAIN_SECURITY, SPF_HELO_PASS, T_RP_MATCHES_RCVD autolearn=no 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; Wed, 06 May 2015 19:51:26 +0000 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id t46JpPrO008971 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL) for ; Wed, 6 May 2015 15:51:25 -0400 Received: from x220.homelab.tallawa.org.com (ovpn-112-106.phx2.redhat.com [10.3.112.106]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t46JpKUl013275; Wed, 6 May 2015 15:51:21 -0400 From: Cleber Rosa To: gdb-patches@sourceware.org Cc: palves@redhat.com, areis@redhat.com, Cleber Rosa Subject: [RFC][PATCH] GDB: inferior standard I/O redirection Date: Wed, 6 May 2015 16:51:06 -0300 Message-Id: <1430941866-21976-1-git-send-email-crosa@redhat.com> While reviewing the proposed "GDBServer stderr redirection" patches, Pedro Alves pointed out that a better approach would be to actually implement "set inferior-std{in,out,err}" commands to complement "set inferior-tty". This would need to be done at both the GDB level and also at the server level. This is a functional RFC for GDB only. Being an RFC it lacks documentation, NEWS file mentions, ChangeLog and (DejaGnu) tests. Since this a feature to be used in Avocado[1], there's a simple (Avocado) test at: https://github.com/clebergnu/avocado/blob/gdb-inferior-io-test/examples/tests/gdb-inferior-io.py I will surely appreciate any comments to help me shape a v1 of this feature. [1] - http://avocado-framework.github.io --- gdb/fork-child.c | 54 +++++++++++++++++++++ gdb/infcmd.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ gdb/inferior.c | 3 ++ gdb/inferior.h | 11 +++++ 4 files changed, 208 insertions(+) diff --git a/gdb/fork-child.c b/gdb/fork-child.c index 66c07fb..4e11150 100644 --- a/gdb/fork-child.c +++ b/gdb/fork-child.c @@ -108,6 +108,51 @@ escape_bang_in_quoted_argument (const char *shell_file) return 0; } +/* Helper function for that attempts to open a file and if successfull + redirects another file descriptor to the one just opened. */ + +static int +set_std_io_helper (const char *file_name, int std_io_fd, int flags, mode_t mode) +{ + int fd; + + if (!file_name) + return 0; + + fd = open (file_name, flags, mode); + if (fd > 0) + dup2 (fd, std_io_fd); + return fd; +} + +/* Performs the redirection of stdin, stdout and stderr, if requested. + There's a single error indication if any of these redirection + fail, which seem to be a good enough granularity. */ + +static int +set_std_io (void) +{ + int fd; + int result = 0; + + fd = set_std_io_helper (get_inferior_io_stdin(), 0, + O_RDONLY, S_IRUSR | S_IWUSR); + if (fd < 0) + result = 1; + + fd = set_std_io_helper (get_inferior_io_stdout(), 1, + O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) + result = 1; + + fd = set_std_io_helper (get_inferior_io_stderr(), 2, + O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) + result = 1; + + 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 @@ -358,6 +403,15 @@ fork_inferior (char *exec_file_arg, char *allargs, char **env, path to find $SHELL. Rich Pixley says so, and I agree. */ environ = env; + /* Set the inferior process standard I/O (input, output, error) + redirection if any was set with the stdin/stdout/stderr + commands. */ + if (set_std_io ()) + fprintf_unfiltered (gdb_stderr, + "Could not set one of stdin, stdout or stderr for " + "%s\n", exec_file); + + if (exec_fun != NULL) (*exec_fun) (argv[0], argv, env); else diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 7e2484b..a1a9a7b 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -118,6 +118,11 @@ static char *inferior_args_scratch; static char *inferior_io_terminal_scratch; +/* Scratch are 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 @@ -185,6 +190,111 @@ show_inferior_tty_command (struct ui_file *file, int from_tty, "is \"%s\".\n"), inferior_io_terminal); } +void +set_inferior_io_stdin (const char *file_name) +{ + xfree (current_inferior ()->standard_io[0]); + current_inferior ()->standard_io[0] = file_name ? xstrdup (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) +{ + xfree (current_inferior ()->standard_io[1]); + current_inferior ()->standard_io[1] = file_name ? xstrdup (file_name) : 0; +} + +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) +{ + xfree (current_inferior ()->standard_io[2]); + current_inferior ()->standard_io[2] = file_name ? xstrdup (file_name) : 0; +} + +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) { @@ -2986,6 +3096,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 ba320b5..8f11751 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 2530777..308a2b9 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. */ @@ -345,6 +353,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;