Exemple #1
0
        public static void UnsafeBitmapClick(MouseEventArgs e)
        {
            OwnerWindow.MemoryBitmapCallStackListView.Items.Clear();

            OwnerWindow.MemoryBitmapAllocationHistoryListView.BeginUpdate();
            OwnerWindow.MemoryBitmapAllocationHistoryListView.Items.Clear();

            ulong  PointerFromPixel = FMemoryBitmapParser.GetPointerFromPixel(MemoryBitmap.Width, BytesPerPixel, e.X - MEMORY_BITMAP_LEFT_MARGIN, e.Y);
            string FilterText       = OwnerWindow.FilterTextBox.Text.ToUpperInvariant();

            using (FScopedLogTimer ParseTiming = new FScopedLogTimer("FMemoryBitmapParser.UnsafeBitmapClick"))
            {
                foreach (FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray)
                {
                    if (CallStack.RunFilters(FilterText, OwnerWindow.Options.ClassGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool))
                    {
                        foreach (FAllocationLifecycle AllocLifecycle in CallStack.CompleteLifecycles)
                        {
                            ProcessLifecycleForPixel(PointerFromPixel, CallStack, AllocLifecycle, true);
                        }

                        foreach (KeyValuePair <ulong, FAllocationLifecycle> AllocLifecycle in CallStack.IncompleteLifecycles)
                        {
                            ProcessLifecycleForPixel(PointerFromPixel, CallStack, AllocLifecycle.Value, false);
                        }
                    }
                }
            }

            // Pointers that were malloced and then realloced by a different callstack will have missing end frames
            // (marked with "-1"), but those end frames are guaranteed to be the same as the start frame of the
            // following allocation at this pointer, so we can just go over the list and fix up the references.
            for (int ItemIndex = 0; ItemIndex < OwnerWindow.MemoryBitmapAllocationHistoryListView.Items.Count; ItemIndex++)
            {
                if (OwnerWindow.MemoryBitmapAllocationHistoryListView.Items[ItemIndex].SubItems.Count > 0 &&
                    OwnerWindow.MemoryBitmapAllocationHistoryListView.Items[ItemIndex].SubItems[1].Text == "-1" &&
                    ItemIndex + 1 < OwnerWindow.MemoryBitmapAllocationHistoryListView.Items.Count)
                {
                    OwnerWindow.MemoryBitmapAllocationHistoryListView.Items[ItemIndex].SubItems[1].Text = OwnerWindow.MemoryBitmapAllocationHistoryListView.Items[ItemIndex + 1].Text;
                }
            }

            OwnerWindow.MemoryBitmapAllocationHistoryListView.EndUpdate();

            if (OwnerWindow.MemoryBitmapAllocationHistoryListView.Items.Count > 0)
            {
                OwnerWindow.MemoryBitmapAllocationHistoryListView.Items[GetMemoryBitmapActiveAllocationForStreamIndex(OwnerWindow.CurrentSnapshot.StreamIndex)].Selected = true;
            }
            else
            {
                // refresh panel to clear selection
                OwnerWindow.MemoryBitmapPanel.Invalidate();
            }
        }
Exemple #2
0
		public static void ParseSnapshot( List<FCallStackAllocationInfo> CallStackList, string FilterText )
		{
			// Progress bar
			long ProgressInterval = CallStackList.Count / 20;
			long NextProgressUpdate = ProgressInterval;
			int CallStackCurrent = 0;
			OwnerWindow.ToolStripProgressBar.Value = 0;
			OwnerWindow.ToolStripProgressBar.Visible = true;

			OwnerWindow.UpdateStatus( "Updating histogram view for " + OwnerWindow.CurrentFilename );

			List<ClassGroup> CallStackGroups = OwnerWindow.Options.ClassGroups;
			List<FHistogramBar>[] Bars = new List<FHistogramBar>[ NUM_MEMORY_BANKS ];

			for( int BankIndex = 0; BankIndex < Bars.Length; BankIndex++ )
			{
				Bars[ BankIndex ] = new List<FHistogramBar>();

				// The first bar in each column is for callstacks unmatched by any pattern.
				Bars[ BankIndex ].Add( new FHistogramBar( "Other", Color.White ) );

				// Add all groups to all memory bank columns.
				foreach( ClassGroup CallStackGroup in CallStackGroups )
				{
					Bars[ BankIndex ].Add( new FHistogramBar( CallStackGroup ) );
				}
			}

			using( FScopedLogTimer ParseTiming = new FScopedLogTimer( "HistogramParser.ParseSnapshot" ) )
			{
				long Size = 0;
				int Count = 0;

				// JarekS@TODO Multithreading
				foreach( FCallStackAllocationInfo AllocationInfo in CallStackList )
				{
					// Update progress bar.
					if( CallStackCurrent >= NextProgressUpdate )
					{
						OwnerWindow.ToolStripProgressBar.PerformStep();
						NextProgressUpdate += ProgressInterval;
						Debug.WriteLine( "FHistogramParser.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20" );
					}
					CallStackCurrent++;

					FCallStack OriginalCallStack = FStreamInfo.GlobalInstance.CallStackArray[ AllocationInfo.CallStackIndex ];
					if( OriginalCallStack.RunFilters( FilterText, CallStackGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool ) )
					{
						bool bFound = false;
						int Column = FMemoryPoolInfo.GetMemoryPoolHistogramColumn( OriginalCallStack.MemoryPool );
						if( Column == -1 )
						{
							// If the callstack is in multiple pools, just put it in the first bank.
							// The user has already been warned about multi-pool callstacks.
							Column = 0;
						}

						for( int GroupIndex = 0; GroupIndex < CallStackGroups.Count; GroupIndex++ )
						{
							foreach( CallStackPattern CallStackPatternIt in CallStackGroups[ GroupIndex ].CallStackPatterns )
							{
								foreach( FCallStack CallStack in CallStackPatternIt.CallStacks )
								{
									if( CallStack == FStreamInfo.GlobalInstance.CallStackArray[ AllocationInfo.CallStackIndex ] )
									{
										Bars[ Column ][ GroupIndex + 1 ].AddAllocation( AllocationInfo );
										bFound = true;
										goto HackyBreakAll;
									}
								}
							}
						}

HackyBreakAll:

						if( !bFound )
						{
							// No pattern matched this callstack, so add it to the Other bar
							Bars[ Column ][ 0 ].AddAllocation( AllocationInfo );
						}
					}

					Size += AllocationInfo.Size;
					Count += AllocationInfo.Count;
				}
			}

			OwnerWindow.ToolStripProgressBar.Visible = false;
			HistogramBars = Bars;
				
			// Select first valid histogram bar.
			SelectFirstValidHistogramBar();
		}
Exemple #3
0
        public static Bitmap ParseSnapshot(int BitmapWidth, int BitmapHeight, ulong SnapshotStreamIndex, ulong DiffbaseSnapshotStreamIndex, string FilterText)
        {
            // Progress bar.
            OwnerWindow.ToolStripProgressBar.Value   = 0;
            OwnerWindow.ToolStripProgressBar.Visible = true;

            AllocatedMemorySize = 0;

            Bitmap   MyBitmap   = new Bitmap(BitmapWidth, BitmapHeight);
            Graphics MyGraphics = Graphics.FromImage(MyBitmap);

            int PixelCount = BitmapWidth * BitmapHeight;

            BytesPerPixel = ( int )Math.Ceiling(( double )MemorySize / ( double )PixelCount);
            long BytesPerLine = ( long )BytesPerPixel * ( long )BitmapWidth;

            ulong MemoryTop = MemoryBase + MemorySize;

            // JarekS@TODO Multithreading
            if (OwnerWindow.MemoryBitmapHeatMapButton.Checked)
            {
                // clear bitmap to black
                MyGraphics.FillRectangle(Brushes.Black, 0, 0, BitmapWidth, BitmapHeight);

                if (DiffbaseSnapshotStreamIndex == FStreamInfo.INVALID_STREAM_INDEX)
                {
                    // if there is no base snapshot, start at the beginning of the profile
                    DiffbaseSnapshotStreamIndex = 0;
                }

                using (FScopedLogTimer ParseTiming = new FScopedLogTimer("FMemoryBitmapParser.ParseSnapshot.HeatMap"))
                {
                    long ProgressInterval   = FStreamInfo.GlobalInstance.CallStackArray.Count / 20;
                    long NextProgressUpdate = ProgressInterval;
                    int  CallStackCurrent   = 0;

                    foreach (FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray)
                    {
                        // Update progress bar.
                        if (CallStackCurrent >= NextProgressUpdate)
                        {
                            OwnerWindow.ToolStripProgressBar.PerformStep();
                            NextProgressUpdate += ProgressInterval;
                            Debug.WriteLine("FMemoryBitmapParser.ParseSnapshot.HeatMap " + OwnerWindow.ToolStripProgressBar.Value + "/20");
                        }
                        CallStackCurrent++;

                        if (CallStack.RunFilters(FilterText, OwnerWindow.Options.ClassGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool))
                        {
                            foreach (FAllocationLifecycle AllocLifecycle in CallStack.CompleteLifecycles)
                            {
                                if (AllocLifecycle.AllocEvent.StreamIndex < SnapshotStreamIndex)
                                {
                                    if (AllocLifecycle.AllocEvent.StreamIndex > DiffbaseSnapshotStreamIndex &&
                                        AllocLifecycle.AllocEvent.Pointer + ( ulong )AllocLifecycle.AllocEvent.Size > MemoryBase &&
                                        AllocLifecycle.AllocEvent.Pointer < MemoryTop)
                                    {
                                        FillMemoryArea(MyGraphics, HeatPen, BitmapWidth, BytesPerLine, BytesPerPixel, AllocLifecycle.AllocEvent.Pointer - MemoryBase, AllocLifecycle.AllocEvent.Size);
                                    }

                                    if (AllocLifecycle.ReallocsEvents != null)
                                    {
                                        for (int i = 0; i < AllocLifecycle.ReallocsEvents.Count; i++)
                                        {
                                            FReallocationEvent ReallocEvent = AllocLifecycle.ReallocsEvents[i];
                                            if (ReallocEvent.StreamIndex < SnapshotStreamIndex)
                                            {
                                                if (ReallocEvent.StreamIndex > DiffbaseSnapshotStreamIndex &&
                                                    ReallocEvent.NewPointer + ( ulong )ReallocEvent.NewSize > MemoryBase &&
                                                    ReallocEvent.NewPointer < MemoryTop)
                                                {
                                                    FillMemoryArea(MyGraphics, HeatPen, BitmapWidth, BytesPerLine, BytesPerPixel, ReallocEvent.NewPointer - MemoryBase, ReallocEvent.NewSize);
                                                }
                                            }
                                            else
                                            {
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                bool bDiffAllocs = DiffbaseSnapshotStreamIndex != FStreamInfo.INVALID_STREAM_INDEX;
                long ProgressIntervalCallstack = FStreamInfo.GlobalInstance.CallStackArray.Count / (bDiffAllocs ? 5 : 10);
                long ProgressIntervalGroup     = OwnerWindow.Options.ClassGroups.Count / 10;
                long NextProgressUpdate        = ProgressIntervalCallstack;
                int  CallStackCurrent          = 0;

                using (FScopedLogTimer ParseTiming = new FScopedLogTimer("FMemoryBitmapParser.ParseSnapshot.AllAllocations"))
                {
                    // render all allocations in black
                    foreach (FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray)
                    {
                        // Update progress bar.
                        if (CallStackCurrent >= NextProgressUpdate)
                        {
                            OwnerWindow.ToolStripProgressBar.PerformStep();
                            NextProgressUpdate += ProgressIntervalCallstack;
                            Debug.WriteLine("FMemoryBitmapParser.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20");
                        }
                        CallStackCurrent++;

                        if (CallStack.RunFilters(FilterText, OwnerWindow.Options.ClassGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool))
                        {
                            // Process complete life cycles.
                            foreach (FAllocationLifecycle AllocLifecycle in CallStack.CompleteLifecycles)
                            {
                                ProcessLifecycle(Pens.Black, BitmapWidth, SnapshotStreamIndex, MyGraphics, BytesPerLine, AllocLifecycle, 1);
                            }
                            // Process incomplete life cycles.
                            foreach (KeyValuePair <ulong, FAllocationLifecycle> AllocLifecycle in CallStack.IncompleteLifecycles)
                            {
                                ProcessLifecycle(Pens.Black, BitmapWidth, SnapshotStreamIndex, MyGraphics, BytesPerLine, AllocLifecycle.Value, 1);
                            }
                        }
                    }
                }

                using (FScopedLogTimer ParseTiming = new FScopedLogTimer("FMemoryBitmapParser.ParseSnapshot.HistogramAllocations"))
                {
                    int  CurrentGroup            = 0;
                    long NextGroupProgressUpdate = ProgressIntervalGroup;
                    // render histogram allocations in colour
                    foreach (ClassGroup CallStackGroup in OwnerWindow.Options.ClassGroups)
                    {
                        // Update progress bar.
                        if (CurrentGroup >= NextGroupProgressUpdate)
                        {
                            OwnerWindow.ToolStripProgressBar.PerformStep();
                            NextGroupProgressUpdate += ProgressIntervalGroup;
                            Debug.WriteLine("FMemoryBitmapParser.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20");
                        }
                        CurrentGroup++;

                        Pen GroupPen = new Pen(CallStackGroup.Color);

                        foreach (CallStackPattern CallStackPatternIt in CallStackGroup.CallStackPatterns)
                        {
                            foreach (FCallStack CallStack in CallStackPatternIt.GetCallStacks())
                            {
                                if (CallStack.RunFilters(FilterText, OwnerWindow.Options.ClassGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool))
                                {
                                    foreach (FAllocationLifecycle AllocLifecycle in CallStack.CompleteLifecycles)
                                    {
                                        ProcessLifecycle(GroupPen, BitmapWidth, SnapshotStreamIndex, MyGraphics, BytesPerLine, AllocLifecycle, 0);
                                    }

                                    foreach (KeyValuePair <ulong, FAllocationLifecycle> AllocLifecycle in CallStack.IncompleteLifecycles)
                                    {
                                        ProcessLifecycle(GroupPen, BitmapWidth, SnapshotStreamIndex, MyGraphics, BytesPerLine, AllocLifecycle.Value, 0);
                                    }
                                }
                            }
                        }

                        GroupPen.Dispose();
                    }
                }

                using (FScopedLogTimer ParseTiming = new FScopedLogTimer("FMemoryBitmapParser.ParseSnapshot.DiffAllocations"))
                {
                    CallStackCurrent   = 0;
                    NextProgressUpdate = ProgressIntervalCallstack;

                    // render white where allocation exists in diff-base snapshot
                    if (DiffbaseSnapshotStreamIndex != FStreamInfo.INVALID_STREAM_INDEX)
                    {
                        foreach (FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray)
                        {
                            // Update progress bar.
                            if (CallStackCurrent >= NextProgressUpdate)
                            {
                                OwnerWindow.ToolStripProgressBar.PerformStep();
                                NextProgressUpdate += ProgressIntervalCallstack;
                                Debug.WriteLine("FMemoryBitmapParser.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20");
                            }
                            CallStackCurrent++;

                            if (CallStack.RunFilters(FilterText, OwnerWindow.Options.ClassGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool))
                            {
                                foreach (FAllocationLifecycle AllocLifecycle in CallStack.CompleteLifecycles)
                                {
                                    ProcessLifecycle(Pens.AliceBlue, BitmapWidth, DiffbaseSnapshotStreamIndex, MyGraphics, BytesPerLine, AllocLifecycle, -1);
                                }

                                foreach (KeyValuePair <ulong, FAllocationLifecycle> AllocLifecycle in CallStack.IncompleteLifecycles)
                                {
                                    ProcessLifecycle(Pens.AliceBlue, BitmapWidth, DiffbaseSnapshotStreamIndex, MyGraphics, BytesPerLine, AllocLifecycle.Value, -1);
                                }
                            }
                        }
                    }
                }
            }

            // shade space at bottom of image to mark end of memory space.
            ulong MemoryTopPointer = MemorySize;
            int   StartX           = ( int )(MemoryTopPointer % ( uint )BytesPerLine / ( uint )BytesPerPixel);
            int   StartY           = ( int )(MemoryTopPointer / ( uint )BytesPerLine);

            MyGraphics.DrawLine(OutOfSpacePen, StartX, StartY, BitmapWidth - 1, StartY);
            if (StartY + 1 < BitmapHeight)
            {
                MyGraphics.FillRectangle(OutOfSpacePen.Brush, 0, StartY + 1, BitmapWidth, BitmapHeight - (StartY + 1));
            }

            MyGraphics.Dispose();

            OwnerWindow.ToolStripProgressBar.Visible = false;

            return(MyBitmap);
        }
        public static void ParseSnapshot(TreeView CallGraphTreeView, List <FCallStackAllocationInfo> CallStackList, bool bShouldSortBySize, string FilterText, bool bInvertCallStacks)
        {
            // Progress bar.
            OwnerWindow.ToolStripProgressBar.Value   = 0;
            OwnerWindow.ToolStripProgressBar.Visible = true;
            long ProgressInterval   = CallStackList.Count / 20;
            long NextProgressUpdate = ProgressInterval;
            int  CallStackCurrent   = 0;

            OwnerWindow.UpdateStatus("Updating call graph for " + OwnerWindow.CurrentFilename);

            CallGraphTreeView.BeginUpdate();

            // Clear out existing nodes and add two root nodes. One for regular call stacks and one for truncated ones.
            CallGraphTreeView.Nodes.Clear();
            TreeNode RegularNode   = new TreeNode("Full Callstacks");
            TreeNode TruncatedNode = new TreeNode("Truncated Callstacks");

            CallGraphTreeView.Nodes.Add(RegularNode);
            CallGraphTreeView.Nodes.Add(TruncatedNode);

            using (FScopedLogTimer ParseTiming = new FScopedLogTimer("FCallGraphTreeViewParser.ParseSnapshot"))
            {
                // Iterate over all call graph paths and add them to the graph.
                foreach (FCallStackAllocationInfo AllocationInfo in CallStackList)
                {
                    // Update progress bar.
                    if (CallStackCurrent >= NextProgressUpdate)
                    {
                        OwnerWindow.ToolStripProgressBar.PerformStep();
                        NextProgressUpdate += ProgressInterval;
                        Debug.WriteLine("FCallGraphTreeViewParser.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20");
                    }
                    CallStackCurrent++;

                    // Add this call graph to the tree view.
                    FCallStack CallStack = FStreamInfo.GlobalInstance.CallStackArray[AllocationInfo.CallStackIndex];
                    // Split the tree into full and truncated callstacks.
                    TreeNode RootNode = CallStack.bIsTruncated ? TruncatedNode : RegularNode;
                    // Don't bother with callstacks that don't have a contribution.
                    if (((AllocationInfo.Count != 0) || (AllocationInfo.Size != 0))
                        // Apply filter based on text representation of address.
                        && CallStack.RunFilters(FilterText, OwnerWindow.Options.ClassGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool))
                    {
                        // Add call stack to proper part of graph.
                        AddCallStackToGraph(RootNode, CallStack, AllocationInfo, ParentFunctionIndex, bInvertCallStacks);
                    }
                }
            }

            // Update the node text by prepending memory usage and allocation count.
            UpdateNodeText(RegularNode);
            UpdateNodeText(TruncatedNode);

            // Last but not least, set the node sorter property to sort nodes.
            if (bShouldSortBySize)
            {
                CallGraphTreeView.TreeViewNodeSorter = new FNodeSizeSorter();
            }
            else
            {
                CallGraphTreeView.TreeViewNodeSorter = new FNodeCountSorter();
            }

            CallGraphTreeView.EndUpdate();

            OwnerWindow.ToolStripProgressBar.Visible = false;
        }
Exemple #5
0
        /// <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> 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;
        }
        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;
        }
		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

			// Sort based on passed in metric.
			if( bShouldSortBySize )
			{
                CallStackList.Sort( CompareAbsSize );
			}
			else
			{
				CallStackList.Sort( CompareCount );
			}

			bool bFilterIn = OwnerWindow.IsFilteringIn();

			using( FScopedLogTimer ParseTiming = new FScopedLogTimer( "FExclusiveListViewParser.ParseSnapshot" ) )
			{
				// Figure out total size and count for percentages.
				long TotalSize = 0;
				long TotalCount = 0;
				foreach( FCallStackAllocationInfo AllocationInfo in CallStackList )
				{
					// Apply optional filter.
					if( FStreamInfo.GlobalInstance.CallStackArray[ AllocationInfo.CallStackIndex ].RunFilters( FilterText, OwnerWindow.Options.ClassGroups, bFilterIn, OwnerWindow.SelectedMemoryPool ) )
					{
						TotalSize += AllocationInfo.Size;
						TotalCount += AllocationInfo.Count;
					}
				}

				// Clear out existing entries and add top 400.
				ExclusiveListView.Items.Clear();
				for( int CallStackIndex = 0; CallStackIndex < CallStackList.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 = CallStackList[ 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 );

						string SizeInKByte = String.Format( "{0:0}", ( float )AllocationInfo.Size / 1024 ).PadLeft( 10, ' ' );
						string SizePercent = String.Format( "{0:0.00}", ( float )AllocationInfo.Size / TotalSize * 100 ).PadLeft( 10, ' ' );
						string Count = String.Format( "{0:0}", AllocationInfo.Count ).PadLeft( 10, ' ' );
						string CountPercent = String.Format( "{0:0.00}", ( float )AllocationInfo.Count / 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;
		}
		public static Bitmap ParseSnapshot( int BitmapWidth, int BitmapHeight, ulong SnapshotStreamIndex, ulong DiffbaseSnapshotStreamIndex, string FilterText )
		{
			// Progress bar.
			OwnerWindow.ToolStripProgressBar.Value = 0;
			OwnerWindow.ToolStripProgressBar.Visible = true;

			AllocatedMemorySize = 0;

			Bitmap MyBitmap = new Bitmap( BitmapWidth, BitmapHeight );
			Graphics MyGraphics = Graphics.FromImage( MyBitmap );

			int PixelCount = BitmapWidth * BitmapHeight;
			BytesPerPixel = ( int )Math.Ceiling( ( double )MemorySize / ( double )PixelCount );
			long BytesPerLine = ( long )BytesPerPixel * ( long )BitmapWidth;

			ulong MemoryTop = MemoryBase + MemorySize;

			// JarekS@TODO Multithreading
			if( OwnerWindow.MemoryBitmapHeatMapButton.Checked )
			{
				// clear bitmap to black
				MyGraphics.FillRectangle( Brushes.Black, 0, 0, BitmapWidth, BitmapHeight );

				if( DiffbaseSnapshotStreamIndex == FStreamInfo.INVALID_STREAM_INDEX )
				{
					// if there is no base snapshot, start at the beginning of the profile
					DiffbaseSnapshotStreamIndex = 0;
				}

				using( FScopedLogTimer ParseTiming = new FScopedLogTimer( "FMemoryBitmapParser.ParseSnapshot.HeatMap" ) )
				{
					long ProgressInterval = FStreamInfo.GlobalInstance.CallStackArray.Count / 20;
					long NextProgressUpdate = ProgressInterval;
					int CallStackCurrent = 0;

					foreach( FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray )
					{
						// Update progress bar.
						if( CallStackCurrent >= NextProgressUpdate )
						{
							OwnerWindow.ToolStripProgressBar.PerformStep();
							NextProgressUpdate += ProgressInterval;
							Debug.WriteLine( "FMemoryBitmapParser.ParseSnapshot.HeatMap " + OwnerWindow.ToolStripProgressBar.Value + "/20" );
						}
						CallStackCurrent++;

						if( CallStack.RunFilters( FilterText, OwnerWindow.Options.ClassGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool ) )
						{
							foreach( FAllocationLifecycle AllocLifecycle in CallStack.CompleteLifecycles )
							{
								if( AllocLifecycle.AllocEvent.StreamIndex < SnapshotStreamIndex )
								{
									if( AllocLifecycle.AllocEvent.StreamIndex > DiffbaseSnapshotStreamIndex
										&& AllocLifecycle.AllocEvent.Pointer + ( ulong )AllocLifecycle.AllocEvent.Size > MemoryBase
										&& AllocLifecycle.AllocEvent.Pointer < MemoryTop )
									{
										FillMemoryArea( MyGraphics, HeatPen, BitmapWidth, BytesPerLine, BytesPerPixel, AllocLifecycle.AllocEvent.Pointer - MemoryBase, AllocLifecycle.AllocEvent.Size );
									}

									if( AllocLifecycle.ReallocsEvents != null )
									{
										for( int i = 0; i < AllocLifecycle.ReallocsEvents.Count; i++ )
										{
											FReallocationEvent ReallocEvent = AllocLifecycle.ReallocsEvents[ i ];
											if( ReallocEvent.StreamIndex < SnapshotStreamIndex )
											{
												if( ReallocEvent.StreamIndex > DiffbaseSnapshotStreamIndex
													&& ReallocEvent.NewPointer + ( ulong )ReallocEvent.NewSize > MemoryBase
													&& ReallocEvent.NewPointer < MemoryTop )
												{
													FillMemoryArea( MyGraphics, HeatPen, BitmapWidth, BytesPerLine, BytesPerPixel, ReallocEvent.NewPointer - MemoryBase, ReallocEvent.NewSize );
												}
											}
											else
											{
												break;
											}
										}
									}
								}
							}
						}
					}
				}
			}
			else
			{
				bool bDiffAllocs = DiffbaseSnapshotStreamIndex != FStreamInfo.INVALID_STREAM_INDEX;
				long ProgressIntervalCallstack = FStreamInfo.GlobalInstance.CallStackArray.Count / ( bDiffAllocs ? 5 : 10 );
				long ProgressIntervalGroup = OwnerWindow.Options.ClassGroups.Count / 10;
				long NextProgressUpdate = ProgressIntervalCallstack;
				int CallStackCurrent = 0;

				using( FScopedLogTimer ParseTiming = new FScopedLogTimer( "FMemoryBitmapParser.ParseSnapshot.AllAllocations" ) )
				{
					// render all allocations in black
					foreach( FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray )
					{
						// Update progress bar.
						if( CallStackCurrent >= NextProgressUpdate )
						{
							OwnerWindow.ToolStripProgressBar.PerformStep();
							NextProgressUpdate += ProgressIntervalCallstack;
							Debug.WriteLine( "FMemoryBitmapParser.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20" );
						}
						CallStackCurrent++;

						if( CallStack.RunFilters( FilterText, OwnerWindow.Options.ClassGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool ) )
						{
							// Process complete life cycles.
							foreach( FAllocationLifecycle AllocLifecycle in CallStack.CompleteLifecycles )
							{
								ProcessLifecycle( Pens.Black, BitmapWidth, SnapshotStreamIndex, MyGraphics, BytesPerLine, AllocLifecycle, 1 );
							}
							// Process incomplete life cycles.
							foreach( KeyValuePair<ulong, FAllocationLifecycle> AllocLifecycle in CallStack.IncompleteLifecycles )
							{
								ProcessLifecycle( Pens.Black, BitmapWidth, SnapshotStreamIndex, MyGraphics, BytesPerLine, AllocLifecycle.Value, 1 );
							}
						}
					}
				}

				using( FScopedLogTimer ParseTiming = new FScopedLogTimer( "FMemoryBitmapParser.ParseSnapshot.HistogramAllocations" ) )
				{
					int CurrentGroup = 0;
					long NextGroupProgressUpdate = ProgressIntervalGroup;
					// render histogram allocations in colour
					foreach( ClassGroup CallStackGroup in OwnerWindow.Options.ClassGroups )
					{
						// Update progress bar.
						if( CurrentGroup >= NextGroupProgressUpdate )
						{
							OwnerWindow.ToolStripProgressBar.PerformStep();
							NextGroupProgressUpdate += ProgressIntervalGroup;
							Debug.WriteLine( "FMemoryBitmapParser.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20" );
						}
						CurrentGroup++;

						Pen GroupPen = new Pen( CallStackGroup.Color );

						foreach( CallStackPattern CallStackPatternIt in CallStackGroup.CallStackPatterns )
						{
							foreach( FCallStack CallStack in CallStackPatternIt.GetCallStacks() )
							{
								if( CallStack.RunFilters( FilterText, OwnerWindow.Options.ClassGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool ) )
								{
									foreach( FAllocationLifecycle AllocLifecycle in CallStack.CompleteLifecycles )
									{
										ProcessLifecycle( GroupPen, BitmapWidth, SnapshotStreamIndex, MyGraphics, BytesPerLine, AllocLifecycle, 0 );
									}

									foreach (KeyValuePair<ulong, FAllocationLifecycle> AllocLifecycle in CallStack.IncompleteLifecycles)
									{
										ProcessLifecycle( GroupPen, BitmapWidth, SnapshotStreamIndex, MyGraphics, BytesPerLine, AllocLifecycle.Value, 0 );
									}
								}
							}
						}

						GroupPen.Dispose();
					}
				}

				using( FScopedLogTimer ParseTiming = new FScopedLogTimer( "FMemoryBitmapParser.ParseSnapshot.DiffAllocations" ) )
				{
					CallStackCurrent = 0;
					NextProgressUpdate = ProgressIntervalCallstack;

					// render white where allocation exists in diff-base snapshot
					if( DiffbaseSnapshotStreamIndex != FStreamInfo.INVALID_STREAM_INDEX )
					{
						foreach( FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray )
						{
							// Update progress bar.
							if( CallStackCurrent >= NextProgressUpdate )
							{
								OwnerWindow.ToolStripProgressBar.PerformStep();
								NextProgressUpdate += ProgressIntervalCallstack;
								Debug.WriteLine( "FMemoryBitmapParser.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20" );
							}
							CallStackCurrent++;

							if( CallStack.RunFilters( FilterText, OwnerWindow.Options.ClassGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool ) )
							{
								foreach( FAllocationLifecycle AllocLifecycle in CallStack.CompleteLifecycles )
								{
									ProcessLifecycle( Pens.AliceBlue, BitmapWidth, DiffbaseSnapshotStreamIndex, MyGraphics, BytesPerLine, AllocLifecycle, -1 );
								}

								foreach( KeyValuePair<ulong, FAllocationLifecycle> AllocLifecycle in CallStack.IncompleteLifecycles )
								{
									ProcessLifecycle( Pens.AliceBlue, BitmapWidth, DiffbaseSnapshotStreamIndex, MyGraphics, BytesPerLine, AllocLifecycle.Value, -1 );
								}
							}
						}
					}
				}
			}

			// shade space at bottom of image to mark end of memory space.
			ulong MemoryTopPointer = MemorySize;
			int StartX = ( int )( MemoryTopPointer % ( uint )BytesPerLine / ( uint )BytesPerPixel );
			int StartY = ( int )( MemoryTopPointer / ( uint )BytesPerLine );
			MyGraphics.DrawLine( OutOfSpacePen, StartX, StartY, BitmapWidth - 1, StartY );
			if( StartY + 1 < BitmapHeight )
			{
				MyGraphics.FillRectangle( OutOfSpacePen.Brush, 0, StartY + 1, BitmapWidth, BitmapHeight - ( StartY + 1 ) );
			}

			MyGraphics.Dispose();

			OwnerWindow.ToolStripProgressBar.Visible = false;

			return MyBitmap;
		}
        //-----------------------------------------------------------------------------

        /// <summary> Parses the passed in token stream and returns list of snapshots. </summary>
        public static void Parse(MainWindow MainMProfWindow, BackgroundWorker BGWorker, StreamObserver Observer, List <int> CustomSnapshots, DoWorkEventArgs EventArgs)
        {
            string PrettyFilename = Path.GetFileNameWithoutExtension(FStreamInfo.GlobalInstance.FileName);

            BGWorker.ReportProgress(0, "1/8 Loading header information for " + PrettyFilename);

            // Create binary reader and file info object from filename.
            bool         bIsBigEndian     = false;
            FileStream   ParserFileStream = File.OpenRead(FStreamInfo.GlobalInstance.FileName);
            BinaryReader BinaryStream     = new BinaryReader(ParserFileStream, System.Text.Encoding.ASCII);

            // Serialize header.
            FProfileDataHeader Header = new FProfileDataHeader(BinaryStream);

            // Determine whether read file has magic header. If no, try again byteswapped.
            if (Header.Magic != FProfileDataHeader.ExpectedMagic)
            {
                // Seek back to beginning of stream before we retry.
                ParserFileStream.Seek(0, SeekOrigin.Begin);

                // Use big endian reader. It transparently endian swaps data on read.
                BinaryStream = new BinaryReaderBigEndian(ParserFileStream);
                bIsBigEndian = true;

                // Serialize header a second time.
                Header = new FProfileDataHeader(BinaryStream);
            }

            // At this point we should have a valid header. If no, throw an exception.
            if (Header.Magic != FProfileDataHeader.ExpectedMagic)
            {
                throw new InvalidDataException();
            }

            // Keep track of the current data file for multi-part recordings
            UInt64 NextDataFile = 1;

            // Initialize shared information across snapshots, namely names, callstacks and addresses.
            FStreamInfo.GlobalInstance.Initialize(Header);

            // Keep track of current position as it's where the token stream starts.
            long TokenStreamOffset = ParserFileStream.Position;

            // Seek to name table and serialize it.
            ParserFileStream.Seek((Int64)Header.NameTableOffset, SeekOrigin.Begin);
            for (UInt64 NameIndex = 0; NameIndex < Header.NameTableEntries; NameIndex++)
            {
                int InsertedNameIndex = FStreamInfo.GlobalInstance.GetNameIndex(ReadString(BinaryStream), true);
                Debug.Assert((int)NameIndex == InsertedNameIndex);
            }

            if (Header.Version >= 6)
            {
                // Seek to meta-data table and serialize it.
                ParserFileStream.Seek((Int64)Header.MetaDataTableOffset, SeekOrigin.Begin);
                for (UInt64 MetaDataIndex = 0; MetaDataIndex < Header.MetaDataTableEntries; MetaDataIndex++)
                {
                    string MetaDataKey   = ReadString(BinaryStream);
                    string MetaDataValue = ReadString(BinaryStream);
                    FStreamInfo.GlobalInstance.MetaData.Add(MetaDataKey, MetaDataValue);
                }
            }

            FStreamInfo.GlobalInstance.TagHierarchy.InitializeHierarchy();
            if (Header.Version >= 7)
            {
                // Seek to tags table and serialize it.
                ParserFileStream.Seek((Int64)Header.TagsTableOffset, SeekOrigin.Begin);
                for (UInt64 TagsIndex = 0; TagsIndex < Header.TagsTableEntries; TagsIndex++)
                {
                    string TagsString = ReadString(BinaryStream);
                    FStreamInfo.GlobalInstance.TagsArray.Add(new FAllocationTags(TagsString));
                }
            }
            FStreamInfo.GlobalInstance.TagHierarchy.FinalizeHierarchy();

            // Seek to callstack address array and serialize it.
            ParserFileStream.Seek((Int64)Header.CallStackAddressTableOffset, SeekOrigin.Begin);
            for (UInt64 AddressIndex = 0; AddressIndex < Header.CallStackAddressTableEntries; AddressIndex++)
            {
                FStreamInfo.GlobalInstance.CallStackAddressArray.Add(new FCallStackAddress(BinaryStream, Header.bShouldSerializeSymbolInfo));
            }

            // Seek to callstack array and serialize it.
            ParserFileStream.Seek((Int64)Header.CallStackTableOffset, SeekOrigin.Begin);

            for (UInt64 CallStackIndex = 0; CallStackIndex < Header.CallStackTableEntries; CallStackIndex++)
            {
                FStreamInfo.GlobalInstance.CallStackArray.Add(new FCallStack(BinaryStream));
            }

            // Check for pending cancellation of a background operation.
            if (BGWorker.CancellationPending)
            {
                EventArgs.Cancel = true;
                return;
            }

            // We need to look up symbol information ourselves if it wasn't serialized.
            try
            {
                LookupSymbols(Header, MainMProfWindow, BinaryStream, BGWorker);
            }
            catch (Exception ex)
            {
                MessageBox.Show(String.Format("Failed to look up symbols ({0}). Attempting to continue parsing stream", ex.Message), "Memory Profiler 2", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            // Seek to script callstack data and serialize it.
            if (Header.bDecodeScriptCallstacks)
            {
                BGWorker.ReportProgress(0, "4/8 Decoding script callstacks for " + PrettyFilename);

                // Read the script name table (it's the full dumped FName table).
                ParserFileStream.Seek(Header.ScriptNameTableOffset, SeekOrigin.Begin);
                int NumScriptNames = BinaryStream.ReadInt32();
                FStreamInfo.GlobalInstance.ScriptNameArray = new List <string>(NumScriptNames);
                for (int ScriptIndex = 0; ScriptIndex < NumScriptNames; ++ScriptIndex)
                {
                    FStreamInfo.GlobalInstance.ScriptNameArray.Add(ReadString(BinaryStream));
                }

                // Read the script call stacks.
                ParserFileStream.Seek(Header.ScriptCallstackTableOffset, SeekOrigin.Begin);
                int NumScriptCallstacks = BinaryStream.ReadInt32();
                FStreamInfo.GlobalInstance.ScriptCallstackArray = new List <FScriptCallStack>(NumScriptCallstacks);
                for (int ScriptIndex = 0; ScriptIndex < NumScriptCallstacks; ScriptIndex++)
                {
                    FStreamInfo.GlobalInstance.ScriptCallstackArray.Add(new FScriptCallStack(BinaryStream));
                }

                // Find the ProcessInternal index for later replacement if script callstacks were captured.
                FStreamInfo.GlobalInstance.ProcessInternalNameIndex = FStreamInfo.GlobalInstance.GetNameIndex("UObject::ProcessInternal(FFrame&, void*)", false);
                if (FStreamInfo.GlobalInstance.ProcessInternalNameIndex == -1)
                {
                    // Try alternative name.
                    FStreamInfo.GlobalInstance.ProcessInternalNameIndex = FStreamInfo.GlobalInstance.GetNameIndex("UObject::ProcessInternal", false);

                    if (FStreamInfo.GlobalInstance.ProcessInternalNameIndex == -1)
                    {
                        Debug.WriteLine("WARNING: Couldn't find name index for ProcessInternal(). Script callstacks will not be decoded.");
                    }
                }

                // Build the list of names
                // UObject::exec*
                // UObject::CallFunction
                // UObject::ProcessEvent

                List <string> ObjectVMFunctionNamesArray = new List <string>();
                ObjectVMFunctionNamesArray.Add("UObject::CallFunction");
                ObjectVMFunctionNamesArray.Add("UObject::ProcessEvent");

                for (int NameIndex = 0; NameIndex < FStreamInfo.GlobalInstance.NameArray.Count; NameIndex++)
                {
                    string Name = FStreamInfo.GlobalInstance.NameArray[NameIndex];
                    if (Name.Contains("UObject::exec"))
                    {
                        ObjectVMFunctionNamesArray.Add(Name);
                    }
                }

                // Build the indices for functions related to object vm for later removal if script callstacks were captured.
                for (int FunctionIndex = 0; FunctionIndex < ObjectVMFunctionNamesArray.Count; FunctionIndex++)
                {
                    string FunctionName        = ObjectVMFunctionNamesArray[FunctionIndex];
                    int    Function2NamesIndex = FStreamInfo.GlobalInstance.GetNameIndex(FunctionName);
                    Debug.Assert(Function2NamesIndex != -1);
                    FStreamInfo.GlobalInstance.ObjectVMFunctionIndexArray.Add(Function2NamesIndex);
                }
            }

            // Check for pending cancellation of a background operation.
            if (BGWorker.CancellationPending)
            {
                EventArgs.Cancel = true;
                return;
            }

            // Find the StaticAllocateObject index for later replacement if script callstacks were captured.
            if (Header.bDecodeScriptCallstacks)
            {
                FStreamInfo.GlobalInstance.StaticAllocateObjectNameIndex = FStreamInfo.GlobalInstance.GetNameIndex("UObject::StaticAllocateObject(UClass*, UObject*, FName, unsigned long long, UObject*, FOutputDevice*, UObject*, UObject*, FObjectInstancingGraph*)", false);
                if (FStreamInfo.GlobalInstance.StaticAllocateObjectNameIndex == -1)
                {
                    // Try alternative name.
                    FStreamInfo.GlobalInstance.StaticAllocateObjectNameIndex = FStreamInfo.GlobalInstance.GetNameIndex("UObject::StaticAllocateObject", false);

                    if (FStreamInfo.GlobalInstance.StaticAllocateObjectNameIndex == -1)
                    {
                        Debug.WriteLine("WARNING: Couldn't find name index for StaticAllocateObject(). Script types will not be processed.");
                    }
                }
            }

            if (MainMProfWindow.Options.TrimAllocatorFunctions)
            {
                BGWorker.ReportProgress(0, "5/8 Trimming allocator entries for " + PrettyFilename);;

                // Trim allocator entries from callstacks.
                foreach (FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray)
                {
                    CallStack.TrimAllocatorEntries(MainMProfWindow.Options.AllocatorFunctions);
                }
            }

            if (MainMProfWindow.Options.FilterOutObjectVMFunctions)
            {
                BGWorker.ReportProgress(0, "6/8 Filtering out functions related to UObject Virtual Machine for " + PrettyFilename);

                // Filter out functions related to UObject Virtual Machine from callstacks.
                foreach (FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray)
                {
                    CallStack.FilterOutObjectVMFunctions();
                }
            }

            // Snapshot used for parsing. A copy will be made if a special token is encountered. Otherwise it
            // will be returned as the only snaphot at the end.
            var Snapshot                = new FStreamSnapshot("End");
            var SnapshotList            = new List <FStreamSnapshot>();
            var PointerToPointerInfoMap = new Dictionary <ulong, FLiveAllocationInfo>();

            // Seek to beginning of token stream.
            ParserFileStream.Seek(TokenStreamOffset, SeekOrigin.Begin);

            FStreamToken Token;

            FStreamToken.bDecodeScriptCallstacks = Header.bDecodeScriptCallstacks;

            ResetSnapshotDescriptions();

            bool bFoundMultiPoolCallStacks = false;

            // Start frame 0.
            FStreamInfo.GlobalInstance.FrameStreamIndices.Add(0);
            FStreamInfo.GlobalInstance.DeltaTimeArray.Add(0.0f);

            // The USE_GLOBAL_REALLOC_ZERO_PTR option is used by dlmalloc.
            // When it's enabled, calls to realloc(NULL, 0) will always return the same valid pointer,
            // which can cause unnecessary warnings (double frees, etc) when we parse the allocation data.
            // Whether the option is enabled or disabled will be automatically detected by the code below.
            bool  bUseGlobalReallocZeroPtr          = false;
            bool  bDetectingUseGlobalReallocZeroPtr = true;
            int   ReallocZeroCount = 0;
            ulong ReallocZeroPtr   = 0;

            FAllocationLifecycle NewLifecycle = new FAllocationLifecycle();

            int SnapshotIndex = 0;

            // Figure out the progress scale
            UInt64 StartOfMetadata = Math.Min(Header.NameTableOffset, Header.CallStackAddressTableOffset);

            StartOfMetadata = Math.Min(StartOfMetadata, Header.CallStackTableOffset);
            StartOfMetadata = Math.Min(StartOfMetadata, Header.ModulesOffset);

            long ProgressInterval = ((Int64)StartOfMetadata - TokenStreamOffset) / 1000;

            if (ProgressInterval < 1)
            {
                ProgressInterval = 1;
            }

            double ProgressScaleFactor = 100.0f / ((Int64)StartOfMetadata - TokenStreamOffset);
            long   NextProgressUpdate  = TokenStreamOffset;

            // Parse tokens till we reach the end of the stream.
            Token = new FStreamToken();

            using (FScopedLogTimer LoadingTime = new FScopedLogTimer("Reading and parsing tokens"))
            {
                EProfilingPayloadType LastTokenType = EProfilingPayloadType.TYPE_Other;

                while (Token.ReadNextToken(BinaryStream))
                {
                    // Check for pending cancellation of a background operation.
                    if (BGWorker.CancellationPending)
                    {
                        EventArgs.Cancel = true;
                        return;
                    }

                    long CurrentStreamPos = ParserFileStream.Position;

                    if (ParserFileStream.Position >= NextProgressUpdate)
                    {
                        BGWorker.ReportProgress(
                            ( int )((CurrentStreamPos - TokenStreamOffset) * ProgressScaleFactor),
                            String.Format("7/8 Parsing token stream for {0}, part {1} of {2}", PrettyFilename, NextDataFile, Header.NumDataFiles));
                        NextProgressUpdate += ProgressInterval;
                    }

                    if (CustomSnapshots.Count > 0 && (Snapshot.AllocationCount >= CustomSnapshots[0] * AllocationsPerSlice))
                    {
                        // Create an unnamed snapshot.
                        FStreamSnapshot MarkerSnapshot = Snapshot.DeepCopy(PointerToPointerInfoMap);

                        MarkerSnapshot.Description            = "Unnamed snapshot allocations: " + Snapshot.AllocationCount;
                        MarkerSnapshot.StreamIndex            = Token.StreamIndex;
                        MarkerSnapshot.FrameNumber            = FStreamInfo.GlobalInstance.FrameStreamIndices.Count;
                        MarkerSnapshot.CurrentTime            = Token.TotalTime;
                        MarkerSnapshot.ElapsedTime            = Token.ElapsedTime;
                        MarkerSnapshot.SubType                = Token.SubType;
                        MarkerSnapshot.SnapshotIndex          = SnapshotIndex;
                        MarkerSnapshot.MetricArray            = new List <long>(Token.Metrics);
                        MarkerSnapshot.LoadedLevels           = new List <int>(Token.LoadedLevels);
                        MarkerSnapshot.OverallMemorySlice     = new List <FMemorySlice>(Snapshot.OverallMemorySlice);
                        MarkerSnapshot.MemoryAllocationStats4 = Token.MemoryAllocationStats4.DeepCopy();

                        FStreamInfo.GlobalInstance.SnapshotList.Add(MarkerSnapshot);

                        CustomSnapshots.RemoveAt(0);

                        Token.ElapsedTime = 0.0f;
                    }

                    switch (Token.Type)
                    {
                    // Malloc
                    case EProfilingPayloadType.TYPE_Malloc:
                    {
#if DEBUG_TIMINGS
                        MallocTimer.Start();
#endif

                        if (Token.Pointer != 0)
                        {
                            Token.CallStackIndex = GetVirtualCallStackIndex(Token, Observer);

                            HandleMalloc(Token, Snapshot, PointerToPointerInfoMap);

                            FCallStack CurrentCallstack = FStreamInfo.GlobalInstance.CallStackArray[Token.CallStackIndex];
                            if (CurrentCallstack.MemoryPool != EMemoryPool.MEMPOOL_None && CurrentCallstack.MemoryPool != Token.Pool)
                            {
                                bFoundMultiPoolCallStacks = true;
                            }

                            CurrentCallstack.MemoryPool |= Token.Pool;
                            FStreamInfo.GlobalInstance.MemoryPoolInfo[Token.Pool].AddPointer(Token.Pointer, Token.Size);

                            CurrentCallstack.ProcessMalloc(Token, ref NewLifecycle);
                        }

#if DEBUG_TIMINGS
                        MallocTimer.Stop();
#endif
                    }
                    break;

                    // Free
                    case EProfilingPayloadType.TYPE_Free:
                    {
#if DEBUG_TIMINGS
                        FreeTimer.Start();
#endif
                        if (bDetectingUseGlobalReallocZeroPtr)
                        {
                            if (ReallocZeroCount > 0 && Token.Pointer == ReallocZeroPtr)
                            {
                                ReallocZeroCount--;
                            }
                        }

                        if (bDetectingUseGlobalReallocZeroPtr || !bUseGlobalReallocZeroPtr || Token.Pointer != ReallocZeroPtr)
                        {
                            // Either USE_GLOBAL_REALLOC_ZERO_PTR is not being used, or we're not
                            // trying to free the ReallocZeroPtr.

                            FLiveAllocationInfo FreedAllocInfo;
                            if (HandleFree(Token, Snapshot, PointerToPointerInfoMap, out FreedAllocInfo))
                            {
                                FCallStack PreviousCallStack = FStreamInfo.GlobalInstance.CallStackArray[FreedAllocInfo.CallStackIndex];
                                PreviousCallStack.ProcessFree(Token);
                            }
                        }

#if DEBUG_TIMINGS
                        FreeTimer.Stop();
#endif
                    }
                    break;

                    // Realloc
                    case EProfilingPayloadType.TYPE_Realloc:
                    {
#if DEBUG_TIMINGS
                        ReallocTimer.Start();
#endif

                        Token.CallStackIndex = GetVirtualCallStackIndex(Token, Observer);

                        FCallStack           PreviousCallstack   = null;
                        FAllocationLifecycle OldReallocLifecycle = null;

                        if (Token.OldPointer != 0)
                        {
                            FLiveAllocationInfo FreedAllocInfo;
                            if (HandleFree(Token, Snapshot, PointerToPointerInfoMap, out FreedAllocInfo))
                            {
                                PreviousCallstack = FStreamInfo.GlobalInstance.CallStackArray[FreedAllocInfo.CallStackIndex];
                                if (Token.Size > 0)
                                {
                                    OldReallocLifecycle = PreviousCallstack.ProcessRealloc(Token, ref NewLifecycle, null, null);
                                }
                                else
                                {
                                    PreviousCallstack.ProcessFree(Token);
                                }
                            }
                        }
                        else if (Token.Size == 0)
                        {
                            if (bDetectingUseGlobalReallocZeroPtr)
                            {
                                if (ReallocZeroCount > 1)
                                {
                                    // This code checks to see if the return values of the second and third realloc(0, NULL) calls
                                    // match. The first one is always different for some reason.

                                    bUseGlobalReallocZeroPtr          = Token.NewPointer == ReallocZeroPtr;
                                    bDetectingUseGlobalReallocZeroPtr = false;

                                    Debug.WriteLine("USE_GLOBAL_REALLOC_ZERO_PTR is " + bUseGlobalReallocZeroPtr);
                                }
                                else
                                {
                                    ReallocZeroPtr = Token.NewPointer;
                                    ReallocZeroCount++;
                                }
                            }


                            if (bUseGlobalReallocZeroPtr)
                            {
                                // break out of case to avoid 'double malloc' warnings
#if DEBUG_TIMINGS
                                ReallocTimer.Stop();
#endif
                                break;
                            }
                        }

                        if (Token.NewPointer != 0)
                        {
                            Token.Pointer = Token.NewPointer;

                            FCallStack CurrentCallstack = FStreamInfo.GlobalInstance.CallStackArray[Token.CallStackIndex];

                            if (CurrentCallstack.MemoryPool != EMemoryPool.MEMPOOL_None && CurrentCallstack.MemoryPool != Token.Pool)
                            {
                                bFoundMultiPoolCallStacks = true;
                            }

                            CurrentCallstack.MemoryPool |= Token.Pool;
                            FStreamInfo.GlobalInstance.MemoryPoolInfo[Token.Pool].AddPointer(Token.Pointer, Token.Size);

                            HandleMalloc(Token, Snapshot, PointerToPointerInfoMap);

                            if (CurrentCallstack != PreviousCallstack)
                            {
                                CurrentCallstack.ProcessRealloc(Token, ref NewLifecycle, PreviousCallstack, OldReallocLifecycle);
                                IncompleteLifeCycles += CurrentCallstack.IncompleteLifecycles.Count;
                            }

#if DEBUG_TIMINGS
                            ReallocTimer.Stop();
#endif
                        }
                    }
                    break;

                    // Status/ payload.
                    case EProfilingPayloadType.TYPE_Other:
                    {
#if DEBUG_TIMINGS
                        OtherTimer.Start();
#endif

                        switch (Token.SubType)
                        {
                        case EProfilingPayloadSubType.SUBTYPE_EndOfStreamMarker:
                        {
                            // Should never receive EOS marker as ReadNextToken should've returned false.
                            throw new InvalidDataException();
                        }

                        case EProfilingPayloadSubType.SUBTYPE_EndOfFileMarker:
                        {
                            // Switch to the next file in the chain
                            ParserFileStream.Close();
                            BinaryStream = SwitchStreams((int)NextDataFile, FStreamInfo.GlobalInstance.FileName, bIsBigEndian, out ParserFileStream);

                            // Update variables used for reporting progress
                            TokenStreamOffset   = 0;
                            ProgressInterval    = ParserFileStream.Length / 100;
                            ProgressScaleFactor = 100.0f / ParserFileStream.Length;
                            NextProgressUpdate  = 0;

                            // Tick over to the next file, and make sure things are still ending as expected
                            NextDataFile++;
                            if (NextDataFile > Header.NumDataFiles)
                            {
                                throw new InvalidDataException("Found an unexpected number of data files (more than indicated in the master file");
                            }
                            break;
                        }

                        // Create snapshot.
                        case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LoadMap_Start:
                        case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LoadMap_Mid:
                        case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LoadMap_End:
                        case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_GC_Start:
                        case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_GC_End:
                        case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LevelStream_Start:
                        case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LevelStream_End:
                        case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker:
                        {
                            if ((Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker) ||
                                (Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LoadMap_Start &&
                                 FStreamInfo.GlobalInstance.CreationOptions.LoadMapStartSnapshotsCheckBox.Checked) ||
                                (Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LoadMap_Mid &&
                                 FStreamInfo.GlobalInstance.CreationOptions.LoadMapMidSnapshotsCheckBox.Checked) ||
                                (Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LoadMap_End &&
                                 FStreamInfo.GlobalInstance.CreationOptions.LoadMapEndSnapshotsCheckBox.Checked) ||
                                (Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_GC_Start &&
                                 FStreamInfo.GlobalInstance.CreationOptions.GCStartSnapshotsCheckBox.Checked) ||
                                (Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_GC_End &&
                                 FStreamInfo.GlobalInstance.CreationOptions.GCEndSnapshotsCheckBox.Checked) ||
                                (Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LevelStream_Start &&
                                 FStreamInfo.GlobalInstance.CreationOptions.LevelStreamStartSnapshotsCheckBox.Checked) ||
                                (Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LevelStream_End) &&
                                FStreamInfo.GlobalInstance.CreationOptions.LevelStreamEndSnapshotsCheckBox.Checked)
                            {
                                FStreamSnapshot MarkerSnapshot = Snapshot.DeepCopy(PointerToPointerInfoMap);

                                MarkerSnapshot.Description            = GetNextSnapshotDescription(Token.SubType, FStreamInfo.GlobalInstance.NameArray[Token.TextIndex]);
                                MarkerSnapshot.StreamIndex            = Token.StreamIndex;
                                MarkerSnapshot.FrameNumber            = FStreamInfo.GlobalInstance.FrameStreamIndices.Count;
                                MarkerSnapshot.CurrentTime            = Token.TotalTime;
                                MarkerSnapshot.ElapsedTime            = Token.ElapsedTime;
                                MarkerSnapshot.SubType                = Token.SubType;
                                MarkerSnapshot.SnapshotIndex          = SnapshotIndex;
                                MarkerSnapshot.MetricArray            = new List <long>(Token.Metrics);
                                MarkerSnapshot.LoadedLevels           = new List <int>(Token.LoadedLevels);
                                MarkerSnapshot.MemoryAllocationStats4 = Snapshot.MemoryAllocationStats4.DeepCopy();
                                MarkerSnapshot.OverallMemorySlice     = new List <FMemorySlice>(Snapshot.OverallMemorySlice);

                                FStreamInfo.GlobalInstance.SnapshotList.Add(MarkerSnapshot);

                                Token.ElapsedTime = 0.0f;
                            }

                            SnapshotIndex++;
                            break;
                        }


                        case EProfilingPayloadSubType.SUBTYPE_TotalUsed:
                        {
                            break;
                        }

                        case EProfilingPayloadSubType.SUBTYPE_TotalAllocated:
                        {
                            break;
                        }

                        case EProfilingPayloadSubType.SUBTYPE_CPUUsed:
                        {
                            break;
                        }

                        case EProfilingPayloadSubType.SUBTYPE_CPUSlack:
                        {
                            break;
                        }

                        case EProfilingPayloadSubType.SUBTYPE_CPUWaste:
                        {
                            break;
                        }

                        case EProfilingPayloadSubType.SUBTYPE_GPUUsed:
                        {
                            break;
                        }

                        case EProfilingPayloadSubType.SUBTYPE_GPUSlack:
                        {
                            break;
                        }

                        case EProfilingPayloadSubType.SUBTYPE_GPUWaste:
                        {
                            break;
                        }

                        case EProfilingPayloadSubType.SUBTYPE_ImageSize:
                        {
                            break;
                        }

                        case EProfilingPayloadSubType.SUBTYPE_OSOverhead:
                        {
                            break;
                        }

                        case EProfilingPayloadSubType.SUBTYPE_FrameTimeMarker:
                        {
                            FStreamInfo.GlobalInstance.FrameStreamIndices.Add(Token.StreamIndex);
                            FStreamInfo.GlobalInstance.DeltaTimeArray.Add(Token.DeltaTime);
                            break;
                        }

                        case EProfilingPayloadSubType.SUBTYPE_TextMarker:
                        {
                            break;
                        }

                        case EProfilingPayloadSubType.SUBTYPE_MemoryAllocationStats:
                        {
                            Snapshot.MemoryAllocationStats4 = Token.MemoryAllocationStats4.DeepCopy();
                            break;
                        }

                        // Unhandled.
                        default:
                        {
                            throw new InvalidDataException();
                        }
                        }
#if DEBUG_TIMINGS
                        OtherTimer.Stop();
#endif
                        break;
                    }

                    // Unhandled.
                    default:
                    {
                        throw new InvalidDataException();
                    }
                    }

                    if (NewLifecycle == null)
                    {
                        NewLifecycle = new FAllocationLifecycle();
                    }

                    // Advance the stream index.
                    Token.StreamIndex++;
                    LastTokenType = Token.Type;
                }
            }
            //-----------------------------------------------------------------------------
            //@DEBUG
            WriteTimings();
            //-----------------------------------------------------------------------------

            if (MainMProfWindow != null && bFoundMultiPoolCallStacks)
            {
                MessageBox.Show("Some callstacks appear to allocate to multiple pools. This will make profiling more difficult and is usually caused by function inlining. It can also be caused by using the wrong executable to decode the profile.");
            }

            FStreamInfo.GlobalInstance.bHasMultiPoolCallStacks = bFoundMultiPoolCallStacks;

            // Closes the file so it can potentially be opened for writing.
            ParserFileStream.Close();

            // Mark end of last frame.
            FStreamInfo.GlobalInstance.FrameStreamIndices.Add(Token.StreamIndex);
            FStreamInfo.GlobalInstance.DeltaTimeArray.Add(0.0f);

            // make sure all lifetimecallstacklists are as big as the latest one
            foreach (FStreamSnapshot PreviousSnapshot in FStreamInfo.GlobalInstance.SnapshotList)
            {
                while (PreviousSnapshot.LifetimeCallStackList.Count < Snapshot.LifetimeCallStackList.Count)
                {
                    PreviousSnapshot.LifetimeCallStackList.Add(new FCallStackAllocationInfo(0, PreviousSnapshot.LifetimeCallStackList.Count, 0, -1));
                }
            }

            List <CallStackPattern> OrderedPatternList = MainMProfWindow.Options.GetOrderedPatternList();
            ClassGroup UngroupedGroup = MainMProfWindow.Options.UngroupedGroup;


            double CallStackScaleFactor        = 100.0f / FStreamInfo.GlobalInstance.CallStackArray.Count;
            long   CallStackInterval           = FStreamInfo.GlobalInstance.CallStackArray.Count / 100;
            long   CallStackNextProgressUpdate = CallStackInterval;

            int CallStackCurrent = 0;
            foreach (FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray)
            {
                if (CallStackCurrent >= CallStackNextProgressUpdate)
                {
                    BGWorker.ReportProgress(( int )(CallStackCurrent * CallStackScaleFactor), "8/8 Matching callstacks to groups for " + PrettyFilename);
                    CallStackNextProgressUpdate += CallStackInterval;
                }
                CallStackCurrent++;

                if (CallStack.AddressIndices.Count > 0)
                {
                    // Find the first non templated entry in each callstack
                    CallStack.EvaluateFirstNonContainer();

                    // Go through each pattern to find the first match.
                    // It's important that the patterns are evaluated in the correct order.
                    foreach (CallStackPattern Pattern in OrderedPatternList)
                    {
                        if (Pattern.Matches(CallStack))
                        {
                            CallStack.Group = Pattern.Group;
                            Pattern.AddCallStack(CallStack);
                            break;
                        }
                    }

                    if (CallStack.Group == null)
                    {
                        CallStack.Group = UngroupedGroup;
                        UngroupedGroup.CallStackPatterns[0].AddCallStack(CallStack);
                    }
                }
            }

            // Add snapshot in end state to the list and return it.
            Snapshot.StreamIndex            = Token.StreamIndex;
            Snapshot.FrameNumber            = FStreamInfo.GlobalInstance.FrameStreamIndices.Count;
            Snapshot.CurrentTime            = Token.TotalTime;
            Snapshot.ElapsedTime            = Token.ElapsedTime;
            Snapshot.MetricArray            = new List <long>(Token.Metrics);
            Snapshot.LoadedLevels           = new List <int>(Token.LoadedLevels);
            Snapshot.MemoryAllocationStats4 = Token.MemoryAllocationStats4.DeepCopy();
            Snapshot.FinalizeSnapshot(PointerToPointerInfoMap);
            FStreamInfo.GlobalInstance.SnapshotList.Add(Snapshot);

            BGWorker.ReportProgress(100, "Finalizing snapshots for " + PrettyFilename);

            // Finalize snapshots. This entails creating the sorted snapshot list.
            foreach (FStreamSnapshot SnapshotToFinalize in SnapshotList)
            {
                SnapshotToFinalize.FinalizeSnapshot(null);
            }
        }
        public static void ParseSnapshot(TreeView CallGraphTreeView, List <FCallStackAllocationInfo> CallStackList, bool bShouldSortBySize, string FilterText, bool bInvertCallStacks)
        {
            // Progress bar.
            OwnerWindow.ToolStripProgressBar.Value   = 0;
            OwnerWindow.ToolStripProgressBar.Visible = true;
            long ProgressInterval   = CallStackList.Count / 20;
            long NextProgressUpdate = ProgressInterval;
            int  CallStackCurrent   = 0;

            OwnerWindow.UpdateStatus("Updating call graph for " + OwnerWindow.CurrentFilename);

            CallGraphTreeView.BeginUpdate();

            CallGraphTreeView.TreeViewNodeSorter = null;             // clear this to avoid a Sort for each call to Add

            Debug.WriteLine("FCallGraphTreeViewParser.ParseSnapshot - Building call graph tree for " + OwnerWindow.CurrentFilename);

            var TruncatedNode = new FCallGraphNode("Truncated Callstacks");
            var RegularNode   = new FCallGraphNode("Full Callstacks");

            bool bFilterIn = OwnerWindow.IsFilteringIn();

            using (FScopedLogTimer ParseTiming = new FScopedLogTimer("FCallGraphTreeViewParser.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);
                    }
                }

                // Iterate over all call graph paths and add them to the graph.
                foreach (FCallStackAllocationInfo AllocationInfo in FilteredCallstackList)
                {
                    // Update progress bar.
                    if (CallStackCurrent >= NextProgressUpdate)
                    {
                        OwnerWindow.ToolStripProgressBar.PerformStep();
                        NextProgressUpdate += ProgressInterval;
                        Debug.WriteLine("FCallGraphTreeViewParser.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20");
                    }
                    CallStackCurrent++;

                    // Add this call graph to the tree view.
                    FCallStack CallStack = FStreamInfo.GlobalInstance.CallStackArray[AllocationInfo.CallStackIndex];
                    // Split the tree into full and truncated callstacks.
                    var RootNode = CallStack.bIsTruncated ? TruncatedNode : RegularNode;
                    // Apply filter based on text representation of address.
                    if (CallStack.RunFilters(FilterText, OwnerWindow.Options.ClassGroups, bFilterIn, OwnerWindow.SelectedMemoryPool))
                    {
                        // Add call stack to proper part of graph.
                        AddCallStackToGraph(RootNode, CallStack, AllocationInfo, ParentFunctionIndex, bInvertCallStacks);
                    }
                }
            }

            Debug.WriteLine("FCallGraphTreeViewParser.ParseSnapshot - Sorting call graph tree for " + OwnerWindow.CurrentFilename);

            // Sort the nodes before adding them to the tree (sorting when in the tree is slow!).
            SortNodes(TruncatedNode, bShouldSortBySize);
            SortNodes(RegularNode, bShouldSortBySize);

            Debug.WriteLine("FCallGraphTreeViewParser.ParseSnapshot - Populating call graph tree for " + OwnerWindow.CurrentFilename);

            // Clear out existing nodes and add two root nodes. One for regular call stacks and one for truncated ones.
            CallGraphTreeView.Nodes.Clear();
            CallGraphTreeView.Tag = new FCallGraphTreeViewTag();
            AddRootTreeNode(TruncatedNode, CallGraphTreeView);
            AddRootTreeNode(RegularNode, CallGraphTreeView);

            CallGraphTreeView.BeforeExpand -= HandlePreExpandTreeNode;
            CallGraphTreeView.BeforeExpand += HandlePreExpandTreeNode;

            CallGraphTreeView.EndUpdate();

            OwnerWindow.ToolStripProgressBar.Visible = false;
        }
		public static void ParseSnapshot( string FilterText )
		{
			if( !FStreamInfo.GlobalInstance.CreationOptions.KeepLifecyclesCheckBox.Checked )
			{
				return;
			}

			// Progress bar
			long ProgressInterval = FStreamInfo.GlobalInstance.CallStackArray.Count / 20;
			long NextProgressUpdate = ProgressInterval;
			int CallStackCurrent = 0;
			OwnerWindow.ToolStripProgressBar.Value = 0;
			OwnerWindow.ToolStripProgressBar.Visible = true;
			OwnerWindow.UpdateStatus( "Updating short lived allocation view for " + OwnerWindow.CurrentFilename );

			OwnerWindow.ShortLivedListView.BeginUpdate();
			OwnerWindow.ShortLivedListView.Items.Clear();

			const int MaxLifetime = 1;
			const int MinAllocations = 100;

			ulong StartStreamIndex = OwnerWindow.GetStartSnapshotStreamIndex();
			ulong EndStreamIndex = OwnerWindow.GetEndSnapshotStreamIndex();

			uint[] AllocationLifetimes = new uint[ MaxLifetime + 1 ];

			using( FScopedLogTimer ParseTiming = new FScopedLogTimer( "FShortLivedAllocationView.ParseSnapshot" ) )
			{
				foreach( FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray )
				{
					// Update progress bar.
					if( CallStackCurrent >= NextProgressUpdate )
					{
						OwnerWindow.ToolStripProgressBar.PerformStep();
						NextProgressUpdate += ProgressInterval;
						Debug.WriteLine( "FShortLivedAllocationView.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20" );
					}
					CallStackCurrent++;

					if( CallStack.RunFilters( FilterText, OwnerWindow.Options.ClassGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool ) )
					{
						Array.Clear( AllocationLifetimes, 0, AllocationLifetimes.Length );

						int NumAllocations = 0;
						uint UniqueFramesWithAge0Allocs = 0;
						int LastFrameWithAge0Allocs = 0;

						int CurrentRun = 0;
						float CurrentTotalAllocSize = 0;
						int CurrentRunFrame = -1;
						int LongestRun = 0;
						float LongestRunTotalAllocSize = 0;

						int LastEndFrame = 1;
						int LastSnapshot = 0;
						foreach( FAllocationLifecycle Lifecycle in CallStack.CompleteLifecycles )
						{
							// only process allocations that were really freed, not just realloced
							if( Lifecycle.FreeStreamIndex != FStreamInfo.INVALID_STREAM_INDEX
								&& Lifecycle.AllocEvent.StreamIndex > StartStreamIndex && Lifecycle.FreeStreamIndex < EndStreamIndex )
							{
								// CompleteLifecycles are sorted by FreeStreamIndex, so this search pattern ensures
								// that the frames will be found as quickly as possible.
								int EndFrame;
								if( Lifecycle.FreeStreamIndex < FStreamInfo.GlobalInstance.FrameStreamIndices[ LastEndFrame ] )
								{
									EndFrame = LastEndFrame;
								}
								else
								{
									if( Lifecycle.FreeStreamIndex > FStreamInfo.GlobalInstance.SnapshotList[ LastSnapshot ].StreamIndex )
									{
										// lifecycle isn't even in same snapshot, so search by snapshot first (much faster than
										// searching through frames one by one)
										LastSnapshot = OwnerWindow.GetSnapshotIndexFromStreamIndex( LastSnapshot, Lifecycle.FreeStreamIndex );

										if( LastSnapshot == 0 )
										{
											LastEndFrame = 1;
										}
										else
										{
											LastEndFrame = FStreamInfo.GlobalInstance.SnapshotList[ LastSnapshot - 1 ].FrameNumber;
										}
									}

									EndFrame = FStreamInfo.GlobalInstance.GetFrameNumberFromStreamIndex( LastEndFrame, Lifecycle.FreeStreamIndex );
								}

								int StartFrame = EndFrame;
								while( FStreamInfo.GlobalInstance.FrameStreamIndices[ StartFrame ] > Lifecycle.AllocEvent.StreamIndex
										   && StartFrame > 0 && EndFrame - StartFrame <= MaxLifetime + 1 )
								{
									StartFrame--;
								}
								StartFrame++;

								int Age = EndFrame - StartFrame;

								if( Age <= MaxLifetime )
								{
									AllocationLifetimes[ Age ]++;
									NumAllocations++;

									if( Age == 0 )
									{
										if( StartFrame != LastFrameWithAge0Allocs )
										{
											UniqueFramesWithAge0Allocs++;
											LastFrameWithAge0Allocs = StartFrame;
										}
									}
									else if( Age == 1 )
									{
										if( StartFrame == CurrentRunFrame )
										{
											CurrentRun++;

											CurrentTotalAllocSize += Lifecycle.PeakSize;

											if( CurrentRun > LongestRun )
											{
												LongestRun = CurrentRun;
												LongestRunTotalAllocSize = CurrentTotalAllocSize;
											}
										}
										else if( StartFrame > CurrentRunFrame )
										{
											CurrentRun = 1;
											CurrentTotalAllocSize = Lifecycle.PeakSize;
										}
										else if( EndFrame == CurrentRunFrame )
										{
											CurrentTotalAllocSize += Lifecycle.PeakSize;

											if( CurrentRun == LongestRun && LongestRunTotalAllocSize < CurrentTotalAllocSize )
											{
												LongestRunTotalAllocSize = CurrentTotalAllocSize;
											}
										}

										CurrentRunFrame = EndFrame;
									}
								}
							}
						}

						if( NumAllocations > MinAllocations )
						{
							float LongestRunAvgAllocSize = LongestRun == 0 ? 0 : LongestRunTotalAllocSize / LongestRun;

							ListViewItem LVItem = new ListViewItem();
							uint[] ColumnValues = new uint[ 5 ];
							ColumnValues[ ShortLivedColumnMapping[ 0 ] ] = AllocationLifetimes[ 0 ];
							ColumnValues[ ShortLivedColumnMapping[ 1 ] ] = UniqueFramesWithAge0Allocs;
							ColumnValues[ ShortLivedColumnMapping[ 2 ] ] = AllocationLifetimes[ 1 ];
							ColumnValues[ ShortLivedColumnMapping[ 3 ] ] = ( uint )LongestRun;
							ColumnValues[ ShortLivedColumnMapping[ 4 ] ] = ( uint )LongestRunAvgAllocSize;

							LVItem.Tag = new FShortLivedCallStackTag( CallStack, ColumnValues );
							LVItem.Text = ColumnValues[ ShortLivedColumnMapping[ 0 ] ].ToString();
							for( int ValueIndex = 1; ValueIndex < ColumnValues.Length; ValueIndex++ )
							{
								LVItem.SubItems.Add( ColumnValues[ ShortLivedColumnMapping[ ValueIndex ] ].ToString() );
							}

							bool bInsertedItem = false;
							for( int ItemIndex = OwnerWindow.ShortLivedListView.Items.Count - 1; ItemIndex >= 0; ItemIndex-- )
							{
								uint[] ItemValues = ( ( FShortLivedCallStackTag )OwnerWindow.ShortLivedListView.Items[ ItemIndex ].Tag ).ColumnValues;
								for( int ValueIndex = 0; ValueIndex < ColumnValues.Length; ValueIndex++ )
								{
									if( ItemValues[ ValueIndex ] > ColumnValues[ ValueIndex ] )
									{
										// found correct index to insert item
										OwnerWindow.ShortLivedListView.Items.Insert( ItemIndex + 1, LVItem );
										bInsertedItem = true;
										break;
									}
									else if( ItemValues[ ValueIndex ] < ColumnValues[ ValueIndex ] )
									{
										break;
									}
									else if( ValueIndex == ColumnValues.Length - 1 )
									{
										// the items being compared are identical, so just insert here
										OwnerWindow.ShortLivedListView.Items.Insert( ItemIndex + 1, LVItem );
										bInsertedItem = true;
									}
								}

								if( bInsertedItem )
								{
									break;
								}
							}

							if( !bInsertedItem )
							{
								// item must be at top of list
								OwnerWindow.ShortLivedListView.Items.Insert( 0, LVItem );
							}
						}
					}
				}
			}

			OwnerWindow.ShortLivedListView.SetSortArrow( 3, false );
			OwnerWindow.ShortLivedListView.EndUpdate();

			OwnerWindow.ToolStripProgressBar.Visible = false;
		}
		//-----------------------------------------------------------------------------

		/// <summary> Parses the passed in token stream and returns list of snapshots. </summary>
		public static void Parse( MainWindow MainMProfWindow, BackgroundWorker BGWorker, StreamObserver Observer, List<int> CustomSnapshots, DoWorkEventArgs EventArgs )
		{
            string PrettyFilename = Path.GetFileNameWithoutExtension(FStreamInfo.GlobalInstance.FileName);
			BGWorker.ReportProgress( 0, "1/8 Loading header information for " + PrettyFilename );

			// Create binary reader and file info object from filename.
            bool bIsBigEndian = false;
			FileStream ParserFileStream = File.OpenRead(FStreamInfo.GlobalInstance.FileName);
			BinaryReader BinaryStream = new BinaryReader(ParserFileStream,System.Text.Encoding.ASCII);

			// Serialize header.
			FProfileDataHeader Header = new FProfileDataHeader(BinaryStream);

			// Determine whether read file has magic header. If no, try again byteswapped.
			if(Header.Magic != FProfileDataHeader.ExpectedMagic)
			{
				// Seek back to beginning of stream before we retry.
				ParserFileStream.Seek(0,SeekOrigin.Begin);

				// Use big endian reader. It transparently endian swaps data on read.
				BinaryStream = new BinaryReaderBigEndian(ParserFileStream);
                bIsBigEndian = true;
				
				// Serialize header a second time.
				Header = new FProfileDataHeader(BinaryStream);
			}

			// At this point we should have a valid header. If no, throw an exception.
			if( Header.Magic != FProfileDataHeader.ExpectedMagic )
			{
				throw new InvalidDataException();
			}

            // Keep track of the current data file for multi-part recordings
            UInt64 NextDataFile = 1;

			// Initialize shared information across snapshots, namely names, callstacks and addresses.
			FStreamInfo.GlobalInstance.Initialize( Header );

			// Keep track of current position as it's where the token stream starts.
			long TokenStreamOffset = ParserFileStream.Position;

			if (Header.Version >= 6)
			{
				// Seek to meta-data table and serialize it.
				ParserFileStream.Seek((Int64)Header.MetaDataTableOffset, SeekOrigin.Begin);
				for (UInt64 MetaDataIndex = 0; MetaDataIndex < Header.MetaDataTableEntries; MetaDataIndex++)
				{
					string MetaDataKey = ReadString(BinaryStream);
					string MetaDataValue = ReadString(BinaryStream);
					FStreamInfo.GlobalInstance.MetaData.Add(MetaDataKey, MetaDataValue);
				}
			}

			// Seek to name table and serialize it.
			ParserFileStream.Seek((Int64)Header.NameTableOffset,SeekOrigin.Begin);
			for(UInt64 NameIndex = 0;NameIndex < Header.NameTableEntries;NameIndex++)
			{
				int InsertedNameIndex = FStreamInfo.GlobalInstance.GetNameIndex( ReadString( BinaryStream ), true );
				Debug.Assert((int)NameIndex == InsertedNameIndex);
			}

			// Seek to callstack address array and serialize it.                
			ParserFileStream.Seek( (Int64)Header.CallStackAddressTableOffset, SeekOrigin.Begin );
			for(UInt64 AddressIndex = 0;AddressIndex < Header.CallStackAddressTableEntries;AddressIndex++)
			{
                FStreamInfo.GlobalInstance.CallStackAddressArray.Add(new FCallStackAddress(BinaryStream, Header.bShouldSerializeSymbolInfo));
			}

			// Seek to callstack array and serialize it.
			ParserFileStream.Seek( (Int64)Header.CallStackTableOffset, SeekOrigin.Begin );

			for(UInt64 CallStackIndex = 0;CallStackIndex < Header.CallStackTableEntries;CallStackIndex++)
			{
                FStreamInfo.GlobalInstance.CallStackArray.Add(new FCallStack(BinaryStream));
			}

			// Check for pending cancellation of a background operation.
			if( BGWorker.CancellationPending )
			{
				EventArgs.Cancel = true;
				return;
			}

            // We need to look up symbol information ourselves if it wasn't serialized.
            try
            {
                LookupSymbols(Header, MainMProfWindow, BinaryStream, BGWorker);
            }
            catch (Exception ex)
            {
                MessageBox.Show(String.Format("Failed to look up symbols ({0}). Attempting to continue parsing stream", ex.Message), "Memory Profiler 2", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            // Seek to script callstack data and serialize it.
			if( Header.bDecodeScriptCallstacks )
			{
				BGWorker.ReportProgress( 0, "4/8 Decoding script callstacks for " + PrettyFilename );

				// Read the script name table (it's the full dumped FName table).
				ParserFileStream.Seek( Header.ScriptNameTableOffset, SeekOrigin.Begin );
				int NumScriptNames = BinaryStream.ReadInt32();
				FStreamInfo.GlobalInstance.ScriptNameArray = new List<string>( NumScriptNames );
				for( int ScriptIndex = 0; ScriptIndex < NumScriptNames; ++ScriptIndex )
				{
					FStreamInfo.GlobalInstance.ScriptNameArray.Add( ReadString( BinaryStream ) );
				}

				// Read the script call stacks.
				ParserFileStream.Seek( Header.ScriptCallstackTableOffset, SeekOrigin.Begin );
				int NumScriptCallstacks = BinaryStream.ReadInt32();
				FStreamInfo.GlobalInstance.ScriptCallstackArray = new List<FScriptCallStack>( NumScriptCallstacks );
				for( int ScriptIndex = 0; ScriptIndex < NumScriptCallstacks; ScriptIndex++ )
				{
					FStreamInfo.GlobalInstance.ScriptCallstackArray.Add( new FScriptCallStack( BinaryStream ) );
				}

				// Find the ProcessInternal index for later replacement if script callstacks were captured.
				FStreamInfo.GlobalInstance.ProcessInternalNameIndex = FStreamInfo.GlobalInstance.GetNameIndex( "UObject::ProcessInternal(FFrame&, void*)", false );
				if( FStreamInfo.GlobalInstance.ProcessInternalNameIndex == -1 )
				{
					// Try alternative name.
					FStreamInfo.GlobalInstance.ProcessInternalNameIndex = FStreamInfo.GlobalInstance.GetNameIndex( "UObject::ProcessInternal", false );

					if( FStreamInfo.GlobalInstance.ProcessInternalNameIndex == -1 )
					{
						Debug.WriteLine( "WARNING: Couldn't find name index for ProcessInternal(). Script callstacks will not be decoded." );
					}
				}
			
				// Build the list of names
				// UObject::exec*
				// UObject::CallFunction
				// UObject::ProcessEvent

				List<string> ObjectVMFunctionNamesArray = new List<string>();
				ObjectVMFunctionNamesArray.Add( "UObject::CallFunction" );
				ObjectVMFunctionNamesArray.Add( "UObject::ProcessEvent" );

				for( int NameIndex = 0; NameIndex < FStreamInfo.GlobalInstance.NameArray.Count; NameIndex ++ )
				{
					string Name = FStreamInfo.GlobalInstance.NameArray[ NameIndex ];
					if( Name.Contains( "UObject::exec" ) )
					{
						ObjectVMFunctionNamesArray.Add( Name );
					}
				}

				// Build the indices for functions related to object vm for later removal if script callstacks were captured.
				for( int FunctionIndex = 0; FunctionIndex < ObjectVMFunctionNamesArray.Count; FunctionIndex ++ )
				{
					string FunctionName = ObjectVMFunctionNamesArray[FunctionIndex];
					int Function2NamesIndex = FStreamInfo.GlobalInstance.GetNameIndex( FunctionName );
					Debug.Assert( Function2NamesIndex != -1 );
					FStreamInfo.GlobalInstance.ObjectVMFunctionIndexArray.Add( Function2NamesIndex );
				}
			}

			// Check for pending cancellation of a background operation.
			if( BGWorker.CancellationPending )
			{
				EventArgs.Cancel = true;
				return;
			}

			// Find the StaticAllocateObject index for later replacement if script callstacks were captured.
			if( Header.bDecodeScriptCallstacks )
			{
				FStreamInfo.GlobalInstance.StaticAllocateObjectNameIndex = FStreamInfo.GlobalInstance.GetNameIndex( "UObject::StaticAllocateObject(UClass*, UObject*, FName, unsigned long long, UObject*, FOutputDevice*, UObject*, UObject*, FObjectInstancingGraph*)", false );
				if( FStreamInfo.GlobalInstance.StaticAllocateObjectNameIndex == -1 )
				{
					// Try alternative name.
					FStreamInfo.GlobalInstance.StaticAllocateObjectNameIndex = FStreamInfo.GlobalInstance.GetNameIndex( "UObject::StaticAllocateObject", false );

					if( FStreamInfo.GlobalInstance.StaticAllocateObjectNameIndex == -1 )
					{
						Debug.WriteLine( "WARNING: Couldn't find name index for StaticAllocateObject(). Script types will not be processed." );
					}
				}
			}

			if (MainMProfWindow.Options.TrimAllocatorFunctions)
			{
				BGWorker.ReportProgress(0, "5/8 Trimming allocator entries for " + PrettyFilename); ;

				// Trim allocator entries from callstacks.
				foreach (FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray)
				{
					CallStack.TrimAllocatorEntries(MainMProfWindow.Options.AllocatorFunctions);
				}
			}

			if( MainMProfWindow.Options.FilterOutObjectVMFunctions )
			{
				BGWorker.ReportProgress( 0, "6/8 Filtering out functions related to UObject Virtual Machine for " + PrettyFilename );

				// Filter out functions related to UObject Virtual Machine from callstacks.
				foreach( FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray )
				{
					CallStack.FilterOutObjectVMFunctions();
				}
			}

			// Snapshot used for parsing. A copy will be made if a special token is encountered. Otherwise it
			// will be returned as the only snaphot at the end.
			FStreamSnapshot Snapshot = new FStreamSnapshot("End");
			List<FStreamSnapshot> SnapshotList = new List<FStreamSnapshot>();
            Dictionary<ulong, FCallStackAllocationInfo> PointerToPointerInfoMap = new Dictionary<ulong, FCallStackAllocationInfo>();

			// Seek to beginning of token stream.
			ParserFileStream.Seek(TokenStreamOffset, SeekOrigin.Begin);

            FStreamToken Token;
            FStreamToken.bDecodeScriptCallstacks = Header.bDecodeScriptCallstacks;

            ResetSnapshotDescriptions();

            bool bFoundMultiPoolCallStacks = false;

            // Start frame 0.
            FStreamInfo.GlobalInstance.FrameStreamIndices.Add(0);
			FStreamInfo.GlobalInstance.DeltaTimeArray.Add( 0.0f );

            // The USE_GLOBAL_REALLOC_ZERO_PTR option is used by dlmalloc.
            // When it's enabled, calls to realloc(NULL, 0) will always return the same valid pointer,
            // which can cause unnecessary warnings (double frees, etc) when we parse the allocation data.
            // Whether the option is enabled or disabled will be automatically detected by the code below.
            bool bUseGlobalReallocZeroPtr = false;
            bool bDetectingUseGlobalReallocZeroPtr = true;
            int ReallocZeroCount = 0;
            ulong ReallocZeroPtr = 0;

            FAllocationLifecycle NewLifecycle = new FAllocationLifecycle();

            int SnapshotIndex = 0;

            // Figure out the progress scale
            UInt64 StartOfMetadata = Math.Min(Header.NameTableOffset, Header.CallStackAddressTableOffset);
            StartOfMetadata = Math.Min(StartOfMetadata, Header.CallStackTableOffset);
            StartOfMetadata = Math.Min(StartOfMetadata, Header.ModulesOffset);

			long ProgressInterval = ((Int64)StartOfMetadata - TokenStreamOffset) / 1000;
			if (ProgressInterval < 1)
			{
				ProgressInterval = 1;
			}

			double ProgressScaleFactor = 100.0f / ((Int64)StartOfMetadata - TokenStreamOffset);
            long NextProgressUpdate = TokenStreamOffset;

			// Parse tokens till we reach the end of the stream.
			Token = new FStreamToken();

			using( FScopedLogTimer LoadingTime = new FScopedLogTimer( "Reading and parsing tokens") )
			{
				EProfilingPayloadType LastTokenType = EProfilingPayloadType.TYPE_Other;

				while( Token.ReadNextToken( BinaryStream ) )
				{
					// Check for pending cancellation of a background operation.
					if( BGWorker.CancellationPending )
					{
						EventArgs.Cancel = true;
						return;
					}

					long CurrentStreamPos = ParserFileStream.Position;

					if( ParserFileStream.Position >= NextProgressUpdate )
					{
						BGWorker.ReportProgress(
							( int )( ( CurrentStreamPos - TokenStreamOffset ) * ProgressScaleFactor ),
							String.Format( "7/8 Parsing token stream for {0}, part {1} of {2}", PrettyFilename, NextDataFile, Header.NumDataFiles ) );
						NextProgressUpdate += ProgressInterval;
					}

					if ( CustomSnapshots.Count > 0 && (Snapshot.AllocationCount >= CustomSnapshots[0] * AllocationsPerSlice) ) 
					{
						// Create an unnamed snapshot.
						FStreamSnapshot MarkerSnapshot = Snapshot.DeepCopy( PointerToPointerInfoMap );

						MarkerSnapshot.Description = "Unnamed snapshot allocations: " + Snapshot.AllocationCount;
						MarkerSnapshot.StreamIndex = Token.StreamIndex;
						MarkerSnapshot.FrameNumber = FStreamInfo.GlobalInstance.FrameStreamIndices.Count;
						MarkerSnapshot.CurrentTime = Token.TotalTime;
						MarkerSnapshot.ElapsedTime = Token.ElapsedTime;
						MarkerSnapshot.SubType = Token.SubType;
						MarkerSnapshot.SnapshotIndex = SnapshotIndex;
						MarkerSnapshot.MetricArray = new List<long>( Token.Metrics );
						MarkerSnapshot.LoadedLevels = new List<int>( Token.LoadedLevels );
						MarkerSnapshot.OverallMemorySlice = new List<FMemorySlice>( Snapshot.OverallMemorySlice );
						MarkerSnapshot.MemoryAllocationStats4 = Token.MemoryAllocationStats4.DeepCopy();

						FStreamInfo.GlobalInstance.SnapshotList.Add( MarkerSnapshot );

						CustomSnapshots.RemoveAt( 0 );

						Token.ElapsedTime = 0.0f;
					}

					switch( Token.Type )
					{
						// Malloc
						case EProfilingPayloadType.TYPE_Malloc:
						{
#if DEBUG_TIMINGS
							MallocTimer.Start();
#endif

							if ( Token.Pointer != 0 )
							{
								Token.CallStackIndex = GetVirtualCallStackIndex( Token, Observer );

								HandleMalloc( Token, Snapshot, PointerToPointerInfoMap );

								FCallStack CurrentCallstack = FStreamInfo.GlobalInstance.CallStackArray[ Token.CallStackIndex ];
								if( CurrentCallstack.MemoryPool != EMemoryPool.MEMPOOL_None && CurrentCallstack.MemoryPool != Token.Pool )
								{
									bFoundMultiPoolCallStacks = true;
								}

								CurrentCallstack.MemoryPool |= Token.Pool;
								FStreamInfo.GlobalInstance.MemoryPoolInfo[ Token.Pool ].AddPointer( Token.Pointer, Token.Size );

								CurrentCallstack.ProcessMalloc( Token, ref NewLifecycle );
							}

#if DEBUG_TIMINGS
							MallocTimer.Stop();
#endif
						}
						break;

						// Free
						case EProfilingPayloadType.TYPE_Free:
						{
#if DEBUG_TIMINGS
							FreeTimer.Start();
#endif
							if ( bDetectingUseGlobalReallocZeroPtr )
							{
								if( ReallocZeroCount > 0 && Token.Pointer == ReallocZeroPtr )
								{
									ReallocZeroCount--;
								}
							}

							if( bDetectingUseGlobalReallocZeroPtr || !bUseGlobalReallocZeroPtr || Token.Pointer != ReallocZeroPtr )
							{
								// Either USE_GLOBAL_REALLOC_ZERO_PTR is not being used, or we're not
								// trying to free the ReallocZeroPtr.

								FCallStackAllocationInfo FreedAllocInfo;
								if( HandleFree( Token, Snapshot, PointerToPointerInfoMap, out FreedAllocInfo ) )
								{
									FCallStack PreviousCallStack = FStreamInfo.GlobalInstance.CallStackArray[ FreedAllocInfo.CallStackIndex ];
									PreviousCallStack.ProcessFree( Token );
								}
							}

#if DEBUG_TIMINGS
							FreeTimer.Stop();
#endif
						}
						break;

						// Realloc
						case EProfilingPayloadType.TYPE_Realloc:
						{
#if DEBUG_TIMINGS
							ReallocTimer.Start();
#endif

							Token.CallStackIndex = GetVirtualCallStackIndex( Token, Observer );

							FCallStack PreviousCallstack = null;
							FAllocationLifecycle OldReallocLifecycle = null;

							if( Token.OldPointer != 0 )
							{
								FCallStackAllocationInfo FreedAllocInfo;
								if( HandleFree( Token, Snapshot, PointerToPointerInfoMap, out FreedAllocInfo ) )
								{
									PreviousCallstack = FStreamInfo.GlobalInstance.CallStackArray[ FreedAllocInfo.CallStackIndex ];
									if( Token.Size > 0 )
									{
										OldReallocLifecycle = PreviousCallstack.ProcessRealloc( Token, ref NewLifecycle, null, null );
									}
									else
									{
										PreviousCallstack.ProcessFree( Token );
									}
								}
							}
							else if( Token.Size == 0 )
							{
								if( bDetectingUseGlobalReallocZeroPtr )
								{
									if( ReallocZeroCount > 1 )
									{
										// This code checks to see if the return values of the second and third realloc(0, NULL) calls
										// match. The first one is always different for some reason.

										bUseGlobalReallocZeroPtr = Token.NewPointer == ReallocZeroPtr;
										bDetectingUseGlobalReallocZeroPtr = false;

										Debug.WriteLine( "USE_GLOBAL_REALLOC_ZERO_PTR is " + bUseGlobalReallocZeroPtr );
									}
									else
									{
										ReallocZeroPtr = Token.NewPointer;
										ReallocZeroCount++;
									}
								}


								if( bUseGlobalReallocZeroPtr )
								{
									// break out of case to avoid 'double malloc' warnings
#if DEBUG_TIMINGS
									ReallocTimer.Stop();
#endif
									break;
								}
							}

							if( Token.NewPointer != 0 )
							{
								Token.Pointer = Token.NewPointer;

								FCallStack CurrentCallstack = FStreamInfo.GlobalInstance.CallStackArray[ Token.CallStackIndex ];

								if( CurrentCallstack.MemoryPool != EMemoryPool.MEMPOOL_None && CurrentCallstack.MemoryPool != Token.Pool )
								{
									bFoundMultiPoolCallStacks = true;
								}

								CurrentCallstack.MemoryPool |= Token.Pool;
								FStreamInfo.GlobalInstance.MemoryPoolInfo[ Token.Pool ].AddPointer( Token.Pointer, Token.Size );

								HandleMalloc( Token, Snapshot, PointerToPointerInfoMap );

								if( CurrentCallstack != PreviousCallstack )
								{
									CurrentCallstack.ProcessRealloc( Token, ref NewLifecycle, PreviousCallstack, OldReallocLifecycle );
									IncompleteLifeCycles += CurrentCallstack.IncompleteLifecycles.Count;

								}

#if DEBUG_TIMINGS
								ReallocTimer.Stop();
#endif
							}
						}
						break;

						// Status/ payload.
						case EProfilingPayloadType.TYPE_Other:
						{
#if DEBUG_TIMINGS
							OtherTimer.Start();
#endif

							switch( Token.SubType )
							{
								case EProfilingPayloadSubType.SUBTYPE_EndOfStreamMarker:
								{
									// Should never receive EOS marker as ReadNextToken should've returned false.
									throw new InvalidDataException();
								}

								case EProfilingPayloadSubType.SUBTYPE_EndOfFileMarker:
								{
									// Switch to the next file in the chain
									ParserFileStream.Close();
									BinaryStream = SwitchStreams( (int)NextDataFile, FStreamInfo.GlobalInstance.FileName, bIsBigEndian, out ParserFileStream );

									// Update variables used for reporting progress
									TokenStreamOffset = 0;
									ProgressInterval = ParserFileStream.Length / 100;
									ProgressScaleFactor = 100.0f / ParserFileStream.Length;
									NextProgressUpdate = 0;

									// Tick over to the next file, and make sure things are still ending as expected
									NextDataFile++;
									if( NextDataFile > Header.NumDataFiles )
									{
										throw new InvalidDataException( "Found an unexpected number of data files (more than indicated in the master file" );
									}
									break;
								}

								// Create snapshot.
								case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LoadMap_Start:
								case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LoadMap_Mid:
								case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LoadMap_End:
								case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_GC_Start:
								case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_GC_End:
								case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LevelStream_Start:
								case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LevelStream_End:
								case EProfilingPayloadSubType.SUBTYPE_SnapshotMarker:
								{
									if( ( Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker )
										|| ( Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LoadMap_Start
											&& FStreamInfo.GlobalInstance.CreationOptions.LoadMapStartSnapshotsCheckBox.Checked )
										|| ( Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LoadMap_Mid
											&& FStreamInfo.GlobalInstance.CreationOptions.LoadMapMidSnapshotsCheckBox.Checked )
										|| ( Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LoadMap_End
											&& FStreamInfo.GlobalInstance.CreationOptions.LoadMapEndSnapshotsCheckBox.Checked )
										|| ( Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_GC_Start
											&& FStreamInfo.GlobalInstance.CreationOptions.GCStartSnapshotsCheckBox.Checked )
										|| ( Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_GC_End
											&& FStreamInfo.GlobalInstance.CreationOptions.GCEndSnapshotsCheckBox.Checked )
										|| ( Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LevelStream_Start
											&& FStreamInfo.GlobalInstance.CreationOptions.LevelStreamStartSnapshotsCheckBox.Checked )
										|| ( Token.SubType == EProfilingPayloadSubType.SUBTYPE_SnapshotMarker_LevelStream_End )
											&& FStreamInfo.GlobalInstance.CreationOptions.LevelStreamEndSnapshotsCheckBox.Checked )
									{
										FStreamSnapshot MarkerSnapshot = Snapshot.DeepCopy( PointerToPointerInfoMap );

										MarkerSnapshot.Description = GetNextSnapshotDescription( Token.SubType, FStreamInfo.GlobalInstance.NameArray[ Token.TextIndex ] );
										MarkerSnapshot.StreamIndex = Token.StreamIndex;
										MarkerSnapshot.FrameNumber = FStreamInfo.GlobalInstance.FrameStreamIndices.Count;
										MarkerSnapshot.CurrentTime = Token.TotalTime;
										MarkerSnapshot.ElapsedTime = Token.ElapsedTime;
										MarkerSnapshot.SubType = Token.SubType;
										MarkerSnapshot.SnapshotIndex = SnapshotIndex;
										MarkerSnapshot.MetricArray = new List<long>( Token.Metrics );
										MarkerSnapshot.LoadedLevels = new List<int>( Token.LoadedLevels );
										MarkerSnapshot.MemoryAllocationStats4 = Snapshot.MemoryAllocationStats4.DeepCopy();
										MarkerSnapshot.OverallMemorySlice = new List<FMemorySlice>( Snapshot.OverallMemorySlice );

										FStreamInfo.GlobalInstance.SnapshotList.Add( MarkerSnapshot );

										Token.ElapsedTime = 0.0f;
									}

									SnapshotIndex++;
									break;
								}


								case EProfilingPayloadSubType.SUBTYPE_TotalUsed:
								{
									break;
								}

								case EProfilingPayloadSubType.SUBTYPE_TotalAllocated:
								{
									break;
								}

								case EProfilingPayloadSubType.SUBTYPE_CPUUsed:
								{
									break;
								}

								case EProfilingPayloadSubType.SUBTYPE_CPUSlack:
								{
									break;
								}

								case EProfilingPayloadSubType.SUBTYPE_CPUWaste:
								{
									break;
								}

								case EProfilingPayloadSubType.SUBTYPE_GPUUsed:
								{
									break;
								}

								case EProfilingPayloadSubType.SUBTYPE_GPUSlack:
								{
									break;
								}

								case EProfilingPayloadSubType.SUBTYPE_GPUWaste:
								{
									break;
								}

								case EProfilingPayloadSubType.SUBTYPE_ImageSize:
								{
									break;
								}

								case EProfilingPayloadSubType.SUBTYPE_OSOverhead:
								{
									break;
								}

								case EProfilingPayloadSubType.SUBTYPE_FrameTimeMarker:
								{
									FStreamInfo.GlobalInstance.FrameStreamIndices.Add( Token.StreamIndex );
									FStreamInfo.GlobalInstance.DeltaTimeArray.Add( Token.DeltaTime );
									break;
								}

								case EProfilingPayloadSubType.SUBTYPE_TextMarker:
								{
									break;
								}

								case EProfilingPayloadSubType.SUBTYPE_MemoryAllocationStats:
								{
									Snapshot.MemoryAllocationStats4 = Token.MemoryAllocationStats4.DeepCopy();
									break;
								}

								// Unhandled.
								default:
								{
									throw new InvalidDataException();
								}
							}
#if DEBUG_TIMINGS
							OtherTimer.Stop();
#endif
							break;
						}

						// Unhandled.
						default:
						{
							throw new InvalidDataException();
						}
					}

					if( NewLifecycle == null )
					{
						NewLifecycle = new FAllocationLifecycle();
					}

					// Advance the stream index.
					Token.StreamIndex++;
					LastTokenType = Token.Type;
				}
			}
			//-----------------------------------------------------------------------------
			//@DEBUG
			WriteTimings();
			//-----------------------------------------------------------------------------

            if (MainMProfWindow != null && bFoundMultiPoolCallStacks)
            {
                MessageBox.Show("Some callstacks appear to allocate to multiple pools. This will make profiling more difficult and is usually caused by function inlining. It can also be caused by using the wrong executable to decode the profile.");
            }

            FStreamInfo.GlobalInstance.bHasMultiPoolCallStacks = bFoundMultiPoolCallStacks;

			// Closes the file so it can potentially be opened for writing.
			ParserFileStream.Close();

            // Mark end of last frame.
			FStreamInfo.GlobalInstance.FrameStreamIndices.Add( Token.StreamIndex );
			FStreamInfo.GlobalInstance.DeltaTimeArray.Add( 0.0f );

            // make sure all lifetimecallstacklists are as big as the latest one
			foreach( FStreamSnapshot PreviousSnapshot in FStreamInfo.GlobalInstance.SnapshotList )
			{
				while( PreviousSnapshot.LifetimeCallStackList.Count < Snapshot.LifetimeCallStackList.Count )
				{
					PreviousSnapshot.LifetimeCallStackList.Add( new FCallStackAllocationInfo( 0, PreviousSnapshot.LifetimeCallStackList.Count, 0 ) );
				}
			}

            List<CallStackPattern> OrderedPatternList = MainMProfWindow.Options.GetOrderedPatternList();
            ClassGroup UngroupedGroup = MainMProfWindow.Options.UngroupedGroup;


			double CallStackScaleFactor = 100.0f / FStreamInfo.GlobalInstance.CallStackArray.Count;
			long CallStackInterval = FStreamInfo.GlobalInstance.CallStackArray.Count / 100;
			long CallStackNextProgressUpdate = CallStackInterval;

			int CallStackCurrent = 0;
			foreach( FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray )
			{
				if( CallStackCurrent >= CallStackNextProgressUpdate )
				{
					BGWorker.ReportProgress( ( int )( CallStackCurrent * CallStackScaleFactor ), "8/8 Matching callstacks to groups for " + PrettyFilename );
					CallStackNextProgressUpdate += CallStackInterval;
				}
				CallStackCurrent++;

				if( CallStack.AddressIndices.Count > 0 )
				{
					// Find the first non templated entry in each callstack
					CallStack.EvaluateFirstNonContainer();

					// Go through each pattern to find the first match.
					// It's important that the patterns are evaluated in the correct order.
					foreach( CallStackPattern Pattern in OrderedPatternList )
					{
						if( Pattern.Matches( CallStack ) )
						{
							CallStack.Group = Pattern.Group;
							Pattern.AddCallStack( CallStack );
							break;
						}
					}

					if( CallStack.Group == null )
					{
						CallStack.Group = UngroupedGroup;
						UngroupedGroup.CallStackPatterns[ 0 ].AddCallStack( CallStack );
					}
				}			
            }

			// Add snapshot in end state to the list and return it.
			Snapshot.StreamIndex = Token.StreamIndex;
            Snapshot.FrameNumber = FStreamInfo.GlobalInstance.FrameStreamIndices.Count;
			Snapshot.CurrentTime = Token.TotalTime;
			Snapshot.ElapsedTime = Token.ElapsedTime;
            Snapshot.MetricArray = new List<long>(Token.Metrics);
            Snapshot.LoadedLevels = new List<int>(Token.LoadedLevels);
			Snapshot.MemoryAllocationStats4 = Token.MemoryAllocationStats4.DeepCopy();
            Snapshot.FinalizeSnapshot(PointerToPointerInfoMap);
            FStreamInfo.GlobalInstance.SnapshotList.Add(Snapshot);

            BGWorker.ReportProgress(100, "Finalizing snapshots for " + PrettyFilename);

			// Finalize snapshots. This entails creating the sorted snapshot list.
			foreach( FStreamSnapshot SnapshotToFinalize in SnapshotList )
			{
				SnapshotToFinalize.FinalizeSnapshot(null);
			}
		}
        public static void ParseSnapshot( TreeView CallGraphTreeView, List<FCallStackAllocationInfo> CallStackList, bool bShouldSortBySize, string FilterText, bool bInvertCallStacks )
        {
            // Progress bar.
            OwnerWindow.ToolStripProgressBar.Value = 0;
            OwnerWindow.ToolStripProgressBar.Visible = true;
            long ProgressInterval = CallStackList.Count / 20;
            long NextProgressUpdate = ProgressInterval;
            int CallStackCurrent = 0;

            OwnerWindow.UpdateStatus( "Updating call graph for " + OwnerWindow.CurrentFilename );

            CallGraphTreeView.BeginUpdate();

            // Clear out existing nodes and add two root nodes. One for regular call stacks and one for truncated ones.
            CallGraphTreeView.Nodes.Clear();
            TreeNode RegularNode = new TreeNode( "Full Callstacks" );
            TreeNode TruncatedNode = new TreeNode( "Truncated Callstacks" );
            CallGraphTreeView.Nodes.Add(RegularNode);
            CallGraphTreeView.Nodes.Add(TruncatedNode);

            using( FScopedLogTimer ParseTiming = new FScopedLogTimer( "FCallGraphTreeViewParser.ParseSnapshot" ) )
            {
                // Iterate over all call graph paths and add them to the graph.
                foreach( FCallStackAllocationInfo AllocationInfo in CallStackList )
                {
                    // Update progress bar.
                    if( CallStackCurrent >= NextProgressUpdate )
                    {
                        OwnerWindow.ToolStripProgressBar.PerformStep();
                        NextProgressUpdate += ProgressInterval;
                        Debug.WriteLine( "FCallGraphTreeViewParser.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20" );
                    }
                    CallStackCurrent++;

                    // Add this call graph to the tree view.
                    FCallStack CallStack = FStreamInfo.GlobalInstance.CallStackArray[ AllocationInfo.CallStackIndex ];
                    // Split the tree into full and truncated callstacks.
                    TreeNode RootNode = CallStack.bIsTruncated ? TruncatedNode : RegularNode;
                    // Don't bother with callstacks that don't have a contribution.
                    if( ( ( AllocationInfo.Count != 0 ) || ( AllocationInfo.Size != 0 ) )
                        // Apply filter based on text representation of address.
                        && CallStack.RunFilters( FilterText, OwnerWindow.Options.ClassGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool ) )
                    {
                        // Add call stack to proper part of graph.
                        AddCallStackToGraph( RootNode, CallStack, AllocationInfo, ParentFunctionIndex, bInvertCallStacks );
                    }
                }
            }

            // Update the node text by prepending memory usage and allocation count.
            UpdateNodeText( RegularNode );
            UpdateNodeText( TruncatedNode );

            // Last but not least, set the node sorter property to sort nodes.
            if( bShouldSortBySize )
            {
                CallGraphTreeView.TreeViewNodeSorter = new FNodeSizeSorter();
            }
            else
            {
                CallGraphTreeView.TreeViewNodeSorter = new FNodeCountSorter();
            }

            CallGraphTreeView.EndUpdate();

            OwnerWindow.ToolStripProgressBar.Visible = false;
        }
Exemple #15
0
        public static void ParseSnapshot(string FilterText)
        {
            if (!FStreamInfo.GlobalInstance.CreationOptions.KeepLifecyclesCheckBox.Checked)
            {
                return;
            }

            // Progress bar
            long ProgressInterval   = FStreamInfo.GlobalInstance.CallStackArray.Count / 20;
            long NextProgressUpdate = ProgressInterval;
            int  CallStackCurrent   = 0;

            OwnerWindow.ToolStripProgressBar.Value   = 0;
            OwnerWindow.ToolStripProgressBar.Visible = true;
            OwnerWindow.UpdateStatus("Updating short lived allocation view for " + OwnerWindow.CurrentFilename);

            OwnerWindow.ShortLivedListView.BeginUpdate();
            OwnerWindow.ShortLivedListView.Items.Clear();

            OwnerWindow.ShortLivedListView.ListViewItemSorter = null;             // clear this to avoid a Sort for each call to Add

            const int MaxLifetime    = 1;
            const int MinAllocations = 100;

            ulong StartStreamIndex = OwnerWindow.GetStartSnapshotStreamIndex();
            ulong EndStreamIndex   = OwnerWindow.GetEndSnapshotStreamIndex();

            uint[] AllocationLifetimes = new uint[MaxLifetime + 1];

            using (FScopedLogTimer ParseTiming = new FScopedLogTimer("FShortLivedAllocationView.ParseSnapshot"))
            {
                foreach (FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray)
                {
                    // Update progress bar.
                    if (CallStackCurrent >= NextProgressUpdate)
                    {
                        OwnerWindow.ToolStripProgressBar.PerformStep();
                        NextProgressUpdate += ProgressInterval;
                        Debug.WriteLine("FShortLivedAllocationView.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20");
                    }
                    CallStackCurrent++;

                    if (CallStack.RunFilters(FilterText, OwnerWindow.Options.ClassGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool))
                    {
                        Array.Clear(AllocationLifetimes, 0, AllocationLifetimes.Length);

                        int  NumAllocations             = 0;
                        uint UniqueFramesWithAge0Allocs = 0;
                        int  LastFrameWithAge0Allocs    = 0;

                        int   CurrentRun               = 0;
                        float CurrentTotalAllocSize    = 0;
                        int   CurrentRunFrame          = -1;
                        int   LongestRun               = 0;
                        float LongestRunTotalAllocSize = 0;

                        int LastEndFrame = 1;
                        int LastSnapshot = 0;
                        foreach (FAllocationLifecycle Lifecycle in CallStack.CompleteLifecycles)
                        {
                            // only process allocations that were really freed, not just realloced
                            if (Lifecycle.FreeStreamIndex != FStreamInfo.INVALID_STREAM_INDEX &&
                                Lifecycle.AllocEvent.StreamIndex > StartStreamIndex && Lifecycle.FreeStreamIndex < EndStreamIndex)
                            {
                                // CompleteLifecycles are sorted by FreeStreamIndex, so this search pattern ensures
                                // that the frames will be found as quickly as possible.
                                int EndFrame;
                                if (Lifecycle.FreeStreamIndex < FStreamInfo.GlobalInstance.FrameStreamIndices[LastEndFrame])
                                {
                                    EndFrame = LastEndFrame;
                                }
                                else
                                {
                                    if (Lifecycle.FreeStreamIndex > FStreamInfo.GlobalInstance.SnapshotList[LastSnapshot].StreamIndex)
                                    {
                                        // lifecycle isn't even in same snapshot, so search by snapshot first (much faster than
                                        // searching through frames one by one)
                                        LastSnapshot = OwnerWindow.GetSnapshotIndexFromStreamIndex(LastSnapshot, Lifecycle.FreeStreamIndex);

                                        if (LastSnapshot == 0)
                                        {
                                            LastEndFrame = 1;
                                        }
                                        else
                                        {
                                            LastEndFrame = FStreamInfo.GlobalInstance.SnapshotList[LastSnapshot - 1].FrameNumber;
                                        }
                                    }

                                    EndFrame = FStreamInfo.GlobalInstance.GetFrameNumberFromStreamIndex(LastEndFrame, Lifecycle.FreeStreamIndex);
                                }

                                int StartFrame = EndFrame;
                                while (FStreamInfo.GlobalInstance.FrameStreamIndices[StartFrame] > Lifecycle.AllocEvent.StreamIndex &&
                                       StartFrame > 0 && EndFrame - StartFrame <= MaxLifetime + 1)
                                {
                                    StartFrame--;
                                }
                                StartFrame++;

                                int Age = EndFrame - StartFrame;

                                if (Age <= MaxLifetime)
                                {
                                    AllocationLifetimes[Age]++;
                                    NumAllocations++;

                                    if (Age == 0)
                                    {
                                        if (StartFrame != LastFrameWithAge0Allocs)
                                        {
                                            UniqueFramesWithAge0Allocs++;
                                            LastFrameWithAge0Allocs = StartFrame;
                                        }
                                    }
                                    else if (Age == 1)
                                    {
                                        if (StartFrame == CurrentRunFrame)
                                        {
                                            CurrentRun++;

                                            CurrentTotalAllocSize += Lifecycle.PeakSize;

                                            if (CurrentRun > LongestRun)
                                            {
                                                LongestRun = CurrentRun;
                                                LongestRunTotalAllocSize = CurrentTotalAllocSize;
                                            }
                                        }
                                        else if (StartFrame > CurrentRunFrame)
                                        {
                                            CurrentRun            = 1;
                                            CurrentTotalAllocSize = Lifecycle.PeakSize;
                                        }
                                        else if (EndFrame == CurrentRunFrame)
                                        {
                                            CurrentTotalAllocSize += Lifecycle.PeakSize;

                                            if (CurrentRun == LongestRun && LongestRunTotalAllocSize < CurrentTotalAllocSize)
                                            {
                                                LongestRunTotalAllocSize = CurrentTotalAllocSize;
                                            }
                                        }

                                        CurrentRunFrame = EndFrame;
                                    }
                                }
                            }
                        }

                        if (NumAllocations > MinAllocations)
                        {
                            float LongestRunAvgAllocSize = LongestRun == 0 ? 0 : LongestRunTotalAllocSize / LongestRun;

                            uint[] ColumnValues = new uint[5];
                            ColumnValues[0] = AllocationLifetimes[0];
                            ColumnValues[1] = UniqueFramesWithAge0Allocs;
                            ColumnValues[2] = AllocationLifetimes[1];
                            ColumnValues[3] = (uint)LongestRun;
                            ColumnValues[4] = (uint)LongestRunAvgAllocSize;

                            var LVItem = new ListViewItem();

                            LVItem.Tag = new FShortLivedCallStackTag(CallStack, ColumnValues);

                            LVItem.Text = ColumnValues[0].ToString();
                            for (int ValueIndex = 1; ValueIndex < ColumnValues.Length; ValueIndex++)
                            {
                                LVItem.SubItems.Add(ColumnValues[ValueIndex].ToString());
                            }

                            OwnerWindow.ShortLivedListView.Items.Add(LVItem);
                        }
                    }
                }
            }

            OwnerWindow.ShortLivedListView.ListViewItemSorter = new FShortLivedListViewComparer();             // Assignment automatically calls Sort
            OwnerWindow.ShortLivedListView.SetSortArrow(ColumnToSortBy, ColumnSortModeAscending);

            OwnerWindow.ShortLivedListView.EndUpdate();

            OwnerWindow.ToolStripProgressBar.Visible = false;
        }
		public static void UnsafeBitmapClick( MouseEventArgs e )
		{
			OwnerWindow.MemoryBitmapCallStackListView.Items.Clear();

			OwnerWindow.MemoryBitmapAllocationHistoryListView.BeginUpdate();
			OwnerWindow.MemoryBitmapAllocationHistoryListView.Items.Clear();

			ulong PointerFromPixel = FMemoryBitmapParser.GetPointerFromPixel( MemoryBitmap.Width, BytesPerPixel, e.X - MEMORY_BITMAP_LEFT_MARGIN, e.Y );
			string FilterText = OwnerWindow.FilterTextBox.Text.ToUpperInvariant();

			using( FScopedLogTimer ParseTiming = new FScopedLogTimer( "FMemoryBitmapParser.UnsafeBitmapClick" ) )
			{
				foreach( FCallStack CallStack in FStreamInfo.GlobalInstance.CallStackArray )
				{
					if( CallStack.RunFilters( FilterText, OwnerWindow.Options.ClassGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool ) )
					{
						foreach( FAllocationLifecycle AllocLifecycle in CallStack.CompleteLifecycles )
						{
							ProcessLifecycleForPixel( PointerFromPixel, CallStack, AllocLifecycle, true );
						}

						foreach( KeyValuePair<ulong, FAllocationLifecycle> AllocLifecycle in CallStack.IncompleteLifecycles )
						{
							ProcessLifecycleForPixel( PointerFromPixel, CallStack, AllocLifecycle.Value, false );
						}
					}
				}
			}

			// Pointers that were malloced and then realloced by a different callstack will have missing end frames
			// (marked with "-1"), but those end frames are guaranteed to be the same as the start frame of the
			// following allocation at this pointer, so we can just go over the list and fix up the references.
			for( int ItemIndex = 0; ItemIndex < OwnerWindow.MemoryBitmapAllocationHistoryListView.Items.Count; ItemIndex++ )
			{
				if( OwnerWindow.MemoryBitmapAllocationHistoryListView.Items[ ItemIndex ].SubItems.Count > 0
					&& OwnerWindow.MemoryBitmapAllocationHistoryListView.Items[ ItemIndex ].SubItems[ 1 ].Text == "-1"
					&& ItemIndex + 1 < OwnerWindow.MemoryBitmapAllocationHistoryListView.Items.Count )
				{
					OwnerWindow.MemoryBitmapAllocationHistoryListView.Items[ ItemIndex ].SubItems[ 1 ].Text = OwnerWindow.MemoryBitmapAllocationHistoryListView.Items[ ItemIndex + 1 ].Text;
				}
			}

			OwnerWindow.MemoryBitmapAllocationHistoryListView.EndUpdate();

			if( OwnerWindow.MemoryBitmapAllocationHistoryListView.Items.Count > 0 )
			{
				OwnerWindow.MemoryBitmapAllocationHistoryListView.Items[ GetMemoryBitmapActiveAllocationForStreamIndex( OwnerWindow.CurrentSnapshot.StreamIndex ) ].Selected = true;
			}
			else
			{
				// refresh panel to clear selection
				OwnerWindow.MemoryBitmapPanel.Invalidate();
			}
		}
Exemple #17
0
        public static void ParseSnapshot(List <FCallStackAllocationInfo> CallStackList, string FilterText)
        {
            // Progress bar
            long ProgressInterval   = CallStackList.Count / 20;
            long NextProgressUpdate = ProgressInterval;
            int  CallStackCurrent   = 0;

            OwnerWindow.ToolStripProgressBar.Value   = 0;
            OwnerWindow.ToolStripProgressBar.Visible = true;

            OwnerWindow.UpdateStatus("Updating histogram view for " + OwnerWindow.CurrentFilename);

            List <ClassGroup> CallStackGroups = OwnerWindow.Options.ClassGroups;

            List <FHistogramBar>[] Bars = new List <FHistogramBar> [NUM_MEMORY_BANKS];

            for (int BankIndex = 0; BankIndex < Bars.Length; BankIndex++)
            {
                Bars[BankIndex] = new List <FHistogramBar>();

                // The first bar in each column is for callstacks unmatched by any pattern.
                Bars[BankIndex].Add(new FHistogramBar("Other", Color.White));

                // Add all groups to all memory bank columns.
                foreach (ClassGroup CallStackGroup in CallStackGroups)
                {
                    var Bar = new FHistogramBar(CallStackGroup);
                    Bar.BeginBatchAddition();
                    Bars[BankIndex].Add(Bar);
                }
            }

            using (FScopedLogTimer ParseTiming = new FScopedLogTimer("HistogramParser.ParseSnapshot"))
            {
                long Size  = 0;
                int  Count = 0;

                bool bFilterIn = OwnerWindow.IsFilteringIn();

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

                foreach (FCallStackAllocationInfo AllocationInfo in FilteredCallstackList)
                {
                    // Update progress bar.
                    if (CallStackCurrent >= NextProgressUpdate)
                    {
                        OwnerWindow.ToolStripProgressBar.PerformStep();
                        NextProgressUpdate += ProgressInterval;
                        Debug.WriteLine("FHistogramParser.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20");
                    }
                    CallStackCurrent++;

                    FCallStack OriginalCallStack = FStreamInfo.GlobalInstance.CallStackArray[AllocationInfo.CallStackIndex];
                    if (OriginalCallStack.RunFilters(FilterText, CallStackGroups, bFilterIn, OwnerWindow.SelectedMemoryPool))
                    {
                        bool bFound = false;
                        int  Column = FMemoryPoolInfo.GetMemoryPoolHistogramColumn(OriginalCallStack.MemoryPool);
                        if (Column == -1)
                        {
                            // If the callstack is in multiple pools, just put it in the first bank.
                            // The user has already been warned about multi-pool callstacks.
                            Column = 0;
                        }

                        for (int GroupIndex = 0; GroupIndex < CallStackGroups.Count; GroupIndex++)
                        {
                            foreach (CallStackPattern CallStackPatternIt in CallStackGroups[GroupIndex].CallStackPatterns)
                            {
                                if (CallStackPatternIt.ContainsCallStack(FStreamInfo.GlobalInstance.CallStackArray[AllocationInfo.CallStackIndex]))
                                {
                                    Bars[Column][GroupIndex + 1].AddAllocation(AllocationInfo);
                                    bFound = true;
                                    goto HackyBreakAll;
                                }
                            }
                        }
HackyBreakAll:

                        if (!bFound)
                        {
                            // No pattern matched this callstack, so add it to the Other bar
                            Bars[Column][0].AddAllocation(AllocationInfo);
                        }
                    }

                    Size  += AllocationInfo.TotalSize;
                    Count += AllocationInfo.TotalCount;
                }
            }

            // End the update batch and allow things to sort
            for (int BankIndex = 0; BankIndex < Bars.Length; BankIndex++)
            {
                foreach (ClassGroup CallStackGroup in CallStackGroups)
                {
                    foreach (var Bar in Bars[BankIndex])
                    {
                        Bar.EndBatchAddition();
                    }
                }
            }

            OwnerWindow.ToolStripProgressBar.Visible = false;
            HistogramBars = Bars;

            // Select first valid histogram bar.
            SelectFirstValidHistogramBar();
        }
		public static void ParseSnapshot( TreeView CallGraphTreeView, List<FCallStackAllocationInfo> CallStackList, bool bShouldSortBySize, string FilterText, bool bInvertCallStacks )
		{
			// Progress bar.
			OwnerWindow.ToolStripProgressBar.Value = 0;
			OwnerWindow.ToolStripProgressBar.Visible = true;
			long ProgressInterval = CallStackList.Count / 20;
			long NextProgressUpdate = ProgressInterval;
			int CallStackCurrent = 0;
			
			OwnerWindow.UpdateStatus( "Updating call graph for " + OwnerWindow.CurrentFilename );

			CallGraphTreeView.BeginUpdate();

			CallGraphTreeView.TreeViewNodeSorter = null; // clear this to avoid a Sort for each call to Add

			Debug.WriteLine("FCallGraphTreeViewParser.ParseSnapshot - Building call graph tree for " + OwnerWindow.CurrentFilename);

			var TruncatedNode = new FCallGraphNode("Truncated Callstacks");
			var RegularNode = new FCallGraphNode("Full Callstacks");

			using ( FScopedLogTimer ParseTiming = new FScopedLogTimer( "FCallGraphTreeViewParser.ParseSnapshot" ) )
			{
				// Iterate over all call graph paths and add them to the graph.
				foreach( FCallStackAllocationInfo AllocationInfo in CallStackList )
				{
					// Update progress bar.
					if( CallStackCurrent >= NextProgressUpdate )
					{
						OwnerWindow.ToolStripProgressBar.PerformStep();
						NextProgressUpdate += ProgressInterval;
						Debug.WriteLine( "FCallGraphTreeViewParser.ParseSnapshot " + OwnerWindow.ToolStripProgressBar.Value + "/20" );
					}
					CallStackCurrent++;

					// Add this call graph to the tree view.
					FCallStack CallStack = FStreamInfo.GlobalInstance.CallStackArray[ AllocationInfo.CallStackIndex ];
					// Split the tree into full and truncated callstacks.
					var RootNode = CallStack.bIsTruncated ? TruncatedNode : RegularNode;
					// Don't bother with callstacks that don't have a contribution.
					if( ( ( AllocationInfo.Count != 0 ) || ( AllocationInfo.Size != 0 ) )
						// Apply filter based on text representation of address.
						&& CallStack.RunFilters( FilterText, OwnerWindow.Options.ClassGroups, OwnerWindow.IsFilteringIn(), OwnerWindow.SelectedMemoryPool ) )
					{
						// Add call stack to proper part of graph.
						AddCallStackToGraph( RootNode, CallStack, AllocationInfo, ParentFunctionIndex, bInvertCallStacks );
					}
				}
			}

			Debug.WriteLine("FCallGraphTreeViewParser.ParseSnapshot - Sorting call graph tree for " + OwnerWindow.CurrentFilename);

			// Sort the nodes before adding them to the tree (sorting when in the tree is slow!).
			SortNodes(TruncatedNode, bShouldSortBySize);
			SortNodes(RegularNode, bShouldSortBySize);

			Debug.WriteLine("FCallGraphTreeViewParser.ParseSnapshot - Populating call graph tree for " + OwnerWindow.CurrentFilename);

			// Clear out existing nodes and add two root nodes. One for regular call stacks and one for truncated ones.
			CallGraphTreeView.Nodes.Clear();
			CallGraphTreeView.Tag = new FCallGraphTreeViewTag();
			AddRootTreeNode(TruncatedNode, CallGraphTreeView);
			AddRootTreeNode(RegularNode, CallGraphTreeView);

			CallGraphTreeView.BeforeExpand -= HandlePreExpandTreeNode;
			CallGraphTreeView.BeforeExpand += HandlePreExpandTreeNode;

			CallGraphTreeView.EndUpdate();

			OwnerWindow.ToolStripProgressBar.Visible = false;
		}