LLDB mainline
ConnectionGenericFileWindows.cpp
Go to the documentation of this file.
1//===-- ConnectionGenericFileWindows.cpp ----------------------------------===//
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
11#include "lldb/Utility/Log.h"
12#include "lldb/Utility/Status.h"
14
15#include "llvm/ADT/STLExtras.h"
16#include "llvm/ADT/StringRef.h"
17#include "llvm/Support/ConvertUTF.h"
18
19using namespace lldb;
20using namespace lldb_private;
21
23 : m_file(INVALID_HANDLE_VALUE), m_owns_file(false) {
24 ::ZeroMemory(&m_overlapped, sizeof(m_overlapped));
25 ::ZeroMemory(&m_file_position, sizeof(m_file_position));
27}
28
30 : m_file(file), m_owns_file(owns_file) {
31 ::ZeroMemory(&m_overlapped, sizeof(m_overlapped));
32 ::ZeroMemory(&m_file_position, sizeof(m_file_position));
34}
35
43
45 m_event_handles[kInterruptEvent] = CreateEvent(NULL, FALSE, FALSE, NULL);
46
47 // Note, we should use a manual reset event for the hEvent argument of the
48 // OVERLAPPED. This is because both WaitForMultipleObjects and
49 // GetOverlappedResult (if you set the bWait argument to TRUE) will wait for
50 // the event to be signalled. If we use an auto-reset event,
51 // WaitForMultipleObjects will reset the event, return successfully, and then
52 // GetOverlappedResult will block since the event is no longer signalled.
54 ::CreateEvent(NULL, TRUE, FALSE, NULL);
55}
56
58 return m_file && (m_file != INVALID_HANDLE_VALUE);
59}
60
62 Status *error_ptr) {
64 LLDB_LOGF(log, "%p ConnectionGenericFile::Connect (url = '%s')",
65 static_cast<void *>(this), path.str().c_str());
66
67 if (!path.consume_front("file://")) {
68 if (error_ptr)
70 "unsupported connection URL: '%s'", path.str().c_str());
72 }
73
74 if (IsConnected()) {
75 ConnectionStatus status = Disconnect(error_ptr);
76 if (status != eConnectionStatusSuccess)
77 return status;
78 }
79
80 // Open the file for overlapped access. If it does not exist, create it. We
81 // open it overlapped so that we can issue asynchronous reads and then use
82 // WaitForMultipleObjects to allow the read to be interrupted by an event
83 // object.
84 std::wstring wpath;
85 if (!llvm::ConvertUTF8toWide(path, wpath)) {
86 if (error_ptr)
87 *error_ptr = Status(1, eErrorTypeGeneric);
89 }
90 m_file = ::CreateFileW(wpath.c_str(), GENERIC_READ | GENERIC_WRITE,
91 FILE_SHARE_READ, NULL, OPEN_ALWAYS,
92 FILE_FLAG_OVERLAPPED, NULL);
93 if (m_file == INVALID_HANDLE_VALUE) {
94 if (error_ptr)
95 *error_ptr = Status(::GetLastError(), eErrorTypeWin32);
97 }
98
99 m_owns_file = true;
100 m_uri = path.str();
102}
103
106 LLDB_LOGF(log, "%p ConnectionGenericFile::Disconnect ()",
107 static_cast<void *>(this));
108
109 if (!IsConnected())
111
112 // Reset the handle so that after we unblock any pending reads, subsequent
113 // calls to Read() will see a disconnected state.
114 HANDLE old_file = m_file;
115 m_file = INVALID_HANDLE_VALUE;
116
117 // Set the disconnect event so that any blocking reads unblock, then cancel
118 // any pending IO operations.
119 ::CancelIoEx(old_file, &m_overlapped);
120
121 // Close the file handle if we owned it, but don't close the event handles.
122 // We could always reconnect with the same Connection instance.
123 if (m_owns_file)
124 ::CloseHandle(old_file);
125
126 ::ZeroMemory(&m_file_position, sizeof(m_file_position));
127 m_owns_file = false;
128 m_uri.clear();
130}
131
132size_t ConnectionGenericFile::Read(void *dst, size_t dst_len,
133 const Timeout<std::micro> &timeout,
135 Status *error_ptr) {
136 if (error_ptr)
137 error_ptr->Clear();
138
139 auto finish = [&](size_t bytes, ConnectionStatus s, DWORD error_code) {
141 status = s;
142 if (error_ptr)
143 *error_ptr = Status(error_code, eErrorTypeWin32);
144
145 // kBytesAvailableEvent is a manual reset event. Make sure it gets reset
146 // here so that any subsequent operations don't immediately see bytes
147 // available.
151 LLDB_LOGF(log,
152 "%p ConnectionGenericFile::Read() handle = %p, dst = %p, "
153 "dst_len = %zu) => %zu, error = %s",
154 static_cast<void *>(this), m_file, dst, dst_len, bytes,
155 error_code ? Status(error_code, eErrorTypeWin32).AsCString()
156 : "");
157 return bytes;
158 };
159
160 if (!IsConnected())
161 return finish(0, eConnectionStatusNoConnection, ERROR_INVALID_HANDLE);
162
163 BOOL read_result = FALSE;
164 DWORD read_error = ERROR_SUCCESS;
165 if (!m_read_pending) {
167 read_result = ::ReadFile(m_file, dst, dst_len, NULL, &m_overlapped);
168 read_error = ::GetLastError();
169 }
170
171 if (!m_read_pending && !read_result && read_error != ERROR_IO_PENDING) {
172 if (read_error == ERROR_BROKEN_PIPE) {
173 // The write end of a pipe was closed. This is equivalent to EOF.
174 return finish(0, eConnectionStatusEndOfFile, 0);
175 }
176 // An unknown error occurred. Fail out.
177 return finish(0, eConnectionStatusError, ::GetLastError());
178 }
179
180 if (!read_result || m_read_pending) {
181 // The expected return path. The operation is pending. Wait for the
182 // operation to complete or be interrupted.
183 DWORD milliseconds =
184 timeout
185 ? std::chrono::duration_cast<std::chrono::milliseconds>(*timeout)
186 .count()
187 : INFINITE;
188 DWORD wait_result = ::WaitForMultipleObjects(
189 std::size(m_event_handles), m_event_handles, FALSE, milliseconds);
190 // All of the events are manual reset events, so make sure we reset them
191 // to non-signalled.
192 switch (wait_result) {
193 case WAIT_OBJECT_0 + kBytesAvailableEvent:
194 break;
195 case WAIT_OBJECT_0 + kInterruptEvent:
196 return finish(0, eConnectionStatusInterrupted, 0);
197 case WAIT_TIMEOUT:
198 return finish(0, eConnectionStatusTimedOut, 0);
199 case WAIT_FAILED:
200 return finish(0, eConnectionStatusError, ::GetLastError());
201 }
202 }
203
204 // The data is ready. Figure out how much was read and return;
205 DWORD bytes_read = 0;
206 if (!::GetOverlappedResult(m_file, &m_overlapped, &bytes_read, FALSE)) {
207 DWORD result_error = ::GetLastError();
208 // ERROR_OPERATION_ABORTED occurs when someone calls Disconnect() during
209 // a blocking read. This triggers a call to CancelIoEx, which causes the
210 // operation to complete and the result to be ERROR_OPERATION_ABORTED.
211 if (result_error == ERROR_HANDLE_EOF ||
212 result_error == ERROR_OPERATION_ABORTED ||
213 result_error == ERROR_BROKEN_PIPE)
214 return finish(bytes_read, eConnectionStatusEndOfFile, 0);
215 return finish(bytes_read, eConnectionStatusError, result_error);
216 }
217
218 if (bytes_read == 0)
219 return finish(0, eConnectionStatusEndOfFile, 0);
220 return finish(bytes_read, eConnectionStatusSuccess, 0);
221}
222
223size_t ConnectionGenericFile::Write(const void *src, size_t src_len,
225 Status *error_ptr) {
226 if (error_ptr)
227 error_ptr->Clear();
228
229 auto finish = [&](size_t bytes, ConnectionStatus s, DWORD error_code) {
230 status = s;
231 if (error_ptr)
232 *error_ptr = Status(error_code, eErrorTypeWin32);
235 LLDB_LOGF(log,
236 "%p ConnectionGenericFile::Write() handle = %p, src = %p, "
237 "src_len = %zu) => %zu, error = %s",
238 static_cast<void *>(this), m_file, src, src_len, bytes,
239 Status(error_code, eErrorTypeWin32).AsCString());
240 return bytes;
241 };
242
243 if (!IsConnected())
244 return finish(0, eConnectionStatusNoConnection, ERROR_INVALID_HANDLE);
245
246 m_overlapped.hEvent = NULL;
247
248 DWORD bytes_written = 0;
249 BOOL result = ::WriteFile(m_file, src, src_len, NULL, &m_overlapped);
250 if (!result && ::GetLastError() != ERROR_IO_PENDING)
251 return finish(0, eConnectionStatusError, ::GetLastError());
252
253 if (!::GetOverlappedResult(m_file, &m_overlapped, &bytes_written, TRUE))
254 return finish(bytes_written, eConnectionStatusError, ::GetLastError());
255
256 return finish(bytes_written, eConnectionStatusSuccess, 0);
257}
258
259std::string ConnectionGenericFile::GetURI() { return m_uri; }
260
264
266 LARGE_INTEGER old_pos;
267 old_pos.HighPart = m_overlapped.OffsetHigh;
268 old_pos.LowPart = m_overlapped.Offset;
269 old_pos.QuadPart += amount;
270 m_overlapped.Offset = old_pos.LowPart;
271 m_overlapped.OffsetHigh = old_pos.HighPart;
272}
#define LLDB_LOGF(log,...)
Definition Log.h:376
void * HANDLE
lldb::ConnectionStatus Disconnect(Status *error_ptr) override
Disconnect the communications connection if one is currently connected.
lldb::ConnectionStatus Connect(llvm::StringRef s, Status *error_ptr) override
Connect using the connect string url.
size_t Write(const void *src, size_t src_len, lldb::ConnectionStatus &status, Status *error_ptr) override
The actual write function that attempts to write to the communications protocol.
size_t Read(void *dst, size_t dst_len, const Timeout< std::micro > &timeout, lldb::ConnectionStatus &status, Status *error_ptr) override
The read function that attempts to read from the connection.
bool IsConnected() const override
Check if the connection is valid.
bool InterruptRead() override
Interrupts an ongoing Read() operation.
std::string GetURI() override
Returns a URI that describes this connection object.
An error handling class.
Definition Status.h:118
void Clear()
Clear the object state.
Definition Status.cpp:214
static Status FromErrorStringWithFormat(const char *format,...) __attribute__((format(printf
Definition Status.cpp:106
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:332
int file_t
Definition lldb-types.h:59
ConnectionStatus
Connection Status Types.
@ eConnectionStatusError
Check GetError() for details.
@ eConnectionStatusInterrupted
Interrupted read.
@ eConnectionStatusTimedOut
Request timed out.
@ eConnectionStatusEndOfFile
End-of-file encountered.
@ eConnectionStatusSuccess
Success.
@ eConnectionStatusNoConnection
No connection.
@ eErrorTypeGeneric
Generic errors that can be any value.
@ eErrorTypeWin32
Standard Win32 error codes.