Skip to content

Make env and source map dictionaries #95137 #106919

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion lldb/test/API/tools/lldb-dap/coreFile/TestDAP_coreFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def test_core_file(self):
self.assertEqual(self.get_stackFrames(), expected_frames)

@skipIfLLVMTargetMissing("X86")
def test_core_file_source_mapping(self):
def test_core_file_source_mapping_array(self):
"""Test that sourceMap property is correctly applied when loading a core"""
current_dir = os.path.dirname(__file__)
exe_file = os.path.join(current_dir, "linux-x86_64.out")
Expand All @@ -70,3 +70,17 @@ def test_core_file_source_mapping(self):
self.attach(exe_file, coreFile=core_file, sourceMap=source_map)

self.assertIn(current_dir, self.get_stackFrames()[0]["source"]["path"])

@skipIfLLVMTargetMissing("X86")
def test_core_file_source_mapping_object(self):
"""Test that sourceMap property is correctly applied when loading a core"""
current_dir = os.path.dirname(__file__)
exe_file = os.path.join(current_dir, "linux-x86_64.out")
core_file = os.path.join(current_dir, "linux-x86_64.core")

self.create_debug_adaptor()

source_map = {"/home/labath/test": current_dir}
self.attach(exe_file, coreFile=core_file, sourceMap=source_map)

self.assertIn(current_dir, self.get_stackFrames()[0]["source"]["path"])
37 changes: 36 additions & 1 deletion lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,47 @@ def test_args(self):
'arg[%i] "%s" not in "%s"' % (i + 1, quoted_arg, lines[i]),
)

def test_environment(self):
def test_environment_with_object(self):
"""
Tests launch of a simple program with environment variables
"""
program = self.getBuildArtifact("a.out")
env = {
"NO_VALUE": "",
"WITH_VALUE": "BAR",
"EMPTY_VALUE": "",
"SPACE": "Hello World",
}

self.build_and_launch(program, env=env)
self.continue_to_exit()

# Now get the STDOUT and verify our arguments got passed correctly
output = self.get_stdout()
self.assertTrue(output and len(output) > 0, "expect program output")
lines = output.splitlines()
# Skip the all arguments so we have only environment vars left
while len(lines) and lines[0].startswith("arg["):
lines.pop(0)
# Make sure each environment variable in "env" is actually set in the
# program environment that was printed to STDOUT
for var in env:
found = False
for program_var in lines:
if var in program_var:
found = True
break
self.assertTrue(
found, '"%s" must exist in program environment (%s)' % (var, lines)
)

def test_environment_with_array(self):
"""
Tests launch of a simple program with environment variables
"""
program = self.getBuildArtifact("a.out")
env = ["NO_VALUE", "WITH_VALUE=BAR", "EMPTY_VALUE=", "SPACE=Hello World"]

self.build_and_launch(program, env=env)
self.continue_to_exit()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,28 @@ def test_runInTerminal(self):
env = self.dap_server.request_evaluate("foo")["body"]["result"]
self.assertIn("bar", env)

def test_runInTerminalWithObjectEnv(self):
if not self.isTestSupported():
return
"""
Tests the "runInTerminal" reverse request. It makes sure that the IDE can
launch the inferior with the correct environment variables using an object.
"""
program = self.getBuildArtifact("a.out")
self.build_and_launch(program, runInTerminal=True, env={"FOO": "BAR"})

self.assertEqual(
len(self.dap_server.reverse_requests),
1,
"make sure we got a reverse request",
)

request = self.dap_server.reverse_requests[0]
request_envs = request["arguments"]["env"]

self.assertIn("FOO", request_envs)
self.assertEqual("BAR", request_envs["FOO"])

@skipIfWindows
@skipIf(archs=no_match(["x86_64"]))
def test_runInTerminalInvalidTarget(self):
Expand Down
47 changes: 39 additions & 8 deletions lldb/tools/lldb-dap/JSONUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,31 @@ std::vector<std::string> GetStrings(const llvm::json::Object *obj,
return strs;
}

std::unordered_map<std::string, std::string>
GetStringMap(const llvm::json::Object &obj, llvm::StringRef key) {
std::unordered_map<std::string, std::string> strs;
const auto *const json_object = obj.getObject(key);
if (!json_object)
return strs;

for (const auto &[key, value] : *json_object) {
switch (value.kind()) {
case llvm::json::Value::String:
strs.emplace(key.str(), value.getAsString()->str());
break;
case llvm::json::Value::Number:
case llvm::json::Value::Boolean:
strs.emplace(key.str(), llvm::to_string(value));
break;
case llvm::json::Value::Null:
case llvm::json::Value::Object:
case llvm::json::Value::Array:
break;
}
}
return strs;
}

static bool IsClassStructOrUnionType(lldb::SBType t) {
return (t.GetTypeClass() & (lldb::eTypeClassUnion | lldb::eTypeClassStruct |
lldb::eTypeClassArray)) != 0;
Expand Down Expand Up @@ -1370,16 +1395,22 @@ CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
if (!cwd.empty())
run_in_terminal_args.try_emplace("cwd", cwd);

// We need to convert the input list of environments variables into a
// dictionary
std::vector<std::string> envs = GetStrings(launch_request_arguments, "env");
llvm::json::Object environment;
for (const std::string &env : envs) {
size_t index = env.find('=');
environment.try_emplace(env.substr(0, index), env.substr(index + 1));
auto envs = GetEnvironmentFromArguments(*launch_request_arguments);
llvm::json::Object env_json;
for (size_t index = 0, env_count = envs.GetNumValues(); index < env_count;
index++) {
llvm::StringRef key = envs.GetNameAtIndex(index);
llvm::StringRef value = envs.GetValueAtIndex(index);

if (key.empty())
g_dap.SendOutput(OutputType::Stderr,
"empty environment variable for value: \"" +
value.str() + '\"');
else
env_json.try_emplace(key, value);
}
run_in_terminal_args.try_emplace("env",
llvm::json::Value(std::move(environment)));
llvm::json::Value(std::move(env_json)));

return run_in_terminal_args;
}
Expand Down
22 changes: 22 additions & 0 deletions lldb/tools/lldb-dap/JSONUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "llvm/Support/JSON.h"
#include <cstdint>
#include <optional>
#include <unordered_map>

namespace lldb_dap {

Expand Down Expand Up @@ -152,6 +153,27 @@ bool ObjectContainsKey(const llvm::json::Object &obj, llvm::StringRef key);
std::vector<std::string> GetStrings(const llvm::json::Object *obj,
llvm::StringRef key);

/// Extract an object of key value strings for the specified key from an object.
///
/// String values in the object will be extracted without any quotes
/// around them. Numbers and Booleans will be converted into
/// strings. Any NULL, array or objects values in the array will be
/// ignored.
///
/// \param[in] obj
/// A JSON object that we will attempt to extract the array from
///
/// \param[in] key
/// The key to use when extracting the value
///
/// \return
/// An object of key value strings for the specified \a key, or
/// \a fail_value if there is no key that matches or if the
/// value is not an object or key and values in the object are not
/// strings, numbers or booleans.
std::unordered_map<std::string, std::string>
GetStringMap(const llvm::json::Object &obj, llvm::StringRef key);

/// Fill a response object given the request object.
///
/// The \a response object will get its "type" set to "response",
Expand Down
25 changes: 25 additions & 0 deletions lldb/tools/lldb-dap/LLDBUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,29 @@ int64_t MakeDAPFrameID(lldb::SBFrame &frame) {
frame.GetFrameID();
}

lldb::SBEnvironment
GetEnvironmentFromArguments(const llvm::json::Object &arguments) {
lldb::SBEnvironment envs{};
constexpr llvm::StringRef env_key = "env";
const llvm::json::Value *raw_json_env = arguments.get(env_key);

if (!raw_json_env)
return envs;

if (raw_json_env->kind() == llvm::json::Value::Object) {
auto env_map = GetStringMap(arguments, env_key);
for (const auto &[key, value] : env_map)
envs.Set(key.c_str(), value.c_str(), true);

} else if (raw_json_env->kind() == llvm::json::Value::Array) {
const auto envs_strings = GetStrings(&arguments, env_key);
lldb::SBStringList entries{};
for (const auto &env : envs_strings)
entries.AppendString(env.c_str());

envs.SetEntries(entries, true);
}
return envs;
}

} // namespace lldb_dap
14 changes: 13 additions & 1 deletion lldb/tools/lldb-dap/LLDBUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
#define LLDB_TOOLS_LLDB_DAP_LLDBUTILS_H

#include "DAPForward.h"
#include "lldb/API/SBEnvironment.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/raw_ostream.h"
#include <string>
#include <vector>

namespace lldb_dap {

Expand Down Expand Up @@ -135,6 +136,17 @@ uint32_t GetLLDBThreadIndexID(uint64_t dap_frame_id);
/// The LLDB frame index ID.
uint32_t GetLLDBFrameID(uint64_t dap_frame_id);

/// Gets all the environment variables from the json object depending on if the
/// kind is an object or an array.
///
/// \param[in] arguments
/// The json object with the launch options
///
/// \return
/// The environment variables stored in the env key
lldb::SBEnvironment
GetEnvironmentFromArguments(const llvm::json::Object &arguments);

} // namespace lldb_dap

#endif
5 changes: 4 additions & 1 deletion lldb/tools/lldb-dap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ adds `FOO=1` and `bar` to the environment:
"name": "Debug",
"program": "/tmp/a.out",
"args": [ "one", "two", "three" ],
"env": [ "FOO=1", "BAR" ],
"env": {
"FOO": "1"
"BAR": ""
}
}
```

Expand Down
29 changes: 18 additions & 11 deletions lldb/tools/lldb-dap/lldb-dap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include <thread>
#include <vector>

#include "lldb/API/SBEnvironment.h"
#include "lldb/API/SBStream.h"
#include "lldb/Host/Config.h"
#include "llvm/ADT/ArrayRef.h"
Expand Down Expand Up @@ -605,25 +606,32 @@ void SetSourceMapFromArguments(const llvm::json::Object &arguments) {
std::string sourceMapCommand;
llvm::raw_string_ostream strm(sourceMapCommand);
strm << "settings set target.source-map ";
auto sourcePath = GetString(arguments, "sourcePath");
const auto sourcePath = GetString(arguments, "sourcePath");

// sourceMap is the new, more general form of sourcePath and overrides it.
auto sourceMap = arguments.getArray("sourceMap");
if (sourceMap) {
for (const auto &value : *sourceMap) {
auto mapping = value.getAsArray();
constexpr llvm::StringRef sourceMapKey = "sourceMap";

if (const auto *sourceMapArray = arguments.getArray(sourceMapKey)) {
for (const auto &value : *sourceMapArray) {
const auto *mapping = value.getAsArray();
if (mapping == nullptr || mapping->size() != 2 ||
(*mapping)[0].kind() != llvm::json::Value::String ||
(*mapping)[1].kind() != llvm::json::Value::String) {
g_dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
return;
}
auto mapFrom = GetAsString((*mapping)[0]);
auto mapTo = GetAsString((*mapping)[1]);
const auto mapFrom = GetAsString((*mapping)[0]);
const auto mapTo = GetAsString((*mapping)[1]);
strm << "\"" << mapFrom << "\" \"" << mapTo << "\" ";
}
} else if (const auto *sourceMapObj = arguments.getObject(sourceMapKey)) {
for (const auto &[key, value] : *sourceMapObj) {
if (value.kind() == llvm::json::Value::String) {
strm << "\"" << key.str() << "\" \"" << GetAsString(value) << "\" ";
}
}
} else {
if (ObjectContainsKey(arguments, "sourceMap")) {
if (ObjectContainsKey(arguments, sourceMapKey)) {
g_dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
return;
}
Expand Down Expand Up @@ -1831,9 +1839,8 @@ lldb::SBError LaunchProcess(const llvm::json::Object &request) {
launch_info.SetArguments(MakeArgv(args).data(), true);

// Pass any environment variables along that the user specified.
auto envs = GetStrings(arguments, "env");
if (!envs.empty())
launch_info.SetEnvironmentEntries(MakeArgv(envs).data(), true);
const auto envs = GetEnvironmentFromArguments(*arguments);
launch_info.SetEnvironment(envs, true);

auto flags = launch_info.GetLaunchFlags();

Expand Down
Loading
Loading