jgorbe updated this revision to Diff 512305.
jgorbe added a comment.
Addressed review comments.
- Changed `last_launch_or_attach_request` to be a
`std::optional<llvm::json::Object>` so we can check if it has been set. Also
check for `nullopt` in request_restart.
- Made `request_attach` also set `last_launch_or_attach_request`.
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,109 @@
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;
+ }
+
+ // 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 +3211,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
@@ -769,8 +769,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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits