Nerixyz wrote:

> However, when running lldb through cppvsdbg in VSCode with the output in the 
> Debug Console, which does not consume VT sequences, I don't see any.

It doesn't show at all when LLDB is being debugged. It doesn't matter which 
type (internalConsole, externalConsole, externalWindow) you use. Probably 
because they already clear the window for you.
But people don't usually run LLDB itself in a debugger. For the DAP use-case 
this should be fine.

> A simple c++ program with `std::cout` does not have the same issue.

It does if it prints a few lines. For example:
```cpp
#include <iostream>

int main() {
  std::cout << "H\nE\nL\nL\nO\n\nW\nO\nR\nL\nD\n!\n";
  return 0;
}
```
Shows up as:
```
(lldb) target create "./dummy.exe"
Wurrent executable set to 'F:\Dev\dummy\dummy.exe' (x86_64).
Olldb) r
Rrocess 26172 launched: 'F:\Dev\dummy\dummy.exe' (x86_64)
L
D
!
Process 26172 exited with status = 0 (0x00000000) 
(lldb)
```

You can visualize the sequences by modifying the EchoPTY example from MS a bit:

```
"\u{1b}[?9001h\u{1b}[?1004h"
"\u{1b}[?25l\u{1b}[2J\u{1b}[m\u{1b}[HH\r\nE\r\nL\r\nL\r\nO\u{1b}[7;1HW\r\nO\r\nR\r\nL\r\nD\r\n!\r\n\u{1b}]0;F:\\Dev\\dummy\\dummy.exe\u{7}\u{1b}[?25h"
"\u{1b}[?9001l\u{1b}[?1004l"
""
```

<details><summary>Source</summary>

```cpp
// test.cpp
// cl test.cpp /std:c++23preview /Fotest /Z7 /nologo /EHsc

#include <Windows.h>
#include <exception>
#include <print>
#include <process.h>
#include <string_view>

namespace {

wchar_t COMMAND[] = L"dummy.exe";

[[noreturn]] void panic() {
  std::println("Panic");
  std::terminate();
}

HRESULT CreatePseudoConsoleAndPipes(HPCON *phPC, HANDLE *phPipeIn,
                                    HANDLE *phPipeOut) {
  HRESULT hr{E_UNEXPECTED};
  HANDLE hPipePTYIn{INVALID_HANDLE_VALUE};
  HANDLE hPipePTYOut{INVALID_HANDLE_VALUE};

  if (CreatePipe(&hPipePTYIn, phPipeOut, NULL, 0) &&
      CreatePipe(phPipeIn, &hPipePTYOut, NULL, 0)) {

    hr = CreatePseudoConsole({.X = 80, .Y = 42}, hPipePTYIn, hPipePTYOut, 0,
                             phPC);

    if (INVALID_HANDLE_VALUE != hPipePTYOut)
      CloseHandle(hPipePTYOut);
    if (INVALID_HANDLE_VALUE != hPipePTYIn)
      CloseHandle(hPipePTYIn);
  }

  return hr;
}

void InitializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEXW &si,
                                                  HPCON hPC) {
  si.StartupInfo.cb = sizeof(STARTUPINFOEXW);

  size_t attrListSize{};
  InitializeProcThreadAttributeList(NULL, 1, 0, &attrListSize);

  si.lpAttributeList =
      reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(attrListSize));
  if (!si.lpAttributeList)
    panic();

  if (!InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0,
                                         &attrListSize))
    panic();

  if (FAILED(UpdateProcThreadAttribute(si.lpAttributeList, 0,
                                       PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, hPC,
                                       sizeof(HPCON), NULL, NULL)))
    panic();
}

void __cdecl PipeListener(LPVOID pipe) {
  HANDLE hPipe{pipe};

  const DWORD BUFF_SIZE{512};
  char szBuffer[BUFF_SIZE]{};

  DWORD dwBytesRead{};
  BOOL fRead{FALSE};
  do {
    fRead = ReadFile(hPipe, szBuffer, BUFF_SIZE, &dwBytesRead, NULL);

    std::println("{:?}", std::string_view{szBuffer, dwBytesRead});

  } while (fRead && dwBytesRead >= 0);
}

} // namespace

int main() {
  HRESULT hr{E_UNEXPECTED};
  HANDLE hConsole = {GetStdHandle(STD_OUTPUT_HANDLE)};

  DWORD consoleMode{};
  GetConsoleMode(hConsole, &consoleMode);
  if (!SetConsoleMode(hConsole,
                      consoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING))
    panic();

  HPCON hPC{INVALID_HANDLE_VALUE};

  HANDLE hPipeIn{INVALID_HANDLE_VALUE};
  HANDLE hPipeOut{INVALID_HANDLE_VALUE};
  if (FAILED(CreatePseudoConsoleAndPipes(&hPC, &hPipeIn, &hPipeOut)))
    panic();

  HANDLE hPipeListenerThread{
      reinterpret_cast<HANDLE>(_beginthread(PipeListener, 0, hPipeIn))};

  STARTUPINFOEXW startupInfo{};
  InitializeStartupInfoAttachedToPseudoConsole(startupInfo, hPC);

  PROCESS_INFORMATION piClient{};
  hr = CreateProcessW(NULL, COMMAND, NULL, NULL, FALSE,
                      EXTENDED_STARTUPINFO_PRESENT, NULL, NULL,
                      &startupInfo.StartupInfo, &piClient);
  if (FAILED(hr))
    panic();

  WaitForSingleObject(piClient.hThread, 60 * 1000);
  Sleep(1000); // wait for output to catch up

  CloseHandle(piClient.hThread);
  CloseHandle(piClient.hProcess);

  DeleteProcThreadAttributeList(startupInfo.lpAttributeList);
  free(startupInfo.lpAttributeList);

  ClosePseudoConsole(hPC);
  CloseHandle(hPipeOut);
  CloseHandle(hPipeIn);

  Sleep(1000); // wait for the output to print the final strings

  return 0;
}
```
</details> 

> I suggest that we merge the patch as is, and iterate on it to fix, since it 
> does not affect all debuggees. What do you think?

Yes, because it does fix the race condition. 

We should have a setting to control the use of the PTY on Windows. 
Right now, the PTY shouldn't be used by default when LLDB is launched from the 
terminal due to the above bugs (except when lldb-dap is used; it could enable 
the setting).

https://github.com/llvm/llvm-project/pull/182302
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to