From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 108932 invoked by alias); 10 Jul 2017 21:32:58 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 108909 invoked by uid 89); 10 Jul 2017 21:32:58 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00,GIT_PATCH_0,GIT_PATCH_1,GIT_PATCH_2,GIT_PATCH_3,RP_MATCHES_RCVD,SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=sprintf, Inform, 4112 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 10 Jul 2017 21:32:53 +0000 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id A079B8123E for ; Mon, 10 Jul 2017 21:32:52 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com A079B8123E 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@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com A079B8123E Received: from localhost (unused-10-15-17-193.yyz.redhat.com [10.15.17.193]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 53E9D5D6A6; Mon, 10 Jul 2017 21:32:52 +0000 (UTC) From: Sergio Durigan Junior To: GDB Patches Cc: Pedro Alves Subject: Re: [PATCH] Implement the ability to transmit environment variables to GDBserver when starting the inferior References: <20170629194106.23070-1-sergiodj@redhat.com> Date: Mon, 10 Jul 2017 21:32:00 -0000 In-Reply-To: <20170629194106.23070-1-sergiodj@redhat.com> (Sergio Durigan Junior's message of "Thu, 29 Jun 2017 15:41:06 -0400") Message-ID: <87k23grn98.fsf@redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.1 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain X-IsSubscribed: yes X-SW-Source: 2017-07/txt/msg00086.txt.bz2 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: > > > > 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 > > * 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 > > * 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 > > * gdb.texinfo (set environment): Add @anchor. Explain that > environment variables set by the user are sent to GDBserver. > (Connecting) : Add "environment-hex-encoded" > and "QEnvironmentHexEncoded" to the table. > (Remote Protocol) : New item, explaining > the packet. > > gdb/testsuite/ChangeLog: > yyyy-mm-dd Sergio Durigan Junior > > * 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::iterator it_env; > + std::vector::iterator it_user_env; > + char *found_var; > > /* We iterate until '.end () - 1' because the last element is > always NULL. */ > - for (std::vector::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 (&m_environ_vector[0]); > } > + > +/* See common/environ.h. */ > + > +const char ** > +gdb_environ::user_envp () const > +{ > + return const_cast (&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 m_environ_vector; > + > + /* The vector containing the environment variables set by the > + user. */ > + std::vector 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 > +#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 . */ > + > +#include > +#include > + > +/* 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 . > + > +# 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/