This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [PATCH] Implement the ability to transmit environment variables to GDBserver when starting the inferior
- From: Sergio Durigan Junior <sergiodj at redhat dot com>
- To: GDB Patches <gdb-patches at sourceware dot org>
- Cc: Pedro Alves <palves at redhat dot com>
- Date: Mon, 10 Jul 2017 17:32:51 -0400
- Subject: Re: [PATCH] Implement the ability to transmit environment variables to GDBserver when starting the inferior
- Authentication-results: sourceware.org; auth=none
- Authentication-results: ext-mx01.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com
- Authentication-results: ext-mx01.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=sergiodj at redhat dot com
- Dkim-filter: OpenDKIM Filter v2.11.0 mx1.redhat.com A079B8123E
- Dmarc-filter: OpenDMARC Filter v1.3.2 mx1.redhat.com A079B8123E
- References: <20170629194106.23070-1-sergiodj@redhat.com>
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 = ¤t_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/