/// <summary> /// Diffs the two passed in callstack infos and returns the difference. /// </summary> /// <param name="New"> Newer callstack info to subtract older from </param> /// <param name="Old"> Older callstack info to subtract from older </param> public static FCallStackAllocationInfo Diff(FCallStackAllocationInfo New, FCallStackAllocationInfo Old) { if (New.CallStackIndex != Old.CallStackIndex) { throw new InvalidDataException(); } FCallStackAllocationInfo DiffData = New.DeepCopy(); DiffData.UntaggedAllocationInfo -= Old.UntaggedAllocationInfo; if (Old.TaggedAllocationInfo != null) { DiffData.EnsureTaggedAllocationsAvailable(); foreach (var TaggedAllocationInfoPair in Old.TaggedAllocationInfo) { FCallStackTagsAllocationInfo CurrentTagsAllocationInfo; if (!DiffData.TaggedAllocationInfo.TryGetValue(TaggedAllocationInfoPair.Key, out CurrentTagsAllocationInfo)) { CurrentTagsAllocationInfo = new FCallStackTagsAllocationInfo(0, 0); } DiffData.TaggedAllocationInfo[TaggedAllocationInfoPair.Key] = CurrentTagsAllocationInfo - TaggedAllocationInfoPair.Value; } } return(DiffData); }
public void FillActiveCallStackList(Dictionary <ulong, FCallStackAllocationInfo> PointerToPointerInfoMap) { ActiveCallStackList.Clear(); ActiveCallStackList.Capacity = LifetimeCallStackList.Count; foreach (KeyValuePair <ulong, FCallStackAllocationInfo> PointerData in PointerToPointerInfoMap) { int CallStackIndex = PointerData.Value.CallStackIndex; long Size = PointerData.Value.Size; // make sure allocationInfoList is big enough while (CallStackIndex >= ActiveCallStackList.Count) { ActiveCallStackList.Add(new FCallStackAllocationInfo(0, ActiveCallStackList.Count, 0)); } FCallStackAllocationInfo NewAllocInfo = ActiveCallStackList[CallStackIndex]; NewAllocInfo.Size += Size; NewAllocInfo.Count++; ActiveCallStackList[CallStackIndex] = NewAllocInfo; } // strip out any callstacks with no allocations ActiveCallStackList.RemoveAll(Item => Item.Count == 0); ActiveCallStackList.TrimExcess(); }
/// <summary> Inserts the new allocation so that the list stays in size order. </summary> public void AddAllocation(FCallStackAllocationInfo AllocationInfo) { bool bInserted = false; if (BatchAddingCount == 0) { for (int Index = 0; Index < CallStackList.Count; Index++) { if (CallStackList[Index].TotalSize > AllocationInfo.TotalSize) { CallStackList.Insert(Index, AllocationInfo); bInserted = true; break; } } } if (!bInserted) { CallStackList.Add(AllocationInfo); } MemorySize += AllocationInfo.TotalSize; AllocationCount += AllocationInfo.TotalCount; }
/// <summary> /// Diffs the two passed in callstack infos and returns the difference. /// </summary> /// <param name="New"> Newer callstack info to subtract older from </param> /// <param name="Old"> Older callstack info to subtract from older </param> public static FCallStackAllocationInfo Diff(FCallStackAllocationInfo New, FCallStackAllocationInfo Old) { if (New.CallStackIndex != Old.CallStackIndex) { throw new InvalidDataException(); } return(new FCallStackAllocationInfo(New.Size - Old.Size, New.CallStackIndex, New.Count - Old.Count)); }
/// <summary> /// Diffs the two passed in callstack infos and returns the difference. /// </summary> /// <param name="New"> Newer callstack info to subtract older from </param> /// <param name="Old"> Older callstack info to subtract from older </param> public static FCallStackAllocationInfo Diff( FCallStackAllocationInfo New, FCallStackAllocationInfo Old ) { if( New.CallStackIndex != Old.CallStackIndex ) { throw new InvalidDataException(); } return new FCallStackAllocationInfo( New.Size - Old.Size, New.CallStackIndex, New.Count - Old.Count ); }
/// <summary> Updates internal state with allocation. </summary> private static void HandleMalloc(FStreamToken StreamToken, FStreamSnapshot Snapshot, Dictionary <ulong, FCallStackAllocationInfo> PointerToPointerInfoMap) { // Keep track of size associated with pointer and also current callstack. FCallStackAllocationInfo PointerInfo = new FCallStackAllocationInfo(StreamToken.Size, StreamToken.CallStackIndex, 1); if (PointerToPointerInfoMap.ContainsKey(StreamToken.Pointer)) { Debug.WriteLine("Same pointer malloced twice without being freed: " + StreamToken.Pointer + " in pool " + StreamToken.Pool); PointerToPointerInfoMap.Remove(StreamToken.Pointer); } PointerToPointerInfoMap.Add(StreamToken.Pointer, PointerInfo); if (StreamToken.CallStackIndex >= FStreamInfo.GlobalInstance.CallStackArray.Count) { Debug.WriteLine("CallStackIndex out of range!"); return; } // Add size to lifetime churn tracking. while (StreamToken.CallStackIndex >= Snapshot.LifetimeCallStackList.Count) { Snapshot.LifetimeCallStackList.Add(new FCallStackAllocationInfo(0, Snapshot.LifetimeCallStackList.Count, 0)); } //@todo: Sadly, we have to do all this ugly shuffling because of the way lists of structs work in C# Snapshot.LifetimeCallStackList[StreamToken.CallStackIndex] = Snapshot.LifetimeCallStackList[StreamToken.CallStackIndex].Add(StreamToken.Size, 1); if (Snapshot.AllocationSize > Snapshot.AllocationMaxSize) { Snapshot.AllocationMaxSize = Snapshot.AllocationSize; } // Maintain timeline view Snapshot.AllocationSize += PointerInfo.Size; Snapshot.AllocationCount++; if (Snapshot.AllocationCount % AllocationsPerSlice == 0) { FMemorySlice Slice = new FMemorySlice(Snapshot); Snapshot.OverallMemorySlice.Add(Slice); Snapshot.AllocationMaxSize = 0; } }
/// <summary> Inserts the new allocation so that the list stays in size order. </summary> public void AddAllocation( FCallStackAllocationInfo AllocationInfo ) { bool bInserted = false; for( int Index = 0; Index < CallStackList.Count; Index++ ) { if( CallStackList[ Index ].Size > AllocationInfo.Size ) { CallStackList.Insert( Index, AllocationInfo ); bInserted = true; break; } } if( !bInserted ) { CallStackList.Add( AllocationInfo ); } MemorySize += AllocationInfo.Size; AllocationCount += AllocationInfo.Count; }
/// <summary> /// Adds the passed callstack info to this callstack info. /// </summary> /// <param name="InOther"> Callstack info to add </param> public void Add(FCallStackAllocationInfo InOther) { if (CallStackIndex != InOther.CallStackIndex) { throw new InvalidDataException(); } UntaggedAllocationInfo += InOther.UntaggedAllocationInfo; if (InOther.TaggedAllocationInfo != null) { EnsureTaggedAllocationsAvailable(); foreach (var TaggedAllocationInfoPair in InOther.TaggedAllocationInfo) { FCallStackTagsAllocationInfo CurrentTagsAllocationInfo; if (!TaggedAllocationInfo.TryGetValue(TaggedAllocationInfoPair.Key, out CurrentTagsAllocationInfo)) { CurrentTagsAllocationInfo = new FCallStackTagsAllocationInfo(0, 0); } TaggedAllocationInfo[TaggedAllocationInfoPair.Key] = CurrentTagsAllocationInfo + TaggedAllocationInfoPair.Value; } } }
private static void AddCallStackToGraph(TreeNode RootNode, FCallStack CallStack, FCallStackAllocationInfo AllocationInfo, int ParentFunctionIndex, bool bInvertCallStacks) { // Used to determine whether it is okay to add address to the graph. An index of -1 means we don't care about parenting. bool bAllowNodeUpdate = ParentFunctionIndex == -1; // Iterate over each address and add it to the tree view. TreeNode CurrentNode = RootNode; for (int AdressIndex = 0; AdressIndex < CallStack.AddressIndices.Count; AdressIndex++) { int AddressIndex; if (bInvertCallStacks) { AddressIndex = CallStack.AddressIndices[CallStack.AddressIndices.Count - 1 - AdressIndex]; } else { AddressIndex = CallStack.AddressIndices[AdressIndex]; } // Filter based on function if wanted. This means we only include callstacks that contain the function // and we also reparent the callstack to start at the first occurence of the function. if (ParentFunctionIndex != -1 && !bAllowNodeUpdate) { bAllowNodeUpdate = FStreamInfo.GlobalInstance.CallStackAddressArray[AddressIndex].FunctionIndex == ParentFunctionIndex; } if (bAllowNodeUpdate) { // Update the node for this address. The return value of the function will be the new parent node. CurrentNode = UpdateNodeAndPayload(CurrentNode, AddressIndex, AllocationInfo); } } }
/// <summary> Compare helper function, sorting FCallStackAllocation by count. </summary> private static int CompareCount(FCallStackAllocationInfo A, FCallStackAllocationInfo B) { return(Math.Sign(B.TotalCount - A.TotalCount)); }
/// <summary> Updates internal state with free. </summary> private static bool HandleFree(FStreamToken StreamToken, FStreamSnapshot Snapshot, Dictionary<ulong, FCallStackAllocationInfo> PointerToPointerInfoMap, out FCallStackAllocationInfo FreedAllocInfo) { if (!PointerToPointerInfoMap.TryGetValue(StreamToken.Pointer, out FreedAllocInfo)) { Debug.WriteLine("Free without malloc: " + StreamToken.Pointer + " in pool " + StreamToken.Pool); return false; } // Maintain timeline view Snapshot.AllocationSize -= FreedAllocInfo.Size; Snapshot.AllocationCount++; if (Snapshot.AllocationCount % AllocationsPerSlice == 0) { FMemorySlice Slice = new FMemorySlice( Snapshot ); Snapshot.OverallMemorySlice.Add( Slice ); } // Remove freed pointer if it is in the array. PointerToPointerInfoMap.Remove(StreamToken.Pointer); return true; }
/// <summary> Updates internal state with allocation. </summary> private static void HandleMalloc(FStreamToken StreamToken, FStreamSnapshot Snapshot, Dictionary<ulong, FCallStackAllocationInfo> PointerToPointerInfoMap) { // Keep track of size associated with pointer and also current callstack. FCallStackAllocationInfo PointerInfo = new FCallStackAllocationInfo(StreamToken.Size, StreamToken.CallStackIndex, 1); if (PointerToPointerInfoMap.ContainsKey(StreamToken.Pointer)) { Debug.WriteLine("Same pointer malloced twice without being freed: " + StreamToken.Pointer + " in pool " + StreamToken.Pool); PointerToPointerInfoMap.Remove(StreamToken.Pointer); } PointerToPointerInfoMap.Add(StreamToken.Pointer, PointerInfo); if (StreamToken.CallStackIndex >= FStreamInfo.GlobalInstance.CallStackArray.Count) { Debug.WriteLine("CallStackIndex out of range!"); return; } // Add size to lifetime churn tracking. while (StreamToken.CallStackIndex >= Snapshot.LifetimeCallStackList.Count) { Snapshot.LifetimeCallStackList.Add(new FCallStackAllocationInfo(0, Snapshot.LifetimeCallStackList.Count, 0)); } //@todo: Sadly, we have to do all this ugly shuffling because of the way lists of structs work in C# Snapshot.LifetimeCallStackList[ StreamToken.CallStackIndex ] = Snapshot.LifetimeCallStackList[ StreamToken.CallStackIndex ].Add( StreamToken.Size, 1 ); if( Snapshot.AllocationSize > Snapshot.AllocationMaxSize ) { Snapshot.AllocationMaxSize = Snapshot.AllocationSize; } // Maintain timeline view Snapshot.AllocationSize += PointerInfo.Size; Snapshot.AllocationCount++; if (Snapshot.AllocationCount % AllocationsPerSlice == 0) { FMemorySlice Slice = new FMemorySlice( Snapshot ); Snapshot.OverallMemorySlice.Add( Slice ); Snapshot.AllocationMaxSize = 0; } }
private static TreeNode UpdateNodeAndPayload( TreeNode ParentNode, int AddressIndex, FCallStackAllocationInfo AllocationInfo ) { FCallStack CallStack = FStreamInfo.GlobalInstance.CallStackArray[AllocationInfo.CallStackIndex]; // Iterate over existing nodes to see whether there is a match. foreach( TreeNode Node in ParentNode.Nodes ) { FNodePayload Payload = (FNodePayload) Node.Tag; // If there is a match, update the allocation size and return the current node. if( FStreamInfo.GlobalInstance.CallStackAddressArray[Payload.AddressIndex].FunctionIndex == FStreamInfo.GlobalInstance.CallStackAddressArray[AddressIndex].FunctionIndex ) { Payload.AllocationSize += AllocationInfo.Size; Payload.AllocationCount += AllocationInfo.Count; Payload.CallStacks.Add(CallStack); // Return current node as parent for next iteration. return Node; } } // If we made it here it means that we need to add a new node. string NodeName = FStreamInfo.GlobalInstance.NameArray[FStreamInfo.GlobalInstance.CallStackAddressArray[AddressIndex].FunctionIndex]; TreeNode NewNode = new TreeNode( NodeName ); // Create payload for the node and associate it. FNodePayload NewPayload = new FNodePayload(AddressIndex, AllocationInfo.Size, AllocationInfo.Count, CallStack); NewNode.Tag = NewPayload; // Add to parent node and return new node as subsequent parent. ParentNode.Nodes.Add( NewNode ); return NewNode; }
private static void AddCallStackToGraph( TreeNode RootNode, FCallStack CallStack, FCallStackAllocationInfo AllocationInfo, int ParentFunctionIndex, bool bInvertCallStacks ) { // Used to determine whether it is okay to add address to the graph. An index of -1 means we don't care about parenting. bool bAllowNodeUpdate = ParentFunctionIndex == -1; // Iterate over each address and add it to the tree view. TreeNode CurrentNode = RootNode; for( int AdressIndex = 0; AdressIndex < CallStack.AddressIndices.Count; AdressIndex++ ) { int AddressIndex; if( bInvertCallStacks ) { AddressIndex = CallStack.AddressIndices[ CallStack.AddressIndices.Count - 1 - AdressIndex ]; } else { AddressIndex = CallStack.AddressIndices[ AdressIndex ]; } // Filter based on function if wanted. This means we only include callstacks that contain the function // and we also reparent the callstack to start at the first occurence of the function. if( ParentFunctionIndex != -1 && !bAllowNodeUpdate ) { bAllowNodeUpdate = FStreamInfo.GlobalInstance.CallStackAddressArray[ AddressIndex ].FunctionIndex == ParentFunctionIndex; } if( bAllowNodeUpdate ) { // Update the node for this address. The return value of the function will be the new parent node. CurrentNode = UpdateNodeAndPayload( CurrentNode, AddressIndex, AllocationInfo ); } } }
/// <summary> Updates internal state with free. </summary> private static bool HandleFree(FStreamToken StreamToken, FStreamSnapshot Snapshot, Dictionary <ulong, FCallStackAllocationInfo> PointerToPointerInfoMap, out FCallStackAllocationInfo FreedAllocInfo) { if (!PointerToPointerInfoMap.TryGetValue(StreamToken.Pointer, out FreedAllocInfo)) { Debug.WriteLine("Free without malloc: " + StreamToken.Pointer + " in pool " + StreamToken.Pool); return(false); } // Maintain timeline view Snapshot.AllocationSize -= FreedAllocInfo.Size; Snapshot.AllocationCount++; if (Snapshot.AllocationCount % AllocationsPerSlice == 0) { FMemorySlice Slice = new FMemorySlice(Snapshot); Snapshot.OverallMemorySlice.Add(Slice); } // Remove freed pointer if it is in the array. PointerToPointerInfoMap.Remove(StreamToken.Pointer); return(true); }
// 0 == deepest part of callstack void OpenCallstackEntryInVS( FCallStackAllocationInfo AllocationInfo, int Index ) { FCallStack CallStack = FStreamInfo.GlobalInstance.CallStackArray[ AllocationInfo.CallStackIndex ]; FCallStackAddress Address = FStreamInfo.GlobalInstance.CallStackAddressArray[ CallStack.AddressIndices[ CallStack.AddressIndices.Count - 1 - Index ] ]; string Filename = FStreamInfo.GlobalInstance.NameArray[ Address.FilenameIndex ]; try { OpenFileInVisualStudio( Filename, Address.LineNumber ); } catch( Exception ) { } }
/// <summary> Compare helper function, sorting FCallStackAllocation by abs(size). </summary> private static int CompareAbsSize(FCallStackAllocationInfo A, FCallStackAllocationInfo B) { return(Math.Sign(Math.Abs(B.TotalSize) - Math.Abs(A.TotalSize))); }
private static FCallGraphNode UpdateNodeAndPayload(FCallGraphNode ParentNode, int AddressIndex, FCallStackAllocationInfo AllocationInfo) { int FunctionIndex = FStreamInfo.GlobalInstance.CallStackAddressArray[AddressIndex].FunctionIndex; FCallStack CallStack = FStreamInfo.GlobalInstance.CallStackArray[AllocationInfo.CallStackIndex]; var TotalAllocationSize = AllocationInfo.TotalSize; var TotalAllocationCount = AllocationInfo.TotalCount; // Iterate over existing nodes to see whether there is a match. foreach (FCallGraphNode Node in ParentNode.Children) { // If there is a match, update the allocation size and return the current node. if (FStreamInfo.GlobalInstance.CallStackAddressArray[Node.AddressIndex].FunctionIndex == FunctionIndex) { Node.AllocationSize += TotalAllocationSize; Node.AllocationCount += TotalAllocationCount; Node.CallStacks.Add(CallStack); // Return current node as parent for next iteration. return(Node); } } // If we made it here it means that we need to add a new node. string FunctionName = FStreamInfo.GlobalInstance.NameArray[FunctionIndex]; FCallGraphNode NewNode = new FCallGraphNode(ParentNode, AddressIndex, TotalAllocationSize, TotalAllocationCount, FunctionName, CallStack); return(NewNode); }
public static void ParseSnapshot(ListViewEx ExclusiveListView, List <FCallStackAllocationInfo> CallStackList, bool bShouldSortBySize, string FilterText) { const int MaximumEntries = 400; // Progress bar. long ProgressInterval = MaximumEntries / 20; long NextProgressUpdate = ProgressInterval; int CallStackCurrent = 0; OwnerWindow.ToolStripProgressBar.Value = 0; OwnerWindow.ToolStripProgressBar.Visible = true; OwnerWindow.UpdateStatus("Updating exclusive list view for " + OwnerWindow.CurrentFilename); ExclusiveListView.BeginUpdate(); ExclusiveListView.ListViewItemSorter = null; // clear this to avoid a Sort for each call to Add bool bFilterIn = OwnerWindow.IsFilteringIn(); using (FScopedLogTimer ParseTiming = new FScopedLogTimer("FExclusiveListViewParser.ParseSnapshot")) { var FilteredCallstackList = new List <FCallStackAllocationInfo>(CallStackList.Count); foreach (var AllocationInfo in CallStackList) { var FilteredAllocationInfo = AllocationInfo.GetAllocationInfoForTags(OwnerWindow.GetTagsFilter(), bFilterIn); if (FilteredAllocationInfo.TotalCount != 0) { FilteredCallstackList.Add(FilteredAllocationInfo); } } // Sort based on passed in metric. if (bShouldSortBySize) { FilteredCallstackList.Sort(CompareAbsSize); } else { FilteredCallstackList.Sort(CompareCount); } // Figure out total size and count for percentages. long TotalSize = 0; long TotalCount = 0; foreach (FCallStackAllocationInfo AllocationInfo in FilteredCallstackList) { // Apply optional filter. if (FStreamInfo.GlobalInstance.CallStackArray[AllocationInfo.CallStackIndex].RunFilters(FilterText, OwnerWindow.Options.ClassGroups, bFilterIn, OwnerWindow.SelectedMemoryPool)) { TotalSize += AllocationInfo.TotalSize; TotalCount += AllocationInfo.TotalCount; } } // Clear out existing entries and add top 400. ExclusiveListView.Items.Clear(); for (int CallStackIndex = 0; CallStackIndex < FilteredCallstackList.Count && ExclusiveListView.Items.Count <= MaximumEntries; CallStackIndex++) { // Update progress bar. if (CallStackCurrent >= NextProgressUpdate) { OwnerWindow.ToolStripProgressBar.PerformStep(); NextProgressUpdate += ProgressInterval; Debug.WriteLine("FExclusiveListViewParser.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20"); } CallStackCurrent++; FCallStackAllocationInfo AllocationInfo = FilteredCallstackList[CallStackIndex]; // Apply optional filter. FCallStack CallStack = FStreamInfo.GlobalInstance.CallStackArray[AllocationInfo.CallStackIndex]; if (CallStack.RunFilters(FilterText, OwnerWindow.Options.ClassGroups, bFilterIn, OwnerWindow.SelectedMemoryPool)) { string FunctionName = ""; int FirstStackFrameIndex; if (OwnerWindow.ContainersSplitButton.Text == " Show Containers") { FirstStackFrameIndex = CallStack.AddressIndices.Count - 1; } else { FirstStackFrameIndex = CallStack.FirstNonContainer; } do { FCallStackAddress Address = FStreamInfo.GlobalInstance.CallStackAddressArray[CallStack.AddressIndices[FirstStackFrameIndex]]; FunctionName = FStreamInfo.GlobalInstance.NameArray[Address.FunctionIndex]; FirstStackFrameIndex--; }while(UnhelpfulCallSites.Contains(FunctionName) && FirstStackFrameIndex > 0); var AllocationSize = AllocationInfo.TotalSize; var AllocationCount = AllocationInfo.TotalCount; string SizeInKByte = String.Format("{0:0}", ( float )AllocationSize / 1024).PadLeft(10, ' '); string SizePercent = String.Format("{0:0.00}", ( float )AllocationSize / TotalSize * 100).PadLeft(10, ' '); string Count = String.Format("{0:0}", AllocationCount).PadLeft(10, ' '); string CountPercent = String.Format("{0:0.00}", ( float )AllocationCount / TotalCount * 100).PadLeft(10, ' '); string GroupName = (CallStack.Group != null) ? CallStack.Group.Name : "Ungrouped"; string[] Row = new string[] { SizeInKByte, SizePercent, Count, CountPercent, GroupName, FunctionName }; ListViewItem Item = new ListViewItem(Row); Item.Tag = AllocationInfo; ExclusiveListView.Items.Add(Item); } } } var ColumnSorter = new MainWindow.FColumnSorter(); ColumnSorter.ColumnSortModeAscending = false; ColumnSorter.ColumnToSortBy = 0; ExclusiveListView.ListViewItemSorter = ColumnSorter; // Assignment automatically calls Sort ExclusiveListView.SetSortArrow(ColumnSorter.ColumnToSortBy, ColumnSorter.ColumnSortModeAscending); ExclusiveListView.EndUpdate(); OwnerWindow.ToolStripProgressBar.Visible = false; }
/// <summary> Compare helper function, sorting FCallStackAllocation by size. </summary> private static int CompareSize( FCallStackAllocationInfo A, FCallStackAllocationInfo B ) { return Math.Sign( B.Size - A.Size ); }
private static TreeNode UpdateNodeAndPayload(TreeNode ParentNode, int AddressIndex, FCallStackAllocationInfo AllocationInfo) { FCallStack CallStack = FStreamInfo.GlobalInstance.CallStackArray[AllocationInfo.CallStackIndex]; // Iterate over existing nodes to see whether there is a match. foreach (TreeNode Node in ParentNode.Nodes) { FNodePayload Payload = (FNodePayload)Node.Tag; // If there is a match, update the allocation size and return the current node. if (FStreamInfo.GlobalInstance.CallStackAddressArray[Payload.AddressIndex].FunctionIndex == FStreamInfo.GlobalInstance.CallStackAddressArray[AddressIndex].FunctionIndex) { Payload.AllocationSize += AllocationInfo.Size; Payload.AllocationCount += AllocationInfo.Count; Payload.CallStacks.Add(CallStack); // Return current node as parent for next iteration. return(Node); } } // If we made it here it means that we need to add a new node. string NodeName = FStreamInfo.GlobalInstance.NameArray[FStreamInfo.GlobalInstance.CallStackAddressArray[AddressIndex].FunctionIndex]; TreeNode NewNode = new TreeNode(NodeName); // Create payload for the node and associate it. FNodePayload NewPayload = new FNodePayload(AddressIndex, AllocationInfo.Size, AllocationInfo.Count, CallStack); NewNode.Tag = NewPayload; // Add to parent node and return new node as subsequent parent. ParentNode.Nodes.Add(NewNode); return(NewNode); }
/// <summary> Compare helper function, sorting FCallStackAllocation by abs(size). </summary> private static int CompareAbsSize(FCallStackAllocationInfo A, FCallStackAllocationInfo B) { return Math.Sign(Math.Abs(B.Size) - Math.Abs(A.Size)); }
/// <summary> Diffs two snapshots and creates a result one. </summary> public static FStreamSnapshot DiffSnapshots(FStreamSnapshot Old, FStreamSnapshot New) { // Create result snapshot object. FStreamSnapshot ResultSnapshot = new FStreamSnapshot("Diff " + Old.Description + " <-> " + New.Description); using (FScopedLogTimer LoadingTime = new FScopedLogTimer("FStreamSnapshot.DiffSnapshots")) { // Copy over allocation count so we can track where the graph starts ResultSnapshot.AllocationCount = Old.AllocationCount; Debug.Assert(Old.MetricArray.Count == New.MetricArray.Count); ResultSnapshot.MetricArray = new List <long>(Old.MetricArray.Count); for (int CallstackIndex = 0; CallstackIndex < Old.MetricArray.Count; CallstackIndex++) { ResultSnapshot.MetricArray.Add(New.MetricArray[CallstackIndex] - Old.MetricArray[CallstackIndex]); } ResultSnapshot.MemoryAllocationStats3 = FMemoryAllocationStatsV3.Diff(Old.MemoryAllocationStats3, New.MemoryAllocationStats3); ResultSnapshot.MemoryAllocationStats4 = FMemoryAllocationStatsV4.Diff(Old.MemoryAllocationStats4, New.MemoryAllocationStats4); ResultSnapshot.StreamIndex = New.StreamIndex; ResultSnapshot.bIsDiffResult = true; ResultSnapshot.AllocationMaxSize = New.AllocationMaxSize - Old.AllocationMaxSize; ResultSnapshot.AllocationSize = New.AllocationSize - Old.AllocationSize; ResultSnapshot.CurrentTime = 0; ResultSnapshot.ElapsedTime = New.CurrentTime - Old.CurrentTime; ResultSnapshot.FrameNumber = New.FrameNumber - Old.FrameNumber; ResultSnapshot.LoadedLevels = New.LoadedLevels; // These lists are guaranteed to be sorted by callstack index. List <FCallStackAllocationInfo> OldActiveCallStackList = Old.ActiveCallStackList; List <FCallStackAllocationInfo> NewActiveCallStackList = New.ActiveCallStackList; List <FCallStackAllocationInfo> ResultActiveCallStackList = new List <FCallStackAllocationInfo>(FStreamInfo.GlobalInstance.CallStackArray.Count); int OldIndex = 0; int NewIndex = 0; while (true) { FCallStackAllocationInfo OldAllocInfo = OldActiveCallStackList[OldIndex]; FCallStackAllocationInfo NewAllocInfo = NewActiveCallStackList[NewIndex]; if (OldAllocInfo.CallStackIndex == NewAllocInfo.CallStackIndex) { long ResultSize = NewAllocInfo.Size - OldAllocInfo.Size; int ResultCount = NewAllocInfo.Count - OldAllocInfo.Count; if (ResultSize != 0 || ResultCount != 0) { ResultActiveCallStackList.Add(new FCallStackAllocationInfo(ResultSize, NewAllocInfo.CallStackIndex, ResultCount)); } OldIndex++; NewIndex++; } else if (OldAllocInfo.CallStackIndex > NewAllocInfo.CallStackIndex) { ResultActiveCallStackList.Add(NewAllocInfo); NewIndex++; } else // OldAllocInfo.CallStackIndex < NewAllocInfo.CallStackIndex { ResultActiveCallStackList.Add(new FCallStackAllocationInfo(-OldAllocInfo.Size, OldAllocInfo.CallStackIndex, -OldAllocInfo.Count)); OldIndex++; } if (OldIndex >= OldActiveCallStackList.Count) { for ( ; NewIndex < NewActiveCallStackList.Count; NewIndex++) { ResultActiveCallStackList.Add(NewActiveCallStackList[NewIndex]); } break; } if (NewIndex >= NewActiveCallStackList.Count) { for ( ; OldIndex < OldActiveCallStackList.Count; OldIndex++) { ResultActiveCallStackList.Add(OldActiveCallStackList[OldIndex]); } break; } } // Check that list was correctly constructed. for (int CallstackIndex = 0; CallstackIndex < ResultActiveCallStackList.Count - 1; CallstackIndex++) { Debug.Assert(ResultActiveCallStackList[CallstackIndex].CallStackIndex < ResultActiveCallStackList[CallstackIndex + 1].CallStackIndex); } ResultActiveCallStackList.TrimExcess(); ResultSnapshot.ActiveCallStackList = ResultActiveCallStackList; // Iterate over new lifetime callstack info and subtract previous one. for (int CallStackIndex = 0; CallStackIndex < New.LifetimeCallStackList.Count; CallStackIndex++) { ResultSnapshot.LifetimeCallStackList[CallStackIndex] = FCallStackAllocationInfo.Diff( New.LifetimeCallStackList[CallStackIndex], Old.LifetimeCallStackList[CallStackIndex]); } // Handle overall memory timeline if (New.OverallMemorySlice.Count > Old.OverallMemorySlice.Count) { ResultSnapshot.OverallMemorySlice = new List <FMemorySlice>(New.OverallMemorySlice); ResultSnapshot.OverallMemorySlice.RemoveRange(0, Old.OverallMemorySlice.Count); } else { ResultSnapshot.OverallMemorySlice = new List <FMemorySlice>(Old.OverallMemorySlice); ResultSnapshot.OverallMemorySlice.RemoveRange(0, New.OverallMemorySlice.Count); ResultSnapshot.OverallMemorySlice.Reverse(); } } return(ResultSnapshot); }
/// <summary> Compare helper function, sorting FCallStackAllocation by count. </summary> private static int CompareCount( FCallStackAllocationInfo A, FCallStackAllocationInfo B ) { return Math.Sign( B.Count - A.Count ); }
/// <summary> Compare helper function, sorting FCallStackAllocation by size. </summary> private static int CompareSize(FCallStackAllocationInfo A, FCallStackAllocationInfo B) { return(Math.Sign(B.Size - A.Size)); }
private static FCallGraphNode UpdateNodeAndPayload( FCallGraphNode ParentNode, int AddressIndex, FCallStackAllocationInfo AllocationInfo ) { int FunctionIndex = FStreamInfo.GlobalInstance.CallStackAddressArray[AddressIndex].FunctionIndex; FCallStack CallStack = FStreamInfo.GlobalInstance.CallStackArray[AllocationInfo.CallStackIndex]; // Iterate over existing nodes to see whether there is a match. foreach (FCallGraphNode Node in ParentNode.Children) { // If there is a match, update the allocation size and return the current node. if (FStreamInfo.GlobalInstance.CallStackAddressArray[Node.AddressIndex].FunctionIndex == FunctionIndex) { Node.AllocationSize += AllocationInfo.Size; Node.AllocationCount += AllocationInfo.Count; Node.CallStacks.Add(CallStack); // Return current node as parent for next iteration. return Node; } } // If we made it here it means that we need to add a new node. string FunctionName = FStreamInfo.GlobalInstance.NameArray[FunctionIndex]; FCallGraphNode NewNode = new FCallGraphNode(ParentNode, AddressIndex, AllocationInfo.Size, AllocationInfo.Count, FunctionName, CallStack); return NewNode; }