1621 lines
38 KiB
C++
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;
|
|
}
|