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 }