|
|
//----------------------------------------------------------------------------
//
// 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; }
|