diff --git a/lldb/tools/lldb-vscode/VSCode.h b/lldb/tools/lldb-vscode/VSCode.h --- a/lldb/tools/lldb-vscode/VSCode.h +++ b/lldb/tools/lldb-vscode/VSCode.h @@ -63,6 +63,8 @@ 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 @@ 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/VSCode.cpp b/lldb/tools/lldb-vscode/VSCode.cpp --- a/lldb/tools/lldb-vscode/VSCode.cpp +++ b/lldb/tools/lldb-vscode/VSCode.cpp @@ -303,4 +303,52 @@ 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/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 @@ -69,8 +69,6 @@ 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 @@ // 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 @@ 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 @@ 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 @@ 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 --- 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.",