300 lines
6.3 KiB
C++
300 lines
6.3 KiB
C++
// 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<LPARAM>(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<BYTE*>(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<ReaderThreadParams*>(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?
|