public void Walk(ILiveWatchEngine engine, Dictionary <ulong, ThreadListType> result, HashSet <ulong> processedNodes, int maxThreadsToLoad, LinkedListNodeCache nodeCache, LiveVariableQueryMode queryMode) { int threadsFound = 0; ulong pxNext = 0; for (var pListNode = xListEnd_pxNext.GetValue(queryMode).ToUlong(); pListNode != 0 && !processedNodes.Contains(pListNode); pListNode = pxNext, threadsFound++) { if (threadsFound >= maxThreadsToLoad) { break; } processedNodes.Add(pListNode); try { var pTCB = pListNode - _ListItemOffsetInTCB; var cachedListNode = nodeCache.ProvideNode(pListNode); cachedListNode.ReadValues(queryMode, out ulong pvOwner, out pxNext); if (pvOwner != pTCB) { if ((uint)pvOwner == uint.MaxValue) { //This is the end-of-list node. Continue checking past it. continue; } else { //The list node doesn't point to the object itself anymore. Most likely, it has been freed and reused. cachedListNode.RemoveFromCache(); break; } } result[pTCB] = Type; } catch (Exception ex) { engine.LogException(ex, $"failed to process TCB node at {pListNode}"); break; } } }
public void ReadValues(LiveVariableQueryMode queryMode, out ulong pvOwner, out ulong pxNext) { var value = _Variable.GetValue(queryMode); if (!value.IsValid) { pvOwner = pxNext = 0; } else { pvOwner = BitConverter.ToUInt32(value.Value, _Cache.pvOwner_RelativeOffset); pxNext = BitConverter.ToUInt32(value.Value, _Cache.pxNext_RelativeOffset); } }
public override LiveWatchNodeState UpdateState(LiveWatchUpdateContext context) { var freeBytesValue = _Variable.GetValue(); var freeBytes = (int)freeBytesValue.ToUlong(); int usedBytes = _HeapSize - freeBytes; RawValue = new LiveVariableValue(freeBytesValue.Timestamp, freeBytesValue.Generation, BitConverter.GetBytes(usedBytes)); return(new LiveWatchNodeState { Value = $"{usedBytes}/{_HeapSize} bytes" }); }
public override LiveWatchNodeState UpdateState(LiveWatchUpdateContext context) { var result = new LiveWatchNodeState { Icon = LiveWatchNodeIcon.Graph }; if (_xSchedulerRunning != null) { if (_xSchedulerRunning.GetValue().ToUlong() != 0) { result.Value = "active"; } else { result.Value = "inactive"; } } return(result); }
public override LiveWatchNodeState UpdateState(LiveWatchUpdateContext context) { int estimatedStackSize = (int)(_StackEnd - _StackStart); if (_OverflowDetected) { return(ReportStackOverflow(estimatedStackSize)); } if (_Error != null) { return new LiveWatchNodeState { Icon = LiveWatchNodeIcon.Error, Value = _Error } } ; var rawValue = _BorderVariable?.GetValue() ?? default; if (!rawValue.IsValid || CountUnusedStackMarkers(rawValue.Value) != rawValue.Value.Length) { ulong lastKnownEndOfStack; if (_BorderVariable != null) { lastKnownEndOfStack = _BorderVariable.Address + (uint)_BorderVariable.Size; } else { lastKnownEndOfStack = _StackEnd; } _BorderVariable?.Dispose(); _BorderVariable = null; ulong startOfCheckedArea; if (!_StackOpposesGrowingHeap) { //Maximum size is fixed. No guessing needed. startOfCheckedArea = _StackStart; } else if (_HeapEndVariable != null) { //Stack immediately follows the dynamically growing heap startOfCheckedArea = _HeapEndVariable.GetValue().ToUlong(); if (startOfCheckedArea == 0) { startOfCheckedArea = _StackStart; //The heap has never been used yet } estimatedStackSize = (int)(_StackEnd - _StackStart); } else { //No heap. Stack directly follows the 'end' symbol. startOfCheckedArea = _StackStart; } int position = MeasureUnusedStackArea(startOfCheckedArea, lastKnownEndOfStack); if (position == 0) { _OverflowDetected = true; } else { _PatternEverFound = true; } if (_OverflowDetected) { return(ReportStackOverflow(estimatedStackSize)); } else { int watchSize = Math.Min(_MaxBorderVariableSize, position); _BorderVariable = _Engine.Memory.CreateLiveVariable(startOfCheckedArea + (uint)position - (uint)watchSize, watchSize); } } ulong firstUsedStackSlot = _BorderVariable.Address + (uint)_BorderVariable.Size; Location = new LiveWatchPhysicalLocation(firstUsedStackSlot, null, 0); int stackUsage = (int)(_StackEnd - firstUsedStackSlot); RawValue = new LiveVariableValue(rawValue.Timestamp, rawValue.Generation, BitConverter.GetBytes(stackUsage)); if (_HeapEndVariable != null) { var rawHeapEnd = _HeapEndVariable.GetValue(); ulong heapEnd = rawHeapEnd.ToUlong(); if (heapEnd == 0) { heapEnd = _StackStart; } estimatedStackSize = (int)(_StackEnd - heapEnd); _DistanceToHeapEnd = new LiveVariableValue(rawHeapEnd.Timestamp, rawHeapEnd.Generation, BitConverter.GetBytes((int)(firstUsedStackSlot - heapEnd))); } string text; if (estimatedStackSize > 0) { text = $"{stackUsage}/{estimatedStackSize} bytes"; } else { text = $"{stackUsage} bytes"; } if (context.PreloadChildren && _Children == null && _HeapEndVariable != null) { _Children = new ILiveWatchNode[] { new DistanceToHeapNode(this) }; } return(new LiveWatchNodeState { Value = text, Icon = LiveWatchNodeIcon.Stack, NewChildren = _Children, }); }
public override LiveWatchNodeState UpdateState(LiveWatchUpdateContext context) { if (_PointerVariable != null) { var address = _PointerVariable.GetValue().ToUlong() & ~1UL; if (address != _QueueVariable?.Address) { _QueueVariable = _Engine.Symbols.CreateTypedVariable(address, _QueueType); } } if (_QueueVariable != null && _QueueVariable.Address != _Variables.LastKnownAddress) { _Variables.LastKnownAddress = _QueueVariable.Address; //The previous instance will get auto-disposed by VisualGDB. _QueueObjectNode = null; _Variables.Reset(); if (_QueueVariable.Address != 0) { _Variables.uxMessagesWaiting = _Engine.CreateLiveVariable(_QueueVariable.LookupSpecificChild(nameof(_Variables.uxMessagesWaiting))); _Variables.uxLength = _Engine.CreateLiveVariable(_QueueVariable.LookupSpecificChild(nameof(_Variables.uxLength))); _Variables.u_xSemaphore_xMutexHolder = _Engine.CreateLiveVariable(_QueueVariable.LookupChildRecursively("u.xSemaphore.xMutexHolder")); _Variables.u_xSemaphore_uxRecursiveCallCount = _Engine.CreateLiveVariable(_QueueVariable.LookupChildRecursively("u.xSemaphore.uxRecursiveCallCount")); } } if (context.PreloadChildren && _QueueVariable != null && _QueueObjectNode == null && _QueueVariable.Address != 0) { _QueueObjectNode = _Engine.CreateNodeForPinnedVariable(_QueueVariable, new LiveWatchNodeOverrides { Name = "[Object]" }); } var result = new LiveWatchNodeState(); if ((_QueueVariable?.Address ?? 0) == 0) { result.Value = "[NULL]"; } else if (_Variables.uxLength == null || _Variables.uxMessagesWaiting == null) { result.Value = "???"; } else { if (_Descriptor.TypeOverride != null && !_StaticType.HasValue) { var typeVar = _QueueVariable.LookupSpecificChild("ucQueueType"); if (typeVar != null) { _StaticType = (QueueType)_Engine.ReadMemory(typeVar).ToUlong(); RawType = _StaticType.Value.ToString(); } } var detectedType = _StaticType ?? _Descriptor.Type; var rawValue = _Variables.uxMessagesWaiting.GetValue(); int value = (int)rawValue.ToUlong(); int maxValue = (int)_Variables.uxLength.GetValue().ToUlong(); ulong owner = 0, level = 0; if (detectedType != QueueType.BaseQueue && _Variables.u_xSemaphore_xMutexHolder != null && _Variables.u_xSemaphore_uxRecursiveCallCount != null) { owner = _Variables.u_xSemaphore_xMutexHolder.GetValue().ToUlong(); level = _Variables.u_xSemaphore_uxRecursiveCallCount.GetValue().ToUlong(); if (owner == _QueueVariable.Address) { detectedType = QueueType.Semaphore; } else { detectedType = QueueType.RecursiveMutex; } } if (detectedType == QueueType.RecursiveMutex || detectedType == QueueType.NonRecursiveMutex) { if (value != 0) { result.Value = "free"; RawValue = new LiveVariableValue(rawValue.Timestamp, rawValue.Generation, BitConverter.GetBytes(0)); } else if (_Variables.u_xSemaphore_xMutexHolder != null && _Variables.u_xSemaphore_uxRecursiveCallCount != null) { string threadName = _Root.GetThreadName(owner); result.Value = $"taken by {threadName}"; if (level >= 1) { result.Value += $" (recursion = {level})"; } RawValue = new LiveVariableValue(rawValue.Timestamp, rawValue.Generation, BitConverter.GetBytes((int)level + 1)); } else { result.Value = "taken"; } } else { RawValue = rawValue; result.Value = $"{value}/{maxValue}"; } result.NewType = detectedType.ToString(); switch (detectedType) { case QueueType.BinarySemaphore: case QueueType.Semaphore: case QueueType.RecursiveMutex: case QueueType.NonRecursiveMutex: result.Icon = LiveWatchNodeIcon.Flag; break; case QueueType.BaseQueue: result.Icon = LiveWatchNodeIcon.Queue; break; } if (context.PreloadChildren) { ProvideWaitingThreadsNodes(detectedType); result.NewChildren = new[] { _ReadThreadQueue, _WriteThreadQueue, _QueueObjectNode }.Where(n => n != null).ToArray(); } } return(result); }
public string GetCurrentTaskName(ILiveVariable pxCurrentTCB) { //We use a separate live variable, so that we can suspend it independently. VisualGDB will automatically sort out the redundancies if both variables are enabled. return(GetThreadName(pxCurrentTCB.GetValue().ToUlong())); }
public override LiveWatchNodeState UpdateState(LiveWatchUpdateContext context) { int estimatedStackSize = ProvideEstimatedStackSize(out var pxStack); if (_OverflowDetected) { return(ReportStackOverflow(estimatedStackSize)); } var rawValue = _BorderVariable?.GetValue() ?? default; if (!rawValue.IsValid || CountUnusedStackArea(rawValue.Value) != rawValue.Value.Length) { int queriedStackSize; if (_BorderVariable != null) { queriedStackSize = (int)(_BorderVariable.Address - pxStack); } else { queriedStackSize = (int)(_ThreadNode._Engine.ReadMemory(_ThreadNode._Variables.pxTopOfStack).ToUlong() - pxStack); } _BorderVariable?.Dispose(); _BorderVariable = null; if (queriedStackSize < 0) { return new LiveWatchNodeState { Icon = LiveWatchNodeIcon.Error, Value = $"Unexpected stack size ({queriedStackSize})" } } ; var data = _ThreadNode._Engine.Memory.ReadMemory(pxStack, queriedStackSize); if (!data.IsValid) { return new LiveWatchNodeState { Icon = LiveWatchNodeIcon.Error, Value = $"Failed to read stack contents (0x{pxStack:x8} - 0x{pxStack + (uint)queriedStackSize:x8})" } } ; int offset = CountUnusedStackArea(data.Value); //We don't know whether it is a stack overflow, or if the empty stack is never filled with the pattern. //We assume that if the stack appears overflown from the very beginning, the pattern is not being used at all. _OverflowDetected = offset == 0; if (offset != 0) { _PatternEverFound = true; } if (offset == 0) { return(ReportStackOverflow(estimatedStackSize)); } else { int watchSize = Math.Min(_MaxBorderVariableSize, offset); _BorderVariable = _ThreadNode._Engine.Memory.CreateLiveVariable(pxStack + (uint)(offset - watchSize), watchSize, "Stack Border"); } } int freeStack = (int)(_BorderVariable.Address - pxStack) + _BorderVariable.Size; /* The border variable watches the 1st free slot, not the 1st used one */ int stackUsage = estimatedStackSize - freeStack; RawValue = new LiveVariableValue(rawValue.Timestamp, rawValue.Generation, BitConverter.GetBytes(stackUsage)); string text; if (estimatedStackSize > 0) { text = $"{stackUsage}/{estimatedStackSize} bytes"; } else { text = $"{stackUsage} bytes"; } return(new LiveWatchNodeState { Value = text }); }