diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..940794e --- /dev/null +++ b/.gitignore @@ -0,0 +1,288 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs diff --git a/CLRProfiler/CLRProfiler.doc b/CLRProfiler/CLRProfiler.doc new file mode 100644 index 0000000..346f9d7 Binary files /dev/null and b/CLRProfiler/CLRProfiler.doc differ diff --git a/CLRProfiler/CLRProfiler.sln b/CLRProfiler/CLRProfiler.sln new file mode 100644 index 0000000..1a386b7 --- /dev/null +++ b/CLRProfiler/CLRProfiler.sln @@ -0,0 +1,111 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D4078DF1-17FF-40E2-A442-5AA32E546FA5}" + ProjectSection(SolutionItems) = preProject + Readme.txt = Readme.txt + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLRProfiler", "CLRProfiler\CLRProfiler.csproj", "{147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProfilerOBJ", "ProfilerOBJ\ProfilerOBJ.vcxproj", "{A1BCB61E-359F-46C8-89E1-26CD7E356880}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLRProfilerControl", "CLRProfilerControl\CLRProfilerControl.csproj", "{4A080E01-C48D-4E95-B8E7-280C32BCDAA9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CLRProfilerWindowsStoreAppThreadResumer", "WindowsStoreAppThreadResumer\WindowsStoreAppThreadResumer.vcxproj", "{D145434B-8568-488B-9EFE-677C01BF2997}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLRProfilerWindowsStoreAppHelper", "WindowsStoreAppHelper\CLRProfilerWindowsStoreAppHelper.csproj", "{4D1CF008-15DF-408B-96C3-19531D09E96E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Debug|Any CPU.ActiveCfg = Debug|x86 + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Debug|Win32.ActiveCfg = Debug|x86 + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Debug|Win32.Build.0 = Debug|x86 + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Debug|x64.ActiveCfg = Debug|x64 + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Debug|x64.Build.0 = Debug|x64 + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Debug|x86.ActiveCfg = Debug|x86 + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Debug|x86.Build.0 = Debug|x86 + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Release|Any CPU.ActiveCfg = Release|x86 + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Release|Win32.ActiveCfg = Release|x86 + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Release|Win32.Build.0 = Release|x86 + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Release|x64.ActiveCfg = Release|x64 + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Release|x64.Build.0 = Release|x64 + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Release|x86.ActiveCfg = Release|x86 + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Release|x86.Build.0 = Release|x86 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Debug|Win32.ActiveCfg = Debug|Win32 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Debug|Win32.Build.0 = Debug|Win32 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Debug|x64.ActiveCfg = Debug|x64 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Debug|x64.Build.0 = Debug|x64 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Debug|x86.ActiveCfg = Debug|Win32 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Debug|x86.Build.0 = Debug|Win32 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Release|Any CPU.ActiveCfg = Release|Win32 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Release|Win32.ActiveCfg = Release|Win32 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Release|Win32.Build.0 = Release|Win32 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Release|x64.ActiveCfg = Release|x64 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Release|x64.Build.0 = Release|x64 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Release|x86.ActiveCfg = Release|Win32 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Release|x86.Build.0 = Release|Win32 + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Debug|Win32.ActiveCfg = Debug|x86 + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Debug|Win32.Build.0 = Debug|x86 + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Debug|x64.ActiveCfg = Debug|x64 + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Debug|x64.Build.0 = Debug|x64 + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Debug|x86.ActiveCfg = Debug|x86 + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Debug|x86.Build.0 = Debug|x86 + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Release|Any CPU.Build.0 = Release|Any CPU + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Release|Win32.ActiveCfg = Release|x86 + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Release|Win32.Build.0 = Release|x86 + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Release|x64.ActiveCfg = Release|x64 + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Release|x64.Build.0 = Release|x64 + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Release|x86.ActiveCfg = Release|x86 + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Release|x86.Build.0 = Release|x86 + {D145434B-8568-488B-9EFE-677C01BF2997}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {D145434B-8568-488B-9EFE-677C01BF2997}.Debug|Win32.ActiveCfg = Debug|Win32 + {D145434B-8568-488B-9EFE-677C01BF2997}.Debug|Win32.Build.0 = Debug|Win32 + {D145434B-8568-488B-9EFE-677C01BF2997}.Debug|x64.ActiveCfg = Debug|x64 + {D145434B-8568-488B-9EFE-677C01BF2997}.Debug|x64.Build.0 = Debug|x64 + {D145434B-8568-488B-9EFE-677C01BF2997}.Debug|x86.ActiveCfg = Debug|Win32 + {D145434B-8568-488B-9EFE-677C01BF2997}.Debug|x86.Build.0 = Debug|Win32 + {D145434B-8568-488B-9EFE-677C01BF2997}.Release|Any CPU.ActiveCfg = Release|Win32 + {D145434B-8568-488B-9EFE-677C01BF2997}.Release|Win32.ActiveCfg = Release|Win32 + {D145434B-8568-488B-9EFE-677C01BF2997}.Release|Win32.Build.0 = Release|Win32 + {D145434B-8568-488B-9EFE-677C01BF2997}.Release|x64.ActiveCfg = Release|x64 + {D145434B-8568-488B-9EFE-677C01BF2997}.Release|x64.Build.0 = Release|x64 + {D145434B-8568-488B-9EFE-677C01BF2997}.Release|x86.ActiveCfg = Release|Win32 + {D145434B-8568-488B-9EFE-677C01BF2997}.Release|x86.Build.0 = Release|Win32 + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Debug|Win32.ActiveCfg = Debug|x86 + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Debug|Win32.Build.0 = Debug|x86 + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Debug|x64.ActiveCfg = Debug|x64 + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Debug|x64.Build.0 = Debug|x64 + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Debug|x86.ActiveCfg = Debug|x86 + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Debug|x86.Build.0 = Debug|x86 + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Release|Any CPU.Build.0 = Release|Any CPU + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Release|Win32.ActiveCfg = Release|x86 + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Release|Win32.Build.0 = Release|x86 + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Release|x64.ActiveCfg = Release|x64 + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Release|x64.Build.0 = Release|x64 + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Release|x86.ActiveCfg = Release|x86 + {4D1CF008-15DF-408B-96C3-19531D09E96E}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection +EndGlobal diff --git a/CLRProfiler/CLRProfiler/AgeHistogram.cs b/CLRProfiler/CLRProfiler/AgeHistogram.cs new file mode 100644 index 0000000..d0f2541 --- /dev/null +++ b/CLRProfiler/CLRProfiler/AgeHistogram.cs @@ -0,0 +1,1314 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +using System; +using System.Drawing; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Windows.Forms; +using System.Diagnostics; +using System.Globalization; +using System.IO; + +namespace CLRProfiler +{ + /// + /// Summary description for AgeHistogram. + /// + public class AgeHistogram : System.Windows.Forms.Form + { + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Splitter splitter1; + private System.Windows.Forms.GroupBox verticalScaleGroupBox; + private System.Windows.Forms.Panel graphOuterPanel; + private System.Windows.Forms.Panel typeLegendOuterPanel; + private System.Windows.Forms.Panel graphPanel; + private System.Windows.Forms.Panel typeLegendPanel; + private System.Windows.Forms.RadioButton radioButton1; + private System.Windows.Forms.RadioButton radioButton2; + private System.Windows.Forms.RadioButton radioButton3; + private System.Windows.Forms.RadioButton radioButton4; + private System.Windows.Forms.RadioButton radioButton5; + private System.Windows.Forms.RadioButton radioButton6; + private System.Windows.Forms.RadioButton radioButton7; + private System.Windows.Forms.RadioButton radioButton8; + private System.Windows.Forms.RadioButton radioButton9; + private System.Windows.Forms.RadioButton radioButton10; + private System.Windows.Forms.RadioButton radioButton11; + private System.Windows.Forms.RadioButton radioButton12; + private System.Windows.Forms.RadioButton radioButton13; + private System.Windows.Forms.RadioButton radioButton14; + private System.Windows.Forms.RadioButton radioButton15; + private System.Windows.Forms.RadioButton radioButton16; + private System.Windows.Forms.RadioButton radioButton17; + private System.Windows.Forms.RadioButton radioButton18; + private System.Windows.Forms.RadioButton radioButton19; + private System.Windows.Forms.RadioButton radioButton20; + private System.Windows.Forms.GroupBox timeScaleGroupBox; + private System.ComponentModel.IContainer components; + private System.Windows.Forms.ContextMenu contextMenu; + private System.Windows.Forms.MenuItem showWhoAllocatedMenuItem; + private System.Windows.Forms.MenuItem exportDataMenuItem; + private System.Windows.Forms.SaveFileDialog exportSaveFileDialog; + private System.Windows.Forms.Timer versionTimer; + private System.Windows.Forms.RadioButton markersRadioButton; + private Font font; + private bool useMarkers; + + internal AgeHistogram() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + toolTip = new ToolTip(); + toolTip.Active = false; + toolTip.ShowAlways = true; + toolTip.AutomaticDelay = 70; + toolTip.ReshowDelay = 1; + + autoUpdate = true; + + MainForm form1 = MainForm.instance; + if (form1.lastLogResult != null) + liveObjectTable = form1.lastLogResult.liveObjectTable; + + font = form1.font; + markersRadioButton.Enabled = form1.log.commentEventList.count > 0; + } + + internal AgeHistogram(LiveObjectTable liveObjectTable, string title) : this() + { + this.liveObjectTable = liveObjectTable; + this.Text = title; + autoUpdate = false; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + versionTimer.Stop(); + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.panel1 = new System.Windows.Forms.Panel(); + this.timeScaleGroupBox = new System.Windows.Forms.GroupBox(); + this.markersRadioButton = new System.Windows.Forms.RadioButton(); + this.radioButton20 = new System.Windows.Forms.RadioButton(); + this.radioButton19 = new System.Windows.Forms.RadioButton(); + this.radioButton18 = new System.Windows.Forms.RadioButton(); + this.radioButton17 = new System.Windows.Forms.RadioButton(); + this.radioButton16 = new System.Windows.Forms.RadioButton(); + this.radioButton15 = new System.Windows.Forms.RadioButton(); + this.radioButton14 = new System.Windows.Forms.RadioButton(); + this.radioButton13 = new System.Windows.Forms.RadioButton(); + this.radioButton12 = new System.Windows.Forms.RadioButton(); + this.radioButton11 = new System.Windows.Forms.RadioButton(); + this.verticalScaleGroupBox = new System.Windows.Forms.GroupBox(); + this.radioButton10 = new System.Windows.Forms.RadioButton(); + this.radioButton9 = new System.Windows.Forms.RadioButton(); + this.radioButton8 = new System.Windows.Forms.RadioButton(); + this.radioButton7 = new System.Windows.Forms.RadioButton(); + this.radioButton6 = new System.Windows.Forms.RadioButton(); + this.radioButton5 = new System.Windows.Forms.RadioButton(); + this.radioButton4 = new System.Windows.Forms.RadioButton(); + this.radioButton3 = new System.Windows.Forms.RadioButton(); + this.radioButton2 = new System.Windows.Forms.RadioButton(); + this.radioButton1 = new System.Windows.Forms.RadioButton(); + this.graphOuterPanel = new System.Windows.Forms.Panel(); + this.graphPanel = new System.Windows.Forms.Panel(); + this.splitter1 = new System.Windows.Forms.Splitter(); + this.typeLegendOuterPanel = new System.Windows.Forms.Panel(); + this.typeLegendPanel = new System.Windows.Forms.Panel(); + this.contextMenu = new System.Windows.Forms.ContextMenu(); + this.showWhoAllocatedMenuItem = new System.Windows.Forms.MenuItem(); + this.exportDataMenuItem = new System.Windows.Forms.MenuItem(); + this.exportSaveFileDialog = new System.Windows.Forms.SaveFileDialog(); + this.versionTimer = new System.Windows.Forms.Timer(this.components); + this.panel1.SuspendLayout(); + this.timeScaleGroupBox.SuspendLayout(); + this.verticalScaleGroupBox.SuspendLayout(); + this.graphOuterPanel.SuspendLayout(); + this.typeLegendOuterPanel.SuspendLayout(); + this.SuspendLayout(); + // + // panel1 + // + this.panel1.Controls.Add(this.timeScaleGroupBox); + this.panel1.Controls.Add(this.verticalScaleGroupBox); + this.panel1.Dock = System.Windows.Forms.DockStyle.Top; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(1308, 100); + this.panel1.TabIndex = 0; + // + // timeScaleGroupBox + // + this.timeScaleGroupBox.Controls.Add(this.markersRadioButton); + this.timeScaleGroupBox.Controls.Add(this.radioButton20); + this.timeScaleGroupBox.Controls.Add(this.radioButton19); + this.timeScaleGroupBox.Controls.Add(this.radioButton18); + this.timeScaleGroupBox.Controls.Add(this.radioButton17); + this.timeScaleGroupBox.Controls.Add(this.radioButton16); + this.timeScaleGroupBox.Controls.Add(this.radioButton15); + this.timeScaleGroupBox.Controls.Add(this.radioButton14); + this.timeScaleGroupBox.Controls.Add(this.radioButton13); + this.timeScaleGroupBox.Controls.Add(this.radioButton12); + this.timeScaleGroupBox.Controls.Add(this.radioButton11); + this.timeScaleGroupBox.Location = new System.Drawing.Point(512, 16); + this.timeScaleGroupBox.Name = "timeScaleGroupBox"; + this.timeScaleGroupBox.Size = new System.Drawing.Size(512, 64); + this.timeScaleGroupBox.TabIndex = 1; + this.timeScaleGroupBox.TabStop = false; + this.timeScaleGroupBox.Text = "Time Scale: Seconds/Bar"; + // + // markersRadioButton + // + this.markersRadioButton.Enabled = false; + this.markersRadioButton.Location = new System.Drawing.Point(432, 17); + this.markersRadioButton.Name = "markersRadioButton"; + this.markersRadioButton.Size = new System.Drawing.Size(75, 38); + this.markersRadioButton.TabIndex = 11; + this.markersRadioButton.Text = "Use Markers"; + this.markersRadioButton.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton20 + // + this.radioButton20.Location = new System.Drawing.Point(16, 24); + this.radioButton20.Name = "radioButton20"; + this.radioButton20.Size = new System.Drawing.Size(48, 24); + this.radioButton20.TabIndex = 10; + this.radioButton20.Text = "0.05"; + this.radioButton20.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton19 + // + this.radioButton19.Location = new System.Drawing.Point(387, 24); + this.radioButton19.Name = "radioButton19"; + this.radioButton19.Size = new System.Drawing.Size(40, 24); + this.radioButton19.TabIndex = 9; + this.radioButton19.Text = "50"; + this.radioButton19.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton18 + // + this.radioButton18.Location = new System.Drawing.Point(350, 24); + this.radioButton18.Name = "radioButton18"; + this.radioButton18.Size = new System.Drawing.Size(40, 24); + this.radioButton18.TabIndex = 8; + this.radioButton18.Text = "20"; + this.radioButton18.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton17 + // + this.radioButton17.Location = new System.Drawing.Point(310, 24); + this.radioButton17.Name = "radioButton17"; + this.radioButton17.Size = new System.Drawing.Size(40, 24); + this.radioButton17.TabIndex = 7; + this.radioButton17.Text = "10"; + this.radioButton17.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton16 + // + this.radioButton16.Location = new System.Drawing.Point(105, 24); + this.radioButton16.Name = "radioButton16"; + this.radioButton16.Size = new System.Drawing.Size(42, 24); + this.radioButton16.TabIndex = 6; + this.radioButton16.Text = "0.2"; + this.radioButton16.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton15 + // + this.radioButton15.Location = new System.Drawing.Point(152, 24); + this.radioButton15.Name = "radioButton15"; + this.radioButton15.Size = new System.Drawing.Size(42, 24); + this.radioButton15.TabIndex = 5; + this.radioButton15.Text = "0.5"; + this.radioButton15.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton14 + // + this.radioButton14.Location = new System.Drawing.Point(273, 24); + this.radioButton14.Name = "radioButton14"; + this.radioButton14.Size = new System.Drawing.Size(32, 24); + this.radioButton14.TabIndex = 4; + this.radioButton14.Text = "5"; + this.radioButton14.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton13 + // + this.radioButton13.Location = new System.Drawing.Point(236, 24); + this.radioButton13.Name = "radioButton13"; + this.radioButton13.Size = new System.Drawing.Size(32, 24); + this.radioButton13.TabIndex = 3; + this.radioButton13.Text = "2"; + this.radioButton13.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton12 + // + this.radioButton12.Location = new System.Drawing.Point(199, 24); + this.radioButton12.Name = "radioButton12"; + this.radioButton12.Size = new System.Drawing.Size(32, 24); + this.radioButton12.TabIndex = 2; + this.radioButton12.Text = "1"; + this.radioButton12.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton11 + // + this.radioButton11.Location = new System.Drawing.Point(64, 24); + this.radioButton11.Name = "radioButton11"; + this.radioButton11.Size = new System.Drawing.Size(46, 24); + this.radioButton11.TabIndex = 1; + this.radioButton11.Text = "0.1"; + this.radioButton11.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // verticalScaleGroupBox + // + this.verticalScaleGroupBox.Controls.Add(this.radioButton10); + this.verticalScaleGroupBox.Controls.Add(this.radioButton9); + this.verticalScaleGroupBox.Controls.Add(this.radioButton8); + this.verticalScaleGroupBox.Controls.Add(this.radioButton7); + this.verticalScaleGroupBox.Controls.Add(this.radioButton6); + this.verticalScaleGroupBox.Controls.Add(this.radioButton5); + this.verticalScaleGroupBox.Controls.Add(this.radioButton4); + this.verticalScaleGroupBox.Controls.Add(this.radioButton3); + this.verticalScaleGroupBox.Controls.Add(this.radioButton2); + this.verticalScaleGroupBox.Controls.Add(this.radioButton1); + this.verticalScaleGroupBox.Location = new System.Drawing.Point(24, 16); + this.verticalScaleGroupBox.Name = "verticalScaleGroupBox"; + this.verticalScaleGroupBox.Size = new System.Drawing.Size(464, 64); + this.verticalScaleGroupBox.TabIndex = 0; + this.verticalScaleGroupBox.TabStop = false; + this.verticalScaleGroupBox.Text = "Vertical Scale: KB/Pixel"; + // + // radioButton10 + // + this.radioButton10.Location = new System.Drawing.Point(400, 24); + this.radioButton10.Name = "radioButton10"; + this.radioButton10.Size = new System.Drawing.Size(56, 24); + this.radioButton10.TabIndex = 9; + this.radioButton10.Text = "1000"; + this.radioButton10.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton9 + // + this.radioButton9.Location = new System.Drawing.Point(352, 24); + this.radioButton9.Name = "radioButton9"; + this.radioButton9.Size = new System.Drawing.Size(48, 24); + this.radioButton9.TabIndex = 8; + this.radioButton9.Text = "500"; + this.radioButton9.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton8 + // + this.radioButton8.Location = new System.Drawing.Point(304, 24); + this.radioButton8.Name = "radioButton8"; + this.radioButton8.Size = new System.Drawing.Size(48, 24); + this.radioButton8.TabIndex = 7; + this.radioButton8.Text = "200"; + this.radioButton8.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton7 + // + this.radioButton7.Location = new System.Drawing.Point(216, 24); + this.radioButton7.Name = "radioButton7"; + this.radioButton7.Size = new System.Drawing.Size(40, 24); + this.radioButton7.TabIndex = 6; + this.radioButton7.Text = "50"; + this.radioButton7.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton6 + // + this.radioButton6.Location = new System.Drawing.Point(136, 24); + this.radioButton6.Name = "radioButton6"; + this.radioButton6.Size = new System.Drawing.Size(40, 24); + this.radioButton6.TabIndex = 5; + this.radioButton6.Text = "10"; + this.radioButton6.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton5 + // + this.radioButton5.Location = new System.Drawing.Point(256, 24); + this.radioButton5.Name = "radioButton5"; + this.radioButton5.Size = new System.Drawing.Size(48, 24); + this.radioButton5.TabIndex = 4; + this.radioButton5.Text = "100"; + this.radioButton5.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton4 + // + this.radioButton4.Location = new System.Drawing.Point(176, 24); + this.radioButton4.Name = "radioButton4"; + this.radioButton4.Size = new System.Drawing.Size(40, 24); + this.radioButton4.TabIndex = 3; + this.radioButton4.Text = "20"; + this.radioButton4.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton3 + // + this.radioButton3.Location = new System.Drawing.Point(96, 24); + this.radioButton3.Name = "radioButton3"; + this.radioButton3.Size = new System.Drawing.Size(32, 24); + this.radioButton3.TabIndex = 2; + this.radioButton3.Text = "5"; + this.radioButton3.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton2 + // + this.radioButton2.Location = new System.Drawing.Point(56, 24); + this.radioButton2.Name = "radioButton2"; + this.radioButton2.Size = new System.Drawing.Size(32, 24); + this.radioButton2.TabIndex = 1; + this.radioButton2.Text = "2"; + this.radioButton2.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // radioButton1 + // + this.radioButton1.Location = new System.Drawing.Point(16, 24); + this.radioButton1.Name = "radioButton1"; + this.radioButton1.Size = new System.Drawing.Size(32, 24); + this.radioButton1.TabIndex = 0; + this.radioButton1.Text = "1"; + this.radioButton1.CheckedChanged += new System.EventHandler(this.CheckedChanged); + // + // graphOuterPanel + // + this.graphOuterPanel.AutoScroll = true; + this.graphOuterPanel.BackColor = System.Drawing.SystemColors.Control; + this.graphOuterPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.graphOuterPanel.Controls.Add(this.graphPanel); + this.graphOuterPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.graphOuterPanel.Location = new System.Drawing.Point(0, 100); + this.graphOuterPanel.Name = "graphOuterPanel"; + this.graphOuterPanel.Size = new System.Drawing.Size(912, 561); + this.graphOuterPanel.TabIndex = 1; + // + // graphPanel + // + this.graphPanel.BackColor = System.Drawing.SystemColors.Control; + this.graphPanel.Location = new System.Drawing.Point(0, 0); + this.graphPanel.Name = "graphPanel"; + this.graphPanel.Size = new System.Drawing.Size(488, 528); + this.graphPanel.TabIndex = 0; + this.graphPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.graphPanel_MouseDown); + this.graphPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.graphPanel_MouseMove); + this.graphPanel.Paint += new System.Windows.Forms.PaintEventHandler(this.graphPanel_Paint); + // + // splitter1 + // + this.splitter1.Dock = System.Windows.Forms.DockStyle.Right; + this.splitter1.Location = new System.Drawing.Point(912, 100); + this.splitter1.Name = "splitter1"; + this.splitter1.Size = new System.Drawing.Size(4, 561); + this.splitter1.TabIndex = 2; + this.splitter1.TabStop = false; + // + // typeLegendOuterPanel + // + this.typeLegendOuterPanel.AutoScroll = true; + this.typeLegendOuterPanel.BackColor = System.Drawing.SystemColors.Control; + this.typeLegendOuterPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.typeLegendOuterPanel.Controls.Add(this.typeLegendPanel); + this.typeLegendOuterPanel.Dock = System.Windows.Forms.DockStyle.Right; + this.typeLegendOuterPanel.Location = new System.Drawing.Point(916, 100); + this.typeLegendOuterPanel.Name = "typeLegendOuterPanel"; + this.typeLegendOuterPanel.Size = new System.Drawing.Size(392, 561); + this.typeLegendOuterPanel.TabIndex = 3; + // + // typeLegendPanel + // + this.typeLegendPanel.BackColor = System.Drawing.SystemColors.Control; + this.typeLegendPanel.Location = new System.Drawing.Point(0, 0); + this.typeLegendPanel.Name = "typeLegendPanel"; + this.typeLegendPanel.Size = new System.Drawing.Size(384, 504); + this.typeLegendPanel.TabIndex = 0; + this.typeLegendPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.typeLegendPanel_MouseDown); + this.typeLegendPanel.Paint += new System.Windows.Forms.PaintEventHandler(this.typeLegendPanel_Paint); + // + // contextMenu + // + this.contextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.showWhoAllocatedMenuItem, + this.exportDataMenuItem}); + // + // showWhoAllocatedMenuItem + // + this.showWhoAllocatedMenuItem.Index = 0; + this.showWhoAllocatedMenuItem.Text = "Show Who Allocated"; + this.showWhoAllocatedMenuItem.Click += new System.EventHandler(this.showWhoAllocatedMenuItem_Click); + // + // exportDataMenuItem + // + this.exportDataMenuItem.Index = 1; + this.exportDataMenuItem.Text = "Export Data to File..."; + this.exportDataMenuItem.Click += new System.EventHandler(this.exportMenuItem_Click); + // + // exportSaveFileDialog + // + this.exportSaveFileDialog.FileName = "doc1"; + // + // versionTimer + // + this.versionTimer.Enabled = true; + this.versionTimer.Tick += new System.EventHandler(this.versionTimer_Tick); + // + // AgeHistogram + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(1308, 661); + this.Controls.Add(this.graphOuterPanel); + this.Controls.Add(this.splitter1); + this.Controls.Add(this.typeLegendOuterPanel); + this.Controls.Add(this.panel1); + this.Name = "AgeHistogram"; + this.Text = "Histogram by Age for Live Objects"; + this.panel1.ResumeLayout(false); + this.timeScaleGroupBox.ResumeLayout(false); + this.verticalScaleGroupBox.ResumeLayout(false); + this.graphOuterPanel.ResumeLayout(false); + this.typeLegendOuterPanel.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + + LiveObjectTable liveObjectTable; + + const int leftMargin = 30; + int bottomMargin = 50; + const int gap = 20; + int bucketWidth = 50; + const int topMargin = 30; + const int rightMargin = 30; + const int minHeight = 400; + + double timeScale = 1; + uint verticalScale = 0; + + private double GetTimeScale(double suggestedScale, bool firstTime) + { + if (!firstTime) + { + // If a radio button is already checked, return its scale + foreach (RadioButton rb in timeScaleGroupBox.Controls) + { + if (rb.Checked) + { + if (rb == markersRadioButton) + return 0.001; + else + return Convert.ToDouble(rb.Text, CultureInfo.InvariantCulture); + } + } + } + // Otherwise, try to find the lowest scale that is still at least as + // large as the suggested scale. If there's no such thing, return the highest scale. + double bestHigherScale = double.PositiveInfinity; + RadioButton bestHigherRadioButton = null; + double bestLowerScale = 0.0; + RadioButton bestLowerRadioButton = null; + + foreach (RadioButton rb in timeScaleGroupBox.Controls) + { + if (rb == markersRadioButton) + continue; + double scale = Convert.ToDouble(rb.Text, CultureInfo.InvariantCulture); + if (scale >= suggestedScale) + { + if (scale < bestHigherScale) + { + bestHigherScale = scale; + bestHigherRadioButton = rb; + } + } + else + { + if (scale > bestLowerScale) + { + bestLowerScale = scale; + bestLowerRadioButton = rb; + } + } + } + + if (bestHigherRadioButton != null) + { + bestHigherRadioButton.Checked = true; + return bestHigherScale; + } + else + { + Debug.Assert(bestLowerRadioButton != null); + bestLowerRadioButton.Checked = true; + return bestLowerScale; + } + } + + class TypeDesc : IComparable + { + internal string typeName; + internal ulong totalSize; + internal Color color; + internal Brush brush; + internal Pen pen; + internal bool selected; + internal Rectangle rect; + + internal TypeDesc(string typeName) + { + this.typeName = typeName; + } + + public int CompareTo(Object o) + { + TypeDesc t = (TypeDesc)o; + if (t.totalSize < this.totalSize) + return -1; + else if (t.totalSize > this.totalSize) + return 1; + else + return 0; + } + } + + TypeDesc[] typeIndexToTypeDesc; + + struct Bucket + { + internal ulong totalSize; + internal Dictionary typeDescToSizeCount; + internal bool selected; + internal double minAge; + internal double maxAge; + internal string minComment; + internal string maxComment; + } + + private class SizeCount + { + internal ulong size; + internal uint count; + } + + private Bucket[] bucketTable; + + private ArrayList sortedTypeTable; + ulong totalSize; + + private ulong BuildBuckets(double timeScale, double maxAge) + { + ReadNewLog log = liveObjectTable.readNewLog; + bool useMarkers = markersRadioButton.Checked; + if (this.useMarkers != useMarkers) + { + this.useMarkers = useMarkers; + bucketTable = null; + } + int bucketCount; + if (useMarkers) + { + for (bucketCount = 0; bucketCount < log.commentEventList.count; bucketCount++) + if (log.commentEventList.eventTickIndex[bucketCount] >= liveObjectTable.lastTickIndex) + break; + bucketCount++; + } + else + { + bucketCount = (int)Math.Ceiling(maxAge/timeScale); + if (bucketCount == 0) + bucketCount = 1; + } + if (bucketTable == null || bucketTable.Length != bucketCount) + { + bucketTable = new Bucket[bucketCount]; + double nowTime = log.TickIndexToTime(liveObjectTable.lastTickIndex); + double age = 0; + for (int i = 0; i < bucketTable.Length; i++) + { + bucketTable[i].totalSize = 0; + bucketTable[i].typeDescToSizeCount = new Dictionary(); + bucketTable[i].minAge = age; + if (useMarkers) + { + int markerIndex = bucketTable.Length - i - 1; + if (i > 0) + bucketTable[i].minComment = log.commentEventList.eventString[markerIndex]; + age = nowTime; + if (markerIndex > 0) + { + bucketTable[i].maxComment = log.commentEventList.eventString[markerIndex-1]; + age -= log.TickIndexToTime(log.commentEventList.eventTickIndex[markerIndex-1]); + } + } + else + { + age += timeScale; + } + bucketTable[i].maxAge = age; + } + + if (typeIndexToTypeDesc == null || typeIndexToTypeDesc.Length < log.typeName.Length) + typeIndexToTypeDesc = new TypeDesc[log.typeName.Length]; + else + { + foreach (TypeDesc t in typeIndexToTypeDesc) + { + if (t != null) + t.totalSize = 0; + } + } + LiveObjectTable.LiveObject o; + for (liveObjectTable.GetNextObject(0, ulong.MaxValue, out o); o.id < ulong.MaxValue; liveObjectTable.GetNextObject(o.id + o.size, ulong.MaxValue, out o)) + { + int bucketIndex; + double allocTime = log.TickIndexToTime(o.allocTickIndex); + age = nowTime - allocTime; + bucketIndex = (int)(age/timeScale); + if (bucketIndex >= bucketTable.Length) + bucketIndex = bucketTable.Length - 1; + else if (bucketIndex < 0) + bucketIndex = 0; + while (bucketIndex < bucketTable.Length - 1 && age >= bucketTable[bucketIndex].maxAge) + bucketIndex++; + while (bucketIndex > 0 && age < bucketTable[bucketIndex].minAge) + bucketIndex--; + bucketTable[bucketIndex].totalSize += o.size; + TypeDesc t = typeIndexToTypeDesc[o.typeIndex]; + if (t == null) + { + t = new TypeDesc(log.typeName[o.typeIndex]); + typeIndexToTypeDesc[o.typeIndex] = t; + } + t.totalSize += o.size; + SizeCount sizeCount; + if (!bucketTable[bucketIndex].typeDescToSizeCount.TryGetValue(t, out sizeCount)) + { + sizeCount = new SizeCount(); + bucketTable[bucketIndex].typeDescToSizeCount[t] = sizeCount; + } + sizeCount.size += o.size; + sizeCount.count += 1; + } + } + + ulong maxBucketSize = 0; + foreach (Bucket b in bucketTable) + { + if (maxBucketSize < b.totalSize) + maxBucketSize = b.totalSize; + } + + totalSize = 0; + sortedTypeTable = new ArrayList(); + foreach (TypeDesc t in typeIndexToTypeDesc) + { + if (t != null) + { + sortedTypeTable.Add(t); + totalSize += t.totalSize; + } + } + + sortedTypeTable.Sort(); + + return maxBucketSize; + } + + uint GetScale(GroupBox groupBox, int pixelsAvailable, ulong rangeNeeded, bool firstTime) + { + if (!firstTime) + { + foreach (RadioButton rb in groupBox.Controls) + { + if (rb.Checked) + return UInt32.Parse(rb.Text); + } + } + // No radio button was checked - let's come up with a suitable default + RadioButton maxLowScaleRB = null; + uint maxLowRange = 0; + RadioButton minHighScaleRB = null; + uint minHighRange = Int32.MaxValue; + foreach (RadioButton rb in groupBox.Controls) + { + uint range = (uint)(pixelsAvailable*Int32.Parse(rb.Text)); + if (range < rangeNeeded) + { + if (maxLowRange < range) + { + maxLowRange = range; + maxLowScaleRB = rb; + } + } + else + { + if (minHighRange > range) + { + minHighRange = range; + minHighScaleRB = rb; + } + } + } + if (minHighScaleRB != null) + { + minHighScaleRB.Checked = true; + return UInt32.Parse(minHighScaleRB.Text); + } + else + { + maxLowScaleRB.Checked = true; + return UInt32.Parse(maxLowScaleRB.Text); + } + } + + uint GetVerticalScale(int pixelsAvailable, ulong rangeNeeded, bool firstTime) + { + return GetScale(verticalScaleGroupBox, pixelsAvailable, rangeNeeded, firstTime); + } + + static Color[] firstColors = + { + Color.Red, + Color.Yellow, + Color.Green, + Color.Cyan, + Color.Blue, + Color.Magenta, + }; + + static Color[] colors = new Color[16]; + + Color MixColor(Color a, Color b) + { + int R = (a.R + b.R)/2; + int G = (a.G + b.G)/2; + int B = (a.B + b.B)/2; + + return Color.FromArgb(R, G, B); + } + + static void GrowColors() + { + Color[] newColors = new Color[2*colors.Length]; + for (int i = 0; i < colors.Length; i++) + newColors[i] = colors[i]; + colors = newColors; + } + + private TypeDesc FindSelectedType() + { + foreach (TypeDesc t in sortedTypeTable) + if (t.selected) + return t; + return null; + } + + private void ColorTypes() + { + int count = 0; + + bool anyTypeSelected = FindSelectedType() != null; + + foreach (TypeDesc t in sortedTypeTable) + { + if (count >= colors.Length) + GrowColors(); + if (count < firstColors.Length) + colors[count] = firstColors[count]; + else + colors[count] = MixColor(colors[count - firstColors.Length], colors[count - firstColors.Length + 1]); + t.color = colors[count]; + if (anyTypeSelected) + t.color = MixColor(colors[count], Color.White); + t.brush = new SolidBrush(t.color); + t.pen = new Pen(t.brush); + count++; + } + } + + string FormatSize(ulong size) + { + double w = size; + string byteString = "bytes"; + if (w >= 1024) + { + w /= 1024; + byteString = "kB"; + } + if (w >= 1024) + { + w /= 1024; + byteString = "MB"; + } + if (w >= 1024) + { + w /= 1024; + byteString = "GB"; + } + string format = "{0:f0} {1}"; + if (w < 10) + format = "{0:f1} {1}"; + return string.Format(format, w, byteString); + } + + private string FormatTime(double time) + { + if (timeScale < 0.01) + return string.Format("{0:f3}", time); + else if (timeScale < 0.1) + return string.Format("{0:f2}", time); + else if (timeScale < 1.0) + return string.Format("{0:f1}", time); + else + return string.Format("{0:f0}", time); + } + + private void DrawBuckets(Graphics g) + { + Debug.Assert(verticalScale != 0); + bool noBucketSelected = true; + foreach (Bucket b in bucketTable) + { + if (b.selected) + { + noBucketSelected = false; + break; + } + } + int x = leftMargin; + Brush blackBrush = new SolidBrush(Color.Black); + foreach (Bucket b in bucketTable) + { + int y = graphPanel.Height - bottomMargin; + if (b.minComment != null || b.maxComment != null) + { + string lessOrGreater = "<"; + double age = b.maxAge; + string commentString = b.maxComment; + if (commentString == null) + { + lessOrGreater = ">"; + age = b.minAge; + commentString = b.minComment; + } + string s = string.Format("{0} {1} sec", lessOrGreater, FormatTime(age)); + g.DrawString(s, font, blackBrush, x, y); + s = commentString; + if (g.MeasureString(s, font).Width > bucketWidth) + { + do + s = s.Substring(0, s.Length-1); + while (g.MeasureString(s, font).Width > bucketWidth); + s += "..."; + } + g.DrawString(s, font, blackBrush, x, y + font.Height); + s = FormatSize(b.totalSize); + g.DrawString(s, font, blackBrush, x, y + 2*font.Height); + } + else + { + string s = string.Format("< {0} sec", FormatTime(b.maxAge)); + g.DrawString(s, font, blackBrush, x, y); + s = FormatSize(b.totalSize); + g.DrawString(s, font, blackBrush, x, y + font.Height); + } + + foreach (KeyValuePair d in b.typeDescToSizeCount) + { + TypeDesc t = d.Key; + SizeCount sizeCount = d.Value; + int height = (int)(sizeCount.size/verticalScale); + y -= height; + Brush brush = t.brush; + if (t.selected && (b.selected || noBucketSelected)) + brush = blackBrush; + g.FillRectangle(brush, x, y, bucketWidth, height); + } + + x += bucketWidth + gap; + } + } + + private int BucketWidth(Graphics g) + { + int width1 = (int)g.MeasureString("< 999.999 sec", font).Width; + int width2 = (int)g.MeasureString("999 MB", font).Width; + width1 = Math.Max(width1, width2); + if (markersRadioButton.Checked) + width1 = Math.Max(width1, (int)g.MeasureString("0123456789012345", font).Width); + + return Math.Max(width1, bucketWidth); + } + + private int BottomMargin() + { + return font.Height*3 + 10; + } + + private ulong Init(Graphics g) + { + bucketWidth = BucketWidth(g); + bottomMargin = BottomMargin(); + double maxAge = liveObjectTable.readNewLog.TickIndexToTime(liveObjectTable.lastTickIndex); + int barsVisible = (graphOuterPanel.Width - leftMargin - rightMargin)/(bucketWidth + gap); + + timeScale = GetTimeScale(maxAge/barsVisible, timeScale == 0); + + ulong maxBucketSize = BuildBuckets(timeScale, maxAge); + + ColorTypes(); + + return maxBucketSize; + } + + private bool initialized; + + private void graphPanel_Paint(object sender, System.Windows.Forms.PaintEventArgs e) + { + initialized = false; + + if (liveObjectTable == null) + return; + + Graphics g = e.Graphics; + + ulong maxBucketSize = Init(g); + + int pixelsForBars = graphOuterPanel.Height - topMargin - bottomMargin; + + verticalScale = GetVerticalScale(pixelsForBars, maxBucketSize/1024, verticalScale == 0)*1024; + + int bucketCount = bucketTable.Length; + int width = leftMargin + bucketWidth*bucketCount + gap*(bucketCount-1) + rightMargin; + graphPanel.Width = width; + + int height = topMargin + (int)(maxBucketSize/verticalScale) + bottomMargin; + graphPanel.Height = height; + + DrawBuckets(g); + + initialized = true; + } + + const int typeLegendSpacing = 3; + int dotSize = 8; + + private void DrawTypeLegend(Graphics g) + { + dotSize = (int)g.MeasureString("0", font).Width; + int maxWidth = 0; + int x = leftMargin; + int y = topMargin; + foreach (TypeDesc t in sortedTypeTable) + { + int typeNameWidth = (int)g.MeasureString(t.typeName, font).Width; + int sizeWidth = (int)g.MeasureString(" (999,999,999 bytes, 100.00%)", font).Width; + t.rect = new Rectangle(x, y, Math.Max(typeNameWidth, sizeWidth)+dotSize*2, font.Height*2); + if (maxWidth < t.rect.Width) + maxWidth = t.rect.Width; + y = t.rect.Bottom + typeLegendSpacing; + } + int height = y + bottomMargin; + typeLegendPanel.Height = height; + + int width = leftMargin + maxWidth + rightMargin; + typeLegendPanel.Width = width; + + x = leftMargin; + y = topMargin; + + Brush blackBrush = new SolidBrush(Color.Black); + + int dotOffset = (font.Height - dotSize)/2; + foreach (TypeDesc t in sortedTypeTable) + { + Brush brush = t.brush; + if (t.selected) + brush = blackBrush; + g.FillRectangle(brush, t.rect.Left, t.rect.Top+dotOffset, dotSize, dotSize); + g.DrawString(t.typeName, font, blackBrush, t.rect.Left + dotSize*2, t.rect.Top); + string s = string.Format(" ({0:n0} bytes, {1:f2}%)", t.totalSize, (double)t.totalSize/totalSize*100.0); + g.DrawString(s, font, blackBrush, t.rect.Left + dotSize*2, t.rect.Top + font.Height); + y = t.rect.Bottom + typeLegendSpacing; + } + } + + private void typeLegendPanel_Paint(object sender, System.Windows.Forms.PaintEventArgs e) + { + initialized = false; + + if (liveObjectTable == null) + return; + + Init(e.Graphics); + + DrawTypeLegend(e.Graphics); + + initialized = true; + } + + private void CheckedChanged(object sender, System.EventArgs e) + { + graphPanel.Invalidate(); + } + + private void typeLegendPanel_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) + { + if (!initialized) + return; + + if ((e.Button & MouseButtons.Left) != MouseButtons.None) + { + for (int i = 0; i < bucketTable.Length; i++) + { + if (bucketTable[i].selected) + { + graphPanel.Invalidate(); + typeLegendPanel.Invalidate(); + bucketTable[i].selected = false; + } + } + if (sortedTypeTable != null) + { + foreach (TypeDesc t in sortedTypeTable) + { + if (t.rect.Contains(e.X, e.Y) != t.selected) + { + t.selected = !t.selected; + graphPanel.Invalidate(); + typeLegendPanel.Invalidate(); + } + } + } + } + else if ((e.Button & MouseButtons.Right) != MouseButtons.None) + { + Point p = new Point(e.X, e.Y); + contextMenu.Show(typeLegendPanel, p); + } + } + + private void graphPanel_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) + { + if (!initialized || verticalScale == 0) + return; + + if ((e.Button & MouseButtons.Left) != MouseButtons.None) + { + if (sortedTypeTable != null) + { + foreach (TypeDesc t in sortedTypeTable) + t.selected = false; + } + + int x = leftMargin; + for (int i = 0; i < bucketTable.Length; i++) + { + bucketTable[i].selected = false; + int y = graphPanel.Height - bottomMargin; + foreach (TypeDesc t in bucketTable[i].typeDescToSizeCount.Keys) + { + SizeCount sizeCount = bucketTable[i].typeDescToSizeCount[t]; + ulong size = sizeCount.size; + int height = (int)(size / verticalScale); + + y -= height; + + Rectangle r = new Rectangle(x, y, bucketWidth, height); + if (r.Contains(e.X, e.Y)) + { + t.selected = true; + bucketTable[i].selected = true; + } + } + + x += bucketWidth + gap; + } + graphPanel.Invalidate(); + typeLegendPanel.Invalidate(); + } + else if ((e.Button & MouseButtons.Right) != MouseButtons.None) + { + Point p = new Point(e.X, e.Y); + contextMenu.Show(graphPanel, p); + } + } + + private ToolTip toolTip; + + private void graphPanel_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) + { + if (!initialized || verticalScale == 0) + return; + + if (Form.ActiveForm == this) + { + int x = leftMargin; + foreach (Bucket b in bucketTable) + { + int y = graphPanel.Height - bottomMargin; + foreach (KeyValuePair d in b.typeDescToSizeCount) + { + TypeDesc t = d.Key; + SizeCount sizeCount = d.Value; + ulong size = sizeCount.size; + int height = (int)(size / verticalScale); + + y -= height; + + Rectangle bucketRect = new Rectangle(x, y, bucketWidth, height); + if (bucketRect.Contains(e.X, e.Y)) + { + string caption = string.Format("{0} {1} ({2:f2}%) - {3:n0} instances, {4} average size", t.typeName, FormatSize(size), 100.0*size/totalSize, sizeCount.count, FormatSize((uint)(sizeCount.size/sizeCount.count))); + toolTip.Active = true; + toolTip.SetToolTip(graphPanel, caption); + return; + } + } + x += bucketWidth + gap; + } + } + toolTip.Active = false; + toolTip.SetToolTip(graphPanel, ""); + } + + private bool autoUpdate; + + private void versionTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + { + ReadLogResult readLogResult = MainForm.instance.lastLogResult; + + if (autoUpdate && readLogResult != null && readLogResult.liveObjectTable != liveObjectTable) + { + liveObjectTable = readLogResult.liveObjectTable; + graphPanel.Invalidate(); + typeLegendPanel.Invalidate(); + } + } + + string FormatAge(double age, string ageComment) + { + if (ageComment == null) + return FormatTime(age); + else + return string.Format("{0} ({1} sec)", ageComment, FormatTime(age)); + } + + private void exportMenuItem_Click(object sender, System.EventArgs e) + { + exportSaveFileDialog.FileName = "HistogramByAge.csv"; + exportSaveFileDialog.Filter = "Comma separated files | *.csv"; + if (exportSaveFileDialog.ShowDialog() == DialogResult.OK) + { + StreamWriter w = new StreamWriter(exportSaveFileDialog.FileName); + + TypeDesc selectedType = FindSelectedType(); + + string title = "Histogram by Age"; + if (selectedType != null) + title += " of " + selectedType.typeName + " objects"; + + w.WriteLine(title); + w.WriteLine(); + + w.WriteLine("{0},{1},{2},{3},{4}", "Min Age", "Max Age", "# Instances", "Total Size", "Type"); + + bool noBucketSelected = true; + foreach (Bucket b in bucketTable) + if (b.selected) + noBucketSelected = false; + foreach (Bucket b in bucketTable) + { + if (noBucketSelected || b.selected) + { + foreach (KeyValuePair d in b.typeDescToSizeCount) + { + TypeDesc t = d.Key; + SizeCount sizeCount = d.Value; + + if (selectedType == null || t == selectedType) + w.WriteLine("{0},{1},{2},{3},{4}", FormatAge(b.minAge, b.minComment), FormatAge(b.maxAge, b.maxComment), sizeCount.count, sizeCount.size, t.typeName); + } + } + } + + w.Close(); + } + } + + private void showWhoAllocatedMenuItem_Click(object sender, System.EventArgs e) + { + Histogram selectedHistogram; + string title; + TypeDesc selectedType = FindSelectedType(); + double minAge = 0; + double maxAge = double.PositiveInfinity; + foreach (Bucket b in bucketTable) + { + if (b.selected) + { + minAge = b.minAge; + maxAge = b.maxAge; + } + } + title = "Allocation Graph for objects"; + if (selectedType != null) + title = string.Format("Allocation Graph for {0} objects", selectedType.typeName); + if (minAge > 0.0) + title += string.Format(" of age between {0} and {1} seconds", FormatTime(minAge), FormatTime(maxAge)); + selectedHistogram = new Histogram(liveObjectTable.readNewLog); + LiveObjectTable.LiveObject o; + double nowTime = liveObjectTable.readNewLog.TickIndexToTime(liveObjectTable.lastTickIndex); + for (liveObjectTable.GetNextObject(0, ulong.MaxValue, out o); o.id < ulong.MaxValue; liveObjectTable.GetNextObject(o.id + o.size, uint.MaxValue, out o)) + { + double age = nowTime - liveObjectTable.readNewLog.TickIndexToTime(o.allocTickIndex); + if (minAge <= age && age < maxAge) + { + TypeDesc t = (TypeDesc)typeIndexToTypeDesc[o.typeIndex]; + + if (selectedType == null || t == selectedType) + { + selectedHistogram.AddObject(o.typeSizeStacktraceIndex, 1); + } + } + } + + Graph graph = selectedHistogram.BuildAllocationGraph(new FilterForm()); + + GraphViewForm graphViewForm = new GraphViewForm(graph, title); + graphViewForm.Visible = true; + } + + private void versionTimer_Tick(object sender, System.EventArgs e) + { + if (font != MainForm.instance.font) + { + font = MainForm.instance.font; + graphPanel.Invalidate(); + typeLegendPanel.Invalidate(); + } + } + } +} + diff --git a/CLRProfiler/CLRProfiler/AgeHistogram.resx b/CLRProfiler/CLRProfiler/AgeHistogram.resx new file mode 100644 index 0000000..6d7539d --- /dev/null +++ b/CLRProfiler/CLRProfiler/AgeHistogram.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 132, 17 + + + 287, 17 + + \ No newline at end of file diff --git a/CLRProfiler/CLRProfiler/AllocationDiff.cs b/CLRProfiler/CLRProfiler/AllocationDiff.cs new file mode 100644 index 0000000..8260500 --- /dev/null +++ b/CLRProfiler/CLRProfiler/AllocationDiff.cs @@ -0,0 +1,2265 @@ +//#define V_EXEC + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Data; + + + +namespace CLRProfiler +{ + /// + /// Code organize: + /// AllocationDiff reads two log files one from previous build and one from current build. + /// see region - get base data methods + /// also it builds three tables, + /// see region - build Base table + /// see region - build Caller and callee table + /// see region - build Type Allocation table + /// + /// internal data structure: + /// prevLog and currLog object hold base log data from log files + /// prevG and currG object hold base Graph data, the main purpose was keep the call relations + /// 4 hash table (prevbasedata, currbasedata, prevtypeAllocdata, currtypeAllocdata) holds all useful data from base log/graph + /// been used for build all diff, call relations, and type allocation tables. + /// + /// Table Definitions: + /// basedatatable contains all basic data: inclusive, exclusive, diff, childinclusive, childexclusive, timesBeenCalled, timesmakescalls. + /// caller table contains function Id and its caller Ids + /// callee table contains function Id and its callee Ids + /// typeAlloction table contains type exclusive and diff info + /// + /// Detail Definitions: + /// Memory allocation report can show 9 different details based on allocated memory size + /// detail0 - all size + /// detail01 = max(prevIncl) / 8 + /// detail02 = max(prevIncl) /7 + /// ... + /// + /// + + struct datanode + { + int _id; + int _level; +#if(V_EXEC) + double _incl; + double _excl; + + +#else + int _incl; + int _excl; + +#endif + + int _timesBeenCalled; + int _timesMakeCalls; + int _category; + string _name; + Dictionary _caller; + Dictionary _callee; + Hashtable _callerAlloc; + Hashtable _calleeAlloc; + + public int level + { + get {return _level;} + set {_level = value;} + } + public int id + { + get {return _id;} + set {_id = value;} + } + public string name + { + get{return _name;} + set{_name = value;} + } +#if(V_EXEC) + public double incl + { + get{return _incl;} + set{_incl = value;} + } + public double excl + { + get{return _excl;} + set{_excl = value;} + } + +#else + public int incl + { + get{return _incl;} + set{_incl = value;} + } + public int excl + { + get{return _excl;} + set{_excl = value;} + } + +#endif + public int timesBeenCalled + { + get{return _timesBeenCalled;} + set{_timesBeenCalled = value;} + } + public int timesMakeCalls + { + get{return _timesMakeCalls;} + set{_timesMakeCalls = value;} + } + public int category + { + get{return _category;} + set{_category = value;} + } + public Dictionary caller + { + get { return _caller;} + set { _caller = value;} + } + public Dictionary callee + { + get { return _callee;} + set { _callee = value;} + } + public Hashtable callerAlloc + { + get { return _callerAlloc;} + set { _callerAlloc = value;} + } + public Hashtable calleeAlloc + { + get { return _calleeAlloc;} + set { _calleeAlloc = value;} + } + + } + public class AllocationDiff + { + class CompareIncl : IComparer + { + Hashtable inclOfNode; + + internal CompareIncl(Hashtable inclOfNode) + { + this.inclOfNode = inclOfNode; + } + + int IComparer.Compare(object x, object y) + { + long inclX = (long)inclOfNode[x]; + long inclY = (long)inclOfNode[y]; + if (inclX < inclY) + return 1; + else if (inclX > inclY) + return -1; + else + return 0; + } + } + + #region data member + // log file names + private string _prevFile = null; + private string _currFile = null; + private string _diffFile = null; + + // holds all useful data from base + // for build all diff, call relations, and type allocation tables + internal Hashtable _prevbasedata = null; + internal Hashtable _currbasedata = null; + + + // maps for match search + public Hashtable basedataId =null; + public Hashtable Idbasedata =null; + public Hashtable typeAllocdataId = null; + + // hold log data + private LogBase _prevLog = null; + private LogBase _currLog = null; + // hold base graph related data + private GraphBase _prevG = null; + private GraphBase _currG = null; + + // hold base stacktrace info + internal CallTreeForm _prevcallTrace = null; + internal CallTreeForm _currcallTrace = null; + internal DiffDataNode Root = null; + internal DataTable diffTracetbl = null; + private static int nodeidx = 0; + + internal Hashtable prevFuncExcl = new Hashtable(); + internal Hashtable currFuncExcl = new Hashtable(); + internal Hashtable prevTypeExcl = new Hashtable(); + internal Hashtable currTypeExcl = new Hashtable(); + + + StreamReader r; + byte[] buffer; + int c; + int line; + long pos; + long lastLineStartPos; + int bufPos; + int bufLevel; + private static int sumnodeidx = 0; + private static int depth = 0; + + internal DataTable summaryTracetbl = null; + internal Hashtable diffCallTreeNodes = new Hashtable(); + + private const int idx_parentid = 0; + // private const int idx_parentname = 1; + private const int idx_id = 2; + private const int idx_name = 3; + private const int idx_mapname = 4; + private const int idx_prevIncl = 5; + private const int idx_currIncl = 6; + private const int idx_diffIncl = 7; + + private const int idx_prevCalls = 8; + private const int idx_currCalls = 9; + private const int idx_diffCalls = 10; + + private const int idx_prevFunid = 11; + private const int idx_currFunid = 12; + private const int idx_type = 13; + private const int idx_depth = 14; + + + // table details filter value +#if(V_EXEC) + private const double detail01D = 8.0; +#else + private const int detail01D = 8; +#endif + private ulong maxIncl = 0; + private ulong typemaxIncl = 0; + public DetailFilter prevFilter = new DetailFilter(); + public DetailFilter currFilter =new DetailFilter(); + public DetailFilter prevTypedeFilter = new DetailFilter(); + public DetailFilter currTypedeFilter = new DetailFilter(); + + // dataset and tables + private DataSet _ds = null; + private DataTable _callertbl = null; + private DataTable _calleetbl = null; + private DataTable _basedatatable = null; + //private DataTable _typeAlloctable = null; + private DataTable _ContriTocallertbl = null; + private DataTable _ContriTocalleetbl = null; + + #endregion + + #region struct data methods + + // caller and callee tables node + struct callnode + { + int _id; + int _callerid; + int _calleeid; + public int id + { + get {return _id;} + set { _id = value;} + } + + public int callerid + { + get{return _callerid;} + set{_callerid = value;} + } + public int calleeid + { + get{return _calleeid;} + set{_calleeid = value;} + } + + } + // typeAllocation table node + struct typeAllocnode + { + int _typeid; + int _funcid; +#if(V_EXEC) + double _allocmem; +#else + int _allocmem; +#endif + public int typeid + { + get {return _typeid;} + set { _typeid = value;} + } + + public int funcid + { + get{return _funcid;} + set{_funcid= value;} + } +#if(V_EXEC) + public double allocmem + { + get{return _allocmem;} + set{_allocmem = value;} + } +#else + public int allocmem + { + get{return _allocmem;} + set{_allocmem = value;} + } +#endif + + } + public DataTable basedatatable + { + get {return _basedatatable;} + set { _basedatatable = value;} + } + public DataTable ContriTocallertbl + { + get {return _ContriTocallertbl;} + set { _ContriTocallertbl = value;} + } + public DataTable ContriTocalleetbl + { + get {return _ContriTocalleetbl;} + set { _ContriTocalleetbl = value;} + } + + // detailds for reportform details RadioButton + public struct DetailFilter + { + ulong _max; +#if(V_EXEC) + double _detail01; + double _detail02; + double _detail05; + double _detail1; + double _detail2; + double _detail5; + double _detail10; + public double detail01 + { + get{return _detail01;} + set{_detail01 = value;} + } + public double detail02 + { + get{return _detail02;} + set{_detail02 = value;} + } + public double detail05 + { + get{return _detail05;} + set{_detail05 = value;} + } + public double detail1 + { + get{return _detail1;} + set{_detail1 = value;} + } + public double detail2 + { + get{return _detail2;} + set{_detail2 = value;} + } + public double detail5 + { + get{return _detail5;} + set{_detail5 = value;} + } + public double detail10 + { + get{return _detail10;} + set{_detail10 = value;} + } +#else + ulong _detail01; + ulong _detail02; + ulong _detail05; + ulong _detail1; + ulong _detail2; + ulong _detail5; + ulong _detail10; + internal ulong detail01 + { + get{return _detail01;} + set{_detail01 = value;} + } + internal ulong detail02 + { + get{return _detail02;} + set{_detail02 = value;} + } + internal ulong detail05 + { + get{return _detail05;} + set{_detail05 = value;} + } + internal ulong detail1 + { + get{return _detail1;} + set{_detail1 = value;} + } + internal ulong detail2 + { + get{return _detail2;} + set{_detail2 = value;} + } + internal ulong detail5 + { + get{return _detail5;} + set{_detail5 = value;} + } + internal ulong detail10 + { + get{return _detail10;} + set{_detail10 = value;} + } +#endif + internal ulong max + { + get{return _max;} + set{_max = value;} + } + } + + #endregion + + #region constructor + public AllocationDiff() + { + _prevLog = new LogBase(); + _currLog = new LogBase(); + _prevG = new GraphBase(); + _currG = new GraphBase(); + + ds = new DataSet(); + _prevbasedata = new Hashtable(); + _currbasedata = new Hashtable(); + + + basedataId = new Hashtable(); + Idbasedata = new Hashtable(); + basedatatable = new DataTable("basedatatbl"); + callertbl = new DataTable("caller"); + calleetbl = new DataTable("callee"); + typeAllocdataId = new Hashtable(); + ContriTocallertbl = new DataTable("ContriTocallertbl"); + ContriTocalleetbl = new DataTable("ContriTocalleetbl"); + MakeBaseDataTable(basedatatable); + MakeCallTables(callertbl, true); + MakeCallTables(calleetbl, false); + MakeBaseDataTable(ContriTocallertbl); + MakeBaseDataTable(ContriTocalleetbl); + prevFilter = new DetailFilter(); + currFilter = new DetailFilter(); + prevTypedeFilter = new DetailFilter(); + currTypedeFilter = new DetailFilter(); + } + #endregion + + #region public property methods + // DataSet used to collect tables and + // build relations between table in the near future + // also it usded by DataViewManager in ReportForm + public DataSet ds + { + get{ return _ds;} + set{ _ds = value;} + } + + public DataTable callertbl + { + get {return _callertbl;} + set { _callertbl = value;} + } + + public DataTable calleetbl + { + get {return _calleetbl;} + set { _calleetbl = value;} + } + + public string PrevLogFileName + { + get {return _prevFile;} + set + { + _prevFile = value; + _prevLog.LogFileName = value; + } + } + + public string CurrLogFileName + { + get {return _currFile;} + set + { + _currFile = value; + _currLog.LogFileName = value; + } + } + + public string diffLogFileName + { + get {return _diffFile;} + set + { + _diffFile = value; + } + } + #endregion + + #region public methods + public void BuildAllocationDiffTable() + { + try + { + GetLogData(); + //BuildBaseData(_prevG, _prevcallTree, _prevbasedata); + //BuildBaseData(_currG, _currcallTree, _currbasedata); + + BuildBaseData(_prevG, _prevcallTrace, _prevbasedata, this.prevFuncExcl, this.prevTypeExcl); + BuildBaseData(_currG, _currcallTrace, _currbasedata, this.currFuncExcl, this.currTypeExcl); + BuildBaseDataTable(); + BuildBaseCallTables(); + getDetailFilter(ref prevFilter); + getDetailFilter(ref currFilter); + getDetailFilter(ref prevTypedeFilter); + getDetailFilter(ref currTypedeFilter); + + BuildContributionCalleeTable(); + BuildContributionCallerTable(); + } + catch(Exception e) + { + throw new Exception(e.Message + "\n"); + } + } + public bool IsAllocType(string name) + { + return (_prevLog.logResult.allocatedHistogram.readNewLog.typeSignatureIdHash.ContainsKey(name) || + _currLog.logResult.allocatedHistogram.readNewLog.typeSignatureIdHash.ContainsKey(name)); + } + #endregion + + #region GetLogData + private void GetLogData() + { + // get base data from log files + try + { + _prevLog.readLogFile(); + } + catch + { + throw new Exception("Bad log file: " + _prevLog.LogFileName + "\n"); + } + try + { + _currLog.readLogFile(); + } + catch + { + throw new Exception("Bad log file: " + _currLog.LogFileName + "\n"); + } + + // get mixed data structure graph + try + { + _prevG.GetAllocationGraph(_prevLog.logResult); + } + catch + { + throw new Exception("Bad data structure in log file: " + _prevLog.LogFileName + "\n"); + } + try + { + _currG.GetAllocationGraph(_currLog.logResult); + } + catch + { + throw new Exception("Bad data structure in log file: " + _currLog.LogFileName + "\n"); + } + + // get more detailed allocation info from stack trace + try + { + + _prevcallTrace = new CallTreeForm(_prevLog.LogFileName, _prevLog.logResult, true); + ReadFile(_prevcallTrace, _prevLog.LogFileName, this.prevFuncExcl, this.prevTypeExcl); + } + catch + { + throw new Exception("Bad stacktrace content in log file: " + _prevLog.logResult + "\n"); + } + try + { + _currcallTrace = new CallTreeForm(_currLog.LogFileName, _currLog.logResult, true); + ReadFile(_currcallTrace, _currLog.LogFileName, this.currFuncExcl, this.currTypeExcl); + } + catch + { + throw new Exception("Bad stacktrace content in log file: " + _currLog.logResult + "\n"); + } + nodeidx = 0; + diffTracetbl = new DataTable("diffTrace"); + summaryTracetbl = new DataTable("summaryTracetbl"); + MakeDiffTreceTable(diffTracetbl); + MakeDiffTreceTable(summaryTracetbl); + + + string rname = _currcallTrace.MakeName((TreeNode)_currcallTrace.callTreeView.Root); + Root = new DiffDataNode(rname); + Root.prevIncl = ((TreeNode)_prevcallTrace.callTreeView.Root).data.bytesAllocated; + Root.currIncl = ((TreeNode)_currcallTrace.callTreeView.Root).data.bytesAllocated; + Root.diffIncl = Root.currIncl - Root.prevIncl; + Root.prevCalls = ((TreeNode)_prevcallTrace.callTreeView.Root).data.numberOfFunctionsCalled; + Root.currCalls = ((TreeNode)_currcallTrace.callTreeView.Root).data.numberOfFunctionsCalled; + Root.diffCalls = Root.currCalls - Root.prevCalls; + + + Root.nodeId = nodeidx++; + Root.parentId = -1; + Root.prevFunId = 0; + Root.currFunId = 0; + AddDiffTraceTableRow(diffTracetbl, Root); + + + BuildDiffTraceTable(Root, (TreeNode)_currcallTrace.callTreeView.Root, (TreeNode)_prevcallTrace.callTreeView.Root); + this.ds.Tables.Add(diffTracetbl); + sumnodeidx = 0; + depth = -1; + BuildSummaryTable(Root, -1, "parentid = -1"); + Root = (DiffDataNode)Root.allkids[0]; + this.ds.Tables.Add(summaryTracetbl); + + + + } + #endregion + + #region build Base table method + private void BuildBaseData(GraphBase gb, CallTreeForm tmpcallTree, Hashtable htbl, Hashtable FuncExcl, Hashtable TypeExcl) + { + Vertex selectedVertex; + int selectedVertexCount = gb.SelectedVertexCount(out selectedVertex); + int id = 1; + + string nameAndSignature = null; + datanode n = new datanode(); + + + try + { + foreach (Vertex v in gb.basegraph.vertices.Values) + { + if( !v.name.StartsWith("????")) + { + if (v.selected || (selectedVertexCount == 0) ) + { + nameAndSignature = v.name; + if(v.signature != null) + nameAndSignature += ' ' + v.signature; + n.name = nameAndSignature; + n.incl = FormatSize((int)v.weight); + n.caller = v.incomingEdges; + n.callee = v.outgoingEdges; + n.level = v.level; + n.excl = 0; + n.timesBeenCalled= n.timesMakeCalls = 0; + FillCallAlloc(ref n, v); + if(tmpcallTree.LogResult.allocatedHistogram.readNewLog.funcSignatureIdHash.ContainsKey(nameAndSignature)) + { + n.category = 1; // func + id = tmpcallTree.LogResult.callstackHistogram.readNewLog.funcSignatureIdHash[nameAndSignature]; + if(FuncExcl.ContainsKey(nameAndSignature)) + { + n.excl = FormatSize((int)FuncExcl[nameAndSignature]); + } + if( id > 0 && id 0 && CallStats.ContainsKey(id)) + { + n.timesBeenCalled = int.Parse(((GlobalCallStats)CallStats[id]).timesCalled.ToString()); + n.timesMakeCalls = int.Parse(((GlobalCallStats)CallStats[id]).totalFunctionsCalled.ToString()); + }*/ + if( !htbl.ContainsKey(nameAndSignature)) + { + htbl.Add(nameAndSignature, n); + } + } + else if(tmpcallTree.LogResult.allocatedHistogram.readNewLog.typeSignatureIdHash.ContainsKey(nameAndSignature)) + { + n.category = 2; // type + id = tmpcallTree.LogResult.allocatedHistogram.readNewLog.typeSignatureIdHash[nameAndSignature]; + if(TypeExcl.ContainsKey(nameAndSignature)) + { + n.excl = FormatSize((int)TypeExcl[nameAndSignature]); + } + if( id > 0 && id v.weight) ? typemaxIncl : v.weight; + htbl.Add(nameAndSignature, n); + } + } + else //if( nameAndSignature == "" || nameAndSignature == "") + { + if( !htbl.ContainsKey(nameAndSignature)) + { + maxIncl = v.weight; + htbl.Add(nameAndSignature, n); + } + } + + } + + } + } + } + catch + { + throw new Exception("Faild on build base data structure \n"); + } + // max for caculate function/type 9 details + if( prevFilter.max == 0) + { + prevFilter.max = maxIncl; + prevTypedeFilter.max = typemaxIncl; + } + else + { + currFilter.max = maxIncl; + currTypedeFilter.max = typemaxIncl; + } + maxIncl = 0; + typemaxIncl = 0; + } + private void FillCallAlloc(ref datanode n, Vertex v) + { + n.calleeAlloc = new Hashtable(); + n.callerAlloc = new Hashtable(); + foreach(Edge edge in v.outgoingEdges.Values) + { + string nameAndSignature = edge.ToVertex.name; + if(edge.ToVertex.signature != null) + nameAndSignature += ' ' + edge.ToVertex.signature; + if(!n.calleeAlloc.ContainsKey(nameAndSignature)) + { + n.calleeAlloc.Add(nameAndSignature, FormatSize((int)edge.weight)); + } + } + foreach(Edge edge in v.incomingEdges.Values) + { + string nameAndSignature = edge.FromVertex.name; + if(edge.FromVertex.signature != null) + nameAndSignature += ' ' + edge.FromVertex.signature; + if(!n.callerAlloc.ContainsKey(nameAndSignature)) + { + n.callerAlloc.Add(nameAndSignature, FormatSize((int)edge.weight)); + } + } + } + + private void BuildBaseDataTable() + { + int id = 0; + try + { + + foreach(string nameAndSignature in _prevbasedata.Keys) + { + + datanode pn= new datanode(); + datanode cn= new datanode(); + pn = (datanode)_prevbasedata[nameAndSignature]; + pn.id = id; + if(_currbasedata.ContainsKey(nameAndSignature)) + { + cn = (datanode)_currbasedata[nameAndSignature]; + } + AddBaseTableRow(this.basedatatable, nameAndSignature, pn, cn); + basedataId.Add(nameAndSignature, id); + Idbasedata.Add(id, nameAndSignature); + id++; + } + foreach( string CnameAndSignature in _currbasedata.Keys) + { + if(! _prevbasedata.ContainsKey(CnameAndSignature)) + { + datanode pn= new datanode(); + datanode cn= new datanode(); + cn = (datanode)_currbasedata[CnameAndSignature]; + cn.id = id; + AddBaseTableRow(this.basedatatable, CnameAndSignature, pn, cn); + basedataId.Add(CnameAndSignature, id); + Idbasedata.Add(id, CnameAndSignature); + id++; + } + } + ds.Tables.Add(this.basedatatable); + } + catch + { + throw new Exception("Faild on build base data Tables \n"); + } + } + private void AddBaseTableRow(DataTable tmptbl, string name, datanode pn, datanode cn ) + { + /*if( (cn.incl - pn.incl == 0) && (cn.excl - pn.excl == 0)) + { + return; + }*/ + DataRow tmpRow = tmptbl.NewRow(); + if(pn.name != null) + tmpRow["id"] = pn.id; + else + { + tmpRow["id"] = cn.id; + } + + tmpRow["name"] = name; + tmpRow["prevIncl"] = pn.incl; + tmpRow["currIncl"] = cn.incl; + tmpRow["prevExcl"] = pn.excl; + tmpRow["currExcl"] = cn.excl; +#if(V_EXEC) + //tmpRow["diffIncl"] = Convert.ToDouble(string.Format("{0:f2}", (cn.incl - pn.incl))); + tmpRow["diffIncl"] = Math.Round((cn.incl - pn.incl), 2); + tmpRow["diffExcl"] = Math.Round((cn.excl - pn.excl), 2); + tmpRow["prevChildIncl"] = Math.Round((pn.incl - pn.excl), 2); + tmpRow["currChildIncl"] = Math.Round((cn.incl - cn.excl), 2); + tmpRow["diffChildIncl"] = Math.Round(((cn.incl - cn.excl) - (pn.incl - pn.excl)), 2); +#else + tmpRow["diffIncl"] = cn.incl - pn.incl; + tmpRow["diffExcl"] = cn.excl - pn.excl; + tmpRow["prevChildIncl"] = pn.incl - pn.excl; + tmpRow["currChildIncl"] = cn.incl - cn.excl; + tmpRow["diffChildIncl"] = (cn.incl - cn.excl) - (pn.incl - pn.excl); + +#endif + tmpRow["prevTimesCalled"] = pn.timesBeenCalled; + tmpRow["currTimesCalled"] = cn.timesBeenCalled; + tmpRow["prevTimesMakecalls"] = pn.timesMakeCalls; + tmpRow["currTimesMakecalls"] = cn.timesMakeCalls; + tmpRow["diffTimesCalled"] = cn.timesBeenCalled - pn.timesBeenCalled; + tmpRow["diffTimesMakecalls"] = cn.timesMakeCalls - pn.timesMakeCalls; + + /*tmpRow["prevlevel"] = pn.level; + tmpRow["currlevel"] = cn.level; + + tmpRow["prevcat"] = pn.category; + tmpRow["currcat"] = cn.category;*/ + + tmptbl.Rows.Add(tmpRow); + } + public void MakeBaseDataTable(DataTable tbl) + { + addTableRow(tbl, "System.Int32", "id"); + addTableRow(tbl, "System.String", "name"); +#if(V_EXEC) + addTableRow(tbl, "System.Double", "prevIncl"); + addTableRow(tbl, "System.Double", "currIncl"); + addTableRow(tbl, "System.Double", "diffIncl"); + addTableRow(tbl, "System.Double", "prevExcl"); + addTableRow(tbl, "System.Double", "currExcl"); + addTableRow(tbl, "System.Double", "diffExcl"); + addTableRow(tbl, "System.Double", "prevChildIncl"); + addTableRow(tbl, "System.Double", "currChildIncl"); + addTableRow(tbl, "System.Double", "diffChildIncl"); +#else + addTableRow(tbl, "System.Int32", "prevIncl"); + addTableRow(tbl, "System.Int32", "currIncl"); + addTableRow(tbl, "System.Int32", "diffIncl"); + addTableRow(tbl, "System.Int32", "prevExcl"); + addTableRow(tbl, "System.Int32", "currExcl"); + addTableRow(tbl, "System.Int32", "diffExcl"); + addTableRow(tbl, "System.Int32", "prevChildIncl"); + addTableRow(tbl, "System.Int32", "currChildIncl"); + addTableRow(tbl, "System.Int32", "diffChildIncl"); +#endif + addTableRow(tbl, "System.Int32", "prevTimesCalled"); + addTableRow(tbl, "System.Int32", "currTimesCalled"); + addTableRow(tbl, "System.Int32", "diffTimesCalled"); + addTableRow(tbl, "System.Int32", "prevTimesMakecalls"); + addTableRow(tbl, "System.Int32", "currTimesMakecalls"); + addTableRow(tbl, "System.Int32", "diffTimesMakecalls"); + + addTableRow(tbl, "System.Int32", "prevlevel"); + addTableRow(tbl, "System.Int32", "currlevel"); + addTableRow(tbl, "System.Int32", "prevcat"); + addTableRow(tbl, "System.Int32", "currcat"); + + /* DataColumn[] pk = new DataColumn[1]; + pk[0] = tbl.Columns["name"]; + string tblkey = "PK_" + tbl.TableName; + tbl.Constraints.Add(new UniqueConstraint(tblkey, pk[0])); + tbl.PrimaryKey = pk;*/ + } + + #endregion + + + #region build Caller and callee table + private void BuildBaseCallTables() + { + int id = -1; + string nameAndSignature = null; + try + { + foreach(DictionaryEntry de in basedataId) + { + datanode pn= new datanode(); + datanode cn= new datanode(); + nameAndSignature = (string)de.Key; + id = (int)basedataId[nameAndSignature]; + if(this._prevbasedata.ContainsKey(nameAndSignature)) + { + pn = (datanode)_prevbasedata[nameAndSignature]; + pn.id = id; + BuildCallTables(this.callertbl, id, pn.caller, true); + BuildCallTables(this.calleetbl, id, pn.callee, false); + } + else + { + if(_currbasedata.ContainsKey(nameAndSignature)) + { + cn = (datanode)_currbasedata[nameAndSignature]; + cn.id = id; + BuildCallTables(this.callertbl, id, cn.caller, true); + BuildCallTables(this.calleetbl, id, cn.callee, false); + } + } + + } + ds.Tables.Add(this.callertbl); + ds.Tables.Add(this.calleetbl); + } + catch + { + throw new Exception("Faild on build caller/callee data Tables \n"); + } + } + private void BuildCallTables(DataTable tbl, int id, Dictionary callhash, bool iscaller) + { + string nameAndSignature = null; + callnode cn = new callnode(); + cn.id = id; + foreach(Vertex cv in callhash.Keys) + { + nameAndSignature = cv.name; + if(cv.signature != null) + nameAndSignature += ' ' + cv.signature; + if(iscaller) + { + if( basedataId.ContainsKey(nameAndSignature)) + { + cn.callerid = (int)basedataId[nameAndSignature]; + AddCallTableRow(tbl, cn, iscaller); + } + } + else + { + if( basedataId.ContainsKey(nameAndSignature)) + { + cn.calleeid = (int)basedataId[nameAndSignature]; + AddCallTableRow(tbl, cn, iscaller); + } + + } + } + } + + private void AddCallTableRow(DataTable tmptbl, callnode n, bool iscaller) + { + DataRow tmpRow; + + tmpRow = tmptbl.NewRow(); + tmpRow["id"] = n.id; + if(iscaller) + tmpRow["callerid"] = n.callerid; + else + tmpRow["calleeid"] = n.calleeid; + tmptbl.Rows.Add(tmpRow); + } + + private void MakeCallTables(DataTable tbl, bool iscaller) + { + addTableRow(tbl, "System.Int32", "id"); + if(iscaller) + { + addTableRow(tbl, "System.Int32", "callerid"); + } + else + { + addTableRow(tbl, "System.Int32", "calleeid"); + } + } + + #endregion + + #region build Caller and callee Contribution table + private void BuildContributionCalleeTable() + { + int id = -1; + string nameAndSignature = null; + bool exist = false; + Hashtable cnnew; + datanode pn1; + datanode cn1; + try + { + foreach(DictionaryEntry de in basedataId) + { + datanode pn= new datanode(); + datanode cn= new datanode(); + nameAndSignature = (string)de.Key; + + id = (int)basedataId[nameAndSignature]; + if(this._prevbasedata.ContainsKey(nameAndSignature)) + { + pn = (datanode)_prevbasedata[nameAndSignature]; + if(_currbasedata.ContainsKey(nameAndSignature)) + { + cn = (datanode)_currbasedata[nameAndSignature]; + exist = true; + } + else + { + exist = false; + } + cnnew = new Hashtable(); + foreach(string nameAndSignature1 in pn.calleeAlloc.Keys) + { + pn1 = new datanode(); + cn1 = new datanode(); + + if(this._prevbasedata.ContainsKey(nameAndSignature1)) + { + pn1 = (datanode)_prevbasedata[nameAndSignature1]; + pn1.id = id; + pn1.incl = FormatSize(int.Parse(pn.calleeAlloc[nameAndSignature1].ToString())); + if(exist) + { + if(this._currbasedata.ContainsKey(nameAndSignature1)) + { + cn1 = (datanode)_currbasedata[nameAndSignature1]; + cn1.id = id; + if(cn.calleeAlloc.ContainsKey(nameAndSignature1)) + { + cn1.incl = FormatSize(int.Parse(cn.calleeAlloc[nameAndSignature1].ToString())); + } + cnnew[nameAndSignature1] = 1; + } + } + AddBaseTableRow(this.ContriTocalleetbl, nameAndSignature1, pn1, cn1); + + } + } + // adding item in new and not in old + if(exist) + { + foreach(string nameAndSignature1 in cn.calleeAlloc.Keys) + { + pn1 = new datanode(); + if(!cnnew.ContainsKey(nameAndSignature1)) + { + cn1 = (datanode)_currbasedata[nameAndSignature1]; + cn1.id = id; + cn1.incl = FormatSize(int.Parse(cn.calleeAlloc[nameAndSignature1].ToString())); + AddBaseTableRow(this.ContriTocalleetbl, nameAndSignature1, pn1, cn1); + } + } + } + } + + if(this._currbasedata.ContainsKey(nameAndSignature)) + { + cn = (datanode)_currbasedata[nameAndSignature]; + pn1= new datanode(); + if(!this._prevbasedata.ContainsKey(nameAndSignature)) + { + cn1 = (datanode)_currbasedata[nameAndSignature]; + foreach(string nameAndSignature1 in cn.calleeAlloc.Keys) + { + if(this._currbasedata.ContainsKey(nameAndSignature1)) + { + cn1 = (datanode)_currbasedata[nameAndSignature1]; + cn1.id = id; + cn1.incl = FormatSize(int.Parse(cn.calleeAlloc[nameAndSignature1].ToString())); + AddBaseTableRow(this.ContriTocalleetbl, nameAndSignature1, pn1, cn1); + } + + } + } + } + } + ds.Tables.Add(this.ContriTocalleetbl); + } + catch + { + throw new Exception("Faild on build caller/callee data Tables \n"); + } + } + + + private void BuildContributionCallerTable() + { + int id = -1; + string nameAndSignature = null; + bool exist = false; + Hashtable cnnew; + datanode pn1; + datanode cn1; + try + { + foreach(DictionaryEntry de in basedataId) + { + datanode pn= new datanode(); + datanode cn= new datanode(); + nameAndSignature = (string)de.Key; + id = (int)basedataId[nameAndSignature]; + if(this._prevbasedata.ContainsKey(nameAndSignature)) + { + pn = (datanode)_prevbasedata[nameAndSignature]; + if(_currbasedata.ContainsKey(nameAndSignature)) + { + cn = (datanode)_currbasedata[nameAndSignature]; + exist = true; + } + else + { + exist = false; + } + cnnew = new Hashtable(); + foreach(Edge edge in pn.caller.Values) + { + pn1 = new datanode(); + cn1 = new datanode(); + string nameAndSignature1 = edge.FromVertex.name; + if(edge.FromVertex.signature != null) + nameAndSignature1 += ' ' + edge.FromVertex.signature; + if(this._prevbasedata.ContainsKey(nameAndSignature1)) + { + pn1 = (datanode)_prevbasedata[nameAndSignature1]; + pn1.id = id; + pn1.incl = FormatSize((int)edge.weight); + if(exist) + { + foreach(Edge edgec in cn.caller.Values) + { + if(edgec.FromVertex.name == edge.FromVertex.name && edgec.FromVertex.signature == edge.FromVertex.signature) + { + if(this._currbasedata.ContainsKey(nameAndSignature1)) + { + cn1 = (datanode)_currbasedata[nameAndSignature1]; + cn1.id = id; + cn1.incl = FormatSize((int)edgec.weight); + cnnew[nameAndSignature1] = 1; + } + } + } + } + AddBaseTableRow(this.ContriTocallertbl, nameAndSignature1, pn1, cn1); + + } + } + // adding item in new and not in old + if(exist) + { + foreach(Edge edgec in cn.caller.Values) + { + pn1 = new datanode(); + string nameAndSignature1 = edgec.FromVertex.name; + if(edgec.FromVertex.signature != null) + nameAndSignature1 += ' ' + edgec.FromVertex.signature; + + if(!cnnew.ContainsKey(nameAndSignature1)) + { + cn1 = (datanode)_currbasedata[nameAndSignature1]; + cn1.id = id; + cn1.incl = FormatSize((int)edgec.weight); + AddBaseTableRow(this.ContriTocallertbl, nameAndSignature1, pn1, cn1); + } + } + } + } + + if(this._currbasedata.ContainsKey(nameAndSignature)) + { + cn = (datanode)_currbasedata[nameAndSignature]; + pn1= new datanode(); + if(!this._prevbasedata.ContainsKey(nameAndSignature)) + { + cn1 = (datanode)_currbasedata[nameAndSignature]; + foreach(Edge edge in cn.caller.Values) + { + string nameAndSignature1 = edge.FromVertex.name; + if(edge.FromVertex.signature != null) + nameAndSignature1 += ' ' + edge.FromVertex.signature; + if(this._currbasedata.ContainsKey(nameAndSignature1)) + { + cn1 = (datanode)_currbasedata[nameAndSignature1]; + cn1.id = id; + cn1.incl = FormatSize((int)edge.weight); + AddBaseTableRow(this.ContriTocallertbl, nameAndSignature1, pn1, cn1); + } + + } + } + } + } + ds.Tables.Add(this.ContriTocallertbl); + } + catch + { + throw new Exception("Faild on build caller/callee data Tables \n"); + } + } + #endregion + + #region share used functions + private void addTableRow(DataTable tbl, string colType, string colName) + { + DataColumn tmpColumn; + tmpColumn = new DataColumn(); + tmpColumn.DataType = System.Type.GetType(colType); + tmpColumn.ColumnName = colName; + tbl.Columns.Add(tmpColumn); + } + + private void getDetailFilter(ref DetailFilter df) + { + //DataRow[] r = basedatatable.Select("prevIncl = max(prevIncl)"); + //double max = (double)r[0][2]; +#if(V_EXEC) + double max = FormatSize(df.max); + df.detail01 = Math.Round( (max / detail01D), 2); + df.detail02 = Math.Round((max / (detail01D - 1)), 2); + df.detail05 = Math.Round((max / (detail01D - 2)), 2); + df.detail1 = Math.Round((max / (detail01D - 3)), 2); + df.detail2 = Math.Round((max / (detail01D - 4)), 2); + df.detail5 = Math.Round((max / (detail01D - 5)), 2); + df.detail10 = Math.Round((max / (detail01D - 6)), 2); +#else + ulong max = df.max; + df.detail01 = max / detail01D; + df.detail02 = max / (detail01D - 1); + df.detail05 = max /(detail01D - 2); + df.detail1 = max / (detail01D - 3); + df.detail2 = max / (detail01D - 4); + df.detail5 = max / (detail01D - 5); + df.detail10 = max / (detail01D - 6); +#endif + } + +#if(V_EXEC) + double FormatSize(int size) + { + double w = size; + w /= 1024; + return Math.Round(w, 2); + } +#else + int FormatSize(int size) + { + return size; + } +#endif + #endregion + + #region CallTrace - MakeDiffTreceTable, BuildDiffTraceTable + private void BuildDiffTraceTable(DiffDataNode parent, TreeNode currRoot, TreeNode prevRoot) + { + ArrayList currKids = new ArrayList(); + ArrayList prevKids = new ArrayList(); + ArrayList currDKids = new ArrayList(); + ArrayList prevDKids = new ArrayList(); + + //get kids + if(currRoot != null) + { + currKids = _currcallTrace.FetchKids(null, currRoot); + if(currKids.Count >0) + { + currDKids = TransCurrTree(currKids); + } + } + if(prevRoot != null) + { + prevKids = _prevcallTrace.FetchKids(null, prevRoot); + if(prevKids.Count > 0) + { + prevDKids = TransPrevTree(prevKids); + } + } + + // get diff node + ArrayList diffKids = GetDiffKids(parent, currDKids, prevDKids); + + // recursive for each diff node + for(int i = 0; i < diffKids.Count; i++) + { + BuildDiffTraceTable(diffKids[i] as DiffDataNode, ((DiffDataNode)diffKids[i]).currTreenode as TreeNode, ((DiffDataNode)diffKids[i]).prevTreenode as TreeNode); + + } + + + } + + private ArrayList TransCurrTree(ArrayList treeNode) + { + ArrayList diffnodes = new ArrayList(); + int functionId = 0; + int [] kidStacktrace; + + for( int i = 0; i < treeNode.Count; i++) + { + TreeNode kidNode = treeNode[i] as TreeNode; + if(kidNode.data.bytesAllocated >0) + { + + kidStacktrace = _currcallTrace.IndexToStacktrace(kidNode.stackid); + if (kidNode.nodetype == TreeNode.NodeType.Call) + { + functionId = kidStacktrace[ kidStacktrace.Length - 1 ]; + } + else if (kidNode.nodetype == TreeNode.NodeType.Allocation) + { + functionId = kidStacktrace[ 0 ]; + + } + + string name = _currcallTrace.MakeName(kidNode); + DiffDataNode node = new DiffDataNode(name); + node.currIncl = kidNode.data.bytesAllocated; + node.currCalls = kidNode.data.numberOfFunctionsCalled; + + + node.currTreenode = kidNode; + node.nodetype = (DiffDataNode.NodeType)kidNode.nodetype; + + switch(node.nodetype) + { + case DiffDataNode.NodeType.Allocation: + node.currFunId = functionId; + break; + + case DiffDataNode.NodeType.Call: + node.currFunId = functionId; + node.mapname = _currcallTrace.names[functionId]; + string sig = _currcallTrace.signatures[functionId]; + if(sig != null) + { + node.mapname += ' ' + sig; + } + break; + } + + diffnodes.Add(node); + } + + } + + return diffnodes; + } + + private ArrayList TransPrevTree(ArrayList treeNode) + { + ArrayList diffnodes = new ArrayList(); + int functionId = 0; + int [] kidStacktrace; + + for( int i = 0; i < treeNode.Count; i++) + { + TreeNode kidNode = treeNode[i] as TreeNode; + if(kidNode.data.bytesAllocated >0) + { + + kidStacktrace = _prevcallTrace.IndexToStacktrace(kidNode.stackid); + if (kidNode.nodetype == TreeNode.NodeType.Call) + { + functionId = kidStacktrace[ kidStacktrace.Length - 1 ]; + } + else if (kidNode.nodetype == TreeNode.NodeType.Allocation) + { + functionId = kidStacktrace[ 0 ]; + + } + + string name = _prevcallTrace.MakeName(kidNode); + DiffDataNode node = new DiffDataNode(name); + node.prevIncl = kidNode.data.bytesAllocated; + node.prevCalls = kidNode.data.numberOfFunctionsCalled; + + + node.prevTreenode = kidNode; + node.nodetype = (DiffDataNode.NodeType)kidNode.nodetype; + + switch(node.nodetype) + { + case DiffDataNode.NodeType.Allocation: + node.prevFunId = functionId; + break; + + case DiffDataNode.NodeType.Call: + node.prevFunId= functionId; + node.mapname = _prevcallTrace.names[functionId]; + string sig = _prevcallTrace.signatures[functionId]; + if(sig != null) + { + node.mapname += ' ' + sig; + } + break; + } + diffnodes.Add(node); + + } + } + + return diffnodes; + } + + + private ArrayList GetDiffKids(DiffDataNode parent, ArrayList currKids, ArrayList prevKids) + { + ArrayList curr = new ArrayList(); + Hashtable curr_inclOfNode = new Hashtable(); + ArrayList prev = new ArrayList(); + Hashtable prev_inclOfNode = new Hashtable(); + + ArrayList diffnodes = new ArrayList(); + for(int i = 0; i < currKids.Count; i++) + { + if( !((DiffDataNode)currKids[i]).marked) + { + DiffDataNode node = new DiffDataNode( ((DiffDataNode)currKids[i]).name); + int idx = CurrExactMatchIndex(prevKids, currKids[i] as DiffDataNode); + if(idx >=0) + { + node.currFunId = ((DiffDataNode)currKids[i]).currFunId; + node.prevFunId = ((DiffDataNode)prevKids[idx]).prevFunId; + node.mapname = ((DiffDataNode)currKids[i]).mapname; + node.currIncl = ((DiffDataNode)currKids[i]).currIncl; + node.prevIncl = ((DiffDataNode)prevKids[idx]).prevIncl; + node.diffIncl = node.currIncl - node.prevIncl; + node.currCalls = ((DiffDataNode)currKids[i]).currCalls; + node.prevCalls = ((DiffDataNode)prevKids[idx]).prevCalls; + node.diffCalls = node.currCalls - node.prevCalls; + + node.nodeId = nodeidx; + node.parentId = parent.nodeId; + node.parentname = parent.name; + node.currTreenode = ((DiffDataNode)currKids[i]).currTreenode; + node.prevTreenode = ((DiffDataNode)prevKids[idx]).prevTreenode; + node.nodetype = ((DiffDataNode)currKids[i]).nodetype; + + ((DiffDataNode)currKids[i]).marked = true; + ((DiffDataNode)prevKids[idx]).marked = true; + if(node.diffIncl != 0) + { + diffnodes.Add(node); + AddDiffTraceTableRow(diffTracetbl, node); + nodeidx++; + + } + } + else + { + long incl = ((DiffDataNode)currKids[i]).currIncl; + curr_inclOfNode[currKids[i]] = incl; + //string nm = ((DiffDataNode)currKids[i]).mapname; + //curr_inclOfNode[currKids[i]] = nm; + curr.Add(currKids[i]); + } + } + } + + for(int i = 0; i < prevKids.Count; i++) + { + if( !((DiffDataNode)prevKids[i]).marked) + { + DiffDataNode node = new DiffDataNode( ((DiffDataNode)prevKids[i]).name); + int idx = PrevExactMatchIndex(currKids, prevKids[i] as DiffDataNode); + if(idx >=0) + { + node.currFunId = ((DiffDataNode)currKids[idx]).currFunId; + node.prevFunId = ((DiffDataNode)prevKids[i]).prevFunId; + node.mapname = ((DiffDataNode)currKids[idx]).mapname; + node.currIncl = ((DiffDataNode)currKids[idx]).currIncl; + node.prevIncl = ((DiffDataNode)prevKids[i]).prevIncl; + node.diffIncl = node.currIncl - node.prevIncl; + node.currCalls = ((DiffDataNode)currKids[idx]).currCalls; + node.prevCalls = ((DiffDataNode)prevKids[i]).prevCalls; + node.diffCalls = node.currCalls - node.prevCalls; + + node.nodeId = nodeidx; + node.parentId = parent.nodeId; + node.parentname = parent.name; + node.currTreenode = ((DiffDataNode)currKids[idx]).currTreenode; + node.prevTreenode = ((DiffDataNode)prevKids[i]).prevTreenode; + node.nodetype = ((DiffDataNode)prevKids[i]).nodetype; + + ((DiffDataNode)currKids[idx]).marked = true; + ((DiffDataNode)prevKids[i]).marked = true; + if(node.diffIncl != 0) + { + diffnodes.Add(node); + AddDiffTraceTableRow(diffTracetbl, node); + nodeidx++; + + } + } + else + { + long incl = ((DiffDataNode)prevKids[i]).prevIncl; + prev_inclOfNode[prevKids[i]] = incl; + //string nm = ((DiffDataNode)prevKids[i]).mapname; + //prev_inclOfNode[prevKids[i]] = nm; + prev.Add(prevKids[i]); + } + } + } + curr.Sort(new CompareIncl(curr_inclOfNode)); + prev.Sort(new CompareIncl(prev_inclOfNode)); + for(int i = 0; i < curr.Count; i++) + { + + if( !((DiffDataNode)curr[i]).marked) + { + DiffDataNode node = new DiffDataNode( ((DiffDataNode)curr[i]).name); + int idx = FirstMatchIndex(prevKids, curr[i] as DiffDataNode); + if(idx >=0) + { + node.currFunId = ((DiffDataNode)curr[i]).currFunId; + node.prevFunId = ((DiffDataNode)prevKids[idx]).prevFunId; + node.mapname = ((DiffDataNode)curr[i]).mapname; + node.currIncl = ((DiffDataNode)curr[i]).currIncl; + node.prevIncl = ((DiffDataNode)prevKids[idx]).prevIncl; + node.diffIncl = node.currIncl - node.prevIncl; + node.currCalls = ((DiffDataNode)curr[i]).currCalls; + node.prevCalls = ((DiffDataNode)prevKids[idx]).prevCalls; + node.diffCalls = node.currCalls - node.prevCalls; + + node.nodeId = nodeidx; + node.parentId = parent.nodeId; + node.parentname = parent.name; + node.currTreenode = ((DiffDataNode)curr[i]).currTreenode; + node.prevTreenode = ((DiffDataNode)prevKids[idx]).prevTreenode; + node.nodetype = ((DiffDataNode)curr[i]).nodetype; + + ((DiffDataNode)curr[i]).marked = true; + ((DiffDataNode)prevKids[idx]).marked = true; + if(node.diffIncl != 0) + { + diffnodes.Add(node); + AddDiffTraceTableRow(diffTracetbl, node); + nodeidx++; + + } + } + else + { + node.currFunId = ((DiffDataNode)curr[i]).currFunId; + node.mapname = ((DiffDataNode)curr[i]).mapname; + node.currIncl = ((DiffDataNode)curr[i]).currIncl; + node.prevIncl = 0; + node.diffIncl = node.currIncl; + node.currCalls = ((DiffDataNode)curr[i]).currCalls; + node.prevCalls = 0; + node.diffCalls = node.currCalls; + + node.nodeId = nodeidx; + node.parentId = parent.nodeId; + node.parentname = parent.name; + node.currTreenode = ((DiffDataNode)curr[i]).currTreenode; + node.nodetype = ((DiffDataNode)curr[i]).nodetype; + ((DiffDataNode)curr[i]).marked = true; + if(node.diffIncl != 0) + { + diffnodes.Add(node); + AddDiffTraceTableRow(diffTracetbl, node); + nodeidx++; + } + } + } + + } + + for(int i = 0; i < prev.Count; i++) + { + if(!((DiffDataNode)prev[i]).marked) + { + DiffDataNode node = new DiffDataNode( ((DiffDataNode)prev[i]).name); + + // prev not exist in curr + node.prevFunId = ((DiffDataNode)prev[i]).prevFunId; + node.mapname = ((DiffDataNode)prev[i]).mapname; + node.currIncl = 0; + node.prevIncl = ((DiffDataNode)prev[i]).prevIncl; + node.diffIncl = -node.prevIncl; + node.currCalls = 0; + node.prevCalls = ((DiffDataNode)prev[i]).prevCalls; + node.diffCalls = -node.prevCalls; + + node.nodeId = nodeidx; + node.parentId = parent.nodeId; + node.parentname = parent.name; + node.prevTreenode = ((DiffDataNode)prev[i]).prevTreenode; + node.nodetype = ((DiffDataNode)prev[i]).nodetype; + + ((DiffDataNode)prev[i]).marked = true; + if(node.diffIncl != 0) + { + diffnodes.Add(node); + AddDiffTraceTableRow(diffTracetbl, node); + nodeidx++; + } + } + + } + for(int i = 0; i < currKids.Count; i++) + { + ((DiffDataNode)currKids[i]).marked = false; + } + for(int i = 0; i < prevKids.Count; i++) + { + ((DiffDataNode)prevKids[i]).marked = false; + } + + return diffnodes; + } + + + + private int CurrExactMatchIndex(ArrayList nodelst, DiffDataNode node) + { + for(int i = 0; i < nodelst.Count; i++) + { + if( ((DiffDataNode)nodelst[i]).name.Equals(node.name) && + !((DiffDataNode)nodelst[i]).marked && + (node.currIncl - ((DiffDataNode)nodelst[i]).prevIncl) == 0) + { + return i; + } + } + return -1; + } + private int PrevExactMatchIndex(ArrayList nodelst, DiffDataNode node) + { + for(int i = 0; i < nodelst.Count; i++) + { + if( ((DiffDataNode)nodelst[i]).name.Equals(node.name) && + !((DiffDataNode)nodelst[i]).marked && + (node.prevIncl - ((DiffDataNode)nodelst[i]).currIncl) == 0) + { + return i; + } + } + return -1; + } + /* private int FirstMatchIndex(ArrayList nodelst, DiffDataNode node) + { + for(int i = 0; i < nodelst.Count; i++) + { + if( ((DiffDataNode)nodelst[i]).name.Equals(node.name) && + !((DiffDataNode)nodelst[i]).marked ) + return i; + } + return -1; + }*/ + private int FirstMatchIndex(ArrayList nodelst, DiffDataNode node) + { + + int idx = -1; + long savedalloc = long.MaxValue; + long alloc = 0; + + for(int i = 0; i < nodelst.Count; i++) + { + if( ((DiffDataNode)nodelst[i]).name.Equals(node.name) && + !((DiffDataNode)nodelst[i]).marked ) + { + alloc = Math.Abs(node.currIncl - ((DiffDataNode)nodelst[i]).prevIncl); + if(alloc < savedalloc) + { + idx = i; + savedalloc = alloc; + continue; + } + } + } + + return idx; + } + + #endregion + + #region Summary table + internal void RefreshCallTreeNodes(DiffDataNode node) + { + node.IsExpanded = false; + for(int i = 0; i < node.allkids.Count; i++) + { + RefreshCallTreeNodes(node.allkids[i] as DiffDataNode); + } + + } + internal void GetAllKids(DiffDataNode root, string filter) + { + DataRow[] rKids = summaryTracetbl.Select(filter, "name asc"); + if(rKids.Length > 0) + { + root.HasKids = true; + root.depth = 0; + } + for(int i = 0; i < rKids.Length; i++) + { + DiffDataNode kidNode = Row2Node(rKids[i]); + root.allkids.Add(kidNode); + + } + } + private void BuildSummaryTable(DiffDataNode parent, int parentId, string filter) + { + depth++; + parent.depth = depth; + parent.allkids.Clear(); + parent.HasKids = false; + Hashtable kidSum = new Hashtable(); + string name = null; + + DataRow[] kidsRows = diffTracetbl.Select(filter); + for(int i = 0; i < kidsRows.Length; i++) + { + DiffDataNode sumNode = Row2Node(kidsRows[i]); + name = sumNode.mapname; + if(kidSum.ContainsKey(name)) + { + DiffDataNode updateNode = kidSum[name] as DiffDataNode; + updateNode.prevIncl += sumNode.prevIncl; + updateNode.currIncl += sumNode.currIncl; + updateNode.diffIncl = updateNode.currIncl - updateNode.prevIncl; + if(sumNode.prevIncl != 0) + { + updateNode.prevCalls++; + } + if(sumNode.currIncl != 0) + { + updateNode.currCalls++; + } + updateNode.diffCalls = updateNode.currCalls - updateNode.prevCalls; + updateNode.allkids.Add(sumNode.nodeId); + updateNode.HasKids = true; + + } + else + { + if(sumNode.prevIncl != 0) + { + sumNode.prevCalls = 1; + } + if(sumNode.currIncl != 0) + { + sumNode.currCalls = 1; + } + sumNode.parentId = parentId; + sumNode.allkids.Add(sumNode.nodeId); + sumNode.diffIncl = sumNode.currIncl - sumNode.prevIncl; + sumNode.diffCalls = sumNode.currCalls - sumNode.prevCalls; + kidSum.Add(name, sumNode); + sumNode.HasKids = false; + sumNode.depth = depth; + sumNode.nodeId = sumnodeidx; + sumnodeidx++; + } + + } + if(kidSum.Count > 0) + { + if(parent.nodetype == DiffDataNode.NodeType.Call) + { + parent.HasKids = true; + } + string diffkey = parent.mapname + parent.prevIncl + parent.currIncl + parent.diffIncl + parent.prevFunId + parent.currFunId; + //string diffkey = parent.mapname + parent.diffIncl + parent.prevFunId + parent.currFunId; + if(!diffCallTreeNodes.ContainsKey(diffkey)) + { + diffCallTreeNodes.Add(diffkey, parent); + } + + } + + + foreach(string key in kidSum.Keys) + { + DiffDataNode sumNode = kidSum[key] as DiffDataNode; + if(! (sumNode.diffIncl == 0)) + { + parent.allkids.Add(sumNode); + AddDiffTraceTableRow(summaryTracetbl, sumNode); + } + string kidFilter = getFilter(sumNode.allkids); + BuildSummaryTable(sumNode, sumNode.nodeId,kidFilter); + } + + depth--; + + } + + + private string getFilter(ArrayList kids) + { + string filter = "parentId in ("; + if(kids.Count > 1) + { + for(int i = 0; i < kids.Count-1; i++) + { + filter += kids[i].ToString() + ','; + } + filter += kids[kids.Count-1].ToString(); + } + else if(kids.Count == 1) + { + filter += kids[0].ToString(); + } + filter += ")"; + return filter; + } + internal DiffDataNode Row2Node(DataRow r) + { + string name = r[idx_name].ToString(); + DiffDataNode node = new DiffDataNode(name); + + node.mapname = r[idx_mapname].ToString(); + + node.prevIncl = int.Parse(r[idx_prevIncl].ToString()); + node.currIncl = int.Parse(r[idx_currIncl].ToString()); + node.diffIncl = int.Parse(r[idx_diffIncl].ToString()); + node.prevFunId = int.Parse(r[idx_prevFunid].ToString()); + node.currFunId = int.Parse(r[idx_currFunid].ToString()); + node.prevCalls = int.Parse(r[idx_prevCalls].ToString()); + node.currCalls = int.Parse(r[idx_currCalls].ToString()); + node.diffCalls = int.Parse(r[idx_diffCalls].ToString()); + int nodetype = int.Parse(r[idx_type].ToString()); + if(nodetype == 0) + { + node.nodetype = DiffDataNode.NodeType.Call; + } + else if(nodetype == 1) + { + node.nodetype = DiffDataNode.NodeType.Allocation; + } + else + { + node.nodetype = DiffDataNode.NodeType.AssemblyLoad; + } + + node.nodeId = int.Parse(r[idx_id].ToString()); + node.parentId = int.Parse(r[idx_parentid].ToString()); + node.depth = int.Parse(r[idx_depth].ToString()); + return node; + } + + private void MakeDiffTreceTable(DataTable tbl) + { + addTableRow(tbl, "System.Int32", "parentid"); + addTableRow(tbl, "System.String", "parentname"); + addTableRow(tbl, "System.Int32", "id"); + addTableRow(tbl, "System.String", "name"); + addTableRow(tbl, "System.String", "mapname"); + + addTableRow(tbl, "System.Int32", "prevIncl"); + addTableRow(tbl, "System.Int32", "currIncl"); + addTableRow(tbl, "System.Int32", "diffIncl"); + addTableRow(tbl, "System.Int32", "prevCalls"); + addTableRow(tbl, "System.Int32", "currCalls"); + addTableRow(tbl, "System.Int32", "diffCalls"); + addTableRow(tbl, "System.Int32", "prevFunId"); + addTableRow(tbl, "System.Int32", "currFunId"); + addTableRow(tbl, "System.Int32", "nodetype"); + addTableRow(tbl, "System.Int32", "depth"); + + + + + tbl.Columns["parentid"].DefaultValue = -1; + tbl.Columns["parentname"].DefaultValue = ""; + tbl.Columns["id"].DefaultValue = -1; + tbl.Columns["name"].DefaultValue = ""; + tbl.Columns["mapname"].DefaultValue = ""; + tbl.Columns["prevIncl"].DefaultValue = 0; + tbl.Columns["currIncl"].DefaultValue = 0; + tbl.Columns["diffIncl"].DefaultValue = 0; + tbl.Columns["prevCalls"].DefaultValue = 0; + tbl.Columns["currCalls"].DefaultValue = 0; + tbl.Columns["diffCalls"].DefaultValue = 0; + tbl.Columns["prevFunId"].DefaultValue = -1; + tbl.Columns["currFunId"].DefaultValue = -1; + tbl.Columns["nodetype"].DefaultValue = -1; + tbl.Columns["depth"].DefaultValue = 0; + + + tbl.Columns["diffIncl"].Expression = "currIncl - prevIncl"; + tbl.Columns["diffCalls"].Expression = "currCalls - prevCalls"; + //tbl.Columns["mapname"].Expression = "name"; + } + private void AddDiffTraceTableRow(DataTable tmptbl, DiffDataNode node) + { + DataRow tmpRow = tmptbl.NewRow(); + + tmpRow["parentid"] = node.parentId; + tmpRow["id"] = node.nodeId; + tmpRow["parentname"] = node.parentname; + tmpRow["name"] = node.name; + tmpRow["mapname"] = node.mapname; + tmpRow["prevIncl"] = node.prevIncl; + tmpRow["currIncl"] = node.currIncl; + tmpRow["prevCalls"] = node.prevCalls; + tmpRow["currCalls"] = node.currCalls; + tmpRow["prevFunId"] = node.prevFunId; + tmpRow["currFunId"] = node.currFunId; + if(node.nodetype == DiffDataNode.NodeType.Call) + { + tmpRow["nodetype"] = 0; + } + else if(node.nodetype == DiffDataNode.NodeType.Allocation) + { + tmpRow["nodetype"] = 1; + } + else if(node.nodetype == DiffDataNode.NodeType.AssemblyLoad) + { + tmpRow["nodetype"] = 2; + } + tmpRow["depth"] = node.depth; + tmptbl.Rows.Add(tmpRow); + + } + #endregion + + + #region EXCLUSIVE + private void ReadFile(CallTreeForm callTrace, string fileName, Hashtable FuncExcl, Hashtable TypeExcl) + { + Hashtable funcCalled = new Hashtable(); + Hashtable TypeAlloc = new Hashtable(); + Stream s = null; + ProgressForm progressForm = null; + try + { + /* log parser code (straight from the ReadNewLog.cs) */ + s = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + r = new StreamReader(s); + + progressForm = new ProgressForm(); + progressForm.Text = "Preparing call tree view"; + progressForm.Visible = true; + progressForm.setProgress(0); + progressForm.TopMost = false; + int maxProgress = (int)(r.BaseStream.Length/1024); + progressForm.setMaximum(maxProgress); + + buffer = new byte[4096]; + bufPos = 0; + bufLevel = 0; + line = 1; + StringBuilder sb = new StringBuilder(); + c = ReadChar(); + + bool found; + string assemblyName = null; + int threadid = 0, stackid = 0; + TreeNode.NodeType nodetype = TreeNode.NodeType.Call; + + while (c != -1) + { + found = false; + if ((line % 1024) == 0) + { + int currentProgress = (int)(pos/1024); + if (currentProgress <= maxProgress) + { + progressForm.setProgress(currentProgress); + //Application.DoEvents(); + } + } + lastLineStartPos = pos-1; + switch (c) + { + case -1: + break; + + // 'A' with thread identifier + case '!': + { + found = true; + c = ReadChar(); + threadid = ReadInt(); + ReadInt(); + stackid = ReadInt(); + nodetype = TreeNode.NodeType.Allocation; + if (c == -1) {found = false;} + break; + + } + + case 'C': + case 'c': + { + found = true; + c = ReadChar(); + nodetype = TreeNode.NodeType.Call; + threadid = ReadInt(); + stackid = ReadInt(); + if (c == -1) {found = false;} + break; + } + + + case 'y': + case 'Y': + { + found = true; + c = ReadChar(); + nodetype = TreeNode.NodeType.AssemblyLoad; + threadid = ReadInt(); + /* int assemblyId = */ ReadInt(); + + while (c == ' ' || c == '\t') + { + c = ReadChar(); + } + sb.Length = 0; + while (c > ' ') + { + sb.Append((char)c); + c = ReadChar(); + } + assemblyName = sb.ToString(); + break; + } + + + default: + { + // just ignore the unknown + while(c != '\n' && c != '\r') + { + c = ReadChar(); + } + break; + } + } + while (c == ' ' || c == '\t') + c = ReadChar(); + if (c == '\r') + c = ReadChar(); + if (c == '\n') + { + c = ReadChar(); + line++; + } + if(!found) + { + continue; + } + + string name = null; + string typename = null; + + int[] stacktrace = callTrace.IndexToStacktrace(stackid); + int functionId = (nodetype != TreeNode.NodeType.AssemblyLoad ? stacktrace[stacktrace.Length - 1] : 0); + switch(nodetype) + { + case TreeNode.NodeType.Allocation: + string key = null; + if( (functionId < callTrace.LogResult.callstackHistogram.readNewLog.funcName.Length )&& + ((name = callTrace.LogResult.callstackHistogram.readNewLog.funcName[functionId]) != null)) + { + if( callTrace.LogResult.callstackHistogram.readNewLog.funcSignature[functionId] != null) + { + name += ' ' + callTrace.LogResult.callstackHistogram.readNewLog.funcSignature[functionId]; + } + } + else + { + name = "NATIVE FUNCTION ( UNKNOWN ARGUMENTS )"; + } + + // function Excl + if(FuncExcl.ContainsKey(name)) + { + int alloc = (int)FuncExcl[(string)name]; + alloc += stacktrace[1]; + FuncExcl[name] = alloc; + } + else + { + FuncExcl.Add(name, stacktrace[1]); + } + + // Type Excl + if( stacktrace[0]>=0 && stacktrace[0] < callTrace.LogResult.callstackHistogram.readNewLog.typeName.Length) + { + typename = callTrace.LogResult.callstackHistogram.readNewLog.typeName[stacktrace[0]]; + } + if(typename == null) + typename = "NATIVE FUNCTION ( UNKNOWN ARGUMENTS )"; + + if(TypeExcl.ContainsKey(typename)) + { + int alloc = (int)TypeExcl[(string)typename]; + alloc += stacktrace[1]; + TypeExcl[typename] = alloc; + } + else + { + TypeExcl.Add(typename, stacktrace[1]); + } + + // Type Allocated by Excl + if(name != "NATIVE FUNCTION ( UNKNOWN ARGUMENTS )") + key = typename + '|' + functionId; + else + key = typename + '|' + 0; + if( TypeAlloc.ContainsKey(key)) + { + int alloc = (int)TypeAlloc[key]; + alloc += stacktrace[1]; + TypeAlloc[key] = alloc; + } + else + { + TypeAlloc.Add(key, stacktrace[1]); + } + + break; + case TreeNode.NodeType.Call: + if(funcCalled.ContainsKey(functionId)) + { + int calls = (int)funcCalled[functionId] + 1;; + funcCalled[functionId]= calls; + } + else + { + funcCalled.Add(functionId,1); + } + break; + } + } + + } + catch (Exception) + { + throw new Exception(string.Format("Bad format in log file {0} line {1}", fileName, line)); + } + + finally + { + progressForm.Visible = false; + progressForm.Dispose(); + if (r != null) + r.Close(); + } + } + internal int ReadChar() + { + pos++; + if (bufPos < bufLevel) + return buffer[bufPos++]; + else + return FillBuffer(); + } + + int ReadInt() + { + while (c == ' ' || c == '\t') + c = ReadChar(); + bool negative = false; + if (c == '-') + { + negative = true; + c = ReadChar(); + } + if (c >= '0' && c <= '9') + { + int value = 0; + if (c == '0') + { + c = ReadChar(); + if (c == 'x' || c == 'X') + value = ReadHex(); + } + while (c >= '0' && c <= '9') + { + value = value*10 + c - '0'; + c = ReadChar(); + } + + if (negative) + value = -value; + return value; + } + else + { + return Int32.MinValue; + } + } + int FillBuffer() + { + bufPos = 0; + bufLevel = r.BaseStream.Read(buffer, 0, buffer.Length); + if (bufPos < bufLevel) + return buffer[bufPos++]; + else + return -1; + } + int ReadHex() + { + int value = 0; + while (true) + { + c = ReadChar(); + int digit = c; + if (digit >= '0' && digit <= '9') + digit -= '0'; + else if (digit >= 'a' && digit <= 'f') + digit -= 'a' - 10; + else if (digit >= 'A' && digit <= 'F') + digit -= 'A' - 10; + else + return value; + value = value*16 + digit; + } + } + + + #endregion + + } +} diff --git a/CLRProfiler/CLRProfiler/AssemblyInfo.cs b/CLRProfiler/CLRProfiler/AssemblyInfo.cs new file mode 100644 index 0000000..1b3bd70 --- /dev/null +++ b/CLRProfiler/CLRProfiler/AssemblyInfo.cs @@ -0,0 +1,51 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified - the assembly cannot be signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. +// (*) If the key file and a key name attributes are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP - that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the file is installed into the CSP and used. +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/CLRProfiler/CLRProfiler/AttachTargetPIDForm.cs b/CLRProfiler/CLRProfiler/AttachTargetPIDForm.cs new file mode 100644 index 0000000..3163207 --- /dev/null +++ b/CLRProfiler/CLRProfiler/AttachTargetPIDForm.cs @@ -0,0 +1,144 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace CLRProfiler +{ + /// + /// Summary description for AttachTargetPIDForm. + /// + public class AttachTargetPIDForm : System.Windows.Forms.Form + { + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.TextBox PIDtextBox; + private System.Windows.Forms.Label label1; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public AttachTargetPIDForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + } + + [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "CLRProfiler.exe is a stand-alone tool, not a library.")] + public int GetPID() + { + int pid = 0; + try + { + pid = Int32.Parse(PIDtextBox.Text); + Process.GetProcessById(pid); + } + catch (Exception e) + { + MessageBox.Show( string.Format("The process ID ({0}) is not valid : {1} ", PIDtextBox.Text, e.Message) ); + pid = 0; + } + return pid; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (components != null) + { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.cancelButton = new System.Windows.Forms.Button(); + this.okButton = new System.Windows.Forms.Button(); + this.PIDtextBox = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // cancelButton + // + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(168, 104); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(75, 23); + this.cancelButton.TabIndex = 2; + this.cancelButton.Text = "Cancel"; + // + // okButton + // + this.okButton.DialogResult = System.Windows.Forms.DialogResult.Yes; + this.okButton.Location = new System.Drawing.Point(27, 104); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(88, 23); + this.okButton.TabIndex = 1; + this.okButton.Text = "OK"; + // + // PIDtextBox + // + this.PIDtextBox.Location = new System.Drawing.Point(127, 44); + this.PIDtextBox.Name = "PIDtextBox"; + this.PIDtextBox.Size = new System.Drawing.Size(133, 20); + this.PIDtextBox.TabIndex = 0; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(19, 47); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(96, 13); + this.label1.TabIndex = 9; + this.label1.Text = "Attach Target PID:"; + // + // AttachTargetPIDForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(288, 163); + this.Controls.Add(this.label1); + this.Controls.Add(this.PIDtextBox); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.okButton); + this.Name = "AttachTargetPIDForm"; + this.Text = "Attach Target"; + this.ResumeLayout(false); + this.PerformLayout(); + this.AcceptButton = this.okButton; + this.CancelButton = this.cancelButton; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + + } + + #endregion + + } +} diff --git a/CLRProfiler/CLRProfiler/BitReader.cs b/CLRProfiler/CLRProfiler/BitReader.cs new file mode 100644 index 0000000..5eb10e6 --- /dev/null +++ b/CLRProfiler/CLRProfiler/BitReader.cs @@ -0,0 +1,88 @@ +/* ==++== + * + * Copyright (c) Microsoft Corporation. All rights reserved. + * + * ==--== + * + * Class: BitReader + * + * Purpose: Bitstream parser + */ + +using System; +using System.IO; + +namespace CLRProfiler +{ + internal class BitReader + { + private BinaryReader fp; + + private int incurr; + private ulong current; + + internal ulong ReadBits(int numBits) + { + ulong r; + int haveBits = 64 - incurr; + int remains = (numBits - haveBits); + if(haveBits >= numBits) + { + incurr += numBits; + r = (current >> (-remains)) & ((1ul << numBits) - 1); + } + else + { + r = (current & ((1ul << haveBits) - 1)) << remains; + current = (ulong)fp.ReadInt64(); + r |= (current >> (64 - remains)) & ((1ul << remains) - 1); + incurr = remains; + } + + return r; + } + + internal BitReader(Stream s) + { + fp = new BinaryReader(s); + current = 0; + if (fp.BaseStream.Length != 0) + { + current = (ulong)fp.ReadInt64(); + } + incurr = 0; + } + + internal long Length + { + get + { + return fp.BaseStream.Length * 8; + } + } + + internal long Position + { + get + { + return fp.BaseStream.Position * 8 + incurr; + } + set + { + fp.BaseStream.Position = 8 * (value / 64); + current = (ulong)fp.ReadInt64(); + incurr = 0; + if(value % 64 != 0) + { + ReadBits((int)(value % 64)); + } + } + } + + internal void Close() + { + fp.Close(); + fp = null; + } + } +} diff --git a/CLRProfiler/CLRProfiler/BitWriter.cs b/CLRProfiler/CLRProfiler/BitWriter.cs new file mode 100644 index 0000000..9e4b74c --- /dev/null +++ b/CLRProfiler/CLRProfiler/BitWriter.cs @@ -0,0 +1,82 @@ +/* ==++== + * + * Copyright (c) Microsoft Corporation. All rights reserved. + * + * ==--== + * + * Class: BitWriter + * + * Description: Bitstream writer + */ + +using System; +using System.IO; + +namespace CLRProfiler +{ + internal class BitWriter + { + private int position, incurr; + + private BinaryWriter bw; + + private ulong[] buffer; + + private long bitsRecorded; + + internal BitWriter(Stream o) + { + bw = new BinaryWriter(o); + buffer = new ulong[4096]; + buffer[0] = 0; + position = incurr = 0; + bitsRecorded = 0; + } + + internal void WriteBits(ulong bits, int numBits) + { + int space = 64 - incurr; + if(space >= numBits) + { + buffer[position] <<= numBits; + buffer[position] |= (bits & ((1ul << numBits) - 1)); + incurr += numBits; + } + else + { + buffer[position] <<= space; + buffer[position] |= (bits & ((1ul << numBits) - 1)) >> (numBits - space); + if(++position >= 4096) + { + incurr = 0; + Flush(); + } + incurr = numBits - space; + buffer[position] = bits & ((1ul << incurr) - 1); + } + bitsRecorded += numBits; + } + + internal void Flush() + { + if(incurr != 0) + { + buffer[position++] <<= 64 - incurr; + } + // how to write an array of longs faster? + for(int i = 0; i < position; i++) + { + bw.Write(buffer[i]); + } + position = incurr = 0; + } + + internal long Position + { + get + { + return bitsRecorded; + } + } + } +} diff --git a/CLRProfiler/CLRProfiler/CLRProfiler.csproj b/CLRProfiler/CLRProfiler/CLRProfiler.csproj new file mode 100644 index 0000000..96879bb --- /dev/null +++ b/CLRProfiler/CLRProfiler/CLRProfiler.csproj @@ -0,0 +1,443 @@ + + + + true + ..\Win32\Debug\ + DEBUG;TRACE + 0 + true + x86 + true + GlobalSuppressions.cs + AllRules.ruleset + true + false + + + true + ..\x64\Release\ + TRACE + 0 + true + true + x64 + + + true + GlobalSuppressions.cs + Migrated rules for CLRProfiler.ruleset + true + true + false + + + Local + 8.0.50727 + 2.0 + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769} + Debug + AnyCPU + + + + + CLRProfiler + + + JScript + Grid + IE50 + false + Exe + CLRProfiler + OnBuildSuccess + + + + + + + v4.0 + 2.0 + + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + true + ..\x64\Debug\ + DEBUG;TRACE + 0 + true + x64 + + + true + GlobalSuppressions.cs + Migrated rules for CLRProfiler.ruleset + true + true + false + + + true + ..\Win32\Release\ + TRACE + 0 + true + true + x86 + + + true + GlobalSuppressions.cs + Migrated rules for CLRProfiler.ruleset + true + true + false + + + + System + + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + Form + + + Code + + + Code + + + Form + + + Code + + + Code + + + Form + + + Form + + + Form + + + DevLicenseWarning.cs + + + Form + + + Code + + + Component + + + Code + + + Form + + + Form + + + HelpForm.cs + + + Form + + + + Form + + + WindowsStoreAppChooserForm.cs + + + Form + + + Form + + + Form + + + Form + + + Form + + + Form + + + Code + + + Code + + + Form + + + Form + + + Form + + + Form + + + Code + + + Code + + + Code + + + Component + + + Form + + + Code + + + Code + + + Code + + + Form + + + Code + + + Form + + + Form + + + Form + + + Form + + + Form + + + Component + + + Code + + + Form + + + Form + + + Form + + + Form + + + AgeHistogram.cs + Designer + + + CallTreeForm.cs + Designer + + + CommentRangeForm.cs + Designer + + + DevLicenseWarning.cs + + + DiffCallTreeForm.cs + Designer + + + DiffTreeListView.cs + Designer + + + FilterForm.cs + Designer + + + MainForm.cs + Designer + + + WindowsStoreAppChooserForm.cs + + + ProgressForm.cs + Designer + + + SetParameterForm.cs + Designer + + + FindRoutineForm.cs + Designer + + + FunctionFilter.cs + Designer + + + FunctionFind.cs + Designer + + + GraphViewForm.cs + Designer + + + HistogramViewForm.cs + Designer + + + KillProcessForm.cs + Designer + + + ListViewer.cs + Designer + + + PlacedToolTip.cs + Designer + + + ProfileServiceForm.cs + Designer + + + ReportForm.cs + Designer + + + SaveFileForm.cs + Designer + + + SelectColumns.cs + Designer + + + SortAndHighlightSelector.cs + Designer + + + SummaryForm.cs + Designer + + + TimeLineViewForm.cs + Designer + + + TreeListView.cs + Designer + + + ViewByAddressForm.cs + Designer + + + ViewCommentsForm.cs + Designer + + + ViewFilter.cs + Designer + + + WaitingForConnectionForm.cs + Designer + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + + + \ No newline at end of file diff --git a/CLRProfiler/CLRProfiler/CLRProfiler.sln b/CLRProfiler/CLRProfiler/CLRProfiler.sln new file mode 100644 index 0000000..4a5c534 --- /dev/null +++ b/CLRProfiler/CLRProfiler/CLRProfiler.sln @@ -0,0 +1,36 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLRProfilerControl", "CLRProfilerControl\CLRProfilerControl.csproj", "{4A080E01-C48D-4E95-B8E7-280C32BCDAA9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLRProfiler", "CLRProfiler\CLRProfiler.csproj", "{147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProfilerOBJ", "ProfilerOBJ\ProfilerOBJ.vcproj", "{A1BCB61E-359F-46C8-89E1-26CD7E356880}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B100318D-FBB1-4ABC-ADC2-5451F1E748C5}" + ProjectSection(SolutionItems) = postProject + Readme.txt = Readme.txt + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Default = Debug|Default + Release|Default = Release|Default + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Debug|Default.ActiveCfg = Debug|Any CPU + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Debug|Default.Build.0 = Debug|Any CPU + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Release|Default.ActiveCfg = Release|Any CPU + {4A080E01-C48D-4E95-B8E7-280C32BCDAA9}.Release|Default.Build.0 = Release|Any CPU + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Debug|Default.ActiveCfg = Debug|Any CPU + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Debug|Default.Build.0 = Debug|Any CPU + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Release|Default.ActiveCfg = Release|Any CPU + {147EF91E-C9E4-4E8E-B5E9-5D99D8DB3769}.Release|Default.Build.0 = Release|Any CPU + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Debug|Default.ActiveCfg = Debug|Win32 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Debug|Default.Build.0 = Debug|Win32 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Release|Default.ActiveCfg = Release|Win32 + {A1BCB61E-359F-46C8-89E1-26CD7E356880}.Release|Default.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/CLRProfiler/CLRProfiler/CallTreeForm.cs b/CLRProfiler/CLRProfiler/CallTreeForm.cs new file mode 100644 index 0000000..5a8047b --- /dev/null +++ b/CLRProfiler/CLRProfiler/CallTreeForm.cs @@ -0,0 +1,2801 @@ +/* ==++== + * + * Copyright (c) Microsoft Corporation. All rights reserved. + * + * ==--== + * + * Class: CallTreeForm and many small data-holder classes and structures + * + * Description: Call tree view interface and all internal logic + */ + +using System; +using System.IO; +using System.Text; +using System.Drawing; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.ComponentModel; +using System.Windows.Forms; +using Microsoft.Win32; + +namespace CLRProfiler +{ + /// + /// Summary description for CallTreeForm. + /// + internal class CallTreeForm : System.Windows.Forms.Form, IComparer, ITreeOwner + { + /* info given from the outside */ + private string logFileName; + private ReadLogResult logResult; + internal string[] names, types, signatures; + + /* everything about the backing store */ + private Stream backingStore; + private string backingStoreFileName; + private BitReader reader; + private BitWriter writer; + + private Font defaultFont; + + /* configuration information */ + private RegistryKey rkProfiler; + private Rectangle formRect = new Rectangle( -1, -1, -1, -1 ); + private int splitterX = -1; + + /* sorting info for the stackview control */ + private int sortCol = -1; + private int prevSortCol = -1; + private bool fReverseSort = false; + private int subtreeline = -1; + private int contextSelection; + + /*flag for Compare view*/ + internal bool forCompare = false; + + /* various collections */ + internal struct GlobalCallStats + { + internal bool calledAlready; + internal int timesCalled; + internal int totalBytesAllocated; + internal int totalFunctionsCalled; + internal int totalNewFunctionsBroughtIn; + } + + internal struct GlobalAllocStats + { + internal int timesAllocated; + internal int totalBytesAllocated; + } + + internal class SortingBehaviour + { + internal int sortingOrder; + internal int counterId; + } + + internal class ThreadState + { + internal int[] prevStackTrace; + internal int prevStackLen; + internal int prevDepth; + internal ArrayList stack; + internal ArrayList queuedNodes; + internal SortedList functions; + internal TreeListView callTreeView; + }; + + internal class ViewState + { + internal SortingBehaviour sort, highlight; + internal bool showCalls, showAllocs, showAssemblies; + + internal ViewState(SortingBehaviour in_sort, SortingBehaviour in_highlight) + { + sort = in_sort; + highlight = in_highlight; + + showCalls = showAllocs = showAssemblies = true; + } + }; + + internal struct FnViewFilter + { + internal TreeNode.NodeType nodetype; + internal int functionId; + + internal FnViewFilter( TreeNode.NodeType Nodetype, int FunctionId) + { + nodetype = Nodetype; + functionId = FunctionId; + } + }; + + /* threads */ + private int firstThread; + private Dictionary threads; + + /* cache of display information about the current thread */ + private int currentThreadId; + private bool manyThreads; + private ViewState viewState; + internal TreeListView callTreeView; + + /* random stuff */ + private Label threadIDLabel; + private ComboBox threadIDList; + private int previousSplitterLocation; + private int firstNewStack; + + /* global stats */ + private GlobalCallStats[] globalCallStats; + private GlobalAllocStats[] globalAllocStats; + + /* some lookup info about the assemblies */ + private ArrayList assemblyNames; + private Hashtable assemblyNameToIdMap; + + /* filters */ + private FnViewFilter[] filterInclude; + private FnViewFilter[] filterExclude; + private bool fShowSubtree; + + + internal ReadLogResult LogResult + { + get {return logResult;} + } + internal GlobalCallStats[] CallStats + { + get {return globalCallStats;} + } + + internal GlobalAllocStats[] AllocStats + { + get {return globalAllocStats;} + } + + + /* controls */ + private System.Windows.Forms.Button allFunctionsButton; + private System.Windows.Forms.Panel controlCollection; + private System.Windows.Forms.Button allAllocationsButton; + private System.Windows.Forms.ListView stackView; + private System.Windows.Forms.Splitter splitter; + private System.Windows.Forms.TabControl tabs; + private System.Windows.Forms.MainMenu mainMenu1; + private System.Windows.Forms.MenuItem menuItem1; + private System.Windows.Forms.MenuItem selectColumns; + + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + /* for dumping debugging information */ + // StreamWriter log; + + /* parsing code (straight from the ReadNewLog.cs) */ + StreamReader r; + byte[] buffer; + int c; + int line; + long pos; + long lastLineStartPos; + int bufPos; + private System.Windows.Forms.MenuItem menuItem2; + private System.Windows.Forms.MenuItem menuItem3; + private System.Windows.Forms.MenuItem menuItem4; + private System.Windows.Forms.MenuItem menuItem5; + private System.Windows.Forms.MenuItem menuItem6; + private System.Windows.Forms.MenuItem menuItem7; + private System.Windows.Forms.MenuItem menuItemShowFuture; + private System.Windows.Forms.MenuItem menuItemCopyStack; + int bufLevel; + + /* */ + internal int ReadChar() + { + pos++; + if (bufPos < bufLevel) + return buffer[bufPos++]; + else + return FillBuffer(); + } + + int FillBuffer() + { + bufPos = 0; + bufLevel = r.BaseStream.Read(buffer, 0, buffer.Length); + if (bufPos < bufLevel) + return buffer[bufPos++]; + else + return -1; + } + + int ReadHex() + { + int value = 0; + while (true) + { + c = ReadChar(); + int digit = c; + if (digit >= '0' && digit <= '9') + digit -= '0'; + else if (digit >= 'a' && digit <= 'f') + digit -= 'a' - 10; + else if (digit >= 'A' && digit <= 'F') + digit -= 'A' - 10; + else + return value; + value = value*16 + digit; + } + } + + int ReadInt() + { + while (c == ' ' || c == '\t') + c = ReadChar(); + bool negative = false; + if (c == '-') + { + negative = true; + c = ReadChar(); + } + if (c >= '0' && c <= '9') + { + int value = 0; + if (c == '0') + { + c = ReadChar(); + if (c == 'x' || c == 'X') + value = ReadHex(); + } + while (c >= '0' && c <= '9') + { + value = value*10 + c - '0'; + c = ReadChar(); + } + + if (negative) + value = -value; + return value; + } + else + { + return Int32.MinValue; + } + } + + int ForcePosInt() + { + int value = ReadInt(); + if (value >= 0) + return value; + else + throw new Exception(string.Format("Bad format in log file {0} line {1}", logFileName, line)); + } + /* */ + + private Column AddColumn(TreeListView treeView, int counterId) + { + Column c = treeView.AddColumn(new ColumnInformation(counterId, + Statistics.GetCounterName(counterId), + ColumnInformation.ColumnTypes.String), + 60); + if(Statistics.IsInclusive(counterId)) + { + c.Font = new Font(c.Font, FontStyle.Bold); + } + return c; + } + + internal int[] IndexToStacktrace(int stackid) + { + if(stackid < 0) + { + int[] fakeStackTrace = new int[1 - stackid]; + fakeStackTrace[0] = 0; + for(int i = 1; i < 1 - stackid; i++) + { + fakeStackTrace[i] = -1; + } + return fakeStackTrace; + } + else + { + int[] stackTrace = logResult.callstackHistogram.readNewLog.stacktraceTable.IndexToStacktrace(stackid); + if (stackTrace == null) + { + stackTrace = new int[2]; + stackTrace[0] = 0; + stackTrace[1] = 0; + } + return stackTrace; + } + } + + + + + + internal CallTreeForm(string in_logFileName, ReadLogResult in_result, bool forCompare) + { + this.forCompare = forCompare; + + logFileName = in_logFileName; + logResult = in_result; + names = logResult.callstackHistogram.readNewLog.funcName; + types = logResult.callstackHistogram.readNewLog.typeName; + signatures = logResult.callstackHistogram.readNewLog.funcSignature; + + filterInclude = new FnViewFilter[] { new FnViewFilter(TreeNode.NodeType.Call, -1), new FnViewFilter(TreeNode.NodeType.Call, -1) }; + filterExclude = new FnViewFilter[] { new FnViewFilter(TreeNode.NodeType.Call, -1), new FnViewFilter(TreeNode.NodeType.Call, -1) }; + + fShowSubtree = false; + firstNewStack = -1; + + GetConfiguration(); + + InitForm( true ); + } + + + + + + internal CallTreeForm(string in_logFileName, ReadLogResult in_result) + { + // open debugging log file + // log = new StreamWriter("test.blog"); + + logFileName = in_logFileName; + logResult = in_result; + names = logResult.callstackHistogram.readNewLog.funcName; + types = logResult.callstackHistogram.readNewLog.typeName; + signatures = logResult.callstackHistogram.readNewLog.funcSignature; + + filterInclude = new FnViewFilter[] { new FnViewFilter(TreeNode.NodeType.Call, -1), new FnViewFilter(TreeNode.NodeType.Call, -1) }; + filterExclude = new FnViewFilter[] { new FnViewFilter(TreeNode.NodeType.Call, -1), new FnViewFilter(TreeNode.NodeType.Call, -1) }; + + fShowSubtree = false; + firstNewStack = -1; + + GetConfiguration(); + + InitForm( true ); + } + + private void InitForm( bool fFirstTime ) + { + Controls.Clear(); + if (controlCollection != null) + { + controlCollection.Controls.Clear(); + } + InitializeComponent(); + if(!forCompare) + { + /* extract font for the call tree */ + defaultFont = MainForm.instance.font; + stackView.Font = defaultFont; + //stackView.HeaderStyle = ColumnHeaderStyle.Nonclickable; + menuItemShowFuture.Checked = fShowSubtree; + + ResizeForm(); + } + + + /* intialize backing store */ + backingStoreFileName = Path.GetTempFileName(); + backingStore = new FileStream(backingStoreFileName, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite); + writer = new BitWriter(backingStore); + pos = 0; + + threads = new Dictionary(); + globalCallStats = new GlobalCallStats[1 + names.Length]; + globalAllocStats = new GlobalAllocStats[1 + types.Length]; + + assemblyNames = new ArrayList(); + assemblyNameToIdMap = new Hashtable(); + + /* compute everything about the run */ + if(!BuildTreeAndComputeStats()) + { + Dispose(); + return; + } + + backingStore.Position = 0; + reader = new BitReader(backingStore); + + manyThreads = (threads.Count > 5); + + if(manyThreads) + { + threadIDLabel.Location = new System.Drawing.Point(8, 12); + threadIDLabel.Name = "threadIDLabel"; + threadIDLabel.Size = new System.Drawing.Size(64, 16); + threadIDLabel.TabIndex = 2; + threadIDLabel.Text = "Thread ID"; + + threadIDList.Location = new System.Drawing.Point(72, 10); + threadIDList.Name = "threadIDList"; + threadIDList.TabIndex = 0; + threadIDList.SelectedValueChanged += new EventHandler(threadIDList_SelectedValueChanged); + + Controls.Add(threadIDList); + Controls.Add(threadIDLabel); + } + else + { + tabs = new TabControl(); + tabs.Dock = System.Windows.Forms.DockStyle.Left; + tabs.Font = defaultFont; + tabs.Size = new System.Drawing.Size(332, 108); + tabs.TabIndex = 0; + tabs.SelectedIndexChanged += new EventHandler(TabsSelectedIndexChanged); + + controlCollection.Controls.Add(tabs); + } + + foreach(int e in threads.Keys) + { + TabPage page = new TabPage(e.ToString()); + page.BorderStyle = BorderStyle.None; + + TreeListView treeView = new TreeListView(this); + treeView.Dock = DockStyle.Fill; + treeView.Font = defaultFont; + + /* initial sorting and highlighting behaviour: + * 1) sort in order of execution, + * 2) highlight the one that allocated the most */ + SortingBehaviour sort = new SortingBehaviour(); + SortingBehaviour highlight = new SortingBehaviour(); + sort.sortingOrder = highlight.sortingOrder = -1; + sort.counterId = -1; + highlight.counterId = 2; + + /* add columns */ + treeView.AddColumn(new ColumnInformation(-1, "Function name", ColumnInformation.ColumnTypes.Tree), 250); + foreach(int counter in Statistics.DefaultCounters) + { + AddColumn(treeView, counter); + } + + treeView.ColumnClick += new EventHandler(SortOn); + treeView.SelectedIndexChanged += new EventHandler(ShowCurrentStack); + + treeView.TokenObject = new ViewState(sort, highlight); + treeView.Root = (TreeNode)((ThreadState)threads[e]).stack[0]; + + if(manyThreads) + { + threadIDList.Items.Add(e); + ((ThreadState)threads[e]).callTreeView = treeView; + treeView.Visible = false; + treeView.Size = new System.Drawing.Size(332, 108); + controlCollection.Controls.Add(treeView); + } + else + { + page.Controls.Add(treeView); + tabs.Controls.Add(page); + if(e == firstThread) + { + tabs.SelectedTab = null; + tabs.SelectedTab = page; + } + } + } + + + if(manyThreads) + { + currentThreadId = -1; + threadIDList.SelectedItem = firstThread; + } + + CallTreeForm_Resize(null, null); + + + if (splitterX != -1) + { + splitter.SplitPosition = splitterX; + } + + previousSplitterLocation = splitter.Location.X; + if(!forCompare) + { + Visible = true; + + + // Create a blank context menu for the stackview control. We'll fill it in when the user right clicks + ContextMenu contextMenu = new ContextMenu(); + stackView.ContextMenu = contextMenu; + } + + } + + private void SetcallTreeView() + { + if (tabs.SelectedTab == null) + return; + + foreach(Control c in tabs.SelectedTab.Controls) + { + TreeListView v = null; + try + { + v = (TreeListView)c; + } + catch + { + /* not interested in exceptions */ + } + + if(v != null) + { + callTreeView = v; + viewState = (ViewState)v.TokenObject; + ShowCurrentStack(null, null); + return; + } + } + Debug.Fail("Cannot find tree view on the tab page"); + } + /* show the stack and/or some other info */ + private void ShowCurrentStack(object obj, EventArgs args) + { + if(callTreeView == null) + { + SetcallTreeView(); + } + TreeNode node = (TreeNode)callTreeView.SelectedItem; + if(node == null) + { + return; + } + + int stackid = node.stackid; + if(stackid < 0) + { + return; + } + + stackView.ListViewItemSorter = null; + stackView.Items.Clear(); + + int[] stacktrace = IndexToStacktrace(stackid); + + /* show stack trace */ + subtreeline = -1; + for(int i = (node.nodetype == TreeNode.NodeType.Allocation ? 2 : 0); i < stacktrace.Length; i++) + { + int functionId = stacktrace[i]; + GlobalCallStats s = globalCallStats[functionId]; + + string[] subitems = new string[] + { + names[functionId], + s.timesCalled.ToString(), + s.totalBytesAllocated.ToString(), + s.totalFunctionsCalled.ToString(), + s.totalNewFunctionsBroughtIn.ToString() + }; + stackView.Items.Add(new ListViewItem(subitems)); + } + + /* build and show child subtree */ + if (node.nodetype == TreeNode.NodeType.Call && fShowSubtree) + { + SortedList fns = new SortedList(); + GetChildren( node, fns ); + + if (fns.Count > 0 ) + { + // Add a separator + string functionName; + + if (stacktrace == null || stacktrace.Length == 0) + { + functionName = "All"; + } + else + { + functionName = names[stacktrace[stacktrace.Length - 1]]; + } + + subtreeline = stackView.Items.Count; + stackView.Items.Add( "<-------- Subtree of " + functionName + " --------> " ); + } + + IDictionaryEnumerator enumFns = fns.GetEnumerator(); + + while(enumFns.MoveNext()) + { + GlobalCallStats s = (GlobalCallStats)enumFns.Value; + int fid = (int)enumFns.Key; + string[] subitems = new string[] + { + fid > 0 ? names[fid] : types[-fid], + s.timesCalled.ToString(), + s.totalBytesAllocated.ToString(), + s.totalFunctionsCalled.ToString(), + s.totalNewFunctionsBroughtIn.ToString() + }; + + ListViewItem item = new ListViewItem(subitems); + if (fid < 0) + { + // Allocation item + item.ForeColor = Color.Green; + } + + stackView.Items.Add(item); + } + } + + /* and optionally some global info about the allocated object */ + if(node.nodetype == TreeNode.NodeType.Allocation) + { + int typeId = stacktrace[0]; + GlobalAllocStats s = globalAllocStats[typeId]; + string[] subitems = new string[] + { + types[typeId], // "Name" + s.timesAllocated.ToString(), + s.totalBytesAllocated.ToString() + }; + ListViewItem item = new ListViewItem(subitems); + item.ForeColor = Color.Green; + stackView.Items.Add(item); + } + + /* [re]set the columns if necessary */ + if(stackView.Columns.Count != 5 && node.nodetype != TreeNode.NodeType.Allocation) + { + stackView.Columns.Clear(); + stackView.Columns.Add("Name", 150, HorizontalAlignment.Left); + stackView.Columns.Add("Times Called", 60, HorizontalAlignment.Left); + stackView.Columns.Add("Bytes Allocated", 60, HorizontalAlignment.Left); + stackView.Columns.Add("Functions Called", 60, HorizontalAlignment.Left); + stackView.Columns.Add("New Functions", 60, HorizontalAlignment.Left); + } + if(stackView.Columns.Count != 4 && node.nodetype == TreeNode.NodeType.Allocation) + { + stackView.Columns.Clear(); + stackView.Columns.Add("Name", 150, HorizontalAlignment.Left); + stackView.Columns.Add("Times Called/Allocated", 100, HorizontalAlignment.Left); + stackView.Columns.Add("Bytes", 60, HorizontalAlignment.Left); + } + } + + private bool GetChildren( TreeNode node, SortedList fns ) + { + int[] rootStacktrace = IndexToStacktrace(node.stackid); + int rootStackLength = rootStacktrace.Length; + int prevStackLength = rootStackLength; + int [] kidStacktrace; + int functionId = 0; + + try + { + ArrayList kids = FetchKids( null, node ); + foreach( TreeNode kidNode in kids ) + { + GlobalCallStats s; + bool fAddNode = false; + + kidStacktrace = IndexToStacktrace( kidNode.stackid ); + if (kidNode.nodetype == TreeNode.NodeType.Call) + { + functionId = kidStacktrace[ kidStacktrace.Length - 1 ]; + fAddNode = true; + } + else if (kidNode.nodetype == TreeNode.NodeType.Allocation) + { + functionId = -kidStacktrace[ 0 ]; + fAddNode = true; + } + + if (fAddNode) + { + if ( !fns.ContainsKey( functionId )) + { + s = new GlobalCallStats(); + s.timesCalled = 1; + s.totalBytesAllocated = (int)kidNode.data.bytesAllocated; + s.totalFunctionsCalled = (int)kidNode.data.numberOfFunctionsCalled; + s.totalNewFunctionsBroughtIn = (int)kidNode.data.numberOfNewFunctionsBroughtIn; + } + else + { + s = (GlobalCallStats)fns[functionId]; + s.timesCalled++; + s.totalBytesAllocated += (int)kidNode.data.bytesAllocated; + s.totalFunctionsCalled += (int)kidNode.data.numberOfFunctionsCalled; + s.totalNewFunctionsBroughtIn += (int)kidNode.data.numberOfNewFunctionsBroughtIn; + } + + GetChildren( kidNode, fns ); + fns[functionId] = s; + } + } + } + catch + { + /* exceptions are no good */ + MessageBox.Show(this, "Error getting subtree", "Failure"); + return false; + } + + return true; + } + + public CallTreeForm.FnViewFilter[] GetIncludeFilters() + { + return filterInclude; + } + + public CallTreeForm.FnViewFilter[] GetExcludeFilters() + { + return filterExclude; + } + + public void SetIncludeFilters( CallTreeForm.FnViewFilter[] filters) + { + filterInclude = filters; + } + + public void SetExcludeFilters( CallTreeForm.FnViewFilter[] filters) + { + filterExclude = filters; + } + + internal int GetMaxFnId() + { + return names.Length - 1; + } + + private int GetFunctionId( string functionName ) + { + int i; + + for( i = 0; i < names.Length; i++ ) + { + if (names[i] == functionName ) + { + return i; + } + } + + // Function not found + return -1; + + } + + private int GetTypeId( string typeName ) + { + int i; + + for( i = 0; i < types.Length; i++ ) + { + if (types[i] == typeName ) + { + return i; + } + } + + // Type not found + return -1; + } + + /* construct a human-readable name for a function call */ + public string MakeNameForFunction(int functionId) + { + if(functionId < 0) + { + return "STUB FUNCTION (for calls before profiling was enabled)"; + } + + string res = ""; + string name = names[functionId]; + string signature = signatures[functionId]; + if (name == null) + { + return null; + } + + if(name == "NATIVE") + { + /* transition into the unmanaged code */ + return name + ' ' + signature; + } + else + { + /* something we can actually make sense of */ + int argumentsStart = signature.IndexOf('('); + if(argumentsStart != -1) + { + /* parse (beautify) the strings */ + string arguments = signature.Substring(argumentsStart).Trim(); + string[] argv = arguments.Split(" ".ToCharArray()); + + res = signature.Substring(0, argumentsStart).Trim() + ' ' + name.Trim(); + for(int i = 0; i < argv.Length; i++) + { + if(i != 0) + { + res += ", "; + } + res += argv[i]; + } + } + else + { + res = name + ' ' + signature; + } + } + + return res; + } + + public string MakeNameForAllocation( int typeId, int bytes) + { + string res; + + if (types[typeId] == null) + return null; + + if (bytes == 0) + { + res = types[typeId]; + } + else + { + res = string.Format("{0} ({1} bytes)", types[typeId], bytes); + } + return res; + } + + /* make a name for either call or allocation */ + internal string MakeName(TreeNode n) + { + if(n.nodetype == TreeNode.NodeType.AssemblyLoad) + { + return "[assembly] " + (string)assemblyNames[n.nameId]; + } + + /* other special cases */ + if(n.stackid == 0) + { + return "NATIVE FUNCTION (UNKNOWN ARGUMENTS)"; + } + else if(n.stackid < 0) + { + return "STUB FUNCTION (for calls before profiling was enabled)"; + } + + int[] stacktrace = IndexToStacktrace(n.stackid); + + string res = null; + switch(n.nodetype) + { + case TreeNode.NodeType.Allocation: + res = MakeNameForAllocation( stacktrace[0], stacktrace[1]); + break; + + case TreeNode.NodeType.Call: + int functionId = stacktrace[stacktrace.Length - 1]; + res = MakeNameForFunction(functionId); + break; + } + return res; + } + + /* Get the id of the selected allocation or call node */ + public int GetNodeId( TreeNodeBase node ) + { + TreeNode n = (TreeNode)node; + int id = -1; + + if(n.nodetype == TreeNode.NodeType.AssemblyLoad) + { + return id; + } + + /* other special cases */ + if(n.stackid <= 0) + { + return id; + } + + int[] stacktrace = IndexToStacktrace(n.stackid); + + switch(n.nodetype) + { + case TreeNode.NodeType.Allocation: + id = stacktrace[0]; + break; + + case TreeNode.NodeType.Call: + id = stacktrace[stacktrace.Length - 1]; + break; + } + return id; + } + + /* get the current function given the stack trace */ + internal int GetFunctionIdFromStackId(int stackid) + { + if(stackid == -1) + { + return -1; + } + int[] stacktrace = IndexToStacktrace(stackid); + return stacktrace[stacktrace.Length - 1]; + } + + /* record call to a function */ + void EnterFunction(SortedList functions, int functionId) + { + int index = functions.IndexOfKey(functionId); + if(index == -1) + { + functions.Add(functionId, 1); + } + else + { + /* if in the list, add 1 to its counter (need to keep keys unique) */ + functions.SetByIndex(index, 1 + (int)functions.GetByIndex(index)); + } + } + + /* record leaving of a function */ + void LeaveFunction(SortedList functions, int functionId) + { + int index = functions.IndexOfKey(functionId); + if(index != -1) + { + int newValue = (int)functions.GetByIndex(index) - 1; + if(newValue <= 0) + { + functions.RemoveAt(index); + } + else + { + functions.SetByIndex(index, newValue); + } + } + } + + /* incorporate the information computed about the kid (k) into its parent (r) */ + void UpdateStats(object r, object k) + { + TreeNode root = (TreeNode)r; + TreeNode kid = (TreeNode)k; + + root.data.bytesAllocated += kid.data.bytesAllocated; + root.data.bytesAllocatedByKids += kid.data.bytesAllocated; + + root.data.numberOfFunctionsCalled += (kid.nodetype == TreeNode.NodeType.Call ? 1 : 0) + kid.data.numberOfFunctionsCalled; + root.data.numberOfFunctionsCalledByKids += kid.data.numberOfFunctionsCalledByKids; + + root.data.numberOfUnmanagedTransitions += (kid.isunmanaged ? 1 : 0) + kid.data.numberOfUnmanagedTransitions; + root.data.numberOfUnmanagedTransitionsByKids += kid.data.numberOfUnmanagedTransitions; + + root.data.numberOfAssembliesLoaded += (kid.nodetype == TreeNode.NodeType.AssemblyLoad ? 1 : 0) + kid.data.numberOfAssembliesLoaded; + root.data.numberOfAssembliesLoadedByKids += kid.data.numberOfAssembliesLoaded; + + root.data.numberOfObjectsAllocated += (kid.nodetype == TreeNode.NodeType.Allocation ? 1 : 0) + kid.data.numberOfObjectsAllocated; + root.data.numberOfObjectsAllocatedByKids += kid.data.numberOfObjectsAllocated; + } + + /* read kids of a node from the backing store */ + public ArrayList FetchKids(object tokenObject, TreeNodeBase nodebase) + { + TreeNode node = (TreeNode)nodebase; + ArrayList kids = new ArrayList(); + + for(long offset = node.kidOffset; offset != -1; offset = node.prevOffset) + { + reader.Position = offset; + node = new TreeNode(reader); + node.HasKids = (node.kidOffset != -1); + kids.Add(node); + } + + return kids; + } + + + /* record node to the backing store and return its starting location */ + long Dump(object node) + { + long retValue = (long)writer.Position; + ((TreeNode)node).Write(writer); + return retValue; + } + + + /* compute the tree and all the counters and write all that to the backing store */ + bool BuildTreeAndComputeStats() + { + /* id of the previous thread */ + int prevThreadId = -1; + + /* information about the current thread (to speed up lookup) */ + int prevDepth = 0; + ArrayList stack = null; + SortedList functions = null; + ArrayList queuedNodes = null; + + Stream s = null; + ProgressForm progressForm = null; + + const int prevStackInitialSize = 100; + int prevStackMaxSize = prevStackInitialSize; + int[] prevStackTrace = new int[prevStackInitialSize]; + int prevStackLen = 0; + StacktraceTable stacktraceTable = logResult.callstackHistogram.readNewLog.stacktraceTable; + + // Preprocessing for function filters + int nIncludeCallFilters = 0; + int nIncludeAllocFilters = 0; + + for (int j=0; j < filterInclude.Length; j++) + { + if (filterInclude[j].functionId != -1) + { + if (filterInclude[j].nodetype == TreeNode.NodeType.Call) + nIncludeCallFilters++; + else if (filterInclude[j].nodetype == TreeNode.NodeType.Allocation) + nIncludeAllocFilters++; + } + } + + + if (firstNewStack != -1) + { + stacktraceTable.FreeEntries( firstNewStack ); + } + firstNewStack = -1; + + try + { + /* log parser code (straight from the ReadNewLog.cs) */ + s = new FileStream(logFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + r = new StreamReader(s); + + progressForm = new ProgressForm(); + progressForm.Text = "Preparing call tree view"; + progressForm.Visible = true; + progressForm.setProgress(0); + progressForm.TopMost = false; + int maxProgress = (int)(r.BaseStream.Length/1024); + progressForm.setMaximum(maxProgress); + + buffer = new byte[4096]; + bufPos = 0; + bufLevel = 0; + line = 1; + StringBuilder sb = new StringBuilder(); + c = ReadChar(); + + bool found; + string assemblyName = null; + int threadid = 0, stackid = 0; + TreeNode.NodeType nodetype = TreeNode.NodeType.Call; + + while (c != -1) + { + found = false; + if ((line % 1024) == 0) + { + int currentProgress = (int)(pos/1024); + if (currentProgress <= maxProgress) + { + progressForm.setProgress(currentProgress); + Application.DoEvents(); + } + } + lastLineStartPos = pos-1; + + /* only parse calls and allocations */ + switch (c) + { + case -1: + break; + + // 'A' with thread identifier + case '!': + { + found = true; + c = ReadChar(); + threadid = ReadInt(); + ReadInt(); + stackid = ReadInt(); + nodetype = TreeNode.NodeType.Allocation; + if (c == -1) {found = false;} + break; + } + + case 'C': + case 'c': + { + found = true; + c = ReadChar(); + nodetype = TreeNode.NodeType.Call; + threadid = ReadInt(); + stackid = ReadInt(); + if (c == -1) {found = false;} + break; + } + + case 'y': + case 'Y': + { + found = true; + c = ReadChar(); + nodetype = TreeNode.NodeType.AssemblyLoad; + threadid = ReadInt(); + /* int assemblyId = */ ReadInt(); + + while (c == ' ' || c == '\t') + { + c = ReadChar(); + } + sb.Length = 0; + while (c > ' ') + { + sb.Append((char)c); + c = ReadChar(); + } + assemblyName = sb.ToString(); + break; + } + + default: + { + // just ignore the unknown + while(c != '\n' && c != '\r') + { + c = ReadChar(); + } + break; + } + } + while (c == ' ' || c == '\t') + c = ReadChar(); + if (c == '\r') + c = ReadChar(); + if (c == '\n') + { + c = ReadChar(); + line++; + } + if(!found) + { + continue; + } + + int[] stacktrace = IndexToStacktrace(stackid); + bool fFoundCallInclude = true; + bool fFoundCallExclude = false; + bool fFoundAllocInclude = true; + bool fFoundAllocExclude = false; + + int includeMatches; + + // Apply filters to current node + if (filterInclude.Length != 0 || filterExclude.Length != 0) + { + if (nodetype == TreeNode.NodeType.Call || + nodetype == TreeNode.NodeType.Allocation) + { + int i = 0; + int includesFound = 0; + fFoundCallInclude = false; + + if (nodetype == TreeNode.NodeType.Allocation) + { + i = 2; + } + + for (; i < stacktrace.Length; i++) + { + includeMatches = 0; + + // See if the stack contain the required include functions + for (int j=0; j < filterInclude.Length; j++) + { + if (filterInclude[j].nodetype == TreeNode.NodeType.Call && filterInclude[j].functionId != -1) + { + if (stacktrace[i] == filterInclude[j].functionId ) + includeMatches++; + } + + } + + if (includeMatches > 0) + includesFound++; + + + // Now see if the stack contains any exclude functions + for (int j=0; j < filterExclude.Length; j++) + { + if (filterExclude[j].nodetype == TreeNode.NodeType.Call && + stacktrace[i] == filterExclude[j].functionId ) + { + // Any exclusion match gets this node bounced + fFoundCallExclude = true; + break; + } + } + } + + // This node can pass the filter only if all include filters are found + if (includesFound == nIncludeCallFilters) + fFoundCallInclude = true; + } + else + { + fFoundCallInclude = false; + } + + if (nodetype == TreeNode.NodeType.Allocation) + { + includeMatches = 0; + fFoundAllocInclude = false; + + // See if the stack contain the required include allocations + for (int j=0; j < filterInclude.Length; j++) + if (filterInclude[j].nodetype == TreeNode.NodeType.Allocation) + { + if (stacktrace[0] == filterInclude[j].functionId || filterInclude[j].functionId == -1) + includeMatches++; + } + + if (includeMatches > 0 || nIncludeAllocFilters == 0) + fFoundAllocInclude = true; + + // Now see if the stack contains any exclude allocations + for (int j=0; j < filterExclude.Length; j++) + { + if (filterExclude[j].nodetype == TreeNode.NodeType.Allocation && + stacktrace[0] == filterExclude[j].functionId) + { + fFoundAllocExclude = true; + break; + } + } + } + } + + // Proceed to process this node only if the trace has all functions + // and no exclude functions. + if ( !fFoundCallInclude || fFoundCallExclude || + !fFoundAllocInclude || fFoundAllocExclude) + { + // Skip this node + continue; + } + + /* if thread changed, have to retrieve information about it. + * info about the last thread is cached to speed up the process */ + if(threadid != prevThreadId) + { + if(prevThreadId != -1) + { + /* store everything about the previous thread */ + ThreadState prevState = threads[prevThreadId]; + prevState.prevStackLen = prevStackLen; + prevState.prevStackTrace = prevStackTrace; + prevState.functions = functions; + prevState.prevDepth = prevDepth; + prevState.stack = stack; + prevState.queuedNodes = queuedNodes; + } + else + { + /* this is the first call ever, mark the + * thread where it occured as the main thread */ + firstThread = threadid; + } + + /* get the information about the current (new) thread */ + ThreadState state; + if (!threads.TryGetValue(threadid, out state)) + { + /* create if necessary */ + state = new ThreadState(); + state.prevStackLen = 0; + state.prevStackTrace = new int[prevStackInitialSize]; + state.prevDepth = 0; + state.stack = new ArrayList(); + state.functions = new SortedList(); + state.queuedNodes = new ArrayList(); + + TreeNode threadRoot = new TreeNode(TreeNode.NodeType.Call, 0); + state.stack.Add(threadRoot); + + threads[threadid] = state; + } + + prevStackLen = state.prevStackLen; + prevStackTrace = state.prevStackTrace; + prevStackMaxSize = prevStackTrace.Length; + + prevDepth = state.prevDepth; + stack = state.stack; + functions = state.functions; + queuedNodes = state.queuedNodes; + prevThreadId = threadid; + } + + /* if we're here, `iscall`, `stackid`, and `threadid` are set correctly */ + int depth = 0; + + if ( nodetype == TreeNode.NodeType.Allocation ) + { + // To build a call tree from the allocation log + // Need to recreate the stacks and Call nodes that got us here. + + // Ignore first 2 ints in the stack trace. They are allocation information. + int curStackLen = stacktrace.Length - 2; + int i; + bool fNewStack; + + // Find out how much of the callstack we need to construct + fNewStack = curStackLen != prevStackLen; + for (i = 0; i < curStackLen && i < prevStackLen; i++) + { + if (prevStackTrace[i] != stacktrace[i+2]) + { + fNewStack = true; + break; + } + } + + int nextStackIndex = stacktraceTable.Length; + for ( ; i < curStackLen; i++) + { + // We blindly add a new entry to the stack table, even though this stack may already + // exist. Searching for a match would be expensive, so for now just do a new allocation. + // If this becomes hugely expensive, it will be worth making the stack trace searchable. + stacktraceTable.Add( nextStackIndex, stacktrace, 2, i+1, false); + TreeNode callnode = new TreeNode(TreeNode.NodeType.Call, nextStackIndex); + callnode.nodeOffset = lastLineStartPos; + queuedNodes.Add( callnode ); + + if (firstNewStack == -1) + { + // Remember which stacks we created + firstNewStack = nextStackIndex; + } + + nextStackIndex++; + } + + if (fNewStack) + { + // Reallocate prev stack if neccessary + if (curStackLen > prevStackMaxSize) + { + prevStackMaxSize += prevStackInitialSize; + prevStackTrace = new int[prevStackMaxSize]; + } + + // Save a copy of the current stack + for (i = 0; i < curStackLen; i++) + { + + prevStackTrace[i] = stacktrace[i+2]; + } + prevStackLen = curStackLen; + } + } + else if ( nodetype == TreeNode.NodeType.Call ) + { + // Reallocate prev stack if neccessary + if (stacktrace.Length > prevStackMaxSize) + { + prevStackMaxSize += prevStackInitialSize; + prevStackTrace = new int[prevStackMaxSize]; + } + prevStackLen = stacktrace.Length; + stacktrace.CopyTo( prevStackTrace, 0 ); + } + + TreeNode node = new TreeNode(nodetype, stackid); + int functionId = (nodetype != TreeNode.NodeType.AssemblyLoad ? stacktrace[stacktrace.Length - 1] : 0); + + + switch(nodetype) + { + case TreeNode.NodeType.Allocation: + node.data.bytesAllocated = stacktrace[1]; + break; + + case TreeNode.NodeType.Call: + if(functionId == 0) + { + node.isunmanaged = true; + } + break; + + case TreeNode.NodeType.AssemblyLoad: + if(!assemblyNameToIdMap.Contains(assemblyName)) + { + assemblyNameToIdMap.Add(assemblyName, null); + node.nameId = assemblyNames.Add(assemblyName); + + queuedNodes.Add(node); + } + continue; + } + + queuedNodes.Add(node); + for(int i = 0; i < queuedNodes.Count; i++) + { + node = (TreeNode)queuedNodes[i]; + nodetype = node.nodetype; + stacktrace = IndexToStacktrace(node.stackid); + int stackLength = stacktrace.Length; + + if(nodetype == TreeNode.NodeType.Allocation) + { + // Skip first 2 entries in the stack. They are type-id and bytes-allocated. + // Add 1 to depth so allocation looks like a call to allocation function. + depth = stackLength - 2 + 1; + } + else if(nodetype == TreeNode.NodeType.Call) + { + depth = stackLength; + } + else if(nodetype == TreeNode.NodeType.AssemblyLoad) + { + depth = stackLength; + } + if(depth <= 0) + { + continue; + } + + if(depth > prevDepth) + { + /* kids go to the stack */ + if(depth - prevDepth > 1) + { + for(int idx = 1; idx < depth; idx++) + { + TreeNode n = new TreeNode(TreeNode.NodeType.Call, -idx); + n.nodeOffset = lastLineStartPos; + stack.Add(n); + } + } + stack.Add(node); + } + else + { + /* moving up or sideways, have to adjust the stats + * and dump some of the branches to the backing store */ + for(int j = 1 + prevDepth; j-- > depth + 1;) + { + if(((TreeNode)stack[j]).nodetype == TreeNode.NodeType.Call) + { + /* record functions left */ + LeaveFunction(functions, GetFunctionIdFromStackId(((TreeNode)stack[j]).stackid)); + } + UpdateStats(stack[j - 1], stack[j]); + ((TreeNode)stack[j - 1]).kidOffset = Dump(stack[j]); + } + if(((TreeNode)stack[depth]).nodetype == TreeNode.NodeType.Call) + { + LeaveFunction(functions, GetFunctionIdFromStackId(((TreeNode)stack[depth]).stackid)); + } + UpdateStats(stack[depth - 1], stack[depth]); + node.prevOffset = Dump(stack[depth]); + stack[depth] = node; + stack.RemoveRange(1 + depth, stack.Count - depth - 1); + } + + /* adjust the global statistics */ + if(nodetype == TreeNode.NodeType.Call) + { + functionId = stacktrace[stacktrace.Length - 1]; + globalCallStats[functionId].timesCalled++; + foreach(int fid in functions.Keys) + { + globalCallStats[fid].totalFunctionsCalled++; + } + + if(!globalCallStats[functionId].calledAlready) + { + globalCallStats[functionId].calledAlready = true; + node.data.firstTimeBroughtIn = true; + foreach(TreeNode n in stack) + { + n.data.numberOfNewFunctionsBroughtIn++; + } + /* `node` (the new function itself) is on the + * stack too, so we have to reverse its counter */ + node.data.numberOfNewFunctionsBroughtIn--; + + foreach(int fid in functions.Keys) + { + globalCallStats[fid].totalNewFunctionsBroughtIn++; + } + } + + /* record entering the function */ + EnterFunction(functions, functionId); + } + else if(nodetype == TreeNode.NodeType.Allocation) + { + foreach(int fid in functions.Keys) + { + globalCallStats[fid].totalBytesAllocated += stacktrace[1]; + } + globalAllocStats[stacktrace[0]].timesAllocated++; + globalAllocStats[stacktrace[0]].totalBytesAllocated += stacktrace[1]; + } + + prevDepth = depth; + } + queuedNodes.Clear(); + } + } + catch(Exception e) + { + /* exceptions are no good */ + MessageBox.Show(this, e.Message + "\n" + e.StackTrace); + return false; + // throw new Exception(e.Message + "\n" + e.StackTrace); + } + finally + { + /* get rid of the progress form and close the log file */ + if(progressForm != null) + { + progressForm.Visible = false; + progressForm.Dispose(); + } + if(s != null) + { + s.Close(); + } + } + + /* dump the root and the remains of the tree + * that are still in memory to the backing store */ + foreach(ThreadState state in threads.Values) + { + stack = state.stack; + for(int j = stack.Count; j-- > 1;) + { + LeaveFunction(functions, GetFunctionIdFromStackId(((TreeNode)stack[j]).stackid)); + UpdateStats(stack[j - 1], stack[j]); + ((TreeNode)stack[j - 1]).kidOffset = Dump(stack[j]); + } + ((TreeNode)stack[0]).HasKids = true; + stack.RemoveRange(1, stack.Count - 1); + } + + /* remove spurious threads from the thread array. don't think + * it's an issue anymore but the code doesn't do anybody no harm */ + List nulls = new List(); + foreach(int key in threads.Keys) + { + if (threads[key] == null) + { + nulls.Add(key); + } + } + foreach(int key in nulls) + { + threads.Remove(key); + } + + writer.Flush(); + return true; + } + + private void GetConfiguration() + { + RegistryKey rkMsft; + + // Open or create the CLR Profiler registry key + rkMsft = Registry.CurrentUser.OpenSubKey( "Software\\Microsoft", true); + rkProfiler = rkMsft.OpenSubKey( "CLRProfiler", true ); + if (rkProfiler == null) + { + rkProfiler = rkMsft.CreateSubKey( "CLRProfiler" ); + if (rkProfiler == null) + { + MessageBox.Show( this, "Unable to open regkey " + rkMsft.ToString() + "\\CLRProfiler" ); + } + } + + rkMsft.Close(); + + + try + { + String value; + string svalue; + int o1, o2; + + // Read the saved rectangle from the registry and restore + // the app to that size. + value = (String)rkProfiler.GetValue( "Rectangle" ); + if (value != null) + { + int left, top, right, bottom; + + o1 = 0; + o2 = value.IndexOf(",", o1); + svalue = value.Substring( o1, o2-o1 ); + left = Int32.Parse( svalue ); + + o1 = o2 + 1; + o2 = value.IndexOf(",", o1); + svalue = value.Substring( o1, o2-o1 ); + top = Int32.Parse( svalue ); + + o1 = o2 + 1; + o2 = value.IndexOf(",", o1); + svalue = value.Substring( o1, o2-o1 ); + right = Int32.Parse( svalue ); + + o1 = o2 + 1; + svalue = value.Substring( o1 ); + bottom = Int32.Parse( svalue ); + + formRect = new Rectangle( left, top, right - left, bottom - top ); + } + + value = (String)rkProfiler.GetValue( "SplitterX" ); + if (value != null) + { + splitterX = Int32.Parse( value ); + } + } + catch (Exception ) + { + // No error. This is expected on the first run of this app on a new machine. + } + } + + private void ResizeForm() + { + this.Location = formRect.Location; + this.Size = formRect.Size; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.threadIDLabel = new System.Windows.Forms.Label(); + this.threadIDList = new System.Windows.Forms.ComboBox(); + this.allFunctionsButton = new System.Windows.Forms.Button(); + this.allAllocationsButton = new System.Windows.Forms.Button(); + this.controlCollection = new System.Windows.Forms.Panel(); + this.stackView = new System.Windows.Forms.ListView(); + this.splitter = new System.Windows.Forms.Splitter(); + this.mainMenu1 = new System.Windows.Forms.MainMenu(); + this.menuItem4 = new System.Windows.Forms.MenuItem(); + this.menuItem5 = new System.Windows.Forms.MenuItem(); + this.menuItem6 = new System.Windows.Forms.MenuItem(); + this.menuItem1 = new System.Windows.Forms.MenuItem(); + this.selectColumns = new System.Windows.Forms.MenuItem(); + this.menuItem2 = new System.Windows.Forms.MenuItem(); + this.menuItem3 = new System.Windows.Forms.MenuItem(); + this.menuItem7 = new System.Windows.Forms.MenuItem(); + this.menuItemShowFuture = new System.Windows.Forms.MenuItem(); + this.menuItemCopyStack = new System.Windows.Forms.MenuItem(); + this.controlCollection.SuspendLayout(); + this.SuspendLayout(); + // + // threadIDLabel + // + this.threadIDLabel.Location = new System.Drawing.Point(0, 0); + this.threadIDLabel.Name = "threadIDLabel"; + this.threadIDLabel.TabIndex = 0; + // + // threadIDList + // + this.threadIDList.Location = new System.Drawing.Point(0, 0); + this.threadIDList.Name = "threadIDList"; + this.threadIDList.Size = new System.Drawing.Size(121, 21); + this.threadIDList.TabIndex = 0; + // + // allFunctionsButton + // + this.allFunctionsButton.Location = new System.Drawing.Point(200, 8); + this.allFunctionsButton.Name = "allFunctionsButton"; + this.allFunctionsButton.Size = new System.Drawing.Size(136, 21); + this.allFunctionsButton.TabIndex = 3; + this.allFunctionsButton.Text = "Display All Functions..."; + // + // allAllocationsButton + // + this.allAllocationsButton.Location = new System.Drawing.Point(352, 8); + this.allAllocationsButton.Name = "allAllocationsButton"; + this.allAllocationsButton.Size = new System.Drawing.Size(136, 21); + this.allAllocationsButton.TabIndex = 4; + this.allAllocationsButton.Text = "Display All Allocations..."; + // + // controlCollection + // + this.controlCollection.Controls.Add(this.stackView); + this.controlCollection.Controls.Add(this.splitter); + this.controlCollection.Location = new System.Drawing.Point(16, 40); + this.controlCollection.Name = "controlCollection"; + this.controlCollection.Size = new System.Drawing.Size(400, 288); + this.controlCollection.TabIndex = 5; + // + // stackView + // + this.stackView.Dock = System.Windows.Forms.DockStyle.Fill; + this.stackView.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(204))); + this.stackView.FullRowSelect = true; + this.stackView.GridLines = true; + this.stackView.Location = new System.Drawing.Point(4, 0); + this.stackView.Name = "stackView"; + this.stackView.Size = new System.Drawing.Size(396, 288); + this.stackView.TabIndex = 1; + this.stackView.View = System.Windows.Forms.View.Details; + this.stackView.MouseDown += new System.Windows.Forms.MouseEventHandler(this.stackView_MouseDown); + this.stackView.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.stackView_ColumnClick); + // + // splitter + // + this.splitter.Location = new System.Drawing.Point(0, 0); + this.splitter.Name = "splitter"; + this.splitter.Size = new System.Drawing.Size(4, 288); + this.splitter.TabIndex = 1; + this.splitter.TabStop = false; + this.splitter.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.splitter_SplitterMoved); + // + // mainMenu1 + // + this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItem4, + this.menuItem1}); + // + // menuItem4 + // + this.menuItem4.Index = 0; + this.menuItem4.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItem5, + this.menuItem6}); + this.menuItem4.Text = "&View"; + // + // menuItem5 + // + this.menuItem5.Index = 0; + this.menuItem5.Text = "All &functions"; + this.menuItem5.Click += new System.EventHandler(this.menuItem5_Click); + // + // menuItem6 + // + this.menuItem6.Index = 1; + this.menuItem6.Text = "All &objects"; + this.menuItem6.Click += new System.EventHandler(this.menuItem6_Click); + // + // menuItem1 + // + this.menuItem1.Index = 1; + this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.selectColumns, + this.menuItem2, + this.menuItem3, + this.menuItem7, + this.menuItemShowFuture, + this.menuItemCopyStack}); + this.menuItem1.Text = "&Options"; + // + // selectColumns + // + this.selectColumns.Index = 0; + this.selectColumns.Text = "Select &columns..."; + this.selectColumns.Click += new System.EventHandler(this.selectColumns_Click); + // + // menuItem2 + // + this.menuItem2.Index = 1; + this.menuItem2.Text = "&Sort options..."; + this.menuItem2.Click += new System.EventHandler(this.menuItem2_Click); + // + // menuItem3 + // + this.menuItem3.Index = 2; + this.menuItem3.Text = "&Filtering..."; + this.menuItem3.Click += new System.EventHandler(this.menuItem3_Click); + // + // menuItem7 + // + this.menuItem7.Index = 3; + this.menuItem7.Text = "Filter F&unctions..."; + this.menuItem7.Click += new System.EventHandler(this.menuItem7_Click); + // + // menuItemShowFuture + // + this.menuItemShowFuture.Index = 4; + this.menuItemShowFuture.Text = "Show Su&btree in Stack Window"; + this.menuItemShowFuture.Click += new System.EventHandler(this.menuItem8_Click); + + // + // menuItemCopyStack + // + this.menuItemCopyStack.Index = 5; + this.menuItemCopyStack.Text = "Copy Stac&k View"; + this.menuItemCopyStack.Click += new System.EventHandler(this.menuItem9_Click); + + // + // CallTreeForm + // + this.ClientSize = new System.Drawing.Size(632, 493); + this.Controls.Add(this.controlCollection); + this.Menu = this.mainMenu1; + this.Name = "CallTreeForm"; + this.Text = "Call Tree View"; + this.Resize += new System.EventHandler(this.CallTreeForm_Resize); + this.Closing += new System.ComponentModel.CancelEventHandler(this.CallTreeForm_Closing); + this.controlCollection.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + + /* handler for the mouse click on the column header. + * changes the current sorting order and behaviour */ + private void SortOn(object obj, EventArgs e) + { + ColumnInformation ci = ((Column)obj).ColumnInformation; + if(viewState.sort.counterId == (int)ci.Token) + { + viewState.sort.sortingOrder *= -1; + } + else + { + viewState.sort.counterId = (int)ci.Token; + viewState.sort.sortingOrder = (viewState.sort.counterId == -1 ? -1 : 1); + } + callTreeView.Resort(); + } + + /* implements IComparer that compares the nodes according to the current sorting order */ + public int Compare(object x, object y) + { + TreeNode a = (TreeNode)x; + TreeNode b = (TreeNode)y; + + if(viewState.sort.counterId == -1) + { + // compare based on the invokation order + return a.prevOffset.CompareTo(b.prevOffset); + } + + IComparable aa = (IComparable)GetInfo(null, a, viewState.sort.counterId); + IComparable bb = (IComparable)GetInfo(null, b, viewState.sort.counterId); + try + { + return aa.CompareTo(bb); + } + catch + { + /* if string ("" is used instead of 0) is being compared against a number */ + bool aazero = (aa.ToString() == ""); + bool bbzero = (bb.ToString() == ""); + return aazero && bbzero ? 0 : aazero ? -1 : 1; + } + } + + /* returns font used to display the item (part of the ITreeOwner interface) */ + public Font GetFont(object obj, TreeNodeBase in_node) + { + TreeNode node = (TreeNode)in_node; + FontStyle fs = FontStyle.Regular; + if(node.data.firstTimeBroughtIn) + { + fs |= FontStyle.Italic; + } + if(node.highlighted) + { + fs |= FontStyle.Bold; + } + return (fs == FontStyle.Regular ? defaultFont : new Font(defaultFont, fs)); + } + + /* returns color used to display the item (part of the ITreeOwner interface) */ + public Color GetColor(object obj, TreeNodeBase root, bool positive) + { + TreeNode node = (TreeNode)root; + int idx = (int)node.nodetype + (positive ? 0 : 3); + return new Color[] + { + Color.Black, + Color.Green, + Color.BlueViolet, + Color.White, + Color.Yellow, + Color.Beige + }[idx]; + } + + /* returns data about the item for a given counter. + * object's ToString() is used to display that data */ + private object GetInfo(object obj, TreeNodeBase node, int counterId) + { + long number = 0; + TreeNode root = (TreeNode)node; + if(counterId < 0) + { + return MakeName(root); + } + else + { + number = root.data.GetCounterValue(counterId); + } + /* use empty string to denote `0` */ + if(number == 0) + { + return ""; + } + return number; + } + + /* returns data about the item for a given counter. + * object's ToString() is used to display that data */ + public object GetInfo(object obj, TreeNodeBase node, ColumnInformation info) + { + return GetInfo(obj, node, info == null ? -1 : (int)info.Token); + } + + /* sort nodes at the branch level */ + public ArrayList ProcessNodes(object obj, ArrayList nodes) + { + bool add = false; + ArrayList nodesAtOneLevel = new ArrayList(); + foreach(TreeNode node in nodes) + { + switch(node.nodetype) + { + case TreeNode.NodeType.Call: + add = true; + break; + + case TreeNode.NodeType.Allocation: + add = viewState.showAllocs; + break; + + case TreeNode.NodeType.AssemblyLoad: + add = viewState.showAssemblies; + break; + } + + if(add) + { + nodesAtOneLevel.Add(node); + } + } + if(nodesAtOneLevel.Count == 0) + { + return nodesAtOneLevel; + } + + /* sort nodes first */ + nodesAtOneLevel.Sort(this); + + /* then choose the nodes to highlight */ + SortingBehaviour ss = viewState.sort; + /* this is needed to use the default Compare method */ + viewState.sort = viewState.highlight; + ArrayList nodesToHighlight = new ArrayList(); + TreeNode currentBest = (TreeNode)nodesAtOneLevel[0]; + + currentBest.highlighted = false; + nodesToHighlight.Add(currentBest); + for(int i = 1; i < nodesAtOneLevel.Count; i++) + { + TreeNode n = (TreeNode)nodesAtOneLevel[i]; + n.highlighted = false; + + int res = Compare(currentBest, n) * viewState.highlight.sortingOrder; + if(res == 0) + { + nodesToHighlight.Add(n); + } + else if(res > 0) + { + currentBest = n; + nodesToHighlight.Clear(); + nodesToHighlight.Add(currentBest); + } + } + viewState.sort = ss; + + foreach(TreeNode n in nodesToHighlight) + { + n.highlighted = true; + } + + /* reverse order if required */ + if(viewState.sort.sortingOrder > 0) + { + nodesAtOneLevel.Reverse(); + } + return nodesAtOneLevel; + } + + /* respond to resize event */ + private void CallTreeForm_Resize(object sender, System.EventArgs e) + { + controlCollection.SuspendLayout(); + controlCollection.Left = 0; + controlCollection.Top = (manyThreads ? 36 : 0); + controlCollection.Width = this.ClientSize.Width; + controlCollection.Height = this.ClientSize.Height - controlCollection.Top; + controlCollection.ResumeLayout(); + } + + private void CallTreeForm_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + if(backingStore != null) + { + backingStore.Close(); + } + if(backingStoreFileName != null) + { + File.Delete(backingStoreFileName); + } + + // Save configuration + if (rkProfiler == null) + { + return; + } + + try + { + string strValue; + + // Save rectangle of the app + strValue = Left.ToString() + "," + Top.ToString() + "," + Right.ToString() + "," + Bottom.ToString(); + rkProfiler.SetValue( "Rectangle", strValue ); + + strValue = splitter.Location.X.ToString(); + rkProfiler.SetValue( "SplitterX", strValue ); + } + catch( Exception ex) + { + MessageBox.Show(this, "Unable to save configuration to the registry. " + ex.Message); + } + finally + { + rkProfiler.Close(); + rkProfiler = null; + } + } + + private void threadIDList_SelectedValueChanged(object sender, System.EventArgs e) + { + int selectedThread = (int)threadIDList.SelectedItem; + if(selectedThread != currentThreadId) + { + TreeListView oldTreeView = callTreeView; + if(oldTreeView != null) + { + oldTreeView.Visible = false; + oldTreeView.Dock = DockStyle.None; + } + currentThreadId = selectedThread; + + callTreeView = threads[selectedThread].callTreeView; + callTreeView.Dock = DockStyle.Left; + if(oldTreeView != null) + { + callTreeView.Size = oldTreeView.Size; + } + viewState = (ViewState)callTreeView.TokenObject; + + CallTreeForm_Resize(null, null); + callTreeView.Visible = true; + ShowCurrentStack(null, null); + } + } + + private void TabsSelectedIndexChanged(object sender, System.EventArgs e) + { + if (tabs.SelectedTab == null) + return; + + foreach(Control c in tabs.SelectedTab.Controls) + { + TreeListView v = null; + try + { + v = (TreeListView)c; + } + catch + { + /* not interested in exceptions */ + } + + if(v != null) + { + callTreeView = v; + viewState = (ViewState)v.TokenObject; + ShowCurrentStack(null, null); + return; + } + } + Debug.Fail("Cannot find tree view on the tab page"); + } + + private void selectColumns_Click(object sender, System.EventArgs e) + { + SelectColumns f = new SelectColumns(); + + /* 0 is "function name", it's irrelevant */ + ArrayList l = callTreeView.GetColumns(); + for(int i = 1; i < l.Count; i++) + { + f.Set((int)((Column)l[i]).ColumnInformation.Token); + } + + DialogResult res = f.ShowDialog(this); + if(res == DialogResult.OK) + { + for(int i = l.Count; i-- > 1;) + { + ((Control)l[i]).Dispose(); + l.RemoveAt(i); + } + + ArrayList ids = f.GetCheckedColumns(); + ids.Sort(); + foreach(int id in ids) + { + AddColumn(callTreeView, id); + } + callTreeView.Invalidate(true); + } + } + + private void menuItem2_Click(object sender, System.EventArgs e) + { + SortAndHighlightSelector s = new SortAndHighlightSelector(viewState.sort, viewState.highlight); + DialogResult res = s.ShowDialog(this); + if(res == DialogResult.OK) + { + SortingBehaviour ss = new SortingBehaviour(); + SortingBehaviour hh = new SortingBehaviour(); + s.GetSortResults(ss, hh); + + if(ss.sortingOrder != viewState.sort.sortingOrder || ss.counterId != viewState.sort.counterId || + hh.sortingOrder != viewState.highlight.sortingOrder || hh.counterId != viewState.highlight.counterId) + { + viewState.sort = ss; + viewState.highlight = hh; + callTreeView.Resort(); + } + } + } + + private void menuItem3_Click(object sender, System.EventArgs e) + { + ViewFilter vf = new ViewFilter(viewState.showCalls, viewState.showAllocs, viewState.showAssemblies); + DialogResult res = vf.ShowDialog(this); + if(res == DialogResult.OK) + { + if( viewState.showCalls != vf.callsCheckbox.Checked + || viewState.showAllocs != vf.allocationsCheckbox.Checked + || viewState.showAssemblies != vf.assembliesCheckbox.Checked) + { + viewState.showCalls = vf.callsCheckbox.Checked; + viewState.showAllocs = vf.allocationsCheckbox.Checked; + viewState.showAssemblies = vf.assembliesCheckbox.Checked; + callTreeView.Resort(); + } + } + } + + private void menuItem5_Click(object sender, System.EventArgs e) + { + ListViewer lv = new ListViewer(); + lv.Text = "Functions"; + + lv.list.Columns.Clear(); + lv.list.Columns.Add("Name", 250, HorizontalAlignment.Left); + lv.list.Columns.Add("Times Called", 90, HorizontalAlignment.Left); + lv.list.Columns.Add("Bytes Allocated", 90, HorizontalAlignment.Left); + lv.list.Columns.Add("Functions Called", 90, HorizontalAlignment.Left); + lv.list.Columns.Add("Functions Introduced", 110, HorizontalAlignment.Left); + + for(int i = 1; i < names.Length; i++) + { + GlobalCallStats s = globalCallStats[i]; + if(names[i] == null || names[i].Length == 0) + { + break; + } + string[] subitems = new string[] + { + names[i], + s.timesCalled.ToString(), + s.totalBytesAllocated.ToString(), + s.totalFunctionsCalled.ToString(), + s.totalNewFunctionsBroughtIn.ToString() + }; + lv.list.Items.Add(new ListViewItem(subitems)); + } + lv.list.Sort(); + + // exact behaviour is not important, just make sure + // that as much information as possible is displayed + lv.Width = 670; + lv.Visible = true; + } + + private void menuItem6_Click(object sender, System.EventArgs e) + { + ListViewer lv = new ListViewer(); + lv.Text = "Functions"; + + lv.list.Columns.Clear(); + lv.list.Columns.Add("Name", 250, HorizontalAlignment.Left); + lv.list.Columns.Add("Times Allocated", 90, HorizontalAlignment.Left); + lv.list.Columns.Add("Total Bytes Allocated", 120, HorizontalAlignment.Left); + + for(int i = 1; i < types.Length; i++) + { + GlobalAllocStats s = globalAllocStats[i]; + if(types[i] == null || types[i].Length == 0) + { + break; + } + string[] subitems = new string[] + { + types[i], + s.timesAllocated.ToString(), + s.totalBytesAllocated.ToString() + }; + lv.list.Items.Add(new ListViewItem(subitems)); + } + lv.list.Sort(); + + // exact behaviour is not important, just make sure + // that as much information as possible is displayed + lv.Width = 500; + lv.Visible = true; + } + + private void splitter_SplitterMoved(object sender, System.Windows.Forms.SplitterEventArgs e) + { + if(callTreeView == null) + { + SetcallTreeView(); + } + ArrayList columns = callTreeView.GetColumns(); + Column callTreeColumn = callTreeView.GetColumns()[0] as Column; + int violations = 0, newWidth = callTreeColumn.Width + splitter.Location.X - previousSplitterLocation; + if(newWidth < 20) + { + newWidth = 20; + violations++; + } + if(newWidth > callTreeView.Width - 10) + { + newWidth = callTreeView.Width - 10; + violations++; + } + if(violations < 2) + { + callTreeColumn.Width = newWidth; + callTreeView.RepaintTreeView(); + } + previousSplitterLocation = splitter.Location.X; + } + + private void menuItem7_Click(object sender, System.EventArgs e) + { + DlgFunctionFilter dlgFunctionFilter = new DlgFunctionFilter( this ); + + if ( dlgFunctionFilter.ShowDialog()== DialogResult.OK) + { + // Dialog has updated filterInclude and filterExclude + InitForm( false ); + } + } + + private void menuItem8_Click(object sender, System.EventArgs e) + { + fShowSubtree = !fShowSubtree; + + menuItemShowFuture.Checked = fShowSubtree; + ShowCurrentStack( null, null ); + } + + // copy the stack + private void menuItem9_Click(object sender, System.EventArgs e) + { + StringBuilder sb = new StringBuilder(); + + bool fFirstHeader = true; + + foreach (System.Windows.Forms.ColumnHeader h in stackView.Columns) + { + if (!fFirstHeader) + sb.Append("\t"); + + fFirstHeader = false; + + sb.Append(h.Text); + } + + sb.Append("\r\n"); + + foreach (ListViewItem itm in stackView.Items) + { + bool fFirst = true; + + foreach (ListViewItem.ListViewSubItem sitm in itm.SubItems) + { + if (!fFirst) + sb.Append("\t"); + + fFirst = false; + + sb.Append(sitm.Text); + } + sb.Append("\r\n"); + } + + Clipboard.SetDataObject(sb.ToString()); + } + + + public void RegenerateTree() + { + formRect.Location = this.Location; + formRect.Size = this.Size; + + InitForm( false ); + } + + private void stackView_ColumnClick(object sender, System.Windows.Forms.ColumnClickEventArgs e) + { + // Only sort if subtree is showing + if (subtreeline == -1) + return; + + prevSortCol = sortCol; + sortCol = e.Column; + + if (prevSortCol == sortCol) + { + // Second click on a column reverse direction of the sort + fReverseSort = !fReverseSort; + } + else + { + // New column. Forward sort + fReverseSort = false; + } + + // Set the ListViewItemSorter property to a new ListViewItemComparer object. + stackView.ListViewItemSorter = new ListViewItemComparer(e.Column, fReverseSort, subtreeline); + + // Call the sort method to manually sort the column based on the ListViewItemComparer implementation. + stackView.Sort(); + } + + private void stackView_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) + { + ListViewItem item = stackView.GetItemAt(e.X, e.Y); + int index; + string strFn; + + if (e.Button != MouseButtons.Right) + { + return; + } + + ContextMenu contextMenu = stackView.ContextMenu; + contextMenu.MenuItems.Clear(); + + if(item == null) + { + return; + } + + index = item.Index; + + // Customize the context menu + contextSelection = index; + EventHandler eventHandler = new EventHandler( this.stackView_ContextMenuSelection ); + + for (int i = 0; i < 2; i++) + { + if (filterInclude[i].functionId > 0) + { + if (filterInclude[i].nodetype == TreeNode.NodeType.Call) + strFn = MakeNameForFunction( filterInclude[i].functionId ); + else + strFn = MakeNameForAllocation( filterInclude[i].functionId, 0); + } + else + { + strFn = "none"; + } + contextMenu.MenuItems.Add( new MenuItem( "Set Include filter " + (i+1).ToString() + " (" + strFn + ")", eventHandler)); + } + + for (int i = 0; i < 2; i++) + { + if (filterExclude[i].functionId > 0) + { + if (filterExclude[i].nodetype == TreeNode.NodeType.Call) + strFn = MakeNameForFunction( filterExclude[i].functionId ); + else + strFn = MakeNameForAllocation( filterExclude[i].functionId, 0 ); + } + else + { + strFn = "none"; + } + contextMenu.MenuItems.Add( new MenuItem( "Set Exclude filter " + (i+1).ToString() + " (" + strFn + ")", eventHandler)); + } + + contextMenu.MenuItems.Add( new MenuItem( "Clear Filters" , eventHandler ) ); + contextMenu.MenuItems.Add( new MenuItem( "Regenerate Tree" , eventHandler ) ); + } + + private void stackView_ContextMenuSelection(object sender, System.EventArgs e) + { + MenuItem miClicked = (MenuItem)sender; + + switch (miClicked.Index) + { + case 0: + case 1: + case 2: + case 3: + // Add function to filters + TreeNode.NodeType nodeType; + int idCurrent; + string name; + FnViewFilter []filters; + + // include or exclude filters? + bool fIncludes = miClicked.Index == 0 || miClicked.Index == 1; + int filterId = (miClicked.Index) & 1; // 0 or 1 + + nodeType = TreeNode.NodeType.Call; + + // A bit of a hack. Use font color to distinguish allocations from functions. + if (stackView.Items[contextSelection].ForeColor == Color.Green) + { + nodeType = TreeNode.NodeType.Allocation; + } + + name = stackView.Items[ contextSelection ].Text; + idCurrent = nodeType == TreeNode.NodeType.Call ? GetFunctionId(name) : GetTypeId(name); + + filters = fIncludes ? filterInclude : filterExclude; + filters[ filterId ].nodetype = nodeType; + filters[ filterId ].functionId = idCurrent; + + break; + + case 4: + // Reset filters + filterInclude[0].functionId = -1; + filterInclude[1].functionId = -1; + filterExclude[0].functionId = -1; + filterExclude[1].functionId = -1; + break; + + case 5: + // Regenerate tree + RegenerateTree(); + break; + } + } + } + + // Implements the manual sorting of items by columns. + class ListViewItemComparer : IComparer + { + private int sortCol; + private bool fReverseSort; + private int subtreeline; + + internal ListViewItemComparer() + { + sortCol = 0; + fReverseSort = false; + subtreeline = -1; + } + + internal ListViewItemComparer(int column, bool fReverse, int subtreeline) + { + sortCol = column; + fReverseSort = fReverse; + this.subtreeline = subtreeline; + } + + + public int Compare(object x, object y) + { + int retval; + + if (((ListViewItem)x).Index <= subtreeline || subtreeline == -1) + { + // Don't sort items above the "subtree" line + return 0; + } + + if (sortCol == 0) + { + // function name column. compare as text + retval = String.Compare(((ListViewItem)x).SubItems[sortCol].Text, ((ListViewItem)y).SubItems[sortCol].Text); + } + else + { + // data column. compare a number + int valX = 0, valY = 0; + + if (((ListViewItem)x).SubItems.Count > sortCol) + { + valX = Convert.ToInt32(((ListViewItem)x).SubItems[sortCol].Text); + } + + if (((ListViewItem)y).SubItems.Count > sortCol) + { + valY = Convert.ToInt32(((ListViewItem)y).SubItems[sortCol].Text); + } + + if ( valX < valY) + { + retval = -1; + } + else if (valX == valY) + { + retval = 0; + } + else + { + retval = 1; + } + } + + if (fReverseSort) + { + retval = 0 - retval; + } + + return retval; + } + } + + internal class Statistics + { + private readonly static string[] CounterNames = + { + "Calls (incl)", + "Calls", + "Bytes (incl)", + "Bytes", + "Objects (incl)", + "Objects", + "New functions (incl)", + "Unmanaged calls (incl)", + "Unmanaged calls", + "Assemblies loaded (incl)", + "Assemblies loaded" + }; + + internal readonly static int[] DefaultCounters = {0, 2, 4, 6, 9}; + + internal static bool IsInclusive(int id) + { + return CounterNames[id].EndsWith("(incl)"); + } + + internal long GetCounterValue(int id) + { + switch(id) + { + case 0: return this.numberOfFunctionsCalled; + case 1: return this.numberOfFunctionsCalled - this.numberOfFunctionsCalledByKids; + case 2: return this.bytesAllocated; + case 3: return this.bytesAllocated - this.bytesAllocatedByKids; + case 4: return this.numberOfObjectsAllocated; + case 5: return this.numberOfObjectsAllocated - this.numberOfObjectsAllocatedByKids; + case 6: return this.numberOfNewFunctionsBroughtIn; + case 7: return this.numberOfUnmanagedTransitions; + case 8: return this.numberOfUnmanagedTransitions - this.numberOfUnmanagedTransitionsByKids; + case 9: return this.numberOfAssembliesLoaded; + case 10: return this.numberOfAssembliesLoaded - this.numberOfAssembliesLoadedByKids; + + default: + { + return -1; + } + } + } + + internal static int GetNumberOfCounters() + { + return CounterNames.Length; + } + + internal static string GetCounterName(int id) + { + return CounterNames[id]; + } + + /* made internal since they are fairly independent + * and creating accessors wouldn't gain anything */ + internal long bytesAllocated; + internal long bytesAllocatedByKids; + internal long numberOfObjectsAllocated; + internal long numberOfObjectsAllocatedByKids; + internal long numberOfFunctionsCalled; + internal long numberOfFunctionsCalledByKids; + internal long numberOfNewFunctionsBroughtIn; + internal long numberOfUnmanagedTransitions; + internal long numberOfUnmanagedTransitionsByKids; + internal long numberOfAssembliesLoaded; + internal long numberOfAssembliesLoadedByKids; + + internal bool firstTimeBroughtIn; + + internal Statistics() + { + bytesAllocated = 0; + bytesAllocatedByKids = 0; + numberOfObjectsAllocated = 0; + numberOfObjectsAllocatedByKids = 0; + numberOfFunctionsCalled = 0; + numberOfFunctionsCalledByKids = 0; + numberOfNewFunctionsBroughtIn = 0; + numberOfUnmanagedTransitions = 0; + numberOfUnmanagedTransitionsByKids = 0; + numberOfAssembliesLoaded = 0; + numberOfAssembliesLoadedByKids = 0; + + firstTimeBroughtIn = false; + } + + /* write the contents to the backing store */ + internal void Write(BitWriter bw) + { + Helpers.WriteNumber(bw, bytesAllocated); + Helpers.WriteNumber(bw, bytesAllocatedByKids); + Helpers.WriteNumber(bw, numberOfObjectsAllocated); + Helpers.WriteNumber(bw, numberOfObjectsAllocatedByKids); + Helpers.WriteNumber(bw, numberOfFunctionsCalled); + Helpers.WriteNumber(bw, numberOfFunctionsCalledByKids); + Helpers.WriteNumber(bw, numberOfNewFunctionsBroughtIn); + Helpers.WriteNumber(bw, numberOfUnmanagedTransitions); + Helpers.WriteNumber(bw, numberOfUnmanagedTransitionsByKids); + Helpers.WriteNumber(bw, numberOfAssembliesLoaded); + Helpers.WriteNumber(bw, numberOfAssembliesLoadedByKids); + bw.WriteBits(firstTimeBroughtIn ? 1ul : 0ul, 1); + } + + /* read the contents to the backing store */ + internal void Read(BitReader br) + { + bytesAllocated = Helpers.ReadNumber(br); + bytesAllocatedByKids = Helpers.ReadNumber(br); + numberOfObjectsAllocated = Helpers.ReadNumber(br); + numberOfObjectsAllocatedByKids = Helpers.ReadNumber(br); + numberOfFunctionsCalled = Helpers.ReadNumber(br); + numberOfFunctionsCalledByKids = Helpers.ReadNumber(br); + numberOfNewFunctionsBroughtIn = Helpers.ReadNumber(br); + numberOfUnmanagedTransitions = Helpers.ReadNumber(br); + numberOfUnmanagedTransitionsByKids = Helpers.ReadNumber(br); + numberOfAssembliesLoaded = Helpers.ReadNumber(br); + numberOfAssembliesLoadedByKids = Helpers.ReadNumber(br); + firstTimeBroughtIn = (br.ReadBits(1) != 0); + } + + /* initialize from the backing store */ + internal Statistics(BitReader br) + { + Read(br); + } + } + + internal class TreeNode : TreeNodeBase + { + /* not to be stored externally */ + internal bool isunmanaged; + internal bool highlighted; + + /* stored */ + internal enum NodeType {Call = 0, Allocation, AssemblyLoad}; + + internal NodeType nodetype; + internal int stackid, nameId; + internal long nodeOffset; // Offset of this node in the trace log + + internal long prevOffset, kidOffset; + + internal Statistics data; + + internal TreeNode(NodeType in_nodetype, int in_stackid) : base() + { + highlighted = false; + data = new Statistics(); + nodetype = in_nodetype; + stackid = in_stackid; + + prevOffset = kidOffset = nodeOffset = -1; + } + + internal void Write(BitWriter writer) + { + writer.WriteBits((ulong)nodetype, 2); + Helpers.WriteNumber(writer, stackid); + if(nodetype == NodeType.AssemblyLoad) + { + Helpers.WriteNumber(writer, nameId); + } + Helpers.WriteNumber(writer, 1 + kidOffset); + Helpers.WriteNumber(writer, 1 + prevOffset); + Helpers.WriteNumber(writer, nodeOffset); + data.Write(writer); + } + + internal void Read(BitReader reader) + { + nodetype = (NodeType)reader.ReadBits(2); + stackid = (int)Helpers.ReadNumber(reader); + if(nodetype == NodeType.AssemblyLoad) + { + nameId = (int)Helpers.ReadNumber(reader); + } + kidOffset = Helpers.ReadNumber(reader) - 1; + prevOffset = Helpers.ReadNumber(reader) - 1; + nodeOffset = Helpers.ReadNumber(reader); + data = new Statistics(reader); + } + + /* initialize from the backing store */ + internal TreeNode(BitReader reader) + { + Read(reader); + } + }; +} diff --git a/CLRProfiler/CLRProfiler/CallTreeForm.resx b/CLRProfiler/CLRProfiler/CallTreeForm.resx new file mode 100644 index 0000000..f7b32ef --- /dev/null +++ b/CLRProfiler/CLRProfiler/CallTreeForm.resx @@ -0,0 +1,265 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + Private + + + Private + + + Private + + + False + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + True + + + Private + + + 8, 8 + + + True + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + Private + + + 17, 17 + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + Private + + + False + + + (Default) + + + False + + + False + + + 8, 8 + + + CallTreeForm + + + True + + + 26 + + + True + + + Private + + \ No newline at end of file diff --git a/CLRProfiler/CLRProfiler/CommentRangeForm.cs b/CLRProfiler/CLRProfiler/CommentRangeForm.cs new file mode 100644 index 0000000..98068ec --- /dev/null +++ b/CLRProfiler/CLRProfiler/CommentRangeForm.cs @@ -0,0 +1,192 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace CLRProfiler +{ + /// + /// Summary description for CommentRangeForm. + /// + public class CommentRangeForm : System.Windows.Forms.Form + { + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.ComboBox startComboBox; + private System.Windows.Forms.ComboBox endComboBox; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + internal const string startCommentString = "Start of Application"; + internal const string shutdownCommentString = "Shutdown of Application"; + + private void FillComboBoxes() + { + ReadNewLog log = MainForm.instance.log; + + startComboBox.Items.Add(startCommentString); + endComboBox.Items.Add(startCommentString); + + for (int i = 0; i < log.commentEventList.count; i++) + { + string comment = log.commentEventList.eventString[i]; + startComboBox.Items.Add(comment); + endComboBox.Items.Add(comment); + } + + startComboBox.Items.Add(shutdownCommentString); + endComboBox.Items.Add(shutdownCommentString); + + startComboBox.SelectedIndex = 0; + endComboBox.SelectedIndex = endComboBox.Items.Count - 1; + if (startComboBox.Items.Count > 2) + startComboBox.SelectedIndex = 1; + if (endComboBox.Items.Count > 2) + endComboBox.SelectedIndex = 2; + } + + internal CommentRangeForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + FillComboBoxes(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.startComboBox = new System.Windows.Forms.ComboBox(); + this.endComboBox = new System.Windows.Forms.ComboBox(); + this.okButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // startComboBox + // + this.startComboBox.Location = new System.Drawing.Point(128, 32); + this.startComboBox.Name = "startComboBox"; + this.startComboBox.Size = new System.Drawing.Size(368, 21); + this.startComboBox.TabIndex = 0; + this.startComboBox.Text = "Start of Application"; + // + // endComboBox + // + this.endComboBox.Location = new System.Drawing.Point(128, 88); + this.endComboBox.Name = "endComboBox"; + this.endComboBox.Size = new System.Drawing.Size(368, 21); + this.endComboBox.TabIndex = 1; + this.endComboBox.Text = "Shutdown of Application"; + // + // okButton + // + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(552, 32); + this.okButton.Name = "okButton"; + this.okButton.TabIndex = 2; + this.okButton.Text = "OK"; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // cancelButton + // + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(552, 88); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.TabIndex = 3; + this.cancelButton.Text = "Cancel"; + // + // label1 + // + this.label1.Location = new System.Drawing.Point(32, 32); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(88, 23); + this.label1.TabIndex = 4; + this.label1.Text = "Range starts at:"; + // + // label2 + // + this.label2.Location = new System.Drawing.Point(32, 88); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(88, 23); + this.label2.TabIndex = 5; + this.label2.Text = "Range ends at:"; + // + // CommentRangeForm + // + this.AcceptButton = this.okButton; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(672, 150); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.label2, + this.label1, + this.cancelButton, + this.okButton, + this.endComboBox, + this.startComboBox}); + this.Name = "CommentRangeForm"; + this.Text = "Select Range..."; + this.ResumeLayout(false); + + } + #endregion + + internal int startTickIndex; + internal int endTickIndex; + internal string startComment; + internal string endComment; + + private int GetTickIndexOfSelectedIndex(int selectedIndex) + { + ReadNewLog log = MainForm.instance.log; + if (selectedIndex == 0) + return 0; + else if (selectedIndex - 1 < log.commentEventList.count) + return log.commentEventList.eventTickIndex[selectedIndex - 1]; + else + return log.maxTickIndex; + } + + private void okButton_Click(object sender, System.EventArgs e) + { + int selectedIndex = startComboBox.SelectedIndex; + startComment = (string)startComboBox.Items[selectedIndex]; + startTickIndex = GetTickIndexOfSelectedIndex(selectedIndex); + selectedIndex = endComboBox.SelectedIndex; + endComment = (string)endComboBox.Items[selectedIndex]; + endTickIndex = GetTickIndexOfSelectedIndex(selectedIndex); + } + } +} diff --git a/CLRProfiler/CLRProfiler/CommentRangeForm.resx b/CLRProfiler/CLRProfiler/CommentRangeForm.resx new file mode 100644 index 0000000..3d62367 --- /dev/null +++ b/CLRProfiler/CLRProfiler/CommentRangeForm.resx @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CommentRangeForm + + \ No newline at end of file diff --git a/CLRProfiler/CLRProfiler/DevLicenseWarning.Designer.cs b/CLRProfiler/CLRProfiler/DevLicenseWarning.Designer.cs new file mode 100644 index 0000000..007a258 --- /dev/null +++ b/CLRProfiler/CLRProfiler/DevLicenseWarning.Designer.cs @@ -0,0 +1,110 @@ +namespace CLRProfiler +{ + partial class DevLicenseWarningForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.textBox1 = new System.Windows.Forms.TextBox(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.showThisCheckbox = new System.Windows.Forms.CheckBox(); + this.buttonOK = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // textBox1 + // + this.textBox1.BackColor = System.Drawing.SystemColors.Control; + this.textBox1.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textBox1.Location = new System.Drawing.Point(13, 13); + this.textBox1.Multiline = true; + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(259, 68); + this.textBox1.TabIndex = 1; + this.textBox1.Text = "NOTE: If you have not yet done so, please install a Windows 8 Developer License o" + + "n this machine. Profiling Windows Store apps requires this license to be instal" + + "led."; + // + // textBox2 + // + this.textBox2.BackColor = System.Drawing.SystemColors.Control; + this.textBox2.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textBox2.Location = new System.Drawing.Point(12, 87); + this.textBox2.Multiline = true; + this.textBox2.Name = "textBox2"; + this.textBox2.Size = new System.Drawing.Size(259, 44); + this.textBox2.TabIndex = 2; + this.textBox2.Text = "Visual Studio installs this license automatically when you create a new Windows S" + + "tore application project."; + // + // showThisCheckbox + // + this.showThisCheckbox.AutoSize = true; + this.showThisCheckbox.Checked = true; + this.showThisCheckbox.CheckState = System.Windows.Forms.CheckState.Checked; + this.showThisCheckbox.Location = new System.Drawing.Point(13, 137); + this.showThisCheckbox.Name = "showThisCheckbox"; + this.showThisCheckbox.Size = new System.Drawing.Size(123, 17); + this.showThisCheckbox.TabIndex = 3; + this.showThisCheckbox.Text = "Show this every time"; + this.showThisCheckbox.UseVisualStyleBackColor = true; + this.showThisCheckbox.CheckedChanged += new System.EventHandler(this.showThisCheckbox_CheckedChanged); + // + // buttonOK + // + this.buttonOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.buttonOK.Location = new System.Drawing.Point(197, 149); + this.buttonOK.Name = "buttonOK"; + this.buttonOK.Size = new System.Drawing.Size(75, 23); + this.buttonOK.TabIndex = 0; + this.buttonOK.Text = "OK"; + this.buttonOK.UseVisualStyleBackColor = true; + // + // DevLicenseWarningForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(284, 184); + this.Controls.Add(this.buttonOK); + this.Controls.Add(this.showThisCheckbox); + this.Controls.Add(this.textBox2); + this.Controls.Add(this.textBox1); + this.Name = "DevLicenseWarningForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Developer License"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.TextBox textBox2; + private System.Windows.Forms.CheckBox showThisCheckbox; + private System.Windows.Forms.Button buttonOK; + + } +} \ No newline at end of file diff --git a/CLRProfiler/CLRProfiler/DevLicenseWarning.cs b/CLRProfiler/CLRProfiler/DevLicenseWarning.cs new file mode 100644 index 0000000..109b80e --- /dev/null +++ b/CLRProfiler/CLRProfiler/DevLicenseWarning.cs @@ -0,0 +1,37 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace CLRProfiler +{ + public partial class DevLicenseWarningForm : Form + { + private bool showThis = true; + public bool ShowThis + { + get { return showThis; } + } + + public DevLicenseWarningForm() + { + InitializeComponent(); + AcceptButton = buttonOK; + } + + private void showThisCheckbox_CheckedChanged(object sender, EventArgs e) + { + showThis = showThisCheckbox.Checked; + } + } +} diff --git a/CLRProfiler/CLRProfiler/DevLicenseWarning.resx b/CLRProfiler/CLRProfiler/DevLicenseWarning.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/CLRProfiler/CLRProfiler/DevLicenseWarning.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/CLRProfiler/CLRProfiler/DiffCallTreeForm.cs b/CLRProfiler/CLRProfiler/DiffCallTreeForm.cs new file mode 100644 index 0000000..6ba4e33 --- /dev/null +++ b/CLRProfiler/CLRProfiler/DiffCallTreeForm.cs @@ -0,0 +1,465 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== + +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Data; + +using System.IO; +using System.Text; +using System.Diagnostics; +using Microsoft.Win32; + + +namespace CLRProfiler +{ + + /// + /// Summary description for DiffCallTreeForm. + /// + internal class DiffCallTreeForm : System.Windows.Forms.Form, IComparer, IDiffTreeOwner + { + + internal class SortingBehaviour + { + internal int sortingOrder; + internal int counterId; + } + internal class ViewState + { + internal SortingBehaviour sort, highlight; + internal bool showCalls, showAllocs, showAssemblies; + + internal ViewState(SortingBehaviour in_sort, SortingBehaviour in_highlight) + { + sort = in_sort; + highlight = in_highlight; + + showCalls = showAllocs = showAssemblies = true; + } + }; + + private AllocationDiff _allocDiff= null; + private ViewState viewState; + private Font defaultFont; + private TreeNodeBase Root; + internal CLRProfiler.DiffTreeListView diffCallTreeView; + private Rectangle formRect = new Rectangle( -1, -1, -1, -1 ); + + + private System.Windows.Forms.Panel controlCollection; + + /// + /// Required designer variable. + /// + /// + private System.ComponentModel.Container components = null; + + public DiffCallTreeForm(TreeNodeBase root, AllocationDiff allocDiff) + { + this.Root = root; + this._allocDiff = allocDiff; + + InitForm(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.controlCollection = new System.Windows.Forms.Panel(); + this.SuspendLayout(); + // + // controlCollection + // + this.controlCollection.Dock = System.Windows.Forms.DockStyle.Fill; + this.controlCollection.Location = new System.Drawing.Point(0, 0); + this.controlCollection.Name = "controlCollection"; + this.controlCollection.Size = new System.Drawing.Size(632, 446); + this.controlCollection.TabIndex = 0; + // + // DiffCallTreeForm + // + this.ClientSize = new System.Drawing.Size(632, 446); + this.Controls.Add(this.controlCollection); + this.Name = "DiffCallTreeForm"; + this.Text = "DiffCallTreeForm"; + this.Resize += new System.EventHandler(this.DiffCallTreeForm_Resize); + this.Closing += new System.ComponentModel.CancelEventHandler(this.DiffCallTreeForm_Closing); + this.ResumeLayout(false); + + } + #endregion + + + private void InitForm() + { + Controls.Clear(); + if (controlCollection != null) + { + controlCollection.Controls.Clear(); + } + InitializeComponent(); + defaultFont = new Font(new FontFamily("Tahoma"), 8); + + DiffTreeListView treeView = new DiffTreeListView(this); + treeView.Dock = DockStyle.Fill; + treeView.Font = defaultFont; + + SortingBehaviour sort = new SortingBehaviour(); + SortingBehaviour highlight = new SortingBehaviour(); + sort.sortingOrder = highlight.sortingOrder = -1; + sort.counterId = -1; + highlight.counterId = 2; + + /* add columns */ + treeView.AddColumn(new ColumnInformation(-1, "Function name", ColumnInformation.ColumnTypes.Tree), 250); + foreach(int counter in DiffStatistics.DefaultCounters) + { + AddColumn(treeView, counter); + } + + treeView.ColumnClick += new EventHandler(SortOn); + + treeView.TokenObject = new ViewState(sort, highlight); + treeView.Root = Root; + + + treeView.Size = new System.Drawing.Size(332, 108); + treeView.Visible = true; + controlCollection.Controls.Add(treeView); + SetcallTreeView(); + this.Visible = true; + } + + + public ArrayList FetchKids(object tokenObject, TreeNodeBase nodebase) + { + return nodebase.allkids; + } + private void SortOn(object obj, EventArgs e) + { + ColumnInformation ci = ((DiffColumn)obj).ColumnInformation; + if(viewState.sort.counterId == (int)ci.Token) + { + viewState.sort.sortingOrder *= -1; + } + else + { + viewState.sort.counterId = (int)ci.Token; + viewState.sort.sortingOrder = (viewState.sort.counterId == -1 ? -1 : 1); + } + diffCallTreeView.Resort(); + } + private void SetcallTreeView() + { + foreach(Control c in controlCollection.Controls) + { + DiffTreeListView v = null; + try + { + v = (DiffTreeListView)c; + } + catch + { + /* not interested in exceptions */ + } + + if(v != null) + { + diffCallTreeView = v; + viewState = (ViewState)v.TokenObject; + return; + } + } + Debug.Fail("Cannot find tree view on the tab page"); + } + + + public Color GetColor(object obj, TreeNodeBase root, bool positive) + { + //###TreeNode node = (TreeNode)root; + DiffDataNode node = (DiffDataNode)root; + int idx = (int)node.nodetype + (positive ? 0 : 3); + return new Color[] + { + Color.Black, + Color.Green, + Color.BlueViolet, + Color.White, + Color.Yellow, + Color.Beige + }[idx]; + } + + + /* returns font used to display the item (part of the ITreeOwner interface) */ + public Font GetFont(object obj, TreeNodeBase in_node) + { + //###TreeNode node = (TreeNode)in_node; + DiffDataNode node = (DiffDataNode)in_node; + FontStyle fs = FontStyle.Regular; + if(node.data.firstTimeBroughtIn) + { + fs |= FontStyle.Italic; + } + if(node.highlighted) + { + fs |= FontStyle.Bold; + } + return (fs == FontStyle.Regular ? defaultFont : new Font(defaultFont, fs)); + + + } + + /* sort nodes at the branch level */ + public ArrayList ProcessNodes(object obj, ArrayList nodes) + { + bool add = false; + ArrayList nodesAtOneLevel = new ArrayList(); + //###foreach(TreeNode node in nodes) + foreach(DiffDataNode node in nodes) + { + switch(node.nodetype) + { + case DiffDataNode.NodeType.Call: + add = true; + break; + + case DiffDataNode.NodeType.Allocation: + add = viewState.showAllocs; + break; + + case DiffDataNode.NodeType.AssemblyLoad: + add = viewState.showAssemblies; + break; + } + + if(add) + { + nodesAtOneLevel.Add(node); + } + } + if(nodesAtOneLevel.Count == 0) + { + return nodesAtOneLevel; + } + + /* sort nodes first */ + nodesAtOneLevel.Sort(this); + + /* then choose the nodes to highlight */ + SortingBehaviour ss = viewState.sort; + /* this is needed to use the default Compare method */ + viewState.sort = viewState.highlight; + ArrayList nodesToHighlight = new ArrayList(); + DiffDataNode currentBest = (DiffDataNode)nodesAtOneLevel[0]; + + currentBest.highlighted = false; + nodesToHighlight.Add(currentBest); + for(int i = 1; i < nodesAtOneLevel.Count; i++) + { + DiffDataNode n = (DiffDataNode)nodesAtOneLevel[i]; + n.highlighted = false; + + int res = Compare(currentBest, n) * viewState.highlight.sortingOrder; + if(res == 0) + { + nodesToHighlight.Add(n); + } + else if(res > 0) + { + currentBest = n; + nodesToHighlight.Clear(); + nodesToHighlight.Add(currentBest); + } + } + viewState.sort = ss; + + foreach(DiffDataNode n in nodesToHighlight) + { + n.highlighted = true; + } + + /* reverse order if required */ + if(viewState.sort.sortingOrder > 0) + { + nodesAtOneLevel.Reverse(); + } + return nodesAtOneLevel; + } + + /* implements IComparer that compares the nodes according to the current sorting order */ + public int Compare(object x, object y) + { + DiffDataNode a = (DiffDataNode)x; + DiffDataNode b = (DiffDataNode)y; + + if(viewState.sort.counterId == -1) + { + // compare based on the invokation order + //return a.prevOffset.CompareTo(b.prevOffset); + return a.nodeId.CompareTo(b.nodeId); + } + + IComparable aa = (IComparable)GetInfo(null, a, viewState.sort.counterId); + IComparable bb = (IComparable)GetInfo(null, b, viewState.sort.counterId); + try + { + return aa.CompareTo(bb); + } + catch + { + /* if string ("" is used instead of 0) is being compared against a number */ + bool aazero = (aa.ToString() == ""); + bool bbzero = (bb.ToString() == ""); + return aazero && bbzero ? 0 : aazero ? -1 : 1; + } + } + + /* returns data about the item for a given counter. + * object's ToString() is used to display that data */ + private object GetInfo(object obj, TreeNodeBase node, int counterId) + { + long number = 0; + //###TreeNode root = (TreeNode)node; + DiffDataNode root = (DiffDataNode)node; + if(counterId < 0) + { + //return MakeName(root); + return root.name; + } + else + { + number = root.data.GetCounterValue(counterId); + } + /* use empty string to denote `0` */ + if(number == 0) + { + return ""; + } + return number; + } + + /* returns data about the item for a given counter. + * object's ToString() is used to display that data */ + public object GetInfo(object obj, TreeNodeBase node, ColumnInformation info) + { + return GetInfo(obj, node, info == null ? -1 : (int)info.Token); + } + + #region GUI function + private DiffColumn AddColumn(DiffTreeListView treeView, int counterId) + { + DiffColumn c = treeView.AddColumn(new ColumnInformation(counterId, + DiffStatistics.GetCounterName(counterId), + ColumnInformation.ColumnTypes.String), + 60); + if(DiffStatistics.IsInclusive(counterId)) + { + c.Font = new Font(c.Font, FontStyle.Bold); + } + return c; + } + #endregion + + private void DiffCallTreeForm_Resize(object sender, System.EventArgs e) + { + controlCollection.SuspendLayout(); + controlCollection.Left = 0; + controlCollection.Top = 0; + controlCollection.Width = this.ClientSize.Width; + controlCollection.Height = this.ClientSize.Height - controlCollection.Top; + controlCollection.ResumeLayout(); + } + + + private void DiffCallTreeForm_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + _allocDiff.RefreshCallTreeNodes(_allocDiff.Root); + } + } + + + internal class DiffStatistics + { + private DiffDataNode node; + private readonly static string[] CounterNames = + { + "Prev (incl)", + "curr (incl)", + "diff (incl)", + "prev (Calls)", + "curr (Calls)", + "diff (Calls)" + }; + internal readonly static int[] DefaultCounters = {0, 1, 2, 3, 4, 5}; + + internal static bool IsInclusive(int id) + { + return CounterNames[id].EndsWith("(incl)"); + } + + internal long GetCounterValue(int id) + { + switch(id) + { + case 0: return node.prevIncl; + case 1: return node.currIncl; + case 2: return node.currIncl - node.prevIncl; + case 3: return node.prevCalls; + case 4: return node.currCalls; + case 5: return node.currCalls - node.prevCalls; + default: + { + return -1; + } + } + } + + internal static int GetNumberOfCounters() + { + return CounterNames.Length; + } + + internal static string GetCounterName(int id) + { + return CounterNames[id]; + } + + internal bool firstTimeBroughtIn; + + internal DiffStatistics(DiffDataNode node) + { + this.node = node; + + firstTimeBroughtIn = false; + } + + + } +} diff --git a/CLRProfiler/CLRProfiler/DiffCallTreeForm.resx b/CLRProfiler/CLRProfiler/DiffCallTreeForm.resx new file mode 100644 index 0000000..27841ec --- /dev/null +++ b/CLRProfiler/CLRProfiler/DiffCallTreeForm.resx @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + True + + + Private + + + 8, 8 + + + True + + + Private + + + False + + + (Default) + + + False + + + False + + + 8, 8 + + + True + + + 80 + + + True + + + Private + + + DiffCallTreeForm + + diff --git a/CLRProfiler/CLRProfiler/DiffDataNode.cs b/CLRProfiler/CLRProfiler/DiffDataNode.cs new file mode 100644 index 0000000..ab1c7e3 --- /dev/null +++ b/CLRProfiler/CLRProfiler/DiffDataNode.cs @@ -0,0 +1,51 @@ +using System; +using System.Data; +using System.Collections; +namespace CLRProfiler +{ + /// + /// Summary description for DiffDataNode. + /// + internal class DiffDataNode : TreeNodeBase + { + internal enum NodeType {Call = 0, Allocation, AssemblyLoad}; + + //internal int parenttreeIdx = -1; + //internal int treeIdx = -1; + internal int parentId = -1; + internal int nodeId = -1; + internal string parentname = ""; + internal string name = ""; + internal string mapname = ""; + internal long prevIncl = 0; + internal long currIncl = 0; + internal long diffIncl = 0; + internal int prevFunId = -1; + internal int currFunId = -1; + internal long prevCalls = 0; + internal long currCalls = 0; + internal long diffCalls = 0; + + internal TreeNode currTreenode = null; + internal TreeNode prevTreenode = null; + internal bool marked = false; + + internal NodeType nodetype = 0; + internal DiffStatistics data; + internal bool highlighted; + //internal ArrayList kids; + + + public DiffDataNode(String name) + { + this.name = name; + int at = name.IndexOf('('); + if(at > 0) + { + mapname = name.Substring(0, at); + } + allkids = new ArrayList(); + data = new DiffStatistics(this); + } + } +} diff --git a/CLRProfiler/CLRProfiler/DiffTreeListView.cs b/CLRProfiler/CLRProfiler/DiffTreeListView.cs new file mode 100644 index 0000000..0237411 --- /dev/null +++ b/CLRProfiler/CLRProfiler/DiffTreeListView.cs @@ -0,0 +1,838 @@ +using System; +using System.IO; +using System.Drawing; +using System.Collections; +using System.Windows.Forms; +using System.Runtime.InteropServices; +using System.Text; + + + + +namespace CLRProfiler +{ + internal interface IDiffTreeOwner + { + ArrayList ProcessNodes(object obj, ArrayList nodesAtOneLevel); + + Font GetFont(object obj, TreeNodeBase node); + + Color GetColor(object obj, TreeNodeBase node, bool positive); + + object GetInfo(object obj, TreeNodeBase node, ColumnInformation info); + + ArrayList FetchKids(object obj, TreeNodeBase node); + + } + + internal class DiffResizeBarCapture : Control + { + int track; + ArrayList columns; + + internal DiffResizeBarCapture(ArrayList in_columns) : base() + { + track = -1; + columns = in_columns; + } + + internal void SetCapture(int in_track) + { + track = in_track; + this.Capture = true; + } + + override protected void OnMouseMove(MouseEventArgs e) + { + if(track != -1) + { + DiffColumn left = (DiffColumn)columns[track]; + + int ll = left.Left; + if(e.X < ll + 15) + { + return; + } + left.Width = e.X - left.Left; + + ((DiffTreeListView)Parent).RepaintTreeView(); + } + } + + override protected void OnMouseUp(MouseEventArgs e) + { + track = -1; + this.Capture = false; + } + } + internal class DiffColumn : Button + { + private bool pressed; + private ArrayList columnsRef; + private ColumnInformation ci; + private DiffResizeBarCapture resizeBar; + + internal ColumnInformation ColumnInformation {get {return ci;}} + + internal DiffColumn(ColumnInformation in_ci, DiffResizeBarCapture in_resizeBar, ArrayList in_columnsRef) : base() + { + pressed = false; + + ci = in_ci; + resizeBar = in_resizeBar; + columnsRef = in_columnsRef; + + Text = ci.Text; + + SetStyle(ControlStyles.Selectable, false); + } + + override protected void OnMouseMove(MouseEventArgs e) + { + Cursor oldCursor = Cursor, toSet = Cursors.Arrow; + if(pressed) + { + base.OnMouseMove(e); + } + else + { + int index = columnsRef.IndexOf(this); + int left = 0; + int right = this.Width; + if(e.X - left < 5 && index != 0 || right - e.X < 5) + { + toSet = Cursors.VSplit; + } + } + + if(oldCursor != toSet) + { + Cursor = toSet; + } + } + + override protected void OnMouseDown(MouseEventArgs e) + { + if(e.Button == MouseButtons.Left) + { + int index = columnsRef.IndexOf(this); + int left = 0; + int right = this.Width; + if(e.X - left < 5 && index != 0) + { + resizeBar.SetCapture(index - 1); + return; + } + else if(right - e.X < 5) + { + resizeBar.SetCapture(index); + return; + } + } + + pressed = true; + base.OnMouseDown(e); + } + + override protected void OnMouseUp(MouseEventArgs e) + { + pressed = false; + base.OnMouseUp(e); + } + } + + internal class DiffTreeListView : Control + { + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + #region Data member + private int leftEdge; + private ArrayList columns; + private ListBox treeListBox; + private DiffResizeBarCapture resizeBar; + private object keepSelected; + private PlacedToolTip hoverPopup; + + // events of interest + internal event EventHandler ColumnClick; + internal event EventHandler SelectedIndexChanged; + + // + internal object TokenObject = null; + private IDiffTreeOwner treeOwner = null; + + private int ContextSelection; + + private TreeNodeBase root = null; + internal TreeNodeBase Root + { + set + { + if(value != null) + { + treeListBox.Items.Clear(); + treeListBox.Items.Add(root = value); + root.depth = 0; + } + } + get + { + return root; + } + } + + enum Direction + { + Forward, + Backward + } + #endregion + + #region ListBox + internal ListBox.ObjectCollection Items + { + get + { + return treeListBox.Items; + } + } + + internal object SelectedItem + { + get + { + return treeListBox.SelectedItem; + } + set + { + treeListBox.SelectedItem = value; + } + } + + private void Resort(int depth, TreeNodeBase root) + { + root.depth = depth; + treeListBox.Items.Add(root); + if(root.allkids != null && root.IsExpanded) + { + ArrayList nodes = treeOwner.ProcessNodes(TokenObject, root.allkids); + for(int i = 0; i < nodes.Count; i++) + { + Resort(1 + depth, (TreeNodeBase)nodes[i]); + } + } + } + + internal void Resort() + { + keepSelected = treeListBox.SelectedItem; + treeListBox.Items.Clear(); + Resort(0, root); + } + + private void ReplaceContents(object[] nodes) + { + /* an attempt to prevent the control from jerking */ + object selection = treeListBox.Items[treeListBox.SelectedIndex]; + int topIndex = treeListBox.TopIndex; + int leftEdge = GetScrollPos(treeListBox.Handle, 0); + + treeListBox.BeginUpdate(); + treeListBox.Items.Clear(); + treeListBox.Items.AddRange(nodes); + treeListBox.SelectedItem = selection; + treeListBox.TopIndex = topIndex; + treeListBox.EndUpdate(); + + /* send WM_HSCROLL message to the list box */ + SendMessage(treeListBox.Handle, 0x0114, 4 + (leftEdge << 16), 0); + } + + private int AddAfter(int index, TreeNodeBase root) + { + if(root.allkids == null) + { + root.allkids = treeOwner.FetchKids(TokenObject, root); + } + ArrayList nodes = treeOwner.ProcessNodes(TokenObject, root.allkids); + + int numNodes = nodes.Count, nodesInList = treeListBox.Items.Count; + int costOfRebuilding = 3 * (numNodes + nodesInList); + int costOfInsertions = (1 + nodesInList - index) * numNodes; + int i, newDepth = 1 + root.depth; + + if(costOfInsertions < costOfRebuilding) + { + foreach(TreeNodeBase curr in nodes) + { + curr.depth = newDepth; + treeListBox.Items.Insert(++index, curr); + } + } + else + { + ++index; + ListBox.ObjectCollection items = treeListBox.Items; + object[] allNodes = new object[numNodes + nodesInList]; + for(i = 0; i < index; i++) + { + allNodes[i] = items[i]; + } + for(; i < index + numNodes; i++) + { + allNodes[i] = nodes[i - index]; + ((TreeNodeBase)allNodes[i]).depth = newDepth; + } + for(; i < numNodes + nodesInList; i++) + { + allNodes[i] = items[i - numNodes]; + } + ReplaceContents(allNodes); + } + return index; + } + + override protected void OnFontChanged(EventArgs e) + { + base.OnFontChanged(e); + treeListBox.Font = Font; + treeListBox.ItemHeight = Font.Height + 3; + } + + #endregion + + internal DiffTreeListView(IDiffTreeOwner in_treeOwner) + { + // This call is required by the Windows.Forms Form Designer. + InitializeComponent(); + + treeOwner = in_treeOwner; + + hoverPopup = new PlacedToolTip(); + hoverPopup.Parent = FindForm(); + + treeListBox = new ListBox(); + treeListBox.Parent = this; + treeListBox.BorderStyle = System.Windows.Forms.BorderStyle.None; + treeListBox.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; + treeListBox.IntegralHeight = false; + treeListBox.Name = "treeListBox"; + treeListBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.treeListBox_KeyDown); + treeListBox.MouseDown += new System.Windows.Forms.MouseEventHandler(this.treeListBox_MouseDown); + treeListBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.treeListBox_MouseMove); + treeListBox.DoubleClick += new System.EventHandler(this.treeListBox_DoubleClick); + treeListBox.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.treeListBox_DrawItem); + treeListBox.SelectedIndexChanged += new System.EventHandler(this.treeListBox_SelectedIndexChanged); + + columns = new ArrayList(); + + // must be created before calling `AddColumn` + resizeBar = new DiffResizeBarCapture(columns); + resizeBar.Visible = false; + resizeBar.Parent = this; + + leftEdge = 0; + + OnResize(null); + + // log = new StreamWriter("test.log"); + + // Create a blank context menu. We'll fill it in when the user right clicks + ContextMenu contextMenu = new ContextMenu(); + ContextMenu = contextMenu; + } + + internal void RepaintTreeView() + { + RedoColumnLayout(); + treeListBox.Invalidate(); + treeListBox.Update(); + } + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if( components != null ) + components.Dispose(); + } + base.Dispose( disposing ); + } + + #region Component Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + #endregion + + protected override void OnPaint(PaintEventArgs pe) + { + // TODO: Add custom paint code here + + // Calling the base class OnPaint + base.OnPaint(pe); + } + + void RedoColumnLayout() + { + int position = -leftEdge; + int height = Font.Height + 5; + for(IEnumerator e = columns.GetEnumerator(); e.MoveNext();) + { + DiffColumn current = (DiffColumn)e.Current; + current.SuspendLayout(); + current.Location = new Point(position, 0); + if(height > 0) + { + current.Height = height; + } + current.ResumeLayout(); + position += current.Width; + } + + position += leftEdge; + treeListBox.HorizontalScrollbar = (position > treeListBox.ClientSize.Width); + treeListBox.HorizontalExtent = position; + } + + [DllImport("User32.Dll")] + internal static extern int GetScrollPos(IntPtr hwnd, int bar); + + [DllImport("User32.Dll")] + internal static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, int lParam); + + #region Event -- ListBox + private void treeListBox_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e) + { + /* 0 stands for SB_HORZ */ + int leftEdge = GetScrollPos(treeListBox.Handle, 0); + if(leftEdge != this.leftEdge) + { + this.leftEdge = leftEdge; + RedoColumnLayout(); + } + + int position = 0; + + Graphics g = e.Graphics; + ListBox treeView = treeListBox; + + if(e.Index < 0 || e.Index >= treeView.Items.Count) + { + return; + } + + StringFormat sf = new StringFormat(StringFormatFlags.NoWrap); + sf.Trimming = StringTrimming.EllipsisCharacter; + + TreeNodeBase node = (TreeNodeBase)Items[e.Index]; + + int crossover = (treeListBox.ItemHeight - 1) * (1 + node.depth); + g.FillRectangle(new SolidBrush(Color.White), position, e.Bounds.Top, crossover, e.Bounds.Height); + + Rectangle itemRect = new Rectangle(crossover, e.Bounds.Top, e.Bounds.Right - crossover, e.Bounds.Height); + g.FillRectangle(new SolidBrush(e.BackColor), itemRect); + + if(e.State == DrawItemState.Focus) + { + ControlPaint.DrawFocusRectangle(g, itemRect, e.ForeColor, e.BackColor); + } + + Pen grayPen = new Pen(Color.LightGray); + g.DrawLine(grayPen, 0, e.Bounds.Bottom - 1, e.Bounds.Right, e.Bounds.Bottom - 1); + + Font fontToUse = treeOwner.GetFont(TokenObject, node); + + + Color color = treeOwner.GetColor(TokenObject, node, (e.State & DrawItemState.Selected) != DrawItemState.Selected); + + Brush brush = new SolidBrush(color); + + Region oldClip = g.Clip; + foreach(DiffColumn c in columns) + { + ColumnInformation current = c.ColumnInformation; + Rectangle rect = new Rectangle(position, e.Bounds.Top, c.Width, e.Bounds.Height); + g.Clip = new Region(rect); + + string res = treeOwner.GetInfo(TokenObject, node, current).ToString(); + + if(current.ColumnType == ColumnInformation.ColumnTypes.Tree) + { + rect.Offset((1 + node.depth) * (treeListBox.ItemHeight - 1), 0); + rect.Width -= (1 + node.depth) * (treeListBox.ItemHeight - 1); + + if(node.HasKids) + { + Pen p = new Pen(Color.Gray); + int y0 = e.Bounds.Top; + int x0 = position + node.depth * (treeListBox.ItemHeight - 1); + g.DrawRectangle(p, x0 + 3, y0 + 3, (treeListBox.ItemHeight - 9), (treeListBox.ItemHeight - 9)); + g.DrawLine(p, x0 + 5, y0 + (treeListBox.ItemHeight - 3) / 2, x0 + (treeListBox.ItemHeight - 8), y0 + (treeListBox.ItemHeight - 3) / 2); + if(!node.IsExpanded) + { + g.DrawLine(p, x0 + (treeListBox.ItemHeight - 3) / 2, y0 + 5, x0 + (treeListBox.ItemHeight - 3) / 2, y0 + (treeListBox.ItemHeight - 8)); + } + } + } + + if(res != null) + { + int characters, lines; + + SizeF layoutArea = new SizeF(rect.Width, rect.Height); + SizeF stringSize = g.MeasureString(res, fontToUse, layoutArea, sf, out characters, out lines); + + g.DrawString(res.Substring(0, characters) + (characters < res.Length ? "..." : ""), fontToUse, brush, rect.Location, sf); + g.DrawLine(grayPen, rect.Right - 1, e.Bounds.Top, rect.Right - 1, e.Bounds.Bottom - 1); + } + + position += c.Width; + } + g.Clip = oldClip; + } + + private void treeListBox_MouseMove(object s, MouseEventArgs e) + { + int index = treeListBox.IndexFromPoint(e.X, e.Y); + if(index < 0 || index >= Items.Count) + { + return; + } + + TreeNodeBase node = (TreeNodeBase)Items[index]; + int crossover = (treeListBox.ItemHeight - 1) * (1 + node.depth) - leftEdge; + Rectangle rect = treeListBox.GetItemRectangle(index); + + Point screenPoint = treeListBox.PointToScreen(new Point(crossover, rect.Top)); + if(hoverPopup.Parent == null) + { + hoverPopup.Parent = FindForm(); + } + Point controlPoint = hoverPopup.Parent.PointToClient(screenPoint); + + string textToDisplay = treeOwner.GetInfo(TokenObject, node, null).ToString(); + if(textToDisplay != hoverPopup.CurrentlyDisplayed()) + { + hoverPopup.Display(controlPoint, treeOwner.GetFont(TokenObject, node), rect.Height, textToDisplay); + } + } + + private void treeListBox_MouseDown(object s, MouseEventArgs e) + { + int index = treeListBox.IndexFromPoint(e.X, e.Y); + if(index == ListBox.NoMatches) + { + return; + } + + TreeNodeBase node = (TreeNodeBase)Items[index]; + if (e.Button == MouseButtons.Left) + { + int offset = node.depth * (treeListBox.ItemHeight - 1) - leftEdge; + if(offset <= e.X && e.X < offset + (treeListBox.ItemHeight - 1)) + { + ToggleBranch(index); + } + } + else + { + // Customize the context menu + ContextMenu contextMenu = this.ContextMenu; + ColumnInformation ci = ((DiffColumn)columns[0]).ColumnInformation; + string fnName = treeOwner.GetInfo(TokenObject, node, ci).ToString(); + + + + ContextSelection = index; + EventHandler eventHandler = new EventHandler( this.ContextMenu_Selection ); + contextMenu.MenuItems.Clear(); + contextMenu.MenuItems.Add( new MenuItem( "Save to...", eventHandler)); + //contextMenu.MenuItems.Add( new MenuItem( "Save forward ...", eventHandler)); + //contextMenu.MenuItems.Add( new MenuItem( "Save backward ...", eventHandler)); + + } + + } + + private void ContextMenu_Selection(object sender, System.EventArgs e) + { + // output + StringBuilder sb = new StringBuilder("Function Name, Prev(Incl), Curr(Incl), Diff(Incl), Prev(Calls), Curr(Calls), Diff(Calls)\r\n"); + MenuItem miClicked = (MenuItem)sender; + + string fileName = GetSaveAsFileName(); + + + //TreeNodeBase node; + //DiffDataNode.NodeType nodeType; + + switch (miClicked.Index) + { + + case 0: + // Save Selected item + if(fileName != null) + { + //node = (TreeNodeBase)treeListBox.Items[ ContextSelection ]; + + GetTreeContent(sb, this.Root as DiffDataNode); + } + break; + + case 1: + // Save forward + + break; + case 2: + // Save backward + + break; + } + if(sb.Length > 0) + { + writeToFile(fileName,sb); + } + } + + private void GetTreeContent(StringBuilder sb, DiffDataNode root) + { + string prefx = "-"; + + for(int i = 0; i < root.depth; i++) + { + prefx += '-'; + } + sb.AppendFormat("{0}{1},{2},{3},{4},{5},{6},{7}\r\n", prefx, root.name, root.prevIncl, root.currIncl.ToString(), root.diffIncl.ToString(), root.prevCalls.ToString(), root.currCalls.ToString(), root.diffCalls.ToString()); + + if(root.IsExpanded) + { + for(int i = 0; i < root.allkids.Count; i++) + { + GetTreeContent(sb, root.allkids[i] as DiffDataNode); + } + } + + } + private string GetSaveAsFileName() + { + SaveFileDialog saveFileDialog1 = new SaveFileDialog(); + + saveFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*" ; + saveFileDialog1.FilterIndex = 2 ; + saveFileDialog1.RestoreDirectory = true ; + + if(saveFileDialog1.ShowDialog() == DialogResult.OK) + { + return saveFileDialog1.FileName; + } + return null; + + } + internal void writeToFile(string fileName, StringBuilder sb) + { + try + { + if(File.Exists(fileName)) + File.Delete(fileName); + string path = Path.GetDirectoryName(fileName); + + // Determine whether the directory exists. + if (!Directory.Exists(path)) + { + // Create the directory it does not exist. + Directory.CreateDirectory(path); + } + FileStream fs = new FileStream(fileName, FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite); + StreamWriter sw = new StreamWriter(fs); + sw.Write(sb); + sw.Close(); + } + catch(Exception e) + { + + throw new Exception(e.Message); + } + } + private void treeListBox_DoubleClick(object s, EventArgs e) + { + int index = treeListBox.SelectedIndex; + if(index != -1) + { + ToggleBranch(index); + } + } + + override protected void OnResize(EventArgs e) + { + treeListBox.Size = new Size(ClientSize.Width, + ClientSize.Height - treeListBox.Top); + treeListBox.Refresh(); + } + + private void treeListBox_KeyDown(object s, KeyEventArgs e) + { + int index = treeListBox.SelectedIndex; + if(index == -1) + { + return; + } + + Keys key = e.KeyCode; + TreeNodeBase node = (TreeNodeBase)Items[index]; + switch(key) + { + case Keys.Left: + if(node.IsExpanded) + { + ToggleBranch(index); + } + break; + + case Keys.Right: + if(!node.IsExpanded) + { + ToggleBranch(index); + } + break; + + case Keys.Space: + ToggleBranch(index); + break; + } + } + + private void treeListBox_SelectedIndexChanged(object sender, System.EventArgs e) + { + if(SelectedIndexChanged != null) + { + SelectedIndexChanged(sender, e); + } + } + + #endregion + + #region Event -- Column button + private void DiffColumn_Click(object sender, EventArgs e) + { + if(ColumnClick != null) + { + ColumnClick(sender, e); + } + } + + internal ArrayList GetColumns() + { + return columns; + } + + internal DiffColumn AddColumn(ColumnInformation ci, int defaultWidth) + { + DiffColumn c = new DiffColumn(ci, resizeBar, columns); + + c.Width = defaultWidth; + c.Parent = this; + c.TextAlign = ContentAlignment.MiddleLeft; + c.Click += new EventHandler(DiffColumn_Click); + c.Font = Font; + columns.Add(c); + + RedoColumnLayout(); + + treeListBox.SuspendLayout(); + treeListBox.Location = new Point(0, c.Height); + treeListBox.Size = new Size(ClientSize.Width, ClientSize.Height - c.Height); + treeListBox.ResumeLayout(); + + return c; + } + #endregion + + void ToggleBranch(int index) + { + TreeNodeBase node = (TreeNodeBase)Items[index]; + if(!node.HasKids) + { + /*node.allkids = treeOwner.FetchKids(TokenObject, node); + if(node.allkids.Count ==0) + { + return; + }*/ + return; + } + + int count = Items.Count; + if(node.IsExpanded) + { + index++; + int upTo = index; + TreeNodeBase kid = null; + // eh, hopefully item access is cheap + + ListBox.ObjectCollection items = treeListBox.Items; + + while(upTo < count && (kid = (TreeNodeBase)items[upTo]).depth > node.depth) + { + kid.IsExpanded = false; + upTo++; + } + + int i, nodesInList = items.Count; + int toRemove = upTo - index; + int costOfRemoveAt = toRemove * (1 + nodesInList - upTo); + int costOfRebuilding = 2 * nodesInList + 3 * (nodesInList - (upTo - index)); + + if(costOfRemoveAt < costOfRebuilding) + { + for(i = upTo; i-- > index;) + { + items.RemoveAt(i); + } + } + else + { + object[] allNodes = new object[nodesInList - toRemove]; + for(i = 0; i < index; i++) + { + allNodes[i] = items[i]; + } + for(i = upTo; i < nodesInList; i++) + { + allNodes[i - toRemove] = items[i]; + } + + ReplaceContents(allNodes); + } + + node.IsExpanded = false; + } + else + { + AddAfter(index, node); + node.IsExpanded = true; + } + } + + + + } + + + + + + + + + +} diff --git a/CLRProfiler/CLRProfiler/DiffTreeListView.resx b/CLRProfiler/CLRProfiler/DiffTreeListView.resx new file mode 100644 index 0000000..3f337e0 --- /dev/null +++ b/CLRProfiler/CLRProfiler/DiffTreeListView.resx @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/CLRProfiler/CLRProfiler/Edge.cs b/CLRProfiler/CLRProfiler/Edge.cs new file mode 100644 index 0000000..f509394 --- /dev/null +++ b/CLRProfiler/CLRProfiler/Edge.cs @@ -0,0 +1,72 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +namespace CLRProfiler +{ + using System; + using System.Drawing; + + /// + /// Summary description for Edge. + /// + internal class Edge : IComparable + { + Vertex fromVertex; + Vertex toVertex; + internal bool selected; + internal Brush brush; + internal Pen pen; + internal Vertex ToVertex + { + get + { + return toVertex; + } + } + internal Vertex FromVertex + { + get + { + return fromVertex; + } + set + { + fromVertex = value; + } + } + internal ulong weight; + internal int width; + internal Point fromPoint, toPoint; + public int CompareTo(Object o) + { + Edge e = (Edge)o; + int diff = this.ToVertex.rectangle.Top - e.ToVertex.rectangle.Top; + if (diff != 0) + return diff; + diff = this.FromVertex.rectangle.Top - e.FromVertex.rectangle.Top; + if (diff != 0) + return diff; + if (e.weight < this.weight) + return -1; + else if (e.weight > this.weight) + return 1; + else + return 0; + } + internal Edge(Vertex fromVertex, Vertex toVertex) + { + this.fromVertex = fromVertex; + this.toVertex = toVertex; + this.weight = 0; + } + + internal void AddWeight(ulong weight) + { + this.weight += weight; + this.fromVertex.outgoingWeight += weight; + this.toVertex.incomingWeight += weight; + } + } +} diff --git a/CLRProfiler/CLRProfiler/FilterForm.cs b/CLRProfiler/CLRProfiler/FilterForm.cs new file mode 100644 index 0000000..43c90f8 --- /dev/null +++ b/CLRProfiler/CLRProfiler/FilterForm.cs @@ -0,0 +1,413 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; +using System.Globalization; + +namespace CLRProfiler +{ + internal enum InterestLevel + { + Ignore = 0, + Display = 1<<0, + Interesting = 1<<1, + Parents = 1<<2, + Children = 1<<3, + InterestingParents = Interesting | Parents, + InterestingChildren = Interesting | Children, + ParentsChildren = Parents | Children, + } + + /// + /// Summary description for FilterForm. + /// + public class FilterForm : System.Windows.Forms.Form + { + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox typeFilterTextBox; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.CheckBox parentsCheckBox; + private System.Windows.Forms.CheckBox childrenCheckBox; + private System.Windows.Forms.CheckBox caseInsensitiveCheckBox; + private System.Windows.Forms.CheckBox onlyFinalizableTypesCheckBox; + internal System.Windows.Forms.TextBox methodFilterTextBox; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + private string[] typeFilters = new string[0]; + internal string[] methodFilters = new string[0]; + internal string[] signatureFilters = new string[0]; + internal ulong[] addressFilters = new ulong[0]; + private bool showChildren = true; + private bool showParents = true; + private bool caseInsensitive = true; + private bool onlyFinalizableTypes = false; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TextBox signatureFilterTextBox; + internal int filterVersion; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TextBox addressFilterTextBox; + private static int versionCounter; + + + // Given the set of names: [aaa, aab, aac, aba, abb, abc, aca, acb, acc], + // The filters are applied sequentially and behave in the following manner: + // + // "aa" => 'aa*' => [aaa, aab, aac] + // "~aa" => not 'aa*' => [aba, abb, abc, aca, acb, acc] + // "aa;ab" => 'aa*' or 'ab*' => [aaa, aab, aac, aba, abb, abc] + // "aa;~aab" => 'aa*' and not 'aab*' => [aaa, aac] + // "~aa;aaa" => not 'aa*' or 'aaa*' => [aaa, aba, abb, abc, aca, acb, acc] + // "~aa;~abb" => not 'aa*' and not 'abb*' => [aba, abc, aca, acb, acc] + // + internal bool IsInterestingName(string name, string[] typeFilters) + { + if (name == "" || typeFilters.Length == 0) + return true; + + bool? isInteresting = null; + foreach (string filter in typeFilters) + { + bool isInclusiveFilter = true; + string realFilter = filter.Trim(); + if (realFilter.Length > 0 && (realFilter[0] == '~' || realFilter[0] == '!')) + { + isInclusiveFilter = false; + realFilter = realFilter.Substring(1).Trim(); + } + + // Skip empty filter, which handles accidental leading, trailing, or doubled semi-colons in the filter string + if (realFilter.Length == 0) + continue; + + if (isInteresting == null || (isInteresting.Value ^ isInclusiveFilter)) + { + bool isPrefixMatch = string.Compare(name, 0, realFilter, 0, realFilter.Length, caseInsensitive, CultureInfo.InvariantCulture) == 0; + if (isPrefixMatch) + isInteresting = isInclusiveFilter; + else if (isInteresting == null) + isInteresting = !isInclusiveFilter; + } + } + return isInteresting ?? true; + } + + internal bool IsInterestingAddress(ulong thisAddress) + { + if (thisAddress == 0 || addressFilters.Length == 0) + return true; + foreach (ulong address in addressFilters) + { + if (address == thisAddress) + return true; + } + return false; + } + + private bool IsInterestingSignature(string signature, string[] signatureFilters) + { + if (signature == null || signature == "" || signatureFilters.Length == 0) + return true; + return IsInterestingName(signature, signatureFilters); + } + + internal bool IsInterestingTypeName(string typeName, string signature, bool typeIsFinalizable) + { + if (onlyFinalizableTypes && !typeIsFinalizable && typeName != "") + return false; + return IsInterestingName(typeName, typeFilters) && IsInterestingSignature(signature, signatureFilters); + } + + internal bool IsInterestingMethodName(string methodName, string signature) + { + return IsInterestingName(methodName, methodFilters) && IsInterestingSignature(signature, signatureFilters); + } + + internal InterestLevel InterestLevelForParentsAndChildren() + { + InterestLevel interestLevel = InterestLevel.Ignore; + if (showParents) + interestLevel |= InterestLevel.Parents; + if (showChildren) + interestLevel |= InterestLevel.Children; + return interestLevel; + } + + public FilterForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.okButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.typeFilterTextBox = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.methodFilterTextBox = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.parentsCheckBox = new System.Windows.Forms.CheckBox(); + this.childrenCheckBox = new System.Windows.Forms.CheckBox(); + this.caseInsensitiveCheckBox = new System.Windows.Forms.CheckBox(); + this.onlyFinalizableTypesCheckBox = new System.Windows.Forms.CheckBox(); + this.label4 = new System.Windows.Forms.Label(); + this.signatureFilterTextBox = new System.Windows.Forms.TextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.addressFilterTextBox = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // okButton + // + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(488, 24); + this.okButton.Name = "okButton"; + this.okButton.TabIndex = 0; + this.okButton.Text = "OK"; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // cancelButton + // + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(488, 64); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.TabIndex = 1; + this.cancelButton.Text = "Cancel"; + // + // typeFilterTextBox + // + this.typeFilterTextBox.Location = new System.Drawing.Point(48, 48); + this.typeFilterTextBox.Name = "typeFilterTextBox"; + this.typeFilterTextBox.Size = new System.Drawing.Size(392, 20); + this.typeFilterTextBox.TabIndex = 2; + this.typeFilterTextBox.Text = ""; + // + // label1 + // + this.label1.Location = new System.Drawing.Point(40, 24); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(368, 23); + this.label1.TabIndex = 3; + this.label1.Text = "Show Types starting with (separate multiple entries with \";\"):"; + // + // label2 + // + this.label2.Location = new System.Drawing.Point(40, 96); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(328, 16); + this.label2.TabIndex = 4; + this.label2.Text = "Show Methods starting with (separate multiple entries with \";\"):"; + // + // methodFilterTextBox + // + this.methodFilterTextBox.Location = new System.Drawing.Point(48, 120); + this.methodFilterTextBox.Name = "methodFilterTextBox"; + this.methodFilterTextBox.Size = new System.Drawing.Size(392, 20); + this.methodFilterTextBox.TabIndex = 5; + this.methodFilterTextBox.Text = ""; + // + // label3 + // + this.label3.Location = new System.Drawing.Point(40, 312); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(48, 16); + this.label3.TabIndex = 6; + this.label3.Text = "Options:"; + // + // parentsCheckBox + // + this.parentsCheckBox.Checked = true; + this.parentsCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + this.parentsCheckBox.Location = new System.Drawing.Point(104, 312); + this.parentsCheckBox.Name = "parentsCheckBox"; + this.parentsCheckBox.Size = new System.Drawing.Size(208, 16); + this.parentsCheckBox.TabIndex = 7; + this.parentsCheckBox.Text = "Show Callers/Referencing Objects"; + // + // childrenCheckBox + // + this.childrenCheckBox.Checked = true; + this.childrenCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + this.childrenCheckBox.Location = new System.Drawing.Point(104, 344); + this.childrenCheckBox.Name = "childrenCheckBox"; + this.childrenCheckBox.Size = new System.Drawing.Size(216, 24); + this.childrenCheckBox.TabIndex = 8; + this.childrenCheckBox.Text = "Show Callees/Referenced Objects"; + // + // caseInsensitiveCheckBox + // + this.caseInsensitiveCheckBox.Checked = true; + this.caseInsensitiveCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + this.caseInsensitiveCheckBox.Location = new System.Drawing.Point(336, 344); + this.caseInsensitiveCheckBox.Name = "caseInsensitiveCheckBox"; + this.caseInsensitiveCheckBox.Size = new System.Drawing.Size(168, 24); + this.caseInsensitiveCheckBox.TabIndex = 9; + this.caseInsensitiveCheckBox.Text = "Ignore Case"; + // + // onlyFinalizableTypesCheckBox + // + this.onlyFinalizableTypesCheckBox.Location = new System.Drawing.Point(336, 312); + this.onlyFinalizableTypesCheckBox.Name = "onlyFinalizableTypesCheckBox"; + this.onlyFinalizableTypesCheckBox.Size = new System.Drawing.Size(168, 16); + this.onlyFinalizableTypesCheckBox.TabIndex = 10; + this.onlyFinalizableTypesCheckBox.Text = "Show only finalizable Types"; + // + // label4 + // + this.label4.Location = new System.Drawing.Point(40, 168); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(328, 16); + this.label4.TabIndex = 11; + this.label4.Text = "Signatures (separate multiple entries with \";\"):"; + // + // signatureFilterTextBox + // + this.signatureFilterTextBox.Location = new System.Drawing.Point(48, 192); + this.signatureFilterTextBox.Name = "signatureFilterTextBox"; + this.signatureFilterTextBox.Size = new System.Drawing.Size(392, 20); + this.signatureFilterTextBox.TabIndex = 12; + this.signatureFilterTextBox.Text = ""; + // + // label5 + // + this.label5.Location = new System.Drawing.Point(40, 240); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(328, 16); + this.label5.TabIndex = 13; + this.label5.Text = "Object addresses (separate multiple entries with \";\"):"; + // + // addressFilterTextBox + // + this.addressFilterTextBox.Location = new System.Drawing.Point(48, 264); + this.addressFilterTextBox.Name = "addressFilterTextBox"; + this.addressFilterTextBox.Size = new System.Drawing.Size(392, 20); + this.addressFilterTextBox.TabIndex = 14; + this.addressFilterTextBox.Text = ""; + // + // FilterForm + // + this.AcceptButton = this.okButton; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(584, 390); + this.Controls.Add(this.addressFilterTextBox); + this.Controls.Add(this.label5); + this.Controls.Add(this.signatureFilterTextBox); + this.Controls.Add(this.label4); + this.Controls.Add(this.onlyFinalizableTypesCheckBox); + this.Controls.Add(this.caseInsensitiveCheckBox); + this.Controls.Add(this.childrenCheckBox); + this.Controls.Add(this.parentsCheckBox); + this.Controls.Add(this.label3); + this.Controls.Add(this.methodFilterTextBox); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.typeFilterTextBox); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.okButton); + this.Name = "FilterForm"; + this.Text = "Set Filters for Types and Methods"; + this.ResumeLayout(false); + + } + #endregion + + internal void SetFilterForm(string typeFilter, string methodFilter, string signatureFilter, string addressFilter, + bool showAncestors, bool showDescendants, bool caseInsensitive, bool onlyFinalizableTypes) + { + typeFilter = typeFilter.Trim(); + if (typeFilter == "") + typeFilters = new string[0]; + else + typeFilters = typeFilter.Split(';'); + + methodFilter = methodFilter.Trim(); + if (methodFilter == "") + methodFilters = new string[0]; + else + methodFilters = methodFilter.Split(';'); + + signatureFilter = signatureFilter.Trim(); + if (signatureFilter == "") + signatureFilters = new string[0]; + else + signatureFilters = signatureFilter.Split(';'); + + addressFilter = addressFilter.Trim(); + if (addressFilter == "") + addressFilters = new ulong[0]; + else + { + string[] addressFilterStrings = addressFilter.Split(';'); + addressFilters = new ulong[addressFilterStrings.Length]; + for (int i = 0; i < addressFilterStrings.Length; i++) + { + string thisAddressFilter = addressFilterStrings[i].Replace(".", ""); + if (thisAddressFilter != "") + { + if (thisAddressFilter.StartsWith("0x") || thisAddressFilter.StartsWith("0X")) + addressFilters[i] = ulong.Parse(thisAddressFilter.Substring(2), NumberStyles.HexNumber); + else + addressFilters[i] = ulong.Parse(thisAddressFilter, NumberStyles.HexNumber); + } + } + } + this.showParents = showAncestors; + this.showChildren = showDescendants; + this.caseInsensitive = caseInsensitive; + this.onlyFinalizableTypes = onlyFinalizableTypes; + + this.filterVersion = ++versionCounter; + + typeFilterTextBox.Text = typeFilter; + methodFilterTextBox.Text = methodFilter; + signatureFilterTextBox.Text = signatureFilter; + addressFilterTextBox.Text = addressFilter; + + parentsCheckBox.Checked = showParents; + childrenCheckBox.Checked = showChildren; + caseInsensitiveCheckBox.Checked = caseInsensitive; + onlyFinalizableTypesCheckBox.Checked = onlyFinalizableTypes; + + } + + private void okButton_Click(object sender, System.EventArgs e) + { + SetFilterForm(typeFilterTextBox.Text, methodFilterTextBox.Text, signatureFilterTextBox.Text, addressFilterTextBox.Text, + parentsCheckBox.Checked, childrenCheckBox.Checked, caseInsensitiveCheckBox.Checked, onlyFinalizableTypesCheckBox.Checked); + } + } +} diff --git a/CLRProfiler/CLRProfiler/FilterForm.resx b/CLRProfiler/CLRProfiler/FilterForm.resx new file mode 100644 index 0000000..7977370 --- /dev/null +++ b/CLRProfiler/CLRProfiler/FilterForm.resx @@ -0,0 +1,265 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + Private + + + False + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + Private + + + False + + + Assembly + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + False + + + Private + + + Private + + + Private + + + Private + + + False + + + False + + + Private + + + Private + + + Private + + + False + + + Private + + + False + + + (Default) + + + False + + + False + + + 8, 8 + + + True + + + FilterForm + + + 80 + + + True + + + Private + + \ No newline at end of file diff --git a/CLRProfiler/CLRProfiler/FindRoutineForm.cs b/CLRProfiler/CLRProfiler/FindRoutineForm.cs new file mode 100644 index 0000000..2494a59 --- /dev/null +++ b/CLRProfiler/CLRProfiler/FindRoutineForm.cs @@ -0,0 +1,145 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace CLRProfiler +{ + /// + /// Summary description for Form5. + /// + public class FindRoutineForm : System.Windows.Forms.Form + { + private System.Windows.Forms.Label label1; + internal System.Windows.Forms.TextBox nameTextBox; + private System.Windows.Forms.Label label2; + internal System.Windows.Forms.TextBox signatureTextBox; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Button cancelButton; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public FindRoutineForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose(bool disposing) { + if (disposing) { + if(components != null) + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.cancelButton = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.nameTextBox = new System.Windows.Forms.TextBox(); + this.okButton = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.signatureTextBox = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // cancelButton + // + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(543, 65); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(87, 27); + this.cancelButton.TabIndex = 5; + this.cancelButton.Text = "Cancel"; + // + // label1 + // + this.label1.Location = new System.Drawing.Point(28, 19); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(206, 18); + this.label1.TabIndex = 0; + this.label1.Text = "Name of routine or type to find"; + // + // nameTextBox + // + this.nameTextBox.Location = new System.Drawing.Point(37, 37); + this.nameTextBox.Name = "nameTextBox"; + this.nameTextBox.Size = new System.Drawing.Size(449, 20); + this.nameTextBox.TabIndex = 1; + // + // okButton + // + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.okButton.Location = new System.Drawing.Point(543, 19); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(87, 26); + this.okButton.TabIndex = 4; + this.okButton.Text = "OK"; + // + // label2 + // + this.label2.Location = new System.Drawing.Point(28, 84); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(188, 19); + this.label2.TabIndex = 2; + this.label2.Text = "Signature of routine to find"; + // + // signatureTextBox + // + this.signatureTextBox.Location = new System.Drawing.Point(37, 103); + this.signatureTextBox.Name = "signatureTextBox"; + this.signatureTextBox.Size = new System.Drawing.Size(449, 20); + this.signatureTextBox.TabIndex = 3; + // + // FindRoutineForm + // + this.AcceptButton = this.okButton; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(656, 158); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.okButton); + this.Controls.Add(this.signatureTextBox); + this.Controls.Add(this.label2); + this.Controls.Add(this.nameTextBox); + this.Controls.Add(this.label1); + this.Name = "FindRoutineForm"; + this.Text = "Find Routine"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + #endregion + + private void okButton_Click(object sender, System.EventArgs e) + { + Close(); + } + + private void cancelButton_Click(object sender, System.EventArgs e) + { + Close(); + } + } +} diff --git a/CLRProfiler/CLRProfiler/FindRoutineForm.resx b/CLRProfiler/CLRProfiler/FindRoutineForm.resx new file mode 100644 index 0000000..7080a7d --- /dev/null +++ b/CLRProfiler/CLRProfiler/FindRoutineForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/CLRProfiler/CLRProfiler/FunctionFilter.cs b/CLRProfiler/CLRProfiler/FunctionFilter.cs new file mode 100644 index 0000000..51ed497 --- /dev/null +++ b/CLRProfiler/CLRProfiler/FunctionFilter.cs @@ -0,0 +1,356 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace CLRProfiler +{ + /// + /// Summary description for FunctionFilter. + /// + internal class DlgFunctionFilter : System.Windows.Forms.Form + { + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button btnOk; + private System.Windows.Forms.Button btnCancel; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + private System.Windows.Forms.Button btnIncludeFn0; + private System.Windows.Forms.Button btnIncludeFn1; + private System.Windows.Forms.Button btnExcludeFn0; + private System.Windows.Forms.Button btnExcludeFn1; + private System.Windows.Forms.Button btnClear; + + private ITreeOwner m_treeOwner; + + private CallTreeForm.FnViewFilter[] includeFns; + private System.Windows.Forms.TextBox tbIncludeFn0; + private System.Windows.Forms.TextBox tbIncludeFn1; + private System.Windows.Forms.TextBox tbExcludeFn0; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TextBox tbExcludeFn1; + private CallTreeForm.FnViewFilter[] excludeFns; + + private TreeNode node; + + internal DlgFunctionFilter( ITreeOwner TreeOwner ) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + m_treeOwner = TreeOwner; + node = new TreeNode(TreeNode.NodeType.Call, 0 ); + + includeFns = m_treeOwner.GetIncludeFilters(); + tbIncludeFn0.Text = GetName( includeFns[0].nodetype, includeFns[0].functionId ); + tbIncludeFn0.Enabled = true; + tbIncludeFn1.Text = GetName( includeFns[1].nodetype, includeFns[1].functionId ); + tbIncludeFn1.Enabled = true; + + excludeFns = m_treeOwner.GetExcludeFilters(); + tbExcludeFn0.Text = GetName( excludeFns[0].nodetype, excludeFns[0].functionId ); + tbExcludeFn0.Enabled = true; + tbExcludeFn1.Text = GetName( excludeFns[1].nodetype, excludeFns[1].functionId ); + tbExcludeFn1.Enabled = true; + } + + private string GetName( TreeNode.NodeType nodetype, int id ) + { + if (id == -1) + { + return ""; + } + else + { + if (nodetype == TreeNode.NodeType.Call) + return m_treeOwner.MakeNameForFunction( id ); + else + return m_treeOwner.MakeNameForAllocation( id, 0 ); + } + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.btnOk = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnIncludeFn0 = new System.Windows.Forms.Button(); + this.btnIncludeFn1 = new System.Windows.Forms.Button(); + this.btnExcludeFn1 = new System.Windows.Forms.Button(); + this.btnExcludeFn0 = new System.Windows.Forms.Button(); + this.btnClear = new System.Windows.Forms.Button(); + this.tbIncludeFn0 = new System.Windows.Forms.TextBox(); + this.tbIncludeFn1 = new System.Windows.Forms.TextBox(); + this.tbExcludeFn1 = new System.Windows.Forms.TextBox(); + this.tbExcludeFn0 = new System.Windows.Forms.TextBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(24, 32); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(152, 16); + this.label1.TabIndex = 0; + this.label1.Text = "Must Include Functions"; + // + // label2 + // + this.label2.Location = new System.Drawing.Point(24, 136); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(168, 16); + this.label2.TabIndex = 3; + this.label2.Text = "Exclude Functions (prune)"; + // + // btnOk + // + this.btnOk.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOk.Location = new System.Drawing.Point(104, 304); + this.btnOk.Name = "btnOk"; + this.btnOk.Size = new System.Drawing.Size(72, 32); + this.btnOk.TabIndex = 6; + this.btnOk.Text = "OK"; + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(224, 304); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(72, 32); + this.btnCancel.TabIndex = 7; + this.btnCancel.Text = "Cancel"; + // + // btnIncludeFn0 + // + this.btnIncludeFn0.Location = new System.Drawing.Point(320, 56); + this.btnIncludeFn0.Name = "btnIncludeFn0"; + this.btnIncludeFn0.Size = new System.Drawing.Size(48, 24); + this.btnIncludeFn0.TabIndex = 9; + this.btnIncludeFn0.Text = "Find..."; + this.btnIncludeFn0.Click += new System.EventHandler(this.btnIncludeFn0_Click); + // + // btnIncludeFn1 + // + this.btnIncludeFn1.Location = new System.Drawing.Point(320, 96); + this.btnIncludeFn1.Name = "btnIncludeFn1"; + this.btnIncludeFn1.Size = new System.Drawing.Size(48, 24); + this.btnIncludeFn1.TabIndex = 11; + this.btnIncludeFn1.Text = "Find..."; + this.btnIncludeFn1.Click += new System.EventHandler(this.btnIncludeFn1_Click); + // + // btnExcludeFn1 + // + this.btnExcludeFn1.Location = new System.Drawing.Point(320, 200); + this.btnExcludeFn1.Name = "btnExcludeFn1"; + this.btnExcludeFn1.Size = new System.Drawing.Size(48, 24); + this.btnExcludeFn1.TabIndex = 15; + this.btnExcludeFn1.Text = "Find..."; + this.btnExcludeFn1.Click += new System.EventHandler(this.btnExcludeFn1_Click); + // + // btnExcludeFn0 + // + this.btnExcludeFn0.Location = new System.Drawing.Point(320, 160); + this.btnExcludeFn0.Name = "btnExcludeFn0"; + this.btnExcludeFn0.Size = new System.Drawing.Size(48, 24); + this.btnExcludeFn0.TabIndex = 13; + this.btnExcludeFn0.Text = "Find..."; + this.btnExcludeFn0.Click += new System.EventHandler(this.btnExcludeFn0_Click); + // + // btnClear + // + this.btnClear.Location = new System.Drawing.Point(136, 240); + this.btnClear.Name = "btnClear"; + this.btnClear.Size = new System.Drawing.Size(56, 24); + this.btnClear.TabIndex = 16; + this.btnClear.Text = "Clear"; + this.btnClear.Click += new System.EventHandler(this.btnClear_Click); + // + // tbIncludeFn0 + // + this.tbIncludeFn0.Location = new System.Drawing.Point(24, 56); + this.tbIncludeFn0.Name = "tbIncludeFn0"; + this.tbIncludeFn0.Size = new System.Drawing.Size(280, 20); + this.tbIncludeFn0.TabIndex = 18; + this.tbIncludeFn0.Text = ""; + // + // tbIncludeFn1 + // + this.tbIncludeFn1.Location = new System.Drawing.Point(24, 96); + this.tbIncludeFn1.Name = "tbIncludeFn1"; + this.tbIncludeFn1.Size = new System.Drawing.Size(280, 20); + this.tbIncludeFn1.TabIndex = 19; + this.tbIncludeFn1.Text = ""; + // + // tbExcludeFn1 + // + this.tbExcludeFn1.Location = new System.Drawing.Point(24, 200); + this.tbExcludeFn1.Name = "tbExcludeFn1"; + this.tbExcludeFn1.Size = new System.Drawing.Size(280, 20); + this.tbExcludeFn1.TabIndex = 21; + this.tbExcludeFn1.Text = ""; + // + // tbExcludeFn0 + // + this.tbExcludeFn0.Location = new System.Drawing.Point(24, 160); + this.tbExcludeFn0.Name = "tbExcludeFn0"; + this.tbExcludeFn0.Size = new System.Drawing.Size(280, 20); + this.tbExcludeFn0.TabIndex = 20; + this.tbExcludeFn0.Text = ""; + // + // groupBox1 + // + this.groupBox1.Location = new System.Drawing.Point(8, 8); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(376, 272); + this.groupBox1.TabIndex = 22; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Filters"; + // + // DlgFunctionFilter + // + this.AcceptButton = this.btnOk; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(400, 358); + this.Controls.Add(this.tbExcludeFn1); + this.Controls.Add(this.tbExcludeFn0); + this.Controls.Add(this.tbIncludeFn1); + this.Controls.Add(this.tbIncludeFn0); + this.Controls.Add(this.btnClear); + this.Controls.Add(this.btnExcludeFn1); + this.Controls.Add(this.btnExcludeFn0); + this.Controls.Add(this.btnIncludeFn1); + this.Controls.Add(this.btnIncludeFn0); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOk); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.groupBox1); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "DlgFunctionFilter"; + this.Text = "FunctionFilter"; + this.ResumeLayout(false); + + } + #endregion + + private void btnIncludeFn0_Click(object sender, System.EventArgs e) + { + CallTreeForm.FnViewFilter viewFilter = FindFunction( tbIncludeFn0 ); + if (viewFilter.functionId > 0) + { + includeFns[0] = viewFilter; + } + + } + + private void btnIncludeFn1_Click(object sender, System.EventArgs e) + { + CallTreeForm.FnViewFilter viewFilter = FindFunction( tbIncludeFn1 ); + if (viewFilter.functionId > 0) + { + includeFns[1] = viewFilter; + } + + } + + private void btnExcludeFn0_Click(object sender, System.EventArgs e) + { + CallTreeForm.FnViewFilter viewFilter = FindFunction( tbExcludeFn0 ); + if (viewFilter.functionId > 0) + { + excludeFns[0] = viewFilter; + } + + } + + private void btnExcludeFn1_Click(object sender, System.EventArgs e) + { + CallTreeForm.FnViewFilter viewFilter = FindFunction( tbExcludeFn1 ); + if (viewFilter.functionId > 0) + { + excludeFns[1] = viewFilter; + } + } + + // + // Popup a dialog to let the user select a function name from + // the list of all functions in the current view. + // + // Returns: + // -1: Dialog cancelled + // >=0: Fn id. + // + + private CallTreeForm.FnViewFilter FindFunction( TextBox tb ) + { + int id = -2; + TreeNode.NodeType nodetype = TreeNode.NodeType.Call; + FunctionFind functionFind = new FunctionFind( m_treeOwner, tb.Text ); + CallTreeForm.FnViewFilter viewFilter = new CallTreeForm.FnViewFilter(); + + if (functionFind.ShowDialog() == DialogResult.OK) + { + id = functionFind.SelectedFunctionId; + if (id >= 0) + { + nodetype = functionFind.SelectedNodeType; + tb.Text = GetName( nodetype, id ); + } + } + + viewFilter.functionId = id; + viewFilter.nodetype = nodetype; + + return viewFilter; + } + + private void btnClear_Click(object sender, System.EventArgs e) + { + includeFns[0].functionId = -1; + includeFns[1].functionId = -1; + excludeFns[0].functionId = -1; + excludeFns[1].functionId = -1; + + tbIncludeFn0.Text = GetName( includeFns[0].nodetype, includeFns[0].functionId ); + tbIncludeFn1.Text = GetName( includeFns[1].nodetype, includeFns[1].functionId ); + tbExcludeFn0.Text = GetName( excludeFns[0].nodetype, excludeFns[0].functionId ); + tbExcludeFn1.Text = GetName( excludeFns[1].nodetype, excludeFns[1].functionId ); + } + + + + } +} diff --git a/CLRProfiler/CLRProfiler/FunctionFilter.resx b/CLRProfiler/CLRProfiler/FunctionFilter.resx new file mode 100644 index 0000000..fbe7edd --- /dev/null +++ b/CLRProfiler/CLRProfiler/FunctionFilter.resx @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/CLRProfiler/CLRProfiler/FunctionFind.cs b/CLRProfiler/CLRProfiler/FunctionFind.cs new file mode 100644 index 0000000..9134d3a --- /dev/null +++ b/CLRProfiler/CLRProfiler/FunctionFind.cs @@ -0,0 +1,246 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace CLRProfiler +{ + /// + /// Summary description for FunctionFind. + /// + internal class FunctionFind : System.Windows.Forms.Form + { + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox tbSearchString; + private System.Windows.Forms.ListBox lbFunctions; + private System.Windows.Forms.Button btnSearch; + private System.Windows.Forms.Button btnOk; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Label label2; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + private ITreeOwner TreeOwner; + internal int SelectedFunctionId; + internal TreeNode.NodeType SelectedNodeType; + + class LineItem + { + internal TreeNode.NodeType nodeType; + internal int id; + internal string Name; + + public override string ToString() + { + return Name; + } + }; + + internal FunctionFind( ITreeOwner treeOwner, string SearchString ) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + TreeOwner = treeOwner; + tbSearchString.Text = SearchString; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.tbSearchString = new System.Windows.Forms.TextBox(); + this.lbFunctions = new System.Windows.Forms.ListBox(); + this.btnSearch = new System.Windows.Forms.Button(); + this.btnOk = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(24, 24); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(248, 32); + this.label1.TabIndex = 0; + this.label1.Text = "Enter a search string, then click the \'Search\' button."; + // + // tbSearchString + // + this.tbSearchString.Location = new System.Drawing.Point(24, 56); + this.tbSearchString.Name = "tbSearchString"; + this.tbSearchString.Size = new System.Drawing.Size(240, 20); + this.tbSearchString.TabIndex = 1; + this.tbSearchString.Text = ""; + // + // lbFunctions + // + this.lbFunctions.Location = new System.Drawing.Point(24, 136); + this.lbFunctions.Name = "lbFunctions"; + this.lbFunctions.Size = new System.Drawing.Size(240, 82); + this.lbFunctions.TabIndex = 2; + this.lbFunctions.SelectedIndexChanged += new System.EventHandler(this.lbFunctions_SelectedIndexChanged); + // + // btnSearch + // + this.btnSearch.Location = new System.Drawing.Point(72, 96); + this.btnSearch.Name = "btnSearch"; + this.btnSearch.Size = new System.Drawing.Size(136, 24); + this.btnSearch.TabIndex = 3; + this.btnSearch.Text = "Search"; + this.btnSearch.Click += new System.EventHandler(this.btnSearch_Click); + // + // btnOk + // + this.btnOk.Location = new System.Drawing.Point(40, 288); + this.btnOk.Name = "btnOk"; + this.btnOk.Size = new System.Drawing.Size(56, 24); + this.btnOk.TabIndex = 4; + this.btnOk.Text = "OK"; + this.btnOk.Click += new System.EventHandler(this.btnOk_Click); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(160, 288); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(56, 24); + this.btnCancel.TabIndex = 5; + this.btnCancel.Text = "Cancel"; + // + // label2 + // + this.label2.Location = new System.Drawing.Point(32, 240); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(232, 24); + this.label2.TabIndex = 6; + this.label2.Text = "Select your function from the list above."; + // + // FunctionFind + // + this.AcceptButton = this.btnSearch; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(292, 326); + this.Controls.Add(this.label2); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOk); + this.Controls.Add(this.btnSearch); + this.Controls.Add(this.lbFunctions); + this.Controls.Add(this.tbSearchString); + this.Controls.Add(this.label1); + this.Name = "FunctionFind"; + this.Text = "FunctionFind"; + this.ResumeLayout(false); + + } + #endregion + + + private void btnSearch_Click(object sender, System.EventArgs e) + { + int i; + LineItem lineItem; + string fn; + string matchString = tbSearchString.Text; + + lbFunctions.Items.Clear(); + + // Add functions + for( i = 0; ; i++) + { + fn = TreeOwner.MakeNameForFunction(i); + if (fn == null) + { + break; + } + + if ( fn.IndexOf( matchString ) != -1 ) + { + lineItem = new LineItem(); + lineItem.id = i; + lineItem.Name = fn; + lineItem.nodeType = TreeNode.NodeType.Call; + + lbFunctions.Items.Add( lineItem ); + } + } + + // Add allocations + for( i = 0; ; i++) + { + fn = TreeOwner.MakeNameForAllocation(i, 0); + if (fn == null) + { + break; + } + + if ( fn.IndexOf( matchString ) != -1 ) + { + lineItem = new LineItem(); + lineItem.id = i; + lineItem.Name = fn; + lineItem.nodeType = TreeNode.NodeType.Allocation; + + lbFunctions.Items.Add( lineItem ); + } + } + } + + private void btnOk_Click(object sender, System.EventArgs e) + { + if (lbFunctions.SelectedIndex < 0) + { + // Nothing selected + if (lbFunctions.Items.Count == 0) + { + MessageBox.Show( "To populate the list box, enter a search string and click the search button." ); + } + else + { + MessageBox.Show( "Please select a function from the list box." ); + } + return; + } + + SelectedFunctionId = ((LineItem)lbFunctions.SelectedItem).id; + SelectedNodeType = ((LineItem)lbFunctions.SelectedItem).nodeType; + DialogResult = DialogResult.OK; + Close(); + } + + private void lbFunctions_SelectedIndexChanged(object sender, System.EventArgs e) + { + // User selected function. Make the OK button the default button. + this.AcceptButton = this.btnOk; + } + } +} diff --git a/CLRProfiler/CLRProfiler/FunctionFind.resx b/CLRProfiler/CLRProfiler/FunctionFind.resx new file mode 100644 index 0000000..fbe7edd --- /dev/null +++ b/CLRProfiler/CLRProfiler/FunctionFind.resx @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/CLRProfiler/CLRProfiler/Graph.cs b/CLRProfiler/CLRProfiler/Graph.cs new file mode 100644 index 0000000..004cbba --- /dev/null +++ b/CLRProfiler/CLRProfiler/Graph.cs @@ -0,0 +1,353 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +//#define whatever +namespace CLRProfiler +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Collections.Generic; + + /// + /// Summary description for Graph. + /// + internal class Graph + { + internal object graphSource; + internal Dictionary vertices; + Vertex topVertex; + Vertex bottomVertex; + internal enum GraphType + { + AllocationGraph, + CallGraph, + HeapGraph, + FunctionGraph, + ModuleGraph, + ClassGraph, + AssemblyGraph, + HandleAllocationGraph, + ReferenceGraph, + Invalid + }; + internal GraphType graphType; + internal ObjectGraph.BuildTypeGraphOptions typeGraphOptions; + internal int allocatedAfterTickIndex; + internal int allocatedBeforeTickIndex; + internal int previousGraphTickIndex; + internal int filterVersion; + + internal Vertex TopVertex + { + get + { + return topVertex; + } + } + + internal Vertex BottomVertex + { + get + { + return bottomVertex; + } + } + + internal Graph(object graphSource) + { + this.graphSource = graphSource; + vertices = new Dictionary(); + topVertex = FindOrCreateVertex("", null, null); + bottomVertex = FindOrCreateVertex("", null, null); + } + + private string NameSignatureModule(string name, string signature, string module) + { + string nameSignatureModule = name; + if (signature != null) + nameSignatureModule += signature; + if (module != null) + nameSignatureModule += module; + return nameSignatureModule; + } + + internal Vertex FindOrCreateVertex(string name, string signature, string module) + { + string nameSignatureModule = NameSignatureModule(name, signature, module); + Vertex vertex; + if (!vertices.TryGetValue(nameSignatureModule, out vertex)) + { + vertex = new Vertex(name, signature, module, this); + vertices[nameSignatureModule] = vertex; + } + return vertex; + } + + internal Vertex CreateVertex(string name, string signature, string key) + { + Vertex vertex = new Vertex(name, signature, null, this); + vertices[key] = vertex; + return vertex; + } + + internal Vertex FindVertex(string key) + { + Vertex vertex; + vertices.TryGetValue(key, out vertex); + return vertex; + } + + internal Vertex FindVertex(string name, string signature, string module) + { + string nameSignatureModule = name; + if (signature != null) + nameSignatureModule += signature; + if (module != null) + nameSignatureModule += module; + Vertex v; + if (vertices.TryGetValue(nameSignatureModule, out v)) + return v; + else + return null; + } + + internal Edge FindOrCreateEdge(Vertex fromVertex, Vertex toVertex) + { + Debug.Assert(fromVertex != topVertex || toVertex != bottomVertex); + return fromVertex.FindOrCreateOutgoingEdge(toVertex); + } +#if whatever + internal void AssignLevelsToVertices() + { + foreach (Vertex v in vertices.Values) + { + v.level = Int32.MaxValue; + if ( v != topVertex + && v != bottomVertex + && v.incomingEdges.Count == 0 + && v.outgoingEdges.Count != 0) + topVertex.FindOrCreateOutgoingEdge(v); + } + topVertex.AssignLevel(0); + int maxLevel = 0; + foreach (Vertex v in vertices.Values) + { + if (v.level != Int32.MaxValue && maxLevel < v.level) + { + maxLevel = v.level; + } + } + + bottomVertex.level = maxLevel + 1; + + if (!this.isCallGraph) + { + // line up all the class names in one level + foreach (Edge e in bottomVertex.incomingEdges.Values) + { + e.FromVertex.level = maxLevel; + } + + // this pass optimizes the layout somewhat + // so we have fewer back edges + // the idea is to move vertices to the left, if doing so creates no + // new back edges, but possibly removes some + bool change; + do + { + change = false; + foreach (Vertex v in vertices.Values) + { + int minOutgoingLevel = Int32.MaxValue; + foreach (Edge e in v.outgoingEdges.Values) + { + if (minOutgoingLevel > e.ToVertex.level) + minOutgoingLevel = e.ToVertex.level; + } + if (minOutgoingLevel < Int32.MaxValue && minOutgoingLevel - 1 > v.level) + { + v.level = minOutgoingLevel - 1; + change = true; + } + } + } + while (change); + } + } +#else + internal void AssignLevelsToVertices() + { + const int UNASSIGNED_LEVEL = Int32.MaxValue/2; + foreach (Vertex v in vertices.Values) + { + v.level = UNASSIGNED_LEVEL; + if ( v != topVertex + && v != bottomVertex) + { + if ( v.incomingEdges.Count == 0 + && v.outgoingEdges.Count != 0) + topVertex.FindOrCreateOutgoingEdge(v); + else if (v.incomingEdges.Count != 0 + && v.outgoingEdges.Count == 0) + v.FindOrCreateOutgoingEdge(bottomVertex); + } + } + topVertex.level = 0; + bottomVertex.level = Int32.MaxValue; + + while (true) + { + int assignedVertexCount = 0; + foreach (Vertex v in vertices.Values) + { + // If the vertex already has a level, we're done + if (v.level != UNASSIGNED_LEVEL) + continue; + + // It the vertex is only called by vertices + // that have a level, we assign this vertex + // one more than the max of the callers. + int maxIncomingLevel = 0; + foreach (Edge e in v.incomingEdges.Values) + { + if (maxIncomingLevel < e.FromVertex.level) + maxIncomingLevel = e.FromVertex.level; + } + if (maxIncomingLevel < UNASSIGNED_LEVEL) + { + v.level = maxIncomingLevel + 1; + assignedVertexCount++; + continue; + } + + // If the callees all have levels assigned, + // we assign this vertex one less than + // the min of the callees. + int minOutgoingLevel = Int32.MaxValue; + foreach (Edge e in v.outgoingEdges.Values) + { + if (minOutgoingLevel > e.ToVertex.level) + minOutgoingLevel = e.ToVertex.level; + } + if (minOutgoingLevel > UNASSIGNED_LEVEL) + { + v.level = minOutgoingLevel - 1; + assignedVertexCount++; + continue; + } + } + + // If we made progress, continue. + if (assignedVertexCount > 0) + continue; + + // There are no more easy vertices. + // Among the ones remaining (if any), choose one + // with dependencies on assigned vertices, + // with minimum input dependencies, and among those + // the one with maximum output dependencies. + // This minimizes the number of back edges for this + // vertex and maximizes the number of unblocked vertices. + + Vertex bestVertex = null; + int minInputCount = Int32.MaxValue; + int maxOutputCount = 0; + foreach (Vertex v in vertices.Values) + { + if (v.level != UNASSIGNED_LEVEL) + continue; + int assignedInputCount = 0; + int unAssignedInputCount = 0; + foreach (Edge e in v.incomingEdges.Values) + { + if (e.FromVertex.level == UNASSIGNED_LEVEL) + unAssignedInputCount++; + else + assignedInputCount++; + } + if (assignedInputCount == 0) + continue; + int unAssignedOutputCount = 0; + foreach (Edge e in v.outgoingEdges.Values) + { + if (e.ToVertex.level == UNASSIGNED_LEVEL) + unAssignedOutputCount++; + } + if ( unAssignedInputCount < minInputCount + || unAssignedInputCount == minInputCount && unAssignedOutputCount > maxOutputCount) + { + bestVertex = v; + minInputCount = unAssignedInputCount; + maxOutputCount = unAssignedOutputCount; + } + } + // If we couldn't find a vertex, we are done. + if (bestVertex == null) + break; + int maxInputLevel = 0; + foreach (Edge e in bestVertex.incomingEdges.Values) + { + if ( e.FromVertex.level != UNASSIGNED_LEVEL + && e.FromVertex.level > maxInputLevel) + maxInputLevel = e.FromVertex.level; + } + bestVertex.level = maxInputLevel + 1; + } + // Now every vertex should have a level. + // Reassign the high vertices. + int reassignedVertexCount; + int notReassignableVertexCount; + do + { + reassignedVertexCount = 0; + notReassignableVertexCount = 0; + foreach (Vertex v in vertices.Values) + { + if (v.level < UNASSIGNED_LEVEL) + continue; + int maxInputLevel = 0; + foreach (Edge e in v.incomingEdges.Values) + { + if (maxInputLevel < e.FromVertex.level) + maxInputLevel = e.FromVertex.level; + } + if (maxInputLevel < UNASSIGNED_LEVEL) + { + v.level = maxInputLevel + 1; + reassignedVertexCount++; + } + else + { + notReassignableVertexCount++; + } + } + } + while (reassignedVertexCount > 0); + Debug.Assert(notReassignableVertexCount == 0); + + int maxLevel = 0; + foreach (Vertex v in vertices.Values) + { + if (v != bottomVertex) + { + if (maxLevel < v.level) + maxLevel = v.level; + } + } + + bottomVertex.level = maxLevel + 1; + + // line up all the leaf nodes in one level + foreach (Edge e in bottomVertex.incomingEdges.Values) + { + Vertex v = e.FromVertex; + if (v.outgoingEdges.Count == 1) + e.FromVertex.level = maxLevel; + } + } +#endif + } +} diff --git a/CLRProfiler/CLRProfiler/GraphBase.cs b/CLRProfiler/CLRProfiler/GraphBase.cs new file mode 100644 index 0000000..5dcf749 --- /dev/null +++ b/CLRProfiler/CLRProfiler/GraphBase.cs @@ -0,0 +1,251 @@ +using System; +using System.Collections; +using System.Text; +using System.Diagnostics; +using System.Drawing; + +namespace CLRProfiler +{ + /// + /// Summary description for GraphBase. + /// + public class GraphBase + { + #region private data member + private bool placeVertices = true; + private bool placeEdges = true; + private ulong totalWeight; + private int totalHeight = 100; + private float scale = 1.0f; + private Graph graph = null; + //private Graph callGraph = null; + #endregion + #region public data member + public ArrayList levelList; + #endregion + public GraphBase() + { + + } + #region public methods + internal void GetAllocationGraph(ReadLogResult readLogResult) + { + graph = readLogResult.allocatedHistogram.BuildAllocationGraph(new FilterForm()); + PlaceVertices(); + } + + /*public void GetCallGraph(ReadLogResult readLogResult) + { + callGraph = readLogResult.callstackHistogram.BuildCallGraph(); + PlaceVertices(); + } + + public void GetFunctionGraph(ReadLogResult readLogResult) + { + graph = readLogResult.functionList.BuildFunctionGraph(); + PlaceVertices(); + }*/ + + public int SelectedVertexCount(out Vertex selectedVertex) + { + int selectedCount = 0; + selectedVertex = null; + foreach (Vertex v in graph.vertices.Values) + { + if (v.selected) + { + selectedCount++; + selectedVertex = v; + } + } + return selectedCount; + } + #endregion + #region private methods + private ArrayList BuildLevels(Graph g) + { + ArrayList al = new ArrayList(); + for (int level = 0; level <= g.BottomVertex.level; level++) + { + al.Add(new ArrayList()); + } + foreach (Vertex v in g.vertices.Values) + { + if (v.level <= g.BottomVertex.level) + { + ArrayList all = (ArrayList)al[v.level]; + all.Add(v); + } + else + { + Debug.Assert(v.level == int.MaxValue); + } + } + foreach (ArrayList all in al) + { + all.Sort(); + } + return al; + } + + internal string formatWeight(ulong weight) + { + if (graph.graphType == Graph.GraphType.CallGraph) + { + if (weight == 1) + return "1 call"; + else + return string.Format("{0} calls", weight); + } + if(graph.graphType == Graph.GraphType.AssemblyGraph) + { + if(weight == 1) + { + return "1 assembly"; + } + else + { + return weight + " assemblies"; + } + } + else + { + double w = weight; + string byteString = "bytes"; + if (w >= 1024) + { + w /= 1024; + byteString = "kB "; + } + if (w >= 1024) + { + w /= 1024; + byteString = "MB "; + } + if (w >= 1024) + { + w /= 1024; + byteString = "GB "; + } + string format = "{0,4:f0} {1} ({2:f2}%)"; + if (w < 10) + format = "{0,4:f1} {1} ({2:f2}%)"; + return string.Format(format, w, byteString, weight*100.0/totalWeight); + } + } + + private void PlaceEdges(float scale) + { + foreach (Vertex v in graph.vertices.Values) + { + PlaceEdges(v.incomingEdges.Values, true, scale); + PlaceEdges(v.outgoingEdges.Values, false, scale); + } + } + private void PlaceEdges(ICollection edgeCollection, bool isIncoming, float scale) + { + ArrayList edgeList = new ArrayList(edgeCollection); + edgeList.Sort(); + foreach (Edge e in edgeList) + { + float fwidth = e.weight*scale; + } + } + + private void PlaceVertices() + { + graph.AssignLevelsToVertices(); + totalWeight = 0; + foreach (Vertex v in graph.vertices.Values) + { + v.weight = v.incomingWeight; + if (v.weight < v.outgoingWeight) + v.weight = v.outgoingWeight; + if (graph.graphType == Graph.GraphType.CallGraph) + { + if (totalWeight < v.weight) + totalWeight = v.weight; + } + } + if (graph.graphType != Graph.GraphType.CallGraph) + totalWeight = graph.TopVertex.weight; + if (totalWeight == 0) + { + totalWeight = 1; + } + + ArrayList al = levelList = BuildLevels(graph); + scale = (float)totalHeight/totalWeight; + if (placeVertices) + { + for (int level = graph.TopVertex.level; + level <= graph.BottomVertex.level; + level++) + { + ArrayList all = (ArrayList)al[level]; + foreach (Vertex v in all) + { + if (graph.graphType == Graph.GraphType.CallGraph) + { + v.basicWeight = v.incomingWeight - v.outgoingWeight; + if (v.basicWeight < 0) + v.basicWeight = 0; + v.weightString = string.Format("Gets {0}, causes {1}", + formatWeight(v.basicWeight), + formatWeight(v.outgoingWeight)); + } + else + { + if (v.count == 0) + v.weightString = formatWeight(v.weight); + else if (v.count == 1) + v.weightString = string.Format("{0} (1 object, {1})", formatWeight(v.weight), formatWeight(v.basicWeight)); + else + v.weightString = string.Format("{0} ({1} objects, {2})", formatWeight(v.weight), v.count, formatWeight(v.basicWeight)); + } + + } + int y = 10; + ulong levelWeight = 0; + foreach (Vertex v in all) + levelWeight += v.weight; + float levelHeight = levelWeight*scale; + if (levelHeight < totalHeight*0.5) + y+= (int)((totalHeight - levelHeight)*2); + foreach (Vertex v in all) + { + // For the in-between vertices, sometimes it's good + // to shift them down a little to line them up with + // whatever is going into them. Unless of course + // we would need to shift too much... + if (v.level < graph.BottomVertex.level-1) + { + ulong highestWeight = 0; + foreach (Edge e in v.incomingEdges.Values) + { + if (e.weight > highestWeight && e.FromVertex.level < level) + { + highestWeight = e.weight; + } + } + + } + float fHeight = v.weight*scale; + int iHeight = (int)fHeight; + if (iHeight < 1) + iHeight = 1; + if (placeEdges) + PlaceEdges(v.outgoingEdges.Values, false, scale); + } + } + } + if (placeEdges) + PlaceEdges(scale); + } + #endregion + internal Graph basegraph + { + get { return graph;} + } + } +} diff --git a/CLRProfiler/CLRProfiler/GraphViewForm.cs b/CLRProfiler/CLRProfiler/GraphViewForm.cs new file mode 100644 index 0000000..2f235a1 --- /dev/null +++ b/CLRProfiler/CLRProfiler/GraphViewForm.cs @@ -0,0 +1,2074 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Windows.Forms; +using System.Diagnostics; +using System.Globalization; +using System.Text; + +namespace CLRProfiler +{ + /// + /// Summary description for GraphViewForm. + /// + internal class GraphViewForm : System.Windows.Forms.Form + { + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Panel graphPanel; + private System.Windows.Forms.RadioButton radioButton1; + private System.Windows.Forms.GroupBox scaleGroupBox; + private System.Windows.Forms.RadioButton radioButton2; + private System.Windows.Forms.RadioButton radioButton3; + private System.Windows.Forms.RadioButton radioButton4; + private System.Windows.Forms.RadioButton radioButton5; + private System.Windows.Forms.RadioButton radioButton6; + private System.Windows.Forms.RadioButton radioButton7; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.RadioButton radioButton8; + private System.Windows.Forms.RadioButton radioButton9; + private System.Windows.Forms.RadioButton radioButton10; + private System.Windows.Forms.RadioButton radioButton11; + private System.Windows.Forms.RadioButton radioButton12; + private System.Windows.Forms.RadioButton radioButton13; + private System.Windows.Forms.RadioButton radioButton14; + private System.Windows.Forms.RadioButton radioButton15; + private System.Windows.Forms.RadioButton radioButton16; + private System.Windows.Forms.ContextMenu contextMenu; + private System.Windows.Forms.MenuItem pruneContextMenuItem; + private System.Windows.Forms.MenuItem selectRecursiveMenuItem; + private System.Windows.Forms.MenuItem copyContextMenuItem; + private System.Windows.Forms.Timer versionTimer; + private System.ComponentModel.IContainer components; + + private Graph graph; + private Font font; + private int fontHeight; + private float scale = 1.0f; + private bool placeVertices = true; + private bool placeEdges = true; + private Point lastMouseDownPoint; + private Point lastMousePoint; + private ArrayList levelList; + private ulong totalWeight; + private Vertex selectedVertex; + private System.Windows.Forms.Panel outerPanel; + private System.Windows.Forms.MenuItem filterMenuItem; + private System.Windows.Forms.MenuItem selectAllMenuItem; + private System.Windows.Forms.MenuItem findMenuItem; + private System.Windows.Forms.MenuItem showWhoAllocatedMenuItem; + private System.Windows.Forms.MenuItem findInterestingNodesMenuItem; + private System.Windows.Forms.MainMenu mainMenu1; + private System.Windows.Forms.MenuItem menuItem1; + private System.Windows.Forms.MenuItem selectAllMainMenuItem; + private System.Windows.Forms.MenuItem copyMainMenuItem; + private System.Windows.Forms.MenuItem findMainMenuItem; + private System.Windows.Forms.MenuItem showWhoAllocatedNewMenuItem; + private System.Windows.Forms.MenuItem showNewObjectsMenuItem; + private System.Windows.Forms.MenuItem zoomToNodeMenuItem; + private System.Windows.Forms.MenuItem showObjectsAllocatedBetween; + private System.Windows.Forms.MenuItem showWhoAllocatedObjectsBetweenMenuItem; + private System.Windows.Forms.MenuItem showInstancesMenuItem; + private System.Windows.Forms.MenuItem showHistogramMenuItem; + private System.Windows.Forms.MenuItem showReferencesMenuItem; + private System.Windows.Forms.MenuItem filterToCallersCalleesMenuItem; + private ToolTip toolTip; + private System.Windows.Forms.MenuItem resetFilterMenuItem; + private System.Windows.Forms.MenuItem findAgainMenuItem; + private FilterForm filterForm; + private System.Windows.Forms.MenuItem findAgainMainMenuItem; + private FindRoutineForm findForm; + + + internal GraphViewForm(Graph graph, string title) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + toolTip = new ToolTip(); + toolTip.Active = false; + toolTip.ShowAlways = true; + toolTip.AutomaticDelay = 70; + toolTip.ReshowDelay = 1; + + this.graph = graph; + + font = MainForm.instance.font; + fontHeight = font.Height; + + Text = title; + + EnableDisableMenuItems(); + + filterForm = new FilterForm(); + findForm = new FindRoutineForm(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.panel1 = new System.Windows.Forms.Panel(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.radioButton16 = new System.Windows.Forms.RadioButton(); + this.radioButton15 = new System.Windows.Forms.RadioButton(); + this.radioButton14 = new System.Windows.Forms.RadioButton(); + this.radioButton13 = new System.Windows.Forms.RadioButton(); + this.radioButton12 = new System.Windows.Forms.RadioButton(); + this.radioButton11 = new System.Windows.Forms.RadioButton(); + this.radioButton10 = new System.Windows.Forms.RadioButton(); + this.radioButton9 = new System.Windows.Forms.RadioButton(); + this.radioButton8 = new System.Windows.Forms.RadioButton(); + this.scaleGroupBox = new System.Windows.Forms.GroupBox(); + this.radioButton7 = new System.Windows.Forms.RadioButton(); + this.radioButton6 = new System.Windows.Forms.RadioButton(); + this.radioButton5 = new System.Windows.Forms.RadioButton(); + this.radioButton4 = new System.Windows.Forms.RadioButton(); + this.radioButton3 = new System.Windows.Forms.RadioButton(); + this.radioButton2 = new System.Windows.Forms.RadioButton(); + this.radioButton1 = new System.Windows.Forms.RadioButton(); + this.outerPanel = new System.Windows.Forms.Panel(); + this.graphPanel = new System.Windows.Forms.Panel(); + this.contextMenu = new System.Windows.Forms.ContextMenu(); + this.filterToCallersCalleesMenuItem = new System.Windows.Forms.MenuItem(); + this.filterMenuItem = new System.Windows.Forms.MenuItem(); + this.resetFilterMenuItem = new System.Windows.Forms.MenuItem(); + this.pruneContextMenuItem = new System.Windows.Forms.MenuItem(); + this.selectRecursiveMenuItem = new System.Windows.Forms.MenuItem(); + this.selectAllMenuItem = new System.Windows.Forms.MenuItem(); + this.copyContextMenuItem = new System.Windows.Forms.MenuItem(); + this.zoomToNodeMenuItem = new System.Windows.Forms.MenuItem(); + this.findInterestingNodesMenuItem = new System.Windows.Forms.MenuItem(); + this.findMenuItem = new System.Windows.Forms.MenuItem(); + this.findAgainMenuItem = new System.Windows.Forms.MenuItem(); + this.showWhoAllocatedMenuItem = new System.Windows.Forms.MenuItem(); + this.showNewObjectsMenuItem = new System.Windows.Forms.MenuItem(); + this.showWhoAllocatedNewMenuItem = new System.Windows.Forms.MenuItem(); + this.showObjectsAllocatedBetween = new System.Windows.Forms.MenuItem(); + this.showWhoAllocatedObjectsBetweenMenuItem = new System.Windows.Forms.MenuItem(); + this.showInstancesMenuItem = new System.Windows.Forms.MenuItem(); + this.showHistogramMenuItem = new System.Windows.Forms.MenuItem(); + this.showReferencesMenuItem = new System.Windows.Forms.MenuItem(); + this.versionTimer = new System.Windows.Forms.Timer(this.components); + this.mainMenu1 = new System.Windows.Forms.MainMenu(this.components); + this.menuItem1 = new System.Windows.Forms.MenuItem(); + this.selectAllMainMenuItem = new System.Windows.Forms.MenuItem(); + this.copyMainMenuItem = new System.Windows.Forms.MenuItem(); + this.findMainMenuItem = new System.Windows.Forms.MenuItem(); + this.findAgainMainMenuItem = new System.Windows.Forms.MenuItem(); + this.panel1.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.scaleGroupBox.SuspendLayout(); + this.outerPanel.SuspendLayout(); + this.SuspendLayout(); + // + // panel1 + // + this.panel1.Controls.Add(this.groupBox1); + this.panel1.Controls.Add(this.scaleGroupBox); + this.panel1.Dock = System.Windows.Forms.DockStyle.Top; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(1219, 80); + this.panel1.TabIndex = 0; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.radioButton16); + this.groupBox1.Controls.Add(this.radioButton15); + this.groupBox1.Controls.Add(this.radioButton14); + this.groupBox1.Controls.Add(this.radioButton13); + this.groupBox1.Controls.Add(this.radioButton12); + this.groupBox1.Controls.Add(this.radioButton11); + this.groupBox1.Controls.Add(this.radioButton10); + this.groupBox1.Controls.Add(this.radioButton9); + this.groupBox1.Controls.Add(this.radioButton8); + this.groupBox1.Location = new System.Drawing.Point(576, 16); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(604, 48); + this.groupBox1.TabIndex = 2; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Detail"; + // + // radioButton16 + // + this.radioButton16.Location = new System.Drawing.Point(489, 16); + this.radioButton16.Name = "radioButton16"; + this.radioButton16.Size = new System.Drawing.Size(109, 24); + this.radioButton16.TabIndex = 8; + this.radioButton16.Text = "20 (coarse)"; + this.radioButton16.CheckedChanged += new System.EventHandler(this.detailRadioButton_Click); + // + // radioButton15 + // + this.radioButton15.Location = new System.Drawing.Point(434, 16); + this.radioButton15.Name = "radioButton15"; + this.radioButton15.Size = new System.Drawing.Size(49, 24); + this.radioButton15.TabIndex = 7; + this.radioButton15.Text = "10"; + this.radioButton15.CheckedChanged += new System.EventHandler(this.detailRadioButton_Click); + // + // radioButton14 + // + this.radioButton14.Location = new System.Drawing.Point(387, 16); + this.radioButton14.Name = "radioButton14"; + this.radioButton14.Size = new System.Drawing.Size(41, 24); + this.radioButton14.TabIndex = 6; + this.radioButton14.Text = "5"; + this.radioButton14.CheckedChanged += new System.EventHandler(this.detailRadioButton_Click); + // + // radioButton13 + // + this.radioButton13.Location = new System.Drawing.Point(349, 16); + this.radioButton13.Name = "radioButton13"; + this.radioButton13.Size = new System.Drawing.Size(32, 24); + this.radioButton13.TabIndex = 5; + this.radioButton13.Text = "2"; + this.radioButton13.CheckedChanged += new System.EventHandler(this.detailRadioButton_Click); + // + // radioButton12 + // + this.radioButton12.Checked = true; + this.radioButton12.Location = new System.Drawing.Point(305, 16); + this.radioButton12.Name = "radioButton12"; + this.radioButton12.Size = new System.Drawing.Size(38, 24); + this.radioButton12.TabIndex = 4; + this.radioButton12.TabStop = true; + this.radioButton12.Text = "1"; + this.radioButton12.CheckedChanged += new System.EventHandler(this.detailRadioButton_Click); + // + // radioButton11 + // + this.radioButton11.Location = new System.Drawing.Point(247, 16); + this.radioButton11.Name = "radioButton11"; + this.radioButton11.Size = new System.Drawing.Size(52, 24); + this.radioButton11.TabIndex = 3; + this.radioButton11.Text = "0.5"; + this.radioButton11.CheckedChanged += new System.EventHandler(this.detailRadioButton_Click); + // + // radioButton10 + // + this.radioButton10.Location = new System.Drawing.Point(191, 16); + this.radioButton10.Name = "radioButton10"; + this.radioButton10.Size = new System.Drawing.Size(50, 24); + this.radioButton10.TabIndex = 2; + this.radioButton10.Text = "0.2"; + this.radioButton10.CheckedChanged += new System.EventHandler(this.detailRadioButton_Click); + // + // radioButton9 + // + this.radioButton9.Location = new System.Drawing.Point(132, 16); + this.radioButton9.Name = "radioButton9"; + this.radioButton9.Size = new System.Drawing.Size(50, 24); + this.radioButton9.TabIndex = 1; + this.radioButton9.Text = "0.1"; + this.radioButton9.CheckedChanged += new System.EventHandler(this.detailRadioButton_Click); + // + // radioButton8 + // + this.radioButton8.Location = new System.Drawing.Point(8, 16); + this.radioButton8.Name = "radioButton8"; + this.radioButton8.Size = new System.Drawing.Size(118, 24); + this.radioButton8.TabIndex = 0; + this.radioButton8.Text = "0 (everything)"; + this.radioButton8.CheckedChanged += new System.EventHandler(this.detailRadioButton_Click); + // + // scaleGroupBox + // + this.scaleGroupBox.Controls.Add(this.radioButton7); + this.scaleGroupBox.Controls.Add(this.radioButton6); + this.scaleGroupBox.Controls.Add(this.radioButton5); + this.scaleGroupBox.Controls.Add(this.radioButton4); + this.scaleGroupBox.Controls.Add(this.radioButton3); + this.scaleGroupBox.Controls.Add(this.radioButton2); + this.scaleGroupBox.Controls.Add(this.radioButton1); + this.scaleGroupBox.Location = new System.Drawing.Point(16, 16); + this.scaleGroupBox.Name = "scaleGroupBox"; + this.scaleGroupBox.Size = new System.Drawing.Size(540, 48); + this.scaleGroupBox.TabIndex = 1; + this.scaleGroupBox.TabStop = false; + this.scaleGroupBox.Text = "Scale"; + // + // radioButton7 + // + this.radioButton7.Location = new System.Drawing.Point(402, 16); + this.radioButton7.Name = "radioButton7"; + this.radioButton7.Size = new System.Drawing.Size(108, 24); + this.radioButton7.TabIndex = 6; + this.radioButton7.Text = "1000 (huge)"; + this.radioButton7.CheckedChanged += new System.EventHandler(this.scaleRadioButton_Click); + // + // radioButton6 + // + this.radioButton6.Location = new System.Drawing.Point(336, 16); + this.radioButton6.Name = "radioButton6"; + this.radioButton6.Size = new System.Drawing.Size(60, 24); + this.radioButton6.TabIndex = 5; + this.radioButton6.Text = "500"; + this.radioButton6.CheckedChanged += new System.EventHandler(this.scaleRadioButton_Click); + // + // radioButton5 + // + this.radioButton5.Location = new System.Drawing.Point(277, 16); + this.radioButton5.Name = "radioButton5"; + this.radioButton5.Size = new System.Drawing.Size(53, 24); + this.radioButton5.TabIndex = 4; + this.radioButton5.Text = "200"; + this.radioButton5.CheckedChanged += new System.EventHandler(this.scaleRadioButton_Click); + // + // radioButton4 + // + this.radioButton4.Checked = true; + this.radioButton4.Location = new System.Drawing.Point(218, 16); + this.radioButton4.Name = "radioButton4"; + this.radioButton4.Size = new System.Drawing.Size(53, 24); + this.radioButton4.TabIndex = 3; + this.radioButton4.TabStop = true; + this.radioButton4.Text = "100"; + this.radioButton4.CheckedChanged += new System.EventHandler(this.scaleRadioButton_Click); + // + // radioButton3 + // + this.radioButton3.Location = new System.Drawing.Point(162, 16); + this.radioButton3.Name = "radioButton3"; + this.radioButton3.Size = new System.Drawing.Size(50, 24); + this.radioButton3.TabIndex = 2; + this.radioButton3.Text = "50"; + this.radioButton3.CheckedChanged += new System.EventHandler(this.scaleRadioButton_Click); + // + // radioButton2 + // + this.radioButton2.Location = new System.Drawing.Point(107, 16); + this.radioButton2.Name = "radioButton2"; + this.radioButton2.Size = new System.Drawing.Size(49, 24); + this.radioButton2.TabIndex = 1; + this.radioButton2.Text = "20"; + this.radioButton2.CheckedChanged += new System.EventHandler(this.scaleRadioButton_Click); + // + // radioButton1 + // + this.radioButton1.Location = new System.Drawing.Point(16, 16); + this.radioButton1.Name = "radioButton1"; + this.radioButton1.Size = new System.Drawing.Size(85, 24); + this.radioButton1.TabIndex = 0; + this.radioButton1.Text = "10 (tiny)"; + this.radioButton1.CheckedChanged += new System.EventHandler(this.scaleRadioButton_Click); + // + // outerPanel + // + this.outerPanel.BackColor = System.Drawing.Color.White; + this.outerPanel.Controls.Add(this.graphPanel); + this.outerPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.outerPanel.Location = new System.Drawing.Point(0, 80); + this.outerPanel.Name = "outerPanel"; + this.outerPanel.Size = new System.Drawing.Size(1219, 575); + this.outerPanel.TabIndex = 1; + // + // graphPanel + // + this.graphPanel.Location = new System.Drawing.Point(0, 0); + this.graphPanel.Name = "graphPanel"; + this.graphPanel.Size = new System.Drawing.Size(864, 528); + this.graphPanel.TabIndex = 0; + this.graphPanel.DoubleClick += new System.EventHandler(this.graphPanel_DoubleClick); + this.graphPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.graphPanel_MouseDown); + this.graphPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.graphPanel_MouseMove); + this.graphPanel.Paint += new System.Windows.Forms.PaintEventHandler(this.graphPanel_Paint); + // + // contextMenu + // + this.contextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.filterToCallersCalleesMenuItem, + this.filterMenuItem, + this.resetFilterMenuItem, + this.pruneContextMenuItem, + this.selectRecursiveMenuItem, + this.selectAllMenuItem, + this.copyContextMenuItem, + this.zoomToNodeMenuItem, + this.findInterestingNodesMenuItem, + this.findMenuItem, + this.findAgainMenuItem, + this.showWhoAllocatedMenuItem, + this.showNewObjectsMenuItem, + this.showWhoAllocatedNewMenuItem, + this.showObjectsAllocatedBetween, + this.showWhoAllocatedObjectsBetweenMenuItem, + this.showInstancesMenuItem, + this.showHistogramMenuItem, + this.showReferencesMenuItem}); + // + // filterToCallersCalleesMenuItem + // + this.filterToCallersCalleesMenuItem.Index = 0; + this.filterToCallersCalleesMenuItem.Text = "Filter to callers && callees"; + this.filterToCallersCalleesMenuItem.Click += new System.EventHandler(this.filterToCallersCalleesMenuItem_Click); + // + // filterMenuItem + // + this.filterMenuItem.Index = 1; + this.filterMenuItem.Text = "Filter..."; + this.filterMenuItem.Click += new System.EventHandler(this.filterMenuItem_Click); + // + // resetFilterMenuItem + // + this.resetFilterMenuItem.Index = 2; + this.resetFilterMenuItem.Text = "Reset Filter"; + this.resetFilterMenuItem.Click += new System.EventHandler(this.resetFilterMenuItem_Click); + // + // pruneContextMenuItem + // + this.pruneContextMenuItem.Index = 3; + this.pruneContextMenuItem.Text = "Prune to callers && callees"; + this.pruneContextMenuItem.Click += new System.EventHandler(this.pruneMenuItem_Click); + // + // selectRecursiveMenuItem + // + this.selectRecursiveMenuItem.Index = 4; + this.selectRecursiveMenuItem.Text = "Select callers && callees"; + this.selectRecursiveMenuItem.Click += new System.EventHandler(this.selectRecursiveMenuItem_Click); + // + // selectAllMenuItem + // + this.selectAllMenuItem.Index = 5; + this.selectAllMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlA; + this.selectAllMenuItem.Text = "Select All"; + this.selectAllMenuItem.Click += new System.EventHandler(this.selectAllMenuItem_Click); + // + // copyContextMenuItem + // + this.copyContextMenuItem.Index = 6; + this.copyContextMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlC; + this.copyContextMenuItem.Text = "Copy as text to clipboard"; + this.copyContextMenuItem.Click += new System.EventHandler(this.copyMenuItem_Click); + // + // zoomToNodeMenuItem + // + this.zoomToNodeMenuItem.Index = 7; + this.zoomToNodeMenuItem.Text = "Zoom to Node"; + this.zoomToNodeMenuItem.Click += new System.EventHandler(this.zoomToNodeMenuItem_Click); + // + // findInterestingNodesMenuItem + // + this.findInterestingNodesMenuItem.Index = 8; + this.findInterestingNodesMenuItem.Text = "Find interesting nodes"; + this.findInterestingNodesMenuItem.Click += new System.EventHandler(this.findInterestingNodesMenuItem_Click); + // + // findMenuItem + // + this.findMenuItem.Index = 9; + this.findMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlF; + this.findMenuItem.Text = "Find routine..."; + this.findMenuItem.Click += new System.EventHandler(this.findMenuItem_Click); + // + // findAgainMenuItem + // + this.findAgainMenuItem.Index = 10; + this.findAgainMenuItem.Shortcut = System.Windows.Forms.Shortcut.F3; + this.findAgainMenuItem.Text = "Find Again"; + this.findAgainMenuItem.Click += new System.EventHandler(this.findAgainMenuItem_Click); + // + // showWhoAllocatedMenuItem + // + this.showWhoAllocatedMenuItem.Index = 11; + this.showWhoAllocatedMenuItem.Text = "Show Who Allocated"; + this.showWhoAllocatedMenuItem.Click += new System.EventHandler(this.showWhoAllocatedMenuItem_Click); + // + // showNewObjectsMenuItem + // + this.showNewObjectsMenuItem.Index = 12; + this.showNewObjectsMenuItem.Text = "Show New Objects"; + this.showNewObjectsMenuItem.Click += new System.EventHandler(this.showNewObjectsMenuItem_Click); + // + // showWhoAllocatedNewMenuItem + // + this.showWhoAllocatedNewMenuItem.Index = 13; + this.showWhoAllocatedNewMenuItem.Text = "Show Who Allocated New Objects"; + this.showWhoAllocatedNewMenuItem.Click += new System.EventHandler(this.showWhoAllocatedNewMenuItem_Click); + // + // showObjectsAllocatedBetween + // + this.showObjectsAllocatedBetween.Index = 14; + this.showObjectsAllocatedBetween.Text = "Show Objects Allocated between..."; + this.showObjectsAllocatedBetween.Click += new System.EventHandler(this.showObjectsAllocatedBetween_Click); + // + // showWhoAllocatedObjectsBetweenMenuItem + // + this.showWhoAllocatedObjectsBetweenMenuItem.Index = 15; + this.showWhoAllocatedObjectsBetweenMenuItem.Text = "Show Who Allocated Objects between..."; + this.showWhoAllocatedObjectsBetweenMenuItem.Click += new System.EventHandler(this.showWhoAllocatedObjectsBetweenMenuItem_Click); + // + // showInstancesMenuItem + // + this.showInstancesMenuItem.Index = 16; + this.showInstancesMenuItem.Text = "Show Individual Instances"; + this.showInstancesMenuItem.Click += new System.EventHandler(this.showInstancesMenuItem_Click); + // + // showHistogramMenuItem + // + this.showHistogramMenuItem.Index = 17; + this.showHistogramMenuItem.Text = "Show Histogram"; + this.showHistogramMenuItem.Click += new System.EventHandler(this.showHistogramMenuItem_Click); + // + // showReferencesMenuItem + // + this.showReferencesMenuItem.Index = 18; + this.showReferencesMenuItem.Text = "Show References"; + this.showReferencesMenuItem.Click += new System.EventHandler(this.showReferencesMenuItem_Click); + // + // versionTimer + // + this.versionTimer.Enabled = true; + this.versionTimer.Tick += new System.EventHandler(this.versionTimer_Tick); + // + // mainMenu1 + // + this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItem1}); + // + // menuItem1 + // + this.menuItem1.Index = 0; + this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.selectAllMainMenuItem, + this.copyMainMenuItem, + this.findMainMenuItem, + this.findAgainMainMenuItem}); + this.menuItem1.Text = "Edit"; + // + // selectAllMainMenuItem + // + this.selectAllMainMenuItem.Index = 0; + this.selectAllMainMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlA; + this.selectAllMainMenuItem.Text = "Select All"; + this.selectAllMainMenuItem.Click += new System.EventHandler(this.selectAllMenuItem_Click); + // + // copyMainMenuItem + // + this.copyMainMenuItem.Index = 1; + this.copyMainMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlC; + this.copyMainMenuItem.Text = "Copy as text to clipboard"; + this.copyMainMenuItem.Click += new System.EventHandler(this.copyMenuItem_Click); + // + // findMainMenuItem + // + this.findMainMenuItem.Index = 2; + this.findMainMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlF; + this.findMainMenuItem.Text = "Find routine..."; + this.findMainMenuItem.Click += new System.EventHandler(this.findMenuItem_Click); + // + // findAgainMainMenuItem + // + this.findAgainMainMenuItem.Index = 3; + this.findAgainMainMenuItem.Shortcut = System.Windows.Forms.Shortcut.F3; + this.findAgainMainMenuItem.Text = "Find Again"; + this.findAgainMainMenuItem.Click += new System.EventHandler(this.findAgainMenuItem_Click); + // + // GraphViewForm + // + this.ClientSize = new System.Drawing.Size(1219, 655); + this.Controls.Add(this.outerPanel); + this.Controls.Add(this.panel1); + this.Menu = this.mainMenu1; + this.Name = "GraphViewForm"; + this.Text = "GraphViewForm"; + this.panel1.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.scaleGroupBox.ResumeLayout(false); + this.outerPanel.ResumeLayout(false); + this.ResumeLayout(false); + + } + #endregion + + void EnableDisableMenuItems() + { + bool isHeapGraph = graph.graphType == Graph.GraphType.HeapGraph; + showWhoAllocatedMenuItem.Enabled = isHeapGraph; + showWhoAllocatedNewMenuItem.Enabled = isHeapGraph; + showNewObjectsMenuItem.Enabled = isHeapGraph; + showObjectsAllocatedBetween.Enabled = isHeapGraph; + showWhoAllocatedObjectsBetweenMenuItem.Enabled = isHeapGraph; + showInstancesMenuItem.Enabled = isHeapGraph; + showHistogramMenuItem.Enabled = isHeapGraph; + showReferencesMenuItem.Enabled = isHeapGraph && SelectedVertexCount() != 0; + + if (isHeapGraph && graph.typeGraphOptions == ObjectGraph.BuildTypeGraphOptions.IndividualObjects) + { + showInstancesMenuItem.Text = "Show Groups of Instances"; + } + else + { + showInstancesMenuItem.Text = "Show Individual Instances"; + } + + } + + void PaintVertex(Vertex v, Graphics g, Brush penBrush, Pen pen) + { + Rectangle r = v.rectangle; + v.selectionRectangle = r; + g.DrawRectangle(pen, r); + if (v.selected) + { + using (SolidBrush selectBrush = new SolidBrush(Color.Aqua)) + g.FillRectangle(selectBrush, r); + } + + RectangleF stringRect; + int lineCount = 2; + if (v.signature != null) + lineCount = 3; + if (r.Height > fontHeight*lineCount) + stringRect = new RectangleF(r.X,r.Y,r.Width,fontHeight); + else + { + stringRect = new RectangleF(r.X,r.Y+r.Height+3,r.Width,fontHeight); + // for these very narrow rectangle, start the selection rectangle 5 pixels above + // the actual rectangle, so people can hit it more easily. Even though they could click + // on the text below, which not everybody tries... + const int vTolerance = 5; + v.selectionRectangle = new Rectangle(r.X, r.Y - vTolerance, r.Width, vTolerance + r.Height + 3 + fontHeight*lineCount); + } + + if (v.weightHistory != null) + { + int alpha = 200; + int previousHeight = r.Height; + for (int i = 0; i < v.weightHistory.Length; i++) + { + alpha = alpha*2/3; + ulong weight = v.weightHistory[i]; + int height = (int)((float)r.Height/v.weight*weight); + if (height < previousHeight) + { + Color color = Color.FromArgb(alpha, Color.Red); + using (Brush brush = new SolidBrush(color)) + { + g.FillRectangle(brush, r.X, r.Y+height, r.Width, previousHeight - height); + } + } + else + { + Color color = Color.FromArgb(alpha, Color.Green); + using (Brush brush = new SolidBrush(color)) + { + g.FillRectangle(brush, r.X, r.Y+previousHeight, r.Width, height - previousHeight); + } + } + previousHeight = height; + } + } + + g.DrawString(v.basicName, font, penBrush, stringRect); + stringRect.Y += fontHeight; + if (v.signature != null) + { + g.DrawString(v.basicSignature, font, penBrush, stringRect); + stringRect.Y += fontHeight; + int width = (int)g.MeasureString(v.basicSignature, font).Width; + if (stringRect.Width < width) + v.signatureCurtated = true; + } + + g.DrawString(v.weightString, font, penBrush, stringRect); + } + + ArrayList BuildLevels(Graph g) + { + ArrayList al = new ArrayList(); + for (int level = 0; level <= g.BottomVertex.level; level++) + { + al.Add(new ArrayList()); + } + foreach (Vertex v in g.vertices.Values) + { + if (v.level <= g.BottomVertex.level) + { + ArrayList all = (ArrayList)al[v.level]; + all.Add(v); + } + else + { + Debug.Assert(v.level == int.MaxValue); + } + } + foreach (ArrayList all in al) + { + all.Sort(); + } + return al; + } + + void PlaceEdges(ICollection edgeCollection, bool isIncoming, int x, int y, float scale) + { + ArrayList edgeList = new ArrayList(edgeCollection); + edgeList.Sort(); + float fy = y; + foreach (Edge e in edgeList) + { + float fwidth = e.weight*scale; + Point p = new Point(x, (int)(fy + fwidth/2)); + if (isIncoming) + { + e.toPoint = p; + } + else + { + e.fromPoint = p; + } + fy += fwidth; + } + } + + void PlaceEdges(float scale) + { + foreach (Vertex v in graph.vertices.Values) + { + PlaceEdges(v.incomingEdges.Values, true, v.rectangle.X, v.rectangle.Y, scale); + int y = v.rectangle.Y + (int)(v.basicWeight*scale); + PlaceEdges(v.outgoingEdges.Values, false, v.rectangle.X + v.rectangle.Width, y, scale); + } + } + + int totalHeight = 100; + const int boxWidth = 300; + int gapWidth = 100; + float minHeight = 1.0f; + float minWidth = 1.0f; + + void DrawEdges(Graphics g, float scale) + { + Random r = new Random(0); + Point[] points = new Point[4]; + foreach (Vertex v in graph.vertices.Values) + { + foreach (Edge e in v.outgoingEdges.Values) + { + if (e.ToVertex != graph.BottomVertex + && !e.fromPoint.IsEmpty && !e.toPoint.IsEmpty) + { + int colorInt = r.Next(255*256*256); + int red = (colorInt >> 16) & 255; + int green = (colorInt >> 8) & 255; + int blue = colorInt & 255; + Brush brush = null; + if (e.selected) + { + Color foreColor = Color.FromArgb(150, 0, 255, 0); + Color backColor = Color.FromArgb(150, 255, 0, 0); + brush = new HatchBrush(HatchStyle.DiagonalCross, foreColor, backColor); + } + else if (e.brush != null) + { + brush = e.brush; + } + else + { + if (red <= green && red <= blue) + red = 0; + else if (green <= blue && green <= red) + green = 0; + else if (blue <= red && blue <= green) + blue = 0; + Color color = Color.FromArgb(100, red, green, blue); + Debug.Assert(!color.IsEmpty); + brush = new SolidBrush(color); + e.brush = brush; + } + Debug.Assert(brush != null); + float fWidth = e.weight*scale; + if (fWidth > minWidth && e.FromVertex.active && e.ToVertex.active) + { + int iWidth = (int)fWidth; + if (iWidth < 1) + iWidth = 1; + e.width = iWidth; + Pen pen = e.pen; + if (pen == null || pen.Width != iWidth || e.selected) + { + pen = new Pen(brush, iWidth); + if (!e.selected) + e.pen = pen; + } + Debug.Assert(pen != null); + int deltaX = e.toPoint.X - e.fromPoint.X; + int deltaY = e.toPoint.Y - e.fromPoint.Y; + deltaX = deltaX/4; + deltaY = deltaY/9; + int deltaY1 = deltaY; + int deltaY2 = - deltaY; + if (deltaX < 0) + { + deltaX = 20; + if (Math.Abs(deltaY)*5 < iWidth*2) + { + deltaY1 = deltaY2 = iWidth*2; + deltaX = iWidth; + } + } + points[0] = e.fromPoint; + points[1] = new Point(e.fromPoint.X + deltaX, e.fromPoint.Y + deltaY1); + points[2] = new Point(e. toPoint.X - deltaX, e. toPoint.Y + deltaY2); + points[3] = e.toPoint; + g.DrawCurve(pen, points); + // g.DrawLine(pen, e.fromPoint, e.toPoint); + red = (red + 17) % 256; + green = (green + 101) % 256; + blue = (blue + 29) % 256; + } + } + } + } + } + + string formatWeight(ulong weight) + { + if (graph.graphType == Graph.GraphType.CallGraph) + { + if (weight == 1) + return "1 call"; + else + return string.Format("{0:n0} calls", weight); + } + if (graph.graphType == Graph.GraphType.AssemblyGraph) + { + if (weight == 1) + { + return "1 assembly"; + } + else + { + return weight + " assemblies"; + } + } + else if (graph.graphType == Graph.GraphType.HandleAllocationGraph) + { + if (weight == 1) + return "1 handle"; + else + return string.Format("{0:n0} handles", weight); + } + else + { + double w = weight; + string byteString = "bytes"; + if (w >= 1024) + { + w /= 1024; + byteString = "kB "; + } + if (w >= 1024) + { + w /= 1024; + byteString = "MB "; + } + if (w >= 1024) + { + w /= 1024; + byteString = "GB "; + } + string format = "{0,4:f0} {1} ({2:f2}%)"; + if (w < 10) + format = "{0,4:f1} {1} ({2:f2}%)"; + return string.Format(format, w, byteString, weight*100.0/totalWeight); + } + } + + void PlaceVertices(Graphics g) + { + graph.AssignLevelsToVertices(); + totalWeight = 0; + foreach (Vertex v in graph.vertices.Values) + { + v.weight = v.incomingWeight; + if (v.weight < v.outgoingWeight) + v.weight = v.outgoingWeight; + if (graph.graphType == Graph.GraphType.CallGraph) + { + if (totalWeight < v.weight) + totalWeight = v.weight; + } + } + if (graph.graphType != Graph.GraphType.CallGraph) + totalWeight = graph.TopVertex.weight; + if (totalWeight == 0) + { + totalWeight = 1; + } + + ArrayList al = levelList = BuildLevels(graph); + scale = (float)totalHeight/totalWeight; + if (placeVertices) + { + int x = 10; + int maxY = 0; + for (int level = graph.TopVertex.level; + level <= graph.BottomVertex.level; + level++) + { + ArrayList all = (ArrayList)al[level]; + int drawnVertexCount = 0; + int maxWidth = 0; + foreach (Vertex v in all) + { + if (graph.graphType == Graph.GraphType.CallGraph) + { + if (v.incomingWeight > v.outgoingWeight) + v.basicWeight = v.incomingWeight - v.outgoingWeight; + else + v.basicWeight = 0; + v.weightString = string.Format("Gets {0}, causes {1}", + formatWeight(v.basicWeight), + formatWeight(v.outgoingWeight)); + } + else if (graph.graphType == Graph.GraphType.ReferenceGraph) + { + if (v.weight == 1) + v.weightString = "1 reference"; + else + v.weightString = string.Format("{0} references", v.weight); + if (v.count > 0) + { + if (v.count == 1) + v.weightString += " (1 object)"; + else + v.weightString += string.Format(" ({0} objects)", v.count); + } + } + else + { + if (v.count == 0) + v.weightString = formatWeight(v.weight); + else if (v.count == 1) + v.weightString = string.Format("{0} (1 object, {1})", formatWeight(v.weight), formatWeight(v.basicWeight)); + else + v.weightString = string.Format("{0} ({1} objects, {2})", formatWeight(v.weight), v.count, formatWeight(v.basicWeight)); + } + if (v.weight*scale > minHeight) + { + int width = (int)g.MeasureString(v.basicName, font).Width; + if (maxWidth < width) + maxWidth = width; + + width = (int)g.MeasureString(v.weightString, font).Width; + if (maxWidth < width) + maxWidth = width; + } + } + int y = 10; + ulong levelWeight = 0; + foreach (Vertex v in all) + levelWeight += v.weight; + float levelHeight = levelWeight*scale; + if (levelHeight < totalHeight*0.5) + y+= (int)((totalHeight - levelHeight)*2); + foreach (Vertex v in all) + { + // For the in-between vertices, sometimes it's good + // to shift them down a little to line them up with + // whatever is going into them. Unless of course + // we would need to shift too much... + if (v.level < graph.BottomVertex.level-1) + { + ulong highestWeight = 0; + int bestY = 0; + foreach (Edge e in v.incomingEdges.Values) + { + if (e.weight > highestWeight && e.FromVertex.level < level) + { + highestWeight = e.weight; + bestY = e.fromPoint.Y - (int)(e.weight*scale*0.5); + } + } + if (y < bestY && bestY < totalHeight*5) + y = bestY; + } + float fHeight = v.weight*scale; + int iHeight = (int)fHeight; + if (iHeight < 1) + iHeight = 1; + v.rectangle = new Rectangle(x, y, maxWidth+5, iHeight); +// if (placeEdges) +// PlaceEdges(v.outgoingEdges.Values, false, v.rectangle.X + v.rectangle.Width, v.rectangle.Y, scale); + if (fHeight <= minHeight || !v.active) + { + v.visible = false; + v.rectangle = v.selectionRectangle = new Rectangle(0,0,0,0); + } + else + { + v.visible = true; + y += iHeight; + int lines = 2; + if (v.signature != null) + lines = 3; + if (iHeight <= fontHeight*lines) + y += fontHeight*lines + 3; + y += 30; + drawnVertexCount++; + } + } + if (drawnVertexCount > 0) + { + x += maxWidth + gapWidth; + if (maxY < y) + maxY = y; + } + } + if (x < Size.Width) + x = Size.Width; + if (maxY < Size.Height) + maxY = Size.Height; + graphPanel.Size = new System.Drawing.Size (x, maxY); + } + if (placeEdges) + PlaceEdges(scale); + } + + private void graphPanel_Paint(object sender, System.Windows.Forms.PaintEventArgs e) + { + EnableDisableMenuItems(); + + outerPanel.AutoScroll = true; + Graphics g = e.Graphics; + if (placeVertices || placeEdges) + { + PlaceVertices(g); + placeVertices = placeEdges = false; + } + // Brush brush = new SolidBrush(Color.Gray); + // g.FillRectangle(brush, graphPanel.ClientRectangle); + using (SolidBrush penBrush = new SolidBrush(Color.Blue)) + { + using (Pen pen = new Pen(penBrush, 1)) + { + foreach (Vertex v in graph.vertices.Values) + { + if (v.visible) + PaintVertex(v, g, penBrush, pen); + } + } + } + DrawEdges(g, scale); + } + + private void scaleRadioButton_Click(object sender, System.EventArgs e) + { + RadioButton clickedRadioButton = (RadioButton)sender; + string scaleString = clickedRadioButton.Text.Split(' ')[0]; + totalHeight = gapWidth = Convert.ToInt32(scaleString); + placeVertices = placeEdges = true; + graphPanel.Invalidate(); + } + + private void detailRadioButton_Click(object sender, System.EventArgs e) + { + RadioButton clickedRadioButton = (RadioButton)sender; + string detailString = clickedRadioButton.Text.Split(' ')[0]; + minWidth = minHeight = Convert.ToSingle(detailString, CultureInfo.InvariantCulture); + placeVertices = placeEdges = true; + graphPanel.Invalidate(); + } + + private void selectVertex(Vertex v, bool nowSelected) + { + v.selected = nowSelected; + graphPanel.Invalidate(); + } + + private void selectEdges() + { + foreach (Vertex v in graph.vertices.Values) + foreach (Edge e in v.outgoingEdges.Values) + e.selected = false; + + foreach (Vertex v in graph.vertices.Values) + { + if (v.selected) + { + foreach (Edge e in v.outgoingEdges.Values) + e.selected = true; + foreach (Edge e in v.incomingEdges.Values) + e.selected = true; + } + } + } + + private void selectLevel(Vertex v) + { + // Simple semantics for now - select/delect whole level + bool nowSelected = !v.selected; + foreach (Vertex vv in graph.vertices.Values) + { + if (vv.level == v.level) + { + if (nowSelected != vv.selected) + selectVertex(vv, nowSelected); + } + } + } + + private void selectIncoming(Vertex v) + { + if (v.selected) + return; + v.selected = true; + foreach (Edge e in v.incomingEdges.Values) + { + e.selected = true; + selectIncoming(e.FromVertex); + } + } + + private void selectOutgoing(Vertex v) + { + if (v.selected) + return; + v.selected = true; + foreach (Edge e in v.outgoingEdges.Values) + { + e.selected = true; + selectOutgoing(e.ToVertex); + } + } + + private void selectRecursive(Vertex v) + { + foreach (Vertex vv in graph.vertices.Values) + { + vv.selected = false; + foreach (Edge e in vv.incomingEdges.Values) + e.selected = false; + foreach (Edge e in vv.outgoingEdges.Values) + e.selected = false; + } + + v.selected = true; + + foreach (Edge e in v.incomingEdges.Values) + { + e.selected = true; + selectIncoming(e.FromVertex); + } + foreach (Edge e in v.outgoingEdges.Values) + { + e.selected = true; + selectOutgoing(e.ToVertex); + } + } + + private void graphPanel_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) + { + Point p = new Point(e.X, e.Y); + lastMouseDownPoint = p; + if ((e.Button & MouseButtons.Left) != 0) + { + foreach (Vertex v in graph.vertices.Values) + { + bool nowSelected; + if ((Control.ModifierKeys & Keys.Control) != 0) + { + if ((Control.ModifierKeys & Keys.Shift) != 0) + { + if (v.selectionRectangle.Contains(p)) + { + selectRecursive(v); + graphPanel.Invalidate(); + return; + } + else + continue; + } + else + nowSelected = v.selected != v.selectionRectangle.Contains(p); + } + else if ((Control.ModifierKeys & Keys.Shift) != 0) + { + if (v.selectionRectangle.Contains(p)) + { + selectLevel(v); + break; + } + else + nowSelected = v.selected; + } + else + nowSelected = v.selectionRectangle.Contains(p); + if (nowSelected != v.selected) + selectVertex(v, nowSelected); + } + selectEdges(); + } + else if ((e.Button & MouseButtons.Right) != 0) + { + selectedVertex = null; + foreach (Vertex v in graph.vertices.Values) + { + if (v.selectionRectangle.Contains(p)) + { + selectedVertex = v; + break; + } + } + contextMenu.Show(graphPanel, p); + } + } + + private void graphPanel_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) + { + Point p = new Point(e.X, e.Y); + if (distance(p, lastMousePoint) >= 10) + { + toolTip.Active = false; + } + lastMousePoint = p; + if ((e.Button & MouseButtons.Left) != 0) + { + foreach (Vertex v in graph.vertices.Values) + { + if (v.selected) + { + v.rectangle.X += p.X - lastMouseDownPoint.X; + v.rectangle.Y += p.Y - lastMouseDownPoint.Y; + lastMouseDownPoint = p; + placeEdges = true; + graphPanel.Invalidate(); + } + } + } + else if (e.Button == MouseButtons.None) + { + if (Form.ActiveForm != this) + { + toolTip.Active = false; + return; + } + foreach (Vertex v in graph.vertices.Values) + { + if (v.selectionRectangle.Contains(lastMousePoint)) + { + string caption; + caption = v.name; + if (v.signature != null) + caption += Environment.NewLine + v.signature; + if (v.moduleName != null) + caption += Environment.NewLine + v.moduleName; + toolTip.Active = true; + toolTip.SetToolTip(graphPanel, caption); + break; + } + foreach (Edge edge in v.incomingEdges.Values) + { + if (distance(lastMousePoint, edge.toPoint) < edge.width) + { + edgePopup(edge, false); + return; + } + } + + foreach (Edge edge in v.outgoingEdges.Values) + { + if (distance(lastMousePoint, edge.fromPoint) < edge.width) + { + edgePopup(edge, true); + return; + } + } + } + } + } + + private bool NonEmptyWeightHistory(Vertex v) + { + if (v.weightHistory == null) + return false; + for (int i = 0; i < v.weightHistory.Length; i++) + if (v.weightHistory[i] != 0) + return true; + return false; + } + + private void AppendVertexHeader(StringBuilder sb, Vertex v, int indent) + { + for (int i = 0; i < indent; i++) + sb.Append(" "); + string signature = v.signature != null ? v.signature : ""; + sb.AppendFormat("{0} {1}:\t{2}\r\n", v.name, signature, v.weightString); + if (NonEmptyWeightHistory(v)) + { + for (int i = 0; i < indent; i++) + sb.Append(" "); + sb.Append(" Previous allocations (newest to oldest): "); + for (int i = 0; i < v.weightHistory.Length; i++) + { + sb.Append(formatWeight(v.weightHistory[i])); + sb.Append(", "); + } + sb.Append("\r\n"); + } + } + + private int SelectedVertexCount(out Vertex selectedVertex) + { + int selectedCount = 0; + selectedVertex = null; + foreach (Vertex v in graph.vertices.Values) + { + if (v.selected) + { + selectedCount++; + selectedVertex = v; + } + } + return selectedCount; + } + + private int SelectedVertexCount() + { + Vertex selectedVertex; + return SelectedVertexCount(out selectedVertex); + } + + private void copyMenuItem_Click(object sender, System.EventArgs e) + { + StringBuilder sb = new StringBuilder(); + Vertex selectedVertex; + int selectedVertexCount = SelectedVertexCount(out selectedVertex); + + if (selectedVertexCount == 1) + { + Vertex v = selectedVertex; + AppendVertexHeader(sb, v, 0); + + if (graph.graphType == Graph.GraphType.HeapGraph) + sb.Append("\r\nReferred to by:\r\n"); + else + sb.Append("\r\nContributions from callers:\r\n"); + + ArrayList callers = new ArrayList(); + foreach (Edge edge in v.incomingEdges.Values) + callers.Add(edge); + callers.Sort(); + foreach (Edge edge in callers) + { + Vertex vv = edge.FromVertex; + string signature1 = vv.signature != null ? vv.signature : ""; + string explain = "from"; + if (graph.graphType == Graph.GraphType.CallGraph) + explain = "caused by"; + sb.AppendFormat("\t{0} {1}\t{2}\t{3}\r\n", formatWeight(edge.weight), explain, vv.name, signature1); + } + + if (graph.graphType == Graph.GraphType.HeapGraph) + sb.Append("\r\nReferring to:\r\n"); + else + sb.Append("\r\nContributions to callees:\r\n"); + + ArrayList callees = new ArrayList(); + foreach (Edge edge in v.outgoingEdges.Values) + callees.Add(edge); + callees.Sort(); + foreach (Edge edge in callees) + { + Vertex vv = edge.ToVertex; + string signature2 = vv.signature != null ? vv.signature : ""; + string explain = "to"; + if (graph.graphType == Graph.GraphType.CallGraph) + explain = "caused by"; + sb.AppendFormat("\t{0} {1}\t{2}\t{3}\r\n", formatWeight(edge.weight), explain, vv.name, signature2); + } + } + else + { + foreach (ArrayList al in levelList) + { + foreach (Vertex v in al) + { + if (v.selected || (selectedVertexCount == 0 && v.visible)) + { + AppendVertexHeader(sb, v, v.level); + } + } + } + } + Clipboard.SetDataObject(sb.ToString()); + } + + private void selectAllMenuItem_Click(object sender, System.EventArgs e) + { + foreach (Vertex v in graph.vertices.Values) + { + if (!v.selected) + { + graphPanel.Invalidate(v.rectangle); + v.selected = true; + } + } + } + private double distance(Point p1, Point p2) + { + int deltaX = p1.X - p2.X; + int deltaY = p1.Y - p2.Y; + return Math.Sqrt(deltaX*deltaX + 4.0*deltaY*deltaY); + } + + private void edgePopup(Edge e, bool isOutgoingEdge) + { + Vertex v; + Point p; + if (isOutgoingEdge) + { + p = e.fromPoint; + v = e.ToVertex; + } + else + { + p = e.toPoint; + v = e.FromVertex; + } + string caption = v.basicName + ": " + formatWeight(e.weight); + Rectangle r = new Rectangle(p.X, p.Y, 1, 1); + r = graphPanel.RectangleToScreen(r); + Point screenPoint = new Point(r.X, r.Y-20); + toolTip.Active = true; + toolTip.SetToolTip(graphPanel, caption); + } + + private void activateIncoming(Vertex v) + { + if (v.active) + return; + v.active = true; + foreach (Edge e in v.incomingEdges.Values) + activateIncoming(e.FromVertex); + } + + private void activateOutgoing(Vertex v) + { + if (v.active) + return; + v.active = true; + foreach (Edge e in v.outgoingEdges.Values) + activateOutgoing(e.ToVertex); + } + + private void prune() + { + int selectedVerticesCount = 0; + foreach (Vertex v in graph.vertices.Values) + if (v.selected) + selectedVerticesCount += 1; + + if (selectedVerticesCount == 0) + return; + + foreach (Vertex v in graph.vertices.Values) + v.active = false; + + foreach (Vertex v in graph.vertices.Values) + { + if (v.selected && !v.active) + { + v.active = true; + foreach (Edge edge in v.incomingEdges.Values) + activateIncoming(edge.FromVertex); + foreach (Edge edge in v.outgoingEdges.Values) + activateOutgoing(edge.ToVertex); + } + } + graph.BottomVertex.active = false; + graphPanel.Invalidate(); + placeVertices = placeEdges = true; + } + + private void pruneMenuItem_Click(object sender, System.EventArgs e) + { + prune(); + } + + private void RefreshGraph() + { + Graph orgGraph = graph; + if (orgGraph.graphSource is Graph) + { + orgGraph = (Graph)orgGraph.graphSource; + } + + switch (orgGraph.graphType) + { + case Graph.GraphType.CallGraph: + graph = ((Histogram)orgGraph.graphSource).BuildCallGraph(filterForm); + graph.graphType = Graph.GraphType.CallGraph; + break; + + case Graph.GraphType.AllocationGraph: + graph = ((Histogram)orgGraph.graphSource).BuildAllocationGraph(filterForm); + graph.graphType = Graph.GraphType.AllocationGraph; + break; + + case Graph.GraphType.AssemblyGraph: + graph = ((Histogram)orgGraph.graphSource).BuildAssemblyGraph(filterForm); + graph.graphType = Graph.GraphType.AssemblyGraph; + break; + + case Graph.GraphType.HeapGraph: + graph = ((ObjectGraph)orgGraph.graphSource).BuildTypeGraph(orgGraph.allocatedAfterTickIndex, orgGraph.allocatedBeforeTickIndex, orgGraph.typeGraphOptions, filterForm); + graph.graphType = Graph.GraphType.HeapGraph; + break; + + case Graph.GraphType.HandleAllocationGraph: + graph = ((Histogram)orgGraph.graphSource).BuildHandleAllocationGraph(filterForm); + graph.graphType = Graph.GraphType.HandleAllocationGraph; + break; + } + placeVertices = placeEdges = true; + graphPanel.Invalidate(); + } + + private void filterMenuItem_Click(object sender, System.EventArgs e) + { + if (filterForm.ShowDialog() == DialogResult.OK) + { + RefreshGraph(); + } + } + + private void pruneContextMenuItem_Click(object sender, System.EventArgs e) + { + if (selectedVertex != null) + { + selectedVertex.selected = true; + prune(); + graphPanel.Invalidate(); + } + } + + private void selectRecursiveMenuItem_Click(object sender, System.EventArgs e) + { + if (selectedVertex != null) + { + selectRecursive(selectedVertex); + graphPanel.Invalidate(); + } + } + + private void FindVertex(string name, string signature, bool again) + { + ArrayList foundVertices = new ArrayList(); + foreach (Vertex v in graph.vertices.Values) + { + if ( v.name.IndexOf(name) >= 0 + && (v.signature == null + || v.signature.IndexOf(signature) >= 0)) + { + foundVertices.Add(v); + } + } + foundVertices.Sort(); + + Vertex foundVertex = null; + foreach (Vertex v in foundVertices) + { + if (again) + { + if (v.selected) + again = false; + continue; + } + foundVertex = v; + break; + } + + if (foundVertex != null) + { + foreach (Vertex v in graph.vertices.Values) + { + v.selected = false; + } + foundVertex.selected = true; + if (foundVertex.visible) + { + outerPanel.AutoScrollPosition = new Point(foundVertex.rectangle.X - 100, foundVertex.rectangle.Y - 100); + selectEdges(); + graphPanel.Invalidate(); + } + else + { + if (MessageBox.Show("Routine found but not visible with current settings - zoom to this Routine?", "Not Visible", MessageBoxButtons.OKCancel) == DialogResult.OK) + { + ZoomVertex(foundVertex, ""); + } + } + } + else + { + MessageBox.Show("No Routine found"); + } + } + + private void findMenuItem_Click(object sender, System.EventArgs e) + { + if (findForm.ShowDialog() == DialogResult.OK) + { + FindVertex(findForm.nameTextBox.Text, findForm.signatureTextBox.Text, false); + } + } + + private void versionTimer_Tick(object sender, System.EventArgs e) + { + if (font != MainForm.instance.font) + { + font = MainForm.instance.font; + fontHeight = font.Height; + placeVertices = placeEdges = true; + graphPanel.Invalidate(); + } + } + + private double Diversity(Dictionary d) + { + double sum = 0.0; + double sumSq = 0.0; + foreach (Edge e in d.Values) + { + double dWeight = e.weight; + sum += dWeight; + sumSq += dWeight*dWeight; + } + if (sumSq <= 0.0) + return 0.0; + else + return sum*sum/sumSq; + } + + private double Score(Vertex v) + { + return v.weight*(Diversity(v.incomingEdges) + Diversity(v.outgoingEdges)); + } + + class CompareVerticesByScore : IComparer + { + Dictionary scoreOfVertex; + + internal CompareVerticesByScore(Dictionary scoreOfVertex) + { + this.scoreOfVertex = scoreOfVertex; + } + + int IComparer.Compare(object x, object y) + { + double scoreX = scoreOfVertex[(Vertex)x]; + double scoreY = scoreOfVertex[(Vertex)y]; + if (scoreX < scoreY) + return 1; + else if (scoreX > scoreY) + return -1; + else + return 0; + } + } + + private void findInterestingNodesMenuItem_Click(object sender, System.EventArgs e) + { + Dictionary scoreOfVertex = new Dictionary(); + ArrayList verticesSortedByScore = new ArrayList(); + foreach (Vertex v in graph.vertices.Values) + { + double score = Score(v); + scoreOfVertex[v] = score; + verticesSortedByScore.Add(v); + } + verticesSortedByScore.Sort(new CompareVerticesByScore(scoreOfVertex)); + + for (int i = Math.Min(5, verticesSortedByScore.Count); i > 0; i--) + { + ZoomVertex((Vertex)verticesSortedByScore[i-1], string.Format("Interesting Node (Rank {0}): ", i)); + } + + } + + private Vertex CloneVertex(Graph g, Vertex v) + { + Vertex vn = g.FindOrCreateVertex(v.name, v.signature, v.moduleName); + vn.basicName = v.basicName; + vn.basicSignature = v.basicSignature; + vn.active = true; +// vn.count = v.count; +// vn.basicWeight = v.basicWeight; + + return vn; + } + + private void ZoomVertex(Vertex v, string titlePrefix) + { + toolTip.Active = false; + Graph g; + if (graph.graphSource is Graph) + { + Graph orgGraph = (Graph)graph.graphSource; + g = new Graph(orgGraph); + v = orgGraph.FindOrCreateVertex(v.name, v.signature, v.moduleName); + } + else + g = new Graph(graph); + g.allocatedAfterTickIndex = graph.allocatedAfterTickIndex; + g.allocatedBeforeTickIndex = graph.allocatedBeforeTickIndex; + Vertex vn = CloneVertex(g, v); + vn.count = v.count; + if (v.incomingEdges.Count == 0) + { + if (v != graph.TopVertex) + g.FindOrCreateEdge(g.TopVertex, vn).AddWeight(v.weight); + } + else + { + foreach (Edge e in v.incomingEdges.Values) + { + Vertex vin = CloneVertex(g, e.FromVertex); + g.FindOrCreateEdge(vin, vn).AddWeight(e.weight); + if (vin != g.TopVertex) + g.FindOrCreateEdge(g.TopVertex, vin).AddWeight(e.weight); + } + } + if (v.outgoingEdges.Count == 0) + { + if (v != graph.BottomVertex) + g.FindOrCreateEdge(vn, g.BottomVertex).AddWeight(v.weight); + } + else + { + foreach (Edge e in v.outgoingEdges.Values) + { + Vertex von = CloneVertex(g, e.ToVertex); + g.FindOrCreateEdge(vn, von).AddWeight(e.weight); + if (von != g.BottomVertex) + g.FindOrCreateEdge(von, g.BottomVertex).AddWeight(e.weight); + } + } + g.BottomVertex.active = false; + g.graphType = graph.graphType; + g.typeGraphOptions = graph.typeGraphOptions; + if (titlePrefix == null) + titlePrefix = "Zoom to: "; + string title = titlePrefix + v.name + " " + (v.signature != null? v.signature : ""); + GraphViewForm graphViewForm = new GraphViewForm(g, title); + graphViewForm.Visible = true; + } + + private void graphPanel_DoubleClick(object sender, System.EventArgs e) + { + Point p = lastMouseDownPoint; + foreach (Vertex v in graph.vertices.Values) + { + if (v.selectionRectangle.Contains(p)) + { + ZoomVertex(v, null); + return; + } + } + } + + private Graph GetOriginalGraph() + { + if (graph.graphSource is Graph) + { + // this is the case when we are in a zoom window + return (Graph)graph.graphSource; + } + else + return graph; + } + + private ObjectGraph GetObjectGraph() + { + return (ObjectGraph)GetOriginalGraph().graphSource; + } + + private Histogram MakeHistogram(int allocatedAfterTickIndex, int allocatedBeforeTickIndex) + { + // Build a histogram of types, sizes, allocation stacks from the object graph, + // using only the objects whose vertices are selected + + // First of all, limit the interval to the one of the underlying graph + if (allocatedAfterTickIndex < graph.allocatedAfterTickIndex) + allocatedAfterTickIndex = graph.allocatedAfterTickIndex; + if (allocatedBeforeTickIndex > graph.allocatedBeforeTickIndex) + allocatedBeforeTickIndex = graph.allocatedBeforeTickIndex; + + Graph originalGraph = GetOriginalGraph(); + ObjectGraph objectGraph = GetObjectGraph(); + Histogram histogram = new Histogram(objectGraph.readNewLog); + bool anyVertexSelected = SelectedVertexCount() != 0; + foreach (KeyValuePair keyValuePair in objectGraph.idToObject) + { + ulong id = keyValuePair.Key; + ObjectGraph.GcObject gcObject = keyValuePair.Value; + if (gcObject.TypeSizeStackTraceId > 0 && + gcObject.AllocTickIndex > allocatedAfterTickIndex && + gcObject.AllocTickIndex < allocatedBeforeTickIndex && + gcObject.InterestLevel != InterestLevel.Ignore) + { + if (anyVertexSelected || originalGraph != graph) + { + gcObject.vertex = null; + Vertex v = objectGraph.FindVertex(id, gcObject, originalGraph, ObjectGraph.BuildTypeGraphOptions.LumpBySignature); + if (originalGraph != graph) + { + v = graph.FindVertex(v.name, v.signature, v.moduleName); + if (v == null) + continue; + } + if (anyVertexSelected && !v.selected) + continue; + } + histogram.AddObject(gcObject.TypeSizeStackTraceId, 1); + } + } + return histogram; + } + + private void ShowWhoAllocated(string title, int allocatedAfterTickIndex, int allocatedBeforeTickIndex) + { + Histogram histogram = MakeHistogram(allocatedAfterTickIndex, allocatedBeforeTickIndex); + + // Build the real graph from the histogram + + Graph g = histogram.BuildAllocationGraph(filterForm); + + GraphViewForm graphViewForm = new GraphViewForm(g, title); + graphViewForm.Visible = true; + } + + private void showWhoAllocatedMenuItem_Click(object sender, System.EventArgs e) + { + if (graph.graphType == Graph.GraphType.HeapGraph) + { + string title = "Allocation Graph for Live Objects"; + ShowWhoAllocated(title, -1, int.MaxValue); + } + } + + private void showWhoAllocatedNewMenuItem_Click(object sender, System.EventArgs e) + { + if (graph.graphType == Graph.GraphType.HeapGraph) + { + string title = "Allocation Graph for New Live Objects"; + ShowWhoAllocated(title, graph.previousGraphTickIndex, int.MaxValue); + } + } + + private void showNewObjectsMenuItem_Click(object sender, System.EventArgs e) + { + ObjectGraph objectGraph = (ObjectGraph)graph.graphSource; + Graph g = objectGraph.BuildTypeGraph(graph.previousGraphTickIndex, int.MaxValue, ObjectGraph.BuildTypeGraphOptions.LumpBySignature, filterForm); + + string title = "New Live Objects"; + GraphViewForm graphViewForm = new GraphViewForm(g, title); + graphViewForm.Visible = true; + } + + private void zoomToNodeMenuItem_Click(object sender, System.EventArgs e) + { + int selectedVertexCount = SelectedVertexCount(); + if (selectedVertexCount == 0) + MessageBox.Show("Please select a node first by clicking on it"); + else if (selectedVertexCount > 10) + MessageBox.Show("Too many vertices selected"); + else + { + foreach (Vertex v in graph.vertices.Values) + { + if (v.selected) + ZoomVertex(v, null); + } + } + } + + private void showObjectsAllocatedBetween_Click(object sender, System.EventArgs e) + { + CommentRangeForm commentRangeForm = new CommentRangeForm(); + if (commentRangeForm.ShowDialog() == DialogResult.OK) + { + ObjectGraph objectGraph = (ObjectGraph)graph.graphSource; + Graph g = objectGraph.BuildTypeGraph(commentRangeForm.startTickIndex, commentRangeForm.endTickIndex, ObjectGraph.BuildTypeGraphOptions.LumpBySignature, filterForm); + + string title = string.Format("Live Objects Allocated Between {0} and {1}", commentRangeForm.startComment, commentRangeForm.endComment); + GraphViewForm graphViewForm = new GraphViewForm(g, title); + graphViewForm.Visible = true; + } + } + + private void showWhoAllocatedObjectsBetweenMenuItem_Click(object sender, System.EventArgs e) + { + CommentRangeForm commentRangeForm = new CommentRangeForm(); + if (commentRangeForm.ShowDialog() == DialogResult.OK) + { + string title = string.Format("Allocation Graph for Live Objects Allocated Between {0} and {1}", commentRangeForm.startComment, commentRangeForm.endComment); + ShowWhoAllocated(title, commentRangeForm.startTickIndex, commentRangeForm.endTickIndex); + } + } + + private void showInstancesMenuItem_Click(object sender, System.EventArgs e) + { + Graph orgGraph = graph; + if (orgGraph.graphSource is Graph) + orgGraph = (Graph)orgGraph.graphSource; + ObjectGraph objectGraph = (ObjectGraph)orgGraph.graphSource; + ObjectGraph.BuildTypeGraphOptions options; + if (graph.typeGraphOptions == ObjectGraph.BuildTypeGraphOptions.LumpBySignature) + { + options = ObjectGraph.BuildTypeGraphOptions.IndividualObjects; + } + else + { + options = ObjectGraph.BuildTypeGraphOptions.LumpBySignature; + } + graph = objectGraph.BuildTypeGraph(graph.allocatedAfterTickIndex, graph.allocatedBeforeTickIndex, options, filterForm); + + placeVertices = placeEdges = true; + graphPanel.Invalidate(); + EnableDisableMenuItems(); + } + + private void showHistogramMenuItem_Click(object sender, System.EventArgs e) + { + if (graph.graphType == Graph.GraphType.HeapGraph) + { + Histogram histogram = MakeHistogram(-1, int.MaxValue); + + ObjectGraph objectGraph = GetObjectGraph(); + string title = string.Format("Histogram by Size for Live Objects at {0}", objectGraph.readNewLog.TickIndexToTime(objectGraph.tickIndex)); + HistogramViewForm histogramViewForm = new HistogramViewForm(histogram, title); + histogramViewForm.Show(); + } + } + + private void showReferencesMenuItem_Click(object sender, System.EventArgs e) + { + if (graph.graphType == Graph.GraphType.HeapGraph && SelectedVertexCount() != 0) + { + ObjectGraph objectGraph = GetObjectGraph(); + Graph g = objectGraph.BuildReferenceGraph(graph); + string title = "References to selected objects"; + GraphViewForm graphViewForm = new GraphViewForm(g, title); + graphViewForm.Visible = true; + } + } + + private void FilterToSelectedVertex(bool ancestors, bool descendants) + { + StringBuilder types = new StringBuilder(); + StringBuilder methods = new StringBuilder(); + StringBuilder signatures = new StringBuilder(); + StringBuilder addresses = new StringBuilder(); + + ArrayList selectedVertices = new ArrayList(); + foreach (Vertex v in graph.vertices.Values) + { + if (v.selected) + { + selectedVertices.Add(v); + if (v.signature == null || graph.graphType == Graph.GraphType.HeapGraph) + { + if (types.Length != 0) + types.Append(';'); + types.Append(v.name); + } + else + { + if (methods.Length != 0) + methods.Append(';'); + methods.Append(Vertex.RemoveRecursionCount(v.name)); + } + if (v.signature != null) + { + if (graph.graphType == Graph.GraphType.HeapGraph && + graph.typeGraphOptions == ObjectGraph.BuildTypeGraphOptions.IndividualObjects) + { + if (addresses.Length != 0) + addresses.Append(';'); + string[] pieces = v.signature.Split('=', ','); + Debug.Assert(pieces.Length == 4 && pieces[0] == "Address "); + addresses.Append(pieces[1].Trim()); + } + else + { + if (signatures.Length != 0) + signatures.Append(';'); + signatures.Append(v.signature); + } + } + } + } + + string typeFilter = types.ToString(); + string methodFilter = methods.ToString(); + string signatureFilter = signatures.ToString(); + string addressFilter = addresses.ToString(); + + filterForm.SetFilterForm(typeFilter, methodFilter, signatureFilter, addressFilter, + ancestors, descendants, false, false); + + RefreshGraph(); + + foreach (Vertex v in selectedVertices) + { + Vertex newV = graph.FindOrCreateVertex(v.name, v.signature, v.moduleName); + selectVertex(newV, true); + } + selectEdges(); + } + + private void filterToCallersCalleesMenuItem_Click(object sender, System.EventArgs e) + { + FilterToSelectedVertex(true, true); + } + + private void resetFilterMenuItem_Click(object sender, System.EventArgs e) + { + filterForm.SetFilterForm("", "", "", "", + true, true, false, false); + + RefreshGraph(); + } + + private void findAgainMenuItem_Click(object sender, System.EventArgs e) + { + if (findForm.nameTextBox.Text == "" && findForm.signatureTextBox.Text == "") + { + if (findForm.ShowDialog() != DialogResult.OK) + return; + } + FindVertex(findForm.nameTextBox.Text, findForm.signatureTextBox.Text, true); + } + } +} diff --git a/CLRProfiler/CLRProfiler/GraphViewForm.resx b/CLRProfiler/CLRProfiler/GraphViewForm.resx new file mode 100644 index 0000000..5f6656c --- /dev/null +++ b/CLRProfiler/CLRProfiler/GraphViewForm.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 132, 17 + + + 248, 17 + + \ No newline at end of file diff --git a/CLRProfiler/CLRProfiler/HelpForm.Designer.cs b/CLRProfiler/CLRProfiler/HelpForm.Designer.cs new file mode 100644 index 0000000..d6a2685 --- /dev/null +++ b/CLRProfiler/CLRProfiler/HelpForm.Designer.cs @@ -0,0 +1,70 @@ +namespace CLRProfiler +{ + partial class HelpForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.helpText = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // helpText + // + this.helpText.AcceptsReturn = true; + this.helpText.AccessibleName = "helpTextBox"; + this.helpText.CausesValidation = false; + this.helpText.Cursor = System.Windows.Forms.Cursors.Arrow; + this.helpText.Dock = System.Windows.Forms.DockStyle.Fill; + this.helpText.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.helpText.Location = new System.Drawing.Point(0, 0); + this.helpText.Multiline = true; + this.helpText.Name = "helpText"; + this.helpText.ReadOnly = true; + this.helpText.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.helpText.Size = new System.Drawing.Size(665, 590); + this.helpText.TabIndex = 0; + this.helpText.TabStop = false; + this.helpText.WordWrap = false; + // + // HelpForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(665, 590); + this.Controls.Add(this.helpText); + this.Name = "HelpForm"; + this.Text = "CLRProfiler Help"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + public System.Windows.Forms.TextBox helpText; + + } +} \ No newline at end of file diff --git a/CLRProfiler/CLRProfiler/HelpForm.cs b/CLRProfiler/CLRProfiler/HelpForm.cs new file mode 100644 index 0000000..4306107 --- /dev/null +++ b/CLRProfiler/CLRProfiler/HelpForm.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Text; +using System.Windows.Forms; + +namespace CLRProfiler +{ + public partial class HelpForm : Form + { + public HelpForm() + { + InitializeComponent(); + } + } +} diff --git a/CLRProfiler/CLRProfiler/HistogramViewForm.cs b/CLRProfiler/CLRProfiler/HistogramViewForm.cs new file mode 100644 index 0000000..b21359a --- /dev/null +++ b/CLRProfiler/CLRProfiler/HistogramViewForm.cs @@ -0,0 +1,1258 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +using System; +using System.Drawing; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Windows.Forms; +using System.Diagnostics; +using System.IO; + +namespace CLRProfiler +{ + /// + /// Summary description for HistogramViewForm. + /// + public class HistogramViewForm : System.Windows.Forms.Form + { + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Panel panel2; + private System.Windows.Forms.Panel panel3; + private System.Windows.Forms.RadioButton radioButton1; + private System.Windows.Forms.RadioButton radioButton2; + private System.Windows.Forms.RadioButton radioButton3; + private System.Windows.Forms.RadioButton radioButton4; + private System.Windows.Forms.RadioButton radioButton5; + private System.Windows.Forms.RadioButton radioButton6; + private System.Windows.Forms.RadioButton radioButton7; + private System.Windows.Forms.RadioButton radioButton8; + private System.Windows.Forms.RadioButton radioButton9; + private System.Windows.Forms.RadioButton radioButton10; + private System.Windows.Forms.Panel graphPanel; + private System.Windows.Forms.Panel typeLegendPanel; + private System.Windows.Forms.GroupBox verticalScaleGroupBox; + private System.Timers.Timer versionTimer; + private System.Windows.Forms.GroupBox horizontalScaleGroupBox; + private System.Windows.Forms.RadioButton coarseRadioButton; + private System.Windows.Forms.RadioButton veryFineRadioButton; + private System.Windows.Forms.RadioButton mediumRadioButton; + private System.Windows.Forms.RadioButton fineRadioButton; + private System.Windows.Forms.ContextMenu contextMenu; + private System.Windows.Forms.MenuItem exportMenuItem; + private System.Windows.Forms.SaveFileDialog exportSaveFileDialog; + private System.Windows.Forms.MenuItem showWhoAllocatedMenuItem; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + ReadLogResult lastLogResult; + private System.Windows.Forms.Splitter splitter1; + private RadioButton radioButton13; + private RadioButton radioButton12; + private RadioButton radioButton11; + private RadioButton radioButton14; + + private Font font; + + public HistogramViewForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + toolTip = new ToolTip(); + toolTip.Active = false; + toolTip.ShowAlways = true; + toolTip.AutomaticDelay = 70; + toolTip.ReshowDelay = 1; + + autoUpdate = true; + lastLogResult = MainForm.instance.lastLogResult; + + if (lastLogResult != null) + { + histogram = lastLogResult.allocatedHistogram; + typeName = histogram.readNewLog.typeName; + } + Text = "Histogram by Size for Allocated Objects"; + font = MainForm.instance.font; + } + + internal HistogramViewForm(Histogram histogram, string title) : this() + { + this.histogram = histogram; + autoUpdate = false; + Text = title; + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + versionTimer.Stop(); + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.panel1 = new System.Windows.Forms.Panel(); + this.horizontalScaleGroupBox = new System.Windows.Forms.GroupBox(); + this.veryFineRadioButton = new System.Windows.Forms.RadioButton(); + this.fineRadioButton = new System.Windows.Forms.RadioButton(); + this.mediumRadioButton = new System.Windows.Forms.RadioButton(); + this.coarseRadioButton = new System.Windows.Forms.RadioButton(); + this.verticalScaleGroupBox = new System.Windows.Forms.GroupBox(); + this.radioButton14 = new System.Windows.Forms.RadioButton(); + this.radioButton13 = new System.Windows.Forms.RadioButton(); + this.radioButton12 = new System.Windows.Forms.RadioButton(); + this.radioButton11 = new System.Windows.Forms.RadioButton(); + this.radioButton10 = new System.Windows.Forms.RadioButton(); + this.radioButton9 = new System.Windows.Forms.RadioButton(); + this.radioButton8 = new System.Windows.Forms.RadioButton(); + this.radioButton7 = new System.Windows.Forms.RadioButton(); + this.radioButton6 = new System.Windows.Forms.RadioButton(); + this.radioButton5 = new System.Windows.Forms.RadioButton(); + this.radioButton4 = new System.Windows.Forms.RadioButton(); + this.radioButton3 = new System.Windows.Forms.RadioButton(); + this.radioButton2 = new System.Windows.Forms.RadioButton(); + this.radioButton1 = new System.Windows.Forms.RadioButton(); + this.panel2 = new System.Windows.Forms.Panel(); + this.graphPanel = new System.Windows.Forms.Panel(); + this.panel3 = new System.Windows.Forms.Panel(); + this.typeLegendPanel = new System.Windows.Forms.Panel(); + this.versionTimer = new System.Timers.Timer(); + this.contextMenu = new System.Windows.Forms.ContextMenu(); + this.showWhoAllocatedMenuItem = new System.Windows.Forms.MenuItem(); + this.exportMenuItem = new System.Windows.Forms.MenuItem(); + this.exportSaveFileDialog = new System.Windows.Forms.SaveFileDialog(); + this.splitter1 = new System.Windows.Forms.Splitter(); + this.panel1.SuspendLayout(); + this.horizontalScaleGroupBox.SuspendLayout(); + this.verticalScaleGroupBox.SuspendLayout(); + this.panel2.SuspendLayout(); + this.panel3.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.versionTimer)).BeginInit(); + this.SuspendLayout(); + // + // panel1 + // + this.panel1.Controls.Add(this.horizontalScaleGroupBox); + this.panel1.Controls.Add(this.verticalScaleGroupBox); + this.panel1.Dock = System.Windows.Forms.DockStyle.Top; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(1000, 72); + this.panel1.TabIndex = 0; + // + // horizontalScaleGroupBox + // + this.horizontalScaleGroupBox.Controls.Add(this.veryFineRadioButton); + this.horizontalScaleGroupBox.Controls.Add(this.fineRadioButton); + this.horizontalScaleGroupBox.Controls.Add(this.mediumRadioButton); + this.horizontalScaleGroupBox.Controls.Add(this.coarseRadioButton); + this.horizontalScaleGroupBox.Location = new System.Drawing.Point(692, 8); + this.horizontalScaleGroupBox.Name = "horizontalScaleGroupBox"; + this.horizontalScaleGroupBox.Size = new System.Drawing.Size(292, 48); + this.horizontalScaleGroupBox.TabIndex = 1; + this.horizontalScaleGroupBox.TabStop = false; + this.horizontalScaleGroupBox.Text = "Horizontal Scale"; + // + // veryFineRadioButton + // + this.veryFineRadioButton.Location = new System.Drawing.Point(212, 16); + this.veryFineRadioButton.Name = "veryFineRadioButton"; + this.veryFineRadioButton.Size = new System.Drawing.Size(72, 24); + this.veryFineRadioButton.TabIndex = 3; + this.veryFineRadioButton.Text = "Very Fine"; + this.veryFineRadioButton.CheckedChanged += new System.EventHandler(this.Refresh); + // + // fineRadioButton + // + this.fineRadioButton.Location = new System.Drawing.Point(158, 16); + this.fineRadioButton.Name = "fineRadioButton"; + this.fineRadioButton.Size = new System.Drawing.Size(48, 24); + this.fineRadioButton.TabIndex = 2; + this.fineRadioButton.Text = "Fine"; + this.fineRadioButton.CheckedChanged += new System.EventHandler(this.Refresh); + // + // mediumRadioButton + // + this.mediumRadioButton.Location = new System.Drawing.Point(88, 16); + this.mediumRadioButton.Name = "mediumRadioButton"; + this.mediumRadioButton.Size = new System.Drawing.Size(64, 24); + this.mediumRadioButton.TabIndex = 1; + this.mediumRadioButton.Text = "Medium"; + this.mediumRadioButton.CheckedChanged += new System.EventHandler(this.Refresh); + // + // coarseRadioButton + // + this.coarseRadioButton.Checked = true; + this.coarseRadioButton.Location = new System.Drawing.Point(18, 16); + this.coarseRadioButton.Name = "coarseRadioButton"; + this.coarseRadioButton.Size = new System.Drawing.Size(64, 24); + this.coarseRadioButton.TabIndex = 0; + this.coarseRadioButton.TabStop = true; + this.coarseRadioButton.Text = "Coarse"; + this.coarseRadioButton.CheckedChanged += new System.EventHandler(this.Refresh); + // + // verticalScaleGroupBox + // + this.verticalScaleGroupBox.Controls.Add(this.radioButton14); + this.verticalScaleGroupBox.Controls.Add(this.radioButton13); + this.verticalScaleGroupBox.Controls.Add(this.radioButton12); + this.verticalScaleGroupBox.Controls.Add(this.radioButton11); + this.verticalScaleGroupBox.Controls.Add(this.radioButton10); + this.verticalScaleGroupBox.Controls.Add(this.radioButton9); + this.verticalScaleGroupBox.Controls.Add(this.radioButton8); + this.verticalScaleGroupBox.Controls.Add(this.radioButton7); + this.verticalScaleGroupBox.Controls.Add(this.radioButton6); + this.verticalScaleGroupBox.Controls.Add(this.radioButton5); + this.verticalScaleGroupBox.Controls.Add(this.radioButton4); + this.verticalScaleGroupBox.Controls.Add(this.radioButton3); + this.verticalScaleGroupBox.Controls.Add(this.radioButton2); + this.verticalScaleGroupBox.Controls.Add(this.radioButton1); + this.verticalScaleGroupBox.Location = new System.Drawing.Point(16, 8); + this.verticalScaleGroupBox.Name = "verticalScaleGroupBox"; + this.verticalScaleGroupBox.Size = new System.Drawing.Size(670, 48); + this.verticalScaleGroupBox.TabIndex = 0; + this.verticalScaleGroupBox.TabStop = false; + this.verticalScaleGroupBox.Text = "Vertical Scale: Kilobytes/Pixel"; + // + // radioButton14 + // + this.radioButton14.Location = new System.Drawing.Point(601, 16); + this.radioButton14.Name = "radioButton14"; + this.radioButton14.Size = new System.Drawing.Size(63, 24); + this.radioButton14.TabIndex = 13; + this.radioButton14.Text = "20000"; + this.radioButton14.CheckedChanged += new System.EventHandler(this.Refresh); + // + // radioButton13 + // + this.radioButton13.Location = new System.Drawing.Point(541, 16); + this.radioButton13.Name = "radioButton13"; + this.radioButton13.Size = new System.Drawing.Size(63, 24); + this.radioButton13.TabIndex = 12; + this.radioButton13.Text = "10000"; + this.radioButton13.CheckedChanged += new System.EventHandler(this.Refresh); + // + // radioButton12 + // + this.radioButton12.Location = new System.Drawing.Point(487, 16); + this.radioButton12.Name = "radioButton12"; + this.radioButton12.Size = new System.Drawing.Size(57, 24); + this.radioButton12.TabIndex = 11; + this.radioButton12.Text = "5000"; + this.radioButton12.CheckedChanged += new System.EventHandler(this.Refresh); + // + // radioButton11 + // + this.radioButton11.Location = new System.Drawing.Point(433, 16); + this.radioButton11.Name = "radioButton11"; + this.radioButton11.Size = new System.Drawing.Size(55, 24); + this.radioButton11.TabIndex = 10; + this.radioButton11.Text = "2000"; + this.radioButton11.CheckedChanged += new System.EventHandler(this.Refresh); + // + // radioButton10 + // + this.radioButton10.Location = new System.Drawing.Point(376, 16); + this.radioButton10.Name = "radioButton10"; + this.radioButton10.Size = new System.Drawing.Size(56, 24); + this.radioButton10.TabIndex = 9; + this.radioButton10.Text = "1000"; + this.radioButton10.CheckedChanged += new System.EventHandler(this.Refresh); + // + // radioButton9 + // + this.radioButton9.Location = new System.Drawing.Point(328, 16); + this.radioButton9.Name = "radioButton9"; + this.radioButton9.Size = new System.Drawing.Size(48, 24); + this.radioButton9.TabIndex = 8; + this.radioButton9.Text = "500"; + this.radioButton9.CheckedChanged += new System.EventHandler(this.Refresh); + // + // radioButton8 + // + this.radioButton8.Location = new System.Drawing.Point(280, 16); + this.radioButton8.Name = "radioButton8"; + this.radioButton8.Size = new System.Drawing.Size(48, 24); + this.radioButton8.TabIndex = 7; + this.radioButton8.Text = "200"; + this.radioButton8.CheckedChanged += new System.EventHandler(this.Refresh); + // + // radioButton7 + // + this.radioButton7.Location = new System.Drawing.Point(232, 16); + this.radioButton7.Name = "radioButton7"; + this.radioButton7.Size = new System.Drawing.Size(48, 24); + this.radioButton7.TabIndex = 6; + this.radioButton7.Text = "100"; + this.radioButton7.CheckedChanged += new System.EventHandler(this.Refresh); + // + // radioButton6 + // + this.radioButton6.Location = new System.Drawing.Point(192, 16); + this.radioButton6.Name = "radioButton6"; + this.radioButton6.Size = new System.Drawing.Size(40, 24); + this.radioButton6.TabIndex = 5; + this.radioButton6.Text = "50"; + this.radioButton6.CheckedChanged += new System.EventHandler(this.Refresh); + // + // radioButton5 + // + this.radioButton5.Location = new System.Drawing.Point(152, 16); + this.radioButton5.Name = "radioButton5"; + this.radioButton5.Size = new System.Drawing.Size(40, 24); + this.radioButton5.TabIndex = 4; + this.radioButton5.Text = "20"; + this.radioButton5.CheckedChanged += new System.EventHandler(this.Refresh); + // + // radioButton4 + // + this.radioButton4.Location = new System.Drawing.Point(112, 16); + this.radioButton4.Name = "radioButton4"; + this.radioButton4.Size = new System.Drawing.Size(40, 24); + this.radioButton4.TabIndex = 3; + this.radioButton4.Text = "10"; + this.radioButton4.CheckedChanged += new System.EventHandler(this.Refresh); + // + // radioButton3 + // + this.radioButton3.Location = new System.Drawing.Point(80, 16); + this.radioButton3.Name = "radioButton3"; + this.radioButton3.Size = new System.Drawing.Size(32, 24); + this.radioButton3.TabIndex = 2; + this.radioButton3.Text = "5"; + this.radioButton3.CheckedChanged += new System.EventHandler(this.Refresh); + // + // radioButton2 + // + this.radioButton2.Location = new System.Drawing.Point(48, 16); + this.radioButton2.Name = "radioButton2"; + this.radioButton2.Size = new System.Drawing.Size(32, 24); + this.radioButton2.TabIndex = 1; + this.radioButton2.Text = "2"; + this.radioButton2.CheckedChanged += new System.EventHandler(this.Refresh); + // + // radioButton1 + // + this.radioButton1.Location = new System.Drawing.Point(16, 16); + this.radioButton1.Name = "radioButton1"; + this.radioButton1.Size = new System.Drawing.Size(24, 24); + this.radioButton1.TabIndex = 0; + this.radioButton1.Text = "1"; + this.radioButton1.CheckedChanged += new System.EventHandler(this.Refresh); + // + // panel2 + // + this.panel2.AutoScroll = true; + this.panel2.BackColor = System.Drawing.SystemColors.Control; + this.panel2.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panel2.Controls.Add(this.graphPanel); + this.panel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel2.Location = new System.Drawing.Point(0, 72); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(692, 533); + this.panel2.TabIndex = 1; + // + // graphPanel + // + this.graphPanel.BackColor = System.Drawing.SystemColors.Control; + this.graphPanel.Location = new System.Drawing.Point(0, 0); + this.graphPanel.Name = "graphPanel"; + this.graphPanel.Size = new System.Drawing.Size(496, 520); + this.graphPanel.TabIndex = 0; + this.graphPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.graphPanel_MouseDown); + this.graphPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.graphPanel_MouseMove); + this.graphPanel.Paint += new System.Windows.Forms.PaintEventHandler(this.graphPanel_Paint); + // + // panel3 + // + this.panel3.AutoScroll = true; + this.panel3.BackColor = System.Drawing.SystemColors.Control; + this.panel3.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panel3.Controls.Add(this.typeLegendPanel); + this.panel3.Dock = System.Windows.Forms.DockStyle.Right; + this.panel3.Location = new System.Drawing.Point(696, 72); + this.panel3.Name = "panel3"; + this.panel3.Size = new System.Drawing.Size(304, 533); + this.panel3.TabIndex = 3; + // + // typeLegendPanel + // + this.typeLegendPanel.BackColor = System.Drawing.SystemColors.Control; + this.typeLegendPanel.Location = new System.Drawing.Point(0, 0); + this.typeLegendPanel.Name = "typeLegendPanel"; + this.typeLegendPanel.Size = new System.Drawing.Size(262, 531); + this.typeLegendPanel.TabIndex = 0; + this.typeLegendPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.typeLegendPanel_MouseDown); + this.typeLegendPanel.Paint += new System.Windows.Forms.PaintEventHandler(this.typeLegendPanel_Paint); + // + // versionTimer + // + this.versionTimer.Enabled = true; + this.versionTimer.SynchronizingObject = this; + this.versionTimer.Elapsed += new System.Timers.ElapsedEventHandler(this.versionTimer_Elapsed); + // + // contextMenu + // + this.contextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.showWhoAllocatedMenuItem, + this.exportMenuItem}); + // + // showWhoAllocatedMenuItem + // + this.showWhoAllocatedMenuItem.Index = 0; + this.showWhoAllocatedMenuItem.Text = "Show Who Allocated"; + this.showWhoAllocatedMenuItem.Click += new System.EventHandler(this.showWhoAllocatedMenuItem_Click); + // + // exportMenuItem + // + this.exportMenuItem.Index = 1; + this.exportMenuItem.Text = "Export Data to File..."; + this.exportMenuItem.Click += new System.EventHandler(this.exportMenuItem_Click); + // + // exportSaveFileDialog + // + this.exportSaveFileDialog.FileName = "doc1"; + // + // splitter1 + // + this.splitter1.Dock = System.Windows.Forms.DockStyle.Right; + this.splitter1.Location = new System.Drawing.Point(692, 72); + this.splitter1.Name = "splitter1"; + this.splitter1.Size = new System.Drawing.Size(4, 533); + this.splitter1.TabIndex = 4; + this.splitter1.TabStop = false; + // + // HistogramViewForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(1000, 605); + this.Controls.Add(this.panel2); + this.Controls.Add(this.splitter1); + this.Controls.Add(this.panel3); + this.Controls.Add(this.panel1); + this.Name = "HistogramViewForm"; + this.Text = "HistogramViewForm"; + this.panel1.ResumeLayout(false); + this.horizontalScaleGroupBox.ResumeLayout(false); + this.verticalScaleGroupBox.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel3.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.versionTimer)).EndInit(); + this.ResumeLayout(false); + + } + #endregion + + class TypeDesc : IComparable + { + internal string typeName; + internal ulong totalSize; + internal int count; + internal Color color; + internal Brush brush; + internal Pen pen; + internal bool selected; + internal Rectangle rect; + + internal TypeDesc(string typeName) + { + this.typeName = typeName; + } + + public int CompareTo(Object o) + { + TypeDesc t = (TypeDesc)o; + if (t.totalSize < this.totalSize) + return -1; + else if (t.totalSize > this.totalSize) + return 1; + else + return 0; + } + } + + TypeDesc[] typeIndexToTypeDesc; + + ArrayList sortedTypeTable; + + struct Bucket + { + internal int minSize; + internal int maxSize; + internal ulong totalSize; + internal Dictionary typeDescToSizeCount; + internal bool selected; + }; + + Bucket[] buckets; + double currentScaleFactor; + + void BuildBuckets() + { + double scaleFactor = 2.0; + if (coarseRadioButton.Checked) + scaleFactor = 2.0; + else if (mediumRadioButton.Checked) + scaleFactor = Math.Sqrt(2.0); + else if (fineRadioButton.Checked) + scaleFactor = Math.Pow(2.0, 0.25); + else if (veryFineRadioButton.Checked) + scaleFactor = Math.Pow(2.0, 0.125); + + if (currentScaleFactor == scaleFactor) + { + for (int i = 0; i < buckets.Length; i++) + { + buckets[i].typeDescToSizeCount = new Dictionary(); + buckets[i].totalSize = 0; + } + return; + } + + currentScaleFactor = scaleFactor; + + int count = 0; + int startSize = 8; + double minSize; + for (minSize = startSize; minSize < int.MaxValue; minSize *= scaleFactor) + count++; + + buckets = new Bucket[count-1]; + minSize = startSize; + for (int i = 0; i < buckets.Length; i++) + { + buckets[i].minSize = (int)Math.Round(minSize); + minSize *= scaleFactor; + buckets[i].maxSize = (int)Math.Round(minSize)-1; + buckets[i].typeDescToSizeCount = new Dictionary(); + buckets[i].selected = false; + } + } + + private class SizeCount + { + internal ulong size; + internal int count; + } + + void AddToBuckets(TypeDesc t, int size, int count) + { + for (int i = 0; i < buckets.Length; i++) + { + if (buckets[i].minSize <= size && size <= buckets[i].maxSize) + { + ulong totalSize = (ulong)size*(ulong)count; + buckets[i].totalSize += totalSize; + SizeCount sizeCount; + if (!buckets[i].typeDescToSizeCount.TryGetValue(t, out sizeCount)) + { + sizeCount = new SizeCount(); + buckets[i].typeDescToSizeCount[t] = sizeCount; + } + sizeCount.size += totalSize; + sizeCount.count += count; + break; + } + } + } + + void TrimEmptyBuckets() + { + int lo = 0; + for (int i = 0; i < buckets.Length-1; i++) + { + if (buckets[i].totalSize != 0 || buckets[i+1].totalSize != 0) + break; + lo++; + } + int hi = buckets.Length-1; + for (int i = buckets.Length-1; i >= 0; i--) + { + if (buckets[i].totalSize != 0) + break; + hi--; + } + if (lo <= hi) + { + Bucket[] newBuckets = new Bucket[hi-lo+1]; + for (int i = lo; i <= hi; i++) + newBuckets[i-lo] = buckets[i]; + buckets = newBuckets; + } + } + + ulong totalSize; + int totalCount; + + void BuildSizeRangesAndTypeTable(int[] typeSizeStacktraceToCount) + { + BuildBuckets(); + + totalSize = 0; + totalCount = 0; + + if (typeIndexToTypeDesc == null) + typeIndexToTypeDesc = new TypeDesc[histogram.readNewLog.typeName.Length]; + else + { + foreach (TypeDesc t in typeIndexToTypeDesc) + { + if (t != null) + { + t.totalSize = 0; + t.count = 0; + } + } + } + + for (int i = 0; i < typeSizeStacktraceToCount.Length; i++) + { + int count = typeSizeStacktraceToCount[i]; + if (count == 0) + continue; + + int[] stacktrace = histogram.readNewLog.stacktraceTable.IndexToStacktrace(i); + int typeIndex = stacktrace[0]; + int size = stacktrace[1]; + + TypeDesc t = (TypeDesc)typeIndexToTypeDesc[typeIndex]; + if (t == null) + { + t = new TypeDesc(typeName[typeIndex]); + typeIndexToTypeDesc[typeIndex] = t; + } + t.totalSize += (ulong)size*(ulong)count; + t.count += count; + + totalSize += (ulong)size * (ulong)count; + totalCount += count; + + AddToBuckets(t, size, count); + } + + if (totalSize == 0) + totalSize = 1; + if (totalCount == 0) + totalCount = 1; + + TrimEmptyBuckets(); + + sortedTypeTable = new ArrayList(); + foreach (TypeDesc t in typeIndexToTypeDesc) + if (t != null) + sortedTypeTable.Add(t); + sortedTypeTable.Sort(); + } + + static Color[] firstColors = + { + Color.Red, + Color.Yellow, + Color.Green, + Color.Cyan, + Color.Blue, + Color.Magenta, + }; + + static Color[] colors = new Color[16]; + + Color MixColor(Color a, Color b) + { + int R = (a.R + b.R)/2; + int G = (a.G + b.G)/2; + int B = (a.B + b.B)/2; + + return Color.FromArgb(R, G, B); + } + + static void GrowColors() + { + Color[] newColors = new Color[2*colors.Length]; + for (int i = 0; i < colors.Length; i++) + newColors[i] = colors[i]; + colors = newColors; + } + + private TypeDesc FindSelectedType() + { + foreach (TypeDesc t in sortedTypeTable) + if (t.selected) + return t; + return null; + } + + private void ColorTypes() + { + int count = 0; + + bool anyTypeSelected = FindSelectedType() != null; + + foreach (TypeDesc t in sortedTypeTable) + { + if (count >= colors.Length) + GrowColors(); + if (count < firstColors.Length) + colors[count] = firstColors[count]; + else + colors[count] = MixColor(colors[count - firstColors.Length], colors[count - firstColors.Length + 1]); + t.color = colors[count]; + if (anyTypeSelected) + t.color = MixColor(colors[count], Color.White); + t.brush = new SolidBrush(t.color); + t.pen = new Pen(t.brush); + count++; + } + } + + int Scale(GroupBox groupBox, int pixelsAvailable, int rangeNeeded, bool firstTime) + { + if (!firstTime) + { + foreach (RadioButton rb in groupBox.Controls) + { + if (rb.Checked) + return Int32.Parse(rb.Text); + } + } + // No radio button was checked - let's come up with a suitable default + RadioButton maxLowScaleRB = null; + int maxLowRange = 0; + RadioButton minHighScaleRB = null; + int minHighRange = Int32.MaxValue; + foreach (RadioButton rb in groupBox.Controls) + { + int range = pixelsAvailable*Int32.Parse(rb.Text); + if (range < rangeNeeded) + { + if (maxLowRange < range) + { + maxLowRange = range; + maxLowScaleRB = rb; + } + } + else + { + if (minHighRange > range) + { + minHighRange = range; + minHighScaleRB = rb; + } + } + } + if (minHighScaleRB != null) + { + minHighScaleRB.Checked = true; + return Int32.Parse(minHighScaleRB.Text); + } + else + { + maxLowScaleRB.Checked = true; + return Int32.Parse(maxLowScaleRB.Text); + } + } + + int verticalScale = 0; + + int VerticalScale(int pixelsAvailable, ulong rangeNeeded, bool firstTime) + { + return Scale(verticalScaleGroupBox, pixelsAvailable, (int)(rangeNeeded/1024), firstTime)*1024; + } + + const int leftMargin = 30; + int bottomMargin = 50; + const int gap = 20; + int bucketWidth = 50; + const int topMargin = 30; + const int rightMargin = 30; + const int minHeight = 400; + int dotSize = 8; + + string FormatSize(ulong size) + { + double w = size; + string byteString = "bytes"; + if (w >= 1024) + { + w /= 1024; + byteString = "kB"; + } + if (w >= 1024) + { + w /= 1024; + byteString = "MB"; + } + if (w >= 1024) + { + w /= 1024; + byteString = "GB"; + } + string format = "{0:f0} {1}"; + if (w < 10) + format = "{0:f1} {1}"; + return string.Format(format, w, byteString); + } + + private void DrawBuckets(Graphics g) + { + Debug.Assert(verticalScale != 0); + bool noBucketSelected = true; + foreach (Bucket b in buckets) + { + if (b.selected) + { + noBucketSelected = false; + break; + } + } + + using (Brush blackBrush = new SolidBrush(Color.Black)) + { + int x = leftMargin; + foreach (Bucket b in buckets) + { + string s = "< " + FormatSize((ulong)b.maxSize+1); + int y = graphPanel.Height - bottomMargin; + g.DrawString(s, font, blackBrush, x, y + 3); + s = FormatSize(b.totalSize); + g.DrawString(s, font, blackBrush, x, y + 3 + font.Height); + s = string.Format("({0:f2}%)", 100.0*b.totalSize/totalSize); + g.DrawString(s, font, blackBrush, x, y + 3 + font.Height*2); + foreach (KeyValuePair d in b.typeDescToSizeCount) + { + TypeDesc t = d.Key; + SizeCount sizeCount = d.Value; + ulong size = sizeCount.size; + int height = (int)(size / (ulong)verticalScale); + + y -= height; + + Brush brush = t.brush; + if (t.selected && (b.selected || noBucketSelected)) + brush = blackBrush; + g.FillRectangle(brush, x, y, bucketWidth, height); + } + + x += bucketWidth + gap; + } + } + } + + const int typeLegendSpacing = 3; + + private void DrawTypeLegend(Graphics g) + { + dotSize = (int)g.MeasureString("0", font).Width; + int maxWidth = 0; + int x = leftMargin; + int y = topMargin + font.Height + typeLegendSpacing*2; + foreach (TypeDesc t in sortedTypeTable) + { + int typeNameWidth = (int)g.MeasureString(t.typeName, font).Width; + int sizeWidth = (int)g.MeasureString(" (999,999,999 bytes, 100.00% - 999,999 instances, 999 bytes average size)", font).Width; + t.rect = new Rectangle(x, y, Math.Max(typeNameWidth, sizeWidth)+dotSize*2, font.Height*2); + if (maxWidth < t.rect.Width) + maxWidth = t.rect.Width; + y = t.rect.Bottom + typeLegendSpacing; + } + int height = y + bottomMargin; + typeLegendPanel.Height = height; + + int width = leftMargin + maxWidth + rightMargin; + typeLegendPanel.Width = width; + + x = leftMargin; + y = topMargin; + + Brush blackBrush = new SolidBrush(Color.Black); + + string s = string.Format("Grand total: {0:n0} bytes - {1:n0} instances, {2:n0} bytes average size", totalSize, totalCount, totalSize/(ulong)totalCount); + g.DrawString(s, font, blackBrush, x, y); + + y += font.Height + typeLegendSpacing*2; + + int dotOffset = (font.Height - dotSize)/2; + foreach (TypeDesc t in sortedTypeTable) + { + Brush brush = t.brush; + if (t.selected) + brush = blackBrush; + g.FillRectangle(brush, t.rect.Left, t.rect.Top+dotOffset, dotSize, dotSize); + g.DrawString(t.typeName, font, blackBrush, t.rect.Left + dotSize*2, t.rect.Top); + s = string.Format(" ({0:n0} bytes, {1:f2}% - {2:n0} instances, {3:n0} bytes average size)", t.totalSize, (double)t.totalSize/totalSize*100.0, t.count, t.totalSize/(ulong)t.count); + g.DrawString(s, font, blackBrush, t.rect.Left + dotSize*2, t.rect.Top + font.Height); + y = t.rect.Bottom + typeLegendSpacing; + } + } + + bool initialized = false; + + private Histogram histogram; + private string[] typeName; + + private int BucketWidth(Graphics g) + { + int width1 = (int)g.MeasureString("< 999.9 sec", font).Width; + int width2 = (int)g.MeasureString("999 MB", font).Width; + width1 = Math.Max(width1, width2); + return Math.Max(width1, bucketWidth); + } + + private int BottomMargin() + { + return font.Height*3 + 10; + } + + private void graphPanel_Paint(object sender, System.Windows.Forms.PaintEventArgs e) + { + initialized = false; + + if (histogram == null || typeName == null) + return; + + Graphics g = e.Graphics; + + bucketWidth = BucketWidth(g); + bottomMargin = BottomMargin(); + + BuildSizeRangesAndTypeTable(histogram.typeSizeStacktraceToCount); + ColorTypes(); + + ulong maxTotalSize = 0; + foreach (Bucket b in buckets) + { + if (maxTotalSize < b.totalSize) + maxTotalSize = b.totalSize; + } + + verticalScale = VerticalScale(graphPanel.Height - topMargin - bottomMargin, maxTotalSize, verticalScale == 0); + + int maxBucketHeight = (int)(maxTotalSize/(ulong)verticalScale); + int height = topMargin + maxBucketHeight + bottomMargin; + if (height < minHeight) + height = minHeight; + + graphPanel.Height = height; + + int width = leftMargin + buckets.Length*bucketWidth + (buckets.Length-1)*gap + rightMargin; + graphPanel.Width = width; + + DrawBuckets(g); + + initialized = true; + } + + private void typeLegendPanel_Paint(object sender, System.Windows.Forms.PaintEventArgs e) + { + initialized = false; + + if (histogram == null || typeName == null) + return; + + BuildSizeRangesAndTypeTable(histogram.typeSizeStacktraceToCount); + ColorTypes(); + + Graphics g = e.Graphics; + + DrawTypeLegend(g); + + initialized = true; + } + + private void Refresh(object sender, System.EventArgs e) + { + graphPanel.Invalidate(); + } + + private void typeLegendPanel_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) + { + if ((e.Button & MouseButtons.Left) != MouseButtons.None) + { + for (int i = 0; i < buckets.Length; i++) + { + if (buckets[i].selected) + { + graphPanel.Invalidate(); + typeLegendPanel.Invalidate(); + buckets[i].selected = false; + } + } + if (sortedTypeTable != null) + { + foreach (TypeDesc t in sortedTypeTable) + { + if (t.rect.Contains(e.X, e.Y) != t.selected) + { + t.selected = !t.selected; + graphPanel.Invalidate(); + typeLegendPanel.Invalidate(); + } + } + } + } + else if ((e.Button & MouseButtons.Right) != MouseButtons.None) + { + Point p = new Point(e.X, e.Y); + contextMenu.Show(typeLegendPanel, p); + } + } + + private void graphPanel_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) + { + if (!initialized || verticalScale == 0) + return; + + if ((e.Button & MouseButtons.Left) != MouseButtons.None) + { + if (sortedTypeTable != null) + { + foreach (TypeDesc t in sortedTypeTable) + t.selected = false; + } + + int x = leftMargin; + for (int i = 0; i < buckets.Length; i++) + { + buckets[i].selected = false; + int y = graphPanel.Height - bottomMargin; + foreach (TypeDesc t in buckets[i].typeDescToSizeCount.Keys) + { + SizeCount sizeCount = buckets[i].typeDescToSizeCount[t]; + ulong size = sizeCount.size; + int height = (int)(size / (ulong)verticalScale); + + y -= height; + + Rectangle r = new Rectangle(x, y, bucketWidth, height); + if (r.Contains(e.X, e.Y)) + { + t.selected = true; + buckets[i].selected = true; + } + } + + x += bucketWidth + gap; + } + graphPanel.Invalidate(); + typeLegendPanel.Invalidate(); + } + else if ((e.Button & MouseButtons.Right) != MouseButtons.None) + { + Point p = new Point(e.X, e.Y); + contextMenu.Show(graphPanel, p); + } + } + + private ToolTip toolTip; + + private void graphPanel_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) + { + if (!initialized || verticalScale == 0) + return; + + if (Form.ActiveForm == this) + { + int x = leftMargin; + foreach (Bucket b in buckets) + { + int y = graphPanel.Height - bottomMargin; + foreach (KeyValuePair d in b.typeDescToSizeCount) + { + TypeDesc t = d.Key; + SizeCount sizeCount = d.Value; + ulong size = sizeCount.size; + int height = (int)(size / (ulong)verticalScale); + + y -= height; + + Rectangle bucketRect = new Rectangle(x, y, bucketWidth, height); + if (bucketRect.Contains(e.X, e.Y)) + { + string caption = string.Format("{0} {1} ({2:f2}%) - {3:n0} instances, {4} average size", t.typeName, FormatSize(size), 100.0*size/totalSize, sizeCount.count, FormatSize(sizeCount.size/(ulong)sizeCount.count)); + toolTip.Active = true; + toolTip.SetToolTip(graphPanel, caption); + return; + } + } + x += bucketWidth + gap; + } + } + toolTip.Active = false; + toolTip.SetToolTip(graphPanel, ""); + } + + private bool autoUpdate; + + private void versionTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + { + if (font != MainForm.instance.font) + { + font = MainForm.instance.font; + graphPanel.Invalidate(); + typeLegendPanel.Invalidate(); + } + + ReadLogResult readLogResult = MainForm.instance.lastLogResult; + + if (autoUpdate && readLogResult != null && readLogResult.allocatedHistogram != histogram) + { + histogram = readLogResult.allocatedHistogram; + typeName = histogram.readNewLog.typeName; + graphPanel.Invalidate(); + typeLegendPanel.Invalidate(); + } + } + + private void exportMenuItem_Click(object sender, System.EventArgs e) + { + exportSaveFileDialog.FileName = "HistogramBySize.csv"; + exportSaveFileDialog.Filter = "Comma separated files | *.csv"; + if (exportSaveFileDialog.ShowDialog() == DialogResult.OK) + { + StreamWriter w = new StreamWriter(exportSaveFileDialog.FileName); + + TypeDesc selectedType = FindSelectedType(); + + string title = "Histogram by Size"; + if (selectedType != null) + title += " of " + selectedType.typeName + " objects"; + + w.WriteLine(title); + w.WriteLine(); + + w.WriteLine("{0},{1},{2},{3},{4}", "Min Size", "Max Size", "# Instances", "Total Size", "Type"); + + bool noBucketSelected = true; + int minSize = 0; + int maxSize = int.MaxValue; + foreach (Bucket b in buckets) + { + if (b.selected) + { + noBucketSelected = false; + minSize = b.minSize; + maxSize = b.maxSize; + } + } + foreach (Bucket b in buckets) + { + if (noBucketSelected || b.selected) + { + foreach (KeyValuePair d in b.typeDescToSizeCount) + { + TypeDesc t = d.Key; + SizeCount sizeCount = d.Value; + + if (selectedType == null || t == selectedType) + w.WriteLine("{0},{1},{2},{3},{4}", b.minSize, b.maxSize, sizeCount.count, sizeCount.size, t.typeName); + } + } + } + + w.WriteLine(); + w.WriteLine(); + w.WriteLine("Raw data:"); + w.WriteLine(); + + w.WriteLine("{0},{1},{2},{3}", "Instance Size", "# Instances", "Total Size", "Type"); + for (int i = 0; i < histogram.typeSizeStacktraceToCount.Length; i++) + { + int count = histogram.typeSizeStacktraceToCount[i]; + if (count == 0) + continue; + int[] stacktrace = histogram.readNewLog.stacktraceTable.IndexToStacktrace(i); + int typeIndex = stacktrace[0]; + int size = stacktrace[1]; + + if (minSize <= size && size <= maxSize) + { + TypeDesc t = (TypeDesc)typeIndexToTypeDesc[typeIndex]; + + if (selectedType == null || t == selectedType) + { + w.WriteLine("{0},{1},{2},{3}", size, count, size*count, t.typeName); + } + } + } + + w.Close(); + } + } + + private void showWhoAllocatedMenuItem_Click(object sender, System.EventArgs e) + { + Histogram selectedHistogram; + string title; + TypeDesc selectedType = FindSelectedType(); + if (selectedType == null) + { + title = "Allocation Graph"; + selectedHistogram = histogram; + } + else + { + int minSize = 0; + int maxSize = int.MaxValue; + foreach (Bucket b in buckets) + { + if (b.selected) + { + minSize = b.minSize; + maxSize = b.maxSize; + } + } + title = string.Format("Allocation Graph for {0} objects", selectedType.typeName); + if (minSize > 0) + title += string.Format(" of size between {0:n0} and {1:n0} bytes", minSize, maxSize); + selectedHistogram = new Histogram(histogram.readNewLog); + for (int i = 0; i < histogram.typeSizeStacktraceToCount.Length; i++) + { + int count = histogram.typeSizeStacktraceToCount[i]; + if (count > 0) + { + int[] stacktrace = histogram.readNewLog.stacktraceTable.IndexToStacktrace(i); + int typeIndex = stacktrace[0]; + int size = stacktrace[1]; + + if (minSize <= size && size <= maxSize) + { + TypeDesc t = (TypeDesc)typeIndexToTypeDesc[typeIndex]; + + if (t == selectedType) + { + selectedHistogram.AddObject(i, count); + } + } + } + } + } + + Graph graph = selectedHistogram.BuildAllocationGraph(new FilterForm()); + + GraphViewForm graphViewForm = new GraphViewForm(graph, title); + graphViewForm.Visible = true; + } + } +} diff --git a/CLRProfiler/CLRProfiler/HistogramViewForm.resx b/CLRProfiler/CLRProfiler/HistogramViewForm.resx new file mode 100644 index 0000000..6854299 --- /dev/null +++ b/CLRProfiler/CLRProfiler/HistogramViewForm.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 287, 17 + + + 172, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/CLRProfiler/CLRProfiler/KillProcessForm.cs b/CLRProfiler/CLRProfiler/KillProcessForm.cs new file mode 100644 index 0000000..73073db --- /dev/null +++ b/CLRProfiler/CLRProfiler/KillProcessForm.cs @@ -0,0 +1,143 @@ +// ==++== +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// ==--== +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace CLRProfiler +{ + /// + /// Summary description for KillProcessForm. + /// + public class KillProcessForm : System.Windows.Forms.Form + { + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label3; + internal System.Windows.Forms.Label processFileNameLabel; + private System.Windows.Forms.Button yesButton; + private System.Windows.Forms.Button noButton; + private System.Windows.Forms.Button cancelButton; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public KillProcessForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.processFileNameLabel = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.yesButton = new System.Windows.Forms.Button(); + this.noButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Font = new System.Drawing.Font("Arial", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.label1.Location = new System.Drawing.Point(40, 32); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(264, 23); + this.label1.TabIndex = 0; + this.label1.Text = "The process you are profiling is still running:"; + // + // processFileNameLabel + // + this.processFileNameLabel.Font = new System.Drawing.Font("Arial", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.processFileNameLabel.Location = new System.Drawing.Point(48, 64); + this.processFileNameLabel.Name = "processFileNameLabel"; + this.processFileNameLabel.Size = new System.Drawing.Size(368, 32); + this.processFileNameLabel.TabIndex = 1; + this.processFileNameLabel.Text = ""; + // + // label3 + // + this.label3.Font = new System.Drawing.Font("Arial", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.label3.Location = new System.Drawing.Point(48, 112); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(328, 23); + this.label3.TabIndex = 2; + this.label3.Text = "Do you want to terminate it and optionally save the profile?"; + // + // yesButton + // + this.yesButton.DialogResult = System.Windows.Forms.DialogResult.Yes; + this.yesButton.Location = new System.Drawing.Point(48, 152); + this.yesButton.Name = "yesButton"; + this.yesButton.Size = new System.Drawing.Size(88, 23); + this.yesButton.TabIndex = 3; + this.yesButton.Text = "Yes"; + // + // noButton + // + this.noButton.DialogResult = System.Windows.Forms.DialogResult.No; + this.noButton.Location = new System.Drawing.Point(192, 152); + this.noButton.Name = "noButton"; + this.noButton.TabIndex = 4; + this.noButton.Text = "No"; + // + // cancelButton + // + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(320, 152); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.TabIndex = 5; + this.cancelButton.Text = "Cancel"; + // + // KillProcessForm + // + this.AcceptButton = this.yesButton; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(456, 205); + this.Controls.AddRange(new System.Windows.Forms.Control[] { + this.cancelButton, + this.noButton, + this.yesButton, + this.label3, + this.processFileNameLabel, + this.label1}); + this.Name = "KillProcessForm"; + this.Text = "Kill Process?"; + this.ResumeLayout(false); + + } + #endregion + } +} diff --git a/CLRProfiler/CLRProfiler/KillProcessForm.resx b/CLRProfiler/CLRProfiler/KillProcessForm.resx new file mode 100644 index 0000000..ddffb5b --- /dev/null +++ b/CLRProfiler/CLRProfiler/KillProcessForm.resx @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Assembly + + + KillProcessForm + + \ No newline at end of file diff --git a/CLRProfiler/CLRProfiler/ListViewer.cs b/CLRProfiler/CLRProfiler/ListViewer.cs new file mode 100644 index 0000000..3c7d119 --- /dev/null +++ b/CLRProfiler/CLRProfiler/ListViewer.cs @@ -0,0 +1,249 @@ +/* ==++== + * + * Copyright (c) Microsoft Corporation. All rights reserved. + * + * ==--== + * + * Class: ListViewer + * + * Description: Form for displaying function/object lists (invoked + * from call tree view) + */ + +using System; +using System.IO; +using System.Drawing; +using System.Collections; +using System.ComponentModel; +using System.Windows.Forms; + +namespace CLRProfiler +{ + /// + /// Summary description for ListViewer. + /// + public class ListViewer : System.Windows.Forms.Form, IComparer + { + private System.Windows.Forms.MainMenu mainMenu1; + private System.Windows.Forms.MenuItem menuItem1; + private System.Windows.Forms.MenuItem menuItem2; + public System.Windows.Forms.ListView list; + private IContainer components; + + private int sortColumn, sorting; + + public ListViewer() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + sortColumn = 0; + sorting = 0; + + list.ListViewItemSorter = this; + + // + // TODO: Add any constructor code after InitializeComponent call + // + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose( bool disposing ) + { + if( disposing ) + { + if(components != null) + { + components.Dispose(); + } + } + base.Dispose( disposing ); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.mainMenu1 = new System.Windows.Forms.MainMenu(this.components); + this.menuItem1 = new System.Windows.Forms.MenuItem(); + this.menuItem2 = new System.Windows.Forms.MenuItem(); + this.list = new System.Windows.Forms.ListView(); + this.SuspendLayout(); + // + // mainMenu1 + // + this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItem1}); + // + // menuItem1 + // + this.menuItem1.Index = 0; + this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuItem2}); + this.menuItem1.Text = "&File"; + // + // menuItem2 + // + this.menuItem2.Index = 0; + this.menuItem2.Text = "&Save as text..."; + this.menuItem2.Click += new System.EventHandler(this.menuItem2_Click); + // + // list + // + this.list.AllowColumnReorder = true; + this.list.Dock = System.Windows.Forms.DockStyle.Fill; + this.list.FullRowSelect = true; + this.list.GridLines = true; + this.list.Location = new System.Drawing.Point(0, 0); + this.list.Name = "list"; + this.list.Size = new System.Drawing.Size(292, 273); + this.list.TabIndex = 0; + this.list.UseCompatibleStateImageBehavior = false; + this.list.View = System.Windows.Forms.View.Details; + this.list.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.list_ColumnClick); + // + // ListViewer + // + this.ClientSize = new System.Drawing.Size(292, 273); + this.Controls.Add(this.list); + this.Menu = this.mainMenu1; + this.Name = "ListViewer"; + this.Text = "ListViewer"; + this.ResumeLayout(false); + + } + #endregion + + private void list_ColumnClick(object sender, System.Windows.Forms.ColumnClickEventArgs e) + { + if(e.Column == sortColumn) + { + sorting *= -1; + } + else + { + sortColumn = e.Column; + sorting = (sortColumn == 0 ? 1 : -1); + } + list.Sort(); + } + #region IComparer Members + + public int Compare(object x, object y) + { + int res = 0; + ListViewItem a = x as ListViewItem, b = y as ListViewItem; + string aa = a.SubItems[sortColumn].Text, bb = b.SubItems[sortColumn].Text; + if(sortColumn != 0) + { + long a1 = Int64.Parse(aa); + long b1 = Int64.Parse(bb); + res = (a1 > b1 ? 1 : (a1 == b1 ? 0 : -1)); + } + else + { + res = aa.CompareTo(bb); + } + return sorting * res; + } + + #endregion + + private void menuItem2_Click(object sender, System.EventArgs e) + { + SaveFileDialog sf = new SaveFileDialog(); + sf.Filter = "Formatted text|*.txt|Comma-Separated Values|*.csv"; + DialogResult r = sf.ShowDialog(); + if(r == DialogResult.OK) + { + try + { + if(File.Exists(sf.FileName)) + { + File.Delete(sf.FileName); + } + } + catch + { + MessageBox.Show(this, "Cannot delete existing file " + sf.FileName, "Failure"); + return; + } + + bool formatNicely = !sf.FileName.ToLower().EndsWith(".csv"); + + int i, j, columns = list.Columns.Count; + try + { + StreamWriter s = new StreamWriter(sf.FileName); + string[] formats = new string[columns]; + if(formatNicely) + { + // figure out widths of the columns + int[] widths = new int[columns]; + for(i = 0; i < columns; i++) + { + widths[i] = list.Columns[i].Text.Length; + } + for(i = 0; i < list.Items.Count; i++) + { + ListViewItem m = list.Items[i]; + for(j = 0; j < columns; j++) + { + int l = m.SubItems[j].Text.Length; + if(l > widths[j]) + { + widths[j] = l; + } + } + } + + // create formats + for(i = 0; i < columns; i++) + { + formats[i] = "{0," + (i == 0 ? "-" : "") + widths[i] + '}' + (i == columns - 1 ? '\n' : ' '); + } + } + else + { + for(i = 0; i < columns - 1; i++) + { + formats[i] = "{0},"; + } + formats[columns - 1] = "{0}\n"; + } + + for(i = 0; i < columns; i++) + { + s.Write(formats[i], list.Columns[i].Text); + } + if(formatNicely) + { + s.Write('\n'); + } + for(i = 0; i < list.Items.Count; i++) + { + ListViewItem m = list.Items[i]; + for(j = 0; j < columns; j++) + { + s.Write(formats[j], m.SubItems[j].Text); + } + } + s.Close(); + } + catch + { + MessageBox.Show(this, "Error saving table to a file named " + sf.FileName, "Failure"); + return; + } + } + } + } +} diff --git a/CLRProfiler/CLRProfiler/ListViewer.resx b/CLRProfiler/CLRProfiler/ListViewer.resx new file mode 100644 index 0000000..b582509 --- /dev/null +++ b/CLRProfiler/CLRProfiler/ListViewer.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/CLRProfiler/CLRProfiler/LogBase.cs b/CLRProfiler/CLRProfiler/LogBase.cs new file mode 100644 index 0000000..9434770 --- /dev/null +++ b/CLRProfiler/CLRProfiler/LogBase.cs @@ -0,0 +1,70 @@ +using System; +using System.IO; + +namespace CLRProfiler +{ + /// + /// Summary description for LogBase. + /// + public class LogBase + { + #region private data member + private long logFileStartOffset; + private long logFileEndOffset; + private string logFileName; + private ReadNewLog log = null; + #endregion + + #region public member + internal ReadLogResult logResult = null; + #endregion + + public LogBase() + { + logFileStartOffset = 0; + logFileEndOffset = long.MaxValue; + } + #region public property methods + public string LogFileName + { + get + { + return logFileName; + } + set + { + logFileName = value; + } + } + #endregion + #region public methods + public void readLogFile() + { + log = new ReadNewLog(logFileName); + logResult = GetLogResult(); + log.ReadFile(logFileStartOffset, logFileEndOffset, logResult); + + } + #endregion + + #region private methods + // from form1.cs + internal ReadLogResult GetLogResult() + { + logResult = new ReadLogResult(); + logResult.liveObjectTable = new LiveObjectTable(log); + logResult.sampleObjectTable = new SampleObjectTable(log); + logResult.allocatedHistogram = new Histogram(log); + logResult.callstackHistogram = new Histogram(log); + logResult.relocatedHistogram = new Histogram(log); + logResult.objectGraph = new ObjectGraph(log, 0); + logResult.functionList = new FunctionList(log); + logResult.hadCallInfo = logResult.hadAllocInfo = false; + + // We may just have turned a lot of data into garbage - let's try to reclaim the memory + GC.Collect(); + return logResult; + } + #endregion + } +} diff --git a/CLRProfiler/CLRProfiler/MainForm.cs b/CLRProfiler/CLRProfiler/MainForm.cs new file mode 100644 index 0000000..05580c7 --- /dev/null +++ b/CLRProfiler/CLRProfiler/MainForm.cs @@ -0,0 +1,3756 @@ +/* ==++== + * + * Copyright (c) Microsoft Corporation. All rights reserved. + * + * ==--== + * + * Class: Form1 + * + * Description: CLR Profiler interface and logic + */ +using System; +using System.Drawing; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Windows.Forms; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.IO; +using System.Threading; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.AccessControl; +using System.Security.Principal; +using System.Globalization; +using System.Xml; +using Microsoft.Win32.SafeHandles; + +namespace CLRProfiler +{ + + // IMPORTANT: ProfConfig structure has a counterpart native structure defined + // in ProfilerCallback.h. Both must always be in sync. + [Flags] + public enum OmvUsage : int + { + OmvUsageNone = 0, + OmvUsageObjects = 1, + OmvUsageTrace = 2, + OmvUsageBoth = 3 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct ProfConfig + { + public OmvUsage usage; + public int bOldFormat; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string szPath; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string szFileName; + public int bDynamic; + public int bStack; + public uint dwFramesToPrint; + public uint dwSkipObjects; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string szClassToMonitor; + public uint dwInitialSetting; + public uint dwDefaultTimeoutMs; + public bool bWindowsStoreApp; + } + + /// + /// Summary description for Form1. + /// + public class MainForm : System.Windows.Forms.Form + { + private class WindowsStoreAppProfileeInfo + { + public WindowsStoreAppProfileeInfo(string packageFullNameParam, string acSidString) + { + packageFullName = packageFullNameParam; + windowsStoreAppEventPrefix = string.Format("AppContainerNamedObjects\\{0}\\", acSidString); + } + + public string windowsStoreAppEventPrefix; + public string packageFullName; + } + + private System.Windows.Forms.MenuItem menuItem1; + private System.Windows.Forms.MenuItem menuItem3; + private System.Windows.Forms.MainMenu mainMenu; + private System.Windows.Forms.MenuItem exitMenuItem; + private System.ComponentModel.IContainer components; + + private System.Windows.Forms.OpenFileDialog openFileDialog; + private System.Windows.Forms.MenuItem menuItem5; + private System.Windows.Forms.MenuItem fontMenuItem; + private System.Windows.Forms.MenuItem logFileOpenMenuItem; + private System.Windows.Forms.MenuItem profileApplicationMenuItem; + private System.Windows.Forms.Button startApplicationButton; + private System.Windows.Forms.CheckBox profilingActiveCheckBox; + private System.Windows.Forms.Button killApplicationButton; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Timer checkProcessTimer; + private System.Windows.Forms.MenuItem setCommandLineMenuItem; + private System.Windows.Forms.FontDialog fontDialog; + + internal Font font; + private Process profiledProcess; + private string processFileName; + private string serviceName; + private string serviceAccountSid; + private string serviceStartCommand; + private string serviceStopCommand; + private string logFileName; + private long logFileStartOffset; + private long logFileEndOffset; + internal ReadNewLog log; + internal ReadLogResult lastLogResult; + private NamedManualResetEvent loggingActiveEvent; + private NamedManualResetEvent forceGcEvent; + private NamedManualResetEvent loggingActiveCompletedEvent; + private NamedManualResetEvent forceGcCompletedEvent; + private NamedManualResetEvent callGraphActiveEvent; + private NamedManualResetEvent callGraphActiveCompletedEvent; + private NamedManualResetEvent detachEvent; + private WindowsStoreAppProfileeInfo windowsStoreAppProfileeInfo; + private System.Windows.Forms.Button showHeapButton; + private string commandLine = ""; + private string workingDirectory = ""; + private string logDirectory; + private int attachTargetPID; + private SafeFileHandle handshakingPipeHandle; + private SafeFileHandle loggingPipeHandle; + private FileStream handshakingPipe; + private FileStream loggingPipe; + private System.Windows.Forms.MenuItem menuItem8; + private System.Windows.Forms.MenuItem viewTimeLineMenuItem; + private System.Windows.Forms.SaveFileDialog saveFileDialog; + internal static MainForm instance; + private System.Windows.Forms.MenuItem saveAsMenuItem; + private System.Windows.Forms.MenuItem viewHistogramAllocatedMenuItem; + private System.Windows.Forms.MenuItem viewHistogramRelocatedMenuItem; + private System.Windows.Forms.MenuItem viewHistogramFinalizerMenuItem; + private System.Windows.Forms.MenuItem viewHistogramCriticalFinalizerMenuItem; + private System.Windows.Forms.MenuItem viewHeapGraphMenuItem; + private System.Windows.Forms.MenuItem viewCallGraphMenuItem; + private System.Windows.Forms.MenuItem viewAllocationGraphMenuItem; + private System.Windows.Forms.MenuItem viewObjectsByAddressMenuItem; + private System.Windows.Forms.MenuItem viewHistogramByAgeMenuItem; + private System.Windows.Forms.MenuItem profileASP_NETmenuItem; + private System.Windows.Forms.MenuItem profileServiceMenuItem; + private System.Windows.Forms.MenuItem viewFunctionGraphMenuItem; + private System.Windows.Forms.MenuItem viewModuleGraphMenuItem; + private System.Windows.Forms.MenuItem viewClassGraphMenuItem; + private System.Windows.Forms.MenuItem viewCommentsMenuItem; + private System.Windows.Forms.CheckBox allocationsCheckBox; + private System.Windows.Forms.CheckBox callsCheckBox; + private System.Windows.Forms.MenuItem viewCallTreeMenuItem; + private System.Windows.Forms.MenuItem viewAssemblyGraphMenuItem; + private bool saveNever; + private bool gcOnLogFileComments; + + private enum CLRSKU + { + V4DesktopCLR, + V4CoreCLR, + V2DesktopCLR, + }; + + internal bool noUI = true; + private string nameToUse; + private bool profileAllocations, profileCalls, profilingActive; + private bool trackCallStacks = true; + private bool profilerConnected = false; + private CLRSKU targetCLRVersion = CLRSKU.V4DesktopCLR; + private string profilingURL = null; + + private bool help; + internal string prevlogFileName; + internal string currlogFileName; + internal string difflogFileName; + internal Graph.GraphType graphtype = Graph.GraphType.Invalid; + private System.Windows.Forms.MenuItem menuItem2; + private System.Windows.Forms.MenuItem viewComparisonMenuItem; + internal bool viewdiff = false; + internal bool exitProgram = false; + + private enum ReportKind + { + NoReport, + AllocationReport, + RelocationReport, + SurvivorReport, + SurvivorDifferenceReport, + HeapDumpReport, + LeakReport, + FinalizerReport, + CriticalFinalizerReport, + CommentReport, + }; + + private ReportKind reportKind; + private string startMarker = null; + private string endMarker = null; + private System.Windows.Forms.MenuItem viewSummaryMenuItem; + private Button attachProcessButton; + private GroupBox groupBox2; + private Label label2; + private Button detachProcessButton; + private ComboBox targetCLRVersioncomboBox; + private Button startURLButton; + private MenuItem startURLMenuItem; + private MenuItem attachProcessMenuItem; + private MenuItem detachProcessMenuItem; + private string[] timeMarker = new string[0]; + private Button startWindowsStoreAppButton; + private MenuItem menuStartWindowsStoreApp; + + private uint maxWaitingTimeInMiliseconds = 10000; + + internal bool IsProfilingWindowsStoreApp() + { + return (windowsStoreAppProfileeInfo != null); + } + + + internal void CommandLineError(string message) + { + if (!noUI) + { + MessageBox.Show(message, "Report Profiler"); + ShowUsage(); + } + else + { + Console.WriteLine(message); + } + } + + internal void CommandLineError(string message, params object[] args) + { + CommandLineError(string.Format(message, args)); + } + + internal string FetchArgument(string[] arguments, ref int i) + { + i++; + if (arguments.Length > i) + return arguments[i]; + else + { + CommandLineError("Option {0} expects an argument", arguments[i - 1]); + return ""; + } + } + + public MainForm(string[] arguments) + { + int i; + string logFileName = ""; + bool onlyCreateLog = false; + processFileName = null; + windowsStoreAppProfileeInfo = null; + + profileAllocations = profileCalls = profilingActive = true; + + //Check ProfilerOBJ.DLL exist first + if (!File.Exists( getProfilerFullPath() ) ) + { + ShowErrorMessage("Can not find '" + getProfilerFullPath() + "', please make sure ProfilerOBJ.dll exists at the same directory as CLRProfiler.exe."); + Environment.ExitCode = 1; + exitProgram = true; + return; + } + + #region arguments + if (arguments.Length > 0) + { + int pid; + + Environment.ExitCode = 0; + + switch (arguments[0]) + { + case "-attach": + case "/attach": + exitProgram = true; + if ((pid = getPID(arguments)) == 0) + { + Environment.ExitCode = 1; + return; + } + + for (int index = 2; index + 1 < arguments.Length; index = index+2) + { + if (arguments[index] == "-o" || arguments[index] == "/o") + { + string fullPath = Path.GetFullPath(arguments[index+1]); + logDirectory = Path.GetDirectoryName(fullPath); + nameToUse = Path.GetFileName(fullPath); + } + + if (arguments[index] == "-t" || arguments[index] == "/t") + { + maxWaitingTimeInMiliseconds = UInt32.Parse(arguments[index + 1]); + if (maxWaitingTimeInMiliseconds < 3000) + { + maxWaitingTimeInMiliseconds = 3000; + ShowErrorMessage("CLRProfiler must wait a minimum of 3000 milliseconds before it will time out while attaching to the application."); + } + } + } + + if(logDirectory == null) + { + logDirectory = Directory.GetCurrentDirectory(); + } + /* basic initialization stuff */ + CreatePipe(@"\\.\pipe\OMV_PIPE", false, ref handshakingPipeHandle, ref handshakingPipe); + CreatePipe(@"\\.\pipe\OMV_LOGGING_PIPE", false, ref loggingPipeHandle, ref loggingPipe); + instance = this; + + if (attachProfiler(pid, GetLogFullPath(pid)) ) + { + Console.WriteLine("The logging data is saved at " + GetLogFullPath(pid) + "."); + } + else + { + Environment.ExitCode = 1; + } + + ClearProfiledProcessInfo(); + return; + + case "-detach": + case "/detach": + exitProgram = true; + + if ((pid = getPID(arguments)) == 0) + { + Environment.ExitCode = 1; + return; + } + + if (!InitWindowsStoreAppProfileeInfoIfNecessary(pid)) + return; + + if (!isProfilerLoaded(pid)) + { + Console.WriteLine("PID argument is not valid."); + Environment.ExitCode = 1; + return; + } + + CreateEvents(pid); + + SendDetachRequest(); + + Console.WriteLine("Detach is sucessfully requested."); + Console.WriteLine("It may take a while for CLRProfiler to be unloaded completely. "); + Console.WriteLine("You may look for profiler detach event from the event log to determine if detach succeeds."); + + ClearProfiledProcessInfo(); + return; + + case "-dumpheap": + case "/dumpheap": + exitProgram = true; + + if ((pid = getPID(arguments)) == 0) + { + Environment.ExitCode = 1; + return; + } + + if (!InitWindowsStoreAppProfileeInfoIfNecessary(pid)) + return; + + if (!isProfilerLoaded(pid)) + { + Console.WriteLine("PID argument is not valid."); + Environment.ExitCode = 1; + return; + } + + CreateEvents(pid); + forceGcCompletedEvent.Wait(1); + forceGcCompletedEvent.Reset(); + forceGcEvent.Set(); + Console.WriteLine(Path.GetFileName(Application.ExecutablePath) + " is waiting up to 10 minutes for target process to finish dumping the GC heap."); + if (forceGcCompletedEvent.Wait(10 * 60 * 1000)) + { + forceGcCompletedEvent.Reset(); + Console.WriteLine("A heap dump is appended in the log file."); + } + else + { + Console.WriteLine("There was no response from the target process."); + Environment.ExitCode = 1; + } + ClearEvents(); + ClearProfiledProcessInfo(); + return; + } + } + #endregion arguments + #region arguments2 + for (i = 0; i < arguments.Length && !onlyCreateLog; i++) + { + switch (arguments[i]) + { + case "-target": + case "/target": + string version = FetchArgument(arguments, ref i); + + if (String.Compare(version, "V4DesktopCLR", true) == 0) + targetCLRVersion = CLRSKU.V4DesktopCLR; + else if (String.Compare(version, "V4CoreCLR", true) == 0) + targetCLRVersion = CLRSKU.V4CoreCLR; + else if (String.Compare(version, "V2DesktopCLR", true) == 0) + targetCLRVersion = CLRSKU.V2DesktopCLR; + else + { + CommandLineError("No matched target CLR version."); + help = true; + } + break; + + case "-u": + case "/u": + if (logFileName == null) + { + CommandLineError("A log filename needs to be specified with -o beofre -u argument"); + help = true; + } + else + { + profilingURL = FetchArgument(arguments, ref i); + if (profilingURL != null) + onlyCreateLog = true; + } + break; + + case "-na": + case "/na": + profileAllocations = false; + break; + + case "-nc": + case "/nc": + profileCalls = false; + break; + + case "-np": + case "/np": + profilingActive = false; + break; + + case "/ns": + case "-ns": + trackCallStacks = false; + break; + + case "-o": + case "/o": + logFileName = FetchArgument(arguments, ref i); + break; + + case "-p": + case "/p": + processFileName = FetchArgument(arguments, ref i); + if (processFileName != null) + onlyCreateLog = true; + break; + + case "-diff": + case "/diff": + graphtype = Graph.GraphType.AllocationGraph; + break; + + case "-lo": + case "/lo": + prevlogFileName = FetchArgument(arguments, ref i); + break; + + case "-ln": + case "/ln": + currlogFileName = FetchArgument(arguments, ref i); + break; + + case "-ld": + case "/ld": + difflogFileName = FetchArgument(arguments, ref i); + break; + + case "-l": + case "/l": + logFileName = FetchArgument(arguments, ref i); + break; + + case "-a": + case "/a": + reportKind = ReportKind.AllocationReport; + break; + + case "-r": + case "/r": + reportKind = ReportKind.RelocationReport; + break; + + case "-s": + case "/s": + reportKind = ReportKind.SurvivorReport; + break; + + case "-sd": + case "/sd": + reportKind = ReportKind.SurvivorDifferenceReport; + break; + + case "-h": + case "/h": + reportKind = ReportKind.HeapDumpReport; + break; + + case "-lr": + case "/lr": + reportKind = ReportKind.LeakReport; + break; + + case "-f": + case "/f": + reportKind = ReportKind.FinalizerReport; + break; + + case "-cf": + case "/cf": + reportKind = ReportKind.CriticalFinalizerReport; + break; + + case "-c": + case "/c": + reportKind = ReportKind.CommentReport; + break; + + case "-b": + case "/b": + startMarker = FetchArgument(arguments, ref i); + break; + + case "-e": + case "/e": + endMarker = FetchArgument(arguments, ref i); + break; + + case "-t": + case "/t": + do + { + string[] newTimeMarker = new String[timeMarker.Length + 1]; + for (int j = 0; j < timeMarker.Length; j++) + newTimeMarker[j] = timeMarker[j]; + newTimeMarker[timeMarker.Length] = FetchArgument(arguments, ref i); + timeMarker = newTimeMarker; + } + while (i + 1 < arguments.Length && arguments[i + 1][0] != '-' && arguments[i + 1][0] != '/'); + break; + + case "-w": + case "/w": + this.noUI = false; + break; + + case "-?": + case "/?": + case "-help": + case "/help": + help = true; + break; + + case "-gc": + case "/gc": + gcOnLogFileComments = true; + break; + + default: + if (arguments[i][0] == '-' || arguments[i][0] == '/') + CommandLineError("Unrecognized option {0}", arguments[i]); + else if (File.Exists(arguments[i])) + logFileName = arguments[i]; + else + CommandLineError("Log file {0} not found", arguments[i]); + break; + } + } + #endregion arguments2 + #region AllocationGraph + if (graphtype == Graph.GraphType.AllocationGraph) + { + if (File.Exists(prevlogFileName) && File.Exists(currlogFileName)) + { + viewdiff = true; + return; + } + else + { + String s = ""; + if (!File.Exists(prevlogFileName)) + { + s += String.Format("Previous build log File not exists or command line missing -lo. \n"); + } + if (!File.Exists(currlogFileName)) + { + s += String.Format("New build log file not exists or command line missing -ln \n"); + } + CommandLineError(s); + } + } + #endregion AllocationGraph + #region NoReport + else if (reportKind != ReportKind.NoReport) + { + if (logFileName == "") + CommandLineError("Need -l logFileName for report"); + else if (!File.Exists(logFileName)) + CommandLineError("Log file {0} not found", logFileName); + else + { + switch (reportKind) + { + case ReportKind.AllocationReport: + Reports.AllocationReport(logFileName, startMarker, endMarker); + break; + + case ReportKind.RelocationReport: + Reports.RelocationReport(logFileName, startMarker, endMarker); + break; + + case ReportKind.SurvivorReport: + Reports.SurvivorReport(logFileName, startMarker, endMarker, timeMarker); + break; + + case ReportKind.SurvivorDifferenceReport: + Reports.SurvivorDifferenceReport(logFileName, startMarker, endMarker); + break; + + case ReportKind.HeapDumpReport: + Reports.HeapDumpReport(logFileName, startMarker, endMarker); + break; + + case ReportKind.LeakReport: + Reports.LeakReport(logFileName, startMarker, endMarker); + break; + + case ReportKind.FinalizerReport: + Reports.FinalizerReport(false, logFileName, startMarker, endMarker); + break; + + case ReportKind.CriticalFinalizerReport: + Reports.FinalizerReport(true, logFileName, startMarker, endMarker); + break; + + case ReportKind.CommentReport: + Reports.CommentReport(logFileName); + break; + } + } + } + #endregion NoReport + else + { + if (onlyCreateLog) + { + /* treat everything after the exe name to profile as arguments */ + for (; i < arguments.Length; i++) + { + commandLine += arguments[i] + ' '; + } + } + /* basic initialization stuff */ + CreatePipe(@"\\.\pipe\OMV_PIPE", false, ref handshakingPipeHandle, ref handshakingPipe); + CreatePipe(@"\\.\pipe\OMV_LOGGING_PIPE", false, ref loggingPipeHandle, ref loggingPipe); + instance = this; + + if (!onlyCreateLog) + { + /* standard UI operation */ + if (help) + { + ShowUsage(); + exitProgram = true; + return; + } + noUI = false; + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // Set V4 Desktop CLR to be the default value + targetCLRVersioncomboBox.SelectedIndex = 0; + + font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(204))); ; + + if (logFileName != "" && File.Exists(logFileName)) + LoadLogFile(logFileName); + + EnableDisableViewMenuItems(); + } + else + { + /* command-line only */ + noUI = true; + exitProgram = true; + + try + { + string fileName = (logFileName == "" ? Path.ChangeExtension(processFileName, ".log") : logFileName); + string fullPath = Path.GetFullPath(fileName); + logDirectory = Path.GetDirectoryName(fullPath); + nameToUse = Path.GetFileName(fileName); + + if (profilingURL == null) + { + startApplicationButton_Click(null, null); + } + else + { + startURLButton_Click(null, null); + } + + if (profilerConnected) + { + Environment.ExitCode = 0; + } + else + { + Environment.ExitCode = 1; + } + + while (profiledProcess != null && !ProfiledProcessHasExited()) + Thread.Sleep(500); + } + catch (Exception e) + { + throw new Exception(e.Message + '\n' + e.StackTrace); + // Console.WriteLine("There was a problem profiling {0}", processFileName); + } + } + } + } + + private void ShowUsage() + { + graphtype = Graph.GraphType.Invalid; + String s = ""; + s += String.Format("Usage: {0} -attach PID [-o logName] [-t timeoutInMiliseconds]\r\n", Path.GetFileName(Application.ExecutablePath)); + s += " -attach PID \t- attaches to the target process\r\n"; + s += " -o logName \t- file to write the resulting profile log to\r\n"; + s += " -t timeout \t- max time in miliseconds to wait for the attach to succeed\r\n"; + s += "or\r\n"; + s += String.Format("{0} -detach PID\r\n", Path.GetFileName(Application.ExecutablePath)); + s += " -detach PID \t- detaches from the target process (use -attach first)\r\n"; + s += "or\r\n"; + s += String.Format("{0} -dumpheap PID\r\n", Path.GetFileName(Application.ExecutablePath)); + s += " -dumpheap PID \t- triggers a GC and dumps the GC heap in the target process (use -attach first)\r\n"; + s += "or\r\n"; + s += String.Format("{0} -o logName [-na] [-nc] -[np] [-target V4DesktopCLR|V4CoreCLR|V2DesktopCLR] -u URL\r\n", Path.GetFileName(Application.ExecutablePath)); + s += " -u URL\t- URL to profile (must go at the end)\r\n"; + s += " -o logName\t- file to write the resulting profile log to\r\n"; + s += " -na\t\t- don't record allocations\r\n"; + s += " -nc\t\t- don't record calls\r\n"; + s += " -np\t\t- start with profiling active off\r\n"; + s += " -target V4DesktopCLR|V4CoreCLR|V2DesktopCLR\t- specify the target CLR runtime to be profiled (V4DesktopCLR is the default choice)\r\n"; + s += "or\r\n"; + s += String.Format("{0} [-o logName] [-na] [-nc] -[np] [-target V4DesktopCLR|V4CoreCLR|V2DesktopCLR] -p exeName [args]\r\n", Path.GetFileName(Application.ExecutablePath)); + s += " -p exeName args\t- application to profile and its arguments (must go at the end)\r\n"; + s += " -o logName\t- file to write the resulting profile log to\r\n"; + s += " -na\t\t- don't record allocations\r\n"; + s += " -nc\t\t- don't record calls\r\n"; + s += " -np\t\t- start with profiling active off\r\n"; + s += " -target V4DesktopCLR|V4CoreCLR|V2DesktopCLR\t- specify the target CLR runtime to be profiled (V4DesktopCLR is the default choice)\r\n"; + s += "or\r\n"; + s += String.Format("{0} -a -l logName [-b ] [-e ]\r\n", Path.GetFileName(Application.ExecutablePath)); + s += " -a \t - asks for an allocation report\r\n"; + s += " -l logName \t - names the input log file\r\n"; + s += " -b \t - only report allocations after this log file comment\r\n"; + s += " -e \t - only report allocations before this log file comment\r\n"; + s += "or\r\n"; + s += String.Format("{0} -r -l logName [-b ] [-e ]\r\n", Path.GetFileName(Application.ExecutablePath)); + s += " -r \t - asks for a relocation report\r\n"; + s += " -l logName \t - names the input log file\r\n"; + s += " -b \t - only report relocations after this log file comment\r\n"; + s += " -e \t - only report relocations before this log file comment\r\n"; + s += "or\r\n"; + s += String.Format("{0} -s -l logName [-b ] [-e ] [-t