From patchwork Tue Oct 3 20:08:55 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sergio Durigan Junior X-Patchwork-Id: 23306 Received: (qmail 102068 invoked by alias); 3 Oct 2017 20:09:03 -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 101840 invoked by uid 89); 3 Oct 2017 20:09:02 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy= 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 ESMTP; Tue, 03 Oct 2017 20:08:59 +0000 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 02CCD4A702; Tue, 3 Oct 2017 20:08:57 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 02CCD4A702 Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=sergiodj@redhat.com Received: from localhost (unused-10-15-17-193.yyz.redhat.com [10.15.17.193]) by smtp.corp.redhat.com (Postfix) with ESMTPS id CD3CE5D962; Tue, 3 Oct 2017 20:08:55 +0000 (UTC) From: Sergio Durigan Junior To: Pedro Alves Cc: GDB Patches , Eli Zaretskii Subject: Re: [PATCH v5 2/3] Implement "set cwd" command on GDB References: <20170912042325.14927-1-sergiodj@redhat.com> <20170929225852.21872-1-sergiodj@redhat.com> <20170929225852.21872-3-sergiodj@redhat.com> <874lrg5goz.fsf@redhat.com> <6520ea6a-6be7-db93-1d3e-1b156bc95564@redhat.com> <87mv5841qt.fsf@redhat.com> <87y3osxj6i.fsf@redhat.com> Date: Tue, 03 Oct 2017 16:08:55 -0400 In-Reply-To: <87y3osxj6i.fsf@redhat.com> (Sergio Durigan Junior's message of "Tue, 03 Oct 2017 12:58:13 -0400") Message-ID: <871smkxaco.fsf@redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.2 (gnu/linux) MIME-Version: 1.0 X-IsSubscribed: yes On Tuesday, October 03 2017, I wrote: > In order to avoid yet-another-version, here's the updated patch with all > your comments addressed. Well, Pedro kindly pinged me on IRC to let me know that I had totally misinterpreted his request to move a paragraph from the "set cwd" section on gdb.texinfo to the "cd command" section. Anyway, here's the updated patch. Sorry about that. diff --git a/gdb/NEWS b/gdb/NEWS index 81c21b82cc..fcf454833f 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -67,6 +67,9 @@ QStartupWithShell * New commands +set|show cwd + Set and show the current working directory for the inferior. + set|show compile-gcc Set and show compilation command used for compiling and injecting code with the 'compile' commands. diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index dde67ee2b3..dad5ffa42e 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -1727,9 +1727,11 @@ The commands below can be used to select other frames by number or address."), Print working directory. This is used for your program as well.")); c = add_cmd ("cd", class_files, cd_command, _("\ -Set working directory to DIR for debugger and program being debugged.\n\ -The change does not take effect for the program being debugged\n\ -until the next time it is started."), &cmdlist); +Set working directory to DIR for debugger.\n\ +The debugger's current working directory specifies where scripts and other\n\ +files that can be loaded by GDB are located.\n\ +In order to change the inferior's current working directory, the recommended\n\ +way is to use the \"set cwd\" command."), &cmdlist); set_cmd_completer (c, filename_completer); add_com ("echo", class_support, echo_command, _("\ diff --git a/gdb/common/common-inferior.h b/gdb/common/common-inferior.h index 87c13009ed..515a8c0f4e 100644 --- a/gdb/common/common-inferior.h +++ b/gdb/common/common-inferior.h @@ -30,4 +30,8 @@ extern const char *get_exec_wrapper (); otherwise return 0 in that case. */ extern char *get_exec_file (int err); +/* Return the inferior's current working directory. If nothing has + been set, then return NULL. */ +extern const char *get_inferior_cwd (); + #endif /* ! COMMON_INFERIOR_H */ diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 9905ff6513..a68107dddb 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -2057,8 +2057,9 @@ environment} to change parts of the environment that affect your program. @xref{Environment, ,Your Program's Environment}. @item The @emph{working directory.} -Your program inherits its working directory from @value{GDBN}. You can set -the @value{GDBN} working directory with the @code{cd} command in @value{GDBN}. +You can set your program's working directory with the command +@kbd{set cwd}. If you do not set any working directory with this +command, your program will inherit @value{GDBN}'s working directory. @xref{Working Directory, ,Your Program's Working Directory}. @item The @emph{standard input and output.} @@ -2434,23 +2435,51 @@ variables to files that are only run when you sign on, such as @section Your Program's Working Directory @cindex working directory (of your program) -Each time you start your program with @code{run}, it inherits its -working directory from the current working directory of @value{GDBN}. -The @value{GDBN} working directory is initially whatever it inherited -from its parent process (typically the shell), but you can specify a new -working directory in @value{GDBN} with the @code{cd} command. - -The @value{GDBN} working directory also serves as a default for the commands -that specify files for @value{GDBN} to operate on. @xref{Files, ,Commands to -Specify Files}. +Each time you start your program with @code{run}, the inferior will be +initialized with the current working directory specified by the +@kbd{set cwd} command. If no directory has been specified by this +command, then the inferior will inherit @value{GDBN}'s current working +directory as its working directory. + +@table @code +@kindex set cwd +@cindex change inferior's working directory +@anchor{set cwd command} +@item set cwd @r{[}@var{directory}@r{]} +Set the inferior's working directory to @var{directory}, which will be +@code{glob}-expanded in order to resolve tildes (@file{~}). If no +argument has been specified, the command clears the setting and resets +it to an empty state. This setting has no effect on @value{GDBN}'s +working directory, and it only takes effect the next time you start +the inferior. The @file{~} in @var{directory} is a short for the +@dfn{home directory}, usually pointed to by the @env{HOME} environment +variable. On MS-Windows, if @env{HOME} is not defined, @value{GDBN} +uses the concatenation of @env{HOMEDRIVE} and @env{HOMEPATH} as +fallback. + +You can also change @value{GDBN}'s current working directory by using +the @code{cd} command. +@xref{cd command} + +@kindex show cwd +@cindex show inferior's working directory +@item show cwd +Show the inferior's working directory. If no directory has been +specified by @kbd{set cwd}, then the default inferior's working +directory is the same as @value{GDBN}'s working directory. -@table @code @kindex cd -@cindex change working directory +@cindex change @value{GDBN}'s working directory +@anchor{cd command} @item cd @r{[}@var{directory}@r{]} Set the @value{GDBN} working directory to @var{directory}. If not given, @var{directory} uses @file{'~'}. +The @value{GDBN} working directory serves as a default for the +commands that specify files for @value{GDBN} to operate on. +@xref{Files, ,Commands to Specify Files}. +@xref{set cwd command} + @kindex pwd @item pwd Print the @value{GDBN} working directory. diff --git a/gdb/gdbserver/inferiors.c b/gdb/gdbserver/inferiors.c index 3a45959000..57d9956ebb 100644 --- a/gdb/gdbserver/inferiors.c +++ b/gdb/gdbserver/inferiors.c @@ -29,6 +29,9 @@ struct thread_info *current_thread; #define get_thread(inf) ((struct thread_info *)(inf)) +/* The current working directory used to start the inferior. */ +static const char *current_inferior_cwd = NULL; + void add_inferior_to_list (struct inferior_list *list, struct inferior_list_entry *new_inferior) @@ -445,3 +448,11 @@ switch_to_thread (ptid_t ptid) gdb_assert (ptid != minus_one_ptid); current_thread = find_thread_ptid (ptid); } + +/* See common/common-inferior.h. */ + +const char * +get_inferior_cwd () +{ + return current_inferior_cwd; +} diff --git a/gdb/infcmd.c b/gdb/infcmd.c index da16f5ed6d..dbdf273ca5 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -103,6 +103,10 @@ static void run_command (char *, int); static char *inferior_args_scratch; +/* Scratch area where the new cwd will be stored by 'set cwd'. */ + +static char *inferior_cwd_scratch; + /* Scratch area where 'set inferior-tty' will store user-provided value. We'll immediate copy it into per-inferior storage. */ @@ -238,6 +242,60 @@ show_args_command (struct ui_file *file, int from_tty, deprecated_show_value_hack (file, from_tty, c, get_inferior_args ()); } +/* Set the inferior current working directory. If CWD is NULL, unset + the directory. */ + +static void +set_inferior_cwd (const char *cwd) +{ + struct inferior *inf = current_inferior (); + + gdb_assert (inf != NULL); + + if (cwd == NULL) + inf->cwd.reset (); + else + inf->cwd.reset (xstrdup (cwd)); +} + +/* See common/common-inferior.h. */ + +const char * +get_inferior_cwd () +{ + return current_inferior ()->cwd.get (); +} + +/* Handle the 'set cwd' command. */ + +static void +set_cwd_command (char *args, int from_tty, struct cmd_list_element *c) +{ + if (*inferior_cwd_scratch == '\0') + set_inferior_cwd (NULL); + else + set_inferior_cwd (inferior_cwd_scratch); +} + +/* Handle the 'show cwd' command. */ + +static void +show_cwd_command (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) +{ + const char *cwd = get_inferior_cwd (); + + if (cwd == NULL) + fprintf_filtered (gdb_stdout, + _("\ +You have not set the inferior's current working directory.\n\ +The inferior will inherit GDB's cwd.\n")); + else + fprintf_filtered (gdb_stdout, + _("Current working directory that will be used " + "when starting the inferior is \"%s\".\n"), cwd); +} + /* Compute command-line string given argument vector. This does the same shell processing as fork_inferior. */ @@ -3253,6 +3311,25 @@ Follow this command with any number of args, to be passed to the program."), gdb_assert (c != NULL); set_cmd_completer (c, filename_completer); + cmd_name = "cwd"; + add_setshow_string_noescape_cmd (cmd_name, class_run, + &inferior_cwd_scratch, _("\ +Set the current working directory to be used when the inferior is started.\n\ +Changing this setting does not have any effect on inferiors that are\n\ +already running."), + _("\ +Show the current working directory that is used when the inferior is started."), + _("\ +Use this command to change the current working directory that will be used\n\ +when the inferior is started. This setting does not affect GDB's current\n\ +working directory."), + set_cwd_command, + show_cwd_command, + &setlist, &showlist); + c = lookup_cmd (&cmd_name, setlist, "", -1, 1); + gdb_assert (c != NULL); + set_cmd_completer (c, filename_completer); + c = add_cmd ("environment", no_class, environment_info, _("\ The environment to give the program, or one variable's value.\n\ With an argument VAR, prints the value of environment variable VAR to\n\ diff --git a/gdb/inferior.h b/gdb/inferior.h index 7f2d53e5b3..498d74706a 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -355,6 +355,10 @@ public: should never be freed. */ char **argv = NULL; + /* The current working directory that will be used when starting + this inferior. */ + gdb::unique_xmalloc_ptr cwd; + /* The name of terminal device to use for I/O. */ char *terminal = NULL; diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c index 6ff119768c..ee1340b832 100644 --- a/gdb/nat/fork-inferior.c +++ b/gdb/nat/fork-inferior.c @@ -25,6 +25,7 @@ #include "common-inferior.h" #include "common-gdbthread.h" #include "signals-state-save-restore.h" +#include "gdb_tilde_expand.h" #include extern char **environ; @@ -298,6 +299,8 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs, char **save_our_env; int i; int save_errno; + const char *inferior_cwd; + std::string expanded_inferior_cwd; /* If no exec file handed to us, get it from the exec-file command -- with a good, common error message if none is specified. */ @@ -339,6 +342,18 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs, the parent and child flushing the same data after the fork. */ gdb_flush_out_err (); + /* Check if the user wants to set a different working directory for + the inferior. */ + inferior_cwd = get_inferior_cwd (); + + if (inferior_cwd != NULL) + { + /* Expand before forking because between fork and exec, the child + process may only execute async-signal-safe operations. */ + expanded_inferior_cwd = gdb_tilde_expand (inferior_cwd); + inferior_cwd = expanded_inferior_cwd.c_str (); + } + /* 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... */ @@ -374,6 +389,14 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs, UIs. */ close_most_fds (); + /* Change to the requested working directory if the user + requested it. */ + if (inferior_cwd != NULL) + { + if (chdir (inferior_cwd) < 0) + trace_start_error_with_name (inferior_cwd); + } + if (debug_fork) sleep (debug_fork); diff --git a/gdb/testsuite/gdb.base/set-cwd.c b/gdb/testsuite/gdb.base/set-cwd.c new file mode 100644 index 0000000000..58738830d6 --- /dev/null +++ b/gdb/testsuite/gdb.base/set-cwd.c @@ -0,0 +1,31 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2017 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include +#include + +static char dir[4096]; + +int +main (int argc, char *argv[]) +{ + const char *home = getenv ("HOME"); + + getcwd (dir, 4096); + + return 0; /* break-here */ +} diff --git a/gdb/testsuite/gdb.base/set-cwd.exp b/gdb/testsuite/gdb.base/set-cwd.exp new file mode 100644 index 0000000000..0d536488e4 --- /dev/null +++ b/gdb/testsuite/gdb.base/set-cwd.exp @@ -0,0 +1,197 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2017 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +if { [use_gdb_stub] || [target_info gdb_protocol] == "extended-remote" } { + untested "not implemented on remote servers" + return +} + +standard_testfile + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile debug] } { + return -1 +} + +# Test that tilde expansion works fine. + +proc_with_prefix test_tilde_expansion { } { + global decimal gdb_prompt hex + + gdb_test_no_output "set cwd ~/" "set inferior cwd to ~/ dir" + + if { ![runto_main] } { + untested "could not run to main" + return -1 + } + + gdb_breakpoint [gdb_get_line_number "break-here"] + gdb_continue_to_breakpoint "break-here" ".* break-here .*" + + set home "" + set test "print home var" + gdb_test_multiple "print home" $test { + -re "\\\$$decimal = $hex \"\(.+\)\"\r\n$gdb_prompt $" { + set home $expect_out(1,string) + pass $test + } + } + + if { $home == "" } { + untested "could not retrieve home var" + return + } + + set curdir "" + set test "print dir var" + gdb_test_multiple "print dir" $test { + -re "\\\$$decimal = \"\(.+\)\"\(, .*repeats.*\)?\r\n$gdb_prompt $" { + set curdir $expect_out(1,string) + pass $test + } + } + + if { $curdir == "" } { + untested "could not retrieve dir var" + return + } + + gdb_assert [string equal $curdir $home] \ + "successfully chdir'd into home" +} + +# The temporary directory that we will use to start the inferior. +set tmpdir [standard_output_file ""] + +# Test that when we "set cwd" the inferior will be started under the +# correct working directory and GDB will not be affected by this. + +proc_with_prefix test_cd_into_dir { } { + global decimal gdb_prompt tmpdir + + set gdb_cwd_before_run "" + set test "pwd before run" + gdb_test_multiple "pwd" $test { + -re "Working directory \(.*\)\.\r\n$gdb_prompt $" { + set gdb_cwd_before_run $expect_out(1,string) + pass $test + } + } + + if { $gdb_cwd_before_run == "" } { + untested "could not obtain GDB cwd before run" + return + } + + # This test only makes sense if $tmpdir != $gdb_cwd_before_run + if { ![gdb_assert ![string equal $tmpdir $gdb_cwd_before_run] \ + "make sure that tmpdir and GDB's cwd are different"] } { + return -1 + } + + gdb_test_no_output "set cwd $tmpdir" "set inferior cwd to temp dir" + + if { ![runto_main] } { + untested "could not run to main" + return -1 + } + + gdb_breakpoint [gdb_get_line_number "break-here"] + gdb_continue_to_breakpoint "break-here" ".* break-here .*" + + gdb_test "print dir" "\\\$$decimal = \"$tmpdir\", .*" \ + "inferior cwd is correctly set" + + set gdb_cwd_after_run "" + set test "pwd after run" + gdb_test_multiple "pwd" $test { + -re "Working directory \(.*\)\.\r\n$gdb_prompt $" { + set gdb_cwd_after_run $expect_out(1,string) + pass $test + } + } + + if { $gdb_cwd_after_run == "" } { + untested "could not obtain GDB cwd after run" + return + } + + gdb_assert [string equal $gdb_cwd_before_run $gdb_cwd_after_run] \ + "GDB cwd is unchanged after running inferior" +} + +# Test that executing "set cwd" without arguments will reset the +# inferior's cwd setting to its previous state. + +proc_with_prefix test_cwd_reset { } { + global decimal gdb_prompt tmpdir + + set gdb_cwd "" + set test "GDB cwd" + gdb_test_multiple "pwd" $test { + -re "Working directory \(.*\)\.\r\n$gdb_prompt $" { + set gdb_cwd $expect_out(1,string) + } + } + + if { $gdb_cwd == "" } { + untested "could not obtain GDB cwd" + return + } + + # This test only makes sense if $tmpdir != $gdb_cwd. + if { ![gdb_assert ![string equal $tmpdir $gdb_cwd] \ + "make sure that tmpdir and GDB's cwd are different"] } { + return -1 + } + + gdb_test_no_output "set cwd $tmpdir" "set inferior cwd to temp dir" + + with_test_prefix "running with set cwd" { + if { ![runto_main] } { + untested "could not run to main" + return -1 + } + } + + gdb_breakpoint [gdb_get_line_number "break-here"] + gdb_continue_to_breakpoint "break-here" ".* break-here .*" + + gdb_test "print dir" "\\\$$decimal = \"$tmpdir\", .*" \ + "inferior cwd is correctly set" + + # Reset the inferior's cwd. + gdb_test_no_output "set cwd" "resetting inferior cwd" + + with_test_prefix "running without set cwd" { + if { ![runto_main] } { + untested "could not run to main" + return -1 + } + } + + gdb_breakpoint [gdb_get_line_number "break-here"] + gdb_continue_to_breakpoint "break-here" ".* break-here .*" + + gdb_test "print dir" "\\\$$decimal = \"$gdb_cwd\", .*" \ + "inferior cwd got reset correctly" +} + +test_cd_into_dir +clean_restart $binfile +test_tilde_expansion +clean_restart $binfile +test_cwd_reset diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c index 3e1894410d..1ef38fb32b 100644 --- a/gdb/windows-nat.c +++ b/gdb/windows-nat.c @@ -66,6 +66,7 @@ #include "x86-nat.h" #include "complaints.h" #include "inf-child.h" +#include "gdb_tilde_expand.h" #define AdjustTokenPrivileges dyn_AdjustTokenPrivileges #define DebugActiveProcessStop dyn_DebugActiveProcessStop @@ -2428,6 +2429,7 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file, #ifdef __CYGWIN__ cygwin_buf_t real_path[__PMAX]; cygwin_buf_t shell[__PMAX]; /* Path to shell */ + cygwin_buf_t infcwd[__PMAX]; const char *sh; cygwin_buf_t *toexec; cygwin_buf_t *cygallargs; @@ -2465,6 +2467,17 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file, if (!exec_file) error (_("No executable specified, use `target exec'.")); + const char *inferior_cwd = get_inferior_cwd (); + std::string expanded_infcwd; + if (inferior_cwd != NULL) + { + expanded_infcwd = gdb_tilde_expand (inferior_cwd); + /* Mirror slashes on inferior's cwd. */ + std::replace (expanded_infcwd.begin (), expanded_infcwd.end (), + '/', '\\'); + inferior_cwd = expanded_infcwd.c_str (); + } + memset (&si, 0, sizeof (si)); si.cb = sizeof (si); @@ -2514,6 +2527,11 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file, flags |= DEBUG_PROCESS; } + if (inferior_cwd != NULL + && cygwin_conv_path (CCP_POSIX_TO_WIN_W, inferior_cwd, + infcwd, strlen (inferior_cwd)) < 0) + error (_("Error converting inferior cwd: %d"), errno); + #ifdef __USEWIDE args = (cygwin_buf_t *) alloca ((wcslen (toexec) + wcslen (cygallargs) + 2) * sizeof (wchar_t)); @@ -2574,7 +2592,8 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file, TRUE, /* inherit handles */ flags, /* start flags */ w32_env, /* environment */ - NULL, /* current directory */ + inferior_cwd != NULL ? infcwd : NULL, /* current + directory */ &si, &pi); if (w32_env) @@ -2697,7 +2716,7 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file, TRUE, /* inherit handles */ flags, /* start flags */ w32env, /* environment */ - NULL, /* current directory */ + inferior_cwd, /* current directory */ &si, &pi); if (tty != INVALID_HANDLE_VALUE)