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] Implement the ability to transmit environment variables to GDBserver when starting the inferior


On Thursday, June 29 2017, I wrote:

> This is the first version of this patch, which aims to implement the
> ability to transmit user-set environment variables to GDBserver when
> starting the remote inferior.

Ping.

> 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.
>
> The idea behind this patch is to store user-set environment variables
> in a separate vector, part of gdb_environ, and provide this list to
> extended_remote_create_inferior when the preparations to start the
> inferior are happening.  extended_remote_create_inferior, then,
> iterates over this list and sends a new packet,
> QEnvironmentHexEncoded, which contains the hex-encoded string that
> represents the "VAR=VALUE" format (VALUE can be empty if the user set
> a variable with a null value, by doing 'set environment VAR=').
>
> 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.
>
> 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 sending environment variables to GDBserver.
> 	(New remote packets): Add entry of QEnvironmentHexEncoded.
> 	* common/environ.c (gdb_environ::operator=): Extend method to
> 	handle m_user_env_list.
> 	(gdb_environ::clear): Likewise.
> 	(match_var_in_string): Change type of first parameter from 'char
> 	*' to 'const char *'.
> 	(gdb_environ::get): New variable 'fullvar'.  Handle
> 	m_user_env_list.
> 	(gdb_environ::unset): Extend method to handle m_user_env_list.
> 	(gdb_environ::user_envp): New method.
> 	* common/environ.h (gdb_environ): Add NULL to m_user_env_list;
> 	handle m_user_env_list on move constructor/assignment.
> 	(user_envp): New method.
> 	(m_user_env_list): New vector.
> 	* remote.c: Include "environ.h". Add QEnvironmentHexEncoded.
> 	(remote_protocol_features): Add
> 	QEnvironmentHexEncoded packet.
> 	(extended_remote_environment_support): New function.
> 	(extended_remote_create_inferior): Call
> 	extended_remote_environment_support.
> 	(_initialize_remote): Add QEnvironmentHexEncoded packet config.
> 	* unittests/environ-selftests.c (run_tests): Add tests for
> 	m_user_env_list.
>
> gdb/gdbserver/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>
> 	* server.c (handle_general_set): Handle QEnvironmentHexEncoded
> 	packet.
> 	(handle_query): Inform remote that QEnvironmentHexEncoded is
> 	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.
> 	(Connecting) <Remote Packet>: Add "environment-hex-encoded"
> 	and "QEnvironmentHexEncoded" to the table.
> 	(Remote Protocol) <QEnvironmentHexEncoded>: 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                                           |  12 +++
>  gdb/common/environ.c                               |  59 +++++++++---
>  gdb/common/environ.h                               |  14 ++-
>  gdb/doc/gdb.texinfo                                |  45 +++++++++
>  gdb/gdbserver/server.c                             |  54 ++++++++++-
>  gdb/remote.c                                       |  50 ++++++++++
>  gdb/testsuite/gdb.base/share-env-with-gdbserver.c  |  40 ++++++++
>  .../gdb.base/share-env-with-gdbserver.exp          | 105 +++++++++++++++++++++
>  gdb/unittests/environ-selftests.c                  |  30 ++++++
>  9 files changed, 395 insertions(+), 14 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 7c8a8f6..9026733 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,14 @@
>  
>  *** Changes since GDB 8.0
>  
> +* On Unix systems, GDB now supports transmitting environment variables
> +  to GDBserver that are to be passed to the inferior when it is being
> +  started.
> +
> +  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.
> +
>  * On Unix systems, GDBserver now does globbing expansion and variable
>    substitution in inferior command line arguments.
>  
> @@ -14,6 +22,10 @@
>  
>  * New remote packets
>  
> +QEnvironmentHexEncoded
> +  Inform GDBserver of an environment variable that is to be passed to
> +  the inferior when starting it.
> +
>  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 698bda3..da0af98 100644
> --- a/gdb/common/environ.c
> +++ b/gdb/common/environ.c
> @@ -30,8 +30,11 @@ gdb_environ::operator= (gdb_environ &&e)
>      return *this;
>  
>    m_environ_vector = std::move (e.m_environ_vector);
> +  m_user_env_list = std::move (e.m_user_env_list);
>    e.m_environ_vector.clear ();
> +  e.m_user_env_list.clear ();
>    e.m_environ_vector.push_back (NULL);
> +  e.m_user_env_list.push_back (NULL);
>    return *this;
>  }
>  
> @@ -63,8 +66,10 @@ gdb_environ::clear ()
>    for (char *v : m_environ_vector)
>      xfree (v);
>    m_environ_vector.clear ();
> +  m_user_env_list.clear ();
>    /* Always add the NULL element.  */
>    m_environ_vector.push_back (NULL);
> +  m_user_env_list.push_back (NULL);
>  }
>  
>  /* Helper function to check if STRING contains an environment variable
> @@ -72,7 +77,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,12 +104,14 @@ 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);
>  
>    /* 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);
> +  m_user_env_list.insert (m_user_env_list.end () - 1, fullvar);
>  }
>  
>  /* See common/environ.h.  */
> @@ -113,18 +120,38 @@ void
>  gdb_environ::unset (const char *var)
>  {
>    size_t len = strlen (var);
> +  std::vector<char *>::iterator it_env;
> +  std::vector<const char *>::iterator it_user_env;
> +  char *found_var;
>  
>    /* 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)
> +    {
> +      /* No element has been found.  */
> +      return;
> +    }
> +
> +  found_var = *it_env;
> +
> +  /* We iterate until '.end () - 1' because the last element is
> +     always NULL.  */
> +  for (it_user_env = m_user_env_list.begin ();
> +       it_user_env != m_user_env_list.end () - 1;
> +       ++it_user_env)
> +    if (match_var_in_string (*it_user_env, var, len))
> +      break;
> +
> +  m_environ_vector.erase (it_env);
> +  if (it_user_env != m_user_env_list.end () - 1)
> +    m_user_env_list.erase (it_user_env);
> +  xfree (found_var);
>  }
>  
>  /* See common/environ.h.  */
> @@ -134,3 +161,11 @@ gdb_environ::envp () const
>  {
>    return const_cast<char **> (&m_environ_vector[0]);
>  }
> +
> +/* See common/environ.h.  */
> +
> +const char **
> +gdb_environ::user_envp () const
> +{
> +  return const_cast<const char **> (&m_user_env_list[0]);
> +}
> diff --git a/gdb/common/environ.h b/gdb/common/environ.h
> index 0bbb191..cce7f87 100644
> --- a/gdb/common/environ.h
> +++ b/gdb/common/environ.h
> @@ -32,6 +32,7 @@ public:
>         If/when we add more variables to it, NULL will always be the
>         last element.  */
>      m_environ_vector.push_back (NULL);
> +    m_user_env_list.push_back (NULL);
>    }
>  
>    ~gdb_environ ()
> @@ -41,12 +42,15 @@ 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_env_list (std::move (e.m_user_env_list))
>    {
>      /* Make sure that the moved-from vector is left at a valid
>         state (only one NULL element).  */
>      e.m_environ_vector.clear ();
> +    e.m_user_env_list.clear ();
>      e.m_environ_vector.push_back (NULL);
> +    e.m_user_env_list.push_back (NULL);
>    }
>  
>    /* Move assignment.  */
> @@ -73,9 +77,17 @@ public:
>    /* Return the environment vector represented as a 'char **'.  */
>    char **envp () const;
>  
> +  /* Return the user-set environment vector represented as a 'const
> +     char **'.  */
> +  const char **user_envp () const;
> +
>  private:
>    /* A vector containing the environment variables.  */
>    std::vector<char *> m_environ_vector;
> +
> +  /* The vector containing the environment variables set by the
> +     user.  */
> +  std::vector<const char *> m_user_env_list;
>  };
>  
>  #endif /* defined (ENVIRON_H) */
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index c167a86..d632523 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,6 +2392,10 @@ 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
>  @item unset environment @var{varname}
>  Remove variable @var{varname} from the environment to be passed to your
> @@ -20816,6 +20821,10 @@ are:
>  @tab @code{QStartupWithShell}
>  @tab @code{set startup-with-shell}
>  
> +@item @code{environment-hex-encoded}
> +@tab @code{QEnvironmentHexEncoded}
> +@tab @code{set environment}
> +
>  @item @code{conditional-breakpoints-packet}
>  @tab @code{Z0 and Z1}
>  @tab @code{Support for target-side breakpoint condition evaluation}
> @@ -36480,6 +36489,42 @@ 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 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 qfThreadInfo
>  @itemx qsThreadInfo
>  @cindex list active threads, remote request
> diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
> index 3838351..622898d 100644
> --- a/gdb/gdbserver/server.c
> +++ b/gdb/gdbserver/server.c
> @@ -631,6 +631,57 @@ handle_general_set (char *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.  */
> +      char *final_var = (char *) xcalloc (1, strlen (p) * 2);
> +      struct cleanup *c = make_cleanup (xfree, final_var);
> +      /* VAR_NAME will hold the environment variable name; VAR_VALUE
> +	 will hold its value.  These will be just pointers to
> +	 FINAL_VAR.  */
> +      char *var_name, *var_value;
> +
> +      if (remote_debug)
> +	debug_printf (_("[QEnvironmentHexEncoded received '%s']\n"), p);
> +
> +      /* Un-hexify the string received from the remote, which should
> +	 be in the form 'VAR=VALUE'.  */
> +      hex2bin (p, (gdb_byte *) final_var, strlen (p));
> +
> +      if (remote_debug)
> +	{
> +	  debug_printf (_("[Environment variable to be set: '%s']\n"),
> +			final_var);
> +	  debug_flush ();
> +	}
> +
> +      var_name = final_var;
> +      var_value = strchr (final_var, '=');
> +      /* There should always be an equal sign, even if the variable is
> +	 empty.  */
> +      if (var_value == NULL)
> +	{
> +	  warning (_("Unknown environment variable '%s' from remote side."),
> +		   final_var);
> +	  write_enn (own_buf);
> +	  do_cleanups (c);
> +	  return;
> +	}
> +      /* Mark the end of the variable's name.  */
> +      *var_value = '\0';
> +      /* And skip the '=' sign (which now is the '\0').  */
> +      ++var_value;
> +
> +      /* Finally, set the variable to be passed to the inferior.  */
> +      our_environ.set (var_name, var_value);
> +
> +      write_ok (own_buf);
> +      do_cleanups (c);
> +      return;
> +    }
> +
>    if (strcmp (own_buf, "QStartNoAckMode") == 0)
>      {
>        if (remote_debug)
> @@ -2228,7 +2279,8 @@ 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+",
>  	       PBUFSIZ - 1);
>  
>        if (target_supports_catch_syscall ())
> diff --git a/gdb/remote.c b/gdb/remote.c
> index 8e8ee6f..8be6fd1 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -72,6 +72,7 @@
>  #include "btrace.h"
>  #include "record-btrace.h"
>  #include <algorithm>
> +#include "environ.h"
>  
>  /* Temp hacks for tracepoint encoding migration.  */
>  static char *target_buf;
> @@ -1429,6 +1430,7 @@ enum {
>    PACKET_QCatchSyscalls,
>    PACKET_QProgramSignals,
>    PACKET_QStartupWithShell,
> +  PACKET_QEnvironmentHexEncoded,
>    PACKET_qCRC,
>    PACKET_qSearch_memory,
>    PACKET_vAttach,
> @@ -4636,6 +4638,8 @@ 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 },
>    { "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
>      PACKET_QStartNoAckMode },
>    { "multiprocess", PACKET_DISABLE, remote_supported_packet,
> @@ -9585,6 +9589,44 @@ extended_remote_run (const std::string &args)
>      }
>  }
>  
> +/* Helper function to handle the QEnvironmentHexEncoded packet and
> +   send the user-defined environment variables to the remote.  */
> +
> +static void
> +extended_remote_environment_support (struct remote_state *rs)
> +{
> +  gdb_environ *e = &current_inferior ()->environment;
> +  const char **user_envp = e->user_envp ();
> +
> +  for (int i = 0; user_envp[i] != NULL; ++i)
> +    {
> +      char *encoded_fullvar;
> +      const char *fullvar = user_envp[i];
> +      struct cleanup *c;
> +
> +      encoded_fullvar = (char *) xmalloc (get_remote_packet_size ());
> +      c = make_cleanup (xfree, encoded_fullvar);
> +
> +      /* Convert the environment variable to an hex string, which
> +	 is the best format to be transmitted over the wire.  */
> +      bin2hex ((gdb_byte *) fullvar, encoded_fullvar, strlen (fullvar));
> +
> +      xsnprintf (rs->buf, get_remote_packet_size (),
> +		 "QEnvironmentHexEncoded:%s", encoded_fullvar);
> +
> +      if (remote_debug)
> +	fprintf_unfiltered (gdb_stdlog, _("sending packet '%s'\n"),
> +			    rs->buf);
> +
> +      putpkt (rs->buf);
> +      getpkt (&rs->buf, &rs->buf_size, 0);
> +      if (strcmp (rs->buf, "OK") != 0)
> +	warning (_("Unable to set environment variable '%s' on remote."),
> +		 fullvar);
> +      do_cleanups (c);
> +    }
> +}
> +
>  /* 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
> @@ -9625,6 +9667,9 @@ Remote replied unexpectedly while setting startup-with-shell: %s"),
>  	       rs->buf);
>      }
>  
> +  if (packet_support (PACKET_QEnvironmentHexEncoded) != PACKET_DISABLE)
> +    extended_remote_environment_support (rs);
> +
>    /* Now restart the remote server.  */
>    run_worked = extended_remote_run (args) != -1;
>    if (!run_worked)
> @@ -14118,6 +14163,11 @@ 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_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 0000000..740bd0f
> --- /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 0000000..5cae141
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/share-env-with-gdbserver.exp
> @@ -0,0 +1,105 @@
> +# 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 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"
> +    }
> +
> +    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"
> +
> +    gdb_test "print my_getenv (\"$var_name_match\")" "\\\$$decimal = $hex \"$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 ### [];;;"
> +}
> diff --git a/gdb/unittests/environ-selftests.c b/gdb/unittests/environ-selftests.c
> index 28b16f8..b740583 100644
> --- a/gdb/unittests/environ-selftests.c
> +++ b/gdb/unittests/environ-selftests.c
> @@ -38,6 +38,7 @@ 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_envp ()[0] == NULL);
>  
>    /* Make sure that there is no other element.  */
>    SELF_CHECK (env.get ("PWD") == NULL);
> @@ -45,14 +46,20 @@ 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 (strcmp (env.user_envp ()[0], "PWD=test") == 0);
>    /* The second element must be NULL.  */
>    SELF_CHECK (env.envp ()[1] == NULL);
> +  SELF_CHECK (env.user_envp ()[1] == NULL);
>    env.unset ("PWD");
>    SELF_CHECK (env.envp ()[0] == NULL);
> +  SELF_CHECK (env.user_envp ()[0] == NULL);
>  
>    /* Initialize the environment vector using the host's environ.  */
>    env = gdb_environ::from_host_environ ();
>  
> +  /* The user-set list must be empty.  */
> +  SELF_CHECK (env.user_envp ()[0] == NULL);
> +
>    /* Our test environment variable should be present at the
>       vector.  */
>    SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON"), "1") == 0);
> @@ -75,6 +82,7 @@ run_tests ()
>       variable.  */
>    env.clear ();
>    SELF_CHECK (env.envp ()[0] == NULL);
> +  SELF_CHECK (env.user_envp ()[0] == NULL);
>    SELF_CHECK (env.get ("GDB_SELFTEST_ENVIRON") == NULL);
>  
>    /* Reinitialize our environ vector using the host environ.  We
> @@ -97,6 +105,9 @@ 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 (strcmp (env.user_envp ()[0], "GDB_SELFTEST_ENVIRON_1=aaa") == 0);
> +  SELF_CHECK (env.user_envp ()[1] == NULL);
>  
>    env.set ("GDB_SELFTEST_ENVIRON_2", "bbb");
>    SELF_CHECK (strcmp (env.get ("GDB_SELFTEST_ENVIRON_2"), "bbb") == 0);
> @@ -104,6 +115,10 @@ 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 (strcmp (env.user_envp ()[0], "GDB_SELFTEST_ENVIRON_2=bbb") == 0);
> +  SELF_CHECK (env.user_envp ()[1] == NULL);
>  
>    env.clear ();
>  
> @@ -111,11 +126,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 (strcmp (env.user_envp ()[0], "A=1") == 0);
> +  SELF_CHECK (env.user_envp ()[1] == NULL);
>    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_envp ()[0] == NULL);
> +  SELF_CHECK (strcmp (env2.user_envp ()[0], "A=1") == 0);
> +  SELF_CHECK (env2.user_envp ()[1] == NULL);
>    env.set ("B", "2");
>    SELF_CHECK (strcmp (env.get ("B"), "2") == 0);
>    SELF_CHECK (env.envp ()[1] == NULL);
> @@ -125,18 +145,26 @@ run_tests ()
>    env.clear ();
>    env.set ("A", "1");
>    SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
> +  SELF_CHECK (strcmp (env.user_envp ()[0], "A=1") == 0);
>    gdb_environ env3 = std::move (env);
>    SELF_CHECK (env.envp ()[0] == NULL);
> +  SELF_CHECK (env.user_envp ()[0] == NULL);
>    SELF_CHECK (strcmp (env3.get ("A"), "1") == 0);
>    SELF_CHECK (env3.envp ()[1] == NULL);
> +  SELF_CHECK (strcmp (env3.user_envp ()[0], "A=1") == 0);
> +  SELF_CHECK (env3.user_envp ()[1] == NULL);
>    env.set ("B", "2");
>    SELF_CHECK (strcmp (env.get ("B"), "2") == 0);
>    SELF_CHECK (env.envp ()[1] == NULL);
> +  SELF_CHECK (strcmp (env.user_envp ()[0], "B=2") == 0);
> +  SELF_CHECK (env.user_envp ()[2] == NULL);
>  
>    /* Test self-move.  */
>    env.clear ();
>    env.set ("A", "1");
>    SELF_CHECK (strcmp (env.get ("A"), "1") == 0);
> +  SELF_CHECK (strcmp (env.user_envp ()[0], "A=1") == 0);
> +  SELF_CHECK (env.user_envp ()[1] == NULL);
>  
>    /* Some compilers warn about moving to self, but that's precisely what we want
>       to test here, so turn this warning off.  */
> @@ -148,6 +176,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 (strcmp (env.user_envp ()[0], "A=1") == 0);
> +  SELF_CHECK (env.user_envp ()[1] == NULL);
>  }
>  } /* namespace gdb_environ */
>  } /* namespace selftests */
> -- 
> 2.9.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]