//---------------------------------------------------------------------------- // // Debuggee state buffers. // // Copyright (C) Microsoft Corporation, 1999-2000. // //---------------------------------------------------------------------------- #include "precomp.hxx" #pragma hdrstop #include #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; }