[lldb-vscode] Ensure that target matches the executable file

This commit fixes an issue with lldb-vscode failing to run programs that
use different architecture/platform than the "empty" in the target.
Original implementation was creating a default target without specifying
the target architecture, platform or program, and then would set
executable file through SBLaunchInfo, assuming that this would update
architecture and platform accordingly. However this wasn't really
happening, and architecture and platform would remain at whatever values
were in the "empty" target. The simple solution is to create target
already for a desired architecture and platform.

Function request_attach is updated in a similar fashion.

This commit also adds new JSON properties to "launch" and "attach"
packets to allow user to override desired platform and architecture.
This might be especially important for cases where information in ELF is
not enough to derive those values correctly.

New code has a behavior similar to LLDB MI [1], where typically IDE would
specify target file with -file-exec-and-symbols, and then only do -exec-run
command that would launch the process. In lldb-vscode those two actions are
merged into one request_launch function. Similarly in the interpreter
session, user would first do "file" command, then "process launch"

Differential Revision: https://reviews.llvm.org/D70847
Signed-off-by: Anton Kolesov <anton.kolesov@synopsys.com>
This commit is contained in:
Tatyana Krasnukha 2020-02-13 19:33:08 +03:00
parent 9cb227f561
commit 21d09ccf26
4 changed files with 108 additions and 54 deletions

View File

@ -303,4 +303,52 @@ void VSCode::RunExitCommands() {
RunLLDBCommands("Running exitCommands:", exit_commands);
}
lldb::SBTarget VSCode::CreateTargetFromArguments(
const llvm::json::Object &arguments,
lldb::SBError &error) {
// Grab the name of the program we need to debug and create a target using
// the given program as an argument. Executable file can be a source of target
// architecture and platform, if they differ from the host. Setting exe path
// in launch info is useless because Target.Launch() will not change
// architecture and platform, therefore they should be known at the target
// creation. We also use target triple and platform from the launch
// configuration, if given, since in some cases ELF file doesn't contain
// enough information to determine correct arch and platform (or ELF can be
// omitted at all), so it is good to leave the user an apportunity to specify
// those. Any of those three can be left empty.
llvm::StringRef target_triple = GetString(arguments, "targetTriple");
llvm::StringRef platform_name = GetString(arguments, "platformName");
llvm::StringRef program = GetString(arguments, "program");
auto target = this->debugger.CreateTarget(
program.data(),
target_triple.data(),
platform_name.data(),
true, // Add dependent modules.
error
);
if (error.Fail()) {
// Update message if there was an error.
error.SetErrorStringWithFormat(
"Could not create a target for a program '%s': %s.",
program.data(), error.GetCString());
}
return target;
}
void VSCode::SetTarget(const lldb::SBTarget target) {
this->target = target;
if (target.IsValid()) {
// Configure breakpoint event listeners for the target.
lldb::SBListener listener = this->debugger.GetListener();
listener.StartListeningForEvents(
this->target.GetBroadcaster(),
lldb::SBTarget::eBroadcastBitBreakpointChanged);
listener.StartListeningForEvents(this->broadcaster,
eBroadcastBitStopEventThread);
}
}
} // namespace lldb_vscode

View File

@ -63,6 +63,8 @@ typedef llvm::DenseMap<uint32_t, SourceBreakpoint> SourceBreakpointMap;
typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap;
enum class OutputType { Console, Stdout, Stderr, Telemetry };
enum VSCodeBroadcasterBits { eBroadcastBitStopEventThread = 1u << 0 };
struct VSCode {
InputStream input;
OutputStream output;
@ -132,6 +134,24 @@ struct VSCode {
void RunPreRunCommands();
void RunStopCommands();
void RunExitCommands();
/// Create a new SBTarget object from the given request arguments.
/// \param[in] arguments
/// Launch configuration arguments.
///
/// \param[out] error
/// An SBError object that will contain an error description if
/// function failed to create the target.
///
/// \return
/// An SBTarget object.
lldb::SBTarget CreateTargetFromArguments(
const llvm::json::Object &arguments,
lldb::SBError &error);
/// Set given target object as a current target for lldb-vscode and start
/// listeing for its breakpoint events.
void SetTarget(const lldb::SBTarget target);
};
extern VSCode g_vsc;

View File

@ -69,8 +69,6 @@ typedef void (*RequestCallback)(const llvm::json::Object &command);
enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
enum VSCodeBroadcasterBits { eBroadcastBitStopEventThread = 1u << 0 };
SOCKET AcceptConnection(int portno) {
// Accept a socket connection from any host on "portno".
SOCKET newsockfd = -1;
@ -505,26 +503,14 @@ void request_attach(const llvm::json::Object &request) {
// Run any initialize LLDB commands the user specified in the launch.json
g_vsc.RunInitCommands();
// Grab the name of the program we need to debug and set it as the first
// argument that will be passed to the program we will debug.
const auto program = GetString(arguments, "program");
if (!program.empty()) {
lldb::SBFileSpec program_fspec(program.data(), true /*resolve_path*/);
g_vsc.launch_info.SetExecutableFile(program_fspec,
false /*add_as_first_arg*/);
const char *target_triple = nullptr;
const char *uuid_cstr = nullptr;
// Stand alone debug info file if different from executable
const char *symfile = nullptr;
g_vsc.target.AddModule(program.data(), target_triple, uuid_cstr, symfile);
if (error.Fail()) {
lldb::SBError status;
g_vsc.SetTarget(g_vsc.CreateTargetFromArguments(*arguments, status));
if (status.Fail()) {
response["success"] = llvm::json::Value(false);
EmplaceSafeString(response, "message", std::string(error.GetCString()));
EmplaceSafeString(response, "message", status.GetCString());
g_vsc.SendJSON(llvm::json::Value(std::move(response)));
return;
}
}
const bool detatchOnError = GetBoolean(arguments, "detachOnError", false);
g_vsc.launch_info.SetDetachOnError(detatchOnError);
@ -536,7 +522,8 @@ void request_attach(const llvm::json::Object &request) {
char attach_info[256];
auto attach_info_len =
snprintf(attach_info, sizeof(attach_info),
"Waiting to attach to \"%s\"...", program.data());
"Waiting to attach to \"%s\"...",
g_vsc.target.GetExecutable().GetFilename());
g_vsc.SendOutput(OutputType::Console, llvm::StringRef(attach_info,
attach_info_len));
}
@ -1210,13 +1197,6 @@ void request_initialize(const llvm::json::Object &request) {
g_vsc.debugger.SetErrorFileHandle(out, false);
}
g_vsc.target = g_vsc.debugger.CreateTarget(nullptr);
lldb::SBListener listener = g_vsc.debugger.GetListener();
listener.StartListeningForEvents(
g_vsc.target.GetBroadcaster(),
lldb::SBTarget::eBroadcastBitBreakpointChanged);
listener.StartListeningForEvents(g_vsc.broadcaster,
eBroadcastBitStopEventThread);
// Start our event thread so we can receive events from the debugger, target,
// process and more.
g_vsc.event_thread = std::thread(EventThreadFunction);
@ -1358,39 +1338,29 @@ void request_launch(const llvm::json::Object &request) {
SetSourceMapFromArguments(*arguments);
// Run any initialize LLDB commands the user specified in the launch.json
// Run any initialize LLDB commands the user specified in the launch.json.
// This is run before target is created, so commands can't do anything with
// the targets - preRunCommands are run with the target.
g_vsc.RunInitCommands();
lldb::SBError status;
g_vsc.SetTarget(g_vsc.CreateTargetFromArguments(*arguments, status));
if (status.Fail()) {
response["success"] = llvm::json::Value(false);
EmplaceSafeString(response, "message", status.GetCString());
g_vsc.SendJSON(llvm::json::Value(std::move(response)));
return;
}
// Instantiate a launch info instance for the target.
g_vsc.launch_info = g_vsc.target.GetLaunchInfo();
// Grab the current working directory if there is one and set it in the
// launch info.
const auto cwd = GetString(arguments, "cwd");
if (!cwd.empty())
g_vsc.launch_info.SetWorkingDirectory(cwd.data());
// Grab the name of the program we need to debug and set it as the first
// argument that will be passed to the program we will debug.
llvm::StringRef program = GetString(arguments, "program");
if (!program.empty()) {
lldb::SBFileSpec program_fspec(program.data(), true /*resolve_path*/);
g_vsc.launch_info.SetExecutableFile(program_fspec,
true /*add_as_first_arg*/);
const char *target_triple = nullptr;
const char *uuid_cstr = nullptr;
// Stand alone debug info file if different from executable
const char *symfile = nullptr;
lldb::SBModule module = g_vsc.target.AddModule(
program.data(), target_triple, uuid_cstr, symfile);
if (!module.IsValid()) {
response["success"] = llvm::json::Value(false);
EmplaceSafeString(
response, "message",
llvm::formatv("Could not load program '{0}'.", program).str());
g_vsc.SendJSON(llvm::json::Value(std::move(response)));
return;
}
}
// Extract any extra arguments and append them to our program arguments for
// when we launch
auto args = GetStrings(arguments, "args");

View File

@ -122,6 +122,14 @@
"type": "string",
"description": "Specify a working directory to set the debug adaptor to so relative object files can be located."
},
"targetTriple": {
"type": "string",
"description": "Triplet of the target architecture to override value derived from the program file."
},
"platformName": {
"type": "string",
"description": "Name of the execution platform to override value derived from the program file."
},
"initCommands": {
"type": "array",
"description": "Initialization commands executed upon debugger startup.",
@ -175,6 +183,14 @@
"type": "string",
"description": "Specify a working directory to set the debug adaptor to so relative object files can be located."
},
"targetTriple": {
"type": "string",
"description": "Triplet of the target architecture to override value derived from the program file."
},
"platformName": {
"type": "string",
"description": "Name of the execution platform to override value derived from the program file."
},
"attachCommands": {
"type": "array",
"description": "Custom commands that are executed instead of attaching to a process ID or to a process by name. These commands may optionally create a new target and must perform an attach. A valid process must exist after these commands complete or the \"attach\" will fail.",