1*12792af0SZachary Turner //===-- ConnectionGenericFileWindows.cpp ------------------------*- C++ -*-===//
2*12792af0SZachary Turner //
3*12792af0SZachary Turner //                     The LLVM Compiler Infrastructure
4*12792af0SZachary Turner //
5*12792af0SZachary Turner // This file is distributed under the University of Illinois Open Source
6*12792af0SZachary Turner // License. See LICENSE.TXT for details.
7*12792af0SZachary Turner //
8*12792af0SZachary Turner //===----------------------------------------------------------------------===//
9*12792af0SZachary Turner 
10*12792af0SZachary Turner #include "lldb/Core/Error.h"
11*12792af0SZachary Turner #include "lldb/Core/Log.h"
12*12792af0SZachary Turner #include "lldb/Host/TimeValue.h"
13*12792af0SZachary Turner #include "lldb/Host/windows/ConnectionGenericFileWindows.h"
14*12792af0SZachary Turner 
15*12792af0SZachary Turner #include "llvm/ADT/STLExtras.h"
16*12792af0SZachary Turner #include "llvm/ADT/StringRef.h"
17*12792af0SZachary Turner 
18*12792af0SZachary Turner using namespace lldb;
19*12792af0SZachary Turner using namespace lldb_private;
20*12792af0SZachary Turner 
21*12792af0SZachary Turner namespace
22*12792af0SZachary Turner {
23*12792af0SZachary Turner // This is a simple helper class to package up the information needed to return from a Read/Write
24*12792af0SZachary Turner // operation function.  Since there is alot of code to be run before exit regardless of whether the
25*12792af0SZachary Turner // operation succeeded or failed, combined with many possible return paths, this is the cleanest
26*12792af0SZachary Turner // way to represent it.
27*12792af0SZachary Turner class ReturnInfo
28*12792af0SZachary Turner {
29*12792af0SZachary Turner   public:
30*12792af0SZachary Turner     void
31*12792af0SZachary Turner     Set(size_t bytes, ConnectionStatus status, DWORD error_code)
32*12792af0SZachary Turner     {
33*12792af0SZachary Turner         m_error.SetError(error_code, eErrorTypeWin32);
34*12792af0SZachary Turner         m_bytes = bytes;
35*12792af0SZachary Turner         m_status = status;
36*12792af0SZachary Turner     }
37*12792af0SZachary Turner 
38*12792af0SZachary Turner     void
39*12792af0SZachary Turner     Set(size_t bytes, ConnectionStatus status, llvm::StringRef error_msg)
40*12792af0SZachary Turner     {
41*12792af0SZachary Turner         m_error.SetErrorString(error_msg.data());
42*12792af0SZachary Turner         m_bytes = bytes;
43*12792af0SZachary Turner         m_status = status;
44*12792af0SZachary Turner     }
45*12792af0SZachary Turner 
46*12792af0SZachary Turner     size_t
47*12792af0SZachary Turner     GetBytes() const
48*12792af0SZachary Turner     {
49*12792af0SZachary Turner         return m_bytes;
50*12792af0SZachary Turner     }
51*12792af0SZachary Turner     ConnectionStatus
52*12792af0SZachary Turner     GetStatus() const
53*12792af0SZachary Turner     {
54*12792af0SZachary Turner         return m_status;
55*12792af0SZachary Turner     }
56*12792af0SZachary Turner     const Error &
57*12792af0SZachary Turner     GetError() const
58*12792af0SZachary Turner     {
59*12792af0SZachary Turner         return m_error;
60*12792af0SZachary Turner     }
61*12792af0SZachary Turner 
62*12792af0SZachary Turner   private:
63*12792af0SZachary Turner     Error m_error;
64*12792af0SZachary Turner     size_t m_bytes;
65*12792af0SZachary Turner     ConnectionStatus m_status;
66*12792af0SZachary Turner };
67*12792af0SZachary Turner }
68*12792af0SZachary Turner 
69*12792af0SZachary Turner ConnectionGenericFile::ConnectionGenericFile()
70*12792af0SZachary Turner     : m_file(INVALID_HANDLE_VALUE)
71*12792af0SZachary Turner     , m_owns_file(false)
72*12792af0SZachary Turner {
73*12792af0SZachary Turner     ::ZeroMemory(&m_overlapped, sizeof(m_overlapped));
74*12792af0SZachary Turner     ::ZeroMemory(&m_file_position, sizeof(m_file_position));
75*12792af0SZachary Turner     InitializeEventHandles();
76*12792af0SZachary Turner }
77*12792af0SZachary Turner 
78*12792af0SZachary Turner ConnectionGenericFile::ConnectionGenericFile(lldb::file_t file, bool owns_file)
79*12792af0SZachary Turner     : m_file(file)
80*12792af0SZachary Turner     , m_owns_file(owns_file)
81*12792af0SZachary Turner {
82*12792af0SZachary Turner     ::ZeroMemory(&m_overlapped, sizeof(m_overlapped));
83*12792af0SZachary Turner     ::ZeroMemory(&m_file_position, sizeof(m_file_position));
84*12792af0SZachary Turner     InitializeEventHandles();
85*12792af0SZachary Turner }
86*12792af0SZachary Turner 
87*12792af0SZachary Turner ConnectionGenericFile::~ConnectionGenericFile()
88*12792af0SZachary Turner {
89*12792af0SZachary Turner     if (m_owns_file && IsConnected())
90*12792af0SZachary Turner         ::CloseHandle(m_file);
91*12792af0SZachary Turner 
92*12792af0SZachary Turner     ::CloseHandle(m_event_handles[kBytesAvailableEvent]);
93*12792af0SZachary Turner     ::CloseHandle(m_event_handles[kInterruptEvent]);
94*12792af0SZachary Turner }
95*12792af0SZachary Turner 
96*12792af0SZachary Turner void
97*12792af0SZachary Turner ConnectionGenericFile::InitializeEventHandles()
98*12792af0SZachary Turner {
99*12792af0SZachary Turner     m_event_handles[kInterruptEvent] = CreateEvent(NULL, FALSE, FALSE, NULL);
100*12792af0SZachary Turner 
101*12792af0SZachary Turner     // Note, we should use a manual reset event for the hEvent argument of the OVERLAPPED.  This
102*12792af0SZachary Turner     // is because both WaitForMultipleObjects and GetOverlappedResult (if you set the bWait
103*12792af0SZachary Turner     // argument to TRUE) will wait for the event to be signalled.  If we use an auto-reset event,
104*12792af0SZachary Turner     // WaitForMultipleObjects will reset the event, return successfully, and then
105*12792af0SZachary Turner     // GetOverlappedResult will block since the event is no longer signalled.
106*12792af0SZachary Turner     m_event_handles[kBytesAvailableEvent] = ::CreateEvent(NULL, TRUE, FALSE, NULL);
107*12792af0SZachary Turner }
108*12792af0SZachary Turner 
109*12792af0SZachary Turner bool
110*12792af0SZachary Turner ConnectionGenericFile::IsConnected() const
111*12792af0SZachary Turner {
112*12792af0SZachary Turner     return m_file && (m_file != INVALID_HANDLE_VALUE);
113*12792af0SZachary Turner }
114*12792af0SZachary Turner 
115*12792af0SZachary Turner lldb::ConnectionStatus
116*12792af0SZachary Turner ConnectionGenericFile::Connect(const char *s, Error *error_ptr)
117*12792af0SZachary Turner {
118*12792af0SZachary Turner     Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
119*12792af0SZachary Turner     if (log)
120*12792af0SZachary Turner         log->Printf("%p ConnectionGenericFile::Connect (url = '%s')", static_cast<void *>(this), s);
121*12792af0SZachary Turner 
122*12792af0SZachary Turner     if (strstr(s, "file://") != s)
123*12792af0SZachary Turner     {
124*12792af0SZachary Turner         if (error_ptr)
125*12792af0SZachary Turner             error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'", s);
126*12792af0SZachary Turner         return eConnectionStatusError;
127*12792af0SZachary Turner     }
128*12792af0SZachary Turner 
129*12792af0SZachary Turner     if (IsConnected())
130*12792af0SZachary Turner     {
131*12792af0SZachary Turner         ConnectionStatus status = Disconnect(error_ptr);
132*12792af0SZachary Turner         if (status != eConnectionStatusSuccess)
133*12792af0SZachary Turner             return status;
134*12792af0SZachary Turner     }
135*12792af0SZachary Turner 
136*12792af0SZachary Turner     // file://PATH
137*12792af0SZachary Turner     const char *path = s + strlen("file://");
138*12792af0SZachary Turner     // Open the file for overlapped access.  If it does not exist, create it.  We open it overlapped
139*12792af0SZachary Turner     // so that we can issue asynchronous reads and then use WaitForMultipleObjects to allow the read
140*12792af0SZachary Turner     // to be interrupted by an event object.
141*12792af0SZachary Turner     m_file = ::CreateFile(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
142*12792af0SZachary Turner     if (m_file == INVALID_HANDLE_VALUE)
143*12792af0SZachary Turner     {
144*12792af0SZachary Turner         if (error_ptr)
145*12792af0SZachary Turner             error_ptr->SetError(::GetLastError(), eErrorTypeWin32);
146*12792af0SZachary Turner         return eConnectionStatusError;
147*12792af0SZachary Turner     }
148*12792af0SZachary Turner 
149*12792af0SZachary Turner     m_owns_file = true;
150*12792af0SZachary Turner     return eConnectionStatusSuccess;
151*12792af0SZachary Turner }
152*12792af0SZachary Turner 
153*12792af0SZachary Turner lldb::ConnectionStatus
154*12792af0SZachary Turner ConnectionGenericFile::Disconnect(Error *error_ptr)
155*12792af0SZachary Turner {
156*12792af0SZachary Turner     Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
157*12792af0SZachary Turner     if (log)
158*12792af0SZachary Turner         log->Printf("%p ConnectionGenericFile::Disconnect (url = '%s')", static_cast<void *>(this), s);
159*12792af0SZachary Turner 
160*12792af0SZachary Turner     if (!IsConnected())
161*12792af0SZachary Turner         return eConnectionStatusSuccess;
162*12792af0SZachary Turner 
163*12792af0SZachary Turner     // Reset the handle so that after we unblock any pending reads, subsequent calls to Read() will
164*12792af0SZachary Turner     // see a disconnected state.
165*12792af0SZachary Turner     HANDLE old_file = m_file;
166*12792af0SZachary Turner     m_file = INVALID_HANDLE_VALUE;
167*12792af0SZachary Turner 
168*12792af0SZachary Turner     // Set the disconnect event so that any blocking reads unblock, then cancel any pending IO operations.
169*12792af0SZachary Turner     ::CancelIoEx(old_file, &m_overlapped);
170*12792af0SZachary Turner 
171*12792af0SZachary Turner     // Close the file handle if we owned it, but don't close the event handles.  We could always
172*12792af0SZachary Turner     // reconnect with the same Connection instance.
173*12792af0SZachary Turner     if (m_owns_file)
174*12792af0SZachary Turner         ::CloseHandle(old_file);
175*12792af0SZachary Turner 
176*12792af0SZachary Turner     ::ZeroMemory(&m_file_position, sizeof(m_file_position));
177*12792af0SZachary Turner     m_owns_file = false;
178*12792af0SZachary Turner     return eConnectionStatusSuccess;
179*12792af0SZachary Turner }
180*12792af0SZachary Turner 
181*12792af0SZachary Turner size_t
182*12792af0SZachary Turner ConnectionGenericFile::Read(void *dst, size_t dst_len, uint32_t timeout_usec, lldb::ConnectionStatus &status, Error *error_ptr)
183*12792af0SZachary Turner {
184*12792af0SZachary Turner     ReturnInfo return_info;
185*12792af0SZachary Turner 
186*12792af0SZachary Turner     if (error_ptr)
187*12792af0SZachary Turner         error_ptr->Clear();
188*12792af0SZachary Turner 
189*12792af0SZachary Turner     if (!IsConnected())
190*12792af0SZachary Turner     {
191*12792af0SZachary Turner         return_info.Set(0, eConnectionStatusNoConnection, ERROR_INVALID_HANDLE);
192*12792af0SZachary Turner         goto finish;
193*12792af0SZachary Turner     }
194*12792af0SZachary Turner 
195*12792af0SZachary Turner     m_overlapped.hEvent = m_event_handles[kBytesAvailableEvent];
196*12792af0SZachary Turner 
197*12792af0SZachary Turner     BOOL result = ::ReadFile(m_file, dst, dst_len, NULL, &m_overlapped);
198*12792af0SZachary Turner     if (result || ::GetLastError() == ERROR_IO_PENDING)
199*12792af0SZachary Turner     {
200*12792af0SZachary Turner         if (!result)
201*12792af0SZachary Turner         {
202*12792af0SZachary Turner             // The expected return path.  The operation is pending.  Wait for the operation to complete
203*12792af0SZachary Turner             // or be interrupted.
204*12792af0SZachary Turner             TimeValue time_value;
205*12792af0SZachary Turner             time_value.OffsetWithMicroSeconds(timeout_usec);
206*12792af0SZachary Turner             DWORD milliseconds = time_value.milliseconds();
207*12792af0SZachary Turner             result = ::WaitForMultipleObjects(llvm::array_lengthof(m_event_handles), m_event_handles, FALSE, milliseconds);
208*12792af0SZachary Turner             // All of the events are manual reset events, so make sure we reset them to non-signalled.
209*12792af0SZachary Turner             switch (result)
210*12792af0SZachary Turner             {
211*12792af0SZachary Turner                 case WAIT_OBJECT_0 + kBytesAvailableEvent:
212*12792af0SZachary Turner                     break;
213*12792af0SZachary Turner                 case WAIT_OBJECT_0 + kInterruptEvent:
214*12792af0SZachary Turner                     return_info.Set(0, eConnectionStatusInterrupted, 0);
215*12792af0SZachary Turner                     goto finish;
216*12792af0SZachary Turner                 case WAIT_TIMEOUT:
217*12792af0SZachary Turner                     return_info.Set(0, eConnectionStatusTimedOut, 0);
218*12792af0SZachary Turner                     goto finish;
219*12792af0SZachary Turner                 case WAIT_FAILED:
220*12792af0SZachary Turner                     return_info.Set(0, eConnectionStatusError, ::GetLastError());
221*12792af0SZachary Turner                     goto finish;
222*12792af0SZachary Turner             }
223*12792af0SZachary Turner         }
224*12792af0SZachary Turner         // The data is ready.  Figure out how much was read and return;
225*12792af0SZachary Turner         DWORD bytes_read = 0;
226*12792af0SZachary Turner         if (!::GetOverlappedResult(m_file, &m_overlapped, &bytes_read, FALSE))
227*12792af0SZachary Turner         {
228*12792af0SZachary Turner             DWORD result_error = ::GetLastError();
229*12792af0SZachary Turner             // ERROR_OPERATION_ABORTED occurs when someone calls Disconnect() during a blocking read.
230*12792af0SZachary Turner             // This triggers a call to CancelIoEx, which causes the operation to complete and the
231*12792af0SZachary Turner             // result to be ERROR_OPERATION_ABORTED.
232*12792af0SZachary Turner             if (result_error == ERROR_HANDLE_EOF || result_error == ERROR_OPERATION_ABORTED)
233*12792af0SZachary Turner                 return_info.Set(bytes_read, eConnectionStatusEndOfFile, 0);
234*12792af0SZachary Turner             else
235*12792af0SZachary Turner                 return_info.Set(bytes_read, eConnectionStatusError, result_error);
236*12792af0SZachary Turner         }
237*12792af0SZachary Turner         else if (bytes_read == 0)
238*12792af0SZachary Turner             return_info.Set(bytes_read, eConnectionStatusEndOfFile, 0);
239*12792af0SZachary Turner         else
240*12792af0SZachary Turner             return_info.Set(bytes_read, eConnectionStatusSuccess, 0);
241*12792af0SZachary Turner 
242*12792af0SZachary Turner         goto finish;
243*12792af0SZachary Turner     }
244*12792af0SZachary Turner 
245*12792af0SZachary Turner     // An unknown error occured.  Fail out.
246*12792af0SZachary Turner     return_info.Set(0, eConnectionStatusError, ::GetLastError());
247*12792af0SZachary Turner     goto finish;
248*12792af0SZachary Turner 
249*12792af0SZachary Turner finish:
250*12792af0SZachary Turner     status = return_info.GetStatus();
251*12792af0SZachary Turner     if (error_ptr)
252*12792af0SZachary Turner         *error_ptr = return_info.GetError();
253*12792af0SZachary Turner 
254*12792af0SZachary Turner     // kBytesAvailableEvent is a manual reset event.  Make sure it gets reset here so that any
255*12792af0SZachary Turner     // subsequent operations don't immediately see bytes available.
256*12792af0SZachary Turner     ResetEvent(m_event_handles[kBytesAvailableEvent]);
257*12792af0SZachary Turner 
258*12792af0SZachary Turner     IncrementFilePointer(return_info.GetBytes());
259*12792af0SZachary Turner     Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
260*12792af0SZachary Turner     if (log)
261*12792af0SZachary Turner     {
262*12792af0SZachary Turner         log->Printf("%" PRIxPTR " ConnectionGenericFile::Read()  handle = %" PRIxPTR ", dst = %" PRIxPTR ", dst_len = %" PRIu64
263*12792af0SZachary Turner                     ") => %" PRIu64 ", error = %s",
264*12792af0SZachary Turner                     this, m_file, dst, static_cast<uint64_t>(dst_len), static_cast<uint64_t>(return_info.GetBytes()),
265*12792af0SZachary Turner                     return_info.GetError().AsCString());
266*12792af0SZachary Turner     }
267*12792af0SZachary Turner 
268*12792af0SZachary Turner     return return_info.GetBytes();
269*12792af0SZachary Turner }
270*12792af0SZachary Turner 
271*12792af0SZachary Turner size_t
272*12792af0SZachary Turner ConnectionGenericFile::Write(const void *src, size_t src_len, lldb::ConnectionStatus &status, Error *error_ptr)
273*12792af0SZachary Turner {
274*12792af0SZachary Turner     ReturnInfo return_info;
275*12792af0SZachary Turner 
276*12792af0SZachary Turner     if (error_ptr)
277*12792af0SZachary Turner         error_ptr->Clear();
278*12792af0SZachary Turner 
279*12792af0SZachary Turner     if (!IsConnected())
280*12792af0SZachary Turner     {
281*12792af0SZachary Turner         return_info.Set(0, eConnectionStatusNoConnection, ERROR_INVALID_HANDLE);
282*12792af0SZachary Turner         goto finish;
283*12792af0SZachary Turner     }
284*12792af0SZachary Turner 
285*12792af0SZachary Turner     m_overlapped.hEvent = NULL;
286*12792af0SZachary Turner 
287*12792af0SZachary Turner     // Writes are not interruptible like reads are, so just block until it's done.
288*12792af0SZachary Turner     BOOL result = ::WriteFile(m_file, src, src_len, NULL, &m_overlapped);
289*12792af0SZachary Turner     if (!result && ::GetLastError() != ERROR_IO_PENDING)
290*12792af0SZachary Turner     {
291*12792af0SZachary Turner         return_info.Set(0, eConnectionStatusError, ::GetLastError());
292*12792af0SZachary Turner         goto finish;
293*12792af0SZachary Turner     }
294*12792af0SZachary Turner 
295*12792af0SZachary Turner     DWORD bytes_written = 0;
296*12792af0SZachary Turner     if (!::GetOverlappedResult(m_file, &m_overlapped, &bytes_written, TRUE))
297*12792af0SZachary Turner     {
298*12792af0SZachary Turner         return_info.Set(bytes_written, eConnectionStatusError, ::GetLastError());
299*12792af0SZachary Turner         goto finish;
300*12792af0SZachary Turner     }
301*12792af0SZachary Turner 
302*12792af0SZachary Turner     return_info.Set(bytes_written, eConnectionStatusSuccess, 0);
303*12792af0SZachary Turner     goto finish;
304*12792af0SZachary Turner 
305*12792af0SZachary Turner finish:
306*12792af0SZachary Turner     status = return_info.GetStatus();
307*12792af0SZachary Turner     if (error_ptr)
308*12792af0SZachary Turner         *error_ptr = return_info.GetError();
309*12792af0SZachary Turner 
310*12792af0SZachary Turner     IncrementFilePointer(return_info.GetBytes());
311*12792af0SZachary Turner     Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
312*12792af0SZachary Turner     if (log)
313*12792af0SZachary Turner     {
314*12792af0SZachary Turner         log->Printf("%" PRIxPTR " ConnectionGenericFile::Write()  handle = %" PRIxPTR ", src = %" PRIxPTR ", src_len = %" PRIu64
315*12792af0SZachary Turner                     ") => %" PRIu64 ", error = %s",
316*12792af0SZachary Turner                     this, m_file, src, static_cast<uint64_t>(src_len), static_cast<uint64_t>(return_info.GetBytes()),
317*12792af0SZachary Turner                     return_info.GetError().AsCString());
318*12792af0SZachary Turner     }
319*12792af0SZachary Turner     return return_info.GetBytes();
320*12792af0SZachary Turner }
321*12792af0SZachary Turner 
322*12792af0SZachary Turner bool
323*12792af0SZachary Turner ConnectionGenericFile::InterruptRead()
324*12792af0SZachary Turner {
325*12792af0SZachary Turner     return ::SetEvent(m_event_handles[kInterruptEvent]);
326*12792af0SZachary Turner }
327*12792af0SZachary Turner 
328*12792af0SZachary Turner void
329*12792af0SZachary Turner ConnectionGenericFile::IncrementFilePointer(DWORD amount)
330*12792af0SZachary Turner {
331*12792af0SZachary Turner     LARGE_INTEGER old_pos;
332*12792af0SZachary Turner     old_pos.HighPart = m_overlapped.OffsetHigh;
333*12792af0SZachary Turner     old_pos.LowPart = m_overlapped.Offset;
334*12792af0SZachary Turner     old_pos.QuadPart += amount;
335*12792af0SZachary Turner     m_overlapped.Offset = old_pos.LowPart;
336*12792af0SZachary Turner     m_overlapped.OffsetHigh = old_pos.HighPart;
337*12792af0SZachary Turner }