[Lldb-commits] [lldb] [lldb-dap][windows] drain the ConPTY before attaching (PR #180578)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Tue Feb 10 07:12:41 PST 2026
https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/180578
>From 43a81e2be21ec379f2d61bebf24116692cb063c2 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Mon, 9 Feb 2026 18:23:59 +0000
Subject: [PATCH] [lldb-dap][windows] drain the ConPTY before attaching
---
.../include/lldb/Host/windows/PseudoConsole.h | 10 +++
lldb/source/Host/windows/PseudoConsole.cpp | 66 +++++++++++++++++++
2 files changed, 76 insertions(+)
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..7f48290d18a6d 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();
+}
\ No newline at end of file
More information about the lldb-commits
mailing list