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 
10 #include "lldb/Utility/Log.h"
11 #include "lldb/Utility/Status.h"
12 #include "lldb/Utility/Timeout.h"
13 
14 #include "llvm/ADT/STLExtras.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/Support/ConvertUTF.h"
17 
18 using namespace lldb;
19 using namespace lldb_private;
20 
21 namespace {
22 // This is a simple helper class to package up the information needed to return
23 // from a Read/Write operation function. Since there is a lot of code to be
24 // run before exit regardless of whether the operation succeeded or failed,
25 // combined with many possible return paths, this is the cleanest way to
26 // represent it.
27 class ReturnInfo {
28 public:
29  void Set(size_t bytes, ConnectionStatus status, DWORD error_code) {
30  m_error.SetError(error_code, eErrorTypeWin32);
31  m_bytes = bytes;
32  m_status = status;
33  }
34 
35  void Set(size_t bytes, ConnectionStatus status, llvm::StringRef error_msg) {
36  m_error.SetErrorString(error_msg.data());
37  m_bytes = bytes;
38  m_status = status;
39  }
40 
41  size_t GetBytes() const { return m_bytes; }
42  ConnectionStatus GetStatus() const { return m_status; }
43  const Status &GetError() const { return m_error; }
44 
45 private:
46  Status m_error;
47  size_t m_bytes;
48  ConnectionStatus m_status;
49 };
50 }
51 
52 ConnectionGenericFile::ConnectionGenericFile()
53  : m_file(INVALID_HANDLE_VALUE), m_owns_file(false) {
54  ::ZeroMemory(&m_overlapped, sizeof(m_overlapped));
55  ::ZeroMemory(&m_file_position, sizeof(m_file_position));
57 }
58 
60  : m_file(file), m_owns_file(owns_file) {
61  ::ZeroMemory(&m_overlapped, sizeof(m_overlapped));
62  ::ZeroMemory(&m_file_position, sizeof(m_file_position));
64 }
65 
67  if (m_owns_file && IsConnected())
68  ::CloseHandle(m_file);
69 
70  ::CloseHandle(m_event_handles[kBytesAvailableEvent]);
71  ::CloseHandle(m_event_handles[kInterruptEvent]);
72 }
73 
75  m_event_handles[kInterruptEvent] = CreateEvent(NULL, FALSE, FALSE, NULL);
76 
77  // Note, we should use a manual reset event for the hEvent argument of the
78  // OVERLAPPED. This is because both WaitForMultipleObjects and
79  // GetOverlappedResult (if you set the bWait argument to TRUE) will wait for
80  // the event to be signalled. If we use an auto-reset event,
81  // WaitForMultipleObjects will reset the event, return successfully, and then
82  // GetOverlappedResult will block since the event is no longer signalled.
84  ::CreateEvent(NULL, TRUE, FALSE, NULL);
85 }
86 
88  return m_file && (m_file != INVALID_HANDLE_VALUE);
89 }
90 
92  Status *error_ptr) {
94  LLDB_LOGF(log, "%p ConnectionGenericFile::Connect (url = '%s')",
95  static_cast<void *>(this), path.str().c_str());
96 
97  if (!path.consume_front("file://")) {
98  if (error_ptr)
99  error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'",
100  path.str().c_str());
101  return eConnectionStatusError;
102  }
103 
104  if (IsConnected()) {
105  ConnectionStatus status = Disconnect(error_ptr);
106  if (status != eConnectionStatusSuccess)
107  return status;
108  }
109 
110  // Open the file for overlapped access. If it does not exist, create it. We
111  // open it overlapped so that we can issue asynchronous reads and then use
112  // WaitForMultipleObjects to allow the read to be interrupted by an event
113  // object.
114  std::wstring wpath;
115  if (!llvm::ConvertUTF8toWide(path, wpath)) {
116  if (error_ptr)
117  error_ptr->SetError(1, eErrorTypeGeneric);
118  return eConnectionStatusError;
119  }
120  m_file = ::CreateFileW(wpath.c_str(), GENERIC_READ | GENERIC_WRITE,
121  FILE_SHARE_READ, NULL, OPEN_ALWAYS,
122  FILE_FLAG_OVERLAPPED, NULL);
123  if (m_file == INVALID_HANDLE_VALUE) {
124  if (error_ptr)
125  error_ptr->SetError(::GetLastError(), eErrorTypeWin32);
126  return eConnectionStatusError;
127  }
128 
129  m_owns_file = true;
130  m_uri = path.str();
132 }
133 
136  LLDB_LOGF(log, "%p ConnectionGenericFile::Disconnect ()",
137  static_cast<void *>(this));
138 
139  if (!IsConnected())
141 
142  // Reset the handle so that after we unblock any pending reads, subsequent
143  // calls to Read() will see a disconnected state.
144  HANDLE old_file = m_file;
145  m_file = INVALID_HANDLE_VALUE;
146 
147  // Set the disconnect event so that any blocking reads unblock, then cancel
148  // any pending IO operations.
149  ::CancelIoEx(old_file, &m_overlapped);
150 
151  // Close the file handle if we owned it, but don't close the event handles.
152  // We could always reconnect with the same Connection instance.
153  if (m_owns_file)
154  ::CloseHandle(old_file);
155 
156  ::ZeroMemory(&m_file_position, sizeof(m_file_position));
157  m_owns_file = false;
158  m_uri.clear();
160 }
161 
162 size_t ConnectionGenericFile::Read(void *dst, size_t dst_len,
163  const Timeout<std::micro> &timeout,
164  lldb::ConnectionStatus &status,
165  Status *error_ptr) {
166  ReturnInfo return_info;
167  BOOL result = 0;
168  DWORD bytes_read = 0;
169 
170  if (error_ptr)
171  error_ptr->Clear();
172 
173  if (!IsConnected()) {
174  return_info.Set(0, eConnectionStatusNoConnection, ERROR_INVALID_HANDLE);
175  goto finish;
176  }
177 
179 
180  result = ::ReadFile(m_file, dst, dst_len, NULL, &m_overlapped);
181  if (result || ::GetLastError() == ERROR_IO_PENDING) {
182  if (!result) {
183  // The expected return path. The operation is pending. Wait for the
184  // operation to complete or be interrupted.
185  DWORD milliseconds =
186  timeout
187  ? std::chrono::duration_cast<std::chrono::milliseconds>(*timeout)
188  .count()
189  : INFINITE;
190  DWORD wait_result =
191  ::WaitForMultipleObjects(llvm::array_lengthof(m_event_handles),
192  m_event_handles, FALSE, milliseconds);
193  // All of the events are manual reset events, so make sure we reset them
194  // to non-signalled.
195  switch (wait_result) {
196  case WAIT_OBJECT_0 + kBytesAvailableEvent:
197  break;
198  case WAIT_OBJECT_0 + kInterruptEvent:
199  return_info.Set(0, eConnectionStatusInterrupted, 0);
200  goto finish;
201  case WAIT_TIMEOUT:
202  return_info.Set(0, eConnectionStatusTimedOut, 0);
203  goto finish;
204  case WAIT_FAILED:
205  return_info.Set(0, eConnectionStatusError, ::GetLastError());
206  goto finish;
207  }
208  }
209  // The data is ready. Figure out how much was read and return;
210  if (!::GetOverlappedResult(m_file, &m_overlapped, &bytes_read, FALSE)) {
211  DWORD result_error = ::GetLastError();
212  // ERROR_OPERATION_ABORTED occurs when someone calls Disconnect() during
213  // a blocking read. This triggers a call to CancelIoEx, which causes the
214  // operation to complete and the result to be ERROR_OPERATION_ABORTED.
215  if (result_error == ERROR_HANDLE_EOF ||
216  result_error == ERROR_OPERATION_ABORTED ||
217  result_error == ERROR_BROKEN_PIPE)
218  return_info.Set(bytes_read, eConnectionStatusEndOfFile, 0);
219  else
220  return_info.Set(bytes_read, eConnectionStatusError, result_error);
221  } else if (bytes_read == 0)
222  return_info.Set(bytes_read, eConnectionStatusEndOfFile, 0);
223  else
224  return_info.Set(bytes_read, eConnectionStatusSuccess, 0);
225 
226  goto finish;
227  } else if (::GetLastError() == ERROR_BROKEN_PIPE) {
228  // The write end of a pipe was closed. This is equivalent to EOF.
229  return_info.Set(0, eConnectionStatusEndOfFile, 0);
230  } else {
231  // An unknown error occurred. Fail out.
232  return_info.Set(0, eConnectionStatusError, ::GetLastError());
233  }
234  goto finish;
235 
236 finish:
237  status = return_info.GetStatus();
238  if (error_ptr)
239  *error_ptr = return_info.GetError();
240 
241  // kBytesAvailableEvent is a manual reset event. Make sure it gets reset
242  // here so that any subsequent operations don't immediately see bytes
243  // available.
245 
246  IncrementFilePointer(return_info.GetBytes());
248  LLDB_LOGF(log,
249  "%p ConnectionGenericFile::Read() handle = %p, dst = %p, "
250  "dst_len = %zu) => %zu, error = %s",
251  static_cast<void *>(this), m_file, dst, dst_len,
252  return_info.GetBytes(), return_info.GetError().AsCString());
253 
254  return return_info.GetBytes();
255 }
256 
257 size_t ConnectionGenericFile::Write(const void *src, size_t src_len,
258  lldb::ConnectionStatus &status,
259  Status *error_ptr) {
260  ReturnInfo return_info;
261  DWORD bytes_written = 0;
262  BOOL result = 0;
263 
264  if (error_ptr)
265  error_ptr->Clear();
266 
267  if (!IsConnected()) {
268  return_info.Set(0, eConnectionStatusNoConnection, ERROR_INVALID_HANDLE);
269  goto finish;
270  }
271 
272  m_overlapped.hEvent = NULL;
273 
274  // Writes are not interruptible like reads are, so just block until it's
275  // done.
276  result = ::WriteFile(m_file, src, src_len, NULL, &m_overlapped);
277  if (!result && ::GetLastError() != ERROR_IO_PENDING) {
278  return_info.Set(0, eConnectionStatusError, ::GetLastError());
279  goto finish;
280  }
281 
282  if (!::GetOverlappedResult(m_file, &m_overlapped, &bytes_written, TRUE)) {
283  return_info.Set(bytes_written, eConnectionStatusError, ::GetLastError());
284  goto finish;
285  }
286 
287  return_info.Set(bytes_written, eConnectionStatusSuccess, 0);
288  goto finish;
289 
290 finish:
291  status = return_info.GetStatus();
292  if (error_ptr)
293  *error_ptr = return_info.GetError();
294 
295  IncrementFilePointer(return_info.GetBytes());
297  LLDB_LOGF(log,
298  "%p ConnectionGenericFile::Write() handle = %p, src = %p, "
299  "src_len = %zu) => %zu, error = %s",
300  static_cast<void *>(this), m_file, src, src_len,
301  return_info.GetBytes(), return_info.GetError().AsCString());
302  return return_info.GetBytes();
303 }
304 
306 
308  return ::SetEvent(m_event_handles[kInterruptEvent]);
309 }
310 
312  LARGE_INTEGER old_pos;
313  old_pos.HighPart = m_overlapped.OffsetHigh;
314  old_pos.LowPart = m_overlapped.Offset;
315  old_pos.QuadPart += amount;
316  m_overlapped.Offset = old_pos.LowPart;
317  m_overlapped.OffsetHigh = old_pos.HighPart;
318 }
lldb_private::Status::SetError
int void SetError(ValueType err, lldb::ErrorType type)
Set accessor with an error value and type.
Definition: Status.cpp:217
lldb_private::ConnectionGenericFile::kInterruptEvent
@ kInterruptEvent
Definition: ConnectionGenericFileWindows.h:51
lldb_private::ConnectionGenericFile::Disconnect
lldb::ConnectionStatus Disconnect(Status *error_ptr) override
Disconnect the communications connection if one is currently connected.
Definition: ConnectionGenericFileWindows.cpp:134
lldb_private::ConnectionGenericFile::m_event_handles
HANDLE m_event_handles[2]
Definition: ConnectionGenericFileWindows.h:47
lldb_private::ConnectionGenericFile::GetURI
std::string GetURI() override
Returns a URI that describes this connection object.
Definition: ConnectionGenericFileWindows.cpp:305
ConnectionGenericFileWindows.h
lldb_private::ConnectionGenericFile::m_file_position
LARGE_INTEGER m_file_position
Definition: ConnectionGenericFileWindows.h:49
LLDB_LOGF
#define LLDB_LOGF(log,...)
Definition: Log.h:249
lldb_private::ConnectionGenericFile::m_file
HANDLE m_file
Definition: ConnectionGenericFileWindows.h:46
lldb::file_t
int file_t
Definition: lldb-types.h:59
lldb_private::ConnectionGenericFile::ConnectionGenericFile
ConnectionGenericFile()
Definition: ConnectionGenericFileWindows.cpp:52
lldb::eConnectionStatusNoConnection
@ eConnectionStatusNoConnection
No connection.
Definition: lldb-enumerations.h:300
lldb::eConnectionStatusEndOfFile
@ eConnectionStatusEndOfFile
End-of-file encountered.
Definition: lldb-enumerations.h:297
lldb::eErrorTypeWin32
@ eErrorTypeWin32
Standard Win32 error codes.
Definition: lldb-enumerations.h:312
Log.h
lldb_private::ConnectionGenericFile::IsConnected
bool IsConnected() const override
Check if the connection is valid.
Definition: ConnectionGenericFileWindows.cpp:87
lldb_private::Status::SetErrorStringWithFormat
int SetErrorStringWithFormat(const char *format,...) __attribute__((format(printf
Set the current error string to a formatted error string.
Definition: Status.cpp:256
lldb_private::ConnectionGenericFile::IncrementFilePointer
void IncrementFilePointer(DWORD amount)
Definition: ConnectionGenericFileWindows.cpp:311
lldb_private::ConnectionGenericFile::Read
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.
Definition: ConnectionGenericFileWindows.cpp:162
lldb::eConnectionStatusTimedOut
@ eConnectionStatusTimedOut
Request timed out.
Definition: lldb-enumerations.h:299
lldb::ConnectionStatus
ConnectionStatus
Connection Status Types.
Definition: lldb-enumerations.h:295
lldb::eConnectionStatusInterrupted
@ eConnectionStatusInterrupted
Interrupted read.
Definition: lldb-enumerations.h:303
lldb::eErrorTypeGeneric
@ eErrorTypeGeneric
Generic errors that can be any value.
Definition: lldb-enumerations.h:308
lldb_private::ConnectionGenericFile::kBytesAvailableEvent
@ kBytesAvailableEvent
Definition: ConnectionGenericFileWindows.h:51
string
string(SUBSTRING ${p} 10 -1 pStripped) if($
Definition: Plugins/CMakeLists.txt:40
lldb_private::ConnectionGenericFile::m_owns_file
bool m_owns_file
Definition: ConnectionGenericFileWindows.h:48
lldb_private::ConnectionGenericFile::m_uri
std::string m_uri
Definition: ConnectionGenericFileWindows.h:57
lldb_private::ConnectionGenericFile::Connect
lldb::ConnectionStatus Connect(llvm::StringRef s, Status *error_ptr) override
Connect using the connect string url.
Definition: ConnectionGenericFileWindows.cpp:91
lldb_private::ConnectionGenericFile::InterruptRead
bool InterruptRead() override
Interrupts an ongoing Read() operation.
Definition: ConnectionGenericFileWindows.cpp:307
lldb_private::Status
Definition: Status.h:44
lldb_private::Timeout< std::micro >
lldb::eConnectionStatusError
@ eConnectionStatusError
Check GetError() for details.
Definition: lldb-enumerations.h:298
lldb_private::ConnectionGenericFile::Write
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.
Definition: ConnectionGenericFileWindows.cpp:257
Timeout.h
LIBLLDB_LOG_CONNECTION
#define LIBLLDB_LOG_CONNECTION
Definition: Logging.h:27
lldb::eConnectionStatusSuccess
@ eConnectionStatusSuccess
Success.
Definition: lldb-enumerations.h:296
Status.h
lldb_private
A class that represents a running process on the host machine.
Definition: SBCommandInterpreterRunOptions.h:16
lldb_private::ConnectionGenericFile::m_overlapped
OVERLAPPED m_overlapped
Definition: ConnectionGenericFileWindows.h:45
lldb_private::ConnectionGenericFile::InitializeEventHandles
void InitializeEventHandles()
Definition: ConnectionGenericFileWindows.cpp:74
lldb_private::Status::Clear
void Clear()
Clear the object state.
Definition: Status.cpp:168
lldb_private::Log
Definition: Log.h:49
lldb_private::GetLogIfAnyCategoriesSet
Log * GetLogIfAnyCategoriesSet(uint32_t mask)
Definition: Logging.cpp:62
lldb
Definition: SBAddress.h:15
lldb_private::ConnectionGenericFile::~ConnectionGenericFile
~ConnectionGenericFile() override
Definition: ConnectionGenericFileWindows.cpp:66