[Lldb-commits] [lldb] d7e5a7d - [lldb-dap][windows] drain the ConPTY before attaching (#180578)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Feb 11 06:17:22 PST 2026
Author: Charles Zablit
Date: 2026-02-11T14:17:17Z
New Revision: d7e5a7dacdb7c17e48a89e6e4fb50ccaa2fd5c94
URL: https://github.com/llvm/llvm-project/commit/d7e5a7dacdb7c17e48a89e6e4fb50ccaa2fd5c94
DIFF: https://github.com/llvm/llvm-project/commit/d7e5a7dacdb7c17e48a89e6e4fb50ccaa2fd5c94.diff
LOG: [lldb-dap][windows] drain the ConPTY before attaching (#180578)
Add a step to drain the init sequences emitted by the ConPTY before
attaching it to the debuggee.
A ConPTY (PseudoConsole) emits init sequences which flush the screen and
contain the name of the program (ESC[2J for clear screen, ESC[H for
cursor home and more). It's not desirable to filter them out: if a
debuggee also emits them, lldb would filter that output as well. To work
around this, the ConPTY is drained by attaching a dummy process to it,
consuming the init sequences and then attaching the actual debuggee.
---------
Co-authored-by: Nerixyz <nero.9 at hotmail.de>
Added:
Modified:
lldb/include/lldb/Host/windows/PseudoConsole.h
lldb/source/Host/windows/PseudoConsole.cpp
Removed:
################################################################################
diff --git a/lldb/include/lldb/Host/windows/PseudoConsole.h b/lldb/include/lldb/Host/windows/PseudoConsole.h
index a831c6028b252..996faf434585d 100644
--- a/lldb/include/lldb/Host/windows/PseudoConsole.h
+++ b/lldb/include/lldb/Host/windows/PseudoConsole.h
@@ -61,6 +61,16 @@ class PseudoConsole {
/// invalid.
HANDLE GetSTDINHandle() const { return m_conpty_input; };
+ /// Drains initialization sequences from the ConPTY output pipe.
+ ///
+ /// When a process first attaches to a ConPTY, Windows emits VT100/ANSI escape
+ /// sequences (ESC[2J for clear screen, ESC[H for cursor home and more) as
+ /// part of the PseudoConsole initialization. To prevent these sequences from
+ /// appearing in the debugger output (and flushing lldb's shell for instance)
+ /// we launch a short-lived dummy process that triggers the initialization,
+ /// then drain all output before launching the actual debuggee.
+ llvm::Error DrainInitSequences();
+
protected:
HANDLE m_conpty_handle = ((HANDLE)(long long)-1);
HANDLE m_conpty_output = ((HANDLE)(long long)-1);
diff --git a/lldb/source/Host/windows/PseudoConsole.cpp b/lldb/source/Host/windows/PseudoConsole.cpp
index b3314fd58bbb8..413c8a3328445 100644
--- a/lldb/source/Host/windows/PseudoConsole.cpp
+++ b/lldb/source/Host/windows/PseudoConsole.cpp
@@ -11,6 +11,7 @@
#include <mutex>
#include "lldb/Host/windows/PipeWindows.h"
+#include "lldb/Host/windows/ProcessLauncherWindows.h"
#include "lldb/Host/windows/windows.h"
#include "lldb/Utility/LLDBLog.h"
@@ -118,6 +119,12 @@ llvm::Error PseudoConsole::OpenPseudoConsole() {
m_conpty_output = hOutputRead;
m_conpty_input = hInputWrite;
+ if (auto error = DrainInitSequences()) {
+ Log *log = GetLog(LLDBLog::Host);
+ LLDB_LOG_ERROR(log, std::move(error),
+ "failed to finalize ConPTY's setup: {0}");
+ }
+
return llvm::Error::success();
}
@@ -133,3 +140,62 @@ void PseudoConsole::Close() {
m_conpty_input = INVALID_HANDLE_VALUE;
m_conpty_output = INVALID_HANDLE_VALUE;
}
+
+llvm::Error PseudoConsole::DrainInitSequences() {
+ STARTUPINFOEXW startupinfoex = {};
+ startupinfoex.StartupInfo.cb = sizeof(STARTUPINFOEXW);
+ startupinfoex.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+ auto attributelist_or_err = ProcThreadAttributeList::Create(startupinfoex);
+ if (!attributelist_or_err)
+ return llvm::errorCodeToError(attributelist_or_err.getError());
+ ProcThreadAttributeList attributelist = std::move(*attributelist_or_err);
+ if (auto error = attributelist.SetupPseudoConsole(m_conpty_handle))
+ return error;
+
+ PROCESS_INFORMATION pi = {};
+
+ wchar_t comspec[MAX_PATH];
+ DWORD comspecLen = GetEnvironmentVariableW(L"COMSPEC", comspec, MAX_PATH);
+ if (comspecLen == 0 || comspecLen >= MAX_PATH)
+ return llvm::createStringError(
+ std::error_code(GetLastError(), std::system_category()),
+ "Failed to get the 'COMSPEC' environment variable");
+
+ std::wstring cmdline_str = std::wstring(comspec) + L" /c 'echo foo && exit'";
+ std::vector<wchar_t> cmdline(cmdline_str.begin(), cmdline_str.end());
+ cmdline.push_back(L'\0');
+
+ if (!CreateProcessW(/*lpApplicationName=*/comspec, cmdline.data(),
+ /*lpProcessAttributes=*/NULL, /*lpThreadAttributes=*/NULL,
+ /*bInheritHandles=*/TRUE,
+ /*dwCreationFlags=*/EXTENDED_STARTUPINFO_PRESENT |
+ CREATE_UNICODE_ENVIRONMENT,
+ /*lpEnvironment=*/NULL, /*lpCurrentDirectory=*/NULL,
+ /*lpStartupInfo=*/
+ reinterpret_cast<STARTUPINFOW *>(&startupinfoex),
+ /*lpProcessInformation=*/&pi))
+ return llvm::errorCodeToError(
+ std::error_code(GetLastError(), std::system_category()));
+
+ char buf[4096];
+ OVERLAPPED ov = {};
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ DWORD read;
+ ReadFile(m_conpty_output, buf, sizeof(buf), &read, &ov);
+
+ WaitForSingleObject(pi.hProcess, INFINITE);
+
+ if (GetOverlappedResult(m_conpty_output, &ov, &read, FALSE) && read > 0) {
+ ResetEvent(ov.hEvent);
+ ReadFile(m_conpty_output, buf, sizeof(buf), &read, &ov);
+ }
+
+ CancelIo(m_conpty_output);
+ CloseHandle(ov.hEvent);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+
+ return llvm::Error::success();
+}
More information about the lldb-commits
mailing list