windows-nt/Source/XPSP1/NT/sdktools/debuggers/windbg/statebuf.cpp
2020-09-26 16:20:57 +08:00

1621 lines
38 KiB
C++

//----------------------------------------------------------------------------
//
// Debuggee state buffers.
//
// Copyright (C) Microsoft Corporation, 1999-2000.
//
//----------------------------------------------------------------------------
#include "precomp.hxx"
#pragma hdrstop
#include <malloc.h>
#if 0
#define DBG_BUFFER
#endif
StateBuffer g_UiOutputCapture(256);
//----------------------------------------------------------------------------
//
// StateBuffer.
//
//----------------------------------------------------------------------------
StateBuffer::StateBuffer(ULONG ChangeBy)
{
Dbg_InitializeCriticalSection(&m_Lock);
Flink = NULL;
Blink = NULL;
m_ChangeBy = ChangeBy;
m_Win = NULL;
m_UpdateTypes = 0;
m_UpdateType = UPDATE_BUFFER;
m_UpdateMessage = WU_UPDATE;
m_Status = S_OK;
// The buffer must start out with an outstanding
// read request to indicate that it doesn't have valid content.
m_ReadRequest = 1;
m_ReadDone = 0;
SetNoData();
}
StateBuffer::~StateBuffer(void)
{
Free();
Dbg_DeleteCriticalSection(&m_Lock);
}
PVOID
StateBuffer::AddData(ULONG Len)
{
PVOID Ret;
ULONG Needed;
Needed = m_DataUsed + Len;
if (Needed > m_DataLen)
{
if (Resize(Needed) != S_OK)
{
return NULL;
}
}
Ret = m_Data + m_DataUsed;
m_DataUsed += Len;
return Ret;
}
BOOL
StateBuffer::AddString(PCSTR Str, BOOL SoftTerminate)
{
ULONG Len = strlen(Str) + 1;
PSTR Buf = (PSTR)AddData(Len);
if (Buf != NULL)
{
memcpy(Buf, Str, Len);
if (SoftTerminate)
{
// Back up to pack strings without intervening
// terminators. Buffer isn't shrunk so terminator
// remains to terminate the overall buffer until
// new data.
RemoveTail(1);
}
return TRUE;
}
return FALSE;
}
void
StateBuffer::RemoveHead(ULONG Len)
{
if (Len > m_DataUsed)
{
Len = m_DataUsed;
}
ULONG Left = m_DataUsed - Len;
if (Len > 0 && Left > 0)
{
memmove(m_Data, (PBYTE)m_Data + Len, Left);
}
m_DataUsed = Left;
}
void
StateBuffer::RemoveMiddle(ULONG Start, ULONG Len)
{
if (Start >= m_DataUsed)
{
return;
}
if (Start + Len > m_DataUsed)
{
Len = m_DataUsed - Start;
}
ULONG Left = m_DataUsed - Len - Start;
if (Len > 0 && Left > 0)
{
memmove(m_Data + Start, (PBYTE)m_Data + Start + Len, Left);
}
m_DataUsed = Start + Left;
}
void
StateBuffer::RemoveTail(ULONG Len)
{
if (Len > m_DataUsed)
{
Len = m_DataUsed;
}
m_DataUsed -= Len;
}
HRESULT
StateBuffer::Resize(ULONG Len)
{
PBYTE NewData;
ULONG NewLen;
if (Len == m_DataLen)
{
return S_OK;
}
NewLen = m_DataLen;
if (Len < NewLen)
{
do
{
NewLen -= m_ChangeBy;
}
while (NewLen > Len);
NewLen += m_ChangeBy;
}
else
{
do
{
NewLen += m_ChangeBy;
}
while (NewLen < Len);
}
#if DBG
// Force every resize to go to a new memory block
// and backfill the old block to make it obvious
// when pointers are being held across resizes.
if (NewLen == 0)
{
free(m_Data);
NewData = NULL;
}
else
{
NewData = (PBYTE)malloc(NewLen);
if (NewData != NULL && m_Data != NULL)
{
ULONG OldLen = _msize(m_Data);
ULONG CopyLen = min(OldLen, NewLen);
memcpy(NewData, m_Data, CopyLen);
memset(m_Data, 0xfe, OldLen);
free(m_Data);
}
}
#else
NewData = (PBYTE)realloc(m_Data, NewLen);
#endif
if (NewLen > 0 && NewData == NULL)
{
return E_OUTOFMEMORY;
}
m_Data = NewData;
m_DataLen = NewLen;
return S_OK;
}
void
StateBuffer::Free(void)
{
free(m_Data);
SetNoData();
}
HRESULT
StateBuffer::Update(void)
{
ULONG Request;
// First sample the request value. This
// value will be set as the done value if
// a read is performed and therefore must
// be sampled first to make it the most
// conservative estimate of what was done.
Request = m_ReadRequest;
if (Request != m_ReadDone)
{
LockStateBuffer(this);
m_Status = ReadState();
// Always mark the buffer with the latest completed
// sequence so that errors get picked up in addition
// to successful reads.
m_ReadDone = Request;
#ifdef DBG_BUFFER
if (m_Status != S_OK)
{
DebugPrint("State buffer %p:%d fill failed, 0x%X\n",
this, m_enumType, m_Status);
}
if (m_ReadRequest != m_ReadDone)
{
DebugPrint("State buffer %p:%d fill out of date, "
"req %X, done %X\n",
this, m_enumType, m_ReadRequest, m_ReadDone);
}
#endif
UnlockStateBuffer(this);
if (m_Win != NULL)
{
PostMessage(m_Win, m_UpdateMessage, 0, 0);
}
if (m_Status == S_OK && m_UpdateTypes)
{
UpdateBufferWindows(m_UpdateTypes, m_UpdateType);
}
}
return m_Status;
}
void
StateBuffer::UiRequestRead(void)
{
//
// Called on the UI thread.
//
// No need to lock here as a race for
// the read request value is not a problem.
// If the read request value is sampled early
// and a read request does not occur it'll
// happen the next time around since this routine
// also wakes the engine.
RequestRead();
UpdateEngine();
}
HRESULT
StateBuffer::UiLockForRead(void)
{
ULONG Done;
//
// Called on the UI thread.
//
// First sample the read count without locking.
Done = m_ReadDone;
// Now check whether the request is newer than the
// last read done. The UI thread is the only thread
// that updates the request count so this should be safe.
if (Done == m_ReadRequest)
{
HRESULT Status;
LockStateBuffer(this);
Status = m_Status;
if (FAILED(Status))
{
// If there was an error when filling the buffer
// return it and leave the buffer unlocked.
UnlockStateBuffer(this);
return Status;
}
// Buffer is locked and valid.
return S_OK;
}
else
{
// Buffer content is out-of-date so don't lock.
// Make sure the engine is active to update the buffer.
return S_FALSE;
}
}
HRESULT
StateBuffer::ReadState(void)
{
return S_OK;
}
//----------------------------------------------------------------------------
//
// OutputToStateBuffer.
//
//----------------------------------------------------------------------------
HRESULT
OutputToStateBuffer::Start(BOOL Empty)
{
if (Empty)
{
m_Buffer->Empty();
}
m_DataStart = m_Buffer->GetDataLen();
m_Status = S_OK;
m_NewLineCount = 0;
m_PartialLine = 0;
return S_OK;
}
HRESULT
OutputToStateBuffer::End(BOOL RemoveLastNewLine)
{
if (RemoveLastNewLine && m_PartialLine == 0)
{
// Remove the final newline so that richedit doesn't leave
// a blank line at the bottom of the window when the
// text is displayed.
*((PSTR)m_Buffer->GetDataBuffer() + m_Buffer->GetDataLen() - 1) = 0;
}
else
{
// Every individual line allocates space for a terminator
// and then backs up. This requested space should always
// be available.
PVOID Data = m_Buffer->AddData(1);
Assert(Data != NULL);
}
return m_Status;
}
void
OutputToStateBuffer::ReplaceChar(char From, char To)
{
PSTR Buf = (PSTR)m_Buffer->GetDataBuffer() + m_DataStart;
PSTR End = (PSTR)m_Buffer->GetDataBuffer() + m_Buffer->GetDataLen();
while (Buf < End)
{
if (*Buf == From)
{
*Buf = To;
}
Buf++;
}
}
STDMETHODIMP
OutputToStateBuffer::Output(
THIS_
IN ULONG Mask,
IN PCSTR Text
)
{
if (!m_Buffer->AddString(Text, TRUE))
{
return E_OUTOFMEMORY;
}
AddLines(Text);
return S_OK;
}
void
OutputToStateBuffer::AddLines(PCSTR Start)
{
PCSTR LastNl = Start;
PCSTR Nl;
for (;;)
{
Nl = strchr(LastNl, '\n');
if (Nl == NULL)
{
break;
}
m_NewLineCount++;
LastNl = Nl + 1;
}
// If the last newline wasn't at the end of the text there's
// a partial line which needs to count in the line count
// but only until a finishing newline comes in.
m_PartialLine = *LastNl != 0 ? 1 : 0;
}
OutputToStateBuffer g_OutStateBuf;
OutputToStateBuffer g_UiOutStateBuf;
//----------------------------------------------------------------------------
//
// Dynamic state buffers.
//
//----------------------------------------------------------------------------
LIST_ENTRY g_StateList;
DBG_CRITICAL_SECTION g_QuickLock;
ULONG64 g_CodeIp;
char g_CodeFileFound[MAX_SOURCE_PATH];
char g_CodeSymFile[MAX_SOURCE_PATH];
ULONG g_CodeLine;
BOOL g_CodeUserActivated;
ULONG g_CodeBufferSequence;
ULONG64 g_EventIp;
ULONG64 g_EventReturnAddr = DEBUG_INVALID_OFFSET;
ULONG g_CurProcessId, g_CurProcessSysId;
ULONG g_CurThreadId, g_CurThreadSysId;
ULONG g_EventBufferRequest;
ULONG g_EventBufferDone;
void
FillCodeBuffer(ULONG64 Ip, BOOL UserActivated)
{
char File[MAX_SOURCE_PATH];
char Found[MAX_SOURCE_PATH];
ULONG Line;
ULONG64 Disp;
// Fill local information rather than global information
// to avoid changing the global information until all
// event information has been collected.
if (g_pDbgSymbols->
GetLineByOffset(Ip, &Line, File, sizeof(File), NULL, &Disp) != S_OK)
{
// This will be hit if the buffer is too small
// to hold the filename. This could be switched to dynamically
// allocate the filename buffer but that seems like overkill.
File[0] = 0;
Found[0] = 0;
}
else
{
// Source information is one-based but the source
// window lines are zero-based.
Line--;
// Look up the reported file along the source path.
// XXX drewb - Use first-match and then element walk to
// determine ambiguities and display resolution UI.
if (g_pLocSymbols->
FindSourceFile(0, File,
DEBUG_FIND_SOURCE_BEST_MATCH |
DEBUG_FIND_SOURCE_FULL_PATH,
NULL, Found, sizeof(Found), NULL) != S_OK)
{
// XXX drewb - Display UI instead of just disabling source?
Found[0] = 0;
}
}
// Now that all of the information has been collected
// take the lock and update the global state.
Dbg_EnterCriticalSection(&g_QuickLock);
g_CodeIp = Ip;
strcpy(g_CodeFileFound, Found);
strcpy(g_CodeSymFile, File);
g_CodeLine = Line;
g_CodeUserActivated = UserActivated;
g_CodeBufferSequence++;
Dbg_LeaveCriticalSection(&g_QuickLock);
// Wake up the UI thread to process the new event location.
UpdateUi();
}
void
FillEventBuffer(void)
{
ULONG64 Ip;
ULONG64 ScopeIp;
ULONG64 RetAddr;
ULONG ProcessId, ProcessSysId;
ULONG ThreadId, ThreadSysId;
ULONG Done = g_EventBufferRequest;
HRESULT Status;
if (g_pDbgRegisters->GetInstructionOffset(&Ip) != S_OK ||
g_pDbgControl->GetReturnOffset(&RetAddr) != S_OK ||
g_pDbgSystem->GetCurrentProcessId(&ProcessId) != S_OK ||
g_pDbgSystem->GetCurrentThreadId(&ThreadId) != S_OK)
{
return;
}
// Kernel mode doesn't implement system IDs as the process
// and threads are fakes and not real system objects. Just
// use zero if E_NOTIMPL is returned.
if ((Status = g_pDbgSystem->
GetCurrentProcessSystemId(&ProcessSysId)) != S_OK)
{
if (Status == E_NOTIMPL)
{
ProcessSysId = 0;
}
else
{
// Unexpected error, must be a real problem.
return;
}
}
if ((Status = g_pDbgSystem->
GetCurrentThreadSystemId(&ThreadSysId)) != S_OK)
{
if (Status == E_NOTIMPL)
{
ThreadSysId = 0;
}
else
{
// Unexpected error, must be a real problem.
return;
}
}
// Fill code buffer with scope Ip
if (g_pDbgSymbols->GetScope(&ScopeIp, NULL, NULL, 0) != S_OK)
{
return;
}
FillCodeBuffer(ScopeIp, FALSE);
g_EventIp = Ip;
g_EventReturnAddr = RetAddr;
g_CurProcessId = ProcessId;
g_CurProcessSysId = ProcessSysId;
g_CurThreadId = ThreadId;
g_CurThreadSysId = ThreadSysId;
if (!g_CodeLevelLocked)
{
ULONG CodeLevel;
if (g_CodeFileFound[0] == 0)
{
// No source so switch to assembly mode.
CodeLevel = DEBUG_LEVEL_ASSEMBLY;
}
else
{
if (GetSrcMode_StatusBar())
{
CodeLevel = DEBUG_LEVEL_SOURCE;
}
else
{
CodeLevel = DEBUG_LEVEL_ASSEMBLY;
}
}
g_IgnoreCodeLevelChange = TRUE;
g_pDbgControl->SetCodeLevel(CodeLevel);
g_IgnoreCodeLevelChange = FALSE;
}
g_EventBufferDone = Done;
PostMessage(g_hwndFrame, WU_UPDATE, UPDATE_EXEC, 0);
}
class BpStateBuffer : public StateBuffer
{
public:
BpStateBuffer(void) :
StateBuffer(256)
{
m_enumType = BP_BIT;
m_UpdateMessage = LB_RESETCONTENT;
m_UpdateTypes = (1 << DOC_WINDOW) | (1 << DISASM_WINDOW);
m_UpdateType = UPDATE_BP;
}
virtual HRESULT ReadState(void);
};
// #define DBG_BPBUF
#define BP_EXTRA_ENTRIES 8
ULONG g_BpCount;
BpStateBuffer g_PrivateBpBuffer;
StateBuffer* g_BpBuffer = &g_PrivateBpBuffer;
ULONG g_BpTextOffset;
HRESULT
BpStateBuffer::ReadState(void)
{
HRESULT Status;
ULONG Count;
ULONG TextOffset;
ULONG FileOffset;
BpBufferData* Data;
ULONG i;
PDEBUG_BREAKPOINT_PARAMETERS Params;
char FileBuf[MAX_SOURCE_PATH];
// Reserve room for BP descriptions in front of the text.
// When doing so, reserve extra slots to allow for free
// slots the next time around.
Empty();
Status = g_pDbgControl->GetNumberBreakpoints(&Count);
if (Status != S_OK)
{
return Status;
}
TextOffset = (Count + BP_EXTRA_ENTRIES) * sizeof(BpBufferData);
Data = (BpBufferData*)AddData(TextOffset);
if (Data == NULL)
{
return E_OUTOFMEMORY;
}
// Allocate a temporary buffer for bulk breakpoint retrieval.
Params = new DEBUG_BREAKPOINT_PARAMETERS[Count];
if (Params == NULL)
{
return E_OUTOFMEMORY;
}
// GetBreakpointParameters can return S_FALSE when there
// are hidden breakpoints.
if (FAILED(Status = g_pDbgControl->
GetBreakpointParameters(Count, NULL, 0, Params)) != S_OK)
{
delete Params;
return Status;
}
// Iterate over breakpoints and retrieve offsets for
// all execution breakpoints.
// Take advantage of the fact that Empty does not actually
// discard data to distinguish changed breakpoints from
// unchanged breakpoints.
ULONG Write = 0;
for (i = 0; i < Count; i++)
{
if (Params[i].Id == DEBUG_ANY_ID ||
Params[i].Offset == DEBUG_INVALID_OFFSET ||
(Params[i].BreakType == DEBUG_BREAKPOINT_DATA &&
Params[i].DataAccessType != DEBUG_BREAK_EXECUTE))
{
// Not a breakpoint that we care about, skip.
continue;
}
// Check and see if this offset is already known.
ULONG Match;
for (Match = 0; Match < g_BpCount; Match++)
{
// NOTE: This compresses duplicate breakpoints
// with a first-writer-wins on the ID.
if (Data[Match].Offset == Params[i].Offset)
{
break;
}
}
if (Match < g_BpCount)
{
BpBufferData Temp;
// Keep the old record for this offset to minimize
// UI updates.
if (Match > Write)
{
Temp = Data[Match];
Data[Match] = Data[Write];
Data[Write] = Temp;
Match = Write;
}
#ifdef DBG_BPBUF
DebugPrint("Match %d:%I64X %d:%d into %d\n",
Params[i].Id, Params[i].Offset, Data[Match].Id,
Match, Write);
#endif
Write++;
// We mostly ignore flag differences. ENABLED, however,
// is important to have accurate and in the most-enabled
// way.
if ((Data[Match].Flags ^ Params[i].Flags) &
DEBUG_BREAKPOINT_ENABLED)
{
if (Data[Match].Id != Params[i].Id)
{
Data[Match].Flags |=
Params[i].Flags & DEBUG_BREAKPOINT_ENABLED;
}
else
{
Data[Match].Flags = Params[i].Flags;
}
Data[Match].Thread = Params[i].MatchThread;
Data[Match].Sequence = g_CommandSequence;
}
}
else
{
// Fill in a new record. This will potentially destroy
// an old record and so reduce the effectivess of delta
// checking but the front of the buffer is packed
// with the extra entries to handle these changes hopefully
// without eating into the actual entries.
#ifdef DBG_BPBUF
DebugPrint("Write %d:%I64X into %d\n", Params[i].Id,
Params[i].Offset, Write);
#endif
Data[Write].Offset = Params[i].Offset;
Data[Write].Id = Params[i].Id;
Data[Write].Flags = Params[i].Flags;
Data[Write].Thread = Params[i].MatchThread;
Data[Write].Sequence = g_CommandSequence;
Write++;
}
}
delete Params;
// Pack unused entries at the front of the buffer so that
// they get used first in the next delta computation.
Count += BP_EXTRA_ENTRIES;
#ifdef DBG_BPBUF
DebugPrint("Used %d of %d\n", Write, Count);
#endif
if (Write < Count)
{
ULONG Extra = Count - Write;
memmove(Data + Extra, Data, Write * sizeof(*Data));
for (i = 0; i < Extra; i++)
{
Data[i].Offset = DEBUG_INVALID_OFFSET;
}
}
//
// Now go through the valid breakpoints and look up
// what file they're in, if any.
//
for (i = 0; i < Count; i++)
{
ULONG Line;
PSTR FileSpace;
// Refresh every time since growth may have caused
// a realloc.
Data = (BpBufferData*)m_Data;
Data[i].FileOffset = 0;
if (Data[i].Offset != DEBUG_INVALID_OFFSET &&
g_pDbgSymbols->GetLineByOffset(Data[i].Offset, &Line,
FileBuf, sizeof(FileBuf), NULL,
NULL) == S_OK)
{
// Do this first before m_DataUsed is updated and
// Data is invalidated.
Data[i].FileOffset = m_DataUsed;
FileSpace = (PSTR)AddData(sizeof(Line) + strlen(FileBuf) + 1);
if (FileSpace == NULL)
{
return E_OUTOFMEMORY;
}
*(ULONG UNALIGNED *)FileSpace = Line;
FileSpace += sizeof(Line);
strcpy(FileSpace, FileBuf);
}
}
TextOffset = m_DataUsed;
g_OutStateBuf.SetBuffer(this);
if ((Status = g_OutStateBuf.Start(FALSE)) != S_OK)
{
return Status;
}
// Get breakpoint list.
Status = g_pOutCapControl->Execute(DEBUG_OUTCTL_THIS_CLIENT |
DEBUG_OUTCTL_OVERRIDE_MASK |
DEBUG_OUTCTL_NOT_LOGGED,
"bl", DEBUG_EXECUTE_NOT_LOGGED |
DEBUG_EXECUTE_NO_REPEAT);
if (Status == S_OK)
{
Status = g_OutStateBuf.End(FALSE);
if (Status == S_OK)
{
// Separate lines by nulls to make them easier
// to process as individual strings.
g_OutStateBuf.ReplaceChar('\n', 0);
}
}
else
{
g_OutStateBuf.End(FALSE);
}
if (Status == S_OK)
{
g_BpCount = Count;
g_BpTextOffset = TextOffset;
}
return Status;
}
class BpCmdsStateBuffer : public StateBuffer
{
public:
BpCmdsStateBuffer(void) :
StateBuffer(256)
{
m_enumType = BP_CMDS_BIT;
}
virtual HRESULT ReadState(void);
};
BpCmdsStateBuffer g_PrivateBpCmdsBuffer;
StateBuffer* g_BpCmdsBuffer = &g_PrivateBpCmdsBuffer;
HRESULT
BpCmdsStateBuffer::ReadState(void)
{
HRESULT Status;
g_OutStateBuf.SetBuffer(this);
if ((Status = g_OutStateBuf.Start(TRUE)) != S_OK)
{
return Status;
}
// Get breakpoint commands.
Status = g_pOutCapControl->Execute(DEBUG_OUTCTL_THIS_CLIENT |
DEBUG_OUTCTL_OVERRIDE_MASK |
DEBUG_OUTCTL_NOT_LOGGED,
".bpcmds -e -m -p 0",
DEBUG_EXECUTE_NOT_LOGGED |
DEBUG_EXECUTE_NO_REPEAT);
if (Status == S_OK)
{
Status = g_OutStateBuf.End(FALSE);
}
else
{
g_OutStateBuf.End(FALSE);
}
return Status;
}
class FilterTextStateBuffer : public StateBuffer
{
public:
FilterTextStateBuffer(void) :
StateBuffer(256)
{
m_enumType = MINVAL_WINDOW;
m_UpdateMessage = 0;
}
virtual HRESULT ReadState(void);
};
FilterTextStateBuffer g_PrivateFilterTextBuffer;
StateBuffer* g_FilterTextBuffer = &g_PrivateFilterTextBuffer;
HRESULT
FilterTextStateBuffer::ReadState(void)
{
HRESULT Status;
ULONG SpecEvents, SpecEx, ArbEx;
ULONG i;
PSTR Text;
if ((Status = g_pDbgControl->
GetNumberEventFilters(&SpecEvents, &SpecEx, &ArbEx)) != S_OK)
{
return Status;
}
Empty();
DEBUG_SPECIFIC_FILTER_PARAMETERS SpecParams;
for (i = 0; i < SpecEvents; i++)
{
if ((Status = g_pDbgControl->
GetSpecificFilterParameters(i, 1, &SpecParams)) != S_OK)
{
return Status;
}
if (SpecParams.TextSize == 0)
{
// Put a terminator in anyway to keep the
// indexing correct.
if ((Text = (PSTR)AddData(1)) == NULL)
{
return E_OUTOFMEMORY;
}
*Text = 0;
}
else
{
if ((Text = (PSTR)AddData(SpecParams.TextSize)) == NULL)
{
return E_OUTOFMEMORY;
}
if ((Status = g_pDbgControl->
GetEventFilterText(i, Text, SpecParams.TextSize,
NULL)) != S_OK)
{
return Status;
}
}
}
DEBUG_EXCEPTION_FILTER_PARAMETERS ExParams;
for (i = 0; i < SpecEx; i++)
{
if ((Status = g_pDbgControl->
GetExceptionFilterParameters(1, NULL, i + SpecEvents,
&ExParams)) != S_OK)
{
return Status;
}
if (ExParams.TextSize == 0)
{
// Put a terminator in anyway to keep the
// indexing correct.
if ((Text = (PSTR)AddData(1)) == NULL)
{
return E_OUTOFMEMORY;
}
*Text = 0;
}
else
{
if ((Text = (PSTR)AddData(ExParams.TextSize)) == NULL)
{
return E_OUTOFMEMORY;
}
if ((Status = g_pDbgControl->
GetEventFilterText(i + SpecEvents, Text, ExParams.TextSize,
NULL)) != S_OK)
{
return Status;
}
}
}
return S_OK;
}
class FilterStateBuffer : public StateBuffer
{
public:
FilterStateBuffer(void) :
StateBuffer(256)
{
m_enumType = FILTER_BIT;
m_UpdateMessage = LB_RESETCONTENT;
}
virtual HRESULT ReadState(void);
};
FilterStateBuffer g_PrivateFilterBuffer;
StateBuffer* g_FilterBuffer = &g_PrivateFilterBuffer;
ULONG g_FilterArgsOffset;
ULONG g_FilterCmdsOffset;
ULONG g_FilterWspCmdsOffset;
ULONG g_NumSpecEvents, g_NumSpecEx, g_NumArbEx;
HRESULT
FilterStateBuffer::ReadState(void)
{
ULONG SpecEvents, SpecEx, ArbEx;
HRESULT Status;
ULONG ArgsOffset, CmdsOffset, WspCmdsOffset;
PDEBUG_SPECIFIC_FILTER_PARAMETERS SpecParams;
PDEBUG_EXCEPTION_FILTER_PARAMETERS ExParams;
ULONG i;
if ((Status = g_pDbgControl->
GetNumberEventFilters(&SpecEvents, &SpecEx, &ArbEx)) != S_OK)
{
return Status;
}
Empty();
if ((SpecParams = (PDEBUG_SPECIFIC_FILTER_PARAMETERS)
AddData((SpecEvents * sizeof(*SpecParams) +
(SpecEx + ArbEx) * sizeof(*ExParams)))) == NULL)
{
return E_OUTOFMEMORY;
}
ExParams = (PDEBUG_EXCEPTION_FILTER_PARAMETERS)(SpecParams + SpecEvents);
if ((Status = g_pDbgControl->
GetSpecificFilterParameters(0, SpecEvents, SpecParams)) != S_OK ||
(Status = g_pDbgControl->
GetExceptionFilterParameters(SpecEx + ArbEx, NULL, SpecEvents,
ExParams)) != S_OK)
{
return Status;
}
ArgsOffset = m_DataUsed;
for (i = 0; i < SpecEvents; i++)
{
if (SpecParams[i].ArgumentSize > 1)
{
PSTR Arg = (PSTR)AddData(SpecParams[i].ArgumentSize);
if (Arg == NULL)
{
return E_OUTOFMEMORY;
}
if ((Status = g_pDbgControl->
GetSpecificFilterArgument(i, Arg, SpecParams[i].ArgumentSize,
NULL)) != S_OK)
{
return Status;
}
}
}
CmdsOffset = m_DataUsed;
for (i = 0; i < SpecEvents; i++)
{
if (SpecParams[i].CommandSize > 0)
{
PSTR Cmd = (PSTR)AddData(SpecParams[i].CommandSize);
if (Cmd == NULL)
{
return E_OUTOFMEMORY;
}
if ((Status = g_pDbgControl->
GetEventFilterCommand(i, Cmd, SpecParams[i].CommandSize,
NULL)) != S_OK)
{
return Status;
}
}
}
for (i = 0; i < SpecEx + ArbEx; i++)
{
if (ExParams[i].CommandSize > 0)
{
PSTR Cmd = (PSTR)AddData(ExParams[i].CommandSize);
if (Cmd == NULL)
{
return E_OUTOFMEMORY;
}
if ((Status = g_pDbgControl->
GetEventFilterCommand(i + SpecEvents,
Cmd, ExParams[i].CommandSize,
NULL)) != S_OK)
{
return Status;
}
}
if (ExParams[i].SecondCommandSize > 0)
{
PSTR Cmd = (PSTR)AddData(ExParams[i].SecondCommandSize);
if (Cmd == NULL)
{
return E_OUTOFMEMORY;
}
if ((Status = g_pDbgControl->
GetExceptionFilterSecondCommand(i + SpecEvents,
Cmd,
ExParams[i].SecondCommandSize,
NULL)) != S_OK)
{
return Status;
}
}
}
WspCmdsOffset = m_DataUsed;
g_OutStateBuf.SetBuffer(this);
if ((Status = g_OutStateBuf.Start(FALSE)) != S_OK)
{
return Status;
}
// Get filter commands.
Status = g_pOutCapControl->Execute(DEBUG_OUTCTL_THIS_CLIENT |
DEBUG_OUTCTL_OVERRIDE_MASK |
DEBUG_OUTCTL_NOT_LOGGED,
".sxcmds",
DEBUG_EXECUTE_NOT_LOGGED |
DEBUG_EXECUTE_NO_REPEAT);
if (Status == S_OK)
{
Status = g_OutStateBuf.End(FALSE);
}
else
{
g_OutStateBuf.End(FALSE);
}
if (Status == S_OK)
{
g_FilterArgsOffset = ArgsOffset;
g_FilterCmdsOffset = CmdsOffset;
g_FilterWspCmdsOffset = WspCmdsOffset;
g_NumSpecEvents = SpecEvents;
g_NumSpecEx = SpecEx;
g_NumArbEx = ArbEx;
}
return Status;
}
class ModuleStateBuffer : public StateBuffer
{
public:
ModuleStateBuffer(void) :
StateBuffer(256)
{
m_enumType = MODULE_BIT;
m_UpdateMessage = LB_RESETCONTENT;
}
virtual HRESULT ReadState(void);
};
ModuleStateBuffer g_PrivateModuleBuffer;
StateBuffer* g_ModuleBuffer = &g_PrivateModuleBuffer;
ULONG g_NumModules;
HRESULT
ModuleStateBuffer::ReadState(void)
{
HRESULT Status;
ULONG NumModules, Loaded, Unloaded;
PDEBUG_MODULE_PARAMETERS Params;
if ((Status = g_pDbgSymbols->GetNumberModules(&Loaded,
&Unloaded)) != S_OK)
{
return Status;
}
Empty();
NumModules = Loaded + Unloaded;
if (NumModules > 0)
{
if ((Params = (PDEBUG_MODULE_PARAMETERS)
AddData(NumModules * sizeof(*Params))) == NULL)
{
return E_OUTOFMEMORY;
}
if ((Status = g_pDbgSymbols->
GetModuleParameters(NumModules, NULL, 0, Params)) != S_OK)
{
return Status;
}
}
g_NumModules = NumModules;
return S_OK;
}
void
ReadStateBuffers(void)
{
// Fill event information first so other fills can
// refer to it.
if (g_EventBufferRequest != g_EventBufferDone)
{
FillEventBuffer();
}
g_BpBuffer->Update();
g_BpCmdsBuffer->Update();
g_FilterBuffer->Update();
g_ModuleBuffer->Update();
// No need to lock to sample the list head.
StateBuffer* Buffer = (StateBuffer*)g_StateList.Flink;
StateBuffer* BufferNext;
while (Buffer != &g_StateList)
{
BufferNext = (StateBuffer*)Buffer->Flink;
if (Buffer->m_Win == NULL)
{
// This window has been closed and can be cleaned up.
Dbg_EnterCriticalSection(&g_QuickLock);
RemoveEntryList(Buffer);
Dbg_LeaveCriticalSection(&g_QuickLock);
delete Buffer;
}
else
{
Buffer->Update();
}
Buffer = BufferNext;
}
}
void
InvalidateStateBuffers(ULONG Types)
{
// This routine can be called from both
// the engine thread and the UI thread.
// Care should be taken to make the code
// here work in both threads.
if (Types & (1 << EVENT_BIT))
{
InterlockedIncrement((PLONG)&g_EventBufferRequest);
}
if (Types & (1 << BP_BIT))
{
g_BpBuffer->RequestRead();
}
if (Types & (1 << BP_CMDS_BIT))
{
g_BpCmdsBuffer->RequestRead();
}
if (Types & (1 << FILTER_BIT))
{
g_FilterBuffer->RequestRead();
}
if (Types & (1 << MODULE_BIT))
{
g_ModuleBuffer->RequestRead();
}
// This routine must hold the list lock so that it
// can traverse the list properly in the UI thread
// when the engine thread might be deleting things.
// The code in the lock should execute quickly to
// avoid contention.
Dbg_EnterCriticalSection(&g_QuickLock);
StateBuffer* Buffer = (StateBuffer*)g_StateList.Flink;
while (Buffer != &g_StateList)
{
if (Types & (1 << Buffer->m_enumType))
{
// Request a read but do not send an update to
// the window. The window will display the old
// content until the buffer is updated.
Buffer->RequestRead();
}
Buffer = (StateBuffer*)Buffer->Flink;
}
Dbg_LeaveCriticalSection(&g_QuickLock);
}
void
UpdateBufferWindows(ULONG Types, UpdateType Type)
{
// This routine can be called from both
// the engine thread and the UI thread.
// Care should be taken to make the code
// here work in both threads.
// This routine must hold the list lock so that it
// can traverse the list properly in the UI thread
// when the engine thread might be deleting things.
// The code in the lock should execute quickly to
// avoid contention.
Dbg_EnterCriticalSection(&g_QuickLock);
StateBuffer* Buffer = (StateBuffer*)g_StateList.Flink;
while (Buffer != &g_StateList)
{
if ((Types & (1 << Buffer->m_enumType)) &&
Buffer->m_Win != NULL)
{
PostMessage(Buffer->m_Win, WU_UPDATE, Type, 0);
}
Buffer = (StateBuffer*)Buffer->Flink;
}
Dbg_LeaveCriticalSection(&g_QuickLock);
}
//----------------------------------------------------------------------------
//
// Static state buffers.
//
//----------------------------------------------------------------------------
class RegisterNamesStateBuffer : public StateBuffer
{
public:
RegisterNamesStateBuffer(void)
: StateBuffer(128)
{
}
virtual HRESULT ReadState(void);
};
RegisterNamesStateBuffer g_PrivateRegisterNamesBuffer;
StateBuffer* g_RegisterNamesBuffer = &g_PrivateRegisterNamesBuffer;
HRESULT
RegisterNamesStateBuffer::ReadState(void)
{
char Name[1024];
DEBUG_REGISTER_DESCRIPTION Desc;
ULONG i;
HRESULT Hr;
PSTR BufName;
ULONG Len;
Empty();
for (i = 0; i < g_NumRegisters; i++)
{
if ((Hr = g_pDbgRegisters->GetDescription(i, Name, sizeof(Name),
NULL, &Desc)) != S_OK)
{
ErrorExit(g_pDbgClient,
"Debug target initialization failed, 0x%X\n", Hr);
}
Len = strlen(Name) + 1;
BufName = (PSTR)AddData(Len);
if (BufName == NULL)
{
ErrorExit(g_pDbgClient,
"Debug target initialization failed, 0x%X\n", Hr);
}
memcpy(BufName, Name, Len);
}
return S_OK;
}
class WatchWinStateBuffer : public StateBuffer
{
public:
WatchWinStateBuffer(void)
: StateBuffer(1024)
{
}
virtual HRESULT ReadState(void);
};
WatchWinStateBuffer g_PrivateWatchWinBuffer;
StateBuffer* g_WatchWinBuffer = &g_PrivateWatchWinBuffer;
extern IDebugSymbolGroup * g_pDbgSymbolGroup;
HRESULT
WatchWinStateBuffer::ReadState(void)
{
char Name[1024];
ULONG i;
HRESULT Hr;
PSTR BufName;
ULONG Count;
Empty();
// g_pDbgSymbolGroup->GetSymbolParameters(0, 10, &SymParams[0]);
// g_pDbgSymbolGroup->OutputSymbols(0, 0, 0, 10);
return S_OK;
}
PUSHORT g_RegisterMap;
ULONG g_RegisterMapEntries;
void
GetRegisterMapText(HWND Edit)
{
ULONG i;
PSTR Name;
CHARRANGE Range;
AssertStateBufferLocked(g_RegisterNamesBuffer);
Range.cpMin = 0;
Range.cpMax = INT_MAX;
SendMessage(Edit, EM_EXSETSEL, 0, (LPARAM)&Range);
for (i = 0; i < g_NumRegisters; i++)
{
ULONG MapIndex = MAP_REGISTER(i);
Name = (PSTR)g_RegisterNamesBuffer->GetDataBuffer();
while (MapIndex-- > 0)
{
Name += strlen(Name) + 1;
}
if (i > 0)
{
SendMessage(Edit, EM_REPLACESEL, 0, (LPARAM)" ");
}
SendMessage(Edit, EM_REPLACESEL, 0, (LPARAM)Name);
}
}
void
ScanRegisterMapText(HWND Edit)
{
PSTR Text, TextBuffer;
PULONG Used, UsedBuffer;
ULONG i;
AssertStateBufferLocked(g_RegisterNamesBuffer);
//
// Allocate a buffer for the control text
// and a new register map.
//
i = (ULONG)SendMessage(Edit, WM_GETTEXTLENGTH, 0, 0) + 1;
TextBuffer = new CHAR[i];
if (TextBuffer == NULL)
{
return;
}
Text = TextBuffer;
UsedBuffer = new ULONG[g_NumRegisters];
if (UsedBuffer == NULL)
{
delete TextBuffer;
return;
}
Used = UsedBuffer;
// Map may need to change size.
delete g_RegisterMap;
g_RegisterMap = new USHORT[g_NumRegisters];
if (g_RegisterMap == NULL)
{
delete TextBuffer;
delete UsedBuffer;
return;
}
g_RegisterMapEntries = g_NumRegisters;
ZeroMemory(Used, g_NumRegisters * sizeof(Used[0]));
//
// Retrieve the text and scan it for register names.
//
GetWindowText(Edit, Text, i);
Text[i - 1] = 0;
PSTR Name;
BOOL End;
PUSHORT Map;
PUSHORT Check;
PSTR Reg;
Map = g_RegisterMap;
for (;;)
{
while (isspace(*Text))
{
Text++;
}
if (*Text == 0)
{
break;
}
// Collect name.
Name = Text;
while (*Text && !isspace(*Text))
{
Text++;
}
End = *Text == 0;
*Text = 0;
// Check against known registers.
Reg = (PSTR)g_RegisterNamesBuffer->GetDataBuffer();
for (i = 0; i < g_NumRegisters; i++)
{
if (!Used[i] && !_strcmpi(Name, Reg))
{
Used[i] = TRUE;
*Map++ = (USHORT)i;
break;
}
Reg += strlen(Reg) + 1;
}
if (End)
{
break;
}
Text++;
}
//
// Fill out any remaining map entries with registers
// which aren't in the map so far.
//
PUSHORT MapEnd = g_RegisterMap + g_RegisterMapEntries;
i = 0;
while (Map < MapEnd)
{
while (Used[i])
{
i++;
}
Assert(i < g_NumRegisters);
*Map++ = (USHORT)(i++);
}
delete TextBuffer;
delete UsedBuffer;
}