diff --git a/lldb/tools/lldb-vscode/VSCode.h b/lldb/tools/lldb-vscode/VSCode.h --- a/lldb/tools/lldb-vscode/VSCode.h +++ b/lldb/tools/lldb-vscode/VSCode.h @@ -130,6 +130,7 @@ lldb::SBBroadcaster broadcaster; std::thread event_thread; std::thread progress_event_thread; + std::thread command_interpreter_thread; std::unique_ptr log; llvm::DenseMap addr_to_source_ref; llvm::DenseMap source_map; 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 @@ -16,6 +16,7 @@ #include #include #include +#include #if defined(_WIN32) // We need to #define NOMINMAX in order to skip `min()` and `max()` macro // definitions that conflict with other system headers. @@ -552,6 +553,34 @@ } } +static void reset_stdin_termios(); +static bool g_old_stdin_termios_is_valid = false; +static struct termios g_old_stdin_termios; + +// In the Driver::MainLoop, we change the terminal settings. This function is +// added as an atexit handler to make sure we clean them up. +static void reset_stdin_termios() { + if (g_old_stdin_termios_is_valid) { + g_old_stdin_termios_is_valid = false; + ::tcsetattr(STDIN_FILENO, TCSANOW, &g_old_stdin_termios); + } +} + +void CommandInterpreterThreadFunction() { + if (::tcgetattr(STDIN_FILENO, &g_old_stdin_termios) == 0) { + g_old_stdin_termios_is_valid = true; + atexit(reset_stdin_termios); + } +#ifndef _MSC_VER + // Disabling stdin buffering with MSVC's 2015 CRT exposes a bug in fgets + // which causes it to miss newlines depending on whether there have been an + // odd or even number of characters. Bug has been reported to MS via Connect. + ::setbuf(stdin, nullptr); +#endif + ::setbuf(stdout, nullptr); + g_vsc.debugger.RunCommandInterpreter(false, false); +} + // "AttachRequest": { // "allOf": [ { "$ref": "#/definitions/Request" }, { // "type": "object", @@ -878,6 +907,9 @@ g_vsc.broadcaster.BroadcastEventByType(eBroadcastBitStopProgressThread); g_vsc.progress_event_thread.join(); } + + if (g_vsc.command_interpreter_thread.joinable()) + g_vsc.event_thread.join(); } void request_exceptionInfo(const llvm::json::Object &request) { @@ -1415,6 +1447,11 @@ // "supportsRunInTerminalRequest": { // "type": "boolean", // "description": "Client supports the runInTerminal request." +// }, +// "supportsCommandInterpreter": { +// "type": "boolean", +// "description": "Client launches lldb-vscode in a terminal and thus +// supports attaching the CommandInterpreter." // } // }, // "required": [ "adapterID" ] @@ -1439,6 +1476,38 @@ // before we are given an executable to launch in a "launch" request, or a // executable when attaching to a process by process ID in a "attach" // request. + // + // Start our event thread so we can receive events from the debugger, target, + // process and more. + g_vsc.event_thread = std::thread(EventThreadFunction); + + // Set the output and error file handles to redirect into nothing iff the + // input and output are set to stdin and stdout. If we are receiving DAP + // packets over a socket then keep them up. Otherwise if any code in LLDB + // prints to the debugger file handles, the output and error file handles are + // initialized to STDOUT and STDERR and any output will kill our debug + // session. + if (!g_vsc.input.descriptor.m_is_socket) { + FILE *out = llvm::sys::RetryAfterSignal(nullptr, fopen, dev_null_path, "w"); + if (out) { + g_vsc.debugger.SetOutputFileHandle(out, true); + g_vsc.debugger.SetErrorFileHandle(out, false); + } + } + + // We can't attach the command interpreter if the DAP packets are being sent + // via stdin/stdout. We would require extra sockets. So only attach the CI iff + // we are communicating via a socket. + if (g_vsc.input.descriptor.m_is_socket) { + auto arguments = request.getObject("arguments"); + auto supports_ci = + GetBoolean(arguments, "supportsCommandInterpreter", false); + if (supports_ci) { + g_vsc.command_interpreter_thread = + std::thread(CommandInterpreterThreadFunction); + } + } + FILE *out = llvm::sys::RetryAfterSignal(nullptr, fopen, dev_null_path, "w"); if (out) { // Set the output and error file handles to redirect into nothing otherwise @@ -1449,9 +1518,6 @@ g_vsc.debugger.SetErrorFileHandle(out, false); } - // Start our event thread so we can receive events from the debugger, target, - // process and more. - g_vsc.event_thread = std::thread(EventThreadFunction); llvm::json::Object response; FillResponse(request, response);