// Spewview: remote debug spew monitor // // Copyright (c) 2000 Microsoft Corp. // // Thread to read remote debug spew // // 20 Mar 2000 sburns #include "headers.hxx" #include "resource.h" #include "ReaderThread.hpp" #include "SpewDialog.hpp" int readMessageCount = 0; HRESULT CreateAndConnectSpewPipe( const String& appName, HANDLE& result) { LOG_FUNCTION(CreateAndConnectSpewPipe); ASSERT(!appName.empty()); result = INVALID_HANDLE_VALUE; HRESULT hr = S_OK; do { // build a null dacl for the pipe to allow everyone access SECURITY_ATTRIBUTES sa; memset(&sa, 0, sizeof(sa)); SECURITY_DESCRIPTOR sd; hr = Win::InitializeSecurityDescriptor(sd); BREAK_ON_FAILED_HRESULT(hr); hr = Win::SetSecurityDescriptorDacl(sd, true, 0, false); BREAK_ON_FAILED_HRESULT(hr); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = &sd; sa.bInheritHandle = TRUE; String pipename = L"\\\\.\\pipe\\spewview\\" + appName; hr = Win::CreateNamedPipe( pipename, PIPE_ACCESS_INBOUND | WRITE_DAC, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE /* | PIPE_NOWAIT */ , 1, MAX_PATH, MAX_PATH, 500, &sa, result); BREAK_ON_FAILED_HRESULT(hr); // block until the spewing app connects to us. hr = Win::ConnectNamedPipe(result, 0); BREAK_ON_FAILED_HRESULT(hr); } while (0); LOG_HRESULT(hr); return hr; } void AddSpewMessage( HWND spewWindow, const String& message, DWORD readMessageCount = -1) { ASSERT(!message.empty()); // post the received text to the message window. // deleted in the UI thread when the WM_UPDATE_SPEWAGE message // is receieved. String* spew = new String(message); Win::PostMessage( spewWindow, SpewDialog::WM_UPDATE_SPEWAGE, readMessageCount, reinterpret_cast(spew)); } HRESULT ReadSpew(HANDLE pipe, HWND spewWindow) { LOG_FUNCTION(ReadSpew); ASSERT(pipe != INVALID_HANDLE_VALUE); ASSERT(Win::IsWindow(spewWindow)); HRESULT hr = S_OK; String messageBuffer; messageBuffer.reserve(2048); static const size_t SPEW_BUF_MAX_CHARACTERS = 1023; static const size_t SPEW_BUF_MAX_BYTES = SPEW_BUF_MAX_CHARACTERS * sizeof(wchar_t); wchar_t buf[SPEW_BUF_MAX_CHARACTERS + 1]; BYTE* byteBuf = reinterpret_cast(buf); for (;;) { DWORD bytesRead = 0; hr = Win::ReadFile( pipe, byteBuf, SPEW_BUF_MAX_BYTES, bytesRead, 0); if (hr == Win32ToHresult(ERROR_MORE_DATA)) { // don't break: we will pick up the rest of the message in the // next pass } else { BREAK_ON_FAILED_HRESULT(hr); } if (!bytesRead) { // it's possbile that the client wrote zero bytes. continue; } // force null termination byteBuf[bytesRead] = 0; byteBuf[bytesRead + 1] = 0; ++readMessageCount; String message(buf); if (messageBuffer.length() + message.length() >= 2048) { // flush the buffer AddSpewMessage( spewWindow, messageBuffer, readMessageCount); messageBuffer.erase(); messageBuffer.reserve(2048); } messageBuffer.append(message); } // flush the buffer AddSpewMessage( spewWindow, messageBuffer, readMessageCount); return hr; } void _cdecl ReaderThreadProc(void* p) { LOG_FUNCTION(ReaderThreadProc); ASSERT(p); if (!p) { return; } // copy what we need from p, then delete it. ReaderThreadParams* params = reinterpret_cast(p); HWND spewWindow = params->hwnd; int* endReaderThread = params->endFlag; String appName = params->appName; delete params; params = 0; p = 0; HRESULT hr = S_OK; do { // create and connect to the pipe HANDLE pipe = INVALID_HANDLE_VALUE; hr = CreateAndConnectSpewPipe(appName, pipe); if (FAILED(hr)) { AddSpewMessage( spewWindow, String::format(L"Connect failed: %1!08X!", hr)); popup.Error( spewWindow, hr, IDS_ERROR_CONNECTING); break; } // read the contents of the slot until the thread is flagged to die, // or a failure occurs. while (!*endReaderThread) { hr = ReadSpew(pipe, spewWindow); if (FAILED(hr)) { LOG_HRESULT(hr); *endReaderThread = 1; popup.Error( spewWindow, hr, IDS_ERROR_READING); } } // we're supposed to stop. // this will terminate the client's connection too. hr = Win::DisconnectNamedPipe(pipe); BREAK_ON_FAILED_HRESULT(hr); // this will delete the pipe instance Win::CloseHandle(pipe); // Tell the viewer to enable the start button Win::PostMessage(spewWindow, SpewDialog::WM_ENABLE_START, 0, 0); } while (0); } // CODEWORK: // stop button does not work, as ReadFile is blocking, and reader thread // never gets around to evaluating end flag. // therefore, restart and cancel buttons do not work either // to allow non-blocking reads of the pipe, need to use async read so // reader thread is not blocked waiting for client write // replace end flag with event triggered when cancel or stop pressed. // static alloc overlapped struct // put ptr to spew window in overlapped hEvent member // call ReadFileEx // // while WaitForMultpleObjects(cancel event, read, timeout) // if timeout // flush read buffer // if io complete, do nothing // if cancel signalled, // flush read buffer, // cancel io // exit loop // if other error // flush read buffer, // cancel io // exit loop // // completion callback // if error != ERROR_OPERATION_ABORTED // append to read buffer // if buffer full, flush to spew window // call ReadFileEx // will this eat stack forever?