From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 111234 invoked by alias); 29 Jun 2017 19:41:25 -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 111219 invoked by uid 89); 29 Jun 2017 19:41:24 -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,T_FILL_THIS_FORM_SHORT autolearn=ham version=3.3.2 spammy= 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; Thu, 29 Jun 2017 19:41:20 +0000 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 6F048C0587E3 for ; Thu, 29 Jun 2017 19:41:19 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 6F048C0587E3 Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx08.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=sergiodj@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 6F048C0587E3 Received: from psique.yyz.redhat.com (unused-10-15-17-193.yyz.redhat.com [10.15.17.193]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1941F77ED2; Thu, 29 Jun 2017 19:41:16 +0000 (UTC) From: Sergio Durigan Junior To: GDB Patches Cc: Pedro Alves , Sergio Durigan Junior Subject: [PATCH] Implement the ability to transmit environment variables to GDBserver when starting the inferior Date: Thu, 29 Jun 2017 19:41:00 -0000 Message-Id: <20170629194106.23070-1-sergiodj@redhat.com> X-IsSubscribed: yes X-SW-Source: 2017-06/txt/msg00799.txt.bz2 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. 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