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 @@ -110,6 +110,16 @@ return newsockfd; } +void AddBreakpointListener(VSCode &vscode) { + // Configure breakpoint event listeners for the target. + lldb::SBListener listener = vscode.debugger.GetListener(); + listener.StartListeningForEvents( + vscode.target.GetBroadcaster(), + lldb::SBTarget::eBroadcastBitBreakpointChanged); + listener.StartListeningForEvents(vscode.broadcaster, + eBroadcastBitStopEventThread); +} + std::vector MakeArgv(const llvm::ArrayRef &strs) { // Create and return an array of "const char *", one for each C string in // "strs" and terminate the list with a NULL. This can be used for argument @@ -512,27 +522,45 @@ // 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; - } + // 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"); + lldb::SBError status; + g_vsc.target = g_vsc.debugger.CreateTarget( + program.data(), + target_triple.data(), + platform_name.data(), + true, // Add dependent modules. + status + ); + + if (!g_vsc.target.IsValid()) { + response["success"] = llvm::json::Value(false); + std::string errmsg; + if (status.Fail()) + errmsg = llvm::formatv( + "Could not create a target for a program '{0}': {1}.", + program, status.GetCString()); + else + errmsg = llvm::formatv("Could not create a target for a program '{0}'.", + program); + EmplaceSafeString(response, "message", errmsg); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + return; } + AddBreakpointListener(g_vsc); + const bool detatchOnError = GetBoolean(arguments, "detachOnError", false); g_vsc.launch_info.SetDetachOnError(detatchOnError); @@ -951,7 +979,7 @@ for (size_t i = 0; i < count; i++) { std::string match = matches.GetStringAtIndex(i); std::string description = descriptions.GetStringAtIndex(i); - + llvm::json::Object item; llvm::StringRef match_ref = match; @@ -1217,13 +1245,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); @@ -1261,7 +1282,7 @@ // The debug adapter supports the stepInTargetsRequest. body.try_emplace("supportsStepInTargetsRequest", false); // We need to improve the current implementation of completions in order to - // enable it again. For some context, this is how VSCode works: + // enable it again. For some context, this is how VSCode works: // - VSCode sends a completion request whenever chars are added, the user // triggers completion manually via CTRL-space or similar mechanisms, but // not when there's a deletion. Besides, VSCode doesn't let us know which @@ -1365,39 +1386,59 @@ 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(); + // 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"); + lldb::SBError status; + g_vsc.target = g_vsc.debugger.CreateTarget( + program.data(), + target_triple.data(), + platform_name.data(), + true, // Add dependent modules. + status + ); + + if (!g_vsc.target.IsValid()) { + response["success"] = llvm::json::Value(false); + std::string errmsg; + if (status.Fail()) + errmsg = llvm::formatv( + "Could not create a target for a program '{0}': {1}.", + program, status.GetCString()); + else + errmsg = llvm::formatv("Could not create a target for a program '{0}'.", + program); + EmplaceSafeString(response, "message", errmsg); + g_vsc.SendJSON(llvm::json::Value(std::move(response))); + return; + } + + AddBreakpointListener(g_vsc); + + // 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.",