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 @@ -98,6 +98,7 @@ }; typedef void (*RequestCallback)(const llvm::json::Object &command); +typedef void (*ResponseCallback)(const llvm::json::Object &command); enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch }; @@ -1357,6 +1358,8 @@ filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp)); } body.try_emplace("exceptionBreakpointFilters", std::move(filters)); + // The debug adapter supports launching a debugee in intergrated VScode terminal. + body.try_emplace("supportsRunInTerminalRequest", true); // The debug adapter supports stepping back via the stepBack and // reverseContinue requests. body.try_emplace("supportsStepBack", false); @@ -1488,66 +1491,113 @@ return; } - // Instantiate a launch info instance for the target. - auto 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()) - launch_info.SetWorkingDirectory(cwd.data()); - - // Extract any extra arguments and append them to our program arguments for - // when we launch - auto args = GetStrings(arguments, "args"); - if (!args.empty()) - 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); - - auto flags = launch_info.GetLaunchFlags(); - - if (GetBoolean(arguments, "disableASLR", true)) - flags |= lldb::eLaunchFlagDisableASLR; - if (GetBoolean(arguments, "disableSTDIO", false)) - flags |= lldb::eLaunchFlagDisableSTDIO; - if (GetBoolean(arguments, "shellExpandArguments", false)) - flags |= lldb::eLaunchFlagShellExpandArguments; - const bool detatchOnError = GetBoolean(arguments, "detachOnError", false); - launch_info.SetDetachOnError(detatchOnError); - launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug | - lldb::eLaunchFlagStopAtEntry); - - // Run any pre run LLDB commands the user specified in the launch.json - g_vsc.RunPreRunCommands(); - if (launchCommands.empty()) { - // Disable async events so the launch will be successful when we return from - // the launch call and the launch will happen synchronously - g_vsc.debugger.SetAsync(false); - g_vsc.target.Launch(launch_info, error); - g_vsc.debugger.SetAsync(true); + if (GetBoolean(arguments, "launchInTerminal", false)) { + llvm::json::Object reverseRequest; + reverseRequest.try_emplace("type", "request"); + reverseRequest.try_emplace("command", "runInTerminal"); + reverseRequest.try_emplace("seq", 100); + llvm::json::Object runInTerminalArgs; + runInTerminalArgs.try_emplace("kind", "integrated"); + runInTerminalArgs.try_emplace("cwd", GetString(arguments, "cwd")); + std::vector commands = GetStrings(arguments, "args"); + commands.insert(commands.begin(), std::string(GetString(arguments, "program").data())); + runInTerminalArgs.try_emplace("args", commands); + std::vector envVars = GetStrings(arguments, "env"); + llvm::json::Object environment; + for(std::string envVar : envVars) { + size_t ind = envVar.find("="); + environment.try_emplace(envVar.substr(0, ind), envVar.substr(ind+1)); + } + runInTerminalArgs.try_emplace("env", llvm::json::Value(std::move(environment))); + reverseRequest.try_emplace("arguments", llvm::json::Value(std::move(runInTerminalArgs))); + g_vsc.SendJSON(llvm::json::Value(std::move(reverseRequest))); + // g_vsc.SendJSON(llvm::json::Value(std::move(response))); } else { - g_vsc.RunLLDBCommands("Running launchCommands:", launchCommands); - // The custom commands might have created a new target so we should use the - // selected target after these commands are run. - g_vsc.target = g_vsc.debugger.GetSelectedTarget(); + // Instantiate a launch info instance for the target. + auto 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()) + launch_info.SetWorkingDirectory(cwd.data()); + + // Extract any extra arguments and append them to our program arguments for + // when we launch + auto args = GetStrings(arguments, "args"); + if (!args.empty()) + 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); + + auto flags = launch_info.GetLaunchFlags(); + + if (GetBoolean(arguments, "disableASLR", true)) + flags |= lldb::eLaunchFlagDisableASLR; + if (GetBoolean(arguments, "disableSTDIO", false)) + flags |= lldb::eLaunchFlagDisableSTDIO; + if (GetBoolean(arguments, "shellExpandArguments", false)) + flags |= lldb::eLaunchFlagShellExpandArguments; + const bool detatchOnError = GetBoolean(arguments, "detachOnError", false); + launch_info.SetDetachOnError(detatchOnError); + launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug | + lldb::eLaunchFlagStopAtEntry); + + // Run any pre run LLDB commands the user specified in the launch.json + g_vsc.RunPreRunCommands(); + if (launchCommands.empty()) { + // Disable async events so the launch will be successful when we return from + // the launch call and the launch will happen synchronously + g_vsc.debugger.SetAsync(false); + g_vsc.target.Launch(launch_info, error); + g_vsc.debugger.SetAsync(true); + } else { + g_vsc.RunLLDBCommands("Running launchCommands:", launchCommands); + // The custom commands might have created a new target so we should use the + // selected target after these commands are run. + g_vsc.target = g_vsc.debugger.GetSelectedTarget(); + } + + 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))); + SendProcessEvent(Launch); + g_vsc.SendJSON(llvm::json::Value(CreateEventObject("initialized"))); } + // Reenable async events and start the event thread to catch async events. + // g_vsc.debugger.SetAsync(true); +} +void response_runInTerminal(const llvm::json::Object &reverseRequest) { + g_vsc.is_attach = true; + llvm::json::Object response; + lldb::SBError error; + FillResponse(reverseRequest, response); + lldb::SBAttachInfo attach_info; + g_vsc.target.Attach(attach_info, error); + if (error.Success()) { + auto attached_pid = g_vsc.target.GetProcess().GetProcessID(); + if (attached_pid == LLDB_INVALID_PROCESS_ID) { + error.SetErrorString("Failed to attach to a process"); + } + } 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))); - - SendProcessEvent(Launch); - g_vsc.SendJSON(llvm::json::Value(CreateEventObject("initialized"))); - // Reenable async events and start the event thread to catch async events. - // g_vsc.debugger.SetAsync(true); + if (error.Success()) { + SendProcessEvent(Attach); + g_vsc.SendJSON(CreateEventObject("initialized")); + // SendThreadStoppedEvent(); + } } - // "NextRequest": { // "allOf": [ { "$ref": "#/definitions/Request" }, { // "type": "object", @@ -2849,6 +2899,17 @@ return g_request_handlers; } +const std::map &GetResponseHandlers() { +#define RESPONSE_CALLBACK(name) \ + { #name, response_##name } + static std::map g_response_handlers = { + // VSCode Debug Adaptor response to reverse request + RESPONSE_CALLBACK(runInTerminal), + }; +#undef RESPONSE_CALLBACK + return g_response_handlers; +} + } // anonymous namespace static void printHelp(LLDBVSCodeOptTable &table, llvm::StringRef tool_name) { @@ -2961,6 +3022,17 @@ *g_vsc.log << "error: unhandled command \"" << command.data() << std::endl; return 1; } + } else if (packet_type == "response") { + auto response_handlers = GetResponseHandlers(); + const auto command = GetString(object, "command"); + auto handler_pos = response_handlers.find("runInTerminal"); + if (handler_pos != response_handlers.end()) { + handler_pos->second(*object); + } else { + if (g_vsc.log) + *g_vsc.log << "error: unhandled command \"" << command.data() << std::endl; + return 1; + } } ++packet_idx; }