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