aelitashen created this revision.
aelitashen added reviewers: wallace, clayborg.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.
aelitashen requested review of this revision.
Herald added a subscriber: JDevlieghere.

When turn on "Launch In Terminal" in IDE, the debugee will be launched inside 
the integrated terminal so that user can input values for debugging. The 
current version adds a response handler for the runInTerminal response from IDE 
to lldb. lldb will attach the program after the response is received.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D84974

Files:
  lldb/tools/lldb-vscode/lldb-vscode.cpp

Index: lldb/tools/lldb-vscode/lldb-vscode.cpp
===================================================================
--- lldb/tools/lldb-vscode/lldb-vscode.cpp
+++ 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<std::string> commands = GetStrings(arguments, "args");
+    commands.insert(commands.begin(), std::string(GetString(arguments, "program").data()));
+    runInTerminalArgs.try_emplace("args", commands);
+    std::vector<std::string> 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<std::string, ResponseCallback> &GetResponseHandlers() {
+#define RESPONSE_CALLBACK(name)                                                 \
+  { #name, response_##name }
+  static std::map<std::string, ResponseCallback> 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;
   }
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to