From 21d09ccf268dc071d452b0207a3dd91228a51da3 Mon Sep 17 00:00:00 2001 From: Tatyana Krasnukha Date: Thu, 13 Feb 2020 19:33:08 +0300 Subject: [PATCH] [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 --- lldb/tools/lldb-vscode/VSCode.cpp | 48 +++++++++++++++++++++ lldb/tools/lldb-vscode/VSCode.h | 20 +++++++++ lldb/tools/lldb-vscode/lldb-vscode.cpp | 78 +++++++++++----------------------- lldb/tools/lldb-vscode/package.json | 16 +++++++ 4 files changed, 108 insertions(+), 54 deletions(-) diff --git a/lldb/tools/lldb-vscode/VSCode.cpp b/lldb/tools/lldb-vscode/VSCode.cpp index 2f85627..ba9542a 100644 --- a/lldb/tools/lldb-vscode/VSCode.cpp +++ b/lldb/tools/lldb-vscode/VSCode.cpp @@ -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 diff --git a/lldb/tools/lldb-vscode/VSCode.h b/lldb/tools/lldb-vscode/VSCode.h index be8b228..5733073 100644 --- a/lldb/tools/lldb-vscode/VSCode.h +++ b/lldb/tools/lldb-vscode/VSCode.h @@ -63,6 +63,8 @@ typedef llvm::DenseMap SourceBreakpointMap; typedef llvm::StringMap 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; diff --git a/lldb/tools/lldb-vscode/lldb-vscode.cpp b/lldb/tools/lldb-vscode/lldb-vscode.cpp index e6b9340..29aa2f4 100644 --- a/lldb/tools/lldb-vscode/lldb-vscode.cpp +++ b/lldb/tools/lldb-vscode/lldb-vscode.cpp @@ -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,25 +503,13 @@ 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()) { - response["success"] = llvm::json::Value(false); - EmplaceSafeString(response, "message", std::string(error.GetCString())); - g_vsc.SendJSON(llvm::json::Value(std::move(response))); - return; - } + 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; } const bool detatchOnError = GetBoolean(arguments, "detachOnError", false); @@ -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"); diff --git a/lldb/tools/lldb-vscode/package.json b/lldb/tools/lldb-vscode/package.json index 1cec0e6..dc27ab5 100644 --- a/lldb/tools/lldb-vscode/package.json +++ b/lldb/tools/lldb-vscode/package.json @@ -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.", -- 2.7.4