LLDB mainline
IOHandlerProcessSTDIOWindows.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10
13#include "lldb/Utility/Log.h"
14#include "lldb/Utility/State.h"
15
16using namespace lldb_private;
17
19 : IOHandler(process->GetTarget().GetDebugger(), IOHandler::Type::ProcessIO),
20 m_process(process),
21 m_read_file(GetInputFD(), File::eOpenOptionReadOnly, false),
23 CreateEvent(/*lpEventAttributes=*/nullptr, /*bManualReset=*/FALSE,
24 /*bInitialState=*/FALSE, /*lpName=*/nullptr)) {}
25
30
32 std::lock_guard<std::mutex> guard(m_mutex);
33 SetIsDone(!running);
34 m_is_running = running;
35}
36
37/// Peek the console for input. If it has any, drain the pipe until text input
38/// is found or the pipe is empty.
39///
40/// \param hStdin
41/// The handle to the standard input's pipe.
42///
43/// \return
44/// true if the pipe has text input.
45llvm::Expected<bool>
47 // Check if there are already characters buffered. Pressing enter counts as
48 // 2 characters '\r\n' and only one of them is a keyDown event.
49 DWORD bytesAvailable = 0;
50 if (PeekNamedPipe(hStdin, nullptr, 0, nullptr, &bytesAvailable, nullptr)) {
51 if (bytesAvailable > 0)
52 return true;
53 }
54
55 while (true) {
56 INPUT_RECORD inputRecord;
57 DWORD numRead = 0;
58 if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead))
59 return llvm::createStringError("failed to peek standard input");
60
61 if (numRead == 0)
62 return false;
63
64 if (inputRecord.EventType == KEY_EVENT &&
65 inputRecord.Event.KeyEvent.bKeyDown &&
66 inputRecord.Event.KeyEvent.uChar.AsciiChar != 0)
67 return true;
68
69 if (!ReadConsoleInput(hStdin, &inputRecord, 1, &numRead))
70 return llvm::createStringError("failed to read standard input");
71 }
72}
73
75 if (!m_read_file.IsValid()) {
76 SetIsDone(true);
77 return;
78 }
79
80 SetIsDone(false);
81 SetIsRunning(true);
82
83 HANDLE hStdin = m_read_file.GetWaitableHandle();
84 HANDLE waitHandles[2] = {hStdin, m_interrupt_event};
85
86 DWORD consoleMode;
87 bool isConsole = GetConsoleMode(hStdin, &consoleMode) != 0;
88 // With ENABLE_LINE_INPUT, ReadFile returns only when a carriage return is
89 // read. This will block lldb in ReadFile until the user hits enter. Save
90 // the previous console mode to restore it later and remove
91 // ENABLE_LINE_INPUT.
92 DWORD oldConsoleMode = consoleMode;
93 SetConsoleMode(hStdin, consoleMode & ~ENABLE_LINE_INPUT & ~ENABLE_ECHO_INPUT);
94
95 while (true) {
96 {
97 std::lock_guard<std::mutex> guard(m_mutex);
98 if (GetIsDone())
99 goto exit_loop;
100 }
101
102 DWORD result = WaitForMultipleObjects(2, waitHandles, FALSE, INFINITE);
103 switch (result) {
104 case WAIT_FAILED:
105 goto exit_loop;
106 case WAIT_OBJECT_0: {
107 if (isConsole) {
108 auto hasInputOrErr = ConsoleHasTextInput(hStdin);
109 if (!hasInputOrErr) {
111 LLDB_LOG_ERROR(log, hasInputOrErr.takeError(),
112 "failed to process debuggee's IO: {0}");
113 goto exit_loop;
114 }
115
116 // If no text input is ready, go back to waiting.
117 if (!*hasInputOrErr)
118 continue;
119 }
120
121 char ch = 0;
122 DWORD read = 0;
123 if (!ReadFile(hStdin, &ch, 1, &read, nullptr) || read != 1)
124 goto exit_loop;
125
126 Status err;
127 m_process->PutSTDIN(&ch, 1, err);
128 if (err.Fail())
129 goto exit_loop;
130 break;
131 }
132 case WAIT_OBJECT_0 + 1: {
134 if (op == eControlOpQuit)
135 goto exit_loop;
136 if (op == eControlOpInterrupt &&
137 StateIsRunningState(m_process->GetState()))
138 m_process->SendAsyncInterrupt();
139 break;
140 }
141 default:
142 goto exit_loop;
143 }
144 }
145
146exit_loop:;
147 SetIsRunning(false);
148 SetIsDone(true);
149 SetConsoleMode(hStdin, oldConsoleMode);
150}
151
153 std::lock_guard<std::mutex> guard(m_mutex);
154 SetIsDone(true);
155 if (m_is_running) {
157 ::SetEvent(m_interrupt_event);
158 }
159}
160
162 if (m_active) {
164 ::SetEvent(m_interrupt_event);
165 return true;
166 }
167 if (StateIsRunningState(m_process->GetState())) {
168 m_process->SendAsyncInterrupt();
169 return true;
170 }
171 return false;
172}
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:394
void * HANDLE
llvm::Expected< bool > ConsoleHasTextInput(const HANDLE hStdin)
Peek the console for input.
NativeFile m_read_file
Read from this file (usually actual STDIN for LLDB)
An abstract base class for files.
Definition FileBase.h:34
Debugger & GetDebugger()
Definition IOHandler.h:130
IOHandler(Debugger &debugger, IOHandler::Type type)
Definition IOHandler.cpp:55
void SetIsDone(bool b)
Definition IOHandler.h:81
A plug-in interface definition class for debugging a process.
Definition Process.h:357
An error handling class.
Definition Status.h:118
bool Fail() const
Test for error condition.
Definition Status.cpp:293
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:327
bool StateIsRunningState(lldb::StateType state)
Check if a state represents a state where the process or thread is running.
Definition State.cpp:68