def attach(self, program=None, pid=None, waitFor=None, trace=None,
initCommands=None, preRunCommands=None, stopCommands=None,
- exitCommands=None, attachCommands=None, coreFile=None):
+ exitCommands=None, attachCommands=None, coreFile=None, disconnectAutomatically=True):
'''Build the default Makefile target, create the VSCode debug adaptor,
and attach to the process.
'''
# Make sure we disconnect and terminate the VSCode debug adaptor even
# if we throw an exception during the test case.
def cleanup():
- self.vscode.request_disconnect(terminateDebuggee=True)
+ if disconnectAutomatically:
+ self.vscode.request_disconnect(terminateDebuggee=True)
self.vscode.terminate()
# Execute the cleanup function during test case tear down.
--- /dev/null
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
--- /dev/null
+"""
+Test lldb-vscode disconnect request
+"""
+
+
+import unittest2
+import vscode
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+import lldbvscode_testcase
+import subprocess
+import time
+import os
+
+
+class TestVSCode_launch(lldbvscode_testcase.VSCodeTestCaseBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+ source = 'main.cpp'
+
+ def disconnect_and_assert_no_output_printed(self):
+ self.vscode.request_disconnect()
+ # verify we didn't get any input after disconnect
+ time.sleep(2)
+ output = self.get_stdout()
+ self.assertTrue(output is None or len(output) == 0)
+
+ @skipIfWindows
+ @skipIfRemote
+ def test_launch(self):
+ """
+ This test launches a process that would creates a file, but we disconnect
+ before the file is created, which terminates the process and thus the file is not
+ created.
+ """
+ program = self.getBuildArtifact("a.out")
+ self.build_and_launch(program)
+
+ # We set a breakpoint right before the side effect file is created
+ self.set_source_breakpoints(self.source, [line_number(self.source, '// breakpoint')])
+ self.continue_to_next_stop()
+
+ self.vscode.request_disconnect()
+ # verify we didn't produce the side effect file
+ time.sleep(1)
+ self.assertFalse(os.path.exists(program + ".side_effect"))
+
+
+ @skipIfWindows
+ @skipIfRemote
+ def test_attach(self):
+ """
+ This test attaches to a process that creates a file. We attach and disconnect
+ before the file is created, and as the process is not terminated upon disconnection,
+ the file is created anyway.
+ """
+ self.build_and_create_debug_adaptor()
+ program = self.getBuildArtifact("a.out")
+
+ # Use a file as a synchronization point between test and inferior.
+ sync_file_path = lldbutil.append_to_process_working_directory(self,
+ "sync_file_%d" % (int(time.time())))
+ self.addTearDownHook(
+ lambda: self.run_platform_command(
+ "rm %s" %
+ (sync_file_path)))
+
+ self.process = subprocess.Popen([program, sync_file_path])
+ lldbutil.wait_for_file_on_target(self, sync_file_path)
+
+ self.attach(pid=self.process.pid, disconnectAutomatically=False)
+ response = self.vscode.request_evaluate("wait_for_attach = false;")
+ self.assertTrue(response['success'])
+
+ # verify we haven't produced the side effect file yet
+ self.assertFalse(os.path.exists(program + ".side_effect"))
+
+ self.vscode.request_disconnect()
+ time.sleep(2)
+ # verify we produced the side effect file, as the program continued after disconnecting
+ self.assertTrue(os.path.exists(program + ".side_effect"))
--- /dev/null
+#include <chrono>
+#include <cstdio>
+#include <fstream>
+#include <string>
+#include <thread>
+
+volatile bool wait_for_attach = true;
+
+void handle_attach(char *sync_file_path) {
+ lldb_enable_attach();
+
+ {
+ // Create a file to signal that this process has started up.
+ std::ofstream sync_file;
+ sync_file.open(sync_file_path);
+ }
+
+ while (wait_for_attach)
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+}
+
+int main(int argc, char **args) {
+ if (argc == 2)
+ handle_attach(args[1]);
+
+ // We let the binary live a little bit to see if it executed after detaching
+ // from // breakpoint
+
+ // Create a file to signal that this process has started up.
+ std::ofstream out_file; // breakpoint
+ out_file.open(std::string(args[0]) + ".side_effect");
+ return 0;
+}
{"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) {
+ stop_at_entry(false), is_attach(false) {
const char *log_file_path = getenv("LLDBVSCODE_LOG");
#if defined(_WIN32)
// Windows opens stdout and stdin in text mode which converts \n to 13,10
lldb::tid_t focus_tid;
bool sent_terminated_event;
bool stop_at_entry;
+ bool is_attach;
// Keep track of the last stop thread index IDs as threads won't go away
// unless we send a "thread" event to indicate the thread exited.
llvm::DenseSet<lldb::tid_t> thread_ids;
// }]
// }
void request_attach(const llvm::json::Object &request) {
+ g_vsc.is_attach = true;
llvm::json::Object response;
lldb::SBError error;
FillResponse(request, response);
FillResponse(request, response);
auto arguments = request.getObject("arguments");
- bool terminateDebuggee = GetBoolean(arguments, "terminateDebuggee", false);
+ bool defaultTerminateDebuggee = g_vsc.is_attach ? false : true;
+ bool terminateDebuggee =
+ GetBoolean(arguments, "terminateDebuggee", defaultTerminateDebuggee);
lldb::SBProcess process = g_vsc.target.GetProcess();
auto state = process.GetState();
case lldb::eStateStopped:
case lldb::eStateRunning:
g_vsc.debugger.SetAsync(false);
- if (terminateDebuggee)
- process.Kill();
- else
- process.Detach();
+ lldb::SBError error = terminateDebuggee ? process.Kill() : process.Detach();
+ if (!error.Success())
+ response.try_emplace("error", error.GetCString());
g_vsc.debugger.SetAsync(true);
break;
}
// }]
// }
void request_launch(const llvm::json::Object &request) {
+ g_vsc.is_attach = false;
llvm::json::Object response;
lldb::SBError error;
FillResponse(request, response);