This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH v3] Implement the ability to set/unset environment variables to GDBserver when starting the inferior


Ping.

On Sunday, August 13 2017, I wrote:

> Changes from v2 (addressing comments by Simon):
>
> - Make m_user_set_env_list and m_user_unset_env_list std::set's,
>   instead of std::vector's.  This simplified the code and had the nice
>   side effect of "automatically" avoid duplicates in the sets (at the
>   expense of a bit more processing).  This involved the adjustment of
>   several parts of the code.
>
> - Removed automatic parameter from unset method; now the code has two
>   unset's which are called depending on the case.
>
> - Implement bin2hex overload, and get rid of str2hex.
>
> - Nits and small mistakes fixed.
>
> - s/rerun_to_main/do_prepare_inferior/g in the testcase, in order to
>   avoid accidentally overwriting the top rerun_to_main proc (and mess
>   up with subsequent tests).
>
>
> This patch implements the ability to set/unset environment variables
> on the remote target, mimicking what GDB already offers to the user.
> There are two features present here: user-set and user-unset
> environment variables.
>
> User-set environment variables are only the variables that are
> explicitly set by the user, using the 'set environment' command.  This
> means that variables that were already present in the environment when
> starting GDB/GDBserver are not transmitted/considered by this feature.
>
> User-unset environment variables are variables that are explicitly
> unset by the user, using the 'unset environment' command.
>
> The idea behind this patch is to store user-set and user-unset
> environment variables in two separate sets, both part of gdb_environ.
> Then, when extended_remote_create_inferior is preparing to start the
> inferior, it will iterate over the two sets and set/unset variables
> accordingly.  Three new packets are introduced:
>
> - QEnvironmentHexEncoded, which is used to set environment variables,
>   and contains an hex-encoded string in the format "VAR=VALUE" (VALUE
>   can be empty if the user set a variable with a null value, by doing
>   'set environment VAR=').
>
> - QEnvironmentUnset, which is used to unset environment variables, and
>   contains an hex-encoded string in the format "VAR".
>
> - QEnvironmentReset, which is always the first packet to be
>   transmitted, and is used to reset the environment, i.e., discard any
>   changes made by the user on previous runs.
>
> The QEnvironmentHexEncoded packet is inspired on LLDB's extensions to
> the RSP.  Details about it can be seen here:
>
>   <https://raw.githubusercontent.com/llvm-mirror/lldb/master/docs/lldb-gdb-remote.txt>
>
> I decided not to implement the QEnvironment packet because it is
> considered deprecated by LLDB.  This packet, on LLDB, serves the same
> purpose of QEnvironmentHexEncoded, but sends the information using a
> plain text, non-hex-encoded string.
>
> The other two packets are new.
>
> This patch also includes updates to the documentation, testsuite, and
> unit tests, without introducing regressions.
>
> gdb/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>
> 	* NEWS (Changes since GDB 8.0): Add entry mentioning new support
> 	for setting/unsetting environment variables on the remote target.
> 	(New remote packets): Add entries for QEnvironmentHexEncoded,
> 	QEnvironmentUnset and QEnvironmentReset.
> 	* common/environ.c (gdb_environ::operator=): Extend method to
> 	handle m_user_set_env_list and m_user_unset_env_list.
> 	(gdb_environ::clear): Likewise.
> 	(match_var_in_string): Change type of first parameter from 'char
> 	*' to 'const char *'.
> 	(gdb_environ::set): Extend method to handle
> 	m_user_set_env_list and m_user_unset_env_list.
> 	(gdb_environ::unset): Likewise.
> 	(gdb_environ::clear_user_set_env): New method.
> 	(gdb_environ::user_set_envp): Likewise.
> 	(gdb_environ::user_unset_envp): Likewise.
> 	* common/environ.h (gdb_environ): Handle m_user_set_env_list and
> 	m_user_unset_env_list on move constructor/assignment.
> 	(unset): Add new default parameter 'update_unset_list = true'.
> 	(clear_user_set_env): New method.
> 	(user_set_envp): Likewise.
> 	(user_unset_envp): Likewise.
> 	(m_user_set_env_list): New std::set.
> 	(m_user_unset_env_list): Likewise.
> 	* common/rsp-low.c (hex2str): New function.
> 	(bin2hex): New overload for bin2hex function.
> 	* common/rsp-low.c (hex2str): New prototype.
> 	(str2hex): New overload prototype.
> 	* remote.c: Include "environ.h". Add QEnvironmentHexEncoded,
> 	QEnvironmentUnset and QEnvironmentReset.
> 	(remote_protocol_features): Add QEnvironmentHexEncoded,
> 	QEnvironmentUnset and QEnvironmentReset packets.
> 	(send_environment_packet): New function.
> 	(extended_remote_environment_support): Likewise.
> 	(extended_remote_create_inferior): Call
> 	extended_remote_environment_support.
> 	(_initialize_remote): Add QEnvironmentHexEncoded,
> 	QEnvironmentUnset and QEnvironmentReset packet configs.
> 	* unittests/environ-selftests.c (run_tests): Add tests for
> 	m_user_set_env_list and m_user_unset_env_list.
>
> gdb/gdbserver/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>
> 	* server.c (handle_general_set): Handle QEnvironmentHexEncoded,
> 	QEnvironmentUnset and QEnvironmentReset packets.
> 	(handle_query): Inform remote that QEnvironmentHexEncoded,
> 	QEnvironmentUnset and QEnvironmentReset are supported.
>
> gdb/doc/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>
> 	* gdb.texinfo (set environment): Add @anchor.  Explain that
> 	environment variables set by the user are sent to GDBserver.
> 	(unset environment): Likewise, but for unsetting variables.
> 	(Connecting) <Remote Packet>: Add "environment-hex-encoded",
> 	"QEnvironmentHexEncoded", "environment-unset", "QEnvironmentUnset",
> 	"environment-reset" and "QEnvironmentReset" to the table.
> 	(Remote Protocol) <QEnvironmentHexEncoded, QEnvironmentUnset,
> 	QEnvironmentReset>: New item, explaining the packet.
>
> gdb/testsuite/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>
> 	* gdb.base/share-env-with-gdbserver.c: New file.
> 	* gdb.base/share-env-with-gdbserver.exp: Likewise.
> ---
>  gdb/NEWS                                           |  24 ++
>  gdb/common/environ.c                               |  75 ++++--
>  gdb/common/environ.h                               |  24 +-
>  gdb/common/rsp-low.c                               |  39 ++++
>  gdb/common/rsp-low.h                               |   8 +
>  gdb/doc/gdb.texinfo                                | 116 ++++++++++
>  gdb/gdbserver/server.c                             |  65 +++++-
>  gdb/remote.c                                       |  76 ++++++
>  gdb/testsuite/gdb.base/share-env-with-gdbserver.c  |  40 ++++
>  .../gdb.base/share-env-with-gdbserver.exp          | 254 +++++++++++++++++++++
>  gdb/unittests/environ-selftests.c                  |  64 ++++++
>  11 files changed, 769 insertions(+), 16 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.base/share-env-with-gdbserver.c
>  create mode 100644 gdb/testsuite/gdb.base/share-env-with-gdbserver.exp
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 9cd1df1cfe..4349cce6e2 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,18 @@
>  
>  *** Changes since GDB 8.0
>  
> +* On Unix systems, GDB now supports transmitting environment variables
> +  that are to be set or unset to GDBserver.  These variables will
> +  affect the environment to be passed to the remote inferior.
> +
> +  To inform GDB of environment variables that are to be transmitted to
> +  GDBserver, use the "set environment" command.  Only user set
> +  environment variables are sent to GDBserver.
> +
> +  To inform GDB of environment variables that are to be unset before
> +  the remote inferior is started by the GDBserver, use the "unset
> +  environment" command.
> +
>  * On Unix systems, GDBserver now does globbing expansion and variable
>    substitution in inferior command line arguments.
>  
> @@ -14,6 +26,18 @@
>  
>  * New remote packets
>  
> +QEnvironmentHexEncoded
> +  Inform GDBserver of an environment variable that is to be passed to
> +  the inferior when starting it.
> +
> +QEnvironmentUnset
> +  Inform GDBserver of an environment variable that is to be unset
> +  before starting the remote inferior.
> +
> +QEnvironmentReset
> +  Inform GDBserver that the environment should be reset (i.e.,
> +  user-set environment variables should be unset).
> +
>  QStartupWithShell
>    Indicates whether the inferior must be started with a shell or not.
>  
> diff --git a/gdb/common/environ.c b/gdb/common/environ.c
> index 698bda3ac1..7753f6ebca 100644
> --- a/gdb/common/environ.c
> +++ b/gdb/common/environ.c
> @@ -30,8 +30,12 @@ gdb_environ::operator= (gdb_environ &&e)
>      return *this;
>  
>    m_environ_vector = std::move (e.m_environ_vector);
> +  m_user_set_env = std::move (e.m_user_set_env);
> +  m_user_unset_env = std::move (e.m_user_unset_env);
>    e.m_environ_vector.clear ();
>    e.m_environ_vector.push_back (NULL);
> +  e.m_user_set_env.clear ();
> +  e.m_user_unset_env.clear ();
>    return *this;
>  }
>  
> @@ -65,6 +69,8 @@ gdb_environ::clear ()
>    m_environ_vector.clear ();
>    /* Always add the NULL element.  */
>    m_environ_vector.push_back (NULL);
> +  m_user_set_env.clear ();
> +  m_user_unset_env.clear ();
>  }
>  
>  /* Helper function to check if STRING contains an environment variable
> @@ -72,7 +78,7 @@ gdb_environ::clear ()
>     if it contains, false otherwise.  */
>  
>  static bool
> -match_var_in_string (char *string, const char *var, size_t var_len)
> +match_var_in_string (const char *string, const char *var, size_t var_len)
>  {
>    if (strncmp (string, var, var_len) == 0 && string[var_len] == '=')
>      return true;
> @@ -99,32 +105,59 @@ gdb_environ::get (const char *var) const
>  void
>  gdb_environ::set (const char *var, const char *value)
>  {
> +  char *fullvar = concat (var, "=", value, NULL);
> +
>    /* We have to unset the variable in the vector if it exists.  */
> -  unset (var);
> +  unset (var, false);
>  
>    /* Insert the element before the last one, which is always NULL.  */
> -  m_environ_vector.insert (m_environ_vector.end () - 1,
> -			   concat (var, "=", value, NULL));
> +  m_environ_vector.insert (m_environ_vector.end () - 1, fullvar);
> +
> +  /* Mark this environment variable as having been set by the user.
> +     This will be useful when we deal with setting environment
> +     variables on the remote target.  */
> +  m_user_set_env.insert (std::string (fullvar));
> +
> +  /* If this environment variable is marked as unset by the user, then
> +     remove it from the list, because now the user wants to set
> +     it.  */
> +  m_user_unset_env.erase (std::string (var));
>  }
>  
>  /* See common/environ.h.  */
>  
>  void
> -gdb_environ::unset (const char *var)
> +gdb_environ::unset (const char *var, bool update_unset_list)
>  {
>    size_t len = strlen (var);
> +  std::vector<char *>::iterator it_env;
>  
>    /* We iterate until '.end () - 1' because the last element is
>       always NULL.  */
> -  for (std::vector<char *>::iterator el = m_environ_vector.begin ();
> -       el != m_environ_vector.end () - 1;
> -       ++el)
> -    if (match_var_in_string (*el, var, len))
> -      {
> -	xfree (*el);
> -	m_environ_vector.erase (el);
> -	break;
> -      }
> +  for (it_env = m_environ_vector.begin ();
> +       it_env != m_environ_vector.end () - 1;
> +       ++it_env)
> +    if (match_var_in_string (*it_env, var, len))
> +      break;
> +
> +  if (it_env != m_environ_vector.end () - 1)
> +    {
> +      m_user_set_env.erase (std::string (*it_env));
> +      xfree (*it_env);
> +
> +      m_environ_vector.erase (it_env);
> +    }
> +
> +  if (update_unset_list)
> +    m_user_unset_env.insert (std::string (var));
> +}
> +
> +/* See common/environ.h.  */
> +
> +void
> +gdb_environ::unset (const char *var)
> +{
> +  unset (var, true);
>  }
>  
>  /* See common/environ.h.  */
> @@ -134,3 +167,17 @@ gdb_environ::envp () const
>  {
>    return const_cast<char **> (&m_environ_vector[0]);
>  }
> +
> +/* See common/environ.h.  */
> +
> +const std::set<std::string> &
> +gdb_environ::user_set_env () const
> +{
> +  return m_user_set_env;
> +}
> +
> +const std::set<std::string> &
> +gdb_environ::user_unset_env () const
> +{
> +  return m_user_unset_env;
> +}
> diff --git a/gdb/common/environ.h b/gdb/common/environ.h
> index 0bbb191361..5ac27fe761 100644
> --- a/gdb/common/environ.h
> +++ b/gdb/common/environ.h
> @@ -18,6 +18,7 @@
>  #define ENVIRON_H 1
>  
>  #include <vector>
> +#include <set>
>  
>  /* Class that represents the environment variables as seen by the
>     inferior.  */
> @@ -41,12 +42,16 @@ public:
>  
>    /* Move constructor.  */
>    gdb_environ (gdb_environ &&e)
> -    : m_environ_vector (std::move (e.m_environ_vector))
> +    : m_environ_vector (std::move (e.m_environ_vector)),
> +      m_user_set_env (std::move (e.m_user_set_env)),
> +      m_user_unset_env (std::move (e.m_user_unset_env))
>    {
>      /* Make sure that the moved-from vector is left at a valid
>         state (only one NULL element).  */
>      e.m_environ_vector.clear ();
>      e.m_environ_vector.push_back (NULL);
> +    e.m_user_set_env.clear ();
> +    e.m_user_unset_env.clear ();
>    }
>  
>    /* Move assignment.  */
> @@ -73,9 +78,26 @@ public:
>    /* Return the environment vector represented as a 'char **'.  */
>    char **envp () const;
>  
> +  /* Return the user-set environment vector.  */
> +  const std::set<std::string> &user_set_env () const;
> +
> +  /* Return the user-set environment vector.  */
> +  const std::set<std::string> &user_unset_env () const;
> +
>  private:
> +  /* Unset VAR in environment.  If UPDATE_UNSET_LIST is true, then
> +     also update M_USER_UNSET_ENV to reflect the unsetting of the
> +     environment variable.  */
> +  void unset (const char *var, bool update_unset_list);
> +
>    /* A vector containing the environment variables.  */
>    std::vector<char *> m_environ_vector;
> +
> +  /* The environment variables explicitly set by the user.  */
> +  std::set<std::string> m_user_set_env;
> +
> +  /* The environment variables explicitly unset by the user.  */
> +  std::set<std::string> m_user_unset_env;
>  };
>  
>  #endif /* defined (ENVIRON_H) */
> diff --git a/gdb/common/rsp-low.c b/gdb/common/rsp-low.c
> index eb85ab5701..9f71699144 100644
> --- a/gdb/common/rsp-low.c
> +++ b/gdb/common/rsp-low.c
> @@ -132,6 +132,29 @@ hex2bin (const char *hex, gdb_byte *bin, int count)
>  
>  /* See rsp-low.h.  */
>  
> +std::string
> +hex2str (const char *hex)
> +{
> +  std::string ret;
> +  size_t len = strlen (hex);
> +
> +  for (size_t i = 0; i < len; ++i)
> +    {
> +      if (hex[0] == '\0' || hex[1] == '\0')
> +	{
> +	  /* Hex string is short, or of uneven length.  Return what we
> +	     have so far.  */
> +	  return ret;
> +	}
> +      ret += fromhex (hex[0]) * 16 + fromhex (hex[1]);
> +      hex += 2;
> +    }
> +
> +  return ret;
> +}
> +
> +/* See rsp-low.h.  */
> +
>  int
>  bin2hex (const gdb_byte *bin, char *hex, int count)
>  {
> @@ -146,6 +169,22 @@ bin2hex (const gdb_byte *bin, char *hex, int count)
>    return i;
>  }
>  
> +/* See rsp-low.h.  */
> +
> +std::string
> +bin2hex (const gdb_byte *bin, int count)
> +{
> +  std::string ret;
> +
> +  for (int i = 0; i < count; ++i)
> +    {
> +      ret += tohex ((*bin >> 4) & 0xf);
> +      ret += tohex (*bin++ & 0xf);
> +    }
> +
> +  return ret;
> +}
> +
>  /* Return whether byte B needs escaping when sent as part of binary data.  */
>  
>  static int
> diff --git a/gdb/common/rsp-low.h b/gdb/common/rsp-low.h
> index b57f58bc75..91cff4dac5 100644
> --- a/gdb/common/rsp-low.h
> +++ b/gdb/common/rsp-low.h
> @@ -52,6 +52,10 @@ extern char *unpack_varlen_hex (char *buff, ULONGEST *result);
>  
>  extern int hex2bin (const char *hex, gdb_byte *bin, int count);
>  
> +/* Like hex2bin, but return a std::string.  */
> +
> +extern std::string hex2str (const char *hex);
> +
>  /* Convert some bytes to a hexadecimal representation.  BIN holds the
>     bytes to convert.  COUNT says how many bytes to convert.  The
>     resulting characters are stored in HEX, followed by a NUL
> @@ -59,6 +63,10 @@ extern int hex2bin (const char *hex, gdb_byte *bin, int count);
>  
>  extern int bin2hex (const gdb_byte *bin, char *hex, int count);
>  
> +/* Overloaded version of bin2hex that return a std::string.  */
> +
> +extern std::string bin2hex (const gdb_byte *bin, int count);
> +
>  /* Convert BUFFER, binary data at least LEN_UNITS addressable memory units
>     long, into escaped binary data in OUT_BUF.  Only copy memory units that fit
>     completely in OUT_BUF.  Set *OUT_LEN_UNITS to the number of units from
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 7528183506..1c871cb34c 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -2363,6 +2363,7 @@ print the names and values of all environment variables to be given to
>  your program.  You can abbreviate @code{environment} as @code{env}.
>  
>  @kindex set environment
> +@anchor{set environment}
>  @item set environment @var{varname} @r{[}=@var{value}@r{]}
>  Set environment variable @var{varname} to @var{value}.  The value
>  changes for your program (and the shell @value{GDBN} uses to launch
> @@ -2391,12 +2392,21 @@ If necessary, you can avoid that by using the @samp{env} program as a
>  wrapper instead of using @code{set environment}.  @xref{set
>  exec-wrapper}, for an example doing just that.
>  
> +Environment variables that are set by the user are also transmitted to
> +@command{gdbserver} to be used when starting the remote inferior.
> +@pxref{QEnvironmentHexEncoded}.
> +
>  @kindex unset environment
> +@anchor{unset environment}
>  @item unset environment @var{varname}
>  Remove variable @var{varname} from the environment to be passed to your
>  program.  This is different from @samp{set env @var{varname} =};
>  @code{unset environment} removes the variable from the environment,
>  rather than assigning it an empty value.
> +
> +Environment variables that are unset by the user are also unset on
> +@command{gdbserver} when starting the remote inferior.
> +@pxref{QEnvironmentUnset}.
>  @end table
>  
>  @emph{Warning:} On Unix systems, @value{GDBN} runs your program using
> @@ -20816,6 +20826,18 @@ are:
>  @tab @code{QStartupWithShell}
>  @tab @code{set startup-with-shell}
>  
> +@item @code{environment-hex-encoded}
> +@tab @code{QEnvironmentHexEncoded}
> +@tab @code{set environment}
> +
> +@item @code{environment-unset}
> +@tab @code{QEnvironmentUnset}
> +@tab @code{unset environment}
> +
> +@item @code{environment-reset}
> +@tab @code{QEnvironmentReset}
> +@tab @code{Reset the inferior environment (i.e., unset user-set variables)}
> +
>  @item @code{conditional-breakpoints-packet}
>  @tab @code{Z0 and Z1}
>  @tab @code{Support for target-side breakpoint condition evaluation}
> @@ -36490,6 +36512,100 @@ actually support starting the inferior using a shell.
>  Use of this packet is controlled by the @code{set startup-with-shell}
>  command; @pxref{set startup-with-shell}.
>  
> +@item QEnvironmentHexEncoded:@var{hex-value}
> +@anchor{QEnvironmentHexEncoded}
> +@cindex set environment variable, remote request
> +@cindex @samp{QEnvironmentHexEncoded} packet
> +On UNIX-like targets, it is possible to set environment variables that
> +will be passed to the inferior during the startup process.  This
> +packet is used to inform @command{gdbserver} of an environment
> +variable that has been defined by the user on @value{GDBN} (@pxref{set
> +environment}).
> +
> +The packet is composed by @var{hex-value}, an hex encoded
> +representation of the @var{name=value} format representing an
> +environment variable.  The name of the environment variable is
> +represented by @var{name}, and the value to be assigned to the
> +environment variable is represented by @var{value}.  If the variable
> +has no value (i.e., the value is @code{null}), then @var{value} will
> +not be present.
> +
> +This packet is only available in extended mode (@pxref{extended
> +mode}).
> +
> +Reply:
> +@table @samp
> +@item OK
> +The request succeeded.
> +@end table
> +
> +This packet is not probed by default; the remote stub must request it,
> +by supplying an appropriate @samp{qSupported} response
> +(@pxref{qSupported}).  This should only be done on targets that
> +actually support passing environment variables to the starting
> +inferior.
> +
> +This packet is related to the @code{set environment} command;
> +@pxref{set environment}.
> +
> +@item QEnvironmentUnset:@var{hex-value}
> +@anchor{QEnvironmentUnset}
> +@cindex unset environment variable, remote request
> +@cindex @samp{QEnvironmentUnset} packet
> +On UNIX-like targets, it is possible to unset environment variables
> +before starting the inferior in the remote target.  This packet is
> +used to inform @command{gdbserver} of an environment variable that has
> +been unset by the user on @value{GDBN} (@pxref{unset environment}).
> +
> +The packet is composed by @var{hex-value}, an hex encoded
> +representation of the name of the environment variable to be unset.
> +
> +This packet is only available in extended mode (@pxref{extended
> +mode}).
> +
> +Reply:
> +@table @samp
> +@item OK
> +The request succeeded.
> +@end table
> +
> +This packet is not probed by default; the remote stub must request it,
> +by supplying an appropriate @samp{qSupported} response
> +(@pxref{qSupported}).  This should only be done on targets that
> +actually support passing environment variables to the starting
> +inferior.
> +
> +This packet is related to the @code{unset environment} command;
> +@pxref{unset environment}.
> +
> +@item QEnvironmentReset
> +@anchor{QEnvironmentReset}
> +@cindex reset environment, remote request
> +@cindex @samp{QEnvironmentReset} packet
> +On UNIX-like targets, this packet is used to reset the state of
> +environment variables in the remote target before starting the
> +inferior.  In this context, reset means unsetting all environment
> +variables that were previously set by the user (i.e., were not
> +initially present in the environment).  It is sent to
> +@command{gdbserver} before the @samp{QEnvironmentHexEncoded}
> +(@pxref{QEnvironmentHexEncoded}) and the @samp{QEnvironmentUnset}
> +(@pxref{QEnvironmentUnset}) packets.
> +
> +This packet is only available in extended mode (@pxref{extended
> +mode}).
> +
> +Reply:
> +@table @samp
> +@item OK
> +The request succeeded.
> +@end table
> +
> +This packet is not probed by default; the remote stub must request it,
> +by supplying an appropriate @samp{qSupported} response
> +(@pxref{qSupported}).  This should only be done on targets that
> +actually support passing environment variables to the starting
> +inferior.
> +
>  @item qfThreadInfo
>  @itemx qsThreadInfo
>  @cindex list active threads, remote request
> diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
> index 38383510e8..fa1116a5ee 100644
> --- a/gdb/gdbserver/server.c
> +++ b/gdb/gdbserver/server.c
> @@ -631,6 +631,67 @@ handle_general_set (char *own_buf)
>        return;
>      }
>  
> +  if (startswith (own_buf, "QEnvironmentReset"))
> +    {
> +      our_environ = gdb_environ::from_host_environ ();
> +
> +      write_ok (own_buf);
> +      return;
> +    }
> +
> +  if (startswith (own_buf, "QEnvironmentHexEncoded:"))
> +    {
> +      const char *p = own_buf + sizeof ("QEnvironmentHexEncoded:") - 1;
> +      /* The final form of the environment variable.  FINAL_VAR will
> +	 hold the 'VAR=VALUE' format.  */
> +      std::string final_var = hex2str (p);
> +      std::string var_name, var_value;
> +
> +      if (remote_debug)
> +	{
> +	  debug_printf (_("[QEnvironmentHexEncoded received '%s']\n"), p);
> +	  debug_printf (_("[Environment variable to be set: '%s']\n"),
> +			final_var.c_str ());
> +	  debug_flush ();
> +	}
> +
> +      size_t pos = final_var.find ('=');
> +      if (pos == std::string::npos)
> +	{
> +	  warning (_("Unexpected format for environment variable: '%s'"),
> +		   final_var.c_str ());
> +	  write_enn (own_buf);
> +	  return;
> +	}
> +
> +      var_name = final_var.substr (0, pos);
> +      var_value = final_var.substr (pos + 1, std::string::npos);
> +
> +      our_environ.set (var_name.c_str (), var_value.c_str ());
> +
> +      write_ok (own_buf);
> +      return;
> +    }
> +
> +  if (startswith (own_buf, "QEnvironmentUnset:"))
> +    {
> +      const char *p = own_buf + sizeof ("QEnvironmentUnset:") - 1;
> +      std::string varname = hex2str (p);
> +
> +      if (remote_debug)
> +	{
> +	  debug_printf (_("[QEnvironmentUnset received '%s']\n"), p);
> +	  debug_printf (_("[Environment variable to be unset: '%s']\n"),
> +			varname.c_str ());
> +	  debug_flush ();
> +	}
> +
> +      our_environ.unset (varname.c_str ());
> +
> +      write_ok (own_buf);
> +      return;
> +    }
> +
>    if (strcmp (own_buf, "QStartNoAckMode") == 0)
>      {
>        if (remote_debug)
> @@ -2228,7 +2289,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
>  	}
>  
>        sprintf (own_buf,
> -	       "PacketSize=%x;QPassSignals+;QProgramSignals+;QStartupWithShell+",
> +	       "PacketSize=%x;QPassSignals+;QProgramSignals+;"
> +	       "QStartupWithShell+;QEnvironmentHexEncoded+;"
> +	       "QEnvironmentReset+;QEnvironmentUnset+",
>  	       PBUFSIZ - 1);
>  
>        if (target_supports_catch_syscall ())
> diff --git a/gdb/remote.c b/gdb/remote.c
> index ff59a0f81f..8c275fcf4f 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -73,6 +73,7 @@
>  #include "record-btrace.h"
>  #include <algorithm>
>  #include "common/scoped_restore.h"
> +#include "environ.h"
>  
>  /* Temp hacks for tracepoint encoding migration.  */
>  static char *target_buf;
> @@ -1430,6 +1431,9 @@ enum {
>    PACKET_QCatchSyscalls,
>    PACKET_QProgramSignals,
>    PACKET_QStartupWithShell,
> +  PACKET_QEnvironmentHexEncoded,
> +  PACKET_QEnvironmentReset,
> +  PACKET_QEnvironmentUnset,
>    PACKET_qCRC,
>    PACKET_qSearch_memory,
>    PACKET_vAttach,
> @@ -4637,6 +4641,12 @@ static const struct protocol_feature remote_protocol_features[] = {
>      PACKET_QProgramSignals },
>    { "QStartupWithShell", PACKET_DISABLE, remote_supported_packet,
>      PACKET_QStartupWithShell },
> +  { "QEnvironmentHexEncoded", PACKET_DISABLE, remote_supported_packet,
> +    PACKET_QEnvironmentHexEncoded },
> +  { "QEnvironmentReset", PACKET_DISABLE, remote_supported_packet,
> +    PACKET_QEnvironmentReset },
> +  { "QEnvironmentUnset", PACKET_DISABLE, remote_supported_packet,
> +    PACKET_QEnvironmentUnset },
>    { "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
>      PACKET_QStartNoAckMode },
>    { "multiprocess", PACKET_DISABLE, remote_supported_packet,
> @@ -9556,6 +9566,57 @@ extended_remote_run (const std::string &args)
>      }
>  }
>  
> +/* Helper function to send set/unset environment packets.  ACTION is
> +   either "set" or "unset".  PACKET is either "QEnvironmentHexEncoded"
> +   or "QEnvironmentUnsetVariable".  VALUE is the variable to be
> +   sent.  */
> +
> +static void
> +send_environment_packet (struct remote_state *rs,
> +			 const char *action,
> +			 const char *packet,
> +			 const char *value)
> +{
> +  /* Convert the environment variable to an hex string, which
> +     is the best format to be transmitted over the wire.  */
> +  std::string encoded_value = bin2hex ((const gdb_byte *) value,
> +					 strlen (value));
> +
> +  xsnprintf (rs->buf, get_remote_packet_size (),
> +	     "%s:%s", packet, encoded_value.c_str ());
> +
> +  putpkt (rs->buf);
> +  getpkt (&rs->buf, &rs->buf_size, 0);
> +  if (strcmp (rs->buf, "OK") != 0)
> +    warning (_("Unable to %s environment variable '%s' on remote."),
> +	     action, value);
> +}
> +
> +/* Helper function to handle the QEnvironment* packets.  */
> +
> +static void
> +extended_remote_environment_support (struct remote_state *rs)
> +{
> +  if (packet_support (PACKET_QEnvironmentReset) != PACKET_DISABLE)
> +    {
> +      putpkt ("QEnvironmentReset");
> +      getpkt (&rs->buf, &rs->buf_size, 0);
> +      if (strcmp (rs->buf, "OK") != 0)
> +	warning (_("Unable to reset environment on remote."));
> +    }
> +
> +  gdb_environ *e = &current_inferior ()->environment;
> +
> +  if (packet_support (PACKET_QEnvironmentHexEncoded) != PACKET_DISABLE)
> +    for (const std::string &el : e->user_set_env ())
> +      send_environment_packet (rs, "set", "QEnvironmentHexEncoded",
> +			       el.c_str ());
> +
> +  if (packet_support (PACKET_QEnvironmentUnset) != PACKET_DISABLE)
> +    for (const std::string &el : e->user_unset_env ())
> +      send_environment_packet (rs, "unset", "QEnvironmentUnset", el.c_str ());
> +}
> +
>  /* In the extended protocol we want to be able to do things like
>     "run" and have them basically work as expected.  So we need
>     a special create_inferior function.  We support changing the
> @@ -9596,6 +9657,8 @@ Remote replied unexpectedly while setting startup-with-shell: %s"),
>  	       rs->buf);
>      }
>  
> +  extended_remote_environment_support (rs);
> +
>    /* Now restart the remote server.  */
>    run_worked = extended_remote_run (args) != -1;
>    if (!run_worked)
> @@ -14067,6 +14130,19 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
>    add_packet_config_cmd (&remote_protocol_packets[PACKET_QStartupWithShell],
>  			 "QStartupWithShell", "startup-with-shell", 0);
>  
> +  add_packet_config_cmd (&remote_protocol_packets
> +			 [PACKET_QEnvironmentHexEncoded],
> +			 "QEnvironmentHexEncoded", "environment-hex-encoded",
> +			 0);
> +
> +  add_packet_config_cmd (&remote_protocol_packets[PACKET_QEnvironmentReset],
> +			 "QEnvironmentReset", "environment-reset",
> +			 0);
> +
> +  add_packet_config_cmd (&remote_protocol_packets[PACKET_QEnvironmentUnset],
> +			 "QEnvironmentUnset", "environment-unset",
> +			 0);
> +
>    add_packet_config_cmd (&remote_protocol_packets[PACKET_qSymbol],
>  			 "qSymbol", "symbol-lookup", 0);
>  
> diff --git a/gdb/testsuite/gdb.base/share-env-with-gdbserver.c b/gdb/testsuite/gdb.base/share-env-with-gdbserver.c
> new file mode 100644
> index 0000000000..740bd0fb52
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/share-env-with-gdbserver.c
> @@ -0,0 +1,40 @@
> +/* 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>
> +
> +/* Wrapper around getenv for GDB.  */
> +
> +static const char *
> +my_getenv (const char *name)
> +{
> +  return getenv (name);
> +}
> +
> +int
> +main (int argc, char *argv[])
> +{
> +  const char *myvar = getenv ("GDB_TEST_VAR");
> +
> +  if (myvar != NULL)
> +    printf ("It worked!  myvar = '%s'\n", myvar);
> +  else
> +    printf ("It failed.");
> +
> +  return 0;	/* break-here */
> +}
> diff --git a/gdb/testsuite/gdb.base/share-env-with-gdbserver.exp b/gdb/testsuite/gdb.base/share-env-with-gdbserver.exp
> new file mode 100644
> index 0000000000..0aca8f6b5e
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/share-env-with-gdbserver.exp
> @@ -0,0 +1,254 @@
> +# 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/>.
> +
> +# This test doesn't make sense on native-gdbserver.
> +if { [use_gdb_stub] } {
> +    untested "not supported"
> +    return
> +}
> +
> +standard_testfile
> +
> +if { [prepare_for_testing "failed to prepare" $testfile $srcfile debug] } {
> +    return -1
> +}
> +
> +set test_var_name "GDB_TEST_VAR"
> +
> +# Helper function that performs a check on the output of "getenv".
> +#
> +# - VAR_NAME is the name of the variable to be checked.
> +#
> +# - VAR_VALUE is the value expected.
> +#
> +# - TEST_MSG, if not empty, is the test message to be used by the
> +#   "gdb_test".
> +#
> +# - EMPTY_VAR_P, if non-zero, means that the variable is not expected
> +#   to exist.  In this case, VAR_VALUE is not considered.
> +
> +proc check_getenv { var_name var_value { test_msg "" } { empty_var_p 0 } } {
> +    global hex decimal
> +
> +    if { $test_msg == "" } {
> +	set test_msg "print result of getenv for $var_name"
> +    }
> +
> +    if { $empty_var_p } {
> +	set var_value_match "0x0"
> +    } else {
> +	set var_value_match "$hex \"$var_value\""
> +    }
> +
> +    gdb_test "print my_getenv (\"$var_name\")" "\\\$$decimal = $var_value_match" \
> +	$test_msg
> +}
> +
> +# Helper function to re-run to main and breaking at the "break-here"
> +# label.
> +
> +proc do_prepare_inferior { } {
> +    global decimal hex
> +
> +    if { ![runto_main] } {
> +	return -1
> +    }
> +
> +    gdb_breakpoint [gdb_get_line_number "break-here"]
> +
> +    gdb_test "continue" "Breakpoint $decimal, main \\\(argc=1, argv=$hex\\\) at.*" \
> +	"continue until breakpoint"
> +}
> +
> +# Helper function that does the actual testing.
> +#
> +# - VAR_VALUE is the value of the environment variable.
> +#
> +# - VAR_NAME is the name of the environment variable.  If empty,
> +#   defaults to $test_var_name.
> +#
> +# - VAR_NAME_MATCH is the name (regex) that will be used to query the
> +#   environment about the variable (via getenv).  This is useful when
> +#   we're testing variables with strange names (e.g., with an equal
> +#   sign in the name) and we know that the variable will actually be
> +#   set using another name.  If empty, defatults, to $var_name.
> +#
> +# - VAR_VALUE_MATCH is the value (regex) that will be used to match
> +#   the result of getenv.  The rationale is the same as explained for
> +#   VAR_NAME_MATCH.  If empty, defaults, to $var_value.
> +
> +proc do_test { var_value { var_name "" } { var_name_match "" } { var_value_match "" } } {
> +    global hex decimal binfile test_var_name
> +
> +    clean_restart $binfile
> +
> +    if { $var_name == "" } {
> +	set var_name $test_var_name
> +    }
> +
> +    if { $var_name_match == "" } {
> +	set var_name_match $var_name
> +    }
> +
> +    if { $var_value_match == "" } {
> +	set var_value_match $var_value
> +    }
> +
> +    if { $var_value != "" } {
> +	gdb_test_no_output "set environment $var_name = $var_value" \
> +	    "set $var_name = $var_value"
> +    } else {
> +	gdb_test "set environment $var_name =" \
> +	    "Setting environment variable \"$var_name\" to null value." \
> +	    "set $var_name to null value"
> +    }
> +
> +    do_prepare_inferior
> +
> +    check_getenv "$var_name_match" "$var_value_match" \
> +	"print result of getenv for $var_name"
> +}
> +
> +with_test_prefix "long var value" {
> +    do_test "this is my test variable; testing long vars; {}"
> +}
> +
> +with_test_prefix "empty var" {
> +    do_test ""
> +}
> +
> +with_test_prefix "strange named var" {
> +    # In this test we're doing the following:
> +    # 
> +    #   (gdb) set environment 'asd =' = 123 43; asd b ### [];;;
> +    #
> +    # However, due to how GDB parses this line, the environment
> +    # variable will end up named <'asd> (without the <>), and its
> +    # value will be <' = 123 43; asd b ### [];;;> (without the <>).
> +    do_test "123 43; asd b ### [];;;" "'asd ='" "'asd" "' = 123 43; asd b ### [];;;"
> +}
> +
> +# Test setting and unsetting environment variables in various
> +# fashions.
> +
> +proc test_set_unset_vars { } {
> +    global hex decimal binfile
> +
> +    clean_restart $binfile
> +
> +    with_test_prefix "set 3 environment variables" {
> +	# Set some environment variables
> +	gdb_test_no_output "set environment A = 1" \
> +	    "set A to 1"
> +	gdb_test_no_output "set environment B = 2" \
> +	    "set B to 2"
> +	gdb_test_no_output "set environment C = 3" \
> +	    "set C to 3"
> +
> +	do_prepare_inferior
> +
> +	# Check that the variables are known by the inferior
> +	check_getenv "A" "1"
> +	check_getenv "B" "2"
> +	check_getenv "C" "3"
> +    }
> +
> +    with_test_prefix "unset one variable, reset one" {
> +	# Now, unset/reset some values
> +	gdb_test_no_output "unset environment A" \
> +	    "unset A"
> +	gdb_test_no_output "set environment B = 4" \
> +	    "set B to 4"
> +
> +	do_prepare_inferior
> +
> +	check_getenv "A" "" "" 1
> +	check_getenv "B" "4"
> +	check_getenv "C" "3"
> +    }
> +
> +    with_test_prefix "unset two variables, reset one" {
> +	# Unset more values
> +	gdb_test_no_output "unset environment B" \
> +	    "unset B"
> +	gdb_test_no_output "set environment A = 1" \
> +	    "set A to 1 again"
> +	gdb_test_no_output "unset environment C" \
> +	    "unset C"
> +
> +	do_prepare_inferior
> +
> +	check_getenv "A" "1"
> +	check_getenv "B" "" "" 1
> +	check_getenv "C" "" "" 1
> +    }
> +}
> +
> +with_test_prefix "test set/unset of vars" {
> +    test_set_unset_vars
> +}
> +
> +# Test that unsetting works.
> +
> +proc test_unset { } {
> +    global hex decimal binfile gdb_prompt
> +
> +    clean_restart $binfile
> +
> +    do_prepare_inferior
> +
> +    set test_msg "check if unset works"
> +    set found_home 0
> +    gdb_test_multiple "print my_getenv (\"HOME\")" $test_msg {
> +	-re "\\\$$decimal = $hex \".*\"\r\n$gdb_prompt $" {
> +	    pass $test_msg
> +	    set found_home 1
> +	}
> +	-re "\\\$$decimal = 0x0\r\n$gdb_prompt $" {
> +	    untested $test_msg
> +	}
> +    }
> +
> +    if { $found_home == 1 } {
> +	with_test_prefix "simple unset" {
> +	    # We can do the test, because $HOME exists (and therefore can
> +	    # be unset).
> +	    gdb_test_no_output "unset environment HOME" "unset HOME"
> +
> +	    do_prepare_inferior
> +
> +	    # $HOME now must be empty
> +	    check_getenv "HOME" "" "" 1
> +	}
> +
> +	with_test_prefix "set-then-unset" {
> +	    clean_restart $binfile
> +
> +	    # Test if setting and then unsetting $HOME works.
> +	    gdb_test_no_output "set environment HOME = test" "set HOME as test"
> +	    gdb_test_no_output "unset environment HOME" "unset HOME again"
> +
> +	    do_prepare_inferior
> +
> +	    check_getenv "HOME" "" "" 1
> +	}
> +    }
> +}
> +
> +with_test_prefix "test unset of vars" {
> +    test_unset
> +}
> diff --git a/gdb/unittests/environ-selftests.c b/gdb/unittests/environ-selftests.c
> index 28b16f828f..e17715b43c 100644
> --- a/gdb/unittests/environ-selftests.c
> +++ b/gdb/unittests/environ-selftests.c
> @@ -22,6 +22,12 @@
>  #include "common/environ.h"
>  #include "common/diagnostics.h"
>  
> +static bool
> +set_contains (const std::set<std::string> &set, std::string key)
> +{
> +  return set.find (key) != set.end ();
> +}
> +
>  namespace selftests {
>  namespace gdb_environ_tests {
>  
> @@ -38,6 +44,8 @@ run_tests ()
>    /* When the vector is initialized, there should always be one NULL
>       element in it.  */
>    SELF_CHECK (env.envp ()[0] == NULL);
> +  SELF_CHECK (env.user_set_env ().size () == 0);
> +  SELF_CHECK (env.user_unset_env ().size () == 0);
>  
>    /* Make sure that there is no other element.  */
>    SELF_CHECK (env.get ("PWD") == NULL);
> @@ -45,14 +53,24 @@ run_tests ()
>    /* Check if unset followed by a set in an empty vector works.  */
>    env.set ("PWD", "test");
>    SELF_CHECK (strcmp (env.get ("PWD"), "test") == 0);
> +  SELF_CHECK (set_contains (env.user_set_env (), std::string ("PWD=test")));
> +  SELF_CHECK (env.user_unset_env ().size () == 0);
>    /* The second element must be NULL.  */
>    SELF_CHECK (env.envp ()[1] == NULL);
> +  SELF_CHECK (env.user_set_env ().size () == 1);
>    env.unset ("PWD");
>    SELF_CHECK (env.envp ()[0] == NULL);
> +  SELF_CHECK (env.user_set_env ().size () == 0);
> +  SELF_CHECK (env.user_unset_env ().size () == 1);
> +  SELF_CHECK (set_contains (env.user_unset_env (), std::string ("PWD")));
>  
>    /* Initialize the environment vector using the host's environ.  */
>    env = gdb_environ::from_host_environ ();
>  
> +  /* The user-set and user-unset lists must be empty.  */
> +  SELF_CHECK (env.user_set_env ().size () == 0);
> +  SELF_CHECK (env.user_unset_env ().size () == 0);
> +
>    /* Our test environment variable should be present at the
>       vector.  */
>    SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON"), "1") == 0);
> @@ -65,6 +83,9 @@ run_tests ()
>       host's environment, but doesn't exist in our vector.  */
>    env.unset ("GDB_SELFTEST_ENVIRON");
>    SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON") == NULL);
> +  SELF_CHECK (env.user_unset_env ().size () == 1);
> +  SELF_CHECK (set_contains (env.user_unset_env (),
> +			    std::string ("GDB_SELFTEST_ENVIRON")));
>  
>    /* Re-set the test variable.  */
>    env.set ("GDB_SELFTEST_ENVIRON", "1");
> @@ -75,6 +96,8 @@ run_tests ()
>       variable.  */
>    env.clear ();
>    SELF_CHECK (env.envp ()[0] == NULL);
> +  SELF_CHECK (env.user_set_env ().size () == 0);
> +  SELF_CHECK (env.user_unset_env ().size () == 0);
>    SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON") == NULL);
>  
>    /* Reinitialize our environ vector using the host environ.  We
> @@ -89,6 +112,23 @@ run_tests ()
>        ++num_found;
>    SELF_CHECK (num_found == 1);
>  
> +  /* Before unsetting our test variable, test that user-unset works
> +     fine.  */
> +  env.unset ("GDB_SELFTEST_ENVIRON");
> +  SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON") == NULL);
> +  SELF_CHECK (env.user_set_env ().size () == 0);
> +  SELF_CHECK (env.user_unset_env ().size () == 1);
> +  SELF_CHECK (set_contains (env.user_unset_env (),
> +			    std::string ("GDB_SELFTEST_ENVIRON")));
> +  env.set ("GDB_SELFTEST_ENVIRON", "1");
> +  SELF_CHECK (env.user_set_env ().size () == 1);
> +  SELF_CHECK (env.user_unset_env ().size () == 0);
> +  env.unset ("GDB_SELFTEST_ENVIRON");
> +  SELF_CHECK (env.user_set_env ().size () == 0);
> +  SELF_CHECK (env.user_unset_env ().size () == 1);
> +  SELF_CHECK (set_contains (env.user_unset_env (),
> +			    std::string ("GDB_SELFTEST_ENVIRON")));
> +
>    /* Get rid of our test variable.  */
>    unsetenv ("GDB_SELFTEST_ENVIRON");
>  
> @@ -97,6 +137,10 @@ run_tests ()
>       vector, but can still find B.  */
>    env.set ("GDB_SELFTEST_ENVIRON_1", "aaa");
>    SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_1"), "aaa") == 0);
> +  /* User-set environ var list must contain one element.  */
> +  SELF_CHECK (env.user_set_env ().size () == 1);
> +  SELF_CHECK (set_contains (env.user_set_env (),
> +			    std::string ("GDB_SELFTEST_ENVIRON_1=aaa")));
>  
>    env.set ("GDB_SELFTEST_ENVIRON_2", "bbb");
>    SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_2"), "bbb") == 0);
> @@ -104,6 +148,11 @@ run_tests ()
>    env.unset ("GDB_SELFTEST_ENVIRON_1");
>    SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON_1") == NULL);
>    SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_2"), "bbb") == 0);
> +  /* The user-set environ var list must contain only one element
> +     now.  */
> +  SELF_CHECK (set_contains (env.user_set_env (),
> +			    std::string ("GDB_SELFTEST_ENVIRON_2=bbb")));
> +  SELF_CHECK (env.user_set_env ().size () == 1);
>  
>    env.clear ();
>  
> @@ -111,11 +160,16 @@ run_tests ()
>       valid state (i.e., its only element is NULL).  */
>    env.set ("A", "1");
>    SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
> +  SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1")));
> +  SELF_CHECK (env.user_set_env ().size () == 1);
>    gdb_environ env2;
>    env2 = std::move (env);
>    SELF_CHECK (env.envp ()[0] == NULL);
>    SELF_CHECK (strcmp (env2.get ("A"), "1") == 0);
>    SELF_CHECK (env2.envp ()[1] == NULL);
> +  SELF_CHECK (env.user_set_env ().size () == 0);
> +  SELF_CHECK (set_contains (env2.user_set_env (), std::string ("A=1")));
> +  SELF_CHECK (env2.user_set_env ().size () == 1);
>    env.set ("B", "2");
>    SELF_CHECK (strcmp (env.get ("B"), "2") == 0);
>    SELF_CHECK (env.envp ()[1] == NULL);
> @@ -125,18 +179,26 @@ run_tests ()
>    env.clear ();
>    env.set ("A", "1");
>    SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
> +  SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1")));
>    gdb_environ env3 = std::move (env);
>    SELF_CHECK (env.envp ()[0] == NULL);
> +  SELF_CHECK (env.user_set_env ().size () == 0);
>    SELF_CHECK (strcmp (env3.get ("A"), "1") == 0);
>    SELF_CHECK (env3.envp ()[1] == NULL);
> +  SELF_CHECK (set_contains (env3.user_set_env (), std::string ("A=1")));
> +  SELF_CHECK (env3.user_set_env ().size () == 1);
>    env.set ("B", "2");
>    SELF_CHECK (strcmp (env.get ("B"), "2") == 0);
>    SELF_CHECK (env.envp ()[1] == NULL);
> +  SELF_CHECK (set_contains (env.user_set_env (), std::string ("B=2")));
> +  SELF_CHECK (env.user_set_env ().size () == 1);
>  
>    /* Test self-move.  */
>    env.clear ();
>    env.set ("A", "1");
>    SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
> +  SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1")));
> +  SELF_CHECK (env.user_set_env ().size () == 1);
>  
>    /* Some compilers warn about moving to self, but that's precisely what we want
>       to test here, so turn this warning off.  */
> @@ -148,6 +210,8 @@ run_tests ()
>    SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
>    SELF_CHECK (strcmp (env.envp ()[0], "A=1") == 0);
>    SELF_CHECK (env.envp ()[1] == NULL);
> +  SELF_CHECK (set_contains (env.user_set_env (), std::string ("A=1")));
> +  SELF_CHECK (env.user_set_env ().size () == 1);
>  }
>  } /* namespace gdb_environ */
>  } /* namespace selftests */
> -- 
> 2.13.3

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]