You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1537 lines
36 KiB
1537 lines
36 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
output.cxx
|
|
|
|
Abstract:
|
|
|
|
This file contains output state control and output callback classes.
|
|
|
|
Author:
|
|
|
|
Jason Hartman (JasonHa) 2000-11-20
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
|
|
|
|
HRESULT
|
|
OutputControl::SetControl(
|
|
ULONG OutputControl,
|
|
PDEBUG_CLIENT Client
|
|
)
|
|
{
|
|
ULONG SendMask = OutputControl & DEBUG_OUTCTL_SEND_MASK;
|
|
|
|
if (OutputControl != DEBUG_OUTCTL_AMBIENT &&
|
|
(
|
|
#if DEBUG_OUTCTL_THIS_CLIENT > 0
|
|
SendMask < DEBUG_OUTCTL_THIS_CLIENT ||
|
|
#endif
|
|
SendMask > DEBUG_OUTCTL_LOG_ONLY ||
|
|
(OutputControl & ~(DEBUG_OUTCTL_SEND_MASK |
|
|
DEBUG_OUTCTL_NOT_LOGGED |
|
|
DEBUG_OUTCTL_OVERRIDE_MASK))))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (Client != NULL)
|
|
{
|
|
HRESULT hr;
|
|
PDEBUG_CONTROL NewControl;
|
|
|
|
// Switch to new client
|
|
if ((hr = Client->QueryInterface(__uuidof(IDebugControl),
|
|
(void **)&NewControl)) != S_OK)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if (Control != NULL) Control->Release();
|
|
Control = NewControl;
|
|
}
|
|
|
|
OutCtl = OutputControl;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
OutputControl::Output(
|
|
ULONG Mask,
|
|
PCSTR Format,
|
|
...
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
va_list Args;
|
|
|
|
if (Control == NULL) return E_FAIL;
|
|
|
|
va_start(Args, Format);
|
|
if (OutCtl == DEBUG_OUTCTL_AMBIENT)
|
|
{
|
|
hr = Control->OutputVaList(Mask, Format, Args);
|
|
}
|
|
else
|
|
{
|
|
hr = Control->ControlledOutputVaList(OutCtl, Mask, Format, Args);
|
|
}
|
|
va_end(Args);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
OutputControl::OutputVaList(
|
|
ULONG Mask,
|
|
PCSTR Format,
|
|
va_list Args
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (Control == NULL) return E_FAIL;
|
|
|
|
if (OutCtl == DEBUG_OUTCTL_AMBIENT)
|
|
{
|
|
hr = Control->OutputVaList(Mask, Format, Args);
|
|
}
|
|
else
|
|
{
|
|
hr = Control->ControlledOutputVaList(OutCtl, Mask, Format, Args);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
OutputControl::Output(
|
|
PCSTR Format,
|
|
...
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
va_list Args;
|
|
|
|
va_start(Args, Format);
|
|
hr = OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
|
|
va_end(Args);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
OutputControl::OutErr(
|
|
PCSTR Format,
|
|
...
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
va_list Args;
|
|
|
|
va_start(Args, Format);
|
|
hr = OutputVaList(DEBUG_OUTPUT_ERROR, Format, Args);
|
|
va_end(Args);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
OutputControl::OutWarn(
|
|
PCSTR Format,
|
|
...
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
va_list Args;
|
|
|
|
va_start(Args, Format);
|
|
hr = OutputVaList(DEBUG_OUTPUT_WARNING, Format, Args);
|
|
va_end(Args);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
OutputControl::OutVerb(
|
|
PCSTR Format,
|
|
...
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
va_list Args;
|
|
|
|
va_start(Args, Format);
|
|
hr = OutputVaList(DEBUG_OUTPUT_VERBOSE, Format, Args);
|
|
va_end(Args);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
OutputControl::GetInterrupt(
|
|
)
|
|
{
|
|
return (Control == NULL) ?
|
|
E_FAIL :
|
|
Control->GetInterrupt();
|
|
}
|
|
|
|
|
|
HRESULT
|
|
OutputControl::SetInterrupt(
|
|
ULONG Flags
|
|
)
|
|
{
|
|
return (Control == NULL) ?
|
|
E_FAIL :
|
|
Control->SetInterrupt(Flags);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
OutputControl::Evaluate(
|
|
IN PCSTR Expression,
|
|
IN ULONG DesiredType,
|
|
OUT PDEBUG_VALUE Value,
|
|
OUT OPTIONAL PULONG RemainderIndex
|
|
)
|
|
{
|
|
return (Control == NULL) ?
|
|
E_FAIL :
|
|
Control->Evaluate(Expression,
|
|
DesiredType,
|
|
Value,
|
|
RemainderIndex);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
OutputControl::CoerceValue(
|
|
IN PDEBUG_VALUE In,
|
|
IN ULONG OutType,
|
|
OUT PDEBUG_VALUE Out
|
|
)
|
|
{
|
|
return (Control == NULL) ?
|
|
E_FAIL :
|
|
Control->CoerceValue(In,
|
|
OutType,
|
|
Out);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
OutputControl::IsPointer64Bit(
|
|
)
|
|
{
|
|
return (Control == NULL) ?
|
|
E_FAIL :
|
|
Control->IsPointer64Bit();
|
|
}
|
|
|
|
|
|
|
|
OutputState::OutputState(
|
|
PDEBUG_CLIENT OrgClient,
|
|
BOOL SameClient
|
|
)
|
|
{
|
|
hrInit = S_FALSE;
|
|
Client = NULL;
|
|
Control = NULL;
|
|
Symbols = NULL;
|
|
|
|
SetCallbacks = FALSE;
|
|
|
|
CreatedClient = FALSE;
|
|
Saved = FALSE;
|
|
|
|
if (OrgClient != NULL)
|
|
{
|
|
if (SameClient)
|
|
{
|
|
Client = OrgClient;
|
|
Client->AddRef();
|
|
CreatedClient = TRUE;
|
|
hrInit = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hrInit = OrgClient->CreateClient(&Client);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
OutputState::~OutputState()
|
|
{
|
|
if (!CreatedClient) Restore();
|
|
|
|
EXT_RELEASE(Symbols);
|
|
EXT_RELEASE(Control);
|
|
|
|
// If Client was newly created for OutputState, then
|
|
// there shouldn't be any other references to Client.
|
|
if (CreatedClient)
|
|
{
|
|
ULONG RemainingRefs;
|
|
|
|
RemainingRefs = Client->AddRef();
|
|
if (RemainingRefs > 2)
|
|
{
|
|
DbgPrint("OutputState: %lu refs outstanding on created client.\n",
|
|
RemainingRefs-2);
|
|
DbgBreakPoint();
|
|
|
|
// As a precaution, Restore the callbacks;
|
|
// so, any set callback may be cleaned up.
|
|
Restore();
|
|
}
|
|
Client->Release();
|
|
}
|
|
|
|
if (Client != NULL) Client->Release();
|
|
}
|
|
|
|
|
|
HRESULT
|
|
OutputState::Setup(
|
|
ULONG OutMask,
|
|
PDEBUG_OUTPUT_CALLBACKS OutCallbacks
|
|
)
|
|
{
|
|
HRESULT hr = hrInit;
|
|
ULONG LastOutMask;
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
if (CreatedClient && !Saved)
|
|
{
|
|
if ((hr = Client->GetOutputMask(&OrgOutMask)) == S_OK &&
|
|
(hr = Client->GetOutputCallbacks(&OrgOutCallbacks)) == S_OK)
|
|
{
|
|
Saved = TRUE;
|
|
}
|
|
}
|
|
|
|
if (hr == S_OK &&
|
|
(hr = Client->GetOutputMask(&LastOutMask)) == S_OK &&
|
|
(hr = Client->SetOutputMask(OutMask)) == S_OK)
|
|
{
|
|
if (!Saved && !SetCallbacks)
|
|
{
|
|
OrgOutMask = LastOutMask;
|
|
OrgOutCallbacks = NULL;
|
|
}
|
|
|
|
if ((hr = Client->SetOutputCallbacks(OutCallbacks)) == S_OK)
|
|
{
|
|
SetCallbacks = TRUE;
|
|
}
|
|
else
|
|
{
|
|
Client->SetOutputMask(LastOutMask);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (hr == S_OK &&
|
|
Symbols == NULL)
|
|
{
|
|
hr = Client->QueryInterface(__uuidof(IDebugSymbols), (void **)&Symbols);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
OutputState::Execute(
|
|
PCSTR pszCommand
|
|
)
|
|
{
|
|
HRESULT hr = hrInit;
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
if (Control == NULL)
|
|
{
|
|
hr = Client->QueryInterface(__uuidof(IDebugControl), (void **)&Control);
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
hr = Control->Execute(DEBUG_OUTCTL_THIS_CLIENT |
|
|
DEBUG_OUTCTL_NOT_LOGGED |
|
|
DEBUG_OUTCTL_OVERRIDE_MASK,
|
|
pszCommand,
|
|
DEBUG_EXECUTE_NOT_LOGGED |
|
|
DEBUG_EXECUTE_NO_REPEAT);
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
DbgPrint("IDebugControl::Execute returned %s.\n",
|
|
pszHRESULT(hr));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
OutputState::OutputType(
|
|
IN BOOL Physical,
|
|
IN ULONG64 Offset,
|
|
IN PCSTR Type,
|
|
IN ULONG Flags
|
|
)
|
|
{
|
|
HRESULT hr = hrInit;
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
if (Symbols == NULL)
|
|
{
|
|
hr = Client->QueryInterface(__uuidof(IDebugSymbols), (void **)&Symbols);
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
ULONG64 Module;
|
|
ULONG TypeId;
|
|
|
|
hr = GetTypeId(Client, Type, &TypeId, &Module);
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
OutputControl OutCtl(Client);
|
|
ULONG ModuleIndex = 0;
|
|
|
|
while ((hr = Symbols->GetModuleByIndex(ModuleIndex, &Module)) == S_OK &&
|
|
(Module != 0))
|
|
{
|
|
if ((hr = Symbols->GetTypeId(Module, Type, &TypeId)) == S_OK)
|
|
{
|
|
OutCtl.OutVerb("Found %s: TypeId 0x%lx in module @ %p.\n",
|
|
Type, TypeId, Module);
|
|
break;
|
|
}
|
|
|
|
ModuleIndex++;
|
|
Module = 0;
|
|
}
|
|
|
|
if (hr == S_OK &&
|
|
(Module == 0 || TypeId == 0))
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
OutCtl.OutVerb("Couldn't find %s in any of %lu modules.\n",
|
|
Type, ModuleIndex);
|
|
}
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
hr = OutputType(Physical, Offset, Module, TypeId, Flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
OutputState::OutputType(
|
|
IN BOOL Physical,
|
|
IN ULONG64 Offset,
|
|
IN ULONG64 Module,
|
|
IN ULONG TypeId,
|
|
IN ULONG Flags
|
|
)
|
|
{
|
|
HRESULT hr = hrInit;
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
if (Symbols == NULL)
|
|
{
|
|
hr = Client->QueryInterface(__uuidof(IDebugSymbols), (void **)&Symbols);
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
if (Physical)
|
|
{
|
|
hr = Symbols->OutputTypedDataPhysical(DEBUG_OUTCTL_THIS_CLIENT |
|
|
DEBUG_OUTCTL_NOT_LOGGED |
|
|
DEBUG_OUTCTL_OVERRIDE_MASK,
|
|
Offset,
|
|
Module,
|
|
TypeId,
|
|
Flags);
|
|
}
|
|
else
|
|
{
|
|
hr = Symbols->OutputTypedDataVirtual(DEBUG_OUTCTL_THIS_CLIENT |
|
|
DEBUG_OUTCTL_NOT_LOGGED |
|
|
DEBUG_OUTCTL_OVERRIDE_MASK,
|
|
Offset,
|
|
Module,
|
|
TypeId,
|
|
Flags);
|
|
}
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
DbgPrint("IDebugSymbols::OutputTypedData%s returned %s for 0x%I64x.\n",
|
|
(Physical ? "Physical" : "Virtual"),
|
|
pszHRESULT(hr),
|
|
Offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
void OutputState::Restore()
|
|
{
|
|
if (SetCallbacks)
|
|
{
|
|
Client->SetOutputCallbacks(OrgOutCallbacks);
|
|
Client->SetOutputMask(OrgOutMask);
|
|
|
|
SetCallbacks = FALSE;
|
|
}
|
|
|
|
if (Saved)
|
|
{
|
|
if (OrgOutCallbacks != NULL) OrgOutCallbacks->Release();
|
|
Saved = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Default output callbacks implementation, provides IUnknown for
|
|
// static classes.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
DefOutputCallbacks::QueryInterface(
|
|
THIS_
|
|
IN REFIID InterfaceId,
|
|
OUT PVOID* Interface
|
|
)
|
|
{
|
|
*Interface = NULL;
|
|
|
|
if (//(InterfaceId == IID_IUnknown) ||
|
|
(InterfaceId == __uuidof(IDebugOutputCallbacks)))
|
|
{
|
|
*Interface = (IDebugOutputCallbacks *)this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
DefOutputCallbacks::AddRef(
|
|
THIS
|
|
)
|
|
{
|
|
// This class is designed to be allocated on a
|
|
// stack or statically, but we retain a refcount
|
|
// for debugging purposes.
|
|
return ++RefCount;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
DefOutputCallbacks::Release(
|
|
THIS
|
|
)
|
|
{
|
|
// This class is designed to be allocated on a
|
|
// stack or statically, but we retain a refcount
|
|
// for debugging purposes.
|
|
RefCount--;
|
|
|
|
if (RefCount < 1)
|
|
{
|
|
DbgPrint("DefOutputCallbacks@0x%p::RefCount(%lu) < 1.\n", this, RefCount);
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
return RefCount;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
DefOutputCallbacks::Output(
|
|
THIS_
|
|
IN ULONG Mask,
|
|
IN PCSTR Text
|
|
)
|
|
{
|
|
// The default Output ignores all Output calls.
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// DebugOutputCallbacks::Output
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
DebugOutputCallbacks::Output(
|
|
THIS_
|
|
IN ULONG Mask,
|
|
IN PCSTR Text
|
|
)
|
|
{
|
|
DbgPrint("Mask: 0x%lx\tOutput Begin:\n%s:Output End\n", Mask, Text);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// OutputReader
|
|
//
|
|
// General DebugOutputCallback class to parse output.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
OutputReader::Output(
|
|
THIS_
|
|
IN ULONG Mask,
|
|
IN PCSTR Text
|
|
)
|
|
{
|
|
ULONG TextLen = strlen(Text);
|
|
|
|
if (BufferLeft < TextLen)
|
|
{
|
|
PSTR NewBuffer;
|
|
SIZE_T NewBufferSize;
|
|
|
|
if (hHeap == NULL)
|
|
{
|
|
hHeap = GetProcessHeap();
|
|
if (hHeap == NULL) return S_FALSE;
|
|
}
|
|
|
|
// New length we need plus some extra space
|
|
NewBufferSize = BufferSize + TextLen + 256;
|
|
|
|
NewBuffer = (PSTR) ((Buffer == NULL) ?
|
|
HeapAlloc(hHeap, 0, NewBufferSize):
|
|
HeapReAlloc(hHeap, 0, Buffer, NewBufferSize));
|
|
|
|
if (NewBuffer == NULL)
|
|
{
|
|
DbgPrint("Buffer alloc failed.\n");
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// How much was really allocated?
|
|
NewBufferSize = HeapSize(hHeap, 0, NewBuffer);
|
|
|
|
// If this was the first alloc, initialize
|
|
// buffer and account for terminating zero.
|
|
if (Buffer == NULL)
|
|
{
|
|
NewBuffer[0] = '\0';
|
|
BufferLeft = -1;
|
|
}
|
|
|
|
// Update buffer data
|
|
Buffer = NewBuffer;
|
|
BufferLeft += NewBufferSize - BufferSize;
|
|
BufferSize = NewBufferSize;
|
|
}
|
|
|
|
// Append new text
|
|
strcat(Buffer, Text);
|
|
BufferLeft -= TextLen;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// Discard any text left unused by Parse
|
|
void
|
|
OutputReader::DiscardOutput()
|
|
{
|
|
if (Buffer != NULL)
|
|
{
|
|
Buffer[0] = '\0';
|
|
BufferLeft = BufferSize - 1;
|
|
}
|
|
}
|
|
|
|
|
|
// Get a copy of the output buffer
|
|
HRESULT
|
|
OutputReader::GetOutputCopy(
|
|
PSTR *Copy
|
|
)
|
|
{
|
|
if (Copy == NULL) return E_INVALIDARG;
|
|
|
|
*Copy = NULL;
|
|
|
|
if (Buffer == NULL) return S_OK;
|
|
|
|
SIZE_T BufferLength = BufferSize - BufferLeft;
|
|
|
|
*Copy = (PSTR)HeapAlloc(hHeap, 0, BufferLength);
|
|
|
|
if (*Copy != NULL)
|
|
{
|
|
RtlCopyMemory(*Copy, Buffer, BufferLength);
|
|
return S_OK;
|
|
}
|
|
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// OutputParser
|
|
//
|
|
// General DebugOutputCallback class to parse output.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
OutputParser::ParseOutput(FLONG Flags)
|
|
{
|
|
HRESULT hr;
|
|
ULONG TextRemainingIndex;
|
|
|
|
if (Buffer == NULL)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
if (Flags & PARSE_OUTPUT_ALL)
|
|
{
|
|
UnparsedIndex = 0;
|
|
}
|
|
|
|
if ((hr = Parse(Buffer + UnparsedIndex, &TextRemainingIndex)) == S_OK)
|
|
{
|
|
UnparsedIndex += TextRemainingIndex;
|
|
|
|
if (((Flags & PARSE_OUTPUT_NO_DISCARD) == 0) &&
|
|
(UnparsedIndex > 0))
|
|
{
|
|
RtlMoveMemory(Buffer,
|
|
Buffer + UnparsedIndex,
|
|
BufferSize - UnparsedIndex - BufferLeft + 1);
|
|
|
|
BufferLeft += UnparsedIndex;
|
|
UnparsedIndex = 0;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
void
|
|
OutputParser::DiscardOutput()
|
|
{
|
|
OutputReader::DiscardOutput();
|
|
UnparsedIndex = 0;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// BasicOutputParser
|
|
//
|
|
// Basic DebugOutputCallback class to parse output looking for
|
|
// string keys and subsequent values.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
BasicOutputParser::LookFor(
|
|
PDEBUG_VALUE Value,
|
|
PCSTR Key,
|
|
ULONG Type,
|
|
ULONG Radix
|
|
)
|
|
{
|
|
if ((1 > strlen(Key)) ||
|
|
(strlen(Key) >= sizeof(Entries->Key)/sizeof(Entries->Key[0])))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (NumEntries >= MaxEntries) return E_OUTOFMEMORY;
|
|
|
|
Entries[NumEntries].Value = Value;
|
|
strcpy(Entries[NumEntries].Key, Key);
|
|
|
|
if (Value != NULL)
|
|
{
|
|
Value->Type = DEBUG_VALUE_INVALID;
|
|
|
|
if (Radix == PARSER_UNSPECIFIED_RADIX)
|
|
{
|
|
// Set Radix to Hex since value is likely to be an address
|
|
// Otherwise type a deafult of decimal.
|
|
Radix = (Type == DEBUG_VALUE_INT64) ? 16 : 10;
|
|
}
|
|
|
|
Entries[NumEntries].Type = Type;
|
|
Entries[NumEntries].Radix = Radix;
|
|
}
|
|
|
|
NumEntries++;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
BasicOutputParser::Parse(
|
|
IN PCSTR Text,
|
|
OUT OPTIONAL PULONG RemainderIndex
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PCSTR pStrUnused = Text;
|
|
PCSTR pStr;
|
|
ULONG EvalLen;
|
|
|
|
// DbgPrint("BasicOutputParser::Parse: Searching \"%s\"\n", pStrUnused);
|
|
|
|
while (CurEntry < NumEntries)
|
|
{
|
|
pStr = strstr(pStrUnused, Entries[CurEntry].Key);
|
|
if (pStr == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pStr += strlen(Entries[CurEntry].Key);
|
|
|
|
if (Entries[CurEntry].Value != NULL)
|
|
{
|
|
hr = Evaluate(Client,
|
|
pStr,
|
|
Entries[CurEntry].Type,
|
|
Entries[CurEntry].Radix,
|
|
Entries[CurEntry].Value,
|
|
&EvalLen);
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
DbgPrint("Evaluate returned HRESULT 0x%lx.\n", hr);
|
|
break;
|
|
}
|
|
|
|
// DbgPrint("BasicOutputParser::Parse: Found 0x%I64x after \"%s\".\n", Entries[CurEntry].Value->I64, Entries[CurEntry].Key);
|
|
|
|
pStr += EvalLen;
|
|
}
|
|
|
|
CurEntry++;
|
|
pStrUnused = pStr;
|
|
}
|
|
|
|
if (RemainderIndex != NULL)
|
|
{
|
|
*RemainderIndex = (ULONG)(pStrUnused - Text);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// BitFieldParser
|
|
//
|
|
// DebugOutputCallback class to parse bitfield type output
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BitFieldParser::BitFieldParser(
|
|
PDEBUG_CLIENT Client,
|
|
BitFieldInfo *BFI
|
|
) :
|
|
BitFieldReader(Client, 2)
|
|
{
|
|
if (BFI != NULL &&
|
|
BitFieldReader.LookFor(&BitPos, ": Pos ", DEBUG_VALUE_INT32) == S_OK &&
|
|
BitFieldReader.LookFor(&Bits, ", ", DEBUG_VALUE_INT32) == S_OK)
|
|
{
|
|
BitField = BFI;
|
|
BitField->Valid = FALSE;
|
|
BitField->BitPos = 0;
|
|
BitField->Bits = 0;
|
|
BitField->Mask = 0;
|
|
}
|
|
else
|
|
{
|
|
BitField = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
BitFieldParser::Parse(
|
|
IN PCSTR Text,
|
|
OUT OPTIONAL PULONG RemainderIndex
|
|
)
|
|
{
|
|
PCSTR pStrUnused = Text;
|
|
ULONG UnusedIndex = 0;
|
|
PCSTR pStrRemaining;
|
|
BitFieldInfo Field;
|
|
|
|
do
|
|
{
|
|
if (BitFieldReader.Complete() == S_OK)
|
|
{
|
|
BitFieldReader.Relook();
|
|
}
|
|
|
|
BitFieldReader.Parse(pStrUnused, &UnusedIndex);
|
|
pStrUnused += UnusedIndex;
|
|
|
|
if (BitFieldReader.Complete() != S_OK)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!BitField->Valid)
|
|
{
|
|
BitField->Valid = BitField->Compose(BitPos.I32, Bits.I32);
|
|
}
|
|
else
|
|
{
|
|
// Full extent of bit fields seen so far
|
|
BitField->Bits = Bits.I32 + BitPos.I32 - BitField->BitPos;
|
|
|
|
// Full mask of bit fields seen so far
|
|
if (Field.Compose(BitPos.I32, Bits.I32))
|
|
{
|
|
BitField->Mask |= Field.Mask;
|
|
}
|
|
}
|
|
|
|
// See if there is anything else we might want to parse.
|
|
pStrRemaining = pStrUnused;
|
|
while (isspace(*pStrRemaining) || *pStrRemaining == '\n')
|
|
{
|
|
pStrRemaining++;
|
|
}
|
|
} while (pStrRemaining != '\0');
|
|
|
|
if (RemainderIndex != NULL)
|
|
{
|
|
*RemainderIndex = (ULONG)(pStrUnused - Text);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// OutputFilter
|
|
//
|
|
// DebugOutputCallback class to filter output
|
|
// by skipping/replacing lines.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
OutputFilter::Output(
|
|
THIS_
|
|
IN ULONG Mask,
|
|
IN PCSTR Text
|
|
)
|
|
{
|
|
return (Outputing) ?
|
|
S_OK :
|
|
OutputReader::Output(Mask, Text);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
OutputFilter::Query(
|
|
PCSTR Query,
|
|
PDEBUG_VALUE Value,
|
|
ULONG Type,
|
|
ULONG Radix
|
|
)
|
|
{
|
|
if (Query == NULL) return E_INVALIDARG;
|
|
|
|
if (Buffer == NULL) return S_FALSE;
|
|
|
|
HRESULT hr;
|
|
BasicOutputParser Parser(Client, 1);
|
|
|
|
if ((hr = Parser.LookFor(Value, Query, Type, Radix)) == S_OK &&
|
|
(hr = Parser.Parse(Buffer, NULL)) == S_OK)
|
|
{
|
|
hr = Parser.Complete();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
OutputFilter::QuerySpec::QuerySpec(
|
|
ULONG QueryFlags,
|
|
PCSTR QueryText
|
|
)
|
|
{
|
|
Next = NULL;
|
|
Flags = QueryFlags;
|
|
QueryLen = (QueryText== NULL) ? 0 : strlen(QueryText);
|
|
if (QueryLen)
|
|
{
|
|
Query = new CHAR[QueryLen+1];
|
|
if (Query == NULL)
|
|
{
|
|
QueryLen = 0;
|
|
}
|
|
else
|
|
{
|
|
strcpy(Query, QueryText);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Query = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
OutputFilter::ReplacementSpec::ReplacementSpec(
|
|
ULONG QueryFlags,
|
|
PCSTR QueryText,
|
|
PCSTR ReplacementText
|
|
) : QuerySpec(QueryFlags, QueryText)
|
|
{
|
|
ReplacementLen = (ReplacementText== NULL) ? 0 : strlen(ReplacementText);
|
|
if (ReplacementLen)
|
|
{
|
|
Replacement = new CHAR[ReplacementLen+1];
|
|
if (Replacement == NULL)
|
|
{
|
|
ReplacementLen = 0;
|
|
}
|
|
else
|
|
{
|
|
strcpy(Replacement, ReplacementText);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Replacement = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
OutputFilter::QuerySpec **
|
|
OutputFilter::FindPrior(
|
|
ULONG Flags,
|
|
PCSTR Query,
|
|
QuerySpec **List
|
|
)
|
|
{
|
|
QuerySpec **Prior = List;
|
|
QuerySpec *Next;
|
|
|
|
for (Next = *Prior; Next != NULL; Next = Next->Next)
|
|
{
|
|
if (Flags > Next->Flags ||
|
|
(Flags == Next->Flags &&
|
|
strcmp(Query, Next->Query) >= 0))
|
|
{
|
|
break;
|
|
}
|
|
|
|
Prior = &Next->Next;
|
|
}
|
|
|
|
return Prior;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
OutputFilter::Replace(
|
|
ULONG Flags,
|
|
PCSTR Query,
|
|
PCSTR Replacement
|
|
)
|
|
{
|
|
if (Query == NULL ||
|
|
(Flags & OUTFILTER_REPLACE_LINE) == 0 ||
|
|
(Flags & OUTFILTER_REPLACE_LINE) == (OUTFILTER_REPLACE_BEFORE | OUTFILTER_REPLACE_AFTER))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Don't support replacing one query each time in a single line.
|
|
// This is ok if there can be no further replacements on a matching line.
|
|
if (((Flags & (OUTFILTER_REPLACE_ONCE | OUTFILTER_QUERY_ONE_LINE)) ==
|
|
(OUTFILTER_REPLACE_EVERY | OUTFILTER_QUERY_ONE_LINE)) &&
|
|
!(Flags & (OUTFILTER_REPLACE_AFTER | OUTFILTER_REPLACE_NEXT_LINE)))
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
// Set priority to level 0 if not specified.
|
|
if ((Flags & OUTFILTER_REPLACE_PRIORITY(7)) == 0)
|
|
{
|
|
Flags |= OUTFILTER_REPLACE_PRIORITY(0);
|
|
}
|
|
|
|
ReplacementSpec **PriorNext;
|
|
ReplacementSpec *Next;
|
|
ULONG ReplacementLen;
|
|
|
|
ReplacementLen = (Replacement == NULL) ? 0 : strlen(Replacement);
|
|
|
|
PriorNext = (ReplacementSpec **)FindPrior(Flags, Query, (QuerySpec**)&ReplaceList);
|
|
Next = *PriorNext;
|
|
|
|
if (Next != NULL &&
|
|
Flags == Next->Flags &&
|
|
strcmp(Query, Next->Query) == 0)
|
|
{
|
|
if (ReplacementLen == 0)
|
|
{
|
|
if (Next->Replacement != NULL)
|
|
{
|
|
Next->Replacement[0] = '\0';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ReplacementLen > Next->ReplacementLen)
|
|
{
|
|
PSTR NewReplacement = new CHAR[ReplacementLen+1];
|
|
|
|
if (NewReplacement == NULL) return E_OUTOFMEMORY;
|
|
|
|
if (Next->Replacement != NULL)
|
|
{
|
|
delete[] Next->Replacement;
|
|
Next->Replacement = NewReplacement;
|
|
}
|
|
}
|
|
strcpy(Next->Replacement, Replacement);
|
|
}
|
|
Next->ReplacementLen = ReplacementLen;
|
|
}
|
|
else
|
|
{
|
|
ReplacementSpec *NewQuery;
|
|
|
|
NewQuery = new ReplacementSpec(Flags, Query, Replacement);
|
|
|
|
if (NewQuery == NULL) return E_OUTOFMEMORY;
|
|
|
|
if (NewQuery->Query == NULL ||
|
|
(ReplacementLen && NewQuery->Replacement == NULL))
|
|
{
|
|
delete NewQuery;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
NewQuery->Next = Next;
|
|
*PriorNext = NewQuery;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
OutputFilter::Skip(
|
|
ULONG Flags,
|
|
PCSTR Query
|
|
)
|
|
{
|
|
if (Query == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
QuerySpec **PriorNext;
|
|
QuerySpec *Next;
|
|
|
|
PriorNext = FindPrior(Flags, Query, &SkipList);
|
|
Next = *PriorNext;
|
|
|
|
if (Next == NULL ||
|
|
Flags != Next->Flags ||
|
|
strcmp(Query, Next->Query) != 0)
|
|
{
|
|
QuerySpec *NewQuery;
|
|
|
|
NewQuery = new QuerySpec(Flags, Query);
|
|
|
|
if (NewQuery == NULL) return E_OUTOFMEMORY;
|
|
|
|
if (NewQuery->Query == NULL)
|
|
{
|
|
delete NewQuery;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
NewQuery->Next = Next;
|
|
*PriorNext = NewQuery;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
OutputFilter::QuerySpec *
|
|
OutputFilter::FindMatch(
|
|
PCSTR Text,
|
|
QuerySpec *List,
|
|
ULONG Start,
|
|
ULONG Flags,
|
|
PULONG MatchPos
|
|
)
|
|
{
|
|
ULONG Remaining = strlen(Text);
|
|
PCSTR Search;
|
|
QuerySpec *Match;
|
|
|
|
if (MatchPos != NULL) *MatchPos = 0;
|
|
|
|
if (List == NULL ||
|
|
Text == NULL ||
|
|
(Remaining = strlen(Text)) <= Start) return NULL;
|
|
|
|
Search = Text + Start;
|
|
Remaining -= Start;
|
|
|
|
do
|
|
{
|
|
for (Match = List; Match != NULL; Match = Match->Next)
|
|
{
|
|
if (Match->Flags & OUTFILTER_QUERY_ENABLED &&
|
|
Remaining >= Match->QueryLen &&
|
|
strncmp(Search, Match->Query, Match->QueryLen) == 0)
|
|
{
|
|
if (Match->Flags & OUTFILTER_QUERY_WHOLE_WORD)
|
|
{
|
|
if ((Search > Text && iscsym(Search[-1])) ||
|
|
iscsym(Search[Match->QueryLen]))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
/*
|
|
if (Match->Flags & OUTFILTER_QUERY_ONE_LINE)
|
|
{
|
|
Match->Flags &= ~OUTFILTER_QUERY_ENABLED;
|
|
}
|
|
*/
|
|
Match->Flags |= OUTFILTER_QUERY_HIT;
|
|
|
|
if (MatchPos != NULL)
|
|
{
|
|
*MatchPos = (ULONG)(Search - Text);
|
|
}
|
|
|
|
return Match;
|
|
}
|
|
}
|
|
|
|
Search++;
|
|
Remaining--;
|
|
} while (Remaining &&
|
|
!(Flags & OUTFILTER_FINDMATCH_AT_START));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
OutputFilter::OutputText(
|
|
OutputControl *OutCtl,
|
|
ULONG Mask
|
|
)
|
|
{
|
|
// 1. Get line of read buffer
|
|
// 2. Search for a skip query match
|
|
// 3. Search for any/all replace query matches
|
|
|
|
if (Buffer == NULL) return S_OK;
|
|
|
|
HRESULT hr;
|
|
PSTR pNextLine;
|
|
CHAR EndChar;
|
|
PSTR pFilter;
|
|
|
|
if (OutCtl == NULL)
|
|
{
|
|
OutCtl = new OutputControl(Client);
|
|
|
|
if (OutCtl == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OutCtl->AddRef();
|
|
}
|
|
|
|
// Can we quickly just output all text?
|
|
if (SkipList == NULL && ReplaceList == NULL)
|
|
{
|
|
hr = OutCtl->Output(Mask, Buffer);
|
|
OutCtl->Release();
|
|
return hr;
|
|
}
|
|
|
|
Outputing = TRUE;
|
|
|
|
// Enable all queries and reset hit markings.
|
|
QuerySpec *Query;
|
|
for (Query = SkipList; Query != NULL; Query = Query->Next)
|
|
{
|
|
Query->Flags = (Query->Flags & ~OUTFILTER_QUERY_HIT) | OUTFILTER_QUERY_ENABLED;
|
|
}
|
|
for (Query = (QuerySpec *)ReplaceList; Query != NULL; Query = Query->Next)
|
|
{
|
|
Query->Flags = (Query->Flags & ~OUTFILTER_QUERY_HIT) | OUTFILTER_QUERY_ENABLED;
|
|
}
|
|
|
|
hr = S_OK;
|
|
pNextLine = Buffer;
|
|
|
|
while (hr == S_OK && *pNextLine != '\0')
|
|
{
|
|
if (OutCtl->GetInterrupt() == S_OK)
|
|
{
|
|
OutCtl->SetInterrupt(DEBUG_INTERRUPT_PASSIVE);
|
|
break;
|
|
}
|
|
|
|
pFilter = pNextLine;
|
|
|
|
// Find end of this line
|
|
while ((*pNextLine != '\0') &&
|
|
(*pNextLine != '\n') &&
|
|
(*pNextLine != '\r') &&
|
|
(*pNextLine != '\f'))
|
|
{
|
|
pNextLine++;
|
|
}
|
|
|
|
EndChar = *pNextLine;
|
|
*pNextLine = '\0';
|
|
|
|
// Search for a skip match
|
|
Query = FindMatch(pFilter, SkipList);
|
|
|
|
if (Query != NULL)
|
|
{
|
|
DbgPrint("Skipping line with %s.\n", Query->Query);
|
|
|
|
if (Query->Flags & OUTFILTER_QUERY_ONE_LINE)
|
|
{
|
|
Query->Flags &= ~OUTFILTER_QUERY_ENABLED;
|
|
}
|
|
|
|
*pNextLine = EndChar;
|
|
if (EndChar != '\0')
|
|
{
|
|
pNextLine++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PSTR pFilterLine = pFilter;
|
|
ULONG MatchPos = 0;
|
|
ReplacementSpec *Replace;
|
|
|
|
for (Replace = ReplaceList;
|
|
Replace != NULL;
|
|
Replace = (ReplacementSpec *)Replace->Next)
|
|
{
|
|
if (!(Replace->Flags & (OUTFILTER_QUERY_ONE_LINE | OUTFILTER_QUERY_ENABLED)))
|
|
{
|
|
Replace->Flags |= OUTFILTER_QUERY_ENABLED;
|
|
}
|
|
}
|
|
|
|
while (pFilter < pNextLine &&
|
|
(Replace = (ReplacementSpec *)FindMatch(pFilterLine,
|
|
ReplaceList,
|
|
MatchPos,
|
|
OUTFILTER_FINDMATCH_DEFAULT,
|
|
&MatchPos)) != NULL)
|
|
{
|
|
if (Replace->Flags & (OUTFILTER_QUERY_ONE_LINE | OUTFILTER_REPLACE_ONCE))
|
|
{
|
|
Replace->Flags &= ~OUTFILTER_QUERY_ENABLED;
|
|
}
|
|
|
|
if (Replace->Flags & OUTFILTER_REPLACE_BEFORE)
|
|
{
|
|
if (Replace->ReplacementLen)
|
|
{
|
|
hr = OutCtl->Output(Mask, "%s", Replace->Replacement);
|
|
}
|
|
|
|
pFilter = pFilterLine + MatchPos;
|
|
|
|
if (!(Replace->Flags & OUTFILTER_REPLACE_NEXT_LINE) &&
|
|
!(Replace->Flags & OUTFILTER_REPLACE_ONCE) &&
|
|
!(Replace->Flags & OUTFILTER_REPLACE_TO_END))
|
|
{
|
|
// This replacement leaves the query text intact.
|
|
// Hence this query will keep matching; so, look for
|
|
// another query which will actually modify the
|
|
// query text or the text following it.
|
|
Replace = (ReplacementSpec *)Replace->Next;
|
|
|
|
while (Replace != NULL &&
|
|
Replace->Flags & OUTFILTER_REPLACE_BEFORE)
|
|
{
|
|
Replace = (ReplacementSpec *)Replace->Next;
|
|
}
|
|
|
|
Replace = (ReplacementSpec *)
|
|
FindMatch(pFilterLine,
|
|
Replace,
|
|
MatchPos,
|
|
OUTFILTER_FINDMATCH_AT_START |
|
|
OUTFILTER_FINDMATCH_NO_MARK);
|
|
|
|
if (Replace == NULL)
|
|
{
|
|
// Advance MatchPos, but not filtered text.
|
|
// This unfiltered text may yet be replaced.
|
|
MatchPos++;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(Replace->Flags & OUTFILTER_REPLACE_BEFORE))
|
|
{
|
|
CHAR TempHolder;
|
|
ULONG BeginReplacePos = MatchPos;
|
|
|
|
if (!(Replace->Flags & OUTFILTER_REPLACE_THIS))
|
|
{
|
|
BeginReplacePos += Replace->QueryLen;
|
|
}
|
|
|
|
TempHolder = pFilterLine[BeginReplacePos];
|
|
pFilterLine[BeginReplacePos] = '\0';
|
|
|
|
if ((hr = OutCtl->Output(Mask, "%s", pFilter)) == S_OK &&
|
|
Replace->ReplacementLen)
|
|
{
|
|
hr = OutCtl->Output(Mask, "%s", Replace->Replacement);
|
|
}
|
|
|
|
pFilterLine[BeginReplacePos] = TempHolder;
|
|
}
|
|
|
|
if (Replace->Flags & OUTFILTER_REPLACE_AFTER)
|
|
{
|
|
pFilter = pNextLine;
|
|
}
|
|
else
|
|
{
|
|
if (Replace->Flags & OUTFILTER_REPLACE_THIS)
|
|
{
|
|
MatchPos += Replace->QueryLen;
|
|
}
|
|
pFilter = pFilterLine + MatchPos;
|
|
}
|
|
|
|
if (Replace->Flags & OUTFILTER_REPLACE_NEXT_LINE) break;
|
|
}
|
|
|
|
*pNextLine = EndChar;
|
|
if (EndChar != '\0')
|
|
{
|
|
pNextLine++;
|
|
|
|
// Include any following zero length lines
|
|
while ((*pNextLine == '\n') ||
|
|
(*pNextLine == '\r') ||
|
|
(*pNextLine == '\f'))
|
|
{
|
|
pNextLine++;
|
|
}
|
|
|
|
EndChar = *pNextLine;
|
|
*pNextLine = '\0';
|
|
}
|
|
|
|
// Output remaining portion of filtered line
|
|
hr = OutCtl->Output(Mask, pFilter);
|
|
|
|
if (EndChar != '\0')
|
|
{
|
|
*pNextLine = EndChar;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
Outputing = FALSE;
|
|
|
|
OutCtl->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|