2019-12-26 23:01:54 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
using System ;
using System.Collections.Generic ;
using System.Text ;
using System.Drawing ;
using System.Diagnostics ;
using System.IO ;
using System.Linq ;
using System.Windows.Forms ;
using System.Drawing.Drawing2D ;
namespace MemoryProfiler2
{
/// <summary> Histogram parser. </summary>
public static class FHistogramParser
{
/// <summary>
/// The assumption that this constant is 2 is still hardcoded
/// in some places, so increasing the number of banks is not
/// as simple as changing this constant.
///
/// Second memory bank is used only on PS3 to show local memory.
/// </summary>
public const int NUM_MEMORY_BANKS = 2 ;
/// <summary> Array of histogram bars. </summary>
static public List < FHistogramBar > [ ] HistogramBars ;
/// <summary> Array of histogram bars for selected main histogram bar. </summary>
static private List < FHistogramBar > HistogramSelectionBars = new List < FHistogramBar > ( ) ;
/// <summary> Currently selected histogram bar from the memory bank. </summary>
static private FHistogramBar SelectedHistogramBar ;
/// <summary> Currently selected histogram bar from the detailed bank. </summary>
static private FHistogramBar SubselectedHistogramBar ;
/// <summary> Index of the bank for the currently selected histogram bar. </summary>
static private int SelectedMemoryBankIndex = 0 ;
/// <summary> Indices of the currently selected histogram bars in the memory banks. </summary>
static private int [ ] SelectedHistogramBarIndex = new int [ 2 ] { 0 , 0 } ;
static private Pen BlackDashedPen = new Pen ( Color . Black ) ;
static private Pen RedDashedPen = new Pen ( Color . Red ) ;
private static Brush CheckerBWBrush1x1 = new HatchBrush ( HatchStyle . Percent50 , Color . FromArgb ( 224 , Color . Plum ) , Color . FromArgb ( 224 , Color . SkyBlue ) ) ;
private static Brush CheckerBWBrush2x2 = new HatchBrush ( HatchStyle . SmallCheckerBoard , Color . FromArgb ( 192 , Color . Plum ) , Color . FromArgb ( 192 , Color . SkyBlue ) ) ;
private static Brush CheckerBWBrush4x4 = new HatchBrush ( HatchStyle . LargeCheckerBoard , Color . FromArgb ( 160 , Color . Plum ) , Color . FromArgb ( 160 , Color . SkyBlue ) ) ;
private static Pen OutlinePen1 = new Pen ( Color . FromArgb ( 128 , Color . White ) ) ;
private static Pen OutlinePen2 = new Pen ( Color . FromArgb ( 192 , Color . Black ) ) ;
/// <summary> Pen with 1x1 checker used to draw selected histogram bar. </summary>
private static Pen CheckerPen1x1 = new Pen ( CheckerBWBrush1x1 ) ;
/// <summary> Pen with 2x2 checker used to draw selected histogram bar. </summary>
private static Pen CheckerPen2x2 = new Pen ( CheckerBWBrush2x2 ) ;
/// <summary> Pen with 4x4 checker used to draw selected histogram bar. </summary>
private static Pen CheckerPen4x4 = new Pen ( CheckerBWBrush4x4 ) ;
private static Pen SelectedSolidPen = new Pen ( new SolidBrush ( Color . LightSteelBlue ) ) ;
/// <summary> Pen used to draw the selected histogram bar. Size of the checker is based on the selection height. </summary>
private static Pen SelectedBarPen ( FHistogramBar HistogramBar )
{
int SelectionHeight = ( int ) HistogramBar . Rect . Height ;
if ( SelectionHeight < 8 )
{
return CheckerPen1x1 ;
}
else if ( SelectionHeight < 16 )
{
return CheckerPen2x2 ;
}
return CheckerPen4x4 ;
}
/// <summary> Reference to the main memory profiler window. </summary>
private static MainWindow OwnerWindow ;
public static void SetProfilerWindow ( MainWindow InMainWindow )
{
BlackDashedPen . DashStyle = DashStyle . Dash ;
RedDashedPen . DashStyle = DashStyle . Dash ;
OwnerWindow = InMainWindow ;
}
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 )
{
2016-09-21 10:07:18 -04:00
var Bar = new FHistogramBar ( CallStackGroup ) ;
Bar . BeginBatchAddition ( ) ;
Bars [ BankIndex ] . Add ( Bar ) ;
2014-03-14 14:13:41 -04:00
}
}
using ( FScopedLogTimer ParseTiming = new FScopedLogTimer ( "HistogramParser.ParseSnapshot" ) )
{
long Size = 0 ;
int Count = 0 ;
2016-10-05 13:23:01 -04:00
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 )
2014-03-14 14:13:41 -04:00
{
// Update progress bar.
2016-10-05 13:23:01 -04:00
if ( CallStackCurrent > = NextProgressUpdate )
2014-03-14 14:13:41 -04:00
{
OwnerWindow . ToolStripProgressBar . PerformStep ( ) ;
NextProgressUpdate + = ProgressInterval ;
Debug . WriteLine ( "FHistogramParser.ParseSnapshot " + OwnerWindow . ToolStripProgressBar . Value + "/20" ) ;
}
CallStackCurrent + + ;
2016-10-05 13:23:01 -04:00
FCallStack OriginalCallStack = FStreamInfo . GlobalInstance . CallStackArray [ AllocationInfo . CallStackIndex ] ;
if ( OriginalCallStack . RunFilters ( FilterText , CallStackGroups , bFilterIn , OwnerWindow . SelectedMemoryPool ) )
2014-03-14 14:13:41 -04:00
{
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 )
{
2016-09-21 10:07:18 -04:00
if ( CallStackPatternIt . ContainsCallStack ( FStreamInfo . GlobalInstance . CallStackArray [ AllocationInfo . CallStackIndex ] ) )
2014-03-14 14:13:41 -04:00
{
2016-09-21 10:07:18 -04:00
Bars [ Column ] [ GroupIndex + 1 ] . AddAllocation ( AllocationInfo ) ;
bFound = true ;
goto HackyBreakAll ;
2014-03-14 14:13:41 -04:00
}
}
}
HackyBreakAll :
if ( ! bFound )
{
// No pattern matched this callstack, so add it to the Other bar
Bars [ Column ] [ 0 ] . AddAllocation ( AllocationInfo ) ;
}
}
2016-10-05 13:23:01 -04:00
Size + = AllocationInfo . TotalSize ;
Count + = AllocationInfo . TotalCount ;
2014-03-14 14:13:41 -04:00
}
}
2016-09-21 10:07:18 -04:00
// 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 ( ) ;
}
}
}
2014-03-14 14:13:41 -04:00
OwnerWindow . ToolStripProgressBar . Visible = false ;
HistogramBars = Bars ;
// Select first valid histogram bar.
SelectFirstValidHistogramBar ( ) ;
}
/// <summary> Selects first valid histogram bar, searches through all memory banks. </summary>
private static void SelectFirstValidHistogramBar ( )
{
SelectedMemoryBankIndex = 0 ;
SelectedHistogramBarIndex [ 0 ] = 0 ;
SelectedHistogramBarIndex [ 1 ] = 0 ;
SubselectedHistogramBar = null ;
for ( int MemoryBankIndex = 0 ; MemoryBankIndex < HistogramBars . Length ; MemoryBankIndex + + )
{
for ( int BarIndex = 0 ; BarIndex < HistogramBars [ MemoryBankIndex ] . Count ; BarIndex + + )
{
FHistogramBar Bar = FHistogramParser . HistogramBars [ MemoryBankIndex ] [ BarIndex ] ;
if ( Bar . AllocationCount > 0 )
{
SelectedMemoryBankIndex = MemoryBankIndex ;
SelectedHistogramBarIndex [ SelectedMemoryBankIndex ] = BarIndex ;
SelectHistogramBar ( Bar ) ;
return ;
}
}
}
SelectedHistogramBar = null ;
}
public static void ClearView ( )
{
if ( HistogramBars ! = null )
{
HistogramBars [ 0 ] . Clear ( ) ;
HistogramBars [ 1 ] . Clear ( ) ;
}
HistogramSelectionBars . Clear ( ) ;
SelectedMemoryBankIndex = 0 ;
SelectedHistogramBarIndex [ 0 ] = 0 ;
SelectedHistogramBarIndex [ 1 ] = 0 ;
SelectedHistogramBar = null ;
SubselectedHistogramBar = null ;
OwnerWindow . HistogramViewCallStackListView . BeginUpdate ( ) ;
OwnerWindow . HistogramViewCallStackListView . Items . Clear ( ) ;
OwnerWindow . HistogramViewCallStackListView . SelectedItems . Clear ( ) ;
OwnerWindow . HistogramViewCallStackListView . EndUpdate ( ) ;
OwnerWindow . HistogramViewNameLabel . Text = "" ;
OwnerWindow . HistogramViewSizeLabel . Text = "" ;
OwnerWindow . HistogramViewAllocationsLabel . Text = "" ;
OwnerWindow . MemoryBitmapAllocatedMemoryLabel . Text = "" ;
}
public static void PaintPanel ( PaintEventArgs e )
{
int [ ] MemorySizes = new int [ ]
{
FStreamInfo . GetMemoryBankSize ( 0 ) ,
FStreamInfo . GetMemoryBankSize ( 1 )
} ;
string [ ] MemoryCaptions = new string [ ]
{
"MB (Local)" ,
"MB (Video)"
} ;
const int MinorTick = 2 ;
const int MajorTick = 10 ;
const int TickRight = 70 ;
const int GraphYBorder = 30 ;
const int BarLeft = TickRight + 10 ;
int NumColumns = 3 ;
int TotalBordersSize = NumColumns * BarLeft ;
int GraphWidth = Math . Min ( ( OwnerWindow . HistogramPanel . Width - TotalBordersSize ) / 3 , 150 ) ;
float BarWidth = GraphWidth * 0.6f ;
float GraphXGap = GraphWidth * 0.4f ;
int MaxMemorySize = 0 ;
for ( int BankIndex = 0 ; BankIndex < NUM_MEMORY_BANKS ; BankIndex + + )
{
MaxMemorySize = Math . Max ( MemorySizes [ BankIndex ] , MaxMemorySize ) ;
}
float TotalGraphHeight = OwnerWindow . HistogramPanel . Height - GraphYBorder * 2 ;
if ( HistogramBars ! = null )
{
for ( int MemoryBankIndex = 0 ; MemoryBankIndex < MemorySizes . Length ; MemoryBankIndex + + )
{
float GraphTop = GraphYBorder ;
float GraphLeft = MemoryBankIndex * ( BarLeft + BarWidth + GraphXGap ) ;
float YScale = TotalGraphHeight / MemorySizes [ MemoryBankIndex ] ;
// Draw vertical axes.
2016-09-21 10:07:18 -04:00
OwnerWindow . DrawYAxis ( e . Graphics , Pens . Black , Color . Black , MajorTick , MinorTick , GraphLeft + TickRight , GraphTop , TotalGraphHeight , MemoryCaptions [ MemoryBankIndex ] , MemorySizes [ MemoryBankIndex ] ) ;
2014-03-14 14:13:41 -04:00
float BarY = GraphTop + YScale * MemorySizes [ MemoryBankIndex ] ;
for ( int BarIndex = HistogramBars [ MemoryBankIndex ] . Count - 1 ; BarIndex > = 0 ; BarIndex - - )
{
FHistogramBar Bar = HistogramBars [ MemoryBankIndex ] [ BarIndex ] ;
if ( Bar . AllocationCount = = 0 )
{
continue ;
}
float BarHeight = ( float ) Bar . MemorySize / ( 1024 * 1024 ) * YScale ;
Bar . Rect . X = GraphLeft + BarLeft ;
Bar . Rect . Y = BarY - BarHeight ;
Bar . Rect . Width = BarWidth ;
Bar . Rect . Height = BarHeight ;
e . Graphics . FillRectangle ( new SolidBrush ( Bar . Colour ) , Bar . Rect . X , Bar . Rect . Y , Bar . Rect . Width , Bar . Rect . Height ) ;
BarY - = BarHeight ;
}
}
if ( SelectedHistogramBar ! = null )
{
e . Graphics . FillRectangle
(
SelectedBarPen ( SelectedHistogramBar ) . Brush ,
SelectedHistogramBar . Rect . X ,
SelectedHistogramBar . Rect . Y ,
SelectedHistogramBar . Rect . Width ,
Math . Max ( SelectedHistogramBar . Rect . Height , 1.0f )
) ;
float MarkerHeight = Math . Max ( SelectedHistogramBar . Rect . Height , 1.0f ) ;
float MarkerPosX = SelectedHistogramBar . Rect . Left + SelectedHistogramBar . Rect . Width + 1.0f ;
float MarkerPosY = ( float ) Math . Ceiling ( ( double ) SelectedHistogramBar . Rect . Top ) - 1 ;
e . Graphics . DrawLine ( Pens . Black , MarkerPosX + 0 , MarkerPosY , MarkerPosX + 5 , MarkerPosY ) ;
e . Graphics . DrawLine ( Pens . Black , MarkerPosX + 5 , MarkerPosY , MarkerPosX + 5 , MarkerPosY + MarkerHeight ) ;
e . Graphics . DrawLine ( Pens . Black , MarkerPosX + 0 , MarkerPosY + MarkerHeight + 1 , MarkerPosX + 5 , MarkerPosY + MarkerHeight + 1 ) ;
}
2016-09-21 10:07:18 -04:00
TextRenderer . DrawText (
e . Graphics ,
"Use key up or key down to change selected allocation" ,
OwnerWindow . AxisFont ,
new Point ( ( int ) ( BarLeft * 0.5f ) , ( int ) ( OwnerWindow . HistogramPanel . Height - ( GraphYBorder + OwnerWindow . AxisFont . Height ) * 0.5f ) ) ,
Color . Black
) ;
2014-03-14 14:13:41 -04:00
}
/*
// Draw "Total Memory Used" line.
if( OwnerWindow.CurrentSnapshot != null && OwnerWindow.CurrentSnapshot.MetricArray.Count > 0 )
{
float YScale = TotalGraphHeight / MemorySizes[ 0 ];
float GraphBottom = GraphYBorder + YScale * MemorySizes[ 0 ];
float TotalUsedLineY = GraphBottom - YScale * ( OwnerWindow.CurrentSnapshot.MemoryAllocationStats.TotalAllocated / 1024f / 1024f );
float OverheadLineY = TotalUsedLineY + YScale * ( OwnerWindow.CurrentSnapshot.MetricArray[ ( int )ESnapshotMetricV3.MemoryProfilingOverhead ] / 1024f / 1024f );
e.Graphics.DrawLine( BlackDashedPen, TickRight, TotalUsedLineY, BarLeft + BarWidth, TotalUsedLineY );
e.Graphics.DrawLine( RedDashedPen, TickRight, OverheadLineY, BarLeft + BarWidth, OverheadLineY );
}
*/
if ( SelectedHistogramBar ! = null & & SelectedHistogramBar . MemorySize > 0 )
{
float GraphLeft = MemorySizes . Length * ( BarLeft + BarWidth + GraphXGap ) ;
float MemorySizeMB = ( float ) ( ( double ) SelectedHistogramBar . MemorySize / ( 1024 * 1024 ) ) ;
float SelectedYScale = TotalGraphHeight / MemorySizeMB ;
string AxisLabel = "MB" ;
int AxisMemorySize = ( int ) MemorySizeMB ;
if ( AxisMemorySize < 32 )
{
// Drop down into kilobytes.
AxisMemorySize = ( int ) ( SelectedHistogramBar . MemorySize / 1024 ) ;
AxisLabel = "KB" ;
}
if ( AxisMemorySize < 32 )
{
// Drop down into bytes.
AxisMemorySize = ( int ) Math . Max ( SelectedHistogramBar . MemorySize , 32 ) ;
AxisLabel = "bytes" ;
}
// Select a major tick that's divisible by 4 so that the minor tick divides into it without remainder.
int SelectedMajorTick = ( AxisMemorySize / 8 ) / 4 * 4 ;
int SelectedMinorTick = SelectedMajorTick / 4 ;
2016-09-21 10:07:18 -04:00
OwnerWindow . DrawYAxis ( e . Graphics , Pens . Black , Color . Black , SelectedMajorTick , SelectedMinorTick , GraphLeft + TickRight , GraphYBorder , TotalGraphHeight , AxisLabel , AxisMemorySize ) ;
2014-03-14 14:13:41 -04:00
{
2016-09-21 10:07:18 -04:00
// Used to batch up drawing as individual calls are slow
var FillRects = new Dictionary < Color , List < RectangleF > > ( ) ;
var DrawRects = new List < RectangleF > ( ) ;
2014-03-14 14:13:41 -04:00
2016-09-21 10:07:18 -04:00
float BarY = GraphYBorder + SelectedYScale * MemorySizeMB ;
for ( int SelBarIndex = HistogramSelectionBars . Count - 1 ; SelBarIndex > = 0 ; SelBarIndex - - )
{
FHistogramBar Bar = HistogramSelectionBars [ SelBarIndex ] ;
2014-03-14 14:13:41 -04:00
2016-09-21 10:07:18 -04:00
float BarHeight = ( float ) ( ( double ) Bar . MemorySize / ( 1024 * 1024 ) * SelectedYScale ) ;
2014-03-14 14:13:41 -04:00
2016-09-21 10:07:18 -04:00
Bar . Rect . X = GraphLeft + BarLeft ;
Bar . Rect . Y = BarY - BarHeight ;
Bar . Rect . Width = BarWidth ;
Bar . Rect . Height = BarHeight ;
List < RectangleF > FillRectsList ;
if ( FillRects . TryGetValue ( Bar . Colour , out FillRectsList ) )
{
FillRectsList . Add ( new RectangleF ( Bar . Rect . X , Bar . Rect . Y , Bar . Rect . Width , Bar . Rect . Height ) ) ;
}
else
{
FillRectsList = new List < RectangleF > ( ) { new RectangleF ( Bar . Rect . X , Bar . Rect . Y , Bar . Rect . Width , Bar . Rect . Height ) } ;
FillRects . Add ( Bar . Colour , FillRectsList ) ;
}
DrawRects . Add ( new RectangleF ( Bar . Rect . X , Bar . Rect . Y , Bar . Rect . Width - 1 , Bar . Rect . Height ) ) ;
BarY - = BarHeight ;
}
// Draw batched
foreach ( var FillRectPair in FillRects )
{
e . Graphics . FillRectangles ( new SolidBrush ( FillRectPair . Key ) , FillRectPair . Value . ToArray ( ) ) ;
}
e . Graphics . DrawRectangles ( Pens . Black , DrawRects . ToArray ( ) ) ;
2014-03-14 14:13:41 -04:00
}
if ( SubselectedHistogramBar ! = null )
{
e . Graphics . FillRectangle
(
SelectedBarPen ( SubselectedHistogramBar ) . Brush ,
SubselectedHistogramBar . Rect . X ,
SubselectedHistogramBar . Rect . Y ,
SubselectedHistogramBar . Rect . Width ,
Math . Max ( SubselectedHistogramBar . Rect . Height , 1.0f )
) ;
float MarkerHeight = Math . Max ( SubselectedHistogramBar . Rect . Height , 1.0f ) ;
float MarkerPosX = SubselectedHistogramBar . Rect . Left + SubselectedHistogramBar . Rect . Width + 1.0f ;
float MarkerPosY = ( float ) Math . Ceiling ( ( double ) SubselectedHistogramBar . Rect . Top ) - 1 ;
e . Graphics . DrawLine ( Pens . Black , MarkerPosX + 0 , MarkerPosY , MarkerPosX + 5 , MarkerPosY ) ;
e . Graphics . DrawLine ( Pens . Black , MarkerPosX + 5 , MarkerPosY , MarkerPosX + 5 , MarkerPosY + MarkerHeight ) ;
e . Graphics . DrawLine ( Pens . Black , MarkerPosX + 0 , MarkerPosY + MarkerHeight + 1 , MarkerPosX + 5 , MarkerPosY + MarkerHeight + 1 ) ; ;
}
}
}
static public void UnsafeMouseClick ( object sender , MouseEventArgs e )
{
// Work out which bar, if any, the user clicked on.
for ( int MemoryBankIndex = 0 ; MemoryBankIndex < HistogramBars . Length ; MemoryBankIndex + + )
{
for ( int BarIndex = 0 ; BarIndex < HistogramBars [ MemoryBankIndex ] . Count ; BarIndex + + )
{
FHistogramBar Bar = FHistogramParser . HistogramBars [ MemoryBankIndex ] [ BarIndex ] ;
if ( Bar . Rect . Contains ( e . X , e . Y ) )
{
SelectedMemoryBankIndex = MemoryBankIndex ;
SelectedHistogramBarIndex [ SelectedMemoryBankIndex ] = BarIndex ;
SelectHistogramBar ( Bar ) ;
if ( e . Button = = MouseButtons . Right )
{
OwnerWindow . ViewHistoryContextMenu . Tag = Bar ;
OwnerWindow . ViewHistoryContextMenu . Show ( ( Control ) sender , e . Location ) ;
}
return ;
}
}
}
// Check selection bars (the graph that appears when you select a bar in another graph).
if ( SelectedHistogramBar ! = null )
{
for ( int DetailedBarIndex = 0 ; DetailedBarIndex < HistogramSelectionBars . Count ; DetailedBarIndex + + )
{
FHistogramBar HistogramBar = HistogramSelectionBars [ DetailedBarIndex ] ;
if ( HistogramBar . Rect . Contains ( e . X , e . Y ) )
{
SubselectHistogramBar ( HistogramBar ) ;
if ( e . Button = = MouseButtons . Right )
{
OwnerWindow . ViewHistoryContextMenu . Tag = HistogramBar ;
OwnerWindow . ViewHistoryContextMenu . Show ( ( Control ) sender , e . Location ) ;
}
return ;
}
}
}
2016-06-14 12:28:12 -04:00
SubselectHistogramBar ( null ) ;
}
2014-03-14 14:13:41 -04:00
static public bool ProcessKeys ( Keys KeyData )
{
switch ( KeyData )
{
case Keys . Up :
{
if ( SubselectedHistogramBar ! = null )
{
int Index = HistogramSelectionBars . IndexOf ( SubselectedHistogramBar ) ;
if ( Index > 0 )
{
SubselectHistogramBar ( HistogramSelectionBars [ Index - 1 ] ) ;
}
}
else if ( SelectedHistogramBar ! = null )
{
for ( int BarIndex = 0 ; BarIndex < HistogramBars . Length ; BarIndex + + )
{
List < FHistogramBar > HistogramBarArray = HistogramBars [ BarIndex ] ;
int Index = HistogramBarArray . IndexOf ( SelectedHistogramBar ) ;
if ( Index > 0 )
{
for ( ; Index > 0 ; Index - - )
{
if ( HistogramBarArray [ Index - 1 ] . AllocationCount > 0 )
{
SelectedHistogramBarIndex [ SelectedMemoryBankIndex ] = Index - 1 ;
SelectHistogramBar ( HistogramBarArray [ Index - 1 ] ) ;
break ;
}
}
break ;
}
}
}
return true ;
}
case Keys . Down :
{
if ( SubselectedHistogramBar ! = null )
{
int Index = HistogramSelectionBars . IndexOf ( SubselectedHistogramBar ) ;
if ( Index ! = - 1 & & Index < HistogramSelectionBars . Count - 1 )
{
SubselectHistogramBar ( HistogramSelectionBars [ Index + 1 ] ) ;
}
}
else if ( SelectedHistogramBar ! = null )
{
for ( int BarIndex = 0 ; BarIndex < HistogramBars . Length ; BarIndex + + )
{
List < FHistogramBar > HistogramBarArray = HistogramBars [ BarIndex ] ;
int Index = HistogramBarArray . IndexOf ( SelectedHistogramBar ) ;
if ( Index ! = - 1 & & Index < HistogramBarArray . Count - 1 )
{
for ( ; Index < HistogramBarArray . Count - 2 ; Index + + )
{
if ( HistogramBarArray [ Index + 1 ] . AllocationCount > 0 )
{
SelectedHistogramBarIndex [ SelectedMemoryBankIndex ] = Index + 1 ;
SelectHistogramBar ( HistogramBarArray [ Index + 1 ] ) ;
break ;
}
}
break ;
}
}
}
return true ;
}
}
// Not processed
return false ;
}
public static void SelectHistogramBar ( FHistogramBar Bar )
{
if ( Bar ! = SelectedHistogramBar )
{
// Cancel any subselection if the main selection is changing.
SubselectedHistogramBar = null ;
}
SelectedHistogramBar = Bar ;
if ( SelectedHistogramBar ! = null )
{
HistogramSelectionBars . Clear ( ) ;
foreach ( FCallStackAllocationInfo AllocationInfo in SelectedHistogramBar . CallStackList )
{
int Address = FStreamInfo . GlobalInstance . CallStackArray [ AllocationInfo . CallStackIndex ] . AddressIndices [ 0 ] ;
string FunctionName = FStreamInfo . GlobalInstance . NameArray [ FStreamInfo . GlobalInstance . CallStackAddressArray [ Address ] . FunctionIndex ] ;
FHistogramBar AllocBar = new FHistogramBar ( FunctionName , SelectedHistogramBar . Colour ) ;
AllocBar . AddAllocation ( AllocationInfo ) ;
HistogramSelectionBars . Add ( AllocBar ) ;
}
}
UpdateHistogramDetails ( ) ;
OwnerWindow . HistogramPanel . Invalidate ( ) ;
}
public static void SubselectHistogramBar ( FHistogramBar HistogramBar )
{
SubselectedHistogramBar = HistogramBar ;
UpdateHistogramDetails ( ) ;
OwnerWindow . HistogramPanel . Invalidate ( ) ;
}
private static void UpdateHistogramDetails ( )
{
OwnerWindow . HistogramViewCallStackListView . BeginUpdate ( ) ;
OwnerWindow . HistogramViewCallStackListView . Items . Clear ( ) ;
FHistogramBar Bar = null ;
if ( SubselectedHistogramBar ! = null )
{
Bar = SubselectedHistogramBar ;
}
else if ( SelectedHistogramBar ! = null )
{
Bar = SelectedHistogramBar ;
}
if ( Bar ! = null )
{
if ( Bar ! = SubselectedHistogramBar )
{
OwnerWindow . HistogramViewNameLabel . Text = Bar . Description ;
}
2016-09-21 10:07:18 -04:00
OwnerWindow . HistogramViewSizeLabel . Text = MainWindow . FormatSizeString2 ( Bar . MemorySize ) ;
OwnerWindow . HistogramViewAllocationsLabel . Text = Bar . AllocationCount . ToString ( "N0" ) ;
2014-03-14 14:13:41 -04:00
if ( Bar . CallStackList . Count = = 1 )
{
foreach ( int AddressIndex in FStreamInfo . GlobalInstance . CallStackArray [ Bar . CallStackList [ 0 ] . CallStackIndex ] . AddressIndices )
{
string FunctionName = FStreamInfo . GlobalInstance . NameArray [ FStreamInfo . GlobalInstance . CallStackAddressArray [ AddressIndex ] . FunctionIndex ] ;
OwnerWindow . HistogramViewCallStackListView . Items . Add ( FunctionName ) ;
}
}
}
OwnerWindow . HistogramViewCallStackListView . EndUpdate ( ) ;
}
}
/// <summary> Encapsulates histogram bar information. </summary>
public class FHistogramBar
{
/// <summary> List of callstack allocations, sorted by size. </summary>
public List < FCallStackAllocationInfo > CallStackList = new List < FCallStackAllocationInfo > ( ) ;
/// <summary> Memory allocated in this bar. </summary>
public long MemorySize ;
/// <summary> Number of allocation in this bar. </summary>
public int AllocationCount ;
/// <summary> The class group that this bar is associated with. </summary>
public ClassGroup CallStackGroup ;
/// <summary> Description of this bar, usually taken from the callstack group. </summary>
public string Description ;
/// <summary> The colour used to draw this bar, usually taken from the callstack group. </summary>
public Color Colour ;
/// <summary> Rectangle used to draw this bar. </summary>
public RectangleF Rect ;
2016-09-21 10:07:18 -04:00
/// <summary> > 0 if we are batch adding. See BeginBatchAddition and EndBatchAddition. </summary>
int BatchAddingCount = 0 ;
2014-03-14 14:13:41 -04:00
/// <summary> Default constructor. </summary>
public FHistogramBar ( ClassGroup InCallStackGroup )
{
CallStackGroup = InCallStackGroup ;
Description = InCallStackGroup . Name ;
Colour = InCallStackGroup . Color ;
}
/// <summary> Custom constructor. </summary>
public FHistogramBar ( string InDescription , Color InColour )
{
Description = InDescription ;
Colour = InColour ;
}
2016-09-21 10:07:18 -04:00
/// <summary> Begin the process of adding a batch of new entries to this bar. Calls to AddAllocation will defer the Sort until EndBatchAddition is called. </summary>
public void BeginBatchAddition ( )
{
+ + BatchAddingCount ;
}
/// <summary> End the process of adding a batch of new entries to this bar. Calls Sort to ensure new entries are in the correct order. </summary>
public void EndBatchAddition ( )
{
if ( - - BatchAddingCount < 0 )
{
BatchAddingCount = 0 ;
}
if ( BatchAddingCount = = 0 )
{
// Sorting largest -> smallest
2016-10-05 13:23:01 -04:00
CallStackList . Sort ( ( First , Second ) = > Math . Sign ( First . TotalSize - Second . TotalSize ) ) ;
2016-09-21 10:07:18 -04:00
}
}
2014-03-14 14:13:41 -04:00
/// <summary> Inserts the new allocation so that the list stays in size order. </summary>
public void AddAllocation ( FCallStackAllocationInfo AllocationInfo )
{
bool bInserted = false ;
2016-09-21 10:07:18 -04:00
if ( BatchAddingCount = = 0 )
2014-03-14 14:13:41 -04:00
{
2016-09-21 10:07:18 -04:00
for ( int Index = 0 ; Index < CallStackList . Count ; Index + + )
2014-03-14 14:13:41 -04:00
{
2016-10-05 13:23:01 -04:00
if ( CallStackList [ Index ] . TotalSize > AllocationInfo . TotalSize )
2016-09-21 10:07:18 -04:00
{
CallStackList . Insert ( Index , AllocationInfo ) ;
bInserted = true ;
break ;
}
2014-03-14 14:13:41 -04:00
}
}
2016-09-21 10:07:18 -04:00
if ( ! bInserted )
2014-03-14 14:13:41 -04:00
{
2016-09-21 10:07:18 -04:00
CallStackList . Add ( AllocationInfo ) ;
2014-03-14 14:13:41 -04:00
}
2016-10-05 13:23:01 -04:00
MemorySize + = AllocationInfo . TotalSize ;
AllocationCount + = AllocationInfo . TotalCount ;
2014-03-14 14:13:41 -04:00
}
}
}