[v3,4/5] Implement "set cwd" command on GDB

Message ID 20170921225926.23132-5-sergiodj@redhat.com
State New, archived
Headers

Commit Message

Sergio Durigan Junior Sept. 21, 2017, 10:59 p.m. UTC
  This is the actual implementation of the "set/show cwd" commands on
GDB.  The way they work is:

- If the user sets the inferior's cwd by using "set cwd", then this
  directory is saved into current_inferior ()->cwd and is used when
  the inferior is started (see below).

- If the user doesn't set the inferior's cwd by using "set cwd", but
  rather use the "cd" command as before, then this directory is
  inherited by the inferior because GDB will have chdir'd into it.

On Unix-like hosts, the way the directory is changed before the
inferior execution is by expanding the user set directory before the
fork, and then "chdir" after the call to fork/vfork on
"fork_inferior", but before the actual execution.  On Windows, the
inferior cwd set by the user is passed directly to the CreateProcess
call, which takes care of the actual chdir for us.

This way, we'll make sure that GDB's cwd is not affected by the user
set cwd.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* NEWS (New commands): Mention "set/show cwd".
	* cli/cli-cmds.c (_initialize_cli_cmds): Mention "set cwd" on
	"cd" command's help text.
	* common/common-inferior.h (get_inferior_cwd): New prototype.
	* infcmd.c (inferior_cwd_scratch): New global variable.
	(set_inferior_cwd): New function.
	(get_inferior_cwd): Likewise.
	(set_cwd_command): Likewise.
	(show_cwd_command): Likewise.
	(_initialize_infcmd): Add "set/show cwd" commands.
	* inferior.h (class inferior) <cwd>: New field.
	* nat/fork-inferior.c: Include "gdb_tilde_expand.h".
	(fork_inferior): Change inferior's cwd before its execution.
	* windows-nat.c (windows_create_inferior): Pass inferior's cwd
	to CreateProcess.

gdb/gdbserver/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* inferiors.c (current_inferior_cwd): New global variable.
	(get_inferior_cwd): New function.
	* inferiors.h (struct process_info) <cwd>: New field.

gdb/doc/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* gdb.texinfo (Starting your Program) <The working directory.>:
	Mention new "set cwd" command.
	(Working Directory) <Your Program's Working Directory>:
	Rephrase to explain that "set cwd" exists and is the default
	way to change the inferior's cwd.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* gdb.base/set-cwd.c: New file.
	* gdb.base/set-cwd.exp: Likewise.
---
 gdb/NEWS                           |   3 +
 gdb/cli/cli-cmds.c                 |   8 ++-
 gdb/common/common-inferior.h       |   4 ++
 gdb/doc/gdb.texinfo                |  35 +++++++---
 gdb/gdbserver/inferiors.c          |  11 +++
 gdb/infcmd.c                       |  72 +++++++++++++++++++
 gdb/inferior.h                     |   4 ++
 gdb/nat/fork-inferior.c            |  18 +++++
 gdb/testsuite/gdb.base/set-cwd.c   |  32 +++++++++
 gdb/testsuite/gdb.base/set-cwd.exp | 140 +++++++++++++++++++++++++++++++++++++
 gdb/windows-nat.c                  |  13 +++-
 11 files changed, 327 insertions(+), 13 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/set-cwd.c
 create mode 100644 gdb/testsuite/gdb.base/set-cwd.exp
  

Comments

Eli Zaretskii Sept. 22, 2017, 8:02 a.m. UTC | #1
> From: Sergio Durigan Junior <sergiodj@redhat.com>
> Cc: Pedro Alves <palves@redhat.com>,	Sergio Durigan Junior <sergiodj@redhat.com>
> Date: Thu, 21 Sep 2017 18:59:25 -0400
> 
> This is the actual implementation of the "set/show cwd" commands on
> GDB.  The way they work is:
> 
> - If the user sets the inferior's cwd by using "set cwd", then this
>   directory is saved into current_inferior ()->cwd and is used when
>   the inferior is started (see below).
> 
> - If the user doesn't set the inferior's cwd by using "set cwd", but
>   rather use the "cd" command as before, then this directory is
>   inherited by the inferior because GDB will have chdir'd into it.
> 
> On Unix-like hosts, the way the directory is changed before the
> inferior execution is by expanding the user set directory before the
> fork, and then "chdir" after the call to fork/vfork on
> "fork_inferior", but before the actual execution.  On Windows, the
> inferior cwd set by the user is passed directly to the CreateProcess
> call, which takes care of the actual chdir for us.

Thanks, I have some comments.

> diff --git a/gdb/NEWS b/gdb/NEWS
> index 549f511b29..c131713293 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -64,6 +64,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.

This part is OK.

> +@kindex set cwd
> +@cindex change inferior's working directory
> +@item set cwd @r{[}@var{directory}@r{]}
> +Set the inferior's working directory to @var{directory}.  If not
> +given, @var{directory} uses @file{'~'}.

I think we should document here what does "~" mean on MS-Windows,
especially since, when HOME is not in the environment, Gnulib's glob
module doesn't behave according to MS platform recommendations (which
say not to create files directly below %HOMEDRIVE%%HOMEPATH%).

More generally, I think we should say here that the argument is
glob-expanded, because this is user-visible behavior (right?).  Also,
how will TAB-completion react to input of this command? will it expand
the input typed so far?

> +@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 @code{set cwd}, then the default inferior's working
> +directory is the same as @value{GDBN}'s working directory.

Does this show the original value typed by the user, or the expanded
value?  E.g., if the user types "set cwd ~/foo", what will "show cwd"
display?  If it shows the unexpanded form, does that mean the actual
cwd will change if, say, HOME changes?

Should we store the cwd after tilde-expansion?

> @@ -2461,6 +2462,7 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file,
>    BOOL ret;
>    DWORD flags = 0;
>    const char *inferior_io_terminal = get_inferior_io_terminal ();
> +  const char *inferior_cwd = get_inferior_cwd ();
>  
>    if (!exec_file)
>      error (_("No executable specified, use `target exec'."));
> @@ -2488,8 +2490,15 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file,
>  	error (_("Error starting executable: %d"), errno);
>        cygallargs = (wchar_t *) alloca (len * sizeof (wchar_t));
>        mbstowcs (cygallargs, allargs, len);
> +
> +      len = mbstowcs (NULL, inferior_cwd, 0) + 1;
> +      if (len == (size_t) -1)
> +	error (_("Invalid cwd for inferior: %d"), errno);
> +      infcwd = (wchar_t *) alloca (len * sizeof (wchar_t));
> +      mbstowcs (infcwd, inferior_cwd, len);
>  #else  /* !__USEWIDE */
>        cygallargs = allargs;
> +      infcwd = (cygwin_buf_t *) inferior_cwd;
>  #endif
>      }
>    else
> @@ -2574,7 +2583,7 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file,
>  		       TRUE,	/* inherit handles */
>  		       flags,	/* start flags */
>  		       w32_env,	/* environment */
> -		       NULL,	/* current directory */
> +		       infcwd,	/* current directory */
>  		       &si,
>  		       &pi);
>    if (w32_env)
> @@ -2697,7 +2706,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)

This seems to pass the unexpanded cwd directly to CreateProcess.  I
don't think this will work on Windows, as this directory is not
interpreted by any shell, so "~" will cause errors.  I think we should
pass this via gdb_tilde_expand, like we do in the Unix case, and I
also think we should mirror all the slashes in the result, just in
case.
  
Pedro Alves Sept. 22, 2017, 12:31 p.m. UTC | #2
On 09/22/2017 09:02 AM, Eli Zaretskii wrote:
>> From: Sergio Durigan Junior <sergiodj@redhat.com>

> 
>> +@kindex set cwd
>> +@cindex change inferior's working directory
>> +@item set cwd @r{[}@var{directory}@r{]}
>> +Set the inferior's working directory to @var{directory}.  If not
>> +given, @var{directory} uses @file{'~'}.
> 
> I think we should document here what does "~" mean on MS-Windows,
> especially since, when HOME is not in the environment, Gnulib's glob
> module doesn't behave according to MS platform recommendations (which
> say not to create files directly below %HOMEDRIVE%%HOMEPATH%).
> 
> More generally, I think we should say here that the argument is
> glob-expanded, because this is user-visible behavior (right?).  Also,
> how will TAB-completion react to input of this command? will it expand
> the input typed so far?

Actually, should the command default to ~ at all?  Shouldn't we
make "set cwd" clear the setting to the default state, i.e.,
empty?  Otherwise, how do you get back to the default state?

>> +@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 @code{set cwd}, then the default inferior's working
>> +directory is the same as @value{GDBN}'s working directory.
> 
> Does this show the original value typed by the user, or the expanded
> value?  E.g., if the user types "set cwd ~/foo", what will "show cwd"
> display?  If it shows the unexpanded form, does that mean the actual
> cwd will change if, say, HOME changes?
> 
> Should we store the cwd after tilde-expansion?

I don't think so, because this is an inferior setting.  I.e.,
e.g., when remote debugging, only the target (remote side) can
do the expansion, and the user may set this before connecting
to a target (remote, native, etc.), even.

Thanks,
Pedro Alves
  
Sergio Durigan Junior Sept. 22, 2017, 6 p.m. UTC | #3
On Friday, September 22 2017, Eli Zaretskii wrote:

>> +@kindex set cwd
>> +@cindex change inferior's working directory
>> +@item set cwd @r{[}@var{directory}@r{]}
>> +Set the inferior's working directory to @var{directory}.  If not
>> +given, @var{directory} uses @file{'~'}.
>
> I think we should document here what does "~" mean on MS-Windows,
> especially since, when HOME is not in the environment, Gnulib's glob
> module doesn't behave according to MS platform recommendations (which
> say not to create files directly below %HOMEDRIVE%%HOMEPATH%).

Sure, but just to be clear, this text was strongly based on another part
of the docs, which also mentions '~' without explaining further.

As I am totally out of the loop when it comes to Windows environments,
I'd appreciate a suggestion for the new text.

> More generally, I think we should say here that the argument is
> glob-expanded, because this is user-visible behavior (right?).  Also,
> how will TAB-completion react to input of this command? will it expand
> the input typed so far?

TAB-completion will not change anything; we don't have such a facility
on GDB AFAIK.  But it will complete the input.

>> +@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 @code{set cwd}, then the default inferior's working
>> +directory is the same as @value{GDBN}'s working directory.
>
> Does this show the original value typed by the user, or the expanded
> value?  E.g., if the user types "set cwd ~/foo", what will "show cwd"
> display?  If it shows the unexpanded form, does that mean the actual
> cwd will change if, say, HOME changes?

Pedro and I had a conversation about this specific topic yesterday, and
the decision was that the host should not do any path expansion on this
case.  Therefore, whatever the user sets with "set cwd" is not expanded
until the inferior starts, which means that it is the target who
performs the expansion.

> Should we store the cwd after tilde-expansion?

I don't think so.  Or at least I cannot see a reason to do that.

>> @@ -2461,6 +2462,7 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file,
>>    BOOL ret;
>>    DWORD flags = 0;
>>    const char *inferior_io_terminal = get_inferior_io_terminal ();
>> +  const char *inferior_cwd = get_inferior_cwd ();
>>  
>>    if (!exec_file)
>>      error (_("No executable specified, use `target exec'."));
>> @@ -2488,8 +2490,15 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file,
>>  	error (_("Error starting executable: %d"), errno);
>>        cygallargs = (wchar_t *) alloca (len * sizeof (wchar_t));
>>        mbstowcs (cygallargs, allargs, len);
>> +
>> +      len = mbstowcs (NULL, inferior_cwd, 0) + 1;
>> +      if (len == (size_t) -1)
>> +	error (_("Invalid cwd for inferior: %d"), errno);
>> +      infcwd = (wchar_t *) alloca (len * sizeof (wchar_t));
>> +      mbstowcs (infcwd, inferior_cwd, len);
>>  #else  /* !__USEWIDE */
>>        cygallargs = allargs;
>> +      infcwd = (cygwin_buf_t *) inferior_cwd;
>>  #endif
>>      }
>>    else
>> @@ -2574,7 +2583,7 @@ windows_create_inferior (struct target_ops *ops, const char *exec_file,
>>  		       TRUE,	/* inherit handles */
>>  		       flags,	/* start flags */
>>  		       w32_env,	/* environment */
>> -		       NULL,	/* current directory */
>> +		       infcwd,	/* current directory */
>>  		       &si,
>>  		       &pi);
>>    if (w32_env)
>> @@ -2697,7 +2706,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)
>
> This seems to pass the unexpanded cwd directly to CreateProcess.  I
> don't think this will work on Windows, as this directory is not
> interpreted by any shell, so "~" will cause errors.  I think we should
> pass this via gdb_tilde_expand, like we do in the Unix case, and I
> also think we should mirror all the slashes in the result, just in
> case.

Hm, you're right.  I will call "gdb_tilde_expand" here.  I'm not sure
what you mean by "mirror all the slashes in the result".  Do you mean
"escape the slashes"?

Thanks,
  
Sergio Durigan Junior Sept. 22, 2017, 6:15 p.m. UTC | #4
On Friday, September 22 2017, Pedro Alves wrote:

> On 09/22/2017 09:02 AM, Eli Zaretskii wrote:
>>> From: Sergio Durigan Junior <sergiodj@redhat.com>
>
>> 
>>> +@kindex set cwd
>>> +@cindex change inferior's working directory
>>> +@item set cwd @r{[}@var{directory}@r{]}
>>> +Set the inferior's working directory to @var{directory}.  If not
>>> +given, @var{directory} uses @file{'~'}.
>> 
>> I think we should document here what does "~" mean on MS-Windows,
>> especially since, when HOME is not in the environment, Gnulib's glob
>> module doesn't behave according to MS platform recommendations (which
>> say not to create files directly below %HOMEDRIVE%%HOMEPATH%).
>> 
>> More generally, I think we should say here that the argument is
>> glob-expanded, because this is user-visible behavior (right?).  Also,
>> how will TAB-completion react to input of this command? will it expand
>> the input typed so far?
>
> Actually, should the command default to ~ at all?  Shouldn't we
> make "set cwd" clear the setting to the default state, i.e.,
> empty?  Otherwise, how do you get back to the default state?

That's a good point.  "set cwd" currently mimics what "cd" does, and
that's the reason for this "default is to use ~" decision.

I can certainly make "set cwd" without arguments to clear out whatever
has been set by the user.  I think that's a more sensible decision
indeed.

Thanks,
  
Eli Zaretskii Sept. 22, 2017, 6:56 p.m. UTC | #5
> From: Sergio Durigan Junior <sergiodj@redhat.com>
> Cc: gdb-patches@sourceware.org,  palves@redhat.com
> Date: Fri, 22 Sep 2017 14:00:51 -0400
> 
> On Friday, September 22 2017, Eli Zaretskii wrote:
> 
> >> +@kindex set cwd
> >> +@cindex change inferior's working directory
> >> +@item set cwd @r{[}@var{directory}@r{]}
> >> +Set the inferior's working directory to @var{directory}.  If not
> >> +given, @var{directory} uses @file{'~'}.
> >
> > I think we should document here what does "~" mean on MS-Windows,
> > especially since, when HOME is not in the environment, Gnulib's glob
> > module doesn't behave according to MS platform recommendations (which
> > say not to create files directly below %HOMEDRIVE%%HOMEPATH%).
> 
> Sure, but just to be clear, this text was strongly based on another part
> of the docs, which also mentions '~' without explaining further.
> 
> As I am totally out of the loop when it comes to Windows environments,
> I'd appreciate a suggestion for the new text.

If you write the Unix part, I can propose how to amend it to cover
Windows.  OK?

> > Does this show the original value typed by the user, or the expanded
> > value?  E.g., if the user types "set cwd ~/foo", what will "show cwd"
> > display?  If it shows the unexpanded form, does that mean the actual
> > cwd will change if, say, HOME changes?
> 
> Pedro and I had a conversation about this specific topic yesterday, and
> the decision was that the host should not do any path expansion on this
> case.  Therefore, whatever the user sets with "set cwd" is not expanded
> until the inferior starts, which means that it is the target who
> performs the expansion.

There's no contradiction between these two sides of this issue.  For
native debugging, host == target, so the host can expand.  For the
non-native debugging, you can ask the target to do the expansion and
store the result.  Either way IMO is better than expanding at run
time, because the latter makes the expansion dependent on factors
which could be out of the user control, and also requires every use
of the value to call gdb_tilde_expand, thus wasting cycles.

> > This seems to pass the unexpanded cwd directly to CreateProcess.  I
> > don't think this will work on Windows, as this directory is not
> > interpreted by any shell, so "~" will cause errors.  I think we should
> > pass this via gdb_tilde_expand, like we do in the Unix case, and I
> > also think we should mirror all the slashes in the result, just in
> > case.
> 
> Hm, you're right.  I will call "gdb_tilde_expand" here.  I'm not sure
> what you mean by "mirror all the slashes in the result".  Do you mean
> "escape the slashes"?

No, I mean convert forward slashes to backslashes.

Thanks.
  
Pedro Alves Sept. 22, 2017, 7:24 p.m. UTC | #6
On 09/22/2017 07:56 PM, Eli Zaretskii wrote:
> There's no contradiction between these two sides of this issue.  For
> native debugging, host == target, so the host can expand.  For the
> non-native debugging, you can ask the target to do the expansion and
> store the result.  

You can't, not as the actual value of the setting, because the
setting can be tweaked before GDB is even connected to a target.

I.e., you can do:

(gdb) set cwd ~  # I haven't even connected to a target yet.
                 # Where should this be expanded? 
                 # On host may be incorrect.
                 # '~' -> /home/pedro on this machine
(gdb) target extended-remote foo:9999
(gdb) start
error: /home/pedro does not exist   
# '~' is /mount/home/pedro on 'foo'
(gdb) kill
(gdb) target extended-remote bar:9999
(gdb) start
error: /home/pedro does not exist
# '~' is /nfs/home/palves on 'bar'


It may be useful to display the expanded path as extra info,
like in:

(gdb) set cwd ~foo/bar
(gdb) show cwd
The current directory is ~foo/bar (/home/foo/bar)
                                   ^^^^^^^^^^^^^

though that'd require a new remote protocol packet.

> Either way IMO is better than expanding at run
> time, because the latter makes the expansion dependent on factors
> which could be out of the user control, 

I don't see what the problem is here.

> and also requires every use
> of the value to call gdb_tilde_expand, thus wasting cycles.

I don't think that significant, compared to all the other
work / syscalls / remote protocol roundtrips that we have
to do to start a process.

Thanks,
Pedro Alves
  
Eli Zaretskii Sept. 22, 2017, 7:41 p.m. UTC | #7
> Cc: gdb-patches@sourceware.org
> From: Pedro Alves <palves@redhat.com>
> Date: Fri, 22 Sep 2017 20:24:40 +0100
>
> (gdb) set cwd ~  # I haven't even connected to a target yet.
>                  # Where should this be expanded? 
>                  # On host may be incorrect.
>                  # '~' -> /home/pedro on this machine

You can expand it when you connect.  The value has no use until then
anyway.

> > Either way IMO is better than expanding at run
> > time, because the latter makes the expansion dependent on factors
> > which could be out of the user control, 
> 
> I don't see what the problem is here.
> 
> > and also requires every use
> > of the value to call gdb_tilde_expand, thus wasting cycles.
> 
> I don't think that significant, compared to all the other
> work / syscalls / remote protocol roundtrips that we have
> to do to start a process.

It may be insignificant in terms of CPU usage, but it's a nuisance
that people will keep forgetting to do, as demonstrated in this case.

Anyway, I will shut up now.  I just feel that keeping this unexpanded
is wrong in the long run, that's all.
  
Sergio Durigan Junior Sept. 22, 2017, 8:24 p.m. UTC | #8
On Friday, September 22 2017, Eli Zaretskii wrote:

>> From: Sergio Durigan Junior <sergiodj@redhat.com>
>> Cc: gdb-patches@sourceware.org,  palves@redhat.com
>> Date: Fri, 22 Sep 2017 14:00:51 -0400
>> 
>> On Friday, September 22 2017, Eli Zaretskii wrote:
>> 
>> >> +@kindex set cwd
>> >> +@cindex change inferior's working directory
>> >> +@item set cwd @r{[}@var{directory}@r{]}
>> >> +Set the inferior's working directory to @var{directory}.  If not
>> >> +given, @var{directory} uses @file{'~'}.
>> >
>> > I think we should document here what does "~" mean on MS-Windows,
>> > especially since, when HOME is not in the environment, Gnulib's glob
>> > module doesn't behave according to MS platform recommendations (which
>> > say not to create files directly below %HOMEDRIVE%%HOMEPATH%).
>> 
>> Sure, but just to be clear, this text was strongly based on another part
>> of the docs, which also mentions '~' without explaining further.
>> 
>> As I am totally out of the loop when it comes to Windows environments,
>> I'd appreciate a suggestion for the new text.
>
> If you write the Unix part, I can propose how to amend it to cover
> Windows.  OK?

Deal.  Thanks.

>> > This seems to pass the unexpanded cwd directly to CreateProcess.  I
>> > don't think this will work on Windows, as this directory is not
>> > interpreted by any shell, so "~" will cause errors.  I think we should
>> > pass this via gdb_tilde_expand, like we do in the Unix case, and I
>> > also think we should mirror all the slashes in the result, just in
>> > case.
>> 
>> Hm, you're right.  I will call "gdb_tilde_expand" here.  I'm not sure
>> what you mean by "mirror all the slashes in the result".  Do you mean
>> "escape the slashes"?
>
> No, I mean convert forward slashes to backslashes.

Sorry, I wasn't familiar with the term.  Anyway, I think I can implement
that.  In fact, I think gdbserver/win32-low.c:create_process already
does that, right?

  wprogram = alloca ((strlen (program) + 1) * sizeof (wchar_t));
  mbstowcs (wprogram, program, strlen (program) + 1);

  for (p = wprogram; *p; ++p)
    if (L'/' == *p)
      *p = L'\\';

Anyway, I'll do the same for the inferior's cwd.

Thanks,
  
Sergio Durigan Junior Sept. 22, 2017, 8:26 p.m. UTC | #9
On Friday, September 22 2017, Eli Zaretskii wrote:

>> Cc: gdb-patches@sourceware.org
>> From: Pedro Alves <palves@redhat.com>
>> Date: Fri, 22 Sep 2017 20:24:40 +0100
>>
>> (gdb) set cwd ~  # I haven't even connected to a target yet.
>>                  # Where should this be expanded? 
>>                  # On host may be incorrect.
>>                  # '~' -> /home/pedro on this machine
>
> You can expand it when you connect.  The value has no use until then
> anyway.

So you're proposing that we discard the previous path expansion done
before connecting, right?  I'm just trying to understand your proposal
here, not to bikeshed or anything.
  
Pedro Alves Sept. 22, 2017, 8:37 p.m. UTC | #10
On 09/22/2017 09:26 PM, Sergio Durigan Junior wrote:
> On Friday, September 22 2017, Eli Zaretskii wrote:
> 
>>> Cc: gdb-patches@sourceware.org
>>> From: Pedro Alves <palves@redhat.com>
>>> Date: Fri, 22 Sep 2017 20:24:40 +0100
>>>
>>> (gdb) set cwd ~  # I haven't even connected to a target yet.
>>>                  # Where should this be expanded? 
>>>                  # On host may be incorrect.
>>>                  # '~' -> /home/pedro on this machine
>>
>> You can expand it when you connect.  The value has no use until then
>> anyway.
> 
> So you're proposing that we discard the previous path expansion done
> before connecting, right?  I'm just trying to understand your proposal
> here, not to bikeshed or anything.
> 

That would mean keep both non-expanded, and expanded paths around,
which is what I was suggesting with:

 (gdb) set cwd ~foo/bar
 (gdb) show cwd
 The current directory is ~foo/bar (/home/foo/bar)
                                    ^^^^^^^^^^^^^

The (^^^) part was meant to indicate "currently expands to this".

Which means we'd add a new remote packet to request "expand 
this path and give me back the result".

(I think we could do that as a follow up extension.)

But that's not what I understood Eli suggesting.  I understood
it as gdb expanding whatever's the value set on connection.
But I don't see how that could work, because before gdb connects
to a remote target explicitly, it's as if gdb was connected to
the native target (that's how "run" works without typing
"target native" explicitly, though you can type that), so
by the time you connect to the remote target, it's already
too late, gdb has already expanded on the host, and there's
nothing left to expand.

Thanks,
Pedro Alves
  
Sergio Durigan Junior Sept. 22, 2017, 8:55 p.m. UTC | #11
On Friday, September 22 2017, Eli Zaretskii wrote:

>> From: Sergio Durigan Junior <sergiodj@redhat.com>
>> Cc: gdb-patches@sourceware.org,  palves@redhat.com
>> Date: Fri, 22 Sep 2017 14:00:51 -0400
>> 
>> On Friday, September 22 2017, Eli Zaretskii wrote:
>> 
>> >> +@kindex set cwd
>> >> +@cindex change inferior's working directory
>> >> +@item set cwd @r{[}@var{directory}@r{]}
>> >> +Set the inferior's working directory to @var{directory}.  If not
>> >> +given, @var{directory} uses @file{'~'}.
>> >
>> > I think we should document here what does "~" mean on MS-Windows,
>> > especially since, when HOME is not in the environment, Gnulib's glob
>> > module doesn't behave according to MS platform recommendations (which
>> > say not to create files directly below %HOMEDRIVE%%HOMEPATH%).
>> 
>> Sure, but just to be clear, this text was strongly based on another part
>> of the docs, which also mentions '~' without explaining further.
>> 
>> As I am totally out of the loop when it comes to Windows environments,
>> I'd appreciate a suggestion for the new text.
>
> If you write the Unix part, I can propose how to amend it to cover
> Windows.  OK?

What do you think:

@table @code
@kindex set cwd
@cindex change inferior's working directory
@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.

?
  
Eli Zaretskii Sept. 23, 2017, 5:50 a.m. UTC | #12
> From: Sergio Durigan Junior <sergiodj@redhat.com>
> Cc: gdb-patches@sourceware.org,  palves@redhat.com
> Date: Fri, 22 Sep 2017 16:24:26 -0400
> 
> > No, I mean convert forward slashes to backslashes.
> 
> Sorry, I wasn't familiar with the term.

Nothing to be sorry about.

> Anyway, I think I can implement
> that.  In fact, I think gdbserver/win32-low.c:create_process already
> does that, right?
> 
>   wprogram = alloca ((strlen (program) + 1) * sizeof (wchar_t));
>   mbstowcs (wprogram, program, strlen (program) + 1);
> 
>   for (p = wprogram; *p; ++p)
>     if (L'/' == *p)
>       *p = L'\\';

Yes, that's it.  But this is for wchar_t strings, not for char
strings, of course.

Thanks.
  
Eli Zaretskii Sept. 23, 2017, 5:51 a.m. UTC | #13
> From: Sergio Durigan Junior <sergiodj@redhat.com>
> Cc: Pedro Alves <palves@redhat.com>,  gdb-patches@sourceware.org
> Date: Fri, 22 Sep 2017 16:26:56 -0400
> 
> >> (gdb) set cwd ~  # I haven't even connected to a target yet.
> >>                  # Where should this be expanded? 
> >>                  # On host may be incorrect.
> >>                  # '~' -> /home/pedro on this machine
> >
> > You can expand it when you connect.  The value has no use until then
> > anyway.
> 
> So you're proposing that we discard the previous path expansion done
> before connecting, right?

Yes, discard and replace with the actual expansion which the target
returns.
  
Eli Zaretskii Sept. 23, 2017, 5:55 a.m. UTC | #14
> Cc: gdb-patches@sourceware.org
> From: Pedro Alves <palves@redhat.com>
> Date: Fri, 22 Sep 2017 21:37:49 +0100
> 
> That would mean keep both non-expanded, and expanded paths around,
> which is what I was suggesting with:
> 
>  (gdb) set cwd ~foo/bar
>  (gdb) show cwd
>  The current directory is ~foo/bar (/home/foo/bar)
>                                     ^^^^^^^^^^^^^

Keeping both is also OK, although I don't see how it would solve the
problems Pedro mentioned earlier, and also now:

> But that's not what I understood Eli suggesting.  I understood
> it as gdb expanding whatever's the value set on connection.
> But I don't see how that could work, because before gdb connects
> to a remote target explicitly, it's as if gdb was connected to
> the native target (that's how "run" works without typing
> "target native" explicitly, though you can type that), so
> by the time you connect to the remote target, it's already
> too late, gdb has already expanded on the host, and there's
> nothing left to expand.

I don't understand this description, which is not surprising, since my
knowledge of the machinery involved in this is very superficial.
  
Eli Zaretskii Sept. 23, 2017, 6:05 a.m. UTC | #15
> From: Sergio Durigan Junior <sergiodj@redhat.com>
> Cc: gdb-patches@sourceware.org,  palves@redhat.com
> Date: Fri, 22 Sep 2017 16:55:35 -0400
> 
> @table @code
> @kindex set cwd
> @cindex change inferior's working directory
> @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.

OK.  I'd suggest to append this text.

  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.

(This ignores the "~username" case -- is that important enough to
mention?)

Btw, what should the user do if the file name includes a literal '~'?
Escape it with a backslash?
  
Sergio Durigan Junior Sept. 23, 2017, 5:01 p.m. UTC | #16
On Saturday, September 23 2017, Eli Zaretskii wrote:

>> From: Sergio Durigan Junior <sergiodj@redhat.com>
>> Cc: gdb-patches@sourceware.org,  palves@redhat.com
>> Date: Fri, 22 Sep 2017 16:55:35 -0400
>> 
>> @table @code
>> @kindex set cwd
>> @cindex change inferior's working directory
>> @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.
>
> OK.  I'd suggest to append this text.
>
>   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.

Thanks, added.

> (This ignores the "~username" case -- is that important enough to
> mention?)

I don't think so.  I mean, the "~username" tilde expansion is part of
the default behaviour, so I think it's OK to leave it implicit.

> Btw, what should the user do if the file name includes a literal '~'?
> Escape it with a backslash?

Yes.

Thanks,
  
Pedro Alves Sept. 27, 2017, 2:01 p.m. UTC | #17
On 09/23/2017 06:55 AM, Eli Zaretskii wrote:
>> Cc: gdb-patches@sourceware.org
>> From: Pedro Alves <palves@redhat.com>
>> Date: Fri, 22 Sep 2017 21:37:49 +0100
>>
>> That would mean keep both non-expanded, and expanded paths around,
>> which is what I was suggesting with:
>>
>>  (gdb) set cwd ~foo/bar
>>  (gdb) show cwd
>>  The current directory is ~foo/bar (/home/foo/bar)
>>                                     ^^^^^^^^^^^^^
> 
> Keeping both is also OK, although I don't see how it would solve the
> problems Pedro mentioned earlier, and also now:
> 
>> But that's not what I understood Eli suggesting.  I understood
>> it as gdb expanding whatever's the value set on connection.
>> But I don't see how that could work, because before gdb connects
>> to a remote target explicitly, it's as if gdb was connected to
>> the native target (that's how "run" works without typing
>> "target native" explicitly, though you can type that), so
>> by the time you connect to the remote target, it's already
>> too late, gdb has already expanded on the host, and there's
>> nothing left to expand.
> 
> I don't understand this description, which is not surprising, since my
> knowledge of the machinery involved in this is very superficial.
> 

Let's say that GDB keeps track of the desired cwd for the inferior
as a single string, like Sergio's patch is doing.  Say the user does:

 $ gdb
 (gdb) file program
 (gdb) set cwd ~

At this point, GDB it not "connected" to any target yet.  However,
if the user types "run", GDB automatically uses the native
target to run the inferior.  So typing "run" after the above is
equivalent to:

 $ gdb
 (gdb) file program
 (gdb) set cwd ~
 (gdb) target native
 (gdb) run

OK, now the question is, when to expand that '~'.  If it is
expanded immediately at "set cwd" time, then we end up expanding
it incorrectly in case the user actually wanted to debug remotely:

 $ gdb
 (gdb) file program
 (gdb) set cwd ~
 (gdb) show cwd
 /home/pedro   # local path, doesn't exists on the 'foo' machine.
 (gdb) target extended-remote foo:12345
 (gdb) show cwd
 /home/pedro   # does not exist on 'foo'

If is it instead expanded only when GDB connects to a target, then
we get this:

 $ gdb
 (gdb) file program
 (gdb) set cwd ~
 (gdb) show cwd
 ~   # not expanded yet!
 (gdb) start  # gdb expands ~ -> /home/pedro, then starts the inferior
 (gdb) show cwd
 /home/pedro  # now it's expanded.  users scratches head.
 (gdb) kill
 (gdb) target extended-remote foo:12345    # switch same inferior
                                           # to different target
 (gdb) show cwd
 /home/pedro  # whoops, lost original '~' path, and this path
              # doesn't make sense for the 'foo' machine.


If GDB does not expand the set cwd path ever except internally
when starting the inferior, then the problems shown just above
never happen.

> Keeping both is also OK, although I don't see how it would solve the
> problems Pedro mentioned earlier, and also now:

The problems I mentioned are solved by not expanding in the
first place.  Saving both original-path-as-specified-by-user and 
expanded-path (or expanding on demand when displaying the path to the
user and saving it nowhere) would then be a way to let the user
know what the path expands to on the current target, without losing the
originally set path.  Like:

 $ gdb
 (gdb) file program
 (gdb) set cwd ~
 (gdb) show cwd
 cwd is: ~   
 expands to /home/pedro on current target
 (gdb) start
 (gdb) show cwd
 cwd is: ~   
 expands to /home/pedro on current target
 (gdb) kill
 (gdb) target extended-remote foo:12345
 (gdb) show cwd
 cwd is: ~
 expands to /mnt/home/palves on current target
 (gdb) disconnect
 (gdb) target extended-remote bar:12345
 (gdb) show cwd
 cwd is: ~   
 expands to /nfs/homes/pedro on current target

I'm not sure it's worth the trouble to show the
expansions like this.  But if we make GDB _not_ expand
the "set cwd" setting's value itself, as I was proposing,
then we can always come back and improve GDB's output like
above, to inform the user what the setting expands to,
as extra info.

Hope that is clearer.

Thanks,
Pedro Alves
  
Eli Zaretskii Sept. 29, 2017, 3:31 p.m. UTC | #18
> Cc: sergiodj@redhat.com, gdb-patches@sourceware.org
> From: Pedro Alves <palves@redhat.com>
> Date: Wed, 27 Sep 2017 15:01:58 +0100
> 
> Let's say that GDB keeps track of the desired cwd for the inferior
> as a single string, like Sergio's patch is doing.  Say the user does:
> 
>  $ gdb
>  (gdb) file program
>  (gdb) set cwd ~
> 
> At this point, GDB it not "connected" to any target yet.  However,
> if the user types "run", GDB automatically uses the native
> target to run the inferior.  So typing "run" after the above is
> equivalent to:
> 
>  $ gdb
>  (gdb) file program
>  (gdb) set cwd ~
>  (gdb) target native
>  (gdb) run
> 
> OK, now the question is, when to expand that '~'.  If it is
> expanded immediately at "set cwd" time, then we end up expanding
> it incorrectly in case the user actually wanted to debug remotely:

How about expanding it as part of "target FOO"?

> If GDB does not expand the set cwd path ever except internally
> when starting the inferior, then the problems shown just above
> never happen.

But that's an illusory advantage, isn't it?  Because we then keep "~",
but it expands to something different for every target, and the user
has no way of knowing what that will do on another target.  Moreover,
the user might not even want to have a different expansion for each
target.

>  $ gdb
>  (gdb) file program
>  (gdb) set cwd ~
>  (gdb) show cwd
>  cwd is: ~   
>  expands to /home/pedro on current target
>  (gdb) start
>  (gdb) show cwd
>  cwd is: ~   
>  expands to /home/pedro on current target
>  (gdb) kill
>  (gdb) target extended-remote foo:12345
>  (gdb) show cwd
>  cwd is: ~
>  expands to /mnt/home/palves on current target
>  (gdb) disconnect
>  (gdb) target extended-remote bar:12345
>  (gdb) show cwd
>  cwd is: ~   
>  expands to /nfs/homes/pedro on current target
> 
> I'm not sure it's worth the trouble to show the
> expansions like this.  But if we make GDB _not_ expand
> the "set cwd" setting's value itself, as I was proposing,
> then we can always come back and improve GDB's output like
> above, to inform the user what the setting expands to,
> as extra info.

Maybe making "show cwd" expand in the right context would be a good
middle-ground.
  
Pedro Alves Sept. 29, 2017, 3:46 p.m. UTC | #19
On 09/29/2017 04:31 PM, Eli Zaretskii wrote:

> But that's an illusory advantage, isn't it?  

I don't think it is.

> Because we then keep "~",
> but it expands to something different for every target, and the user
> has no way of knowing what that will do on another target. 

If the user types "~" then surely they want the home directory,
whatever it is on the machine.  That's the point of "~" existing,
IMO.

> Moreover,
> the user might not even want to have a different expansion for each
> target.

If the user does not want a different expansion for each
target then they can simply specify an expanded path
like "/home/joe" or whatever without the "~".

Thanks,
Pedro Alves
  
Eli Zaretskii Sept. 29, 2017, 5:51 p.m. UTC | #20
> Cc: sergiodj@redhat.com, gdb-patches@sourceware.org
> From: Pedro Alves <palves@redhat.com>
> Date: Fri, 29 Sep 2017 16:46:13 +0100
> 
> > Because we then keep "~",
> > but it expands to something different for every target, and the user
> > has no way of knowing what that will do on another target. 
> 
> If the user types "~" then surely they want the home directory,
> whatever it is on the machine.  That's the point of "~" existing,
> IMO.

Users might not be aware the same "~" will get re-expanded for each
target.

Anyway, I talked enough about this.  It's your call wrt what to do
with this.

Thanks.
  

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 549f511b29..c131713293 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -64,6 +64,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 507fdc4120..14e6a06d68 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -1731,9 +1731,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 6b320891ad..899afb92b6 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
+@code{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,19 +2435,37 @@  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.
+Each time you start your program with @code{run}, the inferior will be
+initialized with the current working directory specified by the
+@code{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.
+
+You can also change @value{GDBN}'s current working directory by using
+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}.
 
 @table @code
+@kindex set cwd
+@cindex change inferior's working directory
+@item set cwd @r{[}@var{directory}@r{]}
+Set the inferior's working directory to @var{directory}.  If not
+given, @var{directory} uses @file{'~'}.  This has no effect on
+@value{GDBN}'s working directory.  This setting only takes effect the
+next time you start the inferior.
+
+@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 @code{set cwd}, then the default inferior's working
+directory is the same as @value{GDBN}'s working directory.
+
 @kindex cd
-@cindex change working directory
+@cindex change @value{GDBN}'s working directory
 @item cd @r{[}@var{directory}@r{]}
 Set the @value{GDBN} working directory to @var{directory}.  If not
 given, @var{directory} uses @file{'~'}.
diff --git a/gdb/gdbserver/inferiors.c b/gdb/gdbserver/inferiors.c
index 72f0412757..e78ad4faf1 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)
   if (!ptid_equal (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 9c0cead434..4c5bbfdbf2 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -111,6 +111,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.  */
 
@@ -246,6 +250,55 @@  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.  */
+
+static void
+set_inferior_cwd (const char *cwd)
+{
+  if (*cwd == '\0')
+    error (_("You must specify a directory."));
+
+  struct inferior *inf = current_inferior ();
+
+  gdb_assert (inf != NULL);
+  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)
+{
+  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.  */
@@ -3262,6 +3315,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<char> 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..0ce442f162 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 <vector>
 
 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,13 @@  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)
+    expanded_inferior_cwd = gdb_tilde_expand (inferior_cwd);
+
   /* 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 +384,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 (expanded_inferior_cwd.c_str ()) < 0)
+	    trace_start_error_with_name (expanded_inferior_cwd.c_str ());
+	}
+
       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..2a501aa675
--- /dev/null
+++ b/gdb/testsuite/gdb.base/set-cwd.c
@@ -0,0 +1,32 @@ 
+/* 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 <http://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+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..f2700ec44d
--- /dev/null
+++ b/gdb/testsuite/gdb.base/set-cwd.exp
@@ -0,0 +1,140 @@ 
+# 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 <http://www.gnu.org/licenses/>.
+
+if { [use_gdb_stub] || [target_info gdb_protocol] == "extended-remote" } {
+    untested "not implemented on gdbserver"
+    return
+}
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile debug] } {
+    return -1
+}
+
+# Test that tilde expansion works fine.
+
+proc test_tilde_expansion { } {
+    global decimal gdb_prompt hex
+
+    with_test_prefix "test tilde expansion" {
+	gdb_test_no_output "set cwd ~/" "set 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 .*"
+
+	gdb_test_multiple "print home" "print home var" {
+	    -re "\\\$$decimal = $hex \"\(.+\)\"\r\n$gdb_prompt $" {
+		set home $expect_out(1,string)
+	    }
+	    -re "$gdb_prompt $" {
+		untested "could to retrieve home var"
+		return
+	    }
+	    default {
+		untested "could to retrieve home var"
+		return
+	    }
+	}
+
+	if { [string length $home] > 0 } {
+	    gdb_test_multiple "print dir" "print dir var" {
+		-re "\\\$$decimal = \"\(.+\)\"\(, .*repeats.*\)?\r\n$gdb_prompt $" {
+		    set curdir $expect_out(1,string)
+		}
+		-re "$gdb_prompt $" {
+		    fail "failed to retrieve dir var"
+		    return -1
+		}
+		default {
+		    fail "failed to retrieve dir var"
+		    return -1
+		}
+	    }
+
+	    gdb_assert [string equal $curdir $home] \
+		"successfully chdir'd into home"
+	} else {
+	    untested "could not determine value of HOME"
+	    return
+	}
+    }
+}
+
+# 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 test_cd_into_dir { } {
+    global decimal gdb_prompt
+
+    with_test_prefix "test cd into temp dir" {
+	gdb_test_multiple "pwd" "pwd before run" {
+	    -re "Working directory \(.*\)\.\r\n$gdb_prompt $" {
+		set gdb_cwd_before_run $expect_out(1,string)
+	    }
+	    -re ".*$gdb_prompt $" {
+		fail "failed to obtain cwd before run"
+		return -1
+	    }
+	    default {
+		fail "failed to obtain cwd before run"
+		return -1
+	    }
+	}
+
+	set tmpdir [standard_output_file ""]
+
+	gdb_test_no_output "set cwd $tmpdir" "set 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"
+
+	gdb_test_multiple "pwd" "pwd after run" {
+	    -re "Working directory \(.*\)\.\r\n$gdb_prompt $" {
+		set gdb_cwd_after_run $expect_out(1,string)
+	    }
+	    -re ".*$gdb_prompt $" {
+		fail "failed to obtain cwd after run"
+		return -1
+	    }
+	    default {
+		fail "failed to obtain cwd after run"
+		return -1
+	    }
+	}
+
+	gdb_assert [string equal $gdb_cwd_before_run $gdb_cwd_after_run] \
+	    "GDB cwd is unchanged after running inferior"
+    }
+}
+
+test_cd_into_dir
+clean_restart $binfile
+test_tilde_expansion
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 3e1894410d..c762222654 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -2432,6 +2432,7 @@  windows_create_inferior (struct target_ops *ops, const char *exec_file,
   cygwin_buf_t *toexec;
   cygwin_buf_t *cygallargs;
   cygwin_buf_t *args;
+  cygwin_buf_t *infcwd;
   char **old_env = NULL;
   PWCHAR w32_env;
   size_t len;
@@ -2461,6 +2462,7 @@  windows_create_inferior (struct target_ops *ops, const char *exec_file,
   BOOL ret;
   DWORD flags = 0;
   const char *inferior_io_terminal = get_inferior_io_terminal ();
+  const char *inferior_cwd = get_inferior_cwd ();
 
   if (!exec_file)
     error (_("No executable specified, use `target exec'."));
@@ -2488,8 +2490,15 @@  windows_create_inferior (struct target_ops *ops, const char *exec_file,
 	error (_("Error starting executable: %d"), errno);
       cygallargs = (wchar_t *) alloca (len * sizeof (wchar_t));
       mbstowcs (cygallargs, allargs, len);
+
+      len = mbstowcs (NULL, inferior_cwd, 0) + 1;
+      if (len == (size_t) -1)
+	error (_("Invalid cwd for inferior: %d"), errno);
+      infcwd = (wchar_t *) alloca (len * sizeof (wchar_t));
+      mbstowcs (infcwd, inferior_cwd, len);
 #else  /* !__USEWIDE */
       cygallargs = allargs;
+      infcwd = (cygwin_buf_t *) inferior_cwd;
 #endif
     }
   else
@@ -2574,7 +2583,7 @@  windows_create_inferior (struct target_ops *ops, const char *exec_file,
 		       TRUE,	/* inherit handles */
 		       flags,	/* start flags */
 		       w32_env,	/* environment */
-		       NULL,	/* current directory */
+		       infcwd,	/* current directory */
 		       &si,
 		       &pi);
   if (w32_env)
@@ -2697,7 +2706,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)