diff --git a/lldb/bindings/interface/SBLaunchInfo.i b/lldb/bindings/interface/SBLaunchInfo.i --- a/lldb/bindings/interface/SBLaunchInfo.i +++ b/lldb/bindings/interface/SBLaunchInfo.i @@ -62,7 +62,10 @@ GetEnvironmentEntryAtIndex (uint32_t idx); void - SetEnvironmentEntries (const char **envp, bool append); + SetEnvironmentEntries (const char * const*envp, bool append); + + void + SetEnvironment (const SBEnvironment &env, bool append); void Clear (); diff --git a/lldb/include/lldb/API/SBEnvironment.h b/lldb/include/lldb/API/SBEnvironment.h --- a/lldb/include/lldb/API/SBEnvironment.h +++ b/lldb/include/lldb/API/SBEnvironment.h @@ -55,9 +55,12 @@ protected: friend class SBPlatform; friend class SBTarget; + friend class SBLaunchInfo; SBEnvironment(const lldb_private::Environment &rhs); + lldb_private::Environment &GetInnerEnvironment() const; + private: std::unique_ptr m_opaque_up; }; diff --git a/lldb/include/lldb/API/SBLaunchInfo.h b/lldb/include/lldb/API/SBLaunchInfo.h --- a/lldb/include/lldb/API/SBLaunchInfo.h +++ b/lldb/include/lldb/API/SBLaunchInfo.h @@ -94,7 +94,12 @@ const char *GetEnvironmentEntryAtIndex(uint32_t idx); - void SetEnvironmentEntries(const char **envp, bool append); + void SetEnvironmentEntries(const char *const *envp, bool append); + + /// Updates the SBLaunchInfo's environment with the given environment. + /// If append is false, the provided environment will completely replace + /// the existing environment. + void SetEnvironment(const SBEnvironment &env, bool append); void Clear(); diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/lldbvscode_testcase.py @@ -267,7 +267,7 @@ disableSTDIO=False, shellExpandArguments=False, trace=False, initCommands=None, preRunCommands=None, stopCommands=None, exitCommands=None,sourcePath= None, - debuggerRoot=None, launchCommands=None): + debuggerRoot=None, launchCommands=None, inheritEnvironment=False): '''Sending launch request to vscode ''' @@ -298,7 +298,8 @@ exitCommands=exitCommands, sourcePath=sourcePath, debuggerRoot=debuggerRoot, - launchCommands=launchCommands) + launchCommands=launchCommands, + inheritEnvironment=inheritEnvironment) if not (response and response['success']): self.assertTrue(response['success'], 'launch failed (%s)' % (response['message'])) @@ -308,7 +309,7 @@ disableSTDIO=False, shellExpandArguments=False, trace=False, initCommands=None, preRunCommands=None, stopCommands=None, exitCommands=None, - sourcePath=None, debuggerRoot=None): + sourcePath=None, debuggerRoot=None, inheritEnvironment=False): '''Build the default Makefile target, create the VSCode debug adaptor, and launch the process. ''' @@ -318,4 +319,4 @@ self.launch(program, args, cwd, env, stopOnEntry, disableASLR, disableSTDIO, shellExpandArguments, trace, initCommands, preRunCommands, stopCommands, exitCommands, - sourcePath, debuggerRoot) + sourcePath, debuggerRoot, inheritEnvironment=inheritEnvironment) diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py @@ -570,7 +570,7 @@ disableSTDIO=False, shellExpandArguments=False, trace=False, initCommands=None, preRunCommands=None, stopCommands=None, exitCommands=None, sourcePath=None, - debuggerRoot=None, launchCommands=None): + debuggerRoot=None, launchCommands=None, inheritEnvironment=False): args_dict = { 'program': program } @@ -605,6 +605,8 @@ args_dict['debuggerRoot'] = debuggerRoot if launchCommands: args_dict['launchCommands'] = launchCommands + if inheritEnvironment: + args_dict['inheritEnvironment'] = inheritEnvironment command_dict = { 'command': 'launch', 'type': 'request', @@ -912,7 +914,8 @@ initCommands=options.initCmds, preRunCommands=options.preRunCmds, stopCommands=options.stopCmds, - exitCommands=options.exitCmds) + exitCommands=options.exitCmds, + inheritEnvironment=options.inheritEnvironment) if response['success']: if options.sourceBreakpoints: diff --git a/lldb/source/API/SBEnvironment.cpp b/lldb/source/API/SBEnvironment.cpp --- a/lldb/source/API/SBEnvironment.cpp +++ b/lldb/source/API/SBEnvironment.cpp @@ -89,6 +89,8 @@ return LLDB_RECORD_RESULT(m_opaque_up->erase(name)); } +Environment &SBEnvironment::GetInnerEnvironment() const { return *m_opaque_up; } + namespace lldb_private { namespace repro { diff --git a/lldb/source/API/SBLaunchInfo.cpp b/lldb/source/API/SBLaunchInfo.cpp --- a/lldb/source/API/SBLaunchInfo.cpp +++ b/lldb/source/API/SBLaunchInfo.cpp @@ -9,6 +9,7 @@ #include "lldb/API/SBLaunchInfo.h" #include "SBReproducerPrivate.h" +#include "lldb/API/SBEnvironment.h" #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBListener.h" #include "lldb/Host/ProcessLaunchInfo.h" @@ -179,9 +180,9 @@ return m_opaque_sp->GetEnvp()[idx]; } -void SBLaunchInfo::SetEnvironmentEntries(const char **envp, bool append) { +void SBLaunchInfo::SetEnvironmentEntries(const char *const *envp, bool append) { LLDB_RECORD_METHOD(void, SBLaunchInfo, SetEnvironmentEntries, - (const char **, bool), envp, append); + (const char *const *, bool), envp, append); Environment env(envp); if (append) @@ -191,6 +192,12 @@ m_opaque_sp->RegenerateEnvp(); } +void SBLaunchInfo::SetEnvironment(const SBEnvironment &env, bool append) { + LLDB_RECORD_METHOD(void, SBLaunchInfo, SetEnvironment, + (const lldb::SBEnvironment &, bool), env, append) + SetEnvironmentEntries(env.GetInnerEnvironment().getEnvp().get(), append); +} + void SBLaunchInfo::Clear() { LLDB_RECORD_METHOD_NO_ARGS(void, SBLaunchInfo, Clear); @@ -360,7 +367,9 @@ LLDB_REGISTER_METHOD(const char *, SBLaunchInfo, GetEnvironmentEntryAtIndex, (uint32_t)); LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetEnvironmentEntries, - (const char **, bool)); + (const char *const *, bool)); + LLDB_REGISTER_METHOD(void, SBLaunchInfo, SetEnvironment, + (const lldb::SBEnvironment &, bool)); LLDB_REGISTER_METHOD(void, SBLaunchInfo, Clear, ()); LLDB_REGISTER_METHOD_CONST(const char *, SBLaunchInfo, GetWorkingDirectory, ()); diff --git a/lldb/test/API/tools/lldb-vscode/completions/main.cpp b/lldb/test/API/tools/lldb-vscode/completions/main.cpp --- a/lldb/test/API/tools/lldb-vscode/completions/main.cpp +++ b/lldb/test/API/tools/lldb-vscode/completions/main.cpp @@ -27,4 +27,4 @@ bar* bar2 = &bar1; foo foo1 = {3,&bar1, bar1, NULL}; return 0; // breakpoint 2 -} +} \ No newline at end of file diff --git a/lldb/test/API/tools/lldb-vscode/environmentVariables/Makefile b/lldb/test/API/tools/lldb-vscode/environmentVariables/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/environmentVariables/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/tools/lldb-vscode/environmentVariables/TestVSCode_environmentVariables.py b/lldb/test/API/tools/lldb-vscode/environmentVariables/TestVSCode_environmentVariables.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/environmentVariables/TestVSCode_environmentVariables.py @@ -0,0 +1,72 @@ +""" +Test lldb-vscode environment variables +""" + + +import lldbvscode_testcase +import unittest2 +import vscode +import os +from lldbsuite.test import lldbutil +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * + +class TestVSCode_variables(lldbvscode_testcase.VSCodeTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + def getEnvOutputByProgram(self): + env = {} + for line in self.get_stdout().encode('utf-8').splitlines(): + (name, value) = line.split("=") + env[name] = value + return env + + + @skipIfWindows + @skipIfRemote + def test_empty_environment(self): + """ + Tests running a process with an empty environment + """ + program = self.getBuildArtifact("a.out") + self.build_and_launch(program) + self.continue_to_exit() + env = self.getEnvOutputByProgram() + + self.assertTrue("PATH" not in env) + + @skipIfWindows + @skipIfRemote + def test_inheriting_environment(self): + """ + Tests running a process inheriting the environment + """ + program = self.getBuildArtifact("a.out") + self.build_and_launch(program, inheritEnvironment=True, env=["FOO=bar"]) + self.continue_to_exit() + env = self.getEnvOutputByProgram() + + self.assertEqual(env["PATH"], os.environ['PATH']) + self.assertEqual(env["FOO"], "bar") + + + @skipIfWindows + @skipIfRemote + def test_override_when_inheriting_environment(self): + """ + Tests the environment variables priority. + The launch.json's environment has precedence. + """ + program = self.getBuildArtifact("a.out") + new_path_value = "#" + os.environ["PATH"] + + self.build_and_launch( + program, + inheritEnvironment=True, + env=["PATH=" + new_path_value]) + self.continue_to_exit() + env = self.getEnvOutputByProgram() + + self.assertEqual(env["PATH"], new_path_value) + diff --git a/lldb/test/API/tools/lldb-vscode/environmentVariables/main.cpp b/lldb/test/API/tools/lldb-vscode/environmentVariables/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-vscode/environmentVariables/main.cpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +extern char **environ; + +int main(int argc, char const *argv[], char const *envp[]) { + char **env_var_pointer = environ; + for (char *env_variable = *env_var_pointer; env_variable; + env_variable = *++env_var_pointer) { + printf("%s\n", env_variable); + } + return 0; +} diff --git a/lldb/tools/lldb-vscode/lldb-vscode.cpp b/lldb/tools/lldb-vscode/lldb-vscode.cpp --- a/lldb/tools/lldb-vscode/lldb-vscode.cpp +++ b/lldb/tools/lldb-vscode/lldb-vscode.cpp @@ -40,6 +40,7 @@ #include #include +#include "lldb/API/SBEnvironment.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" @@ -1358,6 +1359,8 @@ auto launchCommands = GetStrings(arguments, "launchCommands"); g_vsc.stop_at_entry = GetBoolean(arguments, "stopOnEntry", false); const auto debuggerRoot = GetString(arguments, "debuggerRoot"); + bool launchWithDebuggerEnvironment = + GetBoolean(arguments, "inheritEnvironment", false); // This is a hack for loading DWARF in .o files on Mac where the .o files // in the debug map of the main executable have relative paths which require @@ -1374,6 +1377,12 @@ // the targets - preRunCommands are run with the target. g_vsc.RunInitCommands(); + // Configure the inherit environment setting + std::ostringstream oss; + oss << "settings set target.inherit-env " + << (launchWithDebuggerEnvironment ? "true" : "false"); + g_vsc.RunLLDBCommands(llvm::StringRef(), {oss.str()}); + lldb::SBError status; g_vsc.SetTarget(g_vsc.CreateTargetFromArguments(*arguments, status)); if (status.Fail()) { @@ -1398,10 +1407,13 @@ if (!args.empty()) g_vsc.launch_info.SetArguments(MakeArgv(args).data(), true); - // Pass any environment variables along that the user specified. - auto envs = GetStrings(arguments, "env"); - if (!envs.empty()) - g_vsc.launch_info.SetEnvironmentEntries(MakeArgv(envs).data(), true); + // This mimics what CommandObjectProcess does when launching a process + lldb::SBEnvironment env = g_vsc.target.GetEnvironment(); + for (const auto &name_and_value : GetStrings(arguments, "env")) { + auto kv = llvm::StringRef(name_and_value).split("="); + env.Set(kv.first.str().c_str(), kv.second.str().c_str(), true); + } + g_vsc.launch_info.SetEnvironment(env, true); auto flags = g_vsc.launch_info.GetLaunchFlags(); diff --git a/lldb/tools/lldb-vscode/package.json b/lldb/tools/lldb-vscode/package.json --- a/lldb/tools/lldb-vscode/package.json +++ b/lldb/tools/lldb-vscode/package.json @@ -84,6 +84,11 @@ "description": "Additional environment variables.", "default": [] }, + "inheritEnvironment": { + "type": "boolean", + "description": "Inherit the debugger environment when launching a process. Only works for binaries launched directly by LLDB.", + "default": false + }, "stopOnEntry": { "type": "boolean", "description": "Automatically stop after launch.",