This revision was automatically updated to reflect the committed changes.
Closed by commit rG2b6c5bb995be: [lldb-vscode] Implement RestartRequest 
(authored by jgorbe).

Changed prior to commit:
  https://reviews.llvm.org/D147831?vs=512305&id=518866#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D147831/new/

https://reviews.llvm.org/D147831

Files:
  lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
  lldb/source/API/SBProcess.cpp
  lldb/test/API/tools/lldb-vscode/restart/Makefile
  lldb/test/API/tools/lldb-vscode/restart/TestVSCode_restart.py
  lldb/test/API/tools/lldb-vscode/restart/TestVSCode_restart_runInTerminal.py
  lldb/test/API/tools/lldb-vscode/restart/main.c
  lldb/tools/lldb-vscode/VSCode.cpp
  lldb/tools/lldb-vscode/VSCode.h
  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
@@ -458,7 +458,8 @@
             // manually send a stopped event in request_configurationDone(...)
             // so don't send any before then.
             if (g_vsc.configuration_done_sent) {
-              // Only report a stopped event if the process was not restarted.
+              // Only report a stopped event if the process was not
+              // automatically restarted.
               if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
                 SendStdOutStdErr(process);
                 SendThreadStoppedEvent();
@@ -468,14 +469,22 @@
           case lldb::eStateRunning:
             g_vsc.WillContinue();
             break;
-          case lldb::eStateExited: {
-            // Run any exit LLDB commands the user specified in the
-            // launch.json
-            g_vsc.RunExitCommands();
-            SendProcessExitedEvent(process);
-            SendTerminatedEvent();
-            done = true;
-          } break;
+          case lldb::eStateExited:
+            // When restarting, we can get an "exited" event for the process we
+            // just killed with the old PID, or even with no PID. In that case
+            // we don't have to terminate the session.
+            if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID ||
+                process.GetProcessID() == g_vsc.restarting_process_id) {
+              g_vsc.restarting_process_id = LLDB_INVALID_PROCESS_ID;
+            } else {
+              // Run any exit LLDB commands the user specified in the
+              // launch.json
+              g_vsc.RunExitCommands();
+              SendProcessExitedEvent(process);
+              SendTerminatedEvent();
+              done = true;
+            }
+            break;
           }
         } else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) ||
                    (event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) {
@@ -592,6 +601,7 @@
 // }
 void request_attach(const llvm::json::Object &request) {
   g_vsc.is_attach = true;
+  g_vsc.last_launch_or_attach_request = request;
   llvm::json::Object response;
   lldb::SBError error;
   FillResponse(request, response);
@@ -1527,7 +1537,7 @@
   // The debug adapter supports the RestartRequest. In this case a client
   // should not implement 'restart' by terminating and relaunching the adapter
   // but by calling the RestartRequest.
-  body.try_emplace("supportsRestartRequest", false);
+  body.try_emplace("supportsRestartRequest", true);
   // The debug adapter supports 'exceptionOptions' on the
   // setExceptionBreakpoints request.
   body.try_emplace("supportsExceptionOptions", true);
@@ -1622,6 +1632,71 @@
                                  error.GetCString());
 }
 
+// Takes a LaunchRequest object and launches the process, also handling
+// runInTerminal if applicable. It doesn't do any of the additional
+// initialization and bookkeeping stuff that is needed for `request_launch`.
+// This way we can reuse the process launching logic for RestartRequest too.
+lldb::SBError LaunchProcess(const llvm::json::Object &request) {
+  lldb::SBError error;
+  auto arguments = request.getObject("arguments");
+  auto launchCommands = GetStrings(arguments, "launchCommands");
+
+  // 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 detachOnError = GetBoolean(arguments, "detachOnError", false);
+  launch_info.SetDetachOnError(detachOnError);
+  launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug |
+                             lldb::eLaunchFlagStopAtEntry);
+  const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30);
+
+  if (GetBoolean(arguments, "runInTerminal", false)) {
+    if (llvm::Error err = request_runInTerminal(request))
+      error.SetErrorString(llvm::toString(std::move(err)).c_str());
+  } else 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();
+    // Make sure the process is launched and stopped at the entry point before
+    // proceeding as the the launch commands are not run using the synchronous
+    // mode.
+    error = g_vsc.WaitForProcessToStop(timeout_seconds);
+  }
+  return error;
+}
+
 // "LaunchRequest": {
 //   "allOf": [ { "$ref": "#/definitions/Request" }, {
 //     "type": "object",
@@ -1658,8 +1733,8 @@
 // }
 void request_launch(const llvm::json::Object &request) {
   g_vsc.is_attach = false;
+  g_vsc.last_launch_or_attach_request = request;
   llvm::json::Object response;
-  lldb::SBError error;
   FillResponse(request, response);
   auto arguments = request.getObject("arguments");
   g_vsc.init_commands = GetStrings(arguments, "initCommands");
@@ -1667,12 +1742,10 @@
   g_vsc.stop_commands = GetStrings(arguments, "stopCommands");
   g_vsc.exit_commands = GetStrings(arguments, "exitCommands");
   g_vsc.terminate_commands = GetStrings(arguments, "terminateCommands");
-  auto launchCommands = GetStrings(arguments, "launchCommands");
   std::vector<std::string> postRunCommands =
       GetStrings(arguments, "postRunCommands");
   g_vsc.stop_at_entry = GetBoolean(arguments, "stopOnEntry", false);
   const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot");
-  const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30);
 
   // This is a hack for loading DWARF in .o files on Mac where the .o files
   // in the debug map of the main executable have relative paths which require
@@ -1697,76 +1770,27 @@
     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 (GetBoolean(arguments, "runInTerminal", false)) {
-    if (llvm::Error err = request_runInTerminal(request))
-      error.SetErrorString(llvm::toString(std::move(err)).c_str());
-  } else 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();
-    // Make sure the process is launched and stopped at the entry point before
-    // proceeding as the the launch commands are not run using the synchronous
-    // mode.
-    error = g_vsc.WaitForProcessToStop(timeout_seconds);
-  }
+  status = LaunchProcess(request);
 
-  if (error.Fail()) {
+  if (status.Fail()) {
     response["success"] = llvm::json::Value(false);
-    EmplaceSafeString(response, "message", std::string(error.GetCString()));
+    EmplaceSafeString(response, "message", std::string(status.GetCString()));
   } else {
     g_vsc.RunLLDBCommands("Running postRunCommands:", postRunCommands);
   }
 
   g_vsc.SendJSON(llvm::json::Value(std::move(response)));
 
-  if (g_vsc.is_attach)
-    SendProcessEvent(Attach); // this happens when doing runInTerminal
-  else
-    SendProcessEvent(Launch);
-  g_vsc.SendJSON(llvm::json::Value(CreateEventObject("initialized")));
+  if (!status.Fail()) {
+    if (g_vsc.is_attach)
+      SendProcessEvent(Attach);  // this happens when doing runInTerminal
+    else
+      SendProcessEvent(Launch);
+  }
+  g_vsc.SendJSON(CreateEventObject("initialized"));
 }
 
 // "NextRequest": {
@@ -1867,6 +1891,118 @@
   g_vsc.SendJSON(llvm::json::Value(std::move(response)));
 }
 
+
+// "RestartRequest": {
+//   "allOf": [ { "$ref": "#/definitions/Request" }, {
+//     "type": "object",
+//     "description": "Restarts a debug session. Clients should only call this
+//     request if the corresponding capability `supportsRestartRequest` is
+//     true.\nIf the capability is missing or has the value false, a typical
+//     client emulates `restart` by terminating the debug adapter first and then
+//     launching it anew.",
+//     "properties": {
+//       "command": {
+//         "type": "string",
+//         "enum": [ "restart" ]
+//       },
+//       "arguments": {
+//         "$ref": "#/definitions/RestartArguments"
+//       }
+//     },
+//     "required": [ "command" ]
+//   }]
+// },
+// "RestartArguments": {
+//   "type": "object",
+//   "description": "Arguments for `restart` request.",
+//   "properties": {
+//     "arguments": {
+//       "oneOf": [
+//         { "$ref": "#/definitions/LaunchRequestArguments" },
+//         { "$ref": "#/definitions/AttachRequestArguments" }
+//       ],
+//       "description": "The latest version of the `launch` or `attach`
+//       configuration."
+//     }
+//   }
+// },
+// "RestartResponse": {
+//   "allOf": [ { "$ref": "#/definitions/Response" }, {
+//     "type": "object",
+//     "description": "Response to `restart` request. This is just an
+//     acknowledgement, so no body field is required."
+//   }]
+// },
+void request_restart(const llvm::json::Object &request) {
+  llvm::json::Object response;
+  FillResponse(request, response);
+  if (!g_vsc.last_launch_or_attach_request) {
+    response["success"] = llvm::json::Value(false);
+    EmplaceSafeString(response, "message",
+                      "Restart request received but no process was launched.");
+    g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+  // Check if we were in a "launch" session or an "attach" session.
+  //
+  // Restarting is not well defined when we started the session by attaching to
+  // an existing process, because we don't know how the process was started, so
+  // we don't support it.
+  //
+  // Note that when using runInTerminal we're technically attached, but it's an
+  // implementation detail. The adapter *did* launch the process in response to
+  // a "launch" command, so we can still stop it and re-run it. This is why we
+  // don't just check `g_vsc.is_attach`.
+  if (GetString(*g_vsc.last_launch_or_attach_request, "command") == "attach") {
+    response["success"] = llvm::json::Value(false);
+    EmplaceSafeString(response, "message",
+                      "Restarting an \"attach\" session is not supported.");
+    g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+    return;
+  }
+
+  // The optional `arguments` field in RestartRequest can contain an updated
+  // version of the launch arguments. If there's one, use it.
+  auto request_arguments = request.getObject("arguments");
+  if (request_arguments) {
+    llvm::json::Object arguments = *request_arguments;
+    (*g_vsc.last_launch_or_attach_request)["arguments"] =
+        llvm::json::Value(std::move(arguments));
+  }
+
+  // Keep track of the old PID so when we get a "process exited" event from the
+  // killed process we can detect it and not shut down the whole session.
+  lldb::SBProcess process = g_vsc.target.GetProcess();
+  g_vsc.restarting_process_id = process.GetProcessID();
+
+  // Stop the current process if necessary. The logic here is similar to
+  // CommandObjectProcessLaunchOrAttach::StopProcessIfNecessary, except that
+  // we don't ask the user for confirmation.
+  g_vsc.debugger.SetAsync(false);
+  if (process.IsValid()) {
+    lldb::StateType state = process.GetState();
+    if (state != lldb::eStateConnected) {
+      process.Kill();
+    }
+    // Clear the list of thread ids to avoid sending "thread exited" events
+    // for threads of the process we are terminating.
+    g_vsc.thread_ids.clear();
+  }
+  g_vsc.debugger.SetAsync(true);
+  LaunchProcess(*g_vsc.last_launch_or_attach_request);
+
+  // This is normally done after receiving a "configuration done" request.
+  // Because we're restarting, configuration has already happened so we can
+  // continue the process right away.
+  if (g_vsc.stop_at_entry) {
+    SendThreadStoppedEvent();
+  } else {
+    g_vsc.target.GetProcess().Continue();
+  }
+
+  g_vsc.SendJSON(llvm::json::Value(std::move(response)));
+}
+
 // "ScopesRequest": {
 //   "allOf": [ { "$ref": "#/definitions/Request" }, {
 //     "type": "object",
@@ -3084,6 +3220,7 @@
   g_vsc.RegisterRequestCallback("launch", request_launch);
   g_vsc.RegisterRequestCallback("next", request_next);
   g_vsc.RegisterRequestCallback("pause", request_pause);
+  g_vsc.RegisterRequestCallback("restart", request_restart);
   g_vsc.RegisterRequestCallback("scopes", request_scopes);
   g_vsc.RegisterRequestCallback("setBreakpoints", request_setBreakpoints);
   g_vsc.RegisterRequestCallback("setExceptionBreakpoints",
Index: lldb/tools/lldb-vscode/VSCode.h
===================================================================
--- lldb/tools/lldb-vscode/VSCode.h
+++ lldb/tools/lldb-vscode/VSCode.h
@@ -15,6 +15,7 @@
 #include <cstdio>
 #include <iosfwd>
 #include <map>
+#include <optional>
 #include <set>
 #include <thread>
 
@@ -141,10 +142,17 @@
   std::vector<std::string> exit_commands;
   std::vector<std::string> stop_commands;
   std::vector<std::string> terminate_commands;
+  // A copy of the last LaunchRequest or AttachRequest so we can reuse its
+  // arguments if we get a RestartRequest.
+  std::optional<llvm::json::Object> last_launch_or_attach_request;
   lldb::tid_t focus_tid;
   bool sent_terminated_event;
   bool stop_at_entry;
   bool is_attach;
+  // The process event thread normally responds to process exited events by
+  // shutting down the entire adapter. When we're restarting, we keep the id of
+  // the old process here so we can detect this case and keep running.
+  lldb::pid_t restarting_process_id;
   bool configuration_done_sent;
   uint32_t reverse_request_seq;
   std::map<std::string, RequestCallback> request_handlers;
Index: lldb/tools/lldb-vscode/VSCode.cpp
===================================================================
--- lldb/tools/lldb-vscode/VSCode.cpp
+++ lldb/tools/lldb-vscode/VSCode.cpp
@@ -39,8 +39,10 @@
            {"swift_catch", "Swift Catch", lldb::eLanguageTypeSwift},
            {"swift_throw", "Swift Throw", lldb::eLanguageTypeSwift}}),
       focus_tid(LLDB_INVALID_THREAD_ID), sent_terminated_event(false),
-      stop_at_entry(false), is_attach(false), configuration_done_sent(false),
-      reverse_request_seq(0), waiting_for_run_in_terminal(false),
+      stop_at_entry(false), is_attach(false),
+      restarting_process_id(LLDB_INVALID_PROCESS_ID),
+      configuration_done_sent(false), reverse_request_seq(0),
+      waiting_for_run_in_terminal(false),
       progress_event_reporter(
           [&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }) {
   const char *log_file_path = getenv("LLDBVSCODE_LOG");
Index: lldb/test/API/tools/lldb-vscode/restart/main.c
===================================================================
--- /dev/null
+++ lldb/test/API/tools/lldb-vscode/restart/main.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int main(int argc, char const *argv[], char const *envp[]) {
+  int i = 0;
+  printf("Do something\n"); // breakpoint A
+  printf("Do something else\n");
+  i = 1234;
+  return 0;  // breakpoint B
+}
Index: lldb/test/API/tools/lldb-vscode/restart/TestVSCode_restart_runInTerminal.py
===================================================================
--- /dev/null
+++ lldb/test/API/tools/lldb-vscode/restart/TestVSCode_restart_runInTerminal.py
@@ -0,0 +1,104 @@
+"""
+Test lldb-vscode RestartRequest.
+"""
+
+import os
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import line_number
+import lldbvscode_testcase
+
+
+class TestVSCode_restart_runInTerminal(lldbvscode_testcase.VSCodeTestCaseBase):
+
+    def isTestSupported(self):
+        try:
+            # We skip this test for debug builds because it takes too long
+            # parsing lldb's own debug info. Release builds are fine.
+            # Checking the size of the lldb-vscode binary seems to be a decent
+            # proxy for a quick detection. It should be far less than 1 MB in
+            # Release builds.
+            return os.path.getsize(os.environ["LLDBVSCODE_EXEC"]) < 1000000
+        except:
+            return False
+
+
+    @skipIfWindows
+    @skipIfRemote
+    def test_basic_functionality(self):
+        '''
+            Test basic restarting functionality when the process is running in
+            a terminal.
+        '''
+        if not self.isTestSupported():
+            return
+        line_A = line_number('main.c', '// breakpoint A')
+        line_B = line_number('main.c', '// breakpoint B')
+
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(program, runInTerminal=True)
+        [bp_A, bp_B] = self.set_source_breakpoints('main.c', [line_A, line_B])
+
+        # Verify we hit A, then B.
+        self.vscode.request_configurationDone()
+        self.verify_breakpoint_hit([bp_A])
+        self.vscode.request_continue()
+        self.verify_breakpoint_hit([bp_B])
+
+        # Make sure i has been modified from its initial value of 0.
+        self.assertEquals(int(self.vscode.get_local_variable_value('i')),
+                          1234, 'i != 1234 after hitting breakpoint B')
+
+        # Restart.
+        self.vscode.request_restart()
+
+        # Finally, check we stop back at A and program state has been reset.
+        self.verify_breakpoint_hit([bp_A])
+        self.assertEquals(int(self.vscode.get_local_variable_value('i')),
+                          0, 'i != 0 after hitting breakpoint A on restart')
+
+    @skipIfWindows
+    @skipIfRemote
+    def test_stopOnEntry(self):
+        '''
+            Check that stopOnEntry works correctly when using runInTerminal.
+        '''
+        if not self.isTestSupported():
+            return
+        line_A = line_number('main.c', '// breakpoint A')
+        line_B = line_number('main.c', '// breakpoint B')
+
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(program, runInTerminal=True, stopOnEntry=True)
+        [bp_main] = self.set_function_breakpoints(['main'])
+        self.vscode.request_configurationDone()
+
+        # When using stopOnEntry, configurationDone doesn't result in a running
+        # process, we should immediately get a stopped event instead.
+        stopped_events = self.vscode.wait_for_stopped()
+        # We should be stopped at the entry point.
+        for stopped_event in stopped_events:
+            if 'body' in stopped_event:
+                body = stopped_event['body']
+                if 'reason' in body:
+                    reason = body['reason']
+                    self.assertNotEqual(
+                        reason, 'breakpoint',
+                        'verify stop isn\'t a breakpoint')
+
+        # Then, if we continue, we should hit the breakpoint at main.
+        self.vscode.request_continue()
+        self.verify_breakpoint_hit([bp_main])
+
+        # Restart and check that we still get a stopped event before reaching
+        # main.
+        self.vscode.request_restart()
+        stopped_events = self.vscode.wait_for_stopped()
+        for stopped_event in stopped_events:
+            if 'body' in stopped_event:
+                body = stopped_event['body']
+                if 'reason' in body:
+                    reason = body['reason']
+                    self.assertNotEqual(
+                        reason, 'breakpoint',
+                        'verify stop after restart isn\'t "main" breakpoint')
+
Index: lldb/test/API/tools/lldb-vscode/restart/TestVSCode_restart.py
===================================================================
--- /dev/null
+++ lldb/test/API/tools/lldb-vscode/restart/TestVSCode_restart.py
@@ -0,0 +1,82 @@
+"""
+Test lldb-vscode RestartRequest.
+"""
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import line_number
+import lldbvscode_testcase
+
+
+class TestVSCode_restart(lldbvscode_testcase.VSCodeTestCaseBase):
+
+    @skipIfWindows
+    @skipIfRemote
+    def test_basic_functionality(self):
+        '''
+            Tests the basic restarting functionality: set two breakpoints in
+            sequence, restart at the second, check that we hit the first one.
+        '''
+        line_A = line_number('main.c', '// breakpoint A')
+        line_B = line_number('main.c', '// breakpoint B')
+
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(program)
+        [bp_A, bp_B] = self.set_source_breakpoints('main.c', [line_A, line_B])
+
+        # Verify we hit A, then B.
+        self.vscode.request_configurationDone()
+        self.verify_breakpoint_hit([bp_A])
+        self.vscode.request_continue()
+        self.verify_breakpoint_hit([bp_B])
+
+        # Make sure i has been modified from its initial value of 0.
+        self.assertEquals(int(self.vscode.get_local_variable_value('i')),
+                          1234, 'i != 1234 after hitting breakpoint B')
+
+        # Restart then check we stop back at A and program state has been reset.
+        self.vscode.request_restart()
+        self.verify_breakpoint_hit([bp_A])
+        self.assertEquals(int(self.vscode.get_local_variable_value('i')),
+                          0, 'i != 0 after hitting breakpoint A on restart')
+
+
+    @skipIfWindows
+    @skipIfRemote
+    def test_stopOnEntry(self):
+        '''
+            Check that the stopOnEntry setting is still honored after a restart.
+        '''
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(program, stopOnEntry=True)
+        [bp_main] = self.set_function_breakpoints(['main'])
+        self.vscode.request_configurationDone()
+
+        # Once the "configuration done" event is sent, we should get a stopped
+        # event immediately because of stopOnEntry.
+        stopped_events = self.vscode.wait_for_stopped()
+        for stopped_event in stopped_events:
+            if 'body' in stopped_event:
+                body = stopped_event['body']
+                if 'reason' in body:
+                    reason = body['reason']
+                    self.assertNotEqual(
+                        reason, 'breakpoint',
+                        'verify stop isn\'t "main" breakpoint')
+
+        # Then, if we continue, we should hit the breakpoint at main.
+        self.vscode.request_continue()
+        self.verify_breakpoint_hit([bp_main])
+
+        # Restart and check that we still get a stopped event before reaching
+        # main.
+        self.vscode.request_restart()
+        stopped_events = self.vscode.wait_for_stopped()
+        for stopped_event in stopped_events:
+            if 'body' in stopped_event:
+                body = stopped_event['body']
+                if 'reason' in body:
+                    reason = body['reason']
+                    self.assertNotEqual(
+                        reason, 'breakpoint',
+                        'verify stop after restart isn\'t "main" breakpoint')
+
Index: lldb/test/API/tools/lldb-vscode/restart/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/tools/lldb-vscode/restart/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
Index: lldb/source/API/SBProcess.cpp
===================================================================
--- lldb/source/API/SBProcess.cpp
+++ lldb/source/API/SBProcess.cpp
@@ -779,8 +779,8 @@
 bool SBProcess::EventIsProcessEvent(const SBEvent &event) {
   LLDB_INSTRUMENT_VA(event);
 
-  return (event.GetBroadcasterClass() == SBProcess::GetBroadcasterClass()) &&
-         !EventIsStructuredDataEvent(event);
+  return Process::ProcessEventData::GetEventDataFromEvent(event.get()) !=
+         nullptr;
 }
 
 bool SBProcess::EventIsStructuredDataEvent(const lldb::SBEvent &event) {
Index: lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
+++ lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py
@@ -588,6 +588,15 @@
         # Caller must still call wait_for_stopped.
         return response
 
+    def request_restart(self):
+        command_dict = {
+            'command': 'restart',
+            'type': 'request',
+        }
+        response = self.send_recv(command_dict)
+        # Caller must still call wait_for_stopped.
+        return response
+
     def request_disconnect(self, terminateDebuggee=None):
         args_dict = {}
         if terminateDebuggee is not None:
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to