[lldb-vscode] Add inheritEnvironment option

Summary:
If no custom launching is used, lldb-vscode launches a program with an empty environment by default. In some scenarios, the user might want to simply use the same environment as the IDE to have a set of working environment variables (e.g. PATH wouldn't be empty). In fact, most DAPs in VSCode have this behavior by default. In other cases the user definitely needs to set their custom environment, which is already supported. To make the first case easier for the user (e.g. not having to copy the PATH to the launch.json every time they want to debug simple programs that rely on PATH), a new option is now offered. inheritEnvironment will launch the program copying its own environment, and it's just a boolean flag.

{F11347695}

Reviewers: clayborg, aadsm, diazhector98, kusmour

Subscribers: labath, JDevlieghere, lldb-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D74636
This commit is contained in:
Hector Diaz 2020-03-19 14:08:40 -07:00 committed by Walter Erquinigo
parent a8c22ba416
commit 4ec6ebabfc
7 changed files with 156 additions and 10 deletions

View File

@ -267,7 +267,7 @@ class VSCodeTestCaseBase(TestBase):
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 @@ class VSCodeTestCaseBase(TestBase):
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 @@ class VSCodeTestCaseBase(TestBase):
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 @@ class VSCodeTestCaseBase(TestBase):
self.launch(program, args, cwd, env, stopOnEntry, disableASLR,
disableSTDIO, shellExpandArguments, trace,
initCommands, preRunCommands, stopCommands, exitCommands,
sourcePath, debuggerRoot)
sourcePath, debuggerRoot, inheritEnvironment=inheritEnvironment)

View File

@ -570,7 +570,7 @@ class DebugCommunication(object):
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 @@ class DebugCommunication(object):
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 @@ def run_vscode(dbg, args, options):
initCommands=options.initCmds,
preRunCommands=options.preRunCmds,
stopCommands=options.stopCmds,
exitCommands=options.exitCmds)
exitCommands=options.exitCmds,
inheritEnvironment=options.inheritEnvironment)
if response['success']:
if options.sourceBreakpoints:

View File

@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp
include Makefile.rules

View File

@ -0,0 +1,107 @@
"""
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.assertNotIn("PATH", 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)
@skipIfWindows
@skipIfRemote
def test_empty_environment_custom_launcher(self):
"""
Tests running a process with an empty environment from a custom
launcher
"""
program = self.getBuildArtifact("a.out")
self.build_and_create_debug_adaptor()
launchCommands = [
'target create "%s"' % (program),
"run"
]
self.launch(launchCommands=launchCommands)
self.continue_to_exit()
env = self.getEnvOutputByProgram()
self.assertNotIn("PATH", env)
@skipIfWindows
@skipIfRemote
def test_inheriting_environment_custom_launcher(self):
"""
Tests running a process from a custom launcher inheriting the
environment
"""
program = self.getBuildArtifact("a.out")
self.build_and_create_debug_adaptor()
launchCommands = [
'target create "%s"' % (program),
"run"
]
self.launch(launchCommands=launchCommands, inheritEnvironment=True)
self.continue_to_exit()
env = self.getEnvOutputByProgram()
self.assertIn("PATH", env)

View File

@ -0,0 +1,15 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
extern char **environ;
int main(int argc, char const *argv[]) {
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;
}

View File

@ -40,6 +40,7 @@
#include <sstream>
#include <thread>
#include "lldb/API/SBEnvironment.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
@ -1355,6 +1356,8 @@ void request_launch(const llvm::json::Object &request) {
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
@ -1371,6 +1374,13 @@ void request_launch(const llvm::json::Object &request) {
// the targets - preRunCommands are run with the target.
g_vsc.RunInitCommands();
// Reset the default value of the inherit environment setting, so that the
// environment of programs launched by launchCommands is consistent with the
// "inheritEnvironment" argument.
if (!launchWithDebuggerEnvironment)
g_vsc.RunLLDBCommands(llvm::StringRef(),
{"settings set target.inherit-env false"});
lldb::SBError status;
g_vsc.SetTarget(g_vsc.CreateTargetFromArguments(*arguments, status));
if (status.Fail()) {
@ -1395,10 +1405,12 @@ void request_launch(const llvm::json::Object &request) {
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"))
env.PutEntry(name_and_value.c_str());
g_vsc.launch_info.SetEnvironment(env, true);
auto flags = g_vsc.launch_info.GetLaunchFlags();

View File

@ -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.",