diff options
author | Massimiliano Mantione <massi@mono-cvs.ximian.com> | 2008-08-11 13:39:46 +0400 |
---|---|---|
committer | Massimiliano Mantione <massi@mono-cvs.ximian.com> | 2008-08-11 13:39:46 +0400 |
commit | 66683eee2ec109a7389f2c1c184d48906080a9e7 (patch) | |
tree | 96ec4a889c97a9f94e80cea743da43f50ccc3bd2 | |
parent | 9c7daef20643c099f9ba9709e9613313e2f44d35 (diff) |
Moving the Mono.Profiler module inside mono-tools.
svn path=/trunk/mono-tools/; revision=110097
56 files changed, 7062 insertions, 0 deletions
diff --git a/Mono.Profiler/.gitignore b/Mono.Profiler/.gitignore new file mode 100644 index 00000000..dcd7f071 --- /dev/null +++ b/Mono.Profiler/.gitignore @@ -0,0 +1,33 @@ +Makefile +configure +profiler-decoder-library/Makefile +profiler-decoder-library/profiler-decoder-library.pc +profiler-file-decoder/Makefile +Makefile.in +Mono.Profiler.userprefs +Mono.Profiler.usertasks +aclocal.m4 +autom4te.cache/* +config.log +config.make +config.status +expansions.m4 +heap-snapshot-explorer/Makefile +heap-snapshot-explorer/Makefile.in +heap-snapshot-explorer/bin/* +heap-snapshot-explorer/heap-snapshot-explorer.pc +heap-snapshot-viewer/Makefile +heap-snapshot-viewer/Makefile.in +heap-snapshot-viewer/bin/* +heap-snapshot-viewer/heap-snapshot-viewer +install-sh +missing +profiler-decoder-library/Makefile.in +profiler-decoder-library/bin/* +profiler-decoder.exe +profiler-decoder.exe.mdb +profiler-file-decoder/Makefile.in +profiler-file-decoder/bin/* +heap-snapshot-viewer/mprof-heap-viewer +profiler-file-decoder/mprof-decoder +*.pidb diff --git a/Mono.Profiler/Makefile.am b/Mono.Profiler/Makefile.am new file mode 100644 index 00000000..681437ec --- /dev/null +++ b/Mono.Profiler/Makefile.am @@ -0,0 +1,10 @@ + +EXTRA_DIST = expansions.m4 + +#Warning: This is an automatically generated file, do not edit! +if ENABLE_DEBUG + SUBDIRS = profiler-decoder-library profiler-file-decoder heap-snapshot-explorer heap-snapshot-viewer +endif +if ENABLE_RELEASE + SUBDIRS = profiler-decoder-library profiler-file-decoder heap-snapshot-explorer heap-snapshot-viewer +endif diff --git a/Mono.Profiler/Makefile.include b/Mono.Profiler/Makefile.include new file mode 100644 index 00000000..37112158 --- /dev/null +++ b/Mono.Profiler/Makefile.include @@ -0,0 +1,110 @@ +VALID_CULTURES = ar bg ca zh-CHS cs da de el en es fi fr he hu is it ja ko nl no pl pt ro ru hr sk sq sv th tr id uk be sl et lv lt fa vi hy eu mk af fo hi sw gu ta te kn mr gl kok ar-SA bg-BG ca-ES zh-TW cs-CZ da-DK de-DE el-GR en-US fi-FI fr-FR he-IL hu-HU is-IS it-IT ja-JP ko-KR nl-NL nb-NO pl-PL pt-BR ro-RO ru-RU hr-HR sk-SK sq-AL sv-SE th-TH tr-TR id-ID uk-UA be-BY sl-SI et-EE lv-LV lt-LT fa-IR vi-VN hy-AM eu-ES mk-MK af-ZA fo-FO hi-IN sw-KE gu-IN ta-IN te-IN kn-IN mr-IN gl-ES kok-IN ar-IQ zh-CN de-CH en-GB es-MX fr-BE it-CH nl-BE nn-NO pt-PT sv-FI ar-EG zh-HK de-AT en-AU es-ES fr-CA ar-LY zh-SG de-LU en-CA es-GT fr-CH ar-DZ zh-MO en-NZ es-CR fr-LU ar-MA en-IE es-PA ar-TN en-ZA es-DO ar-OM es-VE ar-YE es-CO ar-SY es-PE ar-JO es-AR ar-LB en-ZW es-EC ar-KW en-PH es-CL ar-AE es-UY ar-BH es-PY ar-QA es-BO es-SV es-HN es-NI es-PR zh-CHT + +s2q=$(subst \ ,?,$1) +q2s=$(subst ?,\ ,$1) +# use this when result will be quoted +unesc2=$(subst ?, ,$1) + +build_sources = $(FILES) $(GENERATED_FILES) +build_sources_esc= $(call s2q,$(build_sources)) +# use unesc2, as build_sources_embed is quoted +build_sources_embed= $(call unesc2,$(build_sources_esc:%='$(srcdir)/%')) + +comma__=, +get_resource_name = $(firstword $(subst $(comma__), ,$1)) +get_culture = $(lastword $(subst ., ,$(basename $1))) +is_cultured_resource = $(and $(word 3,$(subst ., ,$1)), $(filter $(VALID_CULTURES),$(lastword $(subst ., ,$(basename $1))))) + +RESOURCES_ESC=$(call s2q,$(RESOURCES)) + +build_resx_list = $(foreach res, $(RESOURCES_ESC), $(if $(filter %.resx, $(call get_resource_name,$(res))),$(res),)) +build_non_culture_resx_list = $(foreach res, $(build_resx_list),$(if $(call is_cultured_resource,$(call get_resource_name,$(res))),,$(res))) +build_non_culture_others_list = $(foreach res, $(filter-out $(build_resx_list),$(RESOURCES_ESC)),$(if $(call is_cultured_resource,$(call get_resource_name,$(res))),,$(res))) +build_others_list = $(build_non_culture_others_list) +build_xamlg_list = $(filter %.xaml.g.cs, $(FILES)) + +# resgen all .resx resources +build_resx_files = $(foreach res, $(build_resx_list), $(call get_resource_name,$(res))) +build_resx_resources_esc = $(build_resx_files:.resx=.resources) +build_resx_resources = $(call q2s,$(build_resx_resources_esc)) + +# embed resources for the main assembly +build_resx_resources_hack = $(subst .resx,.resources, $(build_non_culture_resx_list)) +# use unesc2, as build_resx_resources_embed is quoted +build_resx_resources_embed = $(call unesc2,$(build_resx_resources_hack:%='-resource:%')) +build_others_files = $(call q2s,$(foreach res, $(build_others_list),$(call get_resource_name,$(res)))) +build_others_resources = $(build_others_files) +# use unesc2, as build_others_resources_embed is quoted +build_others_resources_embed = $(call unesc2,$(build_others_list:%='-resource:$(srcdir)/%')) + +build_resources = $(build_resx_resources) $(build_others_resources) +build_resources_embed = $(build_resx_resources_embed) $(build_others_resources_embed) + +# -usesourcepath is available only for resgen2 +emit_resgen_target_1=$(call q2s,$1) : $(call q2s,$(subst .resources,.resx,$1)); cd '$$(shell dirname '$$<')' && $$(RESGEN) '$$(shell basename '$$<')' '$$(shell basename '$$@')' +emit_resgen_target_2=$(call q2s,$1) : $(call q2s,$(subst .resources,.resx,$1)); $$(RESGEN) -usesourcepath '$$<' '$$@' + +emit_resgen_target=$(if $(filter resgen2,$(RESGEN)),$(emit_resgen_target_2),$(emit_resgen_target_1)) +emit_resgen_targets=$(foreach res,$(build_resx_resources_esc),$(eval $(call emit_resgen_target,$(res)))) + +build_references_ref = $(call q2s,$(foreach ref, $(call s2q,$(REFERENCES)), $(if $(filter -pkg:%, $(ref)), $(ref), $(if $(filter -r:%, $(ref)), $(ref), -r:$(ref))))) +build_references_ref += $(call q2s,$(foreach ref, $(call s2q,$(DLL_REFERENCES)), -r:$(ref))) +build_references_ref += $(call q2s,$(foreach ref, $(call s2q,$(PROJECT_REFERENCES)), -r:$(ref))) + +s2q2s=$(call unesc2,$(call s2q,$1)) +cp_actual=test -z $1 || cp $1 $2 +cp=$(call cp_actual,'$(call s2q2s,$1)','$(call s2q2s,$2)') + +rm_actual=test -z '$1' || rm -f '$2' +rm=$(call rm_actual,$(call s2q2s,$1),$(call s2q2s,$2)/$(shell basename '$(call s2q2s,$1)')) + +EXTRA_DIST += $(build_sources) $(build_resx_files) $(build_others_files) $(ASSEMBLY_WRAPPER_IN) $(EXTRAS) $(DATA_FILES) $(build_culture_res_files) +CLEANFILES += $(ASSEMBLY) $(ASSEMBLY).mdb $(BINARIES) $(build_resx_resources) $(build_satellite_assembly_list) +DISTCLEANFILES = $(GENERATED_FILES) $(pc_files) $(BUILD_DIR)/* + +pkglib_SCRIPTS = $(ASSEMBLY) +bin_SCRIPTS = $(BINARIES) + +linuxdesktopapplicationsdir = @datadir@/applications +linuxdesktopapplications_DATA = $(LINUX_DESKTOPAPPLICATIONS) +programfilesdir = @libdir@/@PACKAGE@ +programfiles_DATA = $(PROGRAMFILES) +commonapplicationdatarootmanman1dir = @datadir@/man/man1 +commonapplicationdatarootmanman1_DATA = $(COMMONAPPLICATIONDATAROOT_MAN_MAN1) + + +# macros + +# $(call emit-deploy-target,deploy-variable-name) +define emit-deploy-target +$($1): $($1_SOURCE) + mkdir -p '$$(shell dirname '$$@')' + cp '$$<' '$$@' +endef + +# $(call emit-deploy-wrapper,wrapper-variable-name,wrapper-sourcefile,x) +# assumes that for a wrapper foo.pc its source template is foo.pc.in +# if $3 is non-empty then wrapper is marked exec +define emit-deploy-wrapper +$($1): $2 + mkdir -p '$$(shell dirname '$$@')' + cp '$$<' '$$@' + $(if $3,chmod +x '$$@') + +endef + +# generating satellite assemblies + +culture_resources = $(foreach res, $(RESOURCES_ESC), $(if $(call is_cultured_resource,$(call get_resource_name, $(res))),$(res))) +cultures = $(sort $(foreach res, $(culture_resources), $(call get_culture,$(call get_resource_name,$(res))))) +culture_resource_dependencies = $(call q2s,$(BUILD_DIR)/$1/$(SATELLITE_ASSEMBLY_NAME): $(subst .resx,.resources,$2)) +culture_resource_commandlines = $(call unesc2,cmd_line_satellite_$1 += '/embed:$(subst .resx,.resources,$2)') +build_satellite_assembly_list = $(call q2s,$(cultures:%=$(BUILD_DIR)/%/$(SATELLITE_ASSEMBLY_NAME))) +build_culture_res_files = $(call q2s,$(foreach res, $(culture_resources),$(call get_resource_name,$(res)))) + +$(eval $(foreach res, $(culture_resources), $(eval $(call culture_resource_dependencies,$(call get_culture,$(call get_resource_name,$(res))),$(call get_resource_name,$(res)))))) +$(eval $(foreach res, $(culture_resources), $(eval $(call culture_resource_commandlines,$(call get_culture,$(call get_resource_name,$(res))),$(res))))) + +$(build_satellite_assembly_list): $(BUILD_DIR)/%/$(SATELLITE_ASSEMBLY_NAME): + mkdir -p '$(@D)' + $(AL) -out:'$@' -culture:$* -t:lib $(cmd_line_satellite_$*)
\ No newline at end of file diff --git a/Mono.Profiler/Mono.Profiler.mds b/Mono.Profiler/Mono.Profiler.mds new file mode 100644 index 00000000..217748fc --- /dev/null +++ b/Mono.Profiler/Mono.Profiler.mds @@ -0,0 +1,29 @@ +<Combine name="Mono.Profiler" fileversion="2.0" outputpath="build/bin/"> + <Configurations active="Debug"> + <Configuration name="Debug" ctype="CombineConfiguration"> + <Entry build="True" name="mprof-decoder-library" configuration="Debug" /> + <Entry build="True" name="mprof-decoder" configuration="Debug" /> + <Entry build="True" name="mprof-heap-viewer" configuration="Debug" /> + <Entry build="True" name="mprof-heap-explorer" configuration="Debug" /> + </Configuration> + <Configuration name="Release" ctype="CombineConfiguration"> + <Entry build="True" name="mprof-decoder-library" configuration="Release" /> + <Entry build="True" name="mprof-decoder" configuration="Release" /> + <Entry build="True" name="mprof-heap-viewer" configuration="Release" /> + <Entry build="True" name="mprof-heap-explorer" configuration="Release" /> + </Configuration> + </Configurations> + <StartMode startupentry="mprof-decoder" single="True"> + <Execute type="None" entry="mprof-decoder-library" /> + <Execute type="None" entry="mprof-decoder" /> + <Execute type="None" entry="mprof-heap-viewer" /> + <Execute type="None" entry="mprof-heap-explorer" /> + </StartMode> + <MonoDevelop.ChangeLogAddIn.ChangeLogInfo policy="UpdateNearestChangeLog" /> + <Entries> + <Entry filename="profiler-decoder-library/profiler-decoder-library.mdp" /> + <Entry filename="profiler-file-decoder/profiler-file-decoder.mdp" /> + <Entry filename="heap-snapshot-viewer/heap-snapshot-viewer.mdp" /> + <Entry filename="heap-snapshot-explorer/heap-snapshot-explorer.mdp" /> + </Entries> +</Combine>
\ No newline at end of file diff --git a/Mono.Profiler/autogen.sh b/Mono.Profiler/autogen.sh new file mode 100755 index 00000000..b94eae29 --- /dev/null +++ b/Mono.Profiler/autogen.sh @@ -0,0 +1,83 @@ +#! /bin/sh + +PROJECT=Mono.Profiler +FILE= +CONFIGURE=configure.ac + +: ${AUTOCONF=autoconf} +: ${AUTOHEADER=autoheader} +: ${AUTOMAKE=automake} +: ${LIBTOOLIZE=libtoolize} +: ${ACLOCAL=aclocal} +: ${LIBTOOL=libtool} + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +ORIGDIR=`pwd` +cd $srcdir +TEST_TYPE=-f +aclocalinclude="-I . $ACLOCAL_FLAGS" + +DIE=0 + +($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have autoconf installed to compile $PROJECT." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have automake installed to compile $PROJECT." + echo "Get ftp://sourceware.cygnus.com/pub/automake/automake-1.4.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +(grep "^AM_PROG_LIBTOOL" $CONFIGURE >/dev/null) && { + ($LIBTOOL --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`libtool' installed to compile $PROJECT." + echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2d.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +#test $TEST_TYPE $FILE || { +# echo "You must run this script in the top-level $PROJECT directory" +# exit 1 +#} + +if test -z "$*"; then + echo "I am going to run ./configure with no arguments - if you wish " + echo "to pass any to it, please specify them on the $0 command line." +fi + +case $CC in +*xlc | *xlc\ * | *lcc | *lcc\ *) am_opt=--include-deps;; +esac + +(grep "^AM_PROG_LIBTOOL" $CONFIGURE >/dev/null) && { + echo "Running $LIBTOOLIZE ..." + $LIBTOOLIZE --force --copy +} + +echo "Running $ACLOCAL $aclocalinclude ..." +$ACLOCAL $aclocalinclude + +echo "Running $AUTOMAKE --gnu $am_opt ..." +$AUTOMAKE --add-missing --gnu $am_opt + +echo "Running $AUTOCONF ..." +$AUTOCONF + +echo Running $srcdir/configure $conf_flags "$@" ... +$srcdir/configure --enable-maintainer-mode $conf_flags "$@" \ diff --git a/Mono.Profiler/compile b/Mono.Profiler/compile new file mode 100755 index 00000000..49f56154 --- /dev/null +++ b/Mono.Profiler/compile @@ -0,0 +1 @@ +gmcs -debug -out:profiler-decoder.exe profiler-decoder-library/*.cs profiler-file-decoder/*.cs diff --git a/Mono.Profiler/configure.ac b/Mono.Profiler/configure.ac new file mode 100644 index 00000000..d44ae0b6 --- /dev/null +++ b/Mono.Profiler/configure.ac @@ -0,0 +1,66 @@ +dnl Warning: This is an automatically generated file, do not edit! +dnl Process this file with autoconf to produce a configure script. +AC_PREREQ([2.54]) +AC_INIT([Mono.Profiler], [0.1]) +AM_INIT_AUTOMAKE([foreign]) + +dnl pkg-config +AC_PATH_PROG(PKG_CONFIG, pkg-config, no) +if test "x$PKG_CONFIG" = "xno"; then + AC_MSG_ERROR([You need to install pkg-config]) +fi + +SHAMROCK_EXPAND_LIBDIR +SHAMROCK_EXPAND_BINDIR +SHAMROCK_EXPAND_DATADIR + +AC_PROG_INSTALL + +AC_PATH_PROG(GMCS, gmcs, no) +if test "x$GMCS" = "xno"; then + AC_MSG_ERROR([gmcs Not found]) +fi + + +AC_ARG_ENABLE(debug, + AC_HELP_STRING([--enable-debug], + [Use 'DEBUG' Configuration [default=YES]]), + enable_debug=yes, enable_debug=no) +AM_CONDITIONAL(ENABLE_DEBUG, test x$enable_debug = xyes) +if test "x$enable_debug" = "xyes" ; then + CONFIG_REQUESTED="yes" +fi +AC_ARG_ENABLE(release, + AC_HELP_STRING([--enable-release], + [Use 'RELEASE' Configuration [default=NO]]), + enable_release=yes, enable_release=no) +AM_CONDITIONAL(ENABLE_RELEASE, test x$enable_release = xyes) +if test "x$enable_release" = "xyes" ; then + CONFIG_REQUESTED="yes" +fi +if test -z "$CONFIG_REQUESTED" ; then + AM_CONDITIONAL(ENABLE_DEBUG, true) + enable_debug=yes +fi + + +dnl package checks, common for all configs +PKG_CHECK_MODULES([GLADE_SHARP_20], [glade-sharp-2.0]) +PKG_CHECK_MODULES([GLIB_SHARP_20], [glib-sharp-2.0]) +PKG_CHECK_MODULES([GTK_SHARP_20], [gtk-sharp-2.0]) + +dnl package checks, per config + + +AC_CONFIG_FILES([ +profiler-file-decoder/Makefile +heap-snapshot-viewer/mprof-heap-viewer +Makefile +heap-snapshot-viewer/Makefile +profiler-decoder-library/Makefile +profiler-file-decoder/mprof-decoder +heap-snapshot-explorer/Makefile + +]) + +AC_OUTPUT diff --git a/Mono.Profiler/heap-snapshot-explorer/AssemblyInfo.cs b/Mono.Profiler/heap-snapshot-explorer/AssemblyInfo.cs new file mode 100644 index 00000000..2a38fb60 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/AssemblyInfo.cs @@ -0,0 +1,30 @@ +// AssemblyInfo.cs created with MonoDevelop +// User: massi at 11:36 AM 4/17/2008 +// +// To change standard headers go to Edit->Preferences->Coding->Standard Headers +// +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("heap-snapshot-explorer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// If the build and revision are set to '*' they will be updated automatically. + +[assembly: AssemblyVersion("1.0.*.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] diff --git a/Mono.Profiler/heap-snapshot-explorer/ChangeLog b/Mono.Profiler/heap-snapshot-explorer/ChangeLog new file mode 100644 index 00000000..450a8d4c --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/ChangeLog @@ -0,0 +1,6 @@ +2008-08-04 Massimiliano Mantione <massi@ximian.com> + Removed .pc file generation. + +2008-08-01 Massimiliano Mantione <massi@ximian.com> + * Makefile.am, gtk-gui/gui.stetic heap-snapshot-explorer.mdp: renamed + output assembly. diff --git a/Mono.Profiler/heap-snapshot-explorer/HeapExplorerActions.cs b/Mono.Profiler/heap-snapshot-explorer/HeapExplorerActions.cs new file mode 100644 index 00000000..e75fbe9e --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/HeapExplorerActions.cs @@ -0,0 +1,34 @@ +// HeapExplorerActions.cs created with MonoDevelop +// User: massi at 11:40 AM 4/17/2008 +// +// To change standard headers go to Edit->Preferences->Coding->Standard Headers +// + +using System; + +namespace Mono.Profiler +{ + public partial class HeapExplorerActions : Gtk.ActionGroup + { + HeapSnapshotExplorer explorer; + public HeapSnapshotExplorer Explorer { + get { + return explorer; + } + set { + explorer = value; + } + } + + public HeapExplorerActions() : + base("Mono.Profiler.HeapExplorerActions") + { + this.Build(); + } + + protected virtual void OnLoadData (object sender, System.EventArgs e) + { + explorer.OnLoadData (); + } + } +} diff --git a/Mono.Profiler/heap-snapshot-explorer/HeapExplorerTreeModel.cs b/Mono.Profiler/heap-snapshot-explorer/HeapExplorerTreeModel.cs new file mode 100644 index 00000000..512ce0ed --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/HeapExplorerTreeModel.cs @@ -0,0 +1,241 @@ +// Author: +// Massimiliano Mantione (massi@ximian.com) +// +// (C) 2008 Novell, Inc http://www.novell.com +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using Gtk; + +namespace Mono.Profiler +{ + public class HeapExplorerTreeModel { + public abstract class Node { + public abstract HeapObjectSet Objects { + get; + } + public abstract string Description { + get; + } + + protected HeapExplorerTreeModel model; + TreeIter treeIter; + public TreeIter TreeIter { + get { + return treeIter; + } + } + + Node parent; + public Node Parent { + get { + return parent; + } + } + + public Node Root { + get { + if (parent != null) { + return parent.Root; + } else { + return this; + } + } + } + + public string Count { + get { + if (Objects != null) { + return Objects.HeapObjects.Length.ToString (); + } else { + return ""; + } + } + } + public string AllocatedBytes { + get { + if (Objects != null) { + return Objects.AllocatedBytes.ToString (); + } else { + return ""; + } + } + } + + protected TreeIter HandleNodeCreation () { + if (parent != null) { + TreeIter result = model.Model.AppendNode (parent.TreeIter); + model.Model.SetValue (result, 0, this); + return result; + } else { + return model.Model.AppendValues (this); + } + } + + public SubSetNode Filter (IHeapObjectFilter filter) { + HeapObjectSetFromFilter subSet = new HeapObjectSetFromFilter (Objects, filter); + SubSetNode result = new SubSetNode (model, this, subSet); + return result; + } + + public static void PerformComparison (Node firstNode, Node secondNode, out SubSetNode onlyInFirstNode, out SubSetNode onlyInSecondNode) { + HeapObjectSet onlyInFirstSet; + HeapObjectSet onlyInSecondSet; + HeapObjectSetFromComparison.PerformComparison (firstNode.Objects, secondNode.Objects, out onlyInFirstSet, out onlyInSecondSet); + onlyInFirstNode = new SubSetNode (firstNode.model, firstNode, onlyInFirstSet); + onlyInSecondNode = new SubSetNode (secondNode.model, secondNode, onlyInSecondSet); + } + + protected Node (HeapExplorerTreeModel model, Node parent) { + this.model = model; + this.parent = parent; + this.treeIter = HandleNodeCreation (); + } + } + + public class SnapshotNode : Node { + SeekableLogFileReader.Block heapBlock; + + HeapSnapshot snapshot; + public HeapSnapshot Snapshot { + get { + return snapshot; + } + } + + HeapObjectSetFromSnapshot objects; + public override HeapObjectSet Objects { + get { + return objects; + } + } + + public void ReadSnapshot () { + model.Reader.ReadBlock (heapBlock).Decode (model.heapEventProcessor, model.Reader); + snapshot = model.heapEventProcessor.LastHeapSnapshot; + objects = new HeapObjectSetFromSnapshot (snapshot); + model.Model.SetValue (TreeIter, 0, this); + } + + public override string Description { + get { + return heapBlock.TimeFromStart.ToString (); + } + } + + public SnapshotNode (HeapExplorerTreeModel model, SeekableLogFileReader.Block heapBlock) : base (model, null) { + this.heapBlock = heapBlock; + this.objects = null; + this.snapshot = null; + } + } + + public class SubSetNode : Node { + HeapObjectSet objects; + public override HeapObjectSet Objects { + get { + return objects; + } + } + + public override string Description { + get { + return objects.ShortDescription; + } + } + + public SubSetNode (HeapExplorerTreeModel model, Node parent, HeapObjectSet objects) : base (model, parent) { + this.objects = objects; + } + } + + TreeStore model; + public TreeStore Model { + get { + return model; + } + } + + SeekableLogFileReader reader; + public SeekableLogFileReader Reader { + get { + return reader; + } + } + + protected class HeapEventProcessor : ProfilerEventHandler { + HeapSnapshot lastHeapSnapshot = null; + public HeapSnapshot LastHeapSnapshot { + get { + return lastHeapSnapshot; + } + } + + public override void HeapReportStart (HeapSnapshot snapshot) { + lastHeapSnapshot = snapshot; + } + public override void HeapObjectUnreachable (LoadedClass c, uint size) { + lastHeapSnapshot.HeapObjectUnreachable (c, size); + } + public override void HeapObjectReachable (HeapObject<LoadedClass> o) { + } + public override void HeapReportEnd (HeapSnapshot snapshot) { + lastHeapSnapshot.InitializeBackReferences (); + } + } + protected HeapEventProcessor heapEventProcessor; + + List<SnapshotNode> rootNodes; + public SnapshotNode[] RootNodes { + get { + return rootNodes.ToArray (); + } + } + + public void Initialize () { + Reset (); + + foreach (SeekableLogFileReader.Block block in reader.Blocks) { + if (block.Code == BlockCode.HEAP_DATA) { + SnapshotNode node = new SnapshotNode (this, block); + rootNodes.Add (node); + } else if (block.Code == BlockCode.MAPPING) { + reader.ReadBlock (block).Decode (heapEventProcessor, reader); + } + } + } + + public void Reset () { + model.Clear (); + } + + public HeapExplorerTreeModel (SeekableLogFileReader reader) { + model = new TreeStore (new Type [] {typeof (Node)}); + heapEventProcessor = new HeapEventProcessor (); + this.reader = reader; + rootNodes = new List<SnapshotNode> (); + } + } +} diff --git a/Mono.Profiler/heap-snapshot-explorer/HeapSnapshotExplorer.cs b/Mono.Profiler/heap-snapshot-explorer/HeapSnapshotExplorer.cs new file mode 100644 index 00000000..391cfb77 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/HeapSnapshotExplorer.cs @@ -0,0 +1,282 @@ +// HeapSnapshotExplorer.cs created with MonoDevelop +// User: massi at 11:38 AM 4/17/2008 +// +// To change standard headers go to Edit->Preferences->Coding->Standard Headers +// + +using System; +using Gtk; + +namespace Mono.Profiler +{ + public partial class HeapSnapshotExplorer : Gtk.Bin + { + HeapExplorerTreeModel model; + public HeapExplorerTreeModel Model { + get { + return model; + } + set { + model = value; + Tree.Model = model.Model; + } + } + + Menu LoadBlock; + Menu FilterSet; + Menu CompareSet; + + HeapExplorerTreeModel.Node currentSelection; + public HeapExplorerTreeModel.Node CurrentSelection { + get { + return currentSelection; + } + } + + [Gtk.TreeNode (ListOnly=true)] + public class ClassStatisticsNode : Gtk.TreeNode { + HeapObjectSet.HeapObjectSetClassStatistics classStatistics; + public HeapObjectSet.HeapObjectSetClassStatistics ClassStatistics { + get { + return classStatistics; + } + } + + public string Name { + get { + return classStatistics.Class.Name; + } + } + public uint AllocatedBytes { + get { + return classStatistics.AllocatedBytes; + } + } + + public ClassStatisticsNode (HeapObjectSet.HeapObjectSetClassStatistics classStatistics) { + this.classStatistics = classStatistics; + } + } + + public static void PrepareTreeViewForClassStatistics (NodeView view) { + view.AppendColumn ("Name", new Gtk.CellRendererText (), delegate (TreeViewColumn column, CellRenderer cell, ITreeNode node) { + ClassStatisticsNode classNode = (ClassStatisticsNode) node; + ((CellRendererText) cell).Markup = classNode.Name; + }); + view.AppendColumn ("Allocated bytes", new Gtk.CellRendererText (), delegate (TreeViewColumn column, CellRenderer cell, ITreeNode node) { + ClassStatisticsNode classNode = (ClassStatisticsNode) node; + ((CellRendererText) cell).Markup = classNode.AllocatedBytes.ToString (); + }); + view.NodeStore = new Gtk.NodeStore (typeof (ClassStatisticsNode)); + } + + public static void FillTreeViewWithClassStatistics (NodeView view, HeapObjectSet.HeapObjectSetClassStatistics[] classes) { + view.NodeStore.Clear (); + foreach (HeapObjectSet.HeapObjectSetClassStatistics c in classes) { + view.NodeStore.AddNode (new ClassStatisticsNode (c)); + } + } + + HeapExplorerTreeModel.Node NodeSelectedForComparison; + + public HeapSnapshotExplorer() + { + this.Build(); + + LoadBlock = new Menu (); + MenuItem loadData = new MenuItem ("Load block data"); + loadData.Activated += delegate { + OnLoadData (); + }; + LoadBlock.Append (loadData); + + FilterSet = new Menu (); + MenuItem filterByClass = new MenuItem ("Filter by object class"); + filterByClass.Activated += delegate { + OnFilterByClass (); + }; + FilterSet.Append (filterByClass); + MenuItem filterByReferencesObjectOfClass = new MenuItem ("Filter by \"references object of class\""); + filterByReferencesObjectOfClass.Activated += delegate { + OnFilterByReferencesObjectOfClass (); + }; + FilterSet.Append (filterByReferencesObjectOfClass); + MenuItem filterByIsReferencedByObjectOfClass = new MenuItem ("Filter by \"is referenced by object of class\""); + filterByIsReferencedByObjectOfClass.Activated += delegate { + OnFilterByIsReferencedByObjectOfClass (); + }; + FilterSet.Append (filterByIsReferencedByObjectOfClass); + MenuItem markSetForComparison = new MenuItem ("Mark set for comparison"); + markSetForComparison.Activated += delegate { + OnMarkSetForComparison (); + }; + FilterSet.Append (markSetForComparison); + + CompareSet = new Menu (); + MenuItem performComparison = new MenuItem ("Perform comparison with this set"); + performComparison.Activated += delegate { + OnPerformComparison (); + }; + CompareSet.Append (performComparison); + MenuItem clearSetForComparison = new MenuItem ("Clear selection for comparison"); + clearSetForComparison.Activated += delegate { + OnClearSetForComparison (); + }; + CompareSet.Append (clearSetForComparison); + + PrepareTreeViewForClassStatistics (PerClassStatistics); + + Tree.Selection.Changed += delegate (object o, EventArgs args) { + TreeSelection selection = (TreeSelection) o; + TreeIter iter; + if (selection.GetSelected (out iter)) { + currentSelection = (HeapExplorerTreeModel.Node) Tree.Model.GetValue (iter, 0); + if (currentSelection != null) { + if (currentSelection.Objects != null) { + FillTreeViewWithClassStatistics (PerClassStatistics, currentSelection.Objects.ClassStatistics); + } else { + PerClassStatistics.NodeStore.Clear (); + } + } + } + }; + + Gtk.TreeViewColumn setColumn = new Gtk.TreeViewColumn (); + Gtk.TreeViewColumn countColumn = new Gtk.TreeViewColumn (); + Gtk.TreeViewColumn bytesColumn = new Gtk.TreeViewColumn (); + setColumn.Title = "Object set"; + countColumn.Title = "Object count"; + bytesColumn.Title = "Bytes"; + Gtk.CellRendererText setCell = new Gtk.CellRendererText (); + Gtk.CellRendererText countCell = new Gtk.CellRendererText (); + Gtk.CellRendererText bytesCell = new Gtk.CellRendererText (); + setColumn.PackStart (setCell, true); + countColumn.PackStart (countCell, true); + bytesColumn.PackStart (bytesCell, true); + + setColumn.SetCellDataFunc (setCell, delegate (Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter) { + HeapExplorerTreeModel.Node node = (HeapExplorerTreeModel.Node) model.GetValue (iter, 0); + CellRendererText textCell = (CellRendererText) cell; + textCell.Markup = node.Description; + if (node != NodeSelectedForComparison) { + textCell.Style = Pango.Style.Normal; + } else { + textCell.Style = Pango.Style.Italic; + } + }); + countColumn.SetCellDataFunc (countCell, delegate (Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter) { + HeapExplorerTreeModel.Node node = (HeapExplorerTreeModel.Node) model.GetValue (iter, 0); + CellRendererText textCell = (CellRendererText) cell; + textCell.Markup = node.Count; + if (node != NodeSelectedForComparison) { + textCell.Style = Pango.Style.Normal; + } else { + textCell.Style = Pango.Style.Italic; + } + }); + bytesColumn.SetCellDataFunc (bytesCell, delegate (Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter) { + HeapExplorerTreeModel.Node node = (HeapExplorerTreeModel.Node) model.GetValue (iter, 0); + CellRendererText textCell = (CellRendererText) cell; + textCell.Markup = node.AllocatedBytes; + if (node != NodeSelectedForComparison) { + textCell.Style = Pango.Style.Normal; + } else { + textCell.Style = Pango.Style.Italic; + } + }); + + setColumn.AddAttribute (setCell, "text", 0); + countColumn.AddAttribute (countCell, "text", 1); + bytesColumn.AddAttribute (bytesCell, "text", 2); + + Tree.AppendColumn (setColumn); + Tree.AppendColumn (countColumn); + Tree.AppendColumn (bytesColumn); + + LoadBlock.ShowAll (); + FilterSet.ShowAll (); + CompareSet.ShowAll (); + + NodeSelectedForComparison = null; + } + + public void OnLoadData () { + if (CurrentSelection != null) { + HeapExplorerTreeModel.SnapshotNode snapshotNode = CurrentSelection as HeapExplorerTreeModel.SnapshotNode; + if ((snapshotNode != null) && (snapshotNode.Objects == null)) { + ((HeapExplorerTreeModel.SnapshotNode)CurrentSelection).ReadSnapshot (); + } + } + } + + public void OnFilterByClass () { + if (CurrentSelection != null) { + LoadedClass c = LoadedClassChooser.ChooseClass (CurrentSelection.Objects.ClassStatistics); + if (c != null) { + IHeapObjectFilter filter = new HeapObjectIsOfClass (c); + CurrentSelection.Filter (filter); + } + } + } + + public void OnFilterByReferencesObjectOfClass () { + if (CurrentSelection != null) { + LoadedClass c = LoadedClassChooser.ChooseClass (CurrentSelection.Root.Objects.ClassStatistics); + if (c != null) { + IHeapObjectFilter filter = new HeapObjectReferencesObjectOfClass (c); + CurrentSelection.Filter (filter); + } + } + } + + public void OnFilterByIsReferencedByObjectOfClass () { + if (CurrentSelection != null) { + LoadedClass c = LoadedClassChooser.ChooseClass (CurrentSelection.Root.Objects.ClassStatistics); + if (c != null) { + IHeapObjectFilter filter = new HeapObjectIsReferencedByObjectOfClass (c); + CurrentSelection.Filter (filter); + } + } + } + + public void OnMarkSetForComparison () { + if (CurrentSelection != null) { + NodeSelectedForComparison = CurrentSelection; + } + } + + public void OnClearSetForComparison () { + NodeSelectedForComparison = null; + } + + public void OnPerformComparison () { + if (CurrentSelection != null) { + HeapExplorerTreeModel.SubSetNode firstSubNode; + HeapExplorerTreeModel.SubSetNode secondSubNode; + HeapExplorerTreeModel.Node.PerformComparison (NodeSelectedForComparison, CurrentSelection, out firstSubNode, out secondSubNode); + NodeSelectedForComparison = null; + } + } + + + + [GLib.ConnectBefore] + protected virtual void OnTreeButtonPress (object o, Gtk.ButtonPressEventArgs args) + { + if (args.Event.Button == 3) { + if (CurrentSelection != null) { + HeapExplorerTreeModel.SnapshotNode snapshotNode = CurrentSelection as HeapExplorerTreeModel.SnapshotNode; + if ((snapshotNode != null) && (snapshotNode.Objects == null)) { + LoadBlock.Popup (); + } else { + if (NodeSelectedForComparison == null) { + FilterSet.Popup (); + } else if (CurrentSelection != NodeSelectedForComparison) { + CompareSet.Popup (); + } + } + } + } + } + } +} diff --git a/Mono.Profiler/heap-snapshot-explorer/LoadedClassChooser.cs b/Mono.Profiler/heap-snapshot-explorer/LoadedClassChooser.cs new file mode 100644 index 00000000..1124f85d --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/LoadedClassChooser.cs @@ -0,0 +1,78 @@ +// LoadedClassChooser.cs created with MonoDevelop +// User: massi at 10:14 PM 4/18/2008 +// +// To change standard headers go to Edit->Preferences->Coding->Standard Headers +// + +using System; +using Gtk; + +namespace Mono.Profiler +{ + public partial class LoadedClassChooser : Gtk.Dialog + { + LoadedClass result; + public LoadedClass Result { + get { + return result; + } + } + + HeapObjectSet.HeapObjectSetClassStatistics currentSelection; + + LoadedClassChooser() + { + this.Build(); + HeapSnapshotExplorer.PrepareTreeViewForClassStatistics (ClassList); + ClassList.NodeSelection.Changed += new EventHandler (OnSelectionChanged); + currentSelection = null; + } + + void OnSelectionChanged (object o, System.EventArgs args) { + Gtk.NodeSelection selection = (Gtk.NodeSelection) o; + HeapSnapshotExplorer.ClassStatisticsNode node = (HeapSnapshotExplorer.ClassStatisticsNode) selection.SelectedNode; + if (node != null) { + currentSelection = node.ClassStatistics; + } else { + currentSelection = null; + } + } + + void FillList (HeapObjectSet.HeapObjectSetClassStatistics[] classes) { + HeapSnapshotExplorer.FillTreeViewWithClassStatistics (ClassList, classes); + currentSelection = null; + } + + protected virtual void OnCancel (object sender, System.EventArgs e) + { + result = null; + } + + protected virtual void OnOK (object sender, System.EventArgs e) + { + if (currentSelection != null) { + result = currentSelection.Class; + } else { + result = null; + } + } + + static LoadedClassChooser chooser; + + public static LoadedClass ChooseClass (HeapObjectSet.HeapObjectSetClassStatistics[] classes) { + LoadedClass result; + if (chooser == null) { + chooser = new LoadedClassChooser (); + } + chooser.FillList (classes); + ResponseType response = (ResponseType) chooser.Run (); + if (response == ResponseType.Ok) { + result = chooser.result; + } else { + result = null; + } + chooser.Hide (); + return result; + } + } +} diff --git a/Mono.Profiler/heap-snapshot-explorer/Main.cs b/Mono.Profiler/heap-snapshot-explorer/Main.cs new file mode 100644 index 00000000..899868c2 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/Main.cs @@ -0,0 +1,21 @@ +// Main.cs created with MonoDevelop +// User: massi at 11:35 AM 4/17/2008 +// +// To change standard headers go to Edit->Preferences->Coding->Standard Headers +// +using System; +using Gtk; + +namespace heapsnapshotexplorer +{ + class MainClass + { + public static void Main (string[] args) + { + Application.Init (); + MainWindow win = new MainWindow (); + win.Show (); + Application.Run (); + } + } +}
\ No newline at end of file diff --git a/Mono.Profiler/heap-snapshot-explorer/MainWindow.cs b/Mono.Profiler/heap-snapshot-explorer/MainWindow.cs new file mode 100644 index 00000000..aa4365d5 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/MainWindow.cs @@ -0,0 +1,21 @@ +// MainWindow.cs created with MonoDevelop +// User: massi at 11:35 AM 4/17/2008 +// +// To change standard headers go to Edit->Preferences->Coding->Standard Headers +// +using System; +using Gtk; + +public partial class MainWindow: Gtk.Window +{ + public MainWindow (): base (Gtk.WindowType.Toplevel) + { + Build (); + } + + protected void OnDeleteEvent (object sender, DeleteEventArgs a) + { + Application.Quit (); + a.RetVal = true; + } +}
\ No newline at end of file diff --git a/Mono.Profiler/heap-snapshot-explorer/Makefile.am b/Mono.Profiler/heap-snapshot-explorer/Makefile.am new file mode 100644 index 00000000..39bcb846 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/Makefile.am @@ -0,0 +1,102 @@ + +EXTRA_DIST = + +# Warning: This is an automatically generated file, do not edit! + +if ENABLE_DEBUG +ASSEMBLY_COMPILER_COMMAND = gmcs +ASSEMBLY_COMPILER_FLAGS = -noconfig -codepage:utf8 -warn:4 -optimize+ -debug "-define:DEBUG" + +ASSEMBLY = bin/Debug/mprof-heap-snapshot-explorer.dll +ASSEMBLY_MDB = $(ASSEMBLY).mdb +COMPILE_TARGET = library +PROJECT_REFERENCES = \ + ../profiler-decoder-library/bin/Debug/mprof-decoder-library.dll +BUILD_DIR = bin/Debug + +MPROF_HEAP_SNAPSHOT_EXPLORER_DLL_MDB_SOURCE=bin/Debug/mprof-heap-snapshot-explorer.dll.mdb +MPROF_HEAP_SNAPSHOT_EXPLORER_DLL_MDB=$(BUILD_DIR)/mprof-heap-snapshot-explorer.dll.mdb +PROFILER_DECODER_LIBRARY_DLL= +MPROF_DECODER_LIBRARY_DLL_MDB_SOURCE=../profiler-decoder-library/bin/Debug/mprof-decoder-library.dll.mdb +MPROF_DECODER_LIBRARY_DLL_MDB=$(BUILD_DIR)/mprof-decoder-library.dll.mdb +MPROF_DECODER_LIBRARY_DLL_SOURCE=../profiler-decoder-library/bin/Debug/mprof-decoder-library.dll +MPROF_DECODER_LIBRARY_DLL=$(BUILD_DIR)/mprof-decoder-library.dll + +endif + +if ENABLE_RELEASE +ASSEMBLY_COMPILER_COMMAND = gmcs +ASSEMBLY_COMPILER_FLAGS = -noconfig -codepage:utf8 -warn:4 -optimize+ +ASSEMBLY = bin/Release/heap-snapshot-explorer.dll +ASSEMBLY_MDB = +COMPILE_TARGET = library +PROJECT_REFERENCES = \ + ../profiler-decoder-library/bin/Release/profiler-decoder-library.dll +BUILD_DIR = bin/Release + +MPROF_HEAP_SNAPSHOT_EXPLORER_DLL_MDB= +PROFILER_DECODER_LIBRARY_DLL_SOURCE=../profiler-decoder-library/bin/Release/profiler-decoder-library.dll +PROFILER_DECODER_LIBRARY_DLL=$(BUILD_DIR)/profiler-decoder-library.dll +MPROF_DECODER_LIBRARY_DLL_MDB= +MPROF_DECODER_LIBRARY_DLL= + +endif + +AL=al2 +SATELLITE_ASSEMBLY_NAME=.resources.dll + +PROGRAMFILES = \ + $(MPROF_HEAP_SNAPSHOT_EXPLORER_DLL_MDB) \ + $(PROFILER_DECODER_LIBRARY_DLL) \ + $(MPROF_DECODER_LIBRARY_DLL_MDB) \ + $(MPROF_DECODER_LIBRARY_DLL) + + +RESGEN=resgen2 + +all: $(ASSEMBLY) $(PROGRAMFILES) + +FILES = \ + MyClass.cs \ + AssemblyInfo.cs \ + gtk-gui/generated.cs \ + HeapSnapshotExplorer.cs \ + HeapExplorerActions.cs \ + gtk-gui/Mono.Profiler.HeapExplorerActions.cs \ + HeapExplorerTreeModel.cs \ + gtk-gui/Mono.Profiler.HeapSnapshotExplorer.cs \ + LoadedClassChooser.cs \ + gtk-gui/Mono.Profiler.LoadedClassChooser.cs + +DATA_FILES = + +RESOURCES = \ + gtk-gui/gui.stetic \ + gtk-gui/objects.xml + +EXTRAS = + +REFERENCES = \ + System \ + $(GTK_SHARP_20_LIBS) \ + Mono.Posix + +DLL_REFERENCES = + +CLEANFILES = $(PROGRAMFILES) + +include $(top_srcdir)/Makefile.include + + +$(eval $(call emit-deploy-target,PROFILER_DECODER_LIBRARY_DLL)) +$(eval $(call emit-deploy-target,MPROF_DECODER_LIBRARY_DLL_MDB)) +$(eval $(call emit-deploy-target,MPROF_DECODER_LIBRARY_DLL)) + + +$(eval $(call emit_resgen_targets)) +$(build_xamlg_list): %.xaml.g.cs: %.xaml + xamlg '$<' + +$(ASSEMBLY) $(ASSEMBLY_MDB): $(build_sources) $(build_resources) $(build_datafiles) $(DLL_REFERENCES) $(PROJECT_REFERENCES) $(build_xamlg_list) $(build_satellite_assembly_list) + mkdir -p $(shell dirname $(ASSEMBLY)) + $(ASSEMBLY_COMPILER_COMMAND) $(ASSEMBLY_COMPILER_FLAGS) -out:$(ASSEMBLY) -target:$(COMPILE_TARGET) $(build_sources_embed) $(build_resources_embed) $(build_references_ref) diff --git a/Mono.Profiler/heap-snapshot-explorer/MyClass.cs b/Mono.Profiler/heap-snapshot-explorer/MyClass.cs new file mode 100644 index 00000000..0e922ff3 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/MyClass.cs @@ -0,0 +1,20 @@ +// MyClass.cs created with MonoDevelop +// User: massi at 11:36 AM 4/17/2008 +// +// To change standard headers go to Edit->Preferences->Coding->Standard Headers +// + +using System; + +namespace heapsnapshotexplorer +{ + + + public class MyClass + { + + public MyClass() + { + } + } +} diff --git a/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.HeapExplorerActions.cs b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.HeapExplorerActions.cs new file mode 100644 index 00000000..98bfb67f --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.HeapExplorerActions.cs @@ -0,0 +1,40 @@ +// ------------------------------------------------------------------------------ +// <autogenerated> +// This code was generated by a tool. +// Mono Runtime Version: 2.0.50727.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </autogenerated> +// ------------------------------------------------------------------------------ + +namespace Mono.Profiler { + + + public partial class HeapExplorerActions { + + private Gtk.Action loadDataAction; + + private Gtk.Action filterByClassAction; + + private Gtk.Action filterByClassOfReferencedObjectAction; + + private Gtk.Action filterByClassOfReferencingObjectAction; + + public virtual void Build() { + // Action group Mono.Profiler.HeapExplorerActions + this.loadDataAction = new Gtk.Action("loadDataAction", Mono.Unix.Catalog.GetString("Load data"), null, "gtk-add"); + this.loadDataAction.ShortLabel = Mono.Unix.Catalog.GetString("Load data"); + this.Add(this.loadDataAction, null); + this.filterByClassAction = new Gtk.Action("filterByClassAction", Mono.Unix.Catalog.GetString("Filter by class"), null, null); + this.filterByClassAction.ShortLabel = Mono.Unix.Catalog.GetString("Filter by class"); + this.Add(this.filterByClassAction, null); + this.filterByClassOfReferencedObjectAction = new Gtk.Action("filterByClassOfReferencedObjectAction", Mono.Unix.Catalog.GetString("Filter by class of referenced object"), null, null); + this.filterByClassOfReferencedObjectAction.ShortLabel = Mono.Unix.Catalog.GetString("Filter by class of referenced object"); + this.Add(this.filterByClassOfReferencedObjectAction, null); + this.filterByClassOfReferencingObjectAction = new Gtk.Action("filterByClassOfReferencingObjectAction", Mono.Unix.Catalog.GetString("Filter by class of referencing object"), null, null); + this.filterByClassOfReferencingObjectAction.ShortLabel = Mono.Unix.Catalog.GetString("Filter by class of referencing object"); + this.Add(this.filterByClassOfReferencingObjectAction, null); + } + } +} diff --git a/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.HeapSnapshotExplorer.cs b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.HeapSnapshotExplorer.cs new file mode 100644 index 00000000..047a8e3f --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.HeapSnapshotExplorer.cs @@ -0,0 +1,68 @@ +// ------------------------------------------------------------------------------ +// <autogenerated> +// This code was generated by a tool. +// Mono Runtime Version: 2.0.50727.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </autogenerated> +// ------------------------------------------------------------------------------ + +namespace Mono.Profiler { + + + public partial class HeapSnapshotExplorer { + + private Gtk.HPaned hpaned1; + + private Gtk.ScrolledWindow GtkScrolledWindow; + + private Gtk.TreeView Tree; + + private Gtk.ScrolledWindow GtkScrolledWindow1; + + private Gtk.NodeView PerClassStatistics; + + protected virtual void Build() { + Stetic.Gui.Initialize(this); + // Widget Mono.Profiler.HeapSnapshotExplorer + Stetic.BinContainer.Attach(this); + this.Name = "Mono.Profiler.HeapSnapshotExplorer"; + // Container child Mono.Profiler.HeapSnapshotExplorer.Gtk.Container+ContainerChild + this.hpaned1 = new Gtk.HPaned(); + this.hpaned1.CanFocus = true; + this.hpaned1.Name = "hpaned1"; + this.hpaned1.Position = 124; + // Container child hpaned1.Gtk.Paned+PanedChild + this.GtkScrolledWindow = new Gtk.ScrolledWindow(); + this.GtkScrolledWindow.Name = "GtkScrolledWindow"; + this.GtkScrolledWindow.ShadowType = ((Gtk.ShadowType)(1)); + // Container child GtkScrolledWindow.Gtk.Container+ContainerChild + this.Tree = new Gtk.TreeView(); + this.Tree.CanFocus = true; + this.Tree.Name = "Tree"; + this.Tree.HeadersClickable = true; + this.GtkScrolledWindow.Add(this.Tree); + this.hpaned1.Add(this.GtkScrolledWindow); + Gtk.Paned.PanedChild w2 = ((Gtk.Paned.PanedChild)(this.hpaned1[this.GtkScrolledWindow])); + w2.Resize = false; + // Container child hpaned1.Gtk.Paned+PanedChild + this.GtkScrolledWindow1 = new Gtk.ScrolledWindow(); + this.GtkScrolledWindow1.Name = "GtkScrolledWindow1"; + this.GtkScrolledWindow1.ShadowType = ((Gtk.ShadowType)(1)); + // Container child GtkScrolledWindow1.Gtk.Container+ContainerChild + this.PerClassStatistics = new Gtk.NodeView(); + this.PerClassStatistics.CanFocus = true; + this.PerClassStatistics.Name = "PerClassStatistics"; + this.PerClassStatistics.HeadersClickable = true; + this.GtkScrolledWindow1.Add(this.PerClassStatistics); + this.hpaned1.Add(this.GtkScrolledWindow1); + this.Add(this.hpaned1); + if ((this.Child != null)) { + this.Child.ShowAll(); + } + this.Show(); + this.Tree.ButtonPressEvent += new Gtk.ButtonPressEventHandler(this.OnTreeButtonPress); + } + } +} diff --git a/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.LoadedClassChooser.cs b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.LoadedClassChooser.cs new file mode 100644 index 00000000..4e0cf4c2 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/Mono.Profiler.LoadedClassChooser.cs @@ -0,0 +1,92 @@ +// ------------------------------------------------------------------------------ +// <autogenerated> +// This code was generated by a tool. +// Mono Runtime Version: 2.0.50727.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </autogenerated> +// ------------------------------------------------------------------------------ + +namespace Mono.Profiler { + + + public partial class LoadedClassChooser { + + private Gtk.ScrolledWindow GtkScrolledWindow; + + private Gtk.NodeView ClassList; + + private Gtk.Button buttonCancel; + + private Gtk.Button buttonOk; + + protected virtual void Build() { + Stetic.Gui.Initialize(this); + // Widget Mono.Profiler.LoadedClassChooser + this.Name = "Mono.Profiler.LoadedClassChooser"; + this.Title = Mono.Unix.Catalog.GetString("Choose class"); + this.WindowPosition = ((Gtk.WindowPosition)(4)); + this.Modal = true; + this.HasSeparator = false; + // Internal child Mono.Profiler.LoadedClassChooser.VBox + Gtk.VBox w1 = this.VBox; + w1.Name = "dialog1_VBox"; + w1.BorderWidth = ((uint)(2)); + // Container child dialog1_VBox.Gtk.Box+BoxChild + this.GtkScrolledWindow = new Gtk.ScrolledWindow(); + this.GtkScrolledWindow.Name = "GtkScrolledWindow"; + this.GtkScrolledWindow.ShadowType = ((Gtk.ShadowType)(1)); + // Container child GtkScrolledWindow.Gtk.Container+ContainerChild + this.ClassList = new Gtk.NodeView(); + this.ClassList.CanFocus = true; + this.ClassList.Name = "ClassList"; + this.ClassList.Reorderable = true; + this.ClassList.SearchColumn = 0; + this.ClassList.HeadersClickable = true; + this.GtkScrolledWindow.Add(this.ClassList); + w1.Add(this.GtkScrolledWindow); + Gtk.Box.BoxChild w3 = ((Gtk.Box.BoxChild)(w1[this.GtkScrolledWindow])); + w3.Position = 0; + // Internal child Mono.Profiler.LoadedClassChooser.ActionArea + Gtk.HButtonBox w4 = this.ActionArea; + w4.Name = "dialog1_ActionArea"; + w4.Spacing = 6; + w4.BorderWidth = ((uint)(5)); + w4.LayoutStyle = ((Gtk.ButtonBoxStyle)(4)); + // Container child dialog1_ActionArea.Gtk.ButtonBox+ButtonBoxChild + this.buttonCancel = new Gtk.Button(); + this.buttonCancel.CanDefault = true; + this.buttonCancel.CanFocus = true; + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.UseStock = true; + this.buttonCancel.UseUnderline = true; + this.buttonCancel.Label = "gtk-cancel"; + this.AddActionWidget(this.buttonCancel, -6); + Gtk.ButtonBox.ButtonBoxChild w5 = ((Gtk.ButtonBox.ButtonBoxChild)(w4[this.buttonCancel])); + w5.Expand = false; + w5.Fill = false; + // Container child dialog1_ActionArea.Gtk.ButtonBox+ButtonBoxChild + this.buttonOk = new Gtk.Button(); + this.buttonOk.CanDefault = true; + this.buttonOk.CanFocus = true; + this.buttonOk.Name = "buttonOk"; + this.buttonOk.UseStock = true; + this.buttonOk.UseUnderline = true; + this.buttonOk.Label = "gtk-ok"; + this.AddActionWidget(this.buttonOk, -5); + Gtk.ButtonBox.ButtonBoxChild w6 = ((Gtk.ButtonBox.ButtonBoxChild)(w4[this.buttonOk])); + w6.Position = 1; + w6.Expand = false; + w6.Fill = false; + if ((this.Child != null)) { + this.Child.ShowAll(); + } + this.DefaultWidth = 400; + this.DefaultHeight = 300; + this.Show(); + this.Response += new Gtk.ResponseHandler(this.OnOK); + this.Close += new System.EventHandler(this.OnCancel); + } + } +} diff --git a/Mono.Profiler/heap-snapshot-explorer/gtk-gui/generated.cs b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/generated.cs new file mode 100644 index 00000000..6ca856d4 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/generated.cs @@ -0,0 +1,92 @@ +// ------------------------------------------------------------------------------ +// <autogenerated> +// This code was generated by a tool. +// Mono Runtime Version: 2.0.50727.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </autogenerated> +// ------------------------------------------------------------------------------ + +namespace Stetic { + + + internal class Gui { + + private static bool initialized; + + internal static void Initialize(Gtk.Widget iconRenderer) { + if ((Stetic.Gui.initialized == false)) { + Stetic.Gui.initialized = true; + } + } + } + + internal class BinContainer { + + private Gtk.Widget child; + + private Gtk.UIManager uimanager; + + public static BinContainer Attach(Gtk.Bin bin) { + BinContainer bc = new BinContainer(); + bin.SizeRequested += new Gtk.SizeRequestedHandler(bc.OnSizeRequested); + bin.SizeAllocated += new Gtk.SizeAllocatedHandler(bc.OnSizeAllocated); + bin.Added += new Gtk.AddedHandler(bc.OnAdded); + return bc; + } + + private void OnSizeRequested(object sender, Gtk.SizeRequestedArgs args) { + if ((this.child != null)) { + args.Requisition = this.child.SizeRequest(); + } + } + + private void OnSizeAllocated(object sender, Gtk.SizeAllocatedArgs args) { + if ((this.child != null)) { + this.child.Allocation = args.Allocation; + } + } + + private void OnAdded(object sender, Gtk.AddedArgs args) { + this.child = args.Widget; + } + + public void SetUiManager(Gtk.UIManager uim) { + this.uimanager = uim; + this.child.Realized += new System.EventHandler(this.OnRealized); + } + + private void OnRealized(object sender, System.EventArgs args) { + if ((this.uimanager != null)) { + Gtk.Widget w; + w = this.child.Toplevel; + if (((w != null) && typeof(Gtk.Window).IsInstanceOfType(w))) { + ((Gtk.Window)(w)).AddAccelGroup(this.uimanager.AccelGroup); + this.uimanager = null; + } + } + } + } + + internal class ActionGroups { + + private static Gtk.ActionGroup group1; + + public static Gtk.ActionGroup GetActionGroup(System.Type type) { + return Stetic.ActionGroups.GetActionGroup(type.FullName); + } + + public static Gtk.ActionGroup GetActionGroup(string name) { + if ((name == "Mono.Profiler.HeapExplorerActions")) { + if ((Stetic.ActionGroups.group1 == null)) { + Stetic.ActionGroups.group1 = new Mono.Profiler.HeapExplorerActions(); + } + return Stetic.ActionGroups.group1; + } + else { + return null; + } + } + } +} diff --git a/Mono.Profiler/heap-snapshot-explorer/gtk-gui/gui.stetic b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/gui.stetic new file mode 100644 index 00000000..da123d2b --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/gui.stetic @@ -0,0 +1,154 @@ +<?xml version="1.0" encoding="utf-8"?> +<stetic-interface> + <configuration> + <images-root-path>..</images-root-path> + <target-gtk-version>2.12.1</target-gtk-version> + </configuration> + <import> + <widget-library name="../bin/Debug/heap-snapshot-explorer.dll" internal="true" /> + </import> + <action-group name="Mono.Profiler.HeapExplorerActions"> + <action id="loadDataAction"> + <property name="Type">Action</property> + <property name="Label" translatable="yes">Load data</property> + <property name="ShortLabel" translatable="yes">Load data</property> + <property name="StockId">gtk-add</property> + </action> + <action id="filterByClassAction"> + <property name="Type">Action</property> + <property name="Label" translatable="yes">Filter by class</property> + <property name="ShortLabel" translatable="yes">Filter by class</property> + </action> + <action id="filterByClassOfReferencedObjectAction"> + <property name="Type">Action</property> + <property name="Label" translatable="yes">Filter by class of referenced object</property> + <property name="ShortLabel" translatable="yes">Filter by class of referenced object</property> + </action> + <action id="filterByClassOfReferencingObjectAction"> + <property name="Type">Action</property> + <property name="Label" translatable="yes">Filter by class of referencing object</property> + <property name="ShortLabel" translatable="yes">Filter by class of referencing object</property> + </action> + </action-group> + <widget class="Gtk.Bin" id="Mono.Profiler.HeapSnapshotExplorer" design-size="300 300"> + <property name="MemberName" /> + <child> + <widget class="Gtk.HPaned" id="hpaned1"> + <property name="MemberName" /> + <property name="CanFocus">True</property> + <property name="Position">124</property> + <child> + <widget class="Gtk.ScrolledWindow" id="GtkScrolledWindow"> + <property name="MemberName" /> + <property name="ShadowType">In</property> + <child> + <widget class="Gtk.TreeView" id="Tree"> + <property name="MemberName" /> + <property name="CanFocus">True</property> + <property name="ShowScrollbars">True</property> + <property name="HeadersClickable">True</property> + <signal name="ButtonPressEvent" handler="OnTreeButtonPress" /> + </widget> + </child> + </widget> + <packing> + <property name="Resize">False</property> + </packing> + </child> + <child> + <widget class="Gtk.ScrolledWindow" id="GtkScrolledWindow1"> + <property name="MemberName" /> + <property name="ShadowType">In</property> + <child> + <widget class="Gtk.NodeView" id="PerClassStatistics"> + <property name="MemberName" /> + <property name="CanFocus">True</property> + <property name="ShowScrollbars">True</property> + <property name="HeadersClickable">True</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <widget class="Gtk.Dialog" id="Mono.Profiler.LoadedClassChooser" design-size="400 300"> + <property name="MemberName" /> + <property name="Title" translatable="yes">Choose class</property> + <property name="WindowPosition">CenterOnParent</property> + <property name="Modal">True</property> + <property name="Buttons">2</property> + <property name="HelpButton">False</property> + <property name="HasSeparator">False</property> + <signal name="Response" handler="OnOK" /> + <signal name="Close" handler="OnCancel" /> + <child internal-child="VBox"> + <widget class="Gtk.VBox" id="dialog1_VBox"> + <property name="MemberName" /> + <property name="BorderWidth">2</property> + <child> + <widget class="Gtk.ScrolledWindow" id="GtkScrolledWindow"> + <property name="MemberName" /> + <property name="ShadowType">In</property> + <child> + <widget class="Gtk.NodeView" id="ClassList"> + <property name="MemberName">ClassList</property> + <property name="CanFocus">True</property> + <property name="ShowScrollbars">True</property> + <property name="Reorderable">True</property> + <property name="SearchColumn">0</property> + <property name="HeadersClickable">True</property> + </widget> + </child> + </widget> + <packing> + <property name="Position">0</property> + <property name="AutoSize">True</property> + </packing> + </child> + </widget> + </child> + <child internal-child="ActionArea"> + <widget class="Gtk.HButtonBox" id="dialog1_ActionArea"> + <property name="MemberName" /> + <property name="Spacing">6</property> + <property name="BorderWidth">5</property> + <property name="Size">2</property> + <property name="LayoutStyle">End</property> + <child> + <widget class="Gtk.Button" id="buttonCancel"> + <property name="MemberName" /> + <property name="CanDefault">True</property> + <property name="CanFocus">True</property> + <property name="UseStock">True</property> + <property name="Type">StockItem</property> + <property name="StockId">gtk-cancel</property> + <property name="ResponseId">-6</property> + <property name="label">gtk-cancel</property> + </widget> + <packing> + <property name="Expand">False</property> + <property name="Fill">False</property> + </packing> + </child> + <child> + <widget class="Gtk.Button" id="buttonOk"> + <property name="MemberName" /> + <property name="CanDefault">True</property> + <property name="CanFocus">True</property> + <property name="UseStock">True</property> + <property name="Type">StockItem</property> + <property name="StockId">gtk-ok</property> + <property name="ResponseId">-5</property> + <property name="label">gtk-ok</property> + </widget> + <packing> + <property name="Position">1</property> + <property name="Expand">False</property> + <property name="Fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> +</stetic-interface>
\ No newline at end of file diff --git a/Mono.Profiler/heap-snapshot-explorer/gtk-gui/heapsnapshotexplorer.HeapExplorerActions.cs b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/heapsnapshotexplorer.HeapExplorerActions.cs new file mode 100644 index 00000000..fd9386f9 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/heapsnapshotexplorer.HeapExplorerActions.cs @@ -0,0 +1,40 @@ +// ------------------------------------------------------------------------------ +// <autogenerated> +// This code was generated by a tool. +// Mono Runtime Version: 2.0.50727.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </autogenerated> +// ------------------------------------------------------------------------------ + +namespace heapsnapshotexplorer { + + + public partial class HeapExplorerActions { + + private Gtk.Action LoadDataAction; + + private Gtk.Action FilterByClassAction; + + private Gtk.Action FilterByClassOfReferencedObjectAction; + + private Gtk.Action FilterByClassOfReferencingObjectAction; + + public virtual void Build() { + // Action group heapsnapshotexplorer.HeapExplorerActions + this.LoadDataAction = new Gtk.Action("LoadDataAction", Mono.Unix.Catalog.GetString("Load data"), null, "gtk-add"); + this.LoadDataAction.ShortLabel = Mono.Unix.Catalog.GetString("Load data"); + this.Add(this.LoadDataAction, null); + this.FilterByClassAction = new Gtk.Action("FilterByClassAction", Mono.Unix.Catalog.GetString("Filter by class"), null, null); + this.FilterByClassAction.ShortLabel = Mono.Unix.Catalog.GetString("Filter by class"); + this.Add(this.FilterByClassAction, null); + this.FilterByClassOfReferencedObjectAction = new Gtk.Action("FilterByClassOfReferencedObjectAction", Mono.Unix.Catalog.GetString("Filter by class of referenced object"), null, null); + this.FilterByClassOfReferencedObjectAction.ShortLabel = Mono.Unix.Catalog.GetString("Filter by class of referenced object"); + this.Add(this.FilterByClassOfReferencedObjectAction, null); + this.FilterByClassOfReferencingObjectAction = new Gtk.Action("FilterByClassOfReferencingObjectAction", Mono.Unix.Catalog.GetString("Filter by class of referencing object"), null, null); + this.FilterByClassOfReferencingObjectAction.ShortLabel = Mono.Unix.Catalog.GetString("Filter by class of referencing object"); + this.Add(this.FilterByClassOfReferencingObjectAction, null); + } + } +} diff --git a/Mono.Profiler/heap-snapshot-explorer/gtk-gui/objects.xml b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/objects.xml new file mode 100644 index 00000000..a1c770fc --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/gtk-gui/objects.xml @@ -0,0 +1,6 @@ +<objects> + <object type="Mono.Profiler.HeapSnapshotExplorer" allow-children="false" base-type="Gtk.Bin" palette-category="mprof-heap-explorer"> + <itemgroups /> + <signals /> + </object> +</objects>
\ No newline at end of file diff --git a/Mono.Profiler/heap-snapshot-explorer/heap-snapshot-explorer.mdp b/Mono.Profiler/heap-snapshot-explorer/heap-snapshot-explorer.mdp new file mode 100644 index 00000000..f33f1290 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-explorer/heap-snapshot-explorer.mdp @@ -0,0 +1,44 @@ +<Project name="mprof-heap-explorer" fileversion="2.0" language="C#" clr-version="Net_2_0" ctype="DotNetProject"> + <Configurations active="Debug"> + <Configuration name="Debug" ctype="DotNetProjectConfiguration"> + <Output directory="bin/Debug" assembly="mprof-heap-snapshot-explorer" /> + <Build debugmode="True" target="Library" /> + <Execution runwithwarnings="True" consolepause="False" runtime="MsNet" clr-version="Net_2_0" /> + <CodeGeneration compiler="Mcs" warninglevel="4" optimize="True" unsafecodeallowed="False" generateoverflowchecks="True" definesymbols="DEBUG" generatexmldocumentation="False" ctype="CSharpCompilerParameters" /> + </Configuration> + <Configuration name="Release" ctype="DotNetProjectConfiguration"> + <Output directory="bin/Release" assembly="heap-snapshot-explorer" /> + <Build debugmode="False" target="Library" /> + <Execution runwithwarnings="True" consolepause="False" runtime="MsNet" clr-version="Net_2_0" /> + <CodeGeneration compiler="Mcs" warninglevel="4" optimize="True" unsafecodeallowed="False" generateoverflowchecks="True" generatexmldocumentation="False" ctype="CSharpCompilerParameters" /> + </Configuration> + </Configurations> + <Contents> + <File name="MyClass.cs" subtype="Code" buildaction="Compile" /> + <File name="AssemblyInfo.cs" subtype="Code" buildaction="Compile" /> + <File name="gtk-gui/gui.stetic" subtype="Code" buildaction="EmbedAsResource" /> + <File name="gtk-gui/generated.cs" subtype="Code" buildaction="Compile" /> + <File name="gtk-gui/objects.xml" subtype="Code" buildaction="EmbedAsResource" /> + <File name="HeapSnapshotExplorer.cs" subtype="Code" buildaction="Compile" /> + <File name="HeapExplorerActions.cs" subtype="Code" buildaction="Compile" /> + <File name="gtk-gui/Mono.Profiler.HeapExplorerActions.cs" subtype="Code" buildaction="Compile" /> + <File name="HeapExplorerTreeModel.cs" subtype="Code" buildaction="Compile" /> + <File name="gtk-gui/Mono.Profiler.HeapSnapshotExplorer.cs" subtype="Code" buildaction="Compile" /> + <File name="LoadedClassChooser.cs" subtype="Code" buildaction="Compile" /> + <File name="gtk-gui/Mono.Profiler.LoadedClassChooser.cs" subtype="Code" buildaction="Compile" /> + </Contents> + <References> + <ProjectReference type="Gac" localcopy="True" refto="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> + <ProjectReference type="Gac" localcopy="True" refto="gtk-sharp, Version=2.10.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" /> + <ProjectReference type="Gac" localcopy="True" refto="gdk-sharp, Version=2.10.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" /> + <ProjectReference type="Gac" localcopy="True" refto="Mono.Posix, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" /> + <ProjectReference type="Gac" localcopy="True" refto="pango-sharp, Version=2.10.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" /> + <ProjectReference type="Project" localcopy="True" refto="mprof-decoder-library" /> + </References> + <Deployment.LinuxDeployData generatePcFile="False" /> + <GtkDesignInfo isWidgetLibrary="True"> + <ExportedWidgets> + <Widget>Mono.Profiler.HeapSnapshotExplorer</Widget> + </ExportedWidgets> + </GtkDesignInfo> +</Project>
\ No newline at end of file diff --git a/Mono.Profiler/heap-snapshot-viewer/AssemblyInfo.cs b/Mono.Profiler/heap-snapshot-viewer/AssemblyInfo.cs new file mode 100644 index 00000000..be8bebd7 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-viewer/AssemblyInfo.cs @@ -0,0 +1,30 @@ +// AssemblyInfo.cs created with MonoDevelop +// User: massi at 10:50 PM 4/14/2008 +// +// To change standard headers go to Edit->Preferences->Coding->Standard Headers +// +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("heap-snapshot-viewer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// If the build and revision are set to '*' they will be updated automatically. + +[assembly: AssemblyVersion("1.0.*.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] diff --git a/Mono.Profiler/heap-snapshot-viewer/ChangeLog b/Mono.Profiler/heap-snapshot-viewer/ChangeLog new file mode 100644 index 00000000..d6bce14a --- /dev/null +++ b/Mono.Profiler/heap-snapshot-viewer/ChangeLog @@ -0,0 +1,12 @@ +2008-08-04 Massimiliano Mantione <massi@ximian.com> + Added man page. + +2008-08-01 Massimiliano Mantione <massi@ximian.com> + * Makefile.am, heap-snapshot-viewer.mdp: renamed output assembly. + * heap-snapshot-viewer.in: renamed to mprof-heap-viewer.in + +2008-08-01 Massimiliano Mantione <massi@ximian.com> + * Makefile.am: Regenerated from MonoDevelop. + +2008-04-13 Massimiliano Mantione <massi@ximian.com> + Initial code commit (just a plain MonoDevelop GTK# project for now). diff --git a/Mono.Profiler/heap-snapshot-viewer/Main.cs b/Mono.Profiler/heap-snapshot-viewer/Main.cs new file mode 100644 index 00000000..786e0a9d --- /dev/null +++ b/Mono.Profiler/heap-snapshot-viewer/Main.cs @@ -0,0 +1,66 @@ +// Author: +// Massimiliano Mantione (massi@ximian.com) +// +// (C) 2008 Novell, Inc http://www.novell.com +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + // + +using System; +using System.IO; +using Gtk; + +namespace Mono.Profiler +{ + class HeapSnapshotViewerMain + { + public static void Main (string[] args) + { + if (args.Length != 1) { + Console.WriteLine ("Please specify one input file"); + return; + } + + SeekableLogFileReader reader = null; + + try { + reader = new SeekableLogFileReader (args [0]); + } catch (IOException e){ + Console.Error.WriteLine ("It was not possible to open the file {0}", args [0]); + return; + } + + foreach (SeekableLogFileReader.Block block in reader.Blocks) { + Console.WriteLine ("Found block {0} at offset {1} (length {2}, time from start {3})", block.Code, block.FileOffset, block.Length, block.TimeFromStart); + } + + Application.Init (); + MainWindow win = new MainWindow (); + win.Show (); + + win.HeapExplorer.Model = new HeapExplorerTreeModel (reader); + win.HeapExplorer.Model.Initialize (); + + Application.Run (); + } + } +}
\ No newline at end of file diff --git a/Mono.Profiler/heap-snapshot-viewer/MainWindow.cs b/Mono.Profiler/heap-snapshot-viewer/MainWindow.cs new file mode 100644 index 00000000..68ff7237 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-viewer/MainWindow.cs @@ -0,0 +1,30 @@ +// MainWindow.cs created with MonoDevelop +// User: massi at 10:50 PM 4/14/2008 +// +// To change standard headers go to Edit->Preferences->Coding->Standard Headers +// +using System; +using Gtk; + +namespace Mono.Profiler { + public partial class MainWindow: Gtk.Window + { + public MainWindow (): base (Gtk.WindowType.Toplevel) + { + Build (); + } + + public Mono.Profiler.HeapSnapshotExplorer HeapExplorer { + get { + return heapExplorer; + } + } + + protected void OnDeleteEvent (object sender, DeleteEventArgs a) + { + Application.Quit (); + a.RetVal = true; + } + } +} + diff --git a/Mono.Profiler/heap-snapshot-viewer/Makefile.am b/Mono.Profiler/heap-snapshot-viewer/Makefile.am new file mode 100644 index 00000000..e3eb9eda --- /dev/null +++ b/Mono.Profiler/heap-snapshot-viewer/Makefile.am @@ -0,0 +1,138 @@ + +EXTRA_DIST = + +# Warning: This is an automatically generated file, do not edit! + +if ENABLE_DEBUG +ASSEMBLY_COMPILER_COMMAND = gmcs +ASSEMBLY_COMPILER_FLAGS = -noconfig -codepage:utf8 -warn:4 -optimize+ -debug "-define:DEBUG" + +ASSEMBLY = bin/Debug/mprof-heap-viewer.exe +ASSEMBLY_MDB = $(ASSEMBLY).mdb +COMPILE_TARGET = exe +PROJECT_REFERENCES = \ + ../heap-snapshot-explorer/bin/Debug/mprof-heap-snapshot-explorer.dll \ + ../profiler-decoder-library/bin/Debug/mprof-decoder-library.dll +BUILD_DIR = bin/Debug + +MPROF_HEAP_VIEWER_DESKTOP_SOURCE=app.desktop +MPROF_HEAP_SNAPSHOT_EXPLORER_DLL_MDB_SOURCE=../heap-snapshot-explorer/bin/Debug/mprof-heap-snapshot-explorer.dll.mdb +MPROF_HEAP_SNAPSHOT_EXPLORER_DLL_MDB=$(BUILD_DIR)/mprof-heap-snapshot-explorer.dll.mdb +PROFILER_DECODER_LIBRARY_DLL= +HEAP_SNAPSHOT_EXPLORER_DLL= +MPROF_DECODER_LIBRARY_DLL_MDB_SOURCE=../profiler-decoder-library/bin/Debug/mprof-decoder-library.dll.mdb +MPROF_DECODER_LIBRARY_DLL_MDB=$(BUILD_DIR)/mprof-decoder-library.dll.mdb +MPROF_DECODER_LIBRARY_DLL_SOURCE=../profiler-decoder-library/bin/Debug/mprof-decoder-library.dll +MPROF_DECODER_LIBRARY_DLL=$(BUILD_DIR)/mprof-decoder-library.dll +MPROF_HEAP_SNAPSHOT_EXPLORER_DLL_SOURCE=../heap-snapshot-explorer/bin/Debug/mprof-heap-snapshot-explorer.dll +MPROF_HEAP_SNAPSHOT_EXPLORER_DLL=$(BUILD_DIR)/mprof-heap-snapshot-explorer.dll +MPROF_HEAP_VIEWER_1_SOURCE=man/man1/mprof-heap-viewer.1 +MPROF_HEAP_VIEWER_EXE_MDB_SOURCE=bin/Debug/mprof-heap-viewer.exe.mdb +MPROF_HEAP_VIEWER_EXE_MDB=$(BUILD_DIR)/mprof-heap-viewer.exe.mdb + +endif + +if ENABLE_RELEASE +ASSEMBLY_COMPILER_COMMAND = gmcs +ASSEMBLY_COMPILER_FLAGS = -noconfig -codepage:utf8 -warn:4 -optimize+ +ASSEMBLY = bin/Release/mprof-heap-viewer.exe +ASSEMBLY_MDB = +COMPILE_TARGET = exe +PROJECT_REFERENCES = \ + ../heap-snapshot-explorer/bin/Release/heap-snapshot-explorer.dll \ + ../profiler-decoder-library/bin/Release/profiler-decoder-library.dll +BUILD_DIR = bin/Release + +MPROF_HEAP_VIEWER_DESKTOP_SOURCE=app.desktop +MPROF_HEAP_SNAPSHOT_EXPLORER_DLL_MDB= +PROFILER_DECODER_LIBRARY_DLL_SOURCE=../profiler-decoder-library/bin/Release/profiler-decoder-library.dll +PROFILER_DECODER_LIBRARY_DLL=$(BUILD_DIR)/profiler-decoder-library.dll +HEAP_SNAPSHOT_EXPLORER_DLL_SOURCE=../heap-snapshot-explorer/bin/Release/heap-snapshot-explorer.dll +HEAP_SNAPSHOT_EXPLORER_DLL=$(BUILD_DIR)/heap-snapshot-explorer.dll +MPROF_DECODER_LIBRARY_DLL_MDB= +MPROF_DECODER_LIBRARY_DLL= +MPROF_HEAP_SNAPSHOT_EXPLORER_DLL= +MPROF_HEAP_VIEWER_1_SOURCE=man/man1/mprof-heap-viewer.1 +MPROF_HEAP_VIEWER_EXE_MDB= + +endif + +AL=al2 +SATELLITE_ASSEMBLY_NAME=.resources.dll + +PROGRAMFILES = \ + $(MPROF_HEAP_SNAPSHOT_EXPLORER_DLL_MDB) \ + $(PROFILER_DECODER_LIBRARY_DLL) \ + $(HEAP_SNAPSHOT_EXPLORER_DLL) \ + $(MPROF_DECODER_LIBRARY_DLL_MDB) \ + $(MPROF_DECODER_LIBRARY_DLL) \ + $(MPROF_HEAP_SNAPSHOT_EXPLORER_DLL) \ + $(MPROF_HEAP_VIEWER_EXE_MDB) + +LINUX_DESKTOPAPPLICATIONS = \ + $(MPROF_HEAP_VIEWER_DESKTOP) + +COMMONAPPLICATIONDATAROOT_MAN_MAN1 = \ + $(MPROF_HEAP_VIEWER_1) + +BINARIES = \ + $(MPROF_HEAP_VIEWER) + + +RESGEN=resgen2 + +all: $(ASSEMBLY) $(PROGRAMFILES) $(LINUX_DESKTOPAPPLICATIONS) $(COMMONAPPLICATIONDATAROOT_MAN_MAN1) $(BINARIES) + +FILES = \ + gtk-gui/generated.cs \ + MainWindow.cs \ + Main.cs \ + AssemblyInfo.cs \ + gtk-gui/Mono.Profiler.MainWindow.cs + +DATA_FILES = \ + app.desktop \ + man/man1/mprof-heap-viewer.1 + +RESOURCES = \ + gtk-gui/gui.stetic \ + gtk-gui/objects.xml + +EXTRAS = \ + mprof-heap-viewer.in + +REFERENCES = \ + $(GTK_SHARP_20_LIBS) \ + $(GLIB_SHARP_20_LIBS) \ + $(GLADE_SHARP_20_LIBS) \ + System \ + Mono.Posix + +DLL_REFERENCES = + +CLEANFILES = $(PROGRAMFILES) $(LINUX_DESKTOPAPPLICATIONS) $(COMMONAPPLICATIONDATAROOT_MAN_MAN1) $(BINARIES) + +include $(top_srcdir)/Makefile.include + +MPROF_HEAP_VIEWER = $(BUILD_DIR)/mprof-heap-viewer +MPROF_HEAP_VIEWER_DESKTOP = $(BUILD_DIR)/mprof-heap-viewer.desktop +MPROF_HEAP_VIEWER_1 = $(BUILD_DIR)/man/man1/mprof-heap-viewer.1 + +$(eval $(call emit-deploy-wrapper,MPROF_HEAP_VIEWER,mprof-heap-viewer,x)) +$(eval $(call emit-deploy-target,MPROF_HEAP_VIEWER_DESKTOP)) +$(eval $(call emit-deploy-target,MPROF_HEAP_SNAPSHOT_EXPLORER_DLL_MDB)) +$(eval $(call emit-deploy-target,PROFILER_DECODER_LIBRARY_DLL)) +$(eval $(call emit-deploy-target,HEAP_SNAPSHOT_EXPLORER_DLL)) +$(eval $(call emit-deploy-target,MPROF_DECODER_LIBRARY_DLL_MDB)) +$(eval $(call emit-deploy-target,MPROF_DECODER_LIBRARY_DLL)) +$(eval $(call emit-deploy-target,MPROF_HEAP_SNAPSHOT_EXPLORER_DLL)) +$(eval $(call emit-deploy-target,MPROF_HEAP_VIEWER_1)) + + +$(eval $(call emit_resgen_targets)) +$(build_xamlg_list): %.xaml.g.cs: %.xaml + xamlg '$<' + +$(ASSEMBLY) $(ASSEMBLY_MDB): $(build_sources) $(build_resources) $(build_datafiles) $(DLL_REFERENCES) $(PROJECT_REFERENCES) $(build_xamlg_list) $(build_satellite_assembly_list) + mkdir -p $(shell dirname $(ASSEMBLY)) + $(ASSEMBLY_COMPILER_COMMAND) $(ASSEMBLY_COMPILER_FLAGS) -out:$(ASSEMBLY) -target:$(COMPILE_TARGET) $(build_sources_embed) $(build_resources_embed) $(build_references_ref) diff --git a/Mono.Profiler/heap-snapshot-viewer/app.desktop b/Mono.Profiler/heap-snapshot-viewer/app.desktop new file mode 100644 index 00000000..5fdc6d8a --- /dev/null +++ b/Mono.Profiler/heap-snapshot-viewer/app.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Application +Name=heap-snapshot-viewer +Exec=heap-snapshot-viewer +Terminal=false diff --git a/Mono.Profiler/heap-snapshot-viewer/gtk-gui/Mono.Profiler.MainWindow.cs b/Mono.Profiler/heap-snapshot-viewer/gtk-gui/Mono.Profiler.MainWindow.cs new file mode 100644 index 00000000..d2eb0c9c --- /dev/null +++ b/Mono.Profiler/heap-snapshot-viewer/gtk-gui/Mono.Profiler.MainWindow.cs @@ -0,0 +1,38 @@ +// ------------------------------------------------------------------------------ +// <autogenerated> +// This code was generated by a tool. +// Mono Runtime Version: 2.0.50727.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </autogenerated> +// ------------------------------------------------------------------------------ + +namespace Mono.Profiler { + + + public partial class MainWindow { + + private Mono.Profiler.HeapSnapshotExplorer heapExplorer; + + protected virtual void Build() { + Stetic.Gui.Initialize(this); + // Widget Mono.Profiler.MainWindow + this.Name = "Mono.Profiler.MainWindow"; + this.Title = Mono.Unix.Catalog.GetString("MainWindow"); + this.WindowPosition = ((Gtk.WindowPosition)(4)); + // Container child Mono.Profiler.MainWindow.Gtk.Container+ContainerChild + this.heapExplorer = new Mono.Profiler.HeapSnapshotExplorer(); + this.heapExplorer.Events = ((Gdk.EventMask)(256)); + this.heapExplorer.Name = "heapExplorer"; + this.Add(this.heapExplorer); + if ((this.Child != null)) { + this.Child.ShowAll(); + } + this.DefaultWidth = 468; + this.DefaultHeight = 300; + this.Show(); + this.DeleteEvent += new Gtk.DeleteEventHandler(this.OnDeleteEvent); + } + } +} diff --git a/Mono.Profiler/heap-snapshot-viewer/gtk-gui/generated.cs b/Mono.Profiler/heap-snapshot-viewer/gtk-gui/generated.cs new file mode 100644 index 00000000..c6702308 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-viewer/gtk-gui/generated.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------------ +// <autogenerated> +// This code was generated by a tool. +// Mono Runtime Version: 2.0.50727.42 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </autogenerated> +// ------------------------------------------------------------------------------ + +namespace Stetic { + + + internal class Gui { + + private static bool initialized; + + internal static void Initialize(Gtk.Widget iconRenderer) { + if ((Stetic.Gui.initialized == false)) { + Stetic.Gui.initialized = true; + } + } + } + + internal class ActionGroups { + + public static Gtk.ActionGroup GetActionGroup(System.Type type) { + return Stetic.ActionGroups.GetActionGroup(type.FullName); + } + + public static Gtk.ActionGroup GetActionGroup(string name) { + return null; + } + } +} diff --git a/Mono.Profiler/heap-snapshot-viewer/gtk-gui/gui.stetic b/Mono.Profiler/heap-snapshot-viewer/gtk-gui/gui.stetic new file mode 100644 index 00000000..6404b11a --- /dev/null +++ b/Mono.Profiler/heap-snapshot-viewer/gtk-gui/gui.stetic @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<stetic-interface> + <configuration> + <images-root-path>..</images-root-path> + <target-gtk-version>2.10.2</target-gtk-version> + </configuration> + <import> + <widget-library name="../../heap-snapshot-explorer/bin/Debug/heap-snapshot-explorer.dll" /> + <widget-library name="../bin/Debug/heap-snapshot-viewer.exe" internal="true" /> + </import> + <widget class="Gtk.Window" id="Mono.Profiler.MainWindow" design-size="468 300"> + <property name="MemberName" /> + <property name="Title" translatable="yes">MainWindow</property> + <property name="WindowPosition">CenterOnParent</property> + <signal name="DeleteEvent" handler="OnDeleteEvent" /> + <child> + <widget class="Mono.Profiler.HeapSnapshotExplorer" id="heapExplorer"> + <property name="MemberName" /> + <property name="Events">ButtonPressMask</property> + </widget> + </child> + </widget> +</stetic-interface>
\ No newline at end of file diff --git a/Mono.Profiler/heap-snapshot-viewer/gtk-gui/objects.xml b/Mono.Profiler/heap-snapshot-viewer/gtk-gui/objects.xml new file mode 100644 index 00000000..0c550b40 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-viewer/gtk-gui/objects.xml @@ -0,0 +1,2 @@ +<objects> +</objects>
\ No newline at end of file diff --git a/Mono.Profiler/heap-snapshot-viewer/heap-snapshot-viewer.mdp b/Mono.Profiler/heap-snapshot-viewer/heap-snapshot-viewer.mdp new file mode 100644 index 00000000..46cfcbc8 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-viewer/heap-snapshot-viewer.mdp @@ -0,0 +1,46 @@ +<Project name="mprof-heap-viewer" fileversion="2.0" language="C#" clr-version="Net_2_0" ctype="DotNetProject"> + <Configurations active="Debug"> + <Configuration name="Debug" ctype="DotNetProjectConfiguration"> + <Output directory="bin/Debug" assembly="mprof-heap-viewer" /> + <Build debugmode="True" target="Exe" /> + <Execution runwithwarnings="True" consolepause="True" runtime="MsNet" clr-version="Net_2_0" /> + <CodeGeneration compiler="Mcs" warninglevel="4" optimize="True" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" definesymbols="DEBUG" generatexmldocumentation="False" win32Icon="." ctype="CSharpCompilerParameters" /> + </Configuration> + <Configuration name="Release" ctype="DotNetProjectConfiguration"> + <Output directory="bin/Release" assembly="mprof-heap-viewer" /> + <Build debugmode="False" target="Exe" /> + <Execution runwithwarnings="True" consolepause="True" runtime="MsNet" clr-version="Net_2_0" /> + <CodeGeneration compiler="Mcs" warninglevel="4" optimize="True" unsafecodeallowed="False" generateoverflowchecks="True" mainclass="" generatexmldocumentation="False" win32Icon="." ctype="CSharpCompilerParameters" /> + </Configuration> + </Configurations> + <Contents> + <File name="gtk-gui/gui.stetic" subtype="Code" buildaction="EmbedAsResource" /> + <File name="gtk-gui/generated.cs" subtype="Code" buildaction="Compile" /> + <File name="MainWindow.cs" subtype="Code" buildaction="Compile" /> + <File name="Main.cs" subtype="Code" buildaction="Compile" /> + <File name="AssemblyInfo.cs" subtype="Code" buildaction="Compile" /> + <File name="app.desktop" subtype="Code" buildaction="FileCopy" DeployService.TargetDirectoryId="Linux.DesktopApplications" /> + <File name="gtk-gui/objects.xml" subtype="Code" buildaction="EmbedAsResource" /> + <File name="gtk-gui/Mono.Profiler.MainWindow.cs" subtype="Code" buildaction="Compile" /> + <File name="man" subtype="Directory" buildaction="Compile" /> + <File name="man/man1" subtype="Directory" buildaction="Compile" /> + <File name="man/man1/mprof-heap-viewer.1" subtype="Code" buildaction="FileCopy" DeployService.TargetDirectoryId="CommonApplicationDataRoot" DeployService.UseProjectRelativePath="True" /> + </Contents> + <References> + <ProjectReference type="Gac" localcopy="True" refto="gtk-sharp, Version=2.10.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" /> + <ProjectReference type="Gac" localcopy="True" refto="gdk-sharp, Version=2.10.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" /> + <ProjectReference type="Gac" localcopy="True" refto="glib-sharp, Version=2.10.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" /> + <ProjectReference type="Gac" localcopy="True" refto="glade-sharp, Version=2.10.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" /> + <ProjectReference type="Gac" localcopy="True" refto="pango-sharp, Version=2.10.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" /> + <ProjectReference type="Gac" localcopy="True" refto="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> + <ProjectReference type="Gac" localcopy="True" refto="Mono.Posix, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" /> + <ProjectReference type="Project" localcopy="True" refto="mprof-heap-explorer" /> + <ProjectReference type="Project" localcopy="True" refto="mprof-decoder-library" /> + </References> + <Deployment.LinuxDeployData scriptName="mprof-heap-viewer" /> + <GtkDesignInfo isWidgetLibrary="True" gtkVersion="2.10.2"> + <ExportedWidgets> + <Widget>heapsnapshotviewer.HeapSnapshotExplorer</Widget> + </ExportedWidgets> + </GtkDesignInfo> +</Project>
\ No newline at end of file diff --git a/Mono.Profiler/heap-snapshot-viewer/man/man1/mprof-heap-viewer.1 b/Mono.Profiler/heap-snapshot-viewer/man/man1/mprof-heap-viewer.1 new file mode 100644 index 00000000..b652ee6c --- /dev/null +++ b/Mono.Profiler/heap-snapshot-viewer/man/man1/mprof-heap-viewer.1 @@ -0,0 +1,49 @@ +.TH "mprof-heap-viewer" 1 +.SH NAME +mprof-heap-viewer \- GUI viewer for the logging profiler heap snapshots +.SH SYNOPSIS +.B mprof-heap-viewer +file +.SH DESCRIPTION +.B mprof-heap-viewer +GUI viewer for the logging profiler heap snapshots +.PP +WARNING: this application is unfinished and experimental. +Nevertheless it should work, and bug reports are encouraged. +.PP +This program decodes the contents of a logging profiler output file and +locates all the heap snapshots inside it. +The user can then select each individual snapshot and decide to load +it in memory and explore its contents. +.PP +The GUI is organized to work on "object sets" (listed in a tree view +on the left). +All operations are performed with a popup menu on the choosen set. +.PP +Initially the sets are the heap snapshots (of course a heap snapshot +can be considered a set of objects!). +For each set the GUI shows on the right a list that breaks it down by +class (one row for each class). +.PP +The user can then refine each set using a "filter", to select a subset. +Examples of filters are "all objects of class X", or "all objects that +reference an object of class X". +This way the user explores the sets breaking them down to subsets +(each subset in the GUI is a child of its owner set on the tree view). +.PP +Moreover the user can issue a "compare" operation between two arbitrary +sets A and B, which will compute two subsets: "A - B" (the objects of +A which are not in B, a subsect of A) and "B - A" (the reverse). +This can help in understanding what changed on the heap between +garbage collections. +.SH Options +.PP +None +.SH ENVIRONMENT VARIABLES +.PP +None +.SH SEE ALSO +mono(1) +.BR +.SH COPYRIGHT +Copyright (C) 2008 Novell, Inc (http://www.novell.com) diff --git a/Mono.Profiler/heap-snapshot-viewer/mprof-heap-viewer.in b/Mono.Profiler/heap-snapshot-viewer/mprof-heap-viewer.in new file mode 100644 index 00000000..91d83247 --- /dev/null +++ b/Mono.Profiler/heap-snapshot-viewer/mprof-heap-viewer.in @@ -0,0 +1,3 @@ +#!/bin/sh + +exec mono "@expanded_libdir@/@PACKAGE@/mprof-heap-viewer.exe" "$@" diff --git a/Mono.Profiler/profiler-decoder-library/AssemblyInfo.cs b/Mono.Profiler/profiler-decoder-library/AssemblyInfo.cs new file mode 100644 index 00000000..af4e2756 --- /dev/null +++ b/Mono.Profiler/profiler-decoder-library/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following +// attributes. +// +// change them to the information which is associated with the assembly +// you compile. + +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all values by your own or you can build default build and revision +// numbers with the '*' character (the default): + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes specify the key for the sign of your assembly. See the +// .NET Framework documentation for more information about signing. +// This is not required, if you don't want signing let these attributes like they're. +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] diff --git a/Mono.Profiler/profiler-decoder-library/BaseTypes.cs b/Mono.Profiler/profiler-decoder-library/BaseTypes.cs new file mode 100644 index 00000000..5e7ef698 --- /dev/null +++ b/Mono.Profiler/profiler-decoder-library/BaseTypes.cs @@ -0,0 +1,1004 @@ +// Author: +// Massimiliano Mantione (massi@ximian.com) +// +// (C) 2008 Novell, Inc http://www.novell.com +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; +using System.Collections.Generic; + +namespace Mono.Profiler { + public interface ILoadedElement { + uint ID {get;} + string Name {get;} + } + public interface ILoadedClass : ILoadedElement { + uint Size {get;} + } + public interface ILoadedMethod<LC> : ILoadedElement where LC : ILoadedClass { + LC Class {get;} + } + public interface IUnmanagedFunctionFromID<MR,UFR> : ILoadedElement where UFR : IUnmanagedFunctionFromRegion where MR : IExecutableMemoryRegion<UFR> { + MR Region {get;} + } + public interface IUnmanagedFunctionFromRegion { + string Name {get; set;} + uint StartOffset {get; set;} + uint EndOffset {get; set;} + } + public interface IExecutableMemoryRegion<UFR> : ILoadedElement where UFR : IUnmanagedFunctionFromRegion { + ulong StartAddress {get;} + ulong EndAddress {get;} + uint FileOffset {get;} + UFR NewFunction (string name, uint offset); + UFR GetFunction (uint offset); + UFR[] Functions {get;} + void SortFunctions (); + } + public interface IHeapObject<HO,LC> where HO: IHeapObject<HO,LC> where LC : ILoadedClass { + ulong ID {get;} + LC Class {get;} + uint Size {get;} + HO[] References {get;} + HO[] BackReferences {get;} + } + public interface IHeapSnapshot<HO,LC> where HO: IHeapObject<HO,LC> where LC : ILoadedClass { + uint Collection {get;} + ulong StartCounter {get;} + DateTime StartTime {get;} + ulong EndCounter {get;} + DateTime EndTime {get;} + HO NewHeapObject (ulong id, LC c, uint size, ulong[] referenceIds, int referencesCount); + HO GetHeapObject (ulong id); + HO[] HeapObjects {get;} + bool RecordSnapshot {get;} + } + + public interface ILoadedElementFactory<LC,LM,UFR,UFI,MR,HO,HS> where LC : ILoadedClass where LM : ILoadedMethod<LC> where UFR : IUnmanagedFunctionFromRegion where UFI : IUnmanagedFunctionFromID<MR,UFR> where MR : IExecutableMemoryRegion<UFR> where HO: IHeapObject<HO,LC> where HS: IHeapSnapshot<HO,LC> { + LC NewClass (uint id, string name, uint size); + LM NewMethod (uint id, LC c, string name); + MR NewExecutableMemoryRegion (uint id, string fileName, uint fileOffset, ulong startAddress, ulong endAddress); + UFI NewUnmanagedFunction (uint id, string name, MR region); + HS NewHeapSnapshot (uint collection, ulong startCounter, DateTime startTime, ulong endCounter, DateTime endTime, LC[] initialAllocations, bool recordSnapshot); + bool RecordHeapSnapshots {get; set;} + } + + public interface ILoadedElementHandler<LC,LM,UFR,UFI,MR,HO,HS> : ILoadedElementFactory<LC,LM,UFR,UFI,MR,HO,HS> where LC : ILoadedClass where LM : ILoadedMethod<LC> where UFR : IUnmanagedFunctionFromRegion where UFI : IUnmanagedFunctionFromID<MR,UFR> where MR : IExecutableMemoryRegion<UFR> where HO: IHeapObject<HO,LC> where HS: IHeapSnapshot<HO,LC> { + LC[] Classes {get;} + LC GetClass (uint id); + LM[] Methods {get;} + LM GetMethod (uint id); + MR[] ExecutableMemoryRegions {get;} + MR GetExecutableMemoryRegion (uint id); + MR GetExecutableMemoryRegion (ulong address); + void InvalidateExecutableMemoryRegion (uint id); + void SortExecutableMemoryRegions (); + UFI[] UnmanagedFunctionsByID {get;} + UFI GetUnmanagedFunctionByID (uint id); + HS[] HeapSnapshots {get;} + } + public interface IProfilerEventHandler<LC,LM,UFR,UFI,MR,EH,HO,HS> where LC : ILoadedClass where LM : ILoadedMethod<LC> where UFR : IUnmanagedFunctionFromRegion where UFI : IUnmanagedFunctionFromID<MR,UFR> where MR : IExecutableMemoryRegion<UFR> where EH : ILoadedElementHandler<LC,LM,UFR,UFI,MR,HO,HS> where HO: IHeapObject<HO,LC> where HS: IHeapSnapshot<HO,LC> { + EH LoadedElements {get;} + + void Start (uint version, string runtimeFile, ProfilerFlags flags, ulong startCounter, DateTime startTime); + void End (uint version, ulong endCounter, DateTime endTime); + + void StartBlock (ulong startCounter, DateTime startTime, ulong threadId); + void EndBlock (ulong endCounter, DateTime endTime, ulong threadId); + + void ModuleLoaded (ulong threadId, ulong startCounter, ulong endCounter, string name, bool success); + void ModuleUnloaded (ulong threadId, ulong startCounter, ulong endCounter, string name); + void AssemblyLoaded (ulong threadId, ulong startCounter, ulong endCounter, string name, bool success); + void AssemblyUnloaded (ulong threadId, ulong startCounter, ulong endCounter, string name); + void ApplicationDomainLoaded (ulong threadId, ulong startCounter, ulong endCounter, string name, bool success); + void ApplicationDomainUnloaded (ulong threadId, ulong startCounter, ulong endCounter, string name); + + void SetCurrentThread (ulong threadId); + + void ClassStartLoad (LC c, ulong counter); + void ClassEndLoad (LC c, ulong counter, bool success); + void ClassStartUnload (LC c, ulong counter); + void ClassEndUnload (LC c, ulong counter); + + void Allocation (LC c, uint size); + void Exception (LC c, ulong counter); + + void MethodEnter (LM m, ulong counter); + void MethodExit (LM m, ulong counter); + void MethodJitStart (LM m, ulong counter); + void MethodJitEnd (LM m, ulong counter, bool success); + void MethodFreed (LM m, ulong counter); + + void MethodStatisticalHit (LM m); + void UnknownMethodStatisticalHit (); + void UnmanagedFunctionStatisticalHit (UFR f); + void UnmanagedFunctionStatisticalHit (UFI f); + void UnknownUnmanagedFunctionStatisticalHit (MR region, uint offset); + void UnknownUnmanagedFunctionStatisticalHit (ulong address); + void StatisticalCallChainStart (uint chainDepth); + + void ThreadStart (ulong threadId, ulong counter); + void ThreadEnd (ulong threadId, ulong counter); + + void GarbageCollectionStart (uint collection, uint generation, ulong counter); + void GarbageCollectionEnd (uint collection, uint generation, ulong counter); + void GarbageCollectionMarkStart (uint collection, uint generation, ulong counter); + void GarbageCollectionMarkEnd (uint collection, uint generation, ulong counter); + void GarbageCollectionSweepStart (uint collection, uint generation, ulong counter); + void GarbageCollectionSweepEnd (uint collection, uint generation, ulong counter); + void GarbageCollectionResize (uint collection, ulong newSize); + void GarbageCollectionStopWorldStart (uint collection, uint generation, ulong counter); + void GarbageCollectionStopWorldEnd (uint collection, uint generation, ulong counter); + void GarbageCollectionStartWorldStart (uint collection, uint generation, ulong counter); + void GarbageCollectionStartWorldEnd (uint collection, uint generation, ulong counter); + + void HeapReportStart (HS snapshot); + void HeapObjectUnreachable (LC c, uint size); + void HeapObjectReachable (HO o); + void HeapReportEnd (HS snapshot); + + void AllocationSummaryStart (uint collection, ulong startCounter, DateTime startTime); + void ClassAllocationSummary (LC c, uint reachableInstances, uint reachableBytes, uint unreachableInstances, uint unreachableBytes); + void AllocationSummaryEnd (uint collection, ulong endCounter, DateTime endTime); + } + + public class BaseLoadedElement : ILoadedElement { + uint id; + public uint ID { + get { + return id; + } + } + + string name; + public string Name { + get { + return name; + } + } + + public BaseLoadedElement (uint id, string name) { + this.id = id; + this.name = name; + } + } + public class BaseLoadedClass : BaseLoadedElement, ILoadedClass { + uint size; + public uint Size { + get { + return size; + } + } + + public BaseLoadedClass (uint id, string name, uint size) : base (id, name) { + this.size = size; + } + } + public class BaseLoadedMethod<LC> : BaseLoadedElement, ILoadedMethod<LC> where LC : ILoadedClass { + LC c; + public LC Class { + get { + return c; + } + } + + public BaseLoadedMethod (uint id, LC c, string name) : base (id, name) { + this.c = c; + } + } + public class BaseUnmanagedFunctionFromRegion : IUnmanagedFunctionFromRegion { + IExecutableMemoryRegion<IUnmanagedFunctionFromRegion> region; + public IExecutableMemoryRegion<IUnmanagedFunctionFromRegion> Region { + get { + return region; + } + set { + region = value; + } + } + string name; + public string Name { + get { + return name; + } + set { + name = value; + } + } + uint startOffset; + public uint StartOffset { + get { + return startOffset; + } + set { + startOffset = value; + } + } + uint endOffset; + public uint EndOffset { + get { + return endOffset; + } + set { + endOffset = value; + } + } + public BaseUnmanagedFunctionFromRegion () { + this.region = null; + this.name = null; + this.startOffset = 0; + this.endOffset = 0; + } + public BaseUnmanagedFunctionFromRegion (IExecutableMemoryRegion<IUnmanagedFunctionFromRegion> region, string name, uint startOffset, uint endOffset) { + this.region = region; + this.name = name; + this.startOffset = startOffset; + this.endOffset = endOffset; + } + } + public class BaseUnmanagedFunctionFromID<MR,UFR>: BaseLoadedElement, IUnmanagedFunctionFromID<MR,UFR> where UFR : IUnmanagedFunctionFromRegion where MR : IExecutableMemoryRegion<UFR> { + MR region; + public MR Region { + get { + return region; + } + } + + public BaseUnmanagedFunctionFromID (uint id, string name, MR region) : base (id, name) { + this.region = region; + } + } + + public class HeapObject<LC> : IHeapObject<HeapObject<LC>,LC> where LC : ILoadedClass { + ulong id; + public ulong ID { + get { + return id; + } + } + LC c; + public LC Class { + get { + return c; + } + internal set { + c = value; + } + } + + uint size; + public uint Size { + get { + return size; + } + internal set { + size = value; + } + } + + static HeapObject<LC>[] emptyReferences = new HeapObject<LC> [0]; + public static HeapObject<LC>[] EmptyReferences { + get { + return emptyReferences; + } + } + + HeapObject<LC>[] references; + public HeapObject<LC>[] References { + get { + return references; + } + internal set { + references = value; + } + } + + HeapObject<LC>[] backReferences; + public HeapObject<LC>[] BackReferences { + get { + return backReferences; + } + internal set { + backReferences = value; + } + } + + int backReferencesCounter; + internal void IncrementBackReferences () { + backReferencesCounter ++; + } + internal void AllocateBackReferences () { + if (references != null) { + int referencesCount = 0; + foreach (HeapObject<LC> reference in references) { + if (reference != null) { + referencesCount ++; + } + } + if (referencesCount != references.Length) { + if (referencesCount > 0) { + HeapObject<LC>[] newReferences = new HeapObject<LC> [referencesCount]; + referencesCount = 0; + foreach (HeapObject<LC> reference in references) { + if (reference != null) { + newReferences [referencesCount] = reference; + referencesCount ++; + } + } + references = newReferences; + } else { + references = emptyReferences; + } + } + } else { + references = emptyReferences; + } + + if (backReferencesCounter > 0) { + backReferences = new HeapObject<LC> [backReferencesCounter]; + backReferencesCounter = 0; + } else { + references = emptyReferences; + } + } + internal void AddBackReference (HeapObject<LC> heapObject) { + backReferences [backReferencesCounter] = heapObject; + backReferencesCounter ++; + } + + public HeapObject (ulong id, LC c, uint size, HeapObject<LC>[] references) { + this.id = id; + this.c = c; + this.size = size; + this.references = references; + this.backReferences = null; + this.backReferencesCounter = 0; + } + public HeapObject (ulong id) { + this.id = id; + this.c = default(LC); + this.size = 0; + this.references = null; + this.backReferences = null; + this.backReferencesCounter = 0; + } + } + + public abstract class BaseHeapSnapshot<HO,LC> : IHeapSnapshot<HeapObject<LC>,LC> where LC : ILoadedClass { + Dictionary<ulong,HeapObject<LC>> heap; + bool backReferencesInitialized; + + uint collection; + public uint Collection { + get { + return collection; + } + } + + ulong startCounter; + public ulong StartCounter { + get { + return startCounter; + } + } + DateTime startTime; + public DateTime StartTime { + get { + return startTime; + } + } + ulong endCounter; + public ulong EndCounter { + get { + return endCounter; + } + } + DateTime endTime; + public DateTime EndTime { + get { + return endTime; + } + } + + public HeapObject<LC> NewHeapObject (ulong id, LC c, uint size, ulong[] referenceIds, int referencesCount) { + if (backReferencesInitialized) { + throw new Exception ("Cannot create heap objects after backReferencesInitialized is true"); + } + + if (recordSnapshot) { + HeapObject<LC>[] references = new HeapObject<LC>[referencesCount]; + HeapObject<LC> result = GetOrCreateHeapObject (id); + for (int i = 0; i < references.Length; i++) { + references [i] = GetOrCreateHeapObject (referenceIds [i]); + references [i].IncrementBackReferences (); + } + result.References = references; + result.Size = size; + result.Class = c; + return result; + } else { + return null; + } + } + + public void InitializeBackReferences () { + if (backReferencesInitialized) { + throw new Exception ("Cannot call InitializeBackReferences twice"); + } + + //FIXME: Bad objects should not happen anymore... + Dictionary<ulong,HeapObject<LC>> badObjects = new Dictionary<ulong,HeapObject<LC>> (); + + foreach (HeapObject<LC> heapObject in heap.Values) { + if (heapObject.Class != null) { + heapObject.AllocateBackReferences (); + } else { + badObjects.Add (heapObject.ID, heapObject); + } + } + + foreach (ulong id in badObjects.Keys) { + heap.Remove (id); + } + + foreach (HeapObject<LC> heapObject in heap.Values) { + foreach (HeapObject<LC> reference in heapObject.References) { + reference.AddBackReference (heapObject); + } + } + + backReferencesInitialized = true; + } + + HeapObject<LC> GetOrCreateHeapObject (ulong id) { + if (recordSnapshot) { + if (heap.ContainsKey (id)) { + return heap [id]; + } else { + HeapObject<LC> result = new HeapObject<LC> (id); + heap [id] = result; + return result; + } + } else { + return null; + } + } + + public HeapObject<LC> GetHeapObject (ulong id) { + return heap [id]; + } + + public HeapObject<LC>[] HeapObjects { + get { + HeapObject<LC>[] result = new HeapObject<LC> [heap.Values.Count]; + heap.Values.CopyTo (result, 0); + return result; + } + } + + bool recordSnapshot; + public bool RecordSnapshot { + get { + return recordSnapshot; + } + } + + public BaseHeapSnapshot (uint collection, ulong startCounter, DateTime startTime, ulong endCounter, DateTime endTime, bool recordSnapshot) { + this.collection = collection; + this.startCounter = startCounter; + this.startTime = startTime; + this.endCounter = endCounter; + this.endTime = endTime; + this.recordSnapshot = recordSnapshot; + heap = new Dictionary<ulong,HeapObject<LC>> (); + backReferencesInitialized = false; + } + } + + public struct AllocationClassData<LC> where LC : ILoadedClass { + LC c; + public LC Class { + get { + return c; + } + } + uint reachableInstances; + public uint ReachableInstances { + get { + return reachableInstances; + } + } + uint reachableBytes; + public uint ReachableBytes { + get { + return reachableBytes; + } + } + uint unreachableInstances; + public uint UnreachableInstances { + get { + return unreachableInstances; + } + } + uint unreachableBytes; + public uint UnreachableBytes { + get { + return unreachableBytes; + } + } + + public static Comparison<AllocationClassData<LC>> CompareByReachableBytes = delegate (AllocationClassData<LC> a, AllocationClassData<LC> b) { + return a.ReachableBytes.CompareTo (b.ReachableBytes); + }; + public static Comparison<AllocationClassData<LC>> CompareByReachableInstances = delegate (AllocationClassData<LC> a, AllocationClassData<LC> b) { + return a.ReachableInstances.CompareTo (b.ReachableInstances); + }; + + public AllocationClassData (LC c, uint reachableInstances, uint reachableBytes, uint unreachableInstances, uint unreachableBytes) { + this.c = c; + this.reachableInstances = reachableInstances; + this.reachableBytes = reachableBytes; + this.unreachableInstances = unreachableInstances; + this.unreachableBytes = unreachableBytes; + } + } + + public class BaseAllocationSummary<LC> where LC : ILoadedClass { + uint collection; + public uint Collection { + get { + return collection; + } + } + List<AllocationClassData<LC>> data; + public AllocationClassData<LC>[] Data { + get { + AllocationClassData<LC>[] result = data.ToArray (); + Array.Sort (result, AllocationClassData<LC>.CompareByReachableBytes); + Array.Reverse (result); + return result; + } + } + + internal void RecordData (LC c, uint reachableInstances, uint reachableBytes, uint unreachableInstances, uint unreachableBytes) { + data.Add (new AllocationClassData<LC> (c, reachableInstances, reachableBytes, unreachableInstances, unreachableBytes)); + } + + ulong startCounter; + public ulong StartCounter { + get { + return startCounter; + } + } + DateTime startTime; + public DateTime StartTime { + get { + return startTime; + } + } + ulong endCounter; + public ulong EndCounter { + get { + return endCounter; + } + internal set { + endCounter = value; + } + } + DateTime endTime; + public DateTime EndTime { + get { + return endTime; + } + internal set { + endTime = value; + } + } + + public BaseAllocationSummary (uint collection, ulong startCounter, DateTime startTime) { + this.collection = collection; + this.startCounter = startCounter; + this.startTime = startTime; + this.endCounter = startCounter; + this.endTime = startTime; + data = new List<AllocationClassData<LC>> (); + } + } + + public class BaseProfilerEventHandler<LC,LM,UFR,UFI,MR,EH,HO,HS> : IProfilerEventHandler<LC,LM,UFR,UFI,MR,EH,HO,HS> where LC : ILoadedClass where LM : ILoadedMethod<LC> where UFR : IUnmanagedFunctionFromRegion where UFI : IUnmanagedFunctionFromID<MR,UFR> where MR : IExecutableMemoryRegion<UFR> where EH : ILoadedElementHandler<LC,LM,UFR,UFI,MR,HO,HS> where HO: IHeapObject<HO,LC> where HS: IHeapSnapshot<HO,LC> { + EH loadedElements; + public EH LoadedElements { + get { + return loadedElements; + } + } + + public BaseProfilerEventHandler (EH loadedElements) { + this.loadedElements = loadedElements; + } + + public virtual void Start (uint version, string runtimeFile, ProfilerFlags flags, ulong startCounter, DateTime startTime) {} + public virtual void End (uint version, ulong endCounter, DateTime endTime) {} + + public virtual void StartBlock (ulong startCounter, DateTime startTime, ulong threadId) {} + public virtual void EndBlock (ulong endCounter, DateTime endTime, ulong threadId) {} + + public virtual void ModuleLoaded (ulong threadId, ulong startCounter, ulong endCounter, string name, bool success) {} + public virtual void ModuleUnloaded (ulong threadId, ulong startCounter, ulong endCounter, string name) {} + public virtual void AssemblyLoaded (ulong threadId, ulong startCounter, ulong endCounter, string name, bool success) {} + public virtual void AssemblyUnloaded (ulong threadId, ulong startCounter, ulong endCounter, string name) {} + public virtual void ApplicationDomainLoaded (ulong threadId, ulong startCounter, ulong endCounter, string name, bool success) {} + public virtual void ApplicationDomainUnloaded (ulong threadId, ulong startCounter, ulong endCounter, string name) {} + + public virtual void SetCurrentThread (ulong threadId) {} + + public virtual void ClassStartLoad (LC c, ulong counter) {} + public virtual void ClassEndLoad (LC c, ulong counter, bool success) {} + public virtual void ClassStartUnload (LC c, ulong counter) {} + public virtual void ClassEndUnload (LC c, ulong counter) {} + + public virtual void Allocation (LC c, uint size) {} + public virtual void Exception (LC c, ulong counter) {} + + public virtual void MethodEnter (LM m, ulong counter) {} + public virtual void MethodExit (LM m, ulong counter) {} + public virtual void MethodJitStart (LM m, ulong counter) {} + public virtual void MethodJitEnd (LM m, ulong counter, bool success) {} + public virtual void MethodFreed (LM m, ulong counter) {} + + public virtual void MethodStatisticalHit (LM m) {} + public virtual void UnknownMethodStatisticalHit () {} + public virtual void UnmanagedFunctionStatisticalHit (UFR f) {} + public virtual void UnmanagedFunctionStatisticalHit (UFI f) {} + public virtual void UnknownUnmanagedFunctionStatisticalHit (MR region, uint offset) {} + public virtual void UnknownUnmanagedFunctionStatisticalHit (ulong address) {} + public virtual void StatisticalCallChainStart (uint chainDepth) {} + + public virtual void ThreadStart (ulong threadId, ulong counter) {} + public virtual void ThreadEnd (ulong threadId, ulong counter) {} + + public virtual void GarbageCollectionStart (uint collection, uint generation, ulong counter) {} + public virtual void GarbageCollectionEnd (uint collection, uint generation, ulong counter) {} + public virtual void GarbageCollectionMarkStart (uint collection, uint generation, ulong counter) {} + public virtual void GarbageCollectionMarkEnd (uint collection, uint generation, ulong counter) {} + public virtual void GarbageCollectionSweepStart (uint collection, uint generation, ulong counter) {} + public virtual void GarbageCollectionSweepEnd (uint collection, uint generation, ulong counter) {} + public virtual void GarbageCollectionResize (uint collection, ulong newSize) {} + public virtual void GarbageCollectionStopWorldStart (uint collection, uint generation, ulong counter) {} + public virtual void GarbageCollectionStopWorldEnd (uint collection, uint generation, ulong counter) {} + public virtual void GarbageCollectionStartWorldStart (uint collection, uint generation, ulong counter) {} + public virtual void GarbageCollectionStartWorldEnd (uint collection, uint generation, ulong counter) {} + + public virtual void HeapReportStart (HS snapshot) {} + public virtual void HeapObjectUnreachable (LC c, uint size) {} + public virtual void HeapObjectReachable (HO o) {} + public virtual void HeapReportEnd (HS snapshot) {} + + public virtual void AllocationSummaryStart (uint collection, ulong startCounter, DateTime startTime) {} + public virtual void ClassAllocationSummary (LC c, uint reachableInstances, uint reachableBytes, uint unreachableInstances, uint unreachableBytes) {} + public virtual void AllocationSummaryEnd (uint collection, ulong endCounter, DateTime endTime) {} + } + + public class BaseExecutableMemoryRegion<UFR> : BaseLoadedElement, IExecutableMemoryRegion<UFR> where UFR : IUnmanagedFunctionFromRegion, new() { + uint fileOffset; + public uint FileOffset { + get { + return fileOffset; + } + } + + ulong startAddress; + public ulong StartAddress { + get { + return startAddress; + } + } + + ulong endAddress; + public ulong EndAddress { + get { + return endAddress; + } + } + + List<UFR> functions; + + public UFR NewFunction (string name, uint offset) { + UFR result = new UFR (); + result.Name = name; + result.StartOffset = offset; + result.EndOffset = offset; + functions.Add (result); + return result; + } + + public UFR GetFunction (uint offset) { + int lowIndex = 0; + int highIndex = functions.Count; + int middleIndex = lowIndex + ((highIndex - lowIndex) / 2); + UFR middleFunction = (middleIndex < functions.Count) ? functions [middleIndex] : default (UFR); + + while (lowIndex != highIndex) { + if (middleFunction.StartOffset > offset) { + if (middleIndex > 0) { + highIndex = middleIndex; + } else { + return default (UFR); + } + } else if (middleFunction.EndOffset < offset) { + if (middleIndex < functions.Count - 1) { + lowIndex = middleIndex; + } else { + return default (UFR); + } + } else { + return middleFunction; + } + + middleIndex = lowIndex + ((highIndex - lowIndex) / 2); + middleFunction = functions [middleIndex]; + } + + if ((middleFunction == null) || (middleFunction.StartOffset > offset) || (middleFunction.EndOffset < offset)) { + return default (UFR); + } else { + return middleFunction; + } + } + + public UFR[] Functions { + get { + UFR[] result = new UFR [functions.Count]; + functions.CopyTo (result); + return result; + } + } + + public static Comparison<UFR> CompareByStartOffset = delegate (UFR a, UFR b) { + return a.StartOffset.CompareTo (b.StartOffset); + }; + public void SortFunctions () { + functions.Sort (CompareByStartOffset); + if (functions.Count > 0) { + UFR previousFunction = functions [0]; + for (int i = 1; i < functions.Count; i++) { + UFR currentFunction = functions [i]; + previousFunction.EndOffset = currentFunction.StartOffset - 1; + previousFunction = currentFunction; + } + previousFunction.EndOffset = (uint) (EndAddress - StartAddress); + } + } + + public BaseExecutableMemoryRegion (uint id, string name, uint fileOffset, ulong startAddress, ulong endAddress) : base (id, name) { + this.fileOffset = fileOffset; + this.startAddress = startAddress; + this.endAddress = endAddress; + functions = new List<UFR> (); + + NativeLibraryReader.FillFunctions<BaseExecutableMemoryRegion<UFR>,UFR> (this); + } + } + + public class LoadedElementHandler<LC,LM,UFR,UFI,MR,HO,HS> : ILoadedElementHandler<LC,LM,UFR,UFI,MR,HO,HS> where LC : ILoadedClass where LM : ILoadedMethod<LC> where UFR : IUnmanagedFunctionFromRegion where UFI : IUnmanagedFunctionFromID<MR,UFR> where MR : IExecutableMemoryRegion<UFR> where HO: IHeapObject<HO,LC> where HS: IHeapSnapshot<HO,LC> { + ILoadedElementFactory<LC,LM,UFR,UFI,MR,HO,HS> factory; + + int loadedClassesCount; + LC[] loadedClasses; + public LC[] Classes { + get { + LC[] result = new LC [loadedClassesCount]; + int resultIndex = 0; + for (int i = 0; i < loadedClasses.Length; i++) { + LC c = loadedClasses [i]; + if (c != null) { + result [resultIndex] = c; + resultIndex ++; + } + } + return result; + } + } + public LC GetClass (uint id) { + return loadedClasses [(int) id]; + } + + int loadedMethodsCount; + LM[] loadedMethods; + public LM[] Methods { + get { + LM[] result = new LM [loadedMethodsCount]; + int resultIndex = 0; + for (int i = 0; i < loadedMethods.Length; i++) { + LM m = loadedMethods [i]; + if (m != null) { + result [resultIndex] = m; + resultIndex ++; + } + } + return result; + } + } + public LM GetMethod (uint id) { + return loadedMethods [(int) id]; + } + + Dictionary<uint,MR> memoryRegions; + List<MR> sortedMemoryRegions; + public MR[] ExecutableMemoryRegions { + get { + MR[] result = new MR [memoryRegions.Count]; + memoryRegions.Values.CopyTo (result, 0); + return result; + } + } + public MR GetExecutableMemoryRegion (uint id) { + return memoryRegions [id]; + } + public MR GetExecutableMemoryRegion (ulong address) { + int lowIndex = 0; + int highIndex = sortedMemoryRegions.Count; + int middleIndex = lowIndex + ((highIndex - lowIndex) / 2); + MR middleRegion = (middleIndex < sortedMemoryRegions.Count) ? sortedMemoryRegions [middleIndex] : default (MR); + + while (lowIndex != highIndex) { + if (middleRegion.StartAddress > address) { + if (middleIndex > 0) { + highIndex = middleIndex; + } else { + return default (MR); + } + } else if (middleRegion.EndAddress < address) { + if (middleIndex < sortedMemoryRegions.Count - 1) { + lowIndex = middleIndex; + } else { + return default (MR); + } + } else { + return middleRegion; + } + + middleIndex = lowIndex + ((highIndex - lowIndex) / 2); + middleRegion = sortedMemoryRegions [middleIndex]; + } + + if ((middleRegion == null) || (middleRegion.StartAddress > address) || (middleRegion.EndAddress < address)) { + return default (MR); + } else { + return middleRegion; + } + } + public void InvalidateExecutableMemoryRegion (uint id) { + MR region = GetExecutableMemoryRegion (id); + if (region != null) { + sortedMemoryRegions.Remove (region); + } + } + static Comparison<MR> CompareByStartAddress = delegate (MR a, MR b) { + return a.StartAddress.CompareTo (b.StartAddress); + }; + public void SortExecutableMemoryRegions () { + sortedMemoryRegions.Sort (CompareByStartAddress); + } + + public LC NewClass (uint id, string name, uint size) { + LC result = factory.NewClass (id, name, size); + if (loadedClasses.Length <= id) { + LC[] newLoadedClasses = new LC [((int) id + 1) * 2]; + loadedClasses.CopyTo (newLoadedClasses, 0); + loadedClasses = newLoadedClasses; + } + loadedClasses [(int) id] = result; + loadedClassesCount ++; + return result; + } + + public LM NewMethod (uint id, LC c, string name) { + LM result = factory.NewMethod (id, c, name); + if (loadedMethods.Length <= id) { + LM[] newLoadedMethods = new LM [((int) id + 1) * 2]; + loadedMethods.CopyTo (newLoadedMethods, 0); + loadedMethods = newLoadedMethods; + } + loadedMethods [(int) id] = result; + loadedMethodsCount ++; + return result; + } + + public MR NewExecutableMemoryRegion (uint id, string fileName, uint fileOffset, ulong startAddress, ulong endAddress) { + MR result = factory.NewExecutableMemoryRegion (id, fileName, fileOffset, startAddress, endAddress); + memoryRegions.Add (id, result); + sortedMemoryRegions.Add (result); + return result; + } + + List<HS> heapSnapshots; + public HS NewHeapSnapshot (uint collection, ulong startCounter, DateTime startTime, ulong endCounter, DateTime endTime, LC[] initialAllocations, bool recordSnapshot) { + HS result = factory.NewHeapSnapshot (collection, startCounter, startTime, endCounter, endTime, initialAllocations, recordSnapshot); + heapSnapshots.Add (result); + return result; + } + public HS[] HeapSnapshots { + get { + HS[] result = new HS [heapSnapshots.Count]; + heapSnapshots.CopyTo (result); + return result; + } + } + + int unmanagedFunctionsByIDCount; + UFI[] unmanagedFunctionsByID; + public UFI[] UnmanagedFunctionsByID { + get { + UFI[] result = new UFI [unmanagedFunctionsByIDCount]; + int resultIndex = 0; + for (int i = 0; i < unmanagedFunctionsByID.Length; i++) { + UFI f = unmanagedFunctionsByID [i]; + if (f != null) { + result [resultIndex] = f; + resultIndex ++; + } + } + return result; + } + } + public UFI GetUnmanagedFunctionByID (uint id) { + return unmanagedFunctionsByID [(int) id]; + } + public UFI NewUnmanagedFunction (uint id, string name, MR region) { + UFI result = factory.NewUnmanagedFunction (id, name, region); + if (unmanagedFunctionsByID.Length <= id) { + UFI[] newUnmanagedFunctionsByID = new UFI [((int) id + 1) * 2]; + unmanagedFunctionsByID.CopyTo (newUnmanagedFunctionsByID, 0); + unmanagedFunctionsByID = newUnmanagedFunctionsByID; + } + unmanagedFunctionsByID [(int) id] = result; + unmanagedFunctionsByIDCount ++; + return result; + } + + public bool RecordHeapSnapshots { + get { + return factory.RecordHeapSnapshots; + } + set { + factory.RecordHeapSnapshots = value; + } + } + + public LoadedElementHandler (ILoadedElementFactory<LC,LM,UFR,UFI,MR,HO,HS> factory) { + this.factory = factory; + loadedClasses = new LC [1000]; + loadedClassesCount = 0; + loadedMethods = new LM [5000]; + loadedMethodsCount = 0; + memoryRegions = new Dictionary<uint,MR> (); + sortedMemoryRegions = new List<MR> (); + heapSnapshots = new List<HS> (); + unmanagedFunctionsByID = new UFI [1000]; + unmanagedFunctionsByIDCount = 0; + } + } +} diff --git a/Mono.Profiler/profiler-decoder-library/ChangeLog b/Mono.Profiler/profiler-decoder-library/ChangeLog new file mode 100644 index 00000000..4c0b8a09 --- /dev/null +++ b/Mono.Profiler/profiler-decoder-library/ChangeLog @@ -0,0 +1,168 @@ +2008-08-05 Massimiliano Mantione <massi@ximian.com> + * Reader.cs: If the file is truncated "cleanly" do not throw exceptions. + +2008-08-04 Massimiliano Mantione <massi@ximian.com> + Removed .pc file generation. + +2008-08-01 Massimiliano Mantione <massi@ximian.com> + * Makefile.am, profiler-decoder-library.mdp: renamed output assembly. + +2008-08-01 Massimiliano Mantione <massi@ximian.com> + Makefile.am, profiler-decoder-library.pc.in: Regenerated from MonoDevelop. + +2008-07-28 Massimiliano Mantione <massi@ximian.com> + * Decoder.cs: Fixed decoding for the "allocation summaries" feature. + +2008-06-18 Massimiliano Mantione <massi@ximian.com> + * ObjectModel.cs (StackFrame, CallStack): Keep track of stack depth. + +2008-06-18 Massimiliano Mantione <massi@ximian.com> + * ObjectModel.cs (CallStack): Added explicit thread id. + * EventProcessor.cs: likewise. + +2008-06-18 Massimiliano Mantione <massi@ximian.com> + * BaseTypes.cs: Initial implementation of allocation summaries. + * Decoder.cs: likewise. + * ObjectModel.cs: likewise. + * EventProcessor.cs: likewise. + +2008-05-23 Massimiliano Mantione <massi@ximian.com> + * BaseTypes.cs (IProfilerEventHandler): Added StatisticalCallChainStart + method to handle call chains. + * Decoder.cs (StatisticalCode): Added CALL_CHAIN. + (Decode): Decode call chains (calling StatisticalCallChainStart). + * ObjectModel.cs: Added caller and callee data to IStatisticalHitItem. + * EventProcessor.cs: Use all of the above to handle call chains. + +2008-05-12 Massimiliano Mantione <massi@ximian.com> + * BaseTypes.cs: Added a flag to ILoadedElementFactory and + IHeapSnapshot to specify if the snapshot objects must be created + in memory or not, so that memory is not wasted if the user is only + interested in the other profiling info. + * Decoder.cs: Likewise. + * ObjectModel.cs: Likewise, and cleaned up "heap-desc" functionality. + * EventProcessor.cs: Likewise. + +2008-05-12 Massimiliano Mantione <massi@ximian.com> + * BaseTypes.cs: Added support for garbage collection counter. + * Decoder.cs: Likewise. + * ObjectModel.cs: Likewise. + * EventProcessor.cs: Likewise, and added support for garbage collection + statistics in general (GcStatistics class and gc events). + +2008-05-05 Massimiliano Mantione <massi@ximian.com> + * Decoder.cs: Fixed stupid decoding bug. + * BaseTypes.cs: Changed FIXME comment. + +2008-05-02 Massimiliano Mantione <massi@ximian.com> + * Decoder.cs: Modified the Decode method to recognize the new way + in which the unmanaged symbols are emitted. + * EventProcessor.cs: Likewise, and added accounting of unrecognized + symbols (statistical hits not found on symbol tables). + * ObjectModel.cs: Similar changes to handle unmanaged symbols. + * BaseTypes.cs: Likewise. + +2008-04-16 Massimiliano Mantione <massi@ximian.com> + * ObjectModel.cs: Added heap object set comparison operation. + +2008-04-16 Massimiliano Mantione <massi@ximian.com> + * BaseTypes.cs (InitializeBackReferences): temporary workaround for + snapshot issue (it seems we get "false" references). + +2008-04-16 Massimiliano Mantione <massi@ximian.com> + * EventProcessor.cs: removed commented code. + +2008-04-16 Massimiliano Mantione <massi@ximian.com> + * Reader (SeekableLogFileReader): Add block timestamps. + +2008-04-16 Massimiliano Mantione <massi@ximian.com> + * Decoder.cs (BlockData): Cope with new timestamp in header. + +2008-04-16 Massimiliano Mantione <massi@ximian.com> + * BaseTypes.cs: Fix spelling of "backReferencesInitialized". + +2008-04-15 Massimiliano Mantione <massi@ximian.com> + * ObjectModel.cs (HeapObjectSet): Added "AllocatedBytes" property. + +2008-04-13 Massimiliano Mantione <massi@ximian.com> + * Reader.cs (SeekableLogFileReader): Implemented a new kind of reader + that simply scans the file and builds a summary of all the blocks and + their file offsets, but reads no data (each block can be read later). + +2008-04-13 Massimiliano Mantione <massi@ximian.com> + * Decoder.cs (BlockData): Added methods to decode the block header. + * Reader.cs (ReadBlock): Use the new methods to decode the block header. + +2008-04-13 Massimiliano Mantione <massi@ximian.com> + * ObjectModel.cs: Completed heap object sets and basic filters. + +2008-04-13 Massimiliano Mantione <massi@ximian.com> + * BaseTypes.cs: Made start and end times proper "DateTime" values. + instead of ambiguous "ulong" counters. + * ObjectModel.cs: likewise. + * EventProcessor.cs: likewise. + * Decoder.cs: likewise. + +2008-04-13 Massimiliano Mantione <massi@ximian.com> + * ObjectModel.cs: Initial implementation of heap object sets. + +2008-04-11 Massimiliano Mantione <massi@ximian.com> + * ObjectModel.cs: Made HeapSnapshot non generic (since it is in the + specific object model keeping it generic was pointless). + * EventProcessor.cs: Likewise. + +2008-04-11 Massimiliano Mantione <massi@ximian.com> + * BaseTypes.cs (ObjextModel.ctor): Added explicit assignments. + +2008-04-11 Massimiliano Mantione <massi@ximian.com> + * BaseTypes.cs: Added back references tracking to HeapObject, and code + to build them to BaseHeapSnapshot. + * EventProcessor.cs (HeapReportEnd): Do back references initialization. + +2008-04-10 Massimiliano Mantione <massi@ximian.com> + * BaseTypes.cs: Renamed HeapSnapshot to BaseHeapSnapshot because + this should be extended, and added a "initialAllocations" argument + to the NewHeapSnapshot factory method to allow the initialization + of heap summary data. + * Decoder.cs: pass "initialAllocations" to the new snapshot. + * EventProcessor.cs: Added new HeapSnapshot class which provides + desc-heap like reporting facilities. + +2008-03-19 Massimiliano Mantione <massi@ximian.com> + NativeLibraryReader.cs: Redirect stderr to hide it. + +2008-03-19 Massimiliano Mantione <massi@ximian.com> + EventProcessor.cs: Added ProfilerEventHandler.clicksToSeconds method. + +2008-03-19 Massimiliano Mantione <massi@ximian.com> + * Decoder.cs: Uncommented LogLine method but made it public to + avoid warnings. + +2008-03-11 Massimiliano Mantione <massi@ximian.com> + BaseTypes.cs, Decoder.cs, EventProcessor.cs, NativeLibraryReader.cs, + ObjectModel.cs: Added heap profiling decoding, and changed statistical + information format to allow further extensibility. + +2008-03-04 Massimiliano Mantione <massi@ximian.com> + * Decoder.cs: changed out parameters into return values, and + commented out tracing code for performance. + * Reader.cs: Support recycling byte[] buffers. + +2008-02-28 Massimiliano Mantione <massi@ximian.com> + * BaseTypes.cs: LoadedElementHandler: changed loadedClasses and + loadedMethods hash tables into arrays. + +2008-02-28 Massimiliano Mantione <massi@ximian.com> + * BaseTypes.cs: added support for gc start-stop world events. + * Decode.cs: likewise. + +2008-01-11 Massimiliano Mantione <massi@ximian.com> + * AssemblyInfo.cs: Added (first code dump). + * NativeLibraryReader.cs: Added (first code dump). + * BaseTypes.cs: Added (first code dump). + * EventProcessor.cs: Added (first code dump). + * Reader.cs: Added (first code dump). + * Decoder.cs: Added (first code dump). + * ObjectModel.cs: Added (first code dump). + * profiler-decoder-library.mdp: Added (first code dump). + * ChangeLog: Added (first code dump). diff --git a/Mono.Profiler/profiler-decoder-library/Decoder.cs b/Mono.Profiler/profiler-decoder-library/Decoder.cs new file mode 100644 index 00000000..cb852725 --- /dev/null +++ b/Mono.Profiler/profiler-decoder-library/Decoder.cs @@ -0,0 +1,904 @@ +// Author: +// Massimiliano Mantione (massi@ximian.com) +// +// (C) 2008 Novell, Inc http://www.novell.com +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; + +namespace Mono.Profiler { + [FlagsAttribute] + public enum ProfilerFlags { + APPDOMAIN_EVENTS = 1 << 0, + ASSEMBLY_EVENTS = 1 << 1, + MODULE_EVENTS = 1 << 2, + CLASS_EVENTS = 1 << 3, + JIT_COMPILATION = 1 << 4, + INLINING = 1 << 5, + EXCEPTIONS = 1 << 6, + ALLOCATIONS = 1 << 7, + GC = 1 << 8, + THREADS = 1 << 9, + REMOTING = 1 << 10, + TRANSITIONS = 1 << 11, + ENTER_LEAVE = 1 << 12, + COVERAGE = 1 << 13, + INS_COVERAGE = 1 << 14, + STATISTICAL = 1 << 15, + METHOD_EVENTS = 1 << 16 + } + + public enum BlockCode { + INTRO = 1, + END = 2, + MAPPING = 3, + LOADED = 4, + UNLOADED = 5, + EVENTS = 6, + STATISTICAL = 7, + HEAP_DATA = 8, + HEAP_SUMMARY = 9 + } + + public class BlockData { + static TextWriter debugLog = null; + public static TextWriter DebugLog { + get { + return debugLog; + } + set { + debugLog = value; + } + } + + public void LogLine (string format, params object[] args) { + if (debugLog != null) { + debugLog.WriteLine (format, args); + } + } + + static readonly DateTime epoch = new DateTime (1970, 1, 1, 0, 0, 0, 0); + static TimeSpan microsecondsToTimeSpan (ulong microseconds) { + return TimeSpan.FromTicks ((long) microseconds * 10); + } + static DateTime microsecondsFromEpochToDateTime (ulong microseconds) { + return epoch + microsecondsToTimeSpan (microseconds); + } + + public readonly static int BLOCK_HEADER_SIZE = 10; + + public static BlockCode DecodeHeaderBlockCode (byte[] header) { + if (header.Length == BLOCK_HEADER_SIZE) { + return (BlockCode) (header [0] | (header [1] << 8)); + } else { + throw new Exception (String.Format ("Wrong header length (0)", header.Length)); + } + } + public static int DecodeHeaderBlockLength (byte[] header) { + if (header.Length == BLOCK_HEADER_SIZE) { + return header [2] | (header [3] << 8) | (header [4] << 16) | (header [5] << 24); + } else { + throw new Exception (String.Format ("Wrong header length (0)", header.Length)); + } + } + public static uint DecodeHeaderBlockCounterDelta (byte[] header) { + if (header.Length == BLOCK_HEADER_SIZE) { + return (uint) (header [6] | (header [7] << 8) | (header [8] << 16) | (header [9] << 24)); + } else { + throw new Exception (String.Format ("Wrong header length (0)", header.Length)); + } + } + + uint fileOffset; + public uint FileOffset { + get { + return fileOffset; + } + } + + BlockCode code; + public BlockCode Code { + get { + return code; + } + } + int length; + public int Length { + get { + return length; + } + } + + byte[] data; + public byte Data (uint index) { + return data [index]; + } + public byte this [uint index] { + get { + return data [index]; + } + } + + const byte SEVEN_BITS_MASK = 0x7f; + const byte EIGHT_BIT_MASK = 0x80; + + byte ReadByte (ref uint offsetInBlock) { + byte result = data [offsetInBlock]; + offsetInBlock ++; + return result; + } + + uint ReadUint (ref uint offsetInBlock) { + int factor = 0; + uint r = 0; + byte v; + do { + v = data [offsetInBlock]; + r |= (((uint)(v & SEVEN_BITS_MASK)) << factor); + offsetInBlock ++; + factor += 7; + } while ((v & EIGHT_BIT_MASK) == 0); + return r; + } + + ulong ReadUlong (ref uint offsetInBlock) { + int factor = 0; + ulong r = 0; + byte v; + do { + v = data [offsetInBlock]; + r |= (((ulong)(v & SEVEN_BITS_MASK)) << factor); + offsetInBlock ++; + factor += 7; + } while ((v & EIGHT_BIT_MASK) == 0); + return r; + } + + string ReadString (ref uint offsetInBlock) { + int count = 0; + while (data [offsetInBlock + count] != 0) { + //LogLine ("Read string: data [offsetInBlock + count] is {0}", (char) data [offsetInBlock + count]); + count++; + } + + //LogLine ("Read string: count is finally {0}", count); + + //result = System.Text.Encoding.UTF8.GetString (data, (int) offsetInBlock, count); + //result = System.Text.Encoding.ASCII.GetString (data, (int) offsetInBlock, count); + System.Text.StringBuilder builder = new System.Text.StringBuilder (); + for (int i = 0; i < count; i++) { + //LogLine ("Read string: putting data [offsetInBlock + i] ({0}, i = {1}) in builder [i]", (char) data [offsetInBlock + i], i); + //builder [i] = (char) data [offsetInBlock + i]; + builder.Append ((char) data [offsetInBlock + i]); + } + offsetInBlock += (uint) (count + 1); + return builder.ToString (); + } + + enum LoadedItemInfo { + MODULE = 1, + ASSEMBLY = 2, + APPDOMAIN = 4, + SUCCESS = 8, + FAILURE = 16 + } + const int PACKED_EVENT_CODE_BITS = 3; + const byte PACKED_EVENT_CODE_MASK = ((1 << PACKED_EVENT_CODE_BITS)-1); + const int PACKED_EVENT_DATA_BITS = (8 - PACKED_EVENT_CODE_BITS); + const byte PACKED_EVENT_DATA_MASK = ((1 << PACKED_EVENT_DATA_BITS)-1); + enum PackedEventCode { + METHOD_ENTER = 1, + METHOD_EXIT_IMPLICIT = 2, + METHOD_EXIT_EXPLICIT = 3, + CLASS_ALLOCATION = 4, + METHOD_EVENT = 5, + CLASS_EVENT = 6, + OTHER_EVENT = 7 + } + enum MethodEvent { + JIT = 0, + FREED = 1, + MASK = 1 + } + MethodEvent MethodEventFromEventCode (int eventCode) { + return (MethodEvent) (eventCode & (byte) MethodEvent.MASK); + } + enum ClassEvent { + LOAD = 0, + UNLOAD = 1, + EXCEPTION = 2, + MASK = 3 + } + ClassEvent ClassEventFromEventCode (int eventCode) { + return (ClassEvent) (eventCode & (byte) ClassEvent.MASK); + } + enum EventResult { + SUCCESS = 0, + FAILURE = 4 + } + const int EVENT_RESULT_MASK = (int) EventResult.FAILURE; + enum GenericEvent { + THREAD = 1, + GC_COLLECTION = 2, + GC_MARK = 3, + GC_SWEEP = 4, + GC_RESIZE = 5, + GC_STOP_WORLD = 6, + GC_START_WORLD = 7, + MASK = 15 + } + GenericEvent GenericEventFromEventCode (int eventCode) { + return (GenericEvent) (eventCode & (byte) GenericEvent.MASK); + } + enum EventKind { + START = 0, + END = 1 + } + EventKind EventKindFromEventCode (int eventCode) { + return ((eventCode & (1<<4)) != 0) ? EventKind.END : EventKind.START; + } + bool EventSuccessFromEventCode (int eventCode) { + return ((eventCode & EVENT_RESULT_MASK) == (int) EventResult.FAILURE) ? false : true; + } + enum StatisticalCode { + END = 0, + METHOD = 1, + UNMANAGED_FUNCTION_ID = 2, + UNMANAGED_FUNCTION_NEW_ID = 3, + UNMANAGED_FUNCTION_OFFSET_IN_REGION = 4, + CALL_CHAIN = 5, + REGIONS = 7 + } + enum HeapSnapshotCode { + NONE = 0, + OBJECT = 1, + FREE_OBJECT_CLASS = 2, + MASK = 3 + } + + static void DecodeGarbageCollectionEventValue (uint eventValue, out uint collection, out uint generation) { + collection = eventValue >> 8; + generation = eventValue & 0xff; + } + + public void Decode<LC,LM,UFR,UFI,MR,EH,HO,HS> (IProfilerEventHandler<LC,LM,UFR,UFI,MR,EH,HO,HS> handler, IDataBlockRecycler blockRecycler) where LC : ILoadedClass where LM : ILoadedMethod<LC> where UFR : IUnmanagedFunctionFromRegion where UFI : IUnmanagedFunctionFromID<MR,UFR> where MR : IExecutableMemoryRegion<UFR> where EH : ILoadedElementHandler<LC,LM,UFR,UFI,MR,HO,HS> where HO: IHeapObject<HO,LC> where HS: IHeapSnapshot<HO,LC> { + try { + Decode<LC,LM,UFR,UFI,MR,EH,HO,HS> (handler); + } finally { + blockRecycler.RecycleData (data); + data = null; + } + } + + public void Decode<LC,LM,UFR,UFI,MR,EH,HO,HS> (IProfilerEventHandler<LC,LM,UFR,UFI,MR,EH,HO,HS> handler) where LC : ILoadedClass where LM : ILoadedMethod<LC> where UFR : IUnmanagedFunctionFromRegion where UFI : IUnmanagedFunctionFromID<MR,UFR> where MR : IExecutableMemoryRegion<UFR> where EH : ILoadedElementHandler<LC,LM,UFR,UFI,MR,HO,HS> where HO: IHeapObject<HO,LC> where HS: IHeapSnapshot<HO,LC> { + uint offsetInBlock = 0; + + if (data == null) { + throw new DecodingException (this, 0, "Decoding used block"); + } + + try { + //LogLine (" *** DECODING at offset {0} (code {1})", fileOffset, code); + switch (code) { + case BlockCode.INTRO : { + uint version; + string runtimeFile; + uint flags; + ulong startCounter; + ulong startTime; + + version = ReadUint (ref offsetInBlock); + runtimeFile = ReadString (ref offsetInBlock); + flags = ReadUint (ref offsetInBlock); + startCounter = ReadUlong (ref offsetInBlock); + startTime = ReadUlong (ref offsetInBlock); + //LogLine ("BLOCK INTRO: version {0}, runtimeFile {1}, flags {2}, startCounter {3}, startTime {4}", version, runtimeFile, (ProfilerFlags) flags, startCounter, startTime); + + handler.Start (version, runtimeFile, (ProfilerFlags) flags, startCounter, microsecondsFromEpochToDateTime (startTime)); + break; + } + case BlockCode.END : { + uint version; + ulong endCounter; + ulong endTime; + + version = ReadUint (ref offsetInBlock); + endCounter = ReadUlong (ref offsetInBlock); + endTime = ReadUlong (ref offsetInBlock); + //LogLine ("BLOCK END: version {0}, endCounter {1}, endTime {2}", version, endCounter, endTime); + + handler.End (version, endCounter, microsecondsFromEpochToDateTime (endTime)); + break; + } + case BlockCode.LOADED : { + byte kind = ReadByte (ref offsetInBlock); + ulong startCounter = ReadUlong (ref offsetInBlock); + ulong endCounter = ReadUlong (ref offsetInBlock); + ulong threadId = ReadUlong (ref offsetInBlock); + string itemName = ReadString (ref offsetInBlock); + + bool success = ((kind & (byte)LoadedItemInfo.SUCCESS) != 0); + kind &= (byte) (LoadedItemInfo.APPDOMAIN|LoadedItemInfo.ASSEMBLY|LoadedItemInfo.MODULE); + //LogLine ("BLOCK LOADED: kind {0}, startCounter {1}, endCounter {2}, threadId {3}, itemName {4}", (LoadedItemInfo) kind, startCounter, endCounter, threadId, itemName); + + switch ((LoadedItemInfo) kind) { + case LoadedItemInfo.APPDOMAIN: { + handler.ApplicationDomainLoaded (threadId, startCounter, endCounter, itemName, success); + break; + } + case LoadedItemInfo.ASSEMBLY: { + handler.AssemblyLoaded (threadId, startCounter, endCounter, itemName, success); + break; + } + case LoadedItemInfo.MODULE: { + handler.ModuleLoaded (threadId, startCounter, endCounter, itemName, success); + break; + } + default: { + throw new DecodingException (this, offsetInBlock, String.Format ("unknown load event kind {0}", kind)); + } + } + break; + } + case BlockCode.UNLOADED : { + byte kind = ReadByte (ref offsetInBlock); + ulong startCounter = ReadUlong (ref offsetInBlock); + ulong endCounter = ReadUlong (ref offsetInBlock); + ulong threadId = ReadUlong (ref offsetInBlock); + string itemName = ReadString (ref offsetInBlock); + + //LogLine ("BLOCK UNLOADED: kind {0}, startCounter {1}, endCounter {2}, threadId {3}, itemName {4}", (LoadedItemInfo) kind, startCounter, endCounter, threadId, itemName); + + switch ((LoadedItemInfo) kind) { + case LoadedItemInfo.APPDOMAIN: { + handler.ApplicationDomainUnloaded (threadId, startCounter, endCounter, itemName); + break; + } + case LoadedItemInfo.ASSEMBLY: { + handler.AssemblyUnloaded (threadId, startCounter, endCounter, itemName); + break; + } + case LoadedItemInfo.MODULE: { + handler.ModuleUnloaded (threadId, startCounter, endCounter, itemName); + break; + } + default: { + throw new DecodingException (this, offsetInBlock, String.Format ("unknown unload event kind {0}", kind)); + } + } + break; + } + case BlockCode.MAPPING : { + ulong startCounter = ReadUlong (ref offsetInBlock); + ulong startTime = ReadUlong (ref offsetInBlock); + ulong threadId = ReadUlong (ref offsetInBlock); + + //LogLine ("BLOCK MAPPING (START): startCounter {0}, startTime {1}, threadId {2}", startCounter, startTime, threadId); + handler.StartBlock (startCounter, microsecondsFromEpochToDateTime (startTime), threadId); + handler.SetCurrentThread (threadId); + + uint itemId; + for (itemId = ReadUint (ref offsetInBlock); itemId != 0; itemId = ReadUint (ref offsetInBlock)) { + string itemName = ReadString (ref offsetInBlock); + //LogLine ("BLOCK MAPPING (CLASS): itemId {0}, itemName {1}, size {2}", itemId, itemName, 0); + handler.LoadedElements.NewClass (itemId, itemName, 0); + } + + for (itemId = ReadUint (ref offsetInBlock); itemId != 0; itemId = ReadUint (ref offsetInBlock)) { + uint classId = ReadUint (ref offsetInBlock); + string itemName = ReadString (ref offsetInBlock); + //LogLine ("BLOCK MAPPING (METHOD): itemId {0}, classId {1}, itemName {2}, size {3}", itemId, classId, itemName, 0); + handler.LoadedElements.NewMethod (itemId, handler.LoadedElements.GetClass (classId), itemName); + } + + ulong endCounter = ReadUlong (ref offsetInBlock); + ulong endTime = ReadUlong (ref offsetInBlock); + + //LogLine ("BLOCK MAPPING (END): endCounter {0}, endTime {1}", endCounter, endTime); + handler.EndBlock (endCounter, microsecondsFromEpochToDateTime (endTime), threadId); + break; + } + case BlockCode.EVENTS : { + ulong startCounter = ReadUlong (ref offsetInBlock); + ulong startTime = ReadUlong (ref offsetInBlock); + ulong threadId = ReadUlong (ref offsetInBlock); + + //LogLine ("BLOCK EVENTS (START): startCounter {0}, startTime {1}, threadId {2}", startCounter, startTime, threadId); + handler.StartBlock (startCounter, microsecondsFromEpochToDateTime (startTime), threadId); + handler.SetCurrentThread (threadId); + + ulong baseCounter = ReadUlong (ref offsetInBlock); + + byte eventCode; + for (eventCode = ReadByte (ref offsetInBlock); eventCode != 0; eventCode = ReadByte (ref offsetInBlock)) { + PackedEventCode packedCode = (PackedEventCode) (eventCode & PACKED_EVENT_CODE_MASK); + int packedData = ((eventCode >> PACKED_EVENT_CODE_BITS) & PACKED_EVENT_DATA_MASK); + + switch (packedCode) { + case PackedEventCode.CLASS_ALLOCATION: { + uint classId = ReadUint (ref offsetInBlock); + uint classSize = ReadUint (ref offsetInBlock); + classId <<= PACKED_EVENT_DATA_BITS; + classId |= (uint) packedData; + + //LogLine ("BLOCK EVENTS (PACKED:CLASS_ALLOCATION): classId {0}, classSize {1}", classId, classSize); + handler.Allocation (handler.LoadedElements.GetClass (classId), classSize); + break; + } + case PackedEventCode.CLASS_EVENT: { + ClassEvent classEventCode = ClassEventFromEventCode (packedData); + switch (classEventCode) { + case ClassEvent.EXCEPTION: { + uint classId = ReadUint (ref offsetInBlock); + ulong counterDelta = ReadUlong (ref offsetInBlock); + baseCounter += counterDelta; + //LogLine ("BLOCK EVENTS (CLASS:EXCEPTION): classId {0}, counterDelta {1}", classId, counterDelta); + handler.Exception (handler.LoadedElements.GetClass (classId), baseCounter); + break; + } + case ClassEvent.LOAD: { + uint classId = ReadUint (ref offsetInBlock); + ulong counterDelta = ReadUlong (ref offsetInBlock); + baseCounter += counterDelta; + EventKind kind = EventKindFromEventCode (packedData); + //LogLine ("BLOCK EVENTS (CLASS:LOAD): classId {0}, classSize {1}, kind {2}", classId, counterDelta, kind); + if (kind == EventKind.START) { + handler.ClassStartLoad (handler.LoadedElements.GetClass (classId), baseCounter); + } else { + handler.ClassEndLoad (handler.LoadedElements.GetClass (classId), baseCounter, EventSuccessFromEventCode (packedData)); + } + break; + } + case ClassEvent.UNLOAD: { + uint classId = ReadUint (ref offsetInBlock); + ulong counterDelta = ReadUlong (ref offsetInBlock); + baseCounter += counterDelta; + EventKind kind = EventKindFromEventCode (packedData); + //LogLine ("BLOCK EVENTS (CLASS:UNLOAD): classId {0}, counterDelta {1}, kind {2}", classId, counterDelta, kind); + if (kind == EventKind.START) { + handler.ClassStartUnload (handler.LoadedElements.GetClass (classId), baseCounter); + } else { + handler.ClassEndUnload (handler.LoadedElements.GetClass (classId), baseCounter); + } + break; + } + default: { + throw new DecodingException (this, offsetInBlock, String.Format ("unknown class event {0}", classEventCode)); + } + } + break; + } + case PackedEventCode.METHOD_ENTER: { + uint methodId = ReadUint (ref offsetInBlock); + ulong counterDelta = ReadUlong (ref offsetInBlock); + baseCounter += counterDelta; + methodId <<= PACKED_EVENT_DATA_BITS; + methodId |= (uint) packedData; + + //LogLine ("BLOCK EVENTS (PACKED:METHOD_ENTER): methodId {0}, counterDelta {1}", methodId, counterDelta); + handler.MethodEnter (handler.LoadedElements.GetMethod (methodId), baseCounter); + break; + } + case PackedEventCode.METHOD_EXIT_EXPLICIT: { + uint methodId = ReadUint (ref offsetInBlock); + ulong counterDelta = ReadUlong (ref offsetInBlock); + baseCounter += counterDelta; + methodId <<= PACKED_EVENT_DATA_BITS; + methodId |= (uint) packedData; + + //LogLine ("BLOCK EVENTS (PACKED:METHOD_EXIT_EXPLICIT): methodId {0}, counterDelta {1}", methodId, counterDelta); + handler.MethodExit (handler.LoadedElements.GetMethod (methodId), baseCounter); + break; + } + case PackedEventCode.METHOD_EXIT_IMPLICIT: { + //LogLine ("BLOCK EVENTS (PACKED:METHOD_EXIT_IMPLICIT): counterDelta {0}", 0); + throw new DecodingException (this, offsetInBlock, "PackedEventCode.METHOD_EXIT_IMPLICIT unsupported"); + } + case PackedEventCode.METHOD_EVENT: { + MethodEvent methodEventCode = MethodEventFromEventCode (packedData); + switch (methodEventCode) { + case MethodEvent.FREED: { + uint methodId = ReadUint (ref offsetInBlock); + ulong counterDelta = ReadUlong (ref offsetInBlock); + baseCounter += counterDelta; + //LogLine ("BLOCK EVENTS (METHOD:FREED): methodId {0}, counterDelta {1}", methodId, counterDelta); + handler.MethodFreed (handler.LoadedElements.GetMethod (methodId), baseCounter); + break; + } + case MethodEvent.JIT: { + uint methodId = ReadUint (ref offsetInBlock); + ulong counterDelta = ReadUlong (ref offsetInBlock); + baseCounter += counterDelta; + EventKind kind = EventKindFromEventCode (packedData); + //LogLine ("BLOCK EVENTS (METHOD:JIT): methodId {0}, counterDelta {1}, kind {2}", methodId, counterDelta, kind); + if (kind == EventKind.START) { + handler.MethodJitStart (handler.LoadedElements.GetMethod (methodId), baseCounter); + } else { + handler.MethodJitEnd (handler.LoadedElements.GetMethod (methodId), baseCounter, EventSuccessFromEventCode (packedData)); + } + break; + } + default: { + throw new DecodingException (this, offsetInBlock, String.Format ("unknown method event {0}", methodEventCode)); + } + } + break; + } + case PackedEventCode.OTHER_EVENT: { + GenericEvent genericEventCode = GenericEventFromEventCode (packedData); + switch (genericEventCode) { + case GenericEvent.GC_COLLECTION: { + uint collection; + uint generation; + DecodeGarbageCollectionEventValue (ReadUint (ref offsetInBlock), out collection, out generation); + ulong counterDelta = ReadUlong (ref offsetInBlock); + baseCounter += counterDelta; + EventKind kind = EventKindFromEventCode (packedData); + //LogLine ("BLOCK EVENTS (OTHER:GC_COLLECTION): generation {0}, counterDelta {1}, kind {2}", generation, counterDelta, kind); + if (kind == EventKind.START) { + handler.GarbageCollectionStart (collection, generation, baseCounter); + } else { + handler.GarbageCollectionEnd (collection, generation, baseCounter); + } + break; + } + case GenericEvent.GC_MARK: { + uint collection; + uint generation; + DecodeGarbageCollectionEventValue (ReadUint (ref offsetInBlock), out collection, out generation); + ulong counterDelta = ReadUlong (ref offsetInBlock); + baseCounter += counterDelta; + EventKind kind = EventKindFromEventCode (packedData); + //LogLine ("BLOCK EVENTS (OTHER:GC_MARK): generation {0}, counterDelta {1}, kind {2}", generation, counterDelta, kind); + if (kind == EventKind.START) { + handler.GarbageCollectionMarkStart (collection, generation, baseCounter); + } else { + handler.GarbageCollectionMarkEnd (collection, generation, baseCounter); + } + break; + } + case GenericEvent.GC_SWEEP: { + uint collection; + uint generation; + DecodeGarbageCollectionEventValue (ReadUint (ref offsetInBlock), out collection, out generation); + ulong counterDelta = ReadUlong (ref offsetInBlock); + baseCounter += counterDelta; + EventKind kind = EventKindFromEventCode (packedData); + //LogLine ("BLOCK EVENTS (OTHER:GC_SWEEP): generation {0}, counterDelta {1}, kind {2}", generation, counterDelta, kind); + if (kind == EventKind.START) { + handler.GarbageCollectionSweepStart (collection, generation, baseCounter); + } else { + handler.GarbageCollectionSweepEnd (collection, generation, baseCounter); + } + break; + } + case GenericEvent.GC_RESIZE: { + ulong newSize = ReadUlong (ref offsetInBlock); + uint collection = ReadUint (ref offsetInBlock); + //LogLine ("BLOCK EVENTS (OTHER:GC_RESIZE): newSize {0}, collection {1}", newSize, collection); + handler.GarbageCollectionResize (collection, newSize); + break; + } + case GenericEvent.GC_STOP_WORLD: { + uint collection; + uint generation; + DecodeGarbageCollectionEventValue (ReadUint (ref offsetInBlock), out collection, out generation); + ulong counterDelta = ReadUlong (ref offsetInBlock); + baseCounter += counterDelta; + EventKind kind = EventKindFromEventCode (packedData); + //LogLine ("BLOCK EVENTS (OTHER:GC_STOP_WORLD): generation {0}, counterDelta {1}, kind {2}", generation, counterDelta, kind); + if (kind == EventKind.START) { + handler.GarbageCollectionStopWorldStart (collection, generation, baseCounter); + } else { + handler.GarbageCollectionStopWorldEnd (collection, generation, baseCounter); + } + break; + } + case GenericEvent.GC_START_WORLD: { + uint collection; + uint generation; + DecodeGarbageCollectionEventValue (ReadUint (ref offsetInBlock), out collection, out generation); + ulong counterDelta = ReadUlong (ref offsetInBlock); + baseCounter += counterDelta; + EventKind kind = EventKindFromEventCode (packedData); + //LogLine ("BLOCK EVENTS (OTHER:GC_START_WORLD): generation {0}, counterDelta {1}, kind {2}", generation, counterDelta, kind); + if (kind == EventKind.START) { + handler.GarbageCollectionStartWorldStart (collection, generation, baseCounter); + } else { + handler.GarbageCollectionStartWorldEnd (collection, generation, baseCounter); + } + break; + } + case GenericEvent.THREAD: { + ulong eventThreadId = ReadUlong (ref offsetInBlock); + ulong counterDelta = ReadUlong (ref offsetInBlock); + baseCounter += counterDelta; + EventKind kind = EventKindFromEventCode (packedData); + //LogLine ("BLOCK EVENTS (OTHER:THREAD): eventThreadId {0}, counterDelta {1}, kind {2}", eventThreadId, counterDelta, kind); + if (kind == EventKind.START) { + handler.ThreadStart (eventThreadId, baseCounter); + } else { + handler.ThreadEnd (eventThreadId, baseCounter); + } + break; + } + default: { + throw new DecodingException (this, offsetInBlock, String.Format ("unknown generic event {0}", genericEventCode)); + } + } + break; + } + default: { + throw new DecodingException (this, offsetInBlock, String.Format ("unknown packed event code {0}", packedCode)); + } + } + } + + ulong endCounter = ReadUlong (ref offsetInBlock); + ulong endTime = ReadUlong (ref offsetInBlock); + //LogLine ("BLOCK EVENTS (END): endCounter {0}, endTime {1}", endCounter, endTime); + handler.EndBlock (endCounter, microsecondsFromEpochToDateTime (endTime), threadId); + break; + } + case BlockCode.STATISTICAL : { + ulong startCounter = ReadUlong (ref offsetInBlock); + ulong startTime = ReadUlong (ref offsetInBlock); + + //LogLine ("BLOCK STATISTICAL (START): startCounter {0}, startTime {1}", startCounter, startTime); + handler.StartBlock (startCounter, microsecondsFromEpochToDateTime (startTime), 0); + + uint id; + for (id = ReadUint (ref offsetInBlock); id != (uint) StatisticalCode.END; id = ReadUint (ref offsetInBlock)) { + StatisticalCode statisticalCode = (StatisticalCode) (id & 7); + switch (statisticalCode) { + case StatisticalCode.METHOD: { + uint methodId = id >> 3; + //LogLine ("BLOCK STATISTICAL (METHOD): methodId {0}", methodId); + if (methodId != 0) { + handler.MethodStatisticalHit (handler.LoadedElements.GetMethod (methodId)); + } else { + handler.UnknownMethodStatisticalHit (); + } + break; + } + case StatisticalCode.UNMANAGED_FUNCTION_ID: { + uint functionId = id >> 3; + UFI function = handler.LoadedElements.GetUnmanagedFunctionByID (functionId); + handler.UnmanagedFunctionStatisticalHit (function); + break; + } + case StatisticalCode.UNMANAGED_FUNCTION_NEW_ID: { + uint regionId = id >> 3; + uint functionId = ReadUint (ref offsetInBlock); + string name = ReadString (ref offsetInBlock); + MR region = handler.LoadedElements.GetExecutableMemoryRegion (regionId); + UFI function = handler.LoadedElements.NewUnmanagedFunction (functionId, name, region); + handler.UnmanagedFunctionStatisticalHit (function); + break; + } + case StatisticalCode.UNMANAGED_FUNCTION_OFFSET_IN_REGION: { + uint regionId = id >> 3; + if (regionId != 0) { + MR region = handler.LoadedElements.GetExecutableMemoryRegion (regionId); + uint offset = ReadUint (ref offsetInBlock); + UFR function = region.GetFunction (offset); + if (function != null) { + //LogLine ("BLOCK STATISTICAL (FUNCTION): regionId {0}, offset {1}", regionId, offset); + handler.UnmanagedFunctionStatisticalHit (function); + } else { + //LogLine ("BLOCK STATISTICAL (FUNCTION): regionId {0}, unknown offset {1}", regionId, offset); + handler.UnknownUnmanagedFunctionStatisticalHit (region, offset); + } + } else { + ulong address = ReadUlong (ref offsetInBlock); + //LogLine ("BLOCK STATISTICAL (FUNCTION): unknown address {0}", address); + handler.UnknownUnmanagedFunctionStatisticalHit (address); + } + break; + } + case StatisticalCode.CALL_CHAIN: { + uint chainDepth = id >> 3; + //LogLine ("BLOCK STATISTICAL (CHAIN): starting chain of depth {0}", chainDepth); + handler.StatisticalCallChainStart (chainDepth); + break; + } + case StatisticalCode.REGIONS: { + uint regionId; + for (regionId = ReadUint (ref offsetInBlock); regionId != 0; regionId = ReadUint (ref offsetInBlock)) { + //LogLine ("BLOCK STATISTICAL (REGION): invalidated regionId {0}", regionId); + handler.LoadedElements.InvalidateExecutableMemoryRegion (regionId); + } + for (regionId = ReadUint (ref offsetInBlock); regionId != 0; regionId = ReadUint (ref offsetInBlock)) { + ulong start = ReadUlong (ref offsetInBlock); + uint size = ReadUint (ref offsetInBlock); + uint regionFileOffset = ReadUint (ref offsetInBlock); + string fileName = ReadString (ref offsetInBlock); + + //LogLine ("BLOCK STATISTICAL (REGION): added regionId {0} (fileName {1}, fileOffset {2}, start {3}, end {4}, size {5})", regionId, fileName, regionFileOffset, start, start + size, size); + handler.LoadedElements.NewExecutableMemoryRegion (regionId, fileName, regionFileOffset, start, start + size); + //MR region = handler.LoadedElements.NewExecutableMemoryRegion (regionId, fileName, fileOffset, start, start + size); + //UF[] functions = region.Functions; + //LogLine ("BLOCK STATISTICAL (REGION): in regionId {0}, got {1} functions", regionId, functions.Length); + //foreach (UF function in functions) { + //LogLine ("BLOCK STATISTICAL (REGION): in regionId {0}, got function [{1}-{2}] {3}", regionId, function.StartOffset, function.EndOffset, function.Name); + //} + } + handler.LoadedElements.SortExecutableMemoryRegions (); + break; + } + } + } + + ulong endCounter = ReadUlong (ref offsetInBlock); + ulong endTime = ReadUlong (ref offsetInBlock); + //LogLine ("BLOCK STATISTICAL (END): endCounter {0}, endTime {1}", endCounter, endTime); + handler.EndBlock (endCounter, microsecondsFromEpochToDateTime (endTime), 0); + break; + } + case BlockCode.HEAP_DATA : { + ulong jobStartCounter = ReadUlong (ref offsetInBlock); + ulong jobStartTime = ReadUlong (ref offsetInBlock); + ulong jobEndCounter = ReadUlong (ref offsetInBlock); + ulong jobEndTime = ReadUlong (ref offsetInBlock); + uint collection = ReadUint (ref offsetInBlock); + + HS snapshot = handler.LoadedElements.NewHeapSnapshot (collection, jobStartCounter, microsecondsFromEpochToDateTime (jobStartTime), jobEndCounter, microsecondsFromEpochToDateTime (jobEndTime), handler.LoadedElements.Classes, handler.LoadedElements.RecordHeapSnapshots); + handler.HeapReportStart (snapshot); + + ulong startCounter = ReadUlong (ref offsetInBlock); + ulong startTime = ReadUlong (ref offsetInBlock); + //LogLine ("BLOCK HEAP_DATA (START): ({0}:{1}-{2}:{3}) startCounter {4}, startTime {5}", jobStartCounter, jobStartTime, jobEndCounter, jobEndTime, startCounter, startTime); + handler.StartBlock (startCounter, microsecondsFromEpochToDateTime (startTime), 0); + + ulong item; + ulong[] references = new ulong [50]; + for (item = ReadUlong (ref offsetInBlock); item != 0; item = ReadUlong (ref offsetInBlock)) { + HeapSnapshotCode itemCode = (HeapSnapshotCode) (((int) item) & ((int) HeapSnapshotCode.MASK)); + //LogLine ("Got raw value {0} (code {1})", item, itemCode); + switch (itemCode) { + case HeapSnapshotCode.FREE_OBJECT_CLASS: { + uint classId = (uint) (item >> 2); + uint size = ReadUint (ref offsetInBlock); + LC c = handler.LoadedElements.GetClass (classId); + //LogLine (" Class id {0}, size {1}", classId, size); + handler.HeapObjectUnreachable (c, size); + break; + } + case HeapSnapshotCode.OBJECT: { + uint classId = ReadUint (ref offsetInBlock); + uint size = ReadUint (ref offsetInBlock); + int referencesCount = (int) ReadUint (ref offsetInBlock); + ulong objectId = item & (~ ((ulong) HeapSnapshotCode.MASK)); + //LogLine (" Object id {0}, references {1}", objectId, referencesCount); + if (references.Length < referencesCount) { + references = new ulong [referencesCount + 50]; + } + for (int i = 0; i < referencesCount; i++) { + references [i] = ReadUlong (ref offsetInBlock); + //LogLine (" reference[{0}] {1}", i, references [i]); + } + LC c = handler.LoadedElements.GetClass (classId); + HO o = snapshot.NewHeapObject (objectId, c, size, references, referencesCount); + handler.HeapObjectReachable (o); + break; + } + default: { + throw new DecodingException (this, offsetInBlock, String.Format ("unknown item code {0}", itemCode)); + } + } + } + handler.HeapReportEnd (snapshot); + + ulong endCounter = ReadUlong (ref offsetInBlock); + ulong endTime = ReadUlong (ref offsetInBlock); + //LogLine ("BLOCK HEAP_DATA (END): endCounter {0}, endTime {1}", endCounter, endTime); + handler.EndBlock (endCounter, microsecondsFromEpochToDateTime (endTime), 0); + break; + } + case BlockCode.HEAP_SUMMARY : { + ulong startCounter = ReadUlong (ref offsetInBlock); + ulong startTime = ReadUlong (ref offsetInBlock); + uint collection = ReadUint (ref offsetInBlock); + + //LogLine ("BLOCK HEAP_SUMMARY (START): ([]{0}:{1}) startCounter {4}, startTime {5}", collection, startCounter, startTime); + handler.StartBlock (startCounter, microsecondsFromEpochToDateTime (startTime), 0); + handler.AllocationSummaryStart (collection, startCounter, microsecondsFromEpochToDateTime (startTime)); + + uint id; + for (id = ReadUint (ref offsetInBlock); id != 0; id = ReadUint (ref offsetInBlock)) { + uint reachableInstances = ReadUint (ref offsetInBlock); + uint reachableBytes = ReadUint (ref offsetInBlock); + uint unreachableInstances = ReadUint (ref offsetInBlock); + uint unreachableBytes = ReadUint (ref offsetInBlock); + LC c = handler.LoadedElements.GetClass (id); + + handler.ClassAllocationSummary (c, reachableInstances, reachableBytes, unreachableInstances, unreachableBytes); + } + + ulong endCounter = ReadUlong (ref offsetInBlock); + ulong endTime = ReadUlong (ref offsetInBlock); + handler.AllocationSummaryEnd (collection, endCounter, microsecondsFromEpochToDateTime (endTime)); + //LogLine ("BLOCK HEAP_SUMMARY (END): endCounter {0}, endTime {1}", endCounter, endTime); + Console.WriteLine ("BLOCK HEAP_SUMMARY (END): endCounter {0}, endTime {1}", endCounter, endTime); + handler.EndBlock (endCounter, microsecondsFromEpochToDateTime (endTime), 0); + break; + } + default: { + throw new DecodingException (this, offsetInBlock, String.Format ("unknown block code {0}", code)); + } + } + + if (length != (int) offsetInBlock) { + throw new DecodingException (this, offsetInBlock, String.Format ("Block ended at offset {0} but its declared length is {1}", offsetInBlock, length)); + } + } catch (DecodingException e) { + throw e; + } catch (Exception e) { + throw new DecodingException(this, offsetInBlock, e.Message, e); + } + } + + public BlockData (uint fileOffset, BlockCode code, int length, byte[] data) { + this.fileOffset = fileOffset; + this.code = code; + this.length = length; + this.data = data; + } + } + + + public class FileOperationException : System.Exception { + public FileOperationException (string message) : base (message) { + } + public FileOperationException (System.Exception e) : + base (String.Format ("Exception {0}: {1}", e.GetType().FullName, e.Message)) { + } + } + + public class DecodingException : System.Exception { + BlockData data; + public BlockData FailingData { + get { + return data; + } + } + + uint offsetInBlock; + public uint OffsetInBlock { + get { + return offsetInBlock; + } + } + + public DecodingException (BlockData data, uint offsetInBlock, string message) : base (message) { + this.data = data; + this.offsetInBlock = offsetInBlock; + } + public DecodingException (BlockData data, uint offsetInBlock, string message, Exception cause) : base (message, cause) { + this.data = data; + this.offsetInBlock = offsetInBlock; + } + } +} diff --git a/Mono.Profiler/profiler-decoder-library/EventProcessor.cs b/Mono.Profiler/profiler-decoder-library/EventProcessor.cs new file mode 100644 index 00000000..122fe1dd --- /dev/null +++ b/Mono.Profiler/profiler-decoder-library/EventProcessor.cs @@ -0,0 +1,576 @@ +// Author: +// Massimiliano Mantione (massi@ximian.com) +// +// (C) 2008 Novell, Inc http://www.novell.com +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; +using System.Collections.Generic; + +namespace Mono.Profiler { + class UnknownStatisticalHitsCollector : IStatisticalHitItem { + uint statisticalHits; + public uint StatisticalHits { + get { + return statisticalHits; + } + internal set { + statisticalHits = value; + } + } + internal void IncrementStatisticalHits () { + statisticalHits ++; + } + + public string Name { + get { + return "[UNKNOWN MEMORY REGION]"; + } + } + + StatisticalHitItemCallCounts callCounts; + public bool HasCallCounts { + get { + return (callCounts != null); + } + } + public StatisticalHitItemCallCounts CallCounts { + get { + if (callCounts == null) { + callCounts = new StatisticalHitItemCallCounts (); + } + return callCounts; + } + } + + public UnknownStatisticalHitsCollector () { + statisticalHits = 0; + } + } + + public class ProfilerEventHandler : BaseProfilerEventHandler<LoadedClass,LoadedMethod,UnmanagedFunctionFromRegion,UnmanagedFunctionFromID,ExecutableMemoryRegion,LoadedElementHandler<LoadedClass,LoadedMethod,UnmanagedFunctionFromRegion,UnmanagedFunctionFromID,ExecutableMemoryRegion,HeapObject<LoadedClass>,HeapSnapshot>,HeapObject<LoadedClass>,HeapSnapshot> { + Dictionary<ulong,CallStack> perThreadStacks; + CallStack stack; + UnknownStatisticalHitsCollector unknownStatisticalHitsCollector; + + uint version; + public uint Version { + get { + return version; + } + } + + string runtimeFile; + public string RuntimeFile { + get { + return runtimeFile; + } + } + + ProfilerFlags flags; + public ProfilerFlags Flags { + get { + return flags; + } + } + + ulong startCounter; + public ulong StartCounter { + get { + return startCounter; + } + } + + DateTime startTime; + public DateTime StartTime { + get { + return startTime; + } + } + + ulong endCounter; + public ulong EndCounter { + get { + return endCounter; + } + } + + DateTime endTime; + public DateTime EndTime { + get { + return endTime; + } + } + + ulong currentCounter; + public ulong CurrentCounter { + get { + return currentCounter; + } + } + + DateTime currentTime; + public DateTime CurrentTime { + get { + return currentTime; + } + } + + double ticksPerCounterUnit; + void updateTicksPerCounterUnit () { + if (currentCounter > startCounter) { + ulong counterSpan = currentCounter - startCounter; + TimeSpan timeSpan = currentTime - startTime; + ticksPerCounterUnit = ((double)timeSpan.Ticks) / ((double)counterSpan); + } + } + void updateCounterAndTime (ulong currentCounter, DateTime currentTime) { + this.currentCounter = currentCounter; + this.currentTime = currentTime; + updateTicksPerCounterUnit (); + } + public DateTime counterToDateTime (ulong counter) { + return StartTime + TimeSpan.FromTicks ((long) (ticksPerCounterUnit * (double)(counter - StartCounter))); + } + public TimeSpan clicksToTimeSpan (ulong clicks) { + return TimeSpan.FromTicks ((long) (ticksPerCounterUnit * (double)clicks)); + } + public double clicksToSeconds (ulong clicks) { + return (ticksPerCounterUnit * (double)clicks) / TimeSpan.TicksPerSecond; + } + + public override void Start (uint version, string runtimeFile, ProfilerFlags flags, ulong startCounter, DateTime startTime) { + this.version = version; + this.runtimeFile = runtimeFile; + this.flags = flags; + this.startCounter = startCounter; + this.startTime = startTime; + } + + public override void End (uint version, ulong endCounter, DateTime endTime) { + if (this.version != version) { + throw new Exception (String.Format ("Version {0} specified at start is inconsistent witn {1} specified at end", this.version, version)); + } + this.endCounter = endCounter; + this.endTime = endTime; + updateCounterAndTime (endCounter, endTime); + } + + public override void StartBlock (ulong startCounter, DateTime startTime, ulong threadId) { + updateCounterAndTime (startCounter, startTime); + } + + public override void EndBlock (ulong endCounter, DateTime endTime, ulong threadId) { + updateCounterAndTime (endCounter, endTime); + } + + public override void ModuleLoaded (ulong threadId, ulong startCounter, ulong endCounter, string name, bool success) {} + public override void ModuleUnloaded (ulong threadId, ulong startCounter, ulong endCounter, string name) {} + public override void AssemblyLoaded (ulong threadId, ulong startCounter, ulong endCounter, string name, bool success) {} + public override void AssemblyUnloaded (ulong threadId, ulong startCounter, ulong endCounter, string name) {} + public override void ApplicationDomainLoaded (ulong threadId, ulong startCounter, ulong endCounter, string name, bool success) {} + public override void ApplicationDomainUnloaded (ulong threadId, ulong startCounter, ulong endCounter, string name) {} + + public override void SetCurrentThread (ulong threadId) { + if (perThreadStacks.ContainsKey (threadId)) { + stack = perThreadStacks [threadId]; + } else { + stack = new CallStack (threadId); + perThreadStacks.Add (threadId, stack); + } + } + + public override void ClassStartLoad (LoadedClass c, ulong counter) {} + public override void ClassEndLoad (LoadedClass c, ulong counter, bool success) {} + public override void ClassStartUnload (LoadedClass c, ulong counter) {} + public override void ClassEndUnload (LoadedClass c, ulong counter) {} + + public override void Allocation (LoadedClass c, uint size) { + LoadedMethod caller; + if ((stack != null) && (stack.StackTop != null)) { + caller = stack.StackTop.Method; + } else { + caller = null; + } + c.InstanceCreated (size, caller); + } + + public override void Exception (LoadedClass c, ulong counter) {} + + public override void MethodEnter (LoadedMethod m, ulong counter) { + stack.MethodEnter (m, counter); + } + + public override void MethodExit (LoadedMethod m, ulong counter) { + stack.MethodExit (m, counter); + } + + public override void MethodJitStart (LoadedMethod m, ulong counter) { + m.StartJit = counter; + } + + public override void MethodJitEnd (LoadedMethod m, ulong counter, bool success) { + m.JitClicks += (counter - m.StartJit); + } + + public override void MethodFreed (LoadedMethod m, ulong counter) {} + + + uint remainingCallersInChain; + IStatisticalHitItem lastCallee; + // Returns true if the hit must be counted (this is the first chain item) + bool HandleCallChain (IStatisticalHitItem caller) { + bool result; + + if (remainingCallersInChain > 0) { + remainingCallersInChain --; + if (lastCallee != null) { + //Console.WriteLine ("HandleCallChain[{0}] {1} on {2}", remainingCallersInChain, caller.Name, lastCallee.Name); + lastCallee.CallCounts.AddCaller (caller); + caller.CallCounts.AddCallee (lastCallee); + } + result = false; + } else { + //Console.WriteLine ("HandleCallChain[{0}] {1}", remainingCallersInChain, caller.Name); + result = true; + } + + lastCallee = caller; + + return result; + } + + public override void StatisticalCallChainStart (uint chainDepth) { + remainingCallersInChain = chainDepth; + //Console.WriteLine ("StatisticalCallChainStart ({0})", chainDepth); + } + + public override void MethodStatisticalHit (LoadedMethod m) { + if (HandleCallChain (m)) { + m.StatisticalHits ++; + } + } + + public override void UnmanagedFunctionStatisticalHit (UnmanagedFunctionFromRegion f) { + if (HandleCallChain (f)) { + f.StatisticalHits ++; + } + } + public override void UnmanagedFunctionStatisticalHit (UnmanagedFunctionFromID f) { + if (HandleCallChain (f)) { + f.StatisticalHits ++; + } + } + public override void UnknownUnmanagedFunctionStatisticalHit (ExecutableMemoryRegion region, uint offset) { + if (HandleCallChain (region)) { + region.IncrementStatisticalHits (); + } + } + public override void UnknownUnmanagedFunctionStatisticalHit (ulong address) { + if (HandleCallChain (unknownStatisticalHitsCollector)) { + unknownStatisticalHitsCollector.IncrementStatisticalHits (); + } + } + + public override void ThreadStart (ulong threadId, ulong counter) {} + public override void ThreadEnd (ulong threadId, ulong counter) {} + + public IStatisticalHitItem[] StatisticalHitItems { + get { + LoadedMethod[] methods = LoadedElements.Methods; + ExecutableMemoryRegion [] regions = LoadedElements.ExecutableMemoryRegions; + UnmanagedFunctionFromRegion[][] regionFunctions = new UnmanagedFunctionFromRegion [regions.Length][]; + UnmanagedFunctionFromID[] idFunctions = LoadedElements.UnmanagedFunctionsByID; + int resultIndex = 0; + for (int i = 0; i < regions.Length; i++) { + ExecutableMemoryRegion region = regions [i]; + regionFunctions [i] = region.Functions; + resultIndex += regionFunctions [i].Length; + } + IStatisticalHitItem[] result = new IStatisticalHitItem [resultIndex + methods.Length + idFunctions.Length + regions.Length + 1]; + + resultIndex = 0; + for (int i = 0; i < regions.Length; i++) { + UnmanagedFunctionFromRegion[] functions = regionFunctions [i]; + Array.ConstrainedCopy (functions, 0, result, resultIndex, functions.Length); + resultIndex += functions.Length; + } + Array.ConstrainedCopy (methods, 0, result, resultIndex, methods.Length); + resultIndex += methods.Length; + Array.ConstrainedCopy (idFunctions, 0, result, resultIndex, idFunctions.Length); + resultIndex += idFunctions.Length; + Array.ConstrainedCopy (regions, 0, result, resultIndex, regions.Length); + resultIndex += regions.Length; + result [resultIndex] = unknownStatisticalHitsCollector; + + return result; + } + } + + public class GcStatistics { + ProfilerEventHandler data; + + uint collection; + public ulong Collection { + get { + return collection; + } + } + + ulong startCounter; + public ulong StartCounter { + get { + return startCounter; + } + internal set { + startCounter = value; + } + } + ulong endCounter; + public ulong EndCounter { + get { + return endCounter; + } + internal set { + endCounter = value; + } + } + ulong markStartCounter; + public ulong MarkStartCounter { + get { + return markStartCounter; + } + internal set { + markStartCounter = value; + } + } + ulong markEndCounter; + public ulong MarkEndCounter { + get { + return markEndCounter; + } + internal set { + markEndCounter = value; + } + } + ulong sweepStartCounter; + public ulong SweepStartCounter { + get { + return sweepStartCounter; + } + internal set { + sweepStartCounter = value; + } + } + ulong sweepEndCounter; + public ulong SweepEndCounter { + get { + return sweepEndCounter; + } + internal set { + sweepEndCounter = value; + } + } + uint generation; + public uint Generation { + get { + return generation; + } + internal set { + generation = value; + } + } + ulong? newHeapSize; + public ulong? NewHeapSize { + get { + return newHeapSize; + } + internal set { + newHeapSize = value; + } + } + + public double Start { + get { + return data.clicksToSeconds (startCounter - data.StartCounter); + } + } + public double Duration { + get { + return data.clicksToSeconds (endCounter - startCounter); + } + } + public double MarkDuration { + get { + return data.clicksToSeconds (markEndCounter - markStartCounter); + } + } + public double SweepDuration { + get { + return data.clicksToSeconds (sweepEndCounter - sweepStartCounter); + } + } + + public GcStatistics (ProfilerEventHandler data, uint collection) { + this.data = data; + this.collection = collection; + startCounter = 0; + endCounter = 0; + markStartCounter = 0; + markEndCounter = 0; + sweepStartCounter = 0; + sweepEndCounter = 0; + generation = 0; + newHeapSize = null; + } + } + + List<GcStatistics> gcStatistics; + static Comparison<GcStatistics> compareGcStatisticsByAllocation = delegate (GcStatistics a, GcStatistics b) { + return a.Collection.CompareTo (b.Collection); + }; + public GcStatistics[] GarbageCollectioncStatistics { + get { + GcStatistics[] result = gcStatistics.ToArray (); + Array.Sort (result, compareGcStatisticsByAllocation); + return result; + } + } + + GcStatistics currentGcStatistics; + List<GcStatistics> pendingGcStatistics; + GcStatistics GetGcStatistics (uint collection) { + if ((currentGcStatistics != null) && (currentGcStatistics.Collection == collection)) { + return currentGcStatistics; + } else { + foreach (GcStatistics gcs in pendingGcStatistics) { + if (gcs.Collection == collection) { + GcStatistics result = gcs; + if (currentGcStatistics != null) { + pendingGcStatistics.Add (currentGcStatistics); + } + currentGcStatistics = result; + return result; + } + } + return NewGcStatistics (collection); + } + } + GcStatistics NewGcStatistics (uint collection) { + GcStatistics result = new GcStatistics (this, collection); + if (currentGcStatistics != null) { + pendingGcStatistics.Add (currentGcStatistics); + } + currentGcStatistics = result; + return result; + } + + public override void GarbageCollectionStart (uint collection, uint generation, ulong counter) { + GcStatistics gcs = NewGcStatistics (collection); + gcs.Generation = generation; + gcs.StartCounter = counter; + } + public override void GarbageCollectionEnd (uint collection, uint generation, ulong counter) { + GcStatistics gcs = GetGcStatistics (collection); + gcs.EndCounter = counter; + pendingGcStatistics.Remove (gcs); + gcStatistics.Add (gcs); + currentGcStatistics = null; + } + public override void GarbageCollectionMarkStart (uint collection, uint generation, ulong counter) { + GcStatistics gcs = GetGcStatistics (collection); + gcs.MarkStartCounter = counter; + } + public override void GarbageCollectionMarkEnd (uint collection, uint generation, ulong counter) { + GcStatistics gcs = GetGcStatistics (collection); + gcs.MarkEndCounter = counter; + } + public override void GarbageCollectionSweepStart (uint collection, uint generation, ulong counter) { + GcStatistics gcs = GetGcStatistics (collection); + gcs.SweepStartCounter = counter; + } + public override void GarbageCollectionSweepEnd (uint collection, uint generation, ulong counter) { + GcStatistics gcs = GetGcStatistics (collection); + gcs.SweepEndCounter = counter; + } + public override void GarbageCollectionResize (uint collection, ulong newSize) { + GcStatistics gcs = NewGcStatistics (collection); + gcs.NewHeapSize = newSize; + gcStatistics.Add (gcs); + currentGcStatistics = null; + } + + HeapSnapshot currentHeapSnapshot = null; + + public override void HeapReportStart (HeapSnapshot snapshot) { + currentHeapSnapshot = snapshot; + } + public override void HeapObjectUnreachable (LoadedClass c, uint size) { + c.InstanceFreed (size); + currentHeapSnapshot.HeapObjectUnreachable (c, size); + } + public override void HeapObjectReachable (HeapObject<LoadedClass> o) { + } + public override void HeapReportEnd (HeapSnapshot snapshot) { + snapshot.InitializeBackReferences (); + } + + List<AllocationSummary> allocationSummaries; + public AllocationSummary [] AllocationSummaries { + get { + return allocationSummaries.ToArray (); + } + } + AllocationSummary currentAllocationSummary; + + public override void AllocationSummaryStart (uint collection, ulong startCounter, DateTime startTime) { + currentAllocationSummary = new AllocationSummary (collection, startCounter, startTime); + } + public override void ClassAllocationSummary (LoadedClass c, uint reachableInstances, uint reachableBytes, uint unreachableInstances, uint unreachableBytes) { + if (currentAllocationSummary != null) { + currentAllocationSummary.RecordData (c, reachableInstances, reachableBytes, unreachableInstances, unreachableBytes); + } + } + public override void AllocationSummaryEnd (uint collection, ulong endCounter, DateTime endTime) { + if ((currentAllocationSummary != null) && (currentAllocationSummary.Collection == collection)) { + currentAllocationSummary.EndCounter = endCounter; + currentAllocationSummary.EndTime = endTime; + allocationSummaries.Add (currentAllocationSummary); + currentAllocationSummary = null; + } + } + + public ProfilerEventHandler () : base (new LoadedElementHandler<LoadedClass,LoadedMethod,UnmanagedFunctionFromRegion,UnmanagedFunctionFromID,ExecutableMemoryRegion,HeapObject<LoadedClass>,HeapSnapshot> (new LoadedElementFactory ())) { + perThreadStacks = new Dictionary<ulong,CallStack> (); + stack = null; + unknownStatisticalHitsCollector = new UnknownStatisticalHitsCollector (); + gcStatistics = new List<GcStatistics> (); + pendingGcStatistics = new List<GcStatistics> (); + currentGcStatistics = null; + allocationSummaries = new List<AllocationSummary> (); + currentAllocationSummary = null; + } + } +} diff --git a/Mono.Profiler/profiler-decoder-library/Makefile.am b/Mono.Profiler/profiler-decoder-library/Makefile.am new file mode 100644 index 00000000..a783624b --- /dev/null +++ b/Mono.Profiler/profiler-decoder-library/Makefile.am @@ -0,0 +1,77 @@ + +EXTRA_DIST = + +# Warning: This is an automatically generated file, do not edit! + +if ENABLE_DEBUG +ASSEMBLY_COMPILER_COMMAND = gmcs +ASSEMBLY_COMPILER_FLAGS = -noconfig -codepage:utf8 -warn:4 -optimize+ -debug -define:DEBUG +ASSEMBLY = bin/Debug/mprof-decoder-library.dll +ASSEMBLY_MDB = $(ASSEMBLY).mdb +COMPILE_TARGET = library +PROJECT_REFERENCES = +BUILD_DIR = bin/Debug + +MPROF_DECODER_LIBRARY_DLL_MDB_SOURCE=bin/Debug/mprof-decoder-library.dll.mdb +MPROF_DECODER_LIBRARY_DLL_MDB=$(BUILD_DIR)/mprof-decoder-library.dll.mdb + +endif + +if ENABLE_RELEASE +ASSEMBLY_COMPILER_COMMAND = gmcs +ASSEMBLY_COMPILER_FLAGS = -noconfig -codepage:utf8 -warn:4 -optimize+ +ASSEMBLY = bin/Release/profiler-decoder-library.dll +ASSEMBLY_MDB = +COMPILE_TARGET = library +PROJECT_REFERENCES = +BUILD_DIR = bin/Release + +MPROF_DECODER_LIBRARY_DLL_MDB= + +endif + +AL=al2 +SATELLITE_ASSEMBLY_NAME=.resources.dll + +PROGRAMFILES = \ + $(MPROF_DECODER_LIBRARY_DLL_MDB) + + +RESGEN=resgen2 + +all: $(ASSEMBLY) $(PROGRAMFILES) + +FILES = \ + AssemblyInfo.cs \ + BaseTypes.cs \ + Decoder.cs \ + EventProcessor.cs \ + NativeLibraryReader.cs \ + ObjectModel.cs \ + Reader.cs + +DATA_FILES = + +RESOURCES = + +EXTRAS = + +REFERENCES = \ + System + +DLL_REFERENCES = + +CLEANFILES = $(PROGRAMFILES) + +include $(top_srcdir)/Makefile.include + + + + +$(eval $(call emit_resgen_targets)) +$(build_xamlg_list): %.xaml.g.cs: %.xaml + xamlg '$<' + +$(ASSEMBLY) $(ASSEMBLY_MDB): $(build_sources) $(build_resources) $(build_datafiles) $(DLL_REFERENCES) $(PROJECT_REFERENCES) $(build_xamlg_list) $(build_satellite_assembly_list) + mkdir -p $(shell dirname $(ASSEMBLY)) + $(ASSEMBLY_COMPILER_COMMAND) $(ASSEMBLY_COMPILER_FLAGS) -out:$(ASSEMBLY) -target:$(COMPILE_TARGET) $(build_sources_embed) $(build_resources_embed) $(build_references_ref) diff --git a/Mono.Profiler/profiler-decoder-library/NativeLibraryReader.cs b/Mono.Profiler/profiler-decoder-library/NativeLibraryReader.cs new file mode 100644 index 00000000..120dc909 --- /dev/null +++ b/Mono.Profiler/profiler-decoder-library/NativeLibraryReader.cs @@ -0,0 +1,124 @@ +// Author: +// Massimiliano Mantione (massi@ximian.com) +// +// (C) 2008 Novell, Inc http://www.novell.com +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; +using System.Globalization; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text.RegularExpressions; + +namespace Mono.Profiler { + public class NativeLibraryReader { + static string[] RunExternalProcess (string executableName, string arguments) { + Process p = new Process (); + p.StartInfo.UseShellExecute = false; + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.RedirectStandardError = true; + + p.StartInfo.FileName = executableName; + p.StartInfo.Arguments = arguments; + + p.Start(); + // Do not wait for the child process to exit before + // reading to the end of its redirected stream. + // p.WaitForExit (); + // Read the output stream first and then wait. + string output = p.StandardOutput.ReadToEnd (); + p.WaitForExit (); + string[] outputLines = output.Split ('\n'); + + return outputLines; + } + + static Regex nmSymbolLine = new Regex ("([a-fA-F0-9]+)\\s([a-zA-Z])\\s(.*)$"); + static Regex nmFileNotFound = new Regex ("(.*)nm:(.*)No such file$"); + static Regex nmNoSymbols = new Regex ("(.*)nm:(.*)No symbols$"); + static Regex nmUnknownProblem = new Regex ("(.*)nm:(.*)$"); + + public static void FillFunctions<MR,UFR> (MR region) where UFR : IUnmanagedFunctionFromRegion where MR : IExecutableMemoryRegion <UFR> { + FillFunctionsUsingNm<MR,UFR> (region); + } + + static void FillFunctionsUsingNm<MR,UFR> (MR region) where UFR : IUnmanagedFunctionFromRegion where MR : IExecutableMemoryRegion <UFR> { + try { + string[] outputLines = RunExternalProcess ("/usr/bin/nm", "-n " + region.Name); + if (outputLines.Length == 1) { + Match m = nmFileNotFound.Match (outputLines [0]); + if (m.Success) { + return; + } + m = nmNoSymbols.Match (outputLines [0]); + if (m.Success || (outputLines [0].Length == 0)) { + outputLines = RunExternalProcess ("/usr/bin/nm", "-n -D " + region.Name); + if (outputLines.Length == 1) { + m = nmUnknownProblem.Match (outputLines [0]); + if (m.Success) { + return; + } + } + } else { + m = nmUnknownProblem.Match (outputLines [0]); + if (m.Success) { + return; + } + } + } else if (outputLines.Length == 0) { + outputLines = RunExternalProcess ("/usr/bin/nm", "-n -D " + region.Name); + } + + IUnmanagedFunctionFromRegion lastFunction = null; + foreach (string outputLine in outputLines) { + Match m = nmSymbolLine.Match (outputLine); + if (m.Success) { + ulong symbolOffset = (ulong) Int64.Parse (m.Groups [1].Value, NumberStyles.HexNumber); + String symbolType = m.Groups [2].Value; + String symbolName = m.Groups [3].Value; + + if (symbolOffset >= region.StartAddress + region.FileOffset) { + symbolOffset -= region.StartAddress; + + if (lastFunction != null) { + lastFunction.EndOffset = (uint) symbolOffset -1; + lastFunction = null; + } + + if ((symbolType == "T") || (symbolType == "t")) { + lastFunction = region.NewFunction (symbolName, (uint) symbolOffset); + } + } + } + } + if (lastFunction != null) { + lastFunction.EndOffset = (uint) (region.EndAddress - region.StartAddress); + } + } catch (Exception e) { + Console.Error.WriteLine ("Exception: {0}", e.Message); + } + } + } +} diff --git a/Mono.Profiler/profiler-decoder-library/ObjectModel.cs b/Mono.Profiler/profiler-decoder-library/ObjectModel.cs new file mode 100644 index 00000000..792710d9 --- /dev/null +++ b/Mono.Profiler/profiler-decoder-library/ObjectModel.cs @@ -0,0 +1,1061 @@ +// Author: +// Massimiliano Mantione (massi@ximian.com) +// +// (C) 2008 Novell, Inc http://www.novell.com +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; +using System.Collections.Generic; + +namespace Mono.Profiler { + public class LoadedClass : BaseLoadedClass { + uint allocatedBytes; + public uint AllocatedBytes { + get { + return allocatedBytes; + } + } + uint currentlyAllocatedBytes; + public uint CurrentlyAllocatedBytes { + get { + return currentlyAllocatedBytes; + } + } + public static Comparison<LoadedClass> CompareByAllocatedBytes = delegate (LoadedClass a, LoadedClass b) { + return a.AllocatedBytes.CompareTo (b.AllocatedBytes); + }; + + internal void InstanceCreated (uint size, LoadedMethod method) { + allocatedBytes += size; + currentlyAllocatedBytes += size; + if (method != null) { + if (allocationsPerMethod == null) { + allocationsPerMethod = new Dictionary<uint,AllocationsPerMethod> (); + } + + AllocationsPerMethod callerMethod; + if (allocationsPerMethod.ContainsKey (method.ID)) { + callerMethod = allocationsPerMethod [method.ID]; + } else { + callerMethod = new AllocationsPerMethod (method); + allocationsPerMethod.Add (method.ID, callerMethod); + } + callerMethod.AllocatedBytes += size; + } + } + + internal void InstanceFreed (uint size) { + currentlyAllocatedBytes -= size; + } + + public class AllocationsPerMethod { + LoadedMethod method; + public LoadedMethod Method { + get { + return method; + } + } + + uint allocatedBytes; + public uint AllocatedBytes { + get { + return allocatedBytes; + } + internal set { + allocatedBytes = value; + } + } + public static Comparison<AllocationsPerMethod> CompareByAllocatedBytes = delegate (AllocationsPerMethod a, AllocationsPerMethod b) { + return a.AllocatedBytes.CompareTo (b.AllocatedBytes); + }; + + public AllocationsPerMethod (LoadedMethod method) { + this.method = method; + allocatedBytes = 0; + } + } + + Dictionary<uint,AllocationsPerMethod> allocationsPerMethod; + public AllocationsPerMethod[] Methods { + get { + if (allocationsPerMethod != null) { + AllocationsPerMethod[] result = new AllocationsPerMethod [allocationsPerMethod.Count]; + allocationsPerMethod.Values.CopyTo (result, 0); + return result; + } else { + return new AllocationsPerMethod [0]; + } + } + } + + public LoadedClass (uint id, string name, uint size): base (id, name, size) { + allocatedBytes = 0; + currentlyAllocatedBytes = 0; + allocationsPerMethod = null; + } + } + + class CallStack { + internal class StackFrame { + LoadedMethod method; + public LoadedMethod Method { + get { + return method; + } + internal set { + method = value; + } + } + ulong startCounter; + public ulong StartCounter { + get { + return startCounter; + } + internal set { + startCounter = value; + } + } + StackFrame caller; + public StackFrame Caller { + get { + return caller; + } + internal set { + caller = value; + } + } + uint level; + public uint Level { + get { + return level; + } + } + + public void SetLevel () { + level = (caller != null) ? (caller.Level + 1) : 1; + } + + internal StackFrame (LoadedMethod method, ulong startCounter, StackFrame caller) { + this.method = method; + this.startCounter = startCounter; + this.caller = caller; + SetLevel (); + } + + static StackFrame freeFrames = null; + internal static StackFrame FrameFactory (LoadedMethod method, ulong startCounter, StackFrame caller) { + if (freeFrames != null) { + StackFrame result = freeFrames; + freeFrames = result.Caller; + result.Method = method; + result.startCounter = startCounter; + result.Caller = caller; + result.SetLevel (); + return result; + } else { + return new StackFrame (method, startCounter, caller); + } + } + internal static void FreeFrame (StackFrame frame) { + frame.Caller = freeFrames; + freeFrames = frame; + } + } + + ulong threadId; + public ulong ThreadId { + get { + return threadId; + } + } + + StackFrame stackTop; + internal StackFrame StackTop { + get { + return stackTop; + } + } + + public uint Depth { + get { + return (stackTop != null) ? stackTop.Level : 0; + } + } + + internal void MethodEnter (LoadedMethod method, ulong counter) { + stackTop = StackFrame.FrameFactory (method, counter, stackTop); + } + internal void MethodExit (LoadedMethod method, ulong counter) { + while (stackTop != null) { + LoadedMethod topMethod = stackTop.Method; + StackFrame callerFrame = stackTop.Caller; + LoadedMethod callerMethod = (callerFrame != null)? callerFrame.Method : null; + + topMethod.MethodCalled (counter - stackTop.StartCounter, callerMethod); + + StackFrame.FreeFrame (stackTop); + stackTop = callerFrame; + if (topMethod == method) { + return; + } + } + } + internal void TopMethodExit (ulong counter) { + MethodExit (stackTop.Method, counter); + } + + internal CallStack (ulong threadId) { + this.threadId = threadId; + stackTop = null; + } + } + + + public class StatisticalHitItemCallInformation { + IStatisticalHitItem item; + public IStatisticalHitItem Item { + get { + return item; + } + } + uint calls; + public uint Calls { + get { + return calls; + } + internal set { + calls = value; + } + } + internal void AddCall () { + calls ++; + } + public StatisticalHitItemCallInformation (IStatisticalHitItem item) { + this.item = item; + this.calls = 0; + } + } + + public class StatisticalHitItemCallCounts { + public static Comparison<IStatisticalHitItem> CompareByStatisticalHits = delegate (IStatisticalHitItem a, IStatisticalHitItem b) { + int result = a.StatisticalHits.CompareTo (b.StatisticalHits); + if ((result == 0) && a.HasCallCounts && b.HasCallCounts) { + StatisticalHitItemCallCounts aCounts = a.CallCounts; + StatisticalHitItemCallCounts bCounts = b.CallCounts; + result = aCounts.CallersCount.CompareTo (bCounts.CallersCount); + if (result == 0) { + result = aCounts.CalleesCount.CompareTo (bCounts.CalleesCount); + } + } + return result; + }; + + List<StatisticalHitItemCallInformation> callers; + List<StatisticalHitItemCallInformation> callees; + + static StatisticalHitItemCallInformation[] GetSortedInfo (List<StatisticalHitItemCallInformation> list) { + StatisticalHitItemCallInformation[] result = list.ToArray (); + Array.Sort (result, delegate (StatisticalHitItemCallInformation a, StatisticalHitItemCallInformation b) { + return a.Calls.CompareTo (b.Calls); + }); + Array.Reverse (result); + return result; + } + + public StatisticalHitItemCallInformation[] Callers { + get { + return GetSortedInfo (callers); + } + } + public StatisticalHitItemCallInformation[] Callees { + get { + return GetSortedInfo (callees); + } + } + + public int CallersCount { + get { + return callers.Count; + } + } + public int CalleesCount { + get { + return callees.Count; + } + } + + void AddCall (List<StatisticalHitItemCallInformation> list, IStatisticalHitItem item) { + foreach (StatisticalHitItemCallInformation info in list) { + if (info.Item == item) { + info.AddCall (); + return; + } + } + StatisticalHitItemCallInformation newInfo = new StatisticalHitItemCallInformation (item); + newInfo.AddCall (); + list.Add (newInfo); + } + + internal void AddCaller (IStatisticalHitItem caller) { + AddCall (callers, caller); + } + internal void AddCallee (IStatisticalHitItem callee) { + AddCall (callees, callee); + } + + public StatisticalHitItemCallCounts () { + callers = new List<StatisticalHitItemCallInformation> (); + callees = new List<StatisticalHitItemCallInformation> (); + } + } + + public interface IStatisticalHitItem { + string Name {get;} + uint StatisticalHits {get;} + bool HasCallCounts {get;} + StatisticalHitItemCallCounts CallCounts {get;} + } + + public class LoadedMethod : BaseLoadedMethod<LoadedClass>, IStatisticalHitItem { + ulong clicks; + public ulong Clicks { + get { + return clicks; + } + internal set { + clicks = value; + } + } + public static Comparison<LoadedMethod> CompareByTotalClicks = delegate (LoadedMethod a, LoadedMethod b) { + return a.Clicks.CompareTo (b.Clicks); + }; + public static Comparison<LoadedMethod> CompareByEffectiveClicks = delegate (LoadedMethod a, LoadedMethod b) { + return (a.Clicks - a.CalledClicks).CompareTo (b.Clicks - b.CalledClicks); + }; + + ulong calledClicks; + public ulong CalledClicks { + get { + return calledClicks; + } + internal set { + calledClicks = value; + } + } + + uint statisticalHits; + public uint StatisticalHits { + get { + return statisticalHits; + } + internal set { + statisticalHits = value; + } + } + string IStatisticalHitItem.Name { + get { + return Class.Name + "." + this.Name; + } + } + + StatisticalHitItemCallCounts callCounts; + public bool HasCallCounts { + get { + return (callCounts != null); + } + } + public StatisticalHitItemCallCounts CallCounts { + get { + if (callCounts == null) { + callCounts = new StatisticalHitItemCallCounts (); + } + return callCounts; + } + } + + ulong startJit; + public ulong StartJit { + get { + return startJit; + } + internal set { + startJit = value; + } + } + ulong jitClicks; + public ulong JitClicks { + get { + return jitClicks; + } + internal set { + jitClicks = value; + } + } + public static Comparison<LoadedMethod> CompareByJitClicks = delegate (LoadedMethod a, LoadedMethod b) { + return a.JitClicks.CompareTo (b.JitClicks); + }; + + public class ClicksPerCalledMethod { + LoadedMethod method; + public LoadedMethod Method { + get { + return method; + } + } + + ulong clicks; + public ulong Clicks { + get { + return clicks; + } + internal set { + clicks = value; + } + } + public static Comparison<ClicksPerCalledMethod> CompareByClicks = delegate (ClicksPerCalledMethod a, ClicksPerCalledMethod b) { + return a.Clicks.CompareTo (b.Clicks); + }; + + public ClicksPerCalledMethod (LoadedMethod method) { + this.method = method; + clicks = 0; + } + } + + Dictionary<uint,ClicksPerCalledMethod> clicksPerCalledMethod; + public ClicksPerCalledMethod[] Methods { + get { + if (clicksPerCalledMethod != null) { + ClicksPerCalledMethod[] result = new ClicksPerCalledMethod [clicksPerCalledMethod.Count]; + clicksPerCalledMethod.Values.CopyTo (result, 0); + return result; + } else { + return new ClicksPerCalledMethod [0]; + } + } + } + + public class CallsPerCallerMethod { + LoadedMethod method; + public LoadedMethod Callees { + get { + return method; + } + } + + uint calls; + public uint Calls { + get { + return calls; + } + internal set { + calls = value; + } + } + public static Comparison<CallsPerCallerMethod> CompareByCalls = delegate (CallsPerCallerMethod a, CallsPerCallerMethod b) { + return a.Calls.CompareTo (b.Calls); + }; + + public CallsPerCallerMethod (LoadedMethod method) { + this.method = method; + calls = 0; + } + } + + Dictionary<uint,CallsPerCallerMethod> callsPerCallerMethod; + public CallsPerCallerMethod[] Callers { + get { + if (callsPerCallerMethod != null) { + CallsPerCallerMethod[] result = new CallsPerCallerMethod [callsPerCallerMethod.Count]; + callsPerCallerMethod.Values.CopyTo (result, 0); + return result; + } else { + return new CallsPerCallerMethod [0]; + } + } + } + + internal void MethodCalled (ulong clicks, LoadedMethod caller) { + this.clicks += clicks; + + if (caller != null) { + caller.CalleeReturns (this, clicks); + + if (callsPerCallerMethod == null) { + callsPerCallerMethod = new Dictionary<uint,CallsPerCallerMethod> (); + } + + CallsPerCallerMethod callerCalls; + if (callsPerCallerMethod.ContainsKey (caller.ID)) { + callerCalls = callsPerCallerMethod [caller.ID]; + } else { + callerCalls = new CallsPerCallerMethod (caller); + callsPerCallerMethod.Add (caller.ID, callerCalls); + } + + callerCalls.Calls += 1; + } + } + + internal void CalleeReturns (LoadedMethod callee, ulong clicks) { + if (clicksPerCalledMethod == null) { + clicksPerCalledMethod = new Dictionary<uint,ClicksPerCalledMethod> (); + } + + ClicksPerCalledMethod calledMethodClicks; + if (clicksPerCalledMethod.ContainsKey (callee.ID)) { + calledMethodClicks = clicksPerCalledMethod [callee.ID]; + } else { + calledMethodClicks = new ClicksPerCalledMethod (callee); + clicksPerCalledMethod.Add (callee.ID, calledMethodClicks); + } + + calledMethodClicks.Clicks += clicks; + calledClicks += clicks; + } + + public LoadedMethod (uint id, LoadedClass c, string name): base (id, c, name) { + clicks = 0; + calledClicks = 0; + jitClicks = 0; + statisticalHits = 0; + } + } + + public class UnmanagedFunctionFromID : BaseUnmanagedFunctionFromID<ExecutableMemoryRegion,UnmanagedFunctionFromRegion>, IStatisticalHitItem { + uint statisticalHits; + public uint StatisticalHits { + get { + return statisticalHits; + } + internal set { + statisticalHits = value; + } + } + + string IStatisticalHitItem.Name { + get { + return "[" + Region.Name + "]" + this.Name; + } + } + + StatisticalHitItemCallCounts callCounts; + public bool HasCallCounts { + get { + return (callCounts != null); + } + } + public StatisticalHitItemCallCounts CallCounts { + get { + if (callCounts == null) { + callCounts = new StatisticalHitItemCallCounts (); + } + return callCounts; + } + } + + public UnmanagedFunctionFromID (uint id, string name, ExecutableMemoryRegion region) : base (id, name, region) { + statisticalHits = 0; + } + } + + public class UnmanagedFunctionFromRegion : BaseUnmanagedFunctionFromRegion, IStatisticalHitItem { + uint statisticalHits; + public uint StatisticalHits { + get { + return statisticalHits; + } + internal set { + statisticalHits = value; + } + } + + public UnmanagedFunctionFromRegion () { + statisticalHits = 0; + } + + StatisticalHitItemCallCounts callCounts; + public bool HasCallCounts { + get { + return (callCounts != null); + } + } + public StatisticalHitItemCallCounts CallCounts { + get { + if (callCounts == null) { + callCounts = new StatisticalHitItemCallCounts (); + } + return callCounts; + } + } + + string IStatisticalHitItem.Name { + get { + IExecutableMemoryRegion<IUnmanagedFunctionFromRegion> r = Region; + return String.Format ("[{0}({1}-{2})]{3}", r != null ? r.Name : "NULL", this.StartOffset, this.EndOffset, this.Name); + } + } + } + + public class ExecutableMemoryRegion : BaseExecutableMemoryRegion<UnmanagedFunctionFromRegion>, IStatisticalHitItem { + uint statisticalHits; + public uint StatisticalHits { + get { + return statisticalHits; + } + internal set { + statisticalHits = value; + } + } + internal void IncrementStatisticalHits () { + statisticalHits ++; + } + + string IStatisticalHitItem.Name { + get { + return String.Format ("[{0}](UNKNOWN)", this.Name); + } + } + + StatisticalHitItemCallCounts callCounts; + public bool HasCallCounts { + get { + return (callCounts != null); + } + } + public StatisticalHitItemCallCounts CallCounts { + get { + if (callCounts == null) { + callCounts = new StatisticalHitItemCallCounts (); + } + return callCounts; + } + } + + public ExecutableMemoryRegion (uint id, string name, uint fileOffset, ulong startAddress, ulong endAddress) : base (id, name, fileOffset, startAddress, endAddress) { + statisticalHits = 0; + } + } + + public class HeapSnapshot : BaseHeapSnapshot<HeapObject<LoadedClass>,LoadedClass> { + public class AllocationStatisticsPerClass { + LoadedClass c; + public LoadedClass Class { + get { + return c; + } + internal set { + c = value; + } + } + uint allocatedBytes; + public uint AllocatedBytes { + get { + return allocatedBytes; + } + } + uint freedBytes; + public uint FreedBytes { + get { + return freedBytes; + } + } + + public static Comparison<AllocationStatisticsPerClass> CompareByAllocatedBytes = delegate (AllocationStatisticsPerClass a, AllocationStatisticsPerClass b) { + return a.AllocatedBytes.CompareTo (b.AllocatedBytes); + }; + + public void BytesFreed (uint bytes) { + allocatedBytes -= bytes; + freedBytes += bytes; + } + + public AllocationStatisticsPerClass (LoadedClass c) { + this.c = c; + this.allocatedBytes = c.CurrentlyAllocatedBytes; + this.freedBytes = 0; + } + } + + AllocationStatisticsPerClass[] allocationStatistics; + public AllocationStatisticsPerClass[] AllocationStatistics { + get { + int count = 0; + foreach (AllocationStatisticsPerClass aspc in allocationStatistics) { + if (aspc != null) { + count ++; + } + } + AllocationStatisticsPerClass[] result = new AllocationStatisticsPerClass [count]; + count = 0; + foreach (AllocationStatisticsPerClass aspc in allocationStatistics) { + if (aspc != null) { + result [count] = aspc; + count ++; + } + } + return result; + } + } + + public void HeapObjectUnreachable (LoadedClass c, uint size) { + AllocationStatisticsPerClass statisticsPerClass = allocationStatistics [c.ID]; + statisticsPerClass.BytesFreed (size); + } + + public HeapSnapshot (uint collection, ulong startCounter, DateTime startTime, ulong endCounter, DateTime endTime, LoadedClass[] initialAllocations, bool recordSnapshot) : base (collection, startCounter, startTime, endCounter, endTime, recordSnapshot) { + uint maxClassId = 0; + foreach (LoadedClass c in initialAllocations) { + if (c.ID > maxClassId) { + maxClassId = c.ID; + } + } + allocationStatistics = new AllocationStatisticsPerClass [maxClassId + 1]; + foreach (LoadedClass c in initialAllocations) { + AllocationStatisticsPerClass statisticsPerClass = new AllocationStatisticsPerClass (c); + allocationStatistics [c.ID] = statisticsPerClass; + } + } + } + + public interface IHeapObjectFilter { + string Description { + get; + } + bool Filter (HeapObject<LoadedClass> heapObject); + } + + public abstract class FilterHeapObjectByClass : IHeapObjectFilter { + protected LoadedClass c; + public LoadedClass Class { + get { + return c; + } + } + + public abstract bool Filter (HeapObject<LoadedClass> heapObject); + + string description; + public string Description { + get { + return description; + } + } + + protected FilterHeapObjectByClass (LoadedClass c, string description) { + this.c = c; + this.description = description; + } + } + + public class HeapObjectIsOfClass : FilterHeapObjectByClass { + static string BuildDescription (LoadedClass c) { + return String.Format ("Object has class {0}", c.Name); + } + + public override bool Filter (HeapObject<LoadedClass> heapObject) { + return heapObject.Class == c; + } + + public HeapObjectIsOfClass (LoadedClass c) : base (c, BuildDescription (c)) { + } + } + + public class HeapObjectReferencesObjectOfClass : FilterHeapObjectByClass { + static string BuildDescription (LoadedClass c) { + return String.Format ("Object references object of class {0}", c.Name); + } + + public override bool Filter (HeapObject<LoadedClass> heapObject) { + foreach (HeapObject<LoadedClass> ho in heapObject.References) { + if (ho.Class == c) { + return true; + } + } + return false; + } + + public HeapObjectReferencesObjectOfClass (LoadedClass c) : base (c, BuildDescription (c)) { + } + } + + public class HeapObjectIsReferencedByObjectOfClass : FilterHeapObjectByClass { + static string BuildDescription (LoadedClass c) { + return String.Format ("Object is referenced by object of class {0}", c.Name); + } + + public override bool Filter (HeapObject<LoadedClass> heapObject) { + foreach (HeapObject<LoadedClass> ho in heapObject.BackReferences) { + if (ho.Class == c) { + return true; + } + } + return false; + } + + public HeapObjectIsReferencedByObjectOfClass (LoadedClass c) : base (c, BuildDescription (c)) { + } + } + + public abstract class HeapObjectSet { + public static Comparison<HeapObject<LoadedClass>> CompareHeapObjectsByID = delegate (HeapObject<LoadedClass> a, HeapObject<LoadedClass> b) { + return a.ID.CompareTo (b.ID); + }; + + public class HeapObjectSetClassStatistics { + LoadedClass c; + public LoadedClass Class { + get { + return c; + } + } + uint allocatedBytes; + public uint AllocatedBytes { + get { + return allocatedBytes; + } + internal set { + allocatedBytes = value; + } + } + public HeapObjectSetClassStatistics (LoadedClass c, uint allocatedBytes) { + this.c = c; + this.allocatedBytes = allocatedBytes; + } + + public static Comparison<HeapObjectSetClassStatistics> CompareByAllocatedBytes = delegate (HeapObjectSetClassStatistics a, HeapObjectSetClassStatistics b) { + return a.AllocatedBytes.CompareTo (b.AllocatedBytes); + }; + } + + string shortDescription; + public string ShortDescription { + get { + return shortDescription; + } + } + string longDescription; + public string LongDescription { + get { + return longDescription; + } + } + HeapObject<LoadedClass>[] heapObjects; + public HeapObject<LoadedClass>[] HeapObjects { + get { + return heapObjects; + } + } + HeapObjectSetClassStatistics[] classStatistics; + public HeapObjectSetClassStatistics[] ClassStatistics { + get { + return classStatistics; + } + } + uint allocatedBytes; + public uint AllocatedBytes { + get { + return allocatedBytes; + } + } + + protected HeapObjectSet (string shortDescription, string longDescription, HeapObject<LoadedClass>[] heapObjects) { + this.shortDescription = shortDescription; + this.longDescription = longDescription; + this.heapObjects = heapObjects; + allocatedBytes = 0; + + Array.Sort (this.heapObjects, CompareHeapObjectsByID); + + Dictionary<ulong,HeapObjectSetClassStatistics> statistics = new Dictionary<ulong,HeapObjectSetClassStatistics> (); + foreach (HeapObject<LoadedClass> ho in heapObjects) { + HeapObjectSetClassStatistics cs; + if (statistics.ContainsKey (ho.Class.ID)) { + cs = statistics [ho.Class.ID]; + cs.AllocatedBytes += ho.Size; + allocatedBytes += ho.Size; + } else { + cs = new HeapObjectSetClassStatistics (ho.Class, ho.Size); + statistics [ho.Class.ID] = cs; + } + } + classStatistics = new HeapObjectSetClassStatistics [statistics.Values.Count]; + statistics.Values.CopyTo (classStatistics, 0); + Array.Sort (classStatistics, HeapObjectSetClassStatistics.CompareByAllocatedBytes); + Array.Reverse (classStatistics); + } + } + + public class HeapObjectSetFromSnapshot : HeapObjectSet { + HeapSnapshot heapSnapshot; + public HeapSnapshot HeapSnapshot { + get { + return heapSnapshot; + } + } + + public HeapObjectSetFromSnapshot (HeapSnapshot heapSnapshot): + base (String.Format ("Snapshot done at {0}", heapSnapshot.StartTime), + String.Format ("Snapshot done at {0}", heapSnapshot.StartTime), + heapSnapshot.HeapObjects) { + this.heapSnapshot = heapSnapshot; + } + } + + public class HeapObjectSetFromFilter : HeapObjectSet { + HeapObjectSet baseSet; + public HeapObjectSet BaseSet { + get { + return baseSet; + } + } + + IHeapObjectFilter filter; + public IHeapObjectFilter Filter { + get { + return filter; + } + } + + static HeapObject<LoadedClass>[] filterSet (HeapObjectSet baseSet, IHeapObjectFilter filter) { + List<HeapObject<LoadedClass>> newSet = new List<HeapObject<LoadedClass>> (); + foreach (HeapObject<LoadedClass> ho in baseSet.HeapObjects) { + if (filter.Filter (ho)) { + newSet.Add (ho); + } + } + HeapObject<LoadedClass>[] result = new HeapObject<LoadedClass> [newSet.Count]; + newSet.CopyTo (result); + return result; + } + + public HeapObjectSetFromFilter (HeapObjectSet baseSet, IHeapObjectFilter filter): base (filter.Description, String.Format ("{0} and {1}", filter.Description, baseSet.LongDescription), filterSet (baseSet, filter)) { + this.baseSet = baseSet; + this.filter = filter; + } + } + + public class HeapObjectSetFromComparison : HeapObjectSet { + HeapObjectSet baseSet; + public HeapObjectSet BaseSet { + get { + return baseSet; + } + } + + HeapObjectSet otherSet; + public HeapObjectSet OtherSet { + get { + return otherSet; + } + } + + static string buildShortDescription (HeapObjectSet otherSet) { + return String.Format("Object not in {0}", otherSet.ShortDescription); + } + + static string buildLongDescription (HeapObjectSet otherSet) { + return String.Format("Object not in {0}", otherSet.LongDescription); + } + + public static void PerformComparison (HeapObjectSet firstSet, HeapObjectSet secondSet, out HeapObjectSet onlyInFirstSet, out HeapObjectSet onlyInSecondSet) { + List<HeapObject<LoadedClass>> onlyInFirst = new List<HeapObject<LoadedClass>> (); + List<HeapObject<LoadedClass>> onlyInSecond = new List<HeapObject<LoadedClass>> (); + + int firstIndex = 0; + int secondIndex = 0; + HeapObject<LoadedClass>[] firstObjects = firstSet.HeapObjects; + HeapObject<LoadedClass>[] secondObjects = secondSet.HeapObjects; + + while ((firstIndex < firstObjects.Length) || (secondIndex < secondObjects.Length)) { + if (firstIndex >= firstObjects.Length) { + while (secondIndex < secondObjects.Length) { + onlyInSecond.Add (secondObjects [secondIndex]); + secondIndex ++; + } + } else if (secondIndex >= secondObjects.Length) { + while (firstIndex < secondObjects.Length) { + onlyInFirst.Add (firstObjects [firstIndex]); + firstIndex ++; + } + } else { + HeapObject<LoadedClass> firstObject = firstObjects [firstIndex]; + HeapObject<LoadedClass> secondObject = secondObjects [secondIndex]; + if (firstObject.ID < secondObject.ID) { + onlyInFirst.Add (firstObject); + firstIndex ++; + } else if (secondObject.ID < firstObject.ID) { + onlyInSecond.Add (secondObject); + secondIndex ++; + } else { + firstIndex ++; + secondIndex ++; + } + } + } + + onlyInFirstSet = new HeapObjectSetFromComparison(firstSet, secondSet, onlyInFirst.ToArray ()); + onlyInSecondSet = new HeapObjectSetFromComparison(secondSet, firstSet, onlyInSecond.ToArray ()); + } + + HeapObjectSetFromComparison (HeapObjectSet baseSet, HeapObjectSet otherSet, HeapObject<LoadedClass>[] heapObjects): base (buildShortDescription (otherSet), buildLongDescription (otherSet), heapObjects) { + this.baseSet = baseSet; + this.otherSet = otherSet; + } + } + + public class LoadedElementFactory : ILoadedElementFactory<LoadedClass,LoadedMethod,UnmanagedFunctionFromRegion,UnmanagedFunctionFromID,ExecutableMemoryRegion,HeapObject<LoadedClass>,HeapSnapshot> { + bool recordHeapSnapshots = true; + public bool RecordHeapSnapshots { + get { + return recordHeapSnapshots; + } + set { + recordHeapSnapshots = value; + } + } + + public LoadedClass NewClass (uint id, string name, uint size) { + return new LoadedClass (id, name, size); + } + public LoadedMethod NewMethod (uint id, LoadedClass c, string name) { + return new LoadedMethod (id, c, name); + } + public ExecutableMemoryRegion NewExecutableMemoryRegion (uint id, string fileName, uint fileOffset, ulong startAddress, ulong endAddress) { + return new ExecutableMemoryRegion (id, fileName, fileOffset, startAddress, endAddress); + } + public HeapSnapshot NewHeapSnapshot (uint collection, ulong startCounter, DateTime startTime, ulong endCounter, DateTime endTime, LoadedClass[] initialAllocations, bool recordSnapshots) { + return new HeapSnapshot (collection, startCounter, startTime, endCounter, endTime, initialAllocations, recordSnapshots); + } + public UnmanagedFunctionFromID NewUnmanagedFunction (uint id, string name, ExecutableMemoryRegion region) { + return new UnmanagedFunctionFromID (id, name, region); + } + } + + public class AllocationSummary : BaseAllocationSummary<LoadedClass> { + public AllocationSummary (uint collection, ulong startCounter, DateTime startTime) : base (collection, startCounter, startTime) { + } + } +} diff --git a/Mono.Profiler/profiler-decoder-library/Reader.cs b/Mono.Profiler/profiler-decoder-library/Reader.cs new file mode 100644 index 00000000..59b69ea2 --- /dev/null +++ b/Mono.Profiler/profiler-decoder-library/Reader.cs @@ -0,0 +1,289 @@ +// Author: +// Massimiliano Mantione (massi@ximian.com) +// +// (C) 2008 Novell, Inc http://www.novell.com +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; +using System.Collections.Generic; + +namespace Mono.Profiler { + public interface ILogFileReader { + BlockData ReadBlock (); + bool HasEnded {get;} + } + + public interface IDataBlockRecycler { + void RecycleData (byte [] data); + byte [] NewData (int size); + } + + public class SyncLogFileReader : ILogFileReader, IDataBlockRecycler { + System.IO.FileStream stream; + bool hasEnded = false; + public bool HasEnded { + get { + return hasEnded; + } + } + + byte[] cachedData; + public virtual void RecycleData (byte [] data) { + if (cachedData == null || cachedData.Length < data.Length) { + cachedData = data; + } + } + public virtual byte[] NewData (int size) { + if (cachedData != null && cachedData.Length > size) { + byte[] result = cachedData; + cachedData = null; + return result; + } else { + return new byte[size]; + } + } + + uint fileOffset = 0; + + public SyncLogFileReader (System.IO.FileStream stream) { + this.stream = stream; + } + public SyncLogFileReader (string fileName) { + this.stream = new System.IO.FileStream (fileName, System.IO.FileMode.Open); + } + + public BlockData ReadBlock () { + if (! hasEnded) { + byte [] header; + byte [] block; + BlockCode code; + int length; + BlockData result; + + header = new byte [BlockData.BLOCK_HEADER_SIZE]; + stream.Read (header, 0, BlockData.BLOCK_HEADER_SIZE); + fileOffset += (uint) BlockData.BLOCK_HEADER_SIZE; + + code = BlockData.DecodeHeaderBlockCode (header); + length = BlockData.DecodeHeaderBlockLength (header); + if (code == BlockCode.END) { + hasEnded = true; + } + + block = NewData (length); + stream.Read (block, 0, length); + result = new BlockData (fileOffset, code, length, block); + fileOffset += (uint) length; + + return result; + } else { + return null; + } + } + } + + public class SeekableLogFileReader : IDataBlockRecycler { + public class Block { + uint fileOffset; + public uint FileOffset { + get { + return fileOffset; + } + } + + BlockCode code; + public BlockCode Code { + get { + return code; + } + } + + uint length; + public uint Length { + get { + return length; + } + } + + ulong counter; + public ulong Counter { + get { + return counter; + } + } + + TimeSpan timeFromStart; + public TimeSpan TimeFromStart { + get { + return timeFromStart; + } + internal set { + timeFromStart = value; + } + } + + public Block (uint fileOffset, BlockCode code, uint length, ulong counter) { + this.fileOffset = fileOffset; + this.code = code; + this.length = length; + this.counter = counter; + } + } + + ulong startCounter; + public ulong StartCounter { + get { + return startCounter; + } + } + + DateTime startTime; + public DateTime StartTime { + get { + return startTime; + } + } + + ulong endCounter; + public ulong EndCounter { + get { + return endCounter; + } + } + + DateTime endTime; + public DateTime EndTime { + get { + return endTime; + } + } + + byte[] cachedData; + public virtual void RecycleData (byte [] data) { + if (cachedData == null || cachedData.Length < data.Length) { + cachedData = data; + } + } + public virtual byte[] NewData (int size) { + if (cachedData != null && cachedData.Length > size) { + byte[] result = cachedData; + cachedData = null; + return result; + } else { + return new byte[size]; + } + } + + System.IO.FileStream stream; + + Block[] blocks; + public Block[] Blocks { + get { + return blocks; + } + } + + void InitializeBlocks () { + uint fileOffset = 0; + bool hasEnded = false; + List<Block> result = new List<Block> (); + ulong counter = 0; + byte [] header = new byte [BlockData.BLOCK_HEADER_SIZE]; + ProfilerEventHandler eventProcessor = new ProfilerEventHandler (); + + while (! hasEnded) { + int bytesRead = stream.Read (header, 0, BlockData.BLOCK_HEADER_SIZE); + if (bytesRead != BlockData.BLOCK_HEADER_SIZE) { + if (bytesRead == 0) { + Console.WriteLine ("WARNING: File truncated at offset {0} without end block", fileOffset); + break; + } else { + throw new Exception (String.Format ("At file offset {0} block header is not complete", fileOffset)); + } + } + fileOffset += (uint) BlockData.BLOCK_HEADER_SIZE; + counter += BlockData.DecodeHeaderBlockCounterDelta (header); + + Block block = new Block (fileOffset, BlockData.DecodeHeaderBlockCode (header), (uint) BlockData.DecodeHeaderBlockLength (header), counter); + result.Add (block); + + fileOffset += block.Length; + stream.Seek (fileOffset, SeekOrigin.Begin); + if (block.Code == BlockCode.INTRO) { + ReadBlock (block).Decode (eventProcessor); + startCounter = eventProcessor.StartCounter; + startTime = eventProcessor.StartTime; + } + if (block.Code == BlockCode.END) { + hasEnded = true; + ReadBlock (block).Decode (eventProcessor); + endCounter = eventProcessor.EndCounter; + endTime = eventProcessor.EndTime; + } + } + + blocks = result.ToArray (); + + foreach (Block block in blocks) { + block.TimeFromStart = eventProcessor.clicksToTimeSpan (block.Counter); + } + } + + public BlockData ReadBlock (Block block) { + stream.Seek (block.FileOffset, SeekOrigin.Begin); + byte[] data = NewData ((int) block.Length); + stream.Read (data, 0, (int) block.Length); + return new BlockData (block.FileOffset, block.Code, (int) block.Length, data); + } + + public SeekableLogFileReader (System.IO.FileStream stream) { + this.stream = stream; + InitializeBlocks (); + } + public SeekableLogFileReader (string fileName) { + this.stream = new System.IO.FileStream (fileName, System.IO.FileMode.Open); + InitializeBlocks (); + } + } + +#if false + public class AsyncLogFileReader { + System.IO.FileStream stream; + System.IAsyncResult nextHeaderOperation; + System.IAsyncResult nextBlockOperation; + byte [] nextHeader; + byte [] nextBlock; + bool hadEnded; + + public void ReadBlock () { + if (! hadEnded) { + } else { + + } + } + + } +#endif +} diff --git a/Mono.Profiler/profiler-decoder-library/doc/design.txt b/Mono.Profiler/profiler-decoder-library/doc/design.txt new file mode 100644 index 00000000..e30e078d --- /dev/null +++ b/Mono.Profiler/profiler-decoder-library/doc/design.txt @@ -0,0 +1,128 @@ + +This is the library that should be used to to decode profiler log files. + +However, things are not so simple: the decoder must also store the data +it reads somewhere and process it before showing it to the user. +Since different profiler front ends will have different needs, and will +possibly want to design their own object models for the profiler data, +the system needs to be extensible. + +We have identified the following functional areas: +[1] Actually reading the file, block by block. +[2] Decode all the raw data found in the blocks. +[3] Memorize the data in appropriate data structures. +[4] Process them, to obtain the information that will be displayed. + +The implementation of each of these areas should be independent on all +the others. +In other words, we can imagine a front end that will use the default +implementation for all of them, but we can also imagine a front end +that will want to reimplement only *one* of them. +The key, here, is allowing it to do so in an easy way without having +to rewrite the other areas. + +Of course, this is possible if each "area" is a software component +with a specific interface that the other areas use, and this is +exactly what we did. +The "downside" is that to understand the decoding library one has to +understand these interfaces. + +So, let's start describing them :-) + +[1] The reader (file "Reader.cs") + +The reader interface is extremely simple: it provides a "ReadBlock ()" +method that returns the next raw data block in the file, and a "HasEnded" +boolean property that is true if it has already found the end block. +There are two implementations: a trivial, blocking one (done), and a +non blocking one which uses asynchronous file operation (not done yet). +Front ends that want to work interactively, while the program is still +running, are supposed to use the asynchronous one. +Of course the asynchronous implementation will also provide an additional +event to signal that a new block is ready to be consumed, but it is +important that it also implements the basic interface, so that it can +be used transparently in batch operations when it would be desirable +that the reading of following blocks (I/O bound) will happen concurrently +with the processing of the current block (CPU bound). + +[2] The decoder (file "Decoder.cs") + +We put the decoding algorithm in an instance method of the "Block" class. +The Block contains the raw data of a file block in a byte array (it is +the reader that produces blocks). +The idea is that the "Decode" method gets an "IProfilerEventHandler" as +input, and invokes the relevant methods for every piece of raw data it +finds in the block. +It is the responsibility of the IProfilerEventHandler implementation to +handle the data in a proper way, typically building the object model that +will then be processed to produce the output data. + +Anyway, implementing the IProfilerEventHandler interface one can handle +all the profiling events in a custom way. + +[3] Memorize the data in appropriate data structures. + +This is, obviously, the tricky part of the library. +The key point in the design is that the decoder (the Decode method) will +work with any possible object model. +In order to do so, we define the abstract properties that any model +should have in base interfaces inside "BaseTypes.cs". +These are +- "ILoadedClass": a class of the profiled program. +- "ILoadedMethod": a method of the profiled program. +- "IUnmanagedFunction": an unmanaged function of the profiled program. +- "IExecutableMemoryRegion": a memory region of the profiled program. +The memory regions are the executable memory sections mapped from a file, +which can contain unmanaged functions hit by the statistical profiler. + +We then have a factory interface, "ILoadedElementFactory", which contains +methods that are used to build new instances of loaded elements. +The interface is generic, so that an implementation will produce objects +of specific types. +The idea is that when in the decoding process it is necessary to create +in memory a representation for a loaded item (a class, a method...) the +appropriate method of the factory will be created. +This decouples the decoding algorithm from the creation of objects of +the correct type. + +Next in the layered design we have the "ILoadedElementHandler" interface, +which extends the "ILoadedElementFactory". +This provides methods to actually "organize" the loaded elements, in +practice store them in hash tables so that when we want we can retrieve +an already created one using its numeric ID. +Again, this is used in the decoder to keep the API at a high level. +For instance, when the decoder sees that method has been called, it +directly recovers the appropriate ILoadedMethod and calls the relevant +method on the event handler passing it as a parameter. +This keeps the API type safe and makes so that all the "boilerplate" +code is implemented once, and can be reused in any profiler model. + +Finally, the "IProfilerEventHandler" interface defines all the methods +that "react" to any possible profiler event. +These are the methods that the Decode method calls. +An IProfilerEventHandler also owns an ILoadedElementHandler, which is +used by the Decode method as explained above. +In short, each profiler front end should provide a complete +IProfilerEventHandler implementation. +In the "BaseTypes.cs" there are base implementations for all of these +interfaces, so that each front end can choose to implement only the +minimum amount of code needed for its goal. + +However, most profilers will want to do the "most obvious" measurements, +like count time spent in methods, count statistical hits per method, +allocated bytes per class, and so on. +Therefore, the "ObjectModel.cs" file contains complete implementations +for each base class (method, class, function...), with all the reasonable +counters implemented. +Moreover, the "EventProcessor.cs" contains an IProfilerEventHandler +implementation that is complete to work with the default object model. +The idea is then that every "reasonable" front end will just use the +classes implemented here. The possibility to implement a customized +object model exists, but it is not mandatory to do so. + +In fact, if one has a look at the simple implementation of the console +file decoder, he will see that all the data processing is done in the +object model, and all that is remaining is taking the sorted arrays +of the objects and displaying them. +So, point [4] above is also implemented in this library. + diff --git a/Mono.Profiler/profiler-decoder-library/profiler-decoder-library.mdp b/Mono.Profiler/profiler-decoder-library/profiler-decoder-library.mdp new file mode 100644 index 00000000..be6f45b5 --- /dev/null +++ b/Mono.Profiler/profiler-decoder-library/profiler-decoder-library.mdp @@ -0,0 +1,29 @@ +<Project name="mprof-decoder-library" fileversion="2.0" language="C#" clr-version="Net_2_0" ctype="DotNetProject"> + <Configurations active="Debug"> + <Configuration name="Debug" ctype="DotNetProjectConfiguration"> + <Output directory="bin/Debug" assembly="mprof-decoder-library" /> + <Build debugmode="True" target="Library" /> + <Execution runwithwarnings="True" consolepause="False" runtime="MsNet" clr-version="Net_2_0" /> + <CodeGeneration compiler="Mcs" warninglevel="4" optimize="True" unsafecodeallowed="False" generateoverflowchecks="True" generatexmldocumentation="False" ctype="CSharpCompilerParameters" /> + </Configuration> + <Configuration name="Release" ctype="DotNetProjectConfiguration"> + <Output directory="bin/Release" assembly="profiler-decoder-library" /> + <Build debugmode="False" target="Library" /> + <Execution runwithwarnings="True" consolepause="False" runtime="MsNet" clr-version="Net_2_0" /> + <CodeGeneration compiler="Mcs" warninglevel="4" optimize="True" unsafecodeallowed="False" generateoverflowchecks="True" generatexmldocumentation="False" ctype="CSharpCompilerParameters" /> + </Configuration> + </Configurations> + <Contents> + <File name="AssemblyInfo.cs" subtype="Code" buildaction="Compile" /> + <File name="BaseTypes.cs" subtype="Code" buildaction="Compile" /> + <File name="Decoder.cs" subtype="Code" buildaction="Compile" /> + <File name="EventProcessor.cs" subtype="Code" buildaction="Compile" /> + <File name="NativeLibraryReader.cs" subtype="Code" buildaction="Compile" /> + <File name="ObjectModel.cs" subtype="Code" buildaction="Compile" /> + <File name="Reader.cs" subtype="Code" buildaction="Compile" /> + </Contents> + <References> + <ProjectReference type="Gac" localcopy="True" refto="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> + </References> + <Deployment.LinuxDeployData generatePcFile="False" /> +</Project>
\ No newline at end of file diff --git a/Mono.Profiler/profiler-file-decoder/ChangeLog b/Mono.Profiler/profiler-file-decoder/ChangeLog new file mode 100644 index 00000000..9a391a0b --- /dev/null +++ b/Mono.Profiler/profiler-file-decoder/ChangeLog @@ -0,0 +1,46 @@ +2008-08-04 Massimiliano Mantione <massi@ximian.com> + * Main.cs: Ignore (but print) decoding exceptions and process the + available data anyway. + +2008-08-04 Massimiliano Mantione <massi@ximian.com> + Added man page. + +2008-08-01 Massimiliano Mantione <massi@ximian.com> + * Makefile.am, profiler-file-decoder.mdp: renamed output assembly. + * profiler-file-decoder.in: renamed to mprof-decoder.in + * profiler-file-decoder: deleted (it is autogenerated). + +2008-08-01 Massimiliano Mantione <massi@ximian.com> + Makefile.am, profiler-file-decoder: Regenerated from MonoDevelop. + +2008-06-18 Massimiliano Mantione <massi@ximian.com> + * Main.cs: Initial implementation of allocation summaries. + +2008-05-23 Massimiliano Mantione <massi@ximian.com> + * Main.cs: Handle statistical profiler call chains. + +2008-05-13 Massimiliano Mantione <massi@ximian.com> + * Main.cs: Cleaned up and optimized "heap-desc" functionality. + +2008-05-12 Massimiliano Mantione <massi@ximian.com> + * Main.cs: Added support for garbage collection statistics. + +2008-04-11 Massimiliano Mantione <massi@ximian.com> + * Main.cs: Made HeapSnapshot non generic. + +2008-04-10 Massimiliano Mantione <massi@ximian.com> + * Main.cs: Added desc-heap like reporting. + +2008-03-19 Massimiliano Mantione <massi@ximian.com> + * Main.cs: More printing adjustments. + +2008-03-11 Massimiliano Mantione <massi@ximian.com> + * Main.cs: Minor adjustments in printing. + +2008-03-04 Massimiliano Mantione <massi@ximian.com> + * Main.cs: Also print allocation and calls caller attribution. + +2008-01-11 Massimiliano Mantione <massi@ximian.com> + * Main.cs: Added (first code dump). + * profiler-file-decoder.mdp: Added (first code dump). + * ChangeLog: Added (first code dump). diff --git a/Mono.Profiler/profiler-file-decoder/Main.cs b/Mono.Profiler/profiler-file-decoder/Main.cs new file mode 100644 index 00000000..fb1d55db --- /dev/null +++ b/Mono.Profiler/profiler-file-decoder/Main.cs @@ -0,0 +1,260 @@ +// Author: +// Massimiliano Mantione (massi@ximian.com) +// +// (C) 2008 Novell, Inc http://www.novell.com +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; + +namespace Mono.Profiler +{ + public class ConsoleDecoder { + static void PrintSeparator (TextWriter writer) { + writer.WriteLine ("\n\n------------------------------------------------"); + } + + static void PrintData (TextWriter writer, ProfilerEventHandler data) { + LoadedClass[] classes = data.LoadedElements.Classes; + LoadedMethod[] methods = data.LoadedElements.Methods; + IStatisticalHitItem[] statisticalHitItems = data.StatisticalHitItems; + + if ((data.Flags & ProfilerFlags.CLASS_EVENTS) != 0) { + Array.Sort (classes, LoadedClass.CompareByAllocatedBytes); + Array.Reverse (classes); + ulong totalAllocatedBytes = 0; + foreach (LoadedClass c in classes) { + totalAllocatedBytes += c.AllocatedBytes; + } + if (totalAllocatedBytes > 0) { + PrintSeparator (writer); + writer.WriteLine ("Reporting allocations (on {0} classes)", classes.Length); + foreach (LoadedClass c in classes) { + if (c.AllocatedBytes > 0) { + writer.WriteLine ("{0,5:F2}% ({1} bytes) {2}", ((((double)c.AllocatedBytes) / totalAllocatedBytes) * 100), c.AllocatedBytes, c.Name); + LoadedClass.AllocationsPerMethod[] allocationsPerMethodArray = c.Methods; + if (allocationsPerMethodArray.Length != 0) { + Array.Sort (allocationsPerMethodArray, LoadedClass.AllocationsPerMethod.CompareByAllocatedBytes); + Array.Reverse (allocationsPerMethodArray); + foreach (LoadedClass.AllocationsPerMethod allocationsPerMethod in allocationsPerMethodArray) { + writer.WriteLine (" {0} bytes from {1}.{2}", allocationsPerMethod.AllocatedBytes, allocationsPerMethod.Method.Class.Name, allocationsPerMethod.Method.Name); + } + } + } + } + } else { + writer.WriteLine ("No allocations reported (on {0} classes)", classes.Length); + } + } + if ((data.Flags & ProfilerFlags.METHOD_EVENTS) != 0) { + Array.Sort (methods, LoadedMethod.CompareByTotalClicks); + Array.Reverse (methods); + ulong totalExecutionClicks = 0; + foreach (LoadedMethod m in methods) { + totalExecutionClicks += m.Clicks; + } + if (totalExecutionClicks > 0) { + PrintSeparator (writer); + writer.WriteLine ("Reporting execution time (on {0} methods)", methods.Length); + foreach (LoadedMethod m in methods) { + if (m.Clicks > 0) { + writer.WriteLine ("{0,5:F2}% ({1:F6}s) {2}.{3}", ((((double)m.Clicks) / totalExecutionClicks) * 100), data.clicksToSeconds (m.Clicks), m.Class.Name, m.Name); + LoadedMethod.CallsPerCallerMethod[] callsPerCallerMethodArray = m.Callers; + if (callsPerCallerMethodArray.Length > 0) { + Array.Sort (callsPerCallerMethodArray, LoadedMethod.CallsPerCallerMethod.CompareByCalls); + Array.Reverse (callsPerCallerMethodArray); + foreach (LoadedMethod.CallsPerCallerMethod callsPerCallerMethod in callsPerCallerMethodArray) { + writer.WriteLine (" {0} calls from {1}.{2}", callsPerCallerMethod.Calls, callsPerCallerMethod.Callees.Class.Name, callsPerCallerMethod.Callees.Name); + } + } + } + } + } else { + writer.WriteLine ("No execution time reported (on {0} methods)", methods.Length); + } + } + if ((data.Flags & ProfilerFlags.JIT_COMPILATION) != 0) { + Array.Sort (methods, LoadedMethod.CompareByJitClicks); + Array.Reverse (methods); + ulong totalJitClicks = 0; + foreach (LoadedMethod m in methods) { + totalJitClicks += m.JitClicks; + } + if (totalJitClicks > 0) { + PrintSeparator (writer); + writer.WriteLine ("Reporting jit time (on {0} methods)", methods.Length); + foreach (LoadedMethod m in methods) { + if (m.JitClicks > 0) { + writer.WriteLine ("{0,5:F2}% ({1:F3}ms) {2}.{3}", ((((double)m.JitClicks) / totalJitClicks) * 100), data.clicksToSeconds (m.JitClicks) * 1000, m.Class.Name, m.Name); + } + } + } else { + writer.WriteLine ("No jit time reported (on {0} methods)", methods.Length); + } + } + if ((data.Flags & ProfilerFlags.STATISTICAL) != 0) { + Array.Sort (statisticalHitItems, StatisticalHitItemCallCounts.CompareByStatisticalHits); + Array.Reverse (statisticalHitItems); + ulong totalHits = 0; + foreach (IStatisticalHitItem s in statisticalHitItems) { + totalHits += s.StatisticalHits; + } + if (totalHits > 0) { + PrintSeparator (writer); + writer.WriteLine ("Reporting statistical hits ({0} hits recorded)", totalHits); + foreach (IStatisticalHitItem s in statisticalHitItems) { + if ((s.StatisticalHits > 0) || s.HasCallCounts) { + writer.WriteLine ("{0,5:F2}% ({1}) {2}", ((((double)s.StatisticalHits) / totalHits) * 100), s.StatisticalHits, s.Name); + if (s.HasCallCounts) { + StatisticalHitItemCallCounts callCounts = s.CallCounts; + if (callCounts.CallersCount > 0) { + StatisticalHitItemCallInformation[] calls = callCounts.Callers; + foreach (StatisticalHitItemCallInformation call in calls) { + writer.WriteLine (" {0} calls from {1}", call.Calls, call.Item.Name); + } + } + if (callCounts.CalleesCount > 0) { + StatisticalHitItemCallInformation[] calls = callCounts.Callees; + foreach (StatisticalHitItemCallInformation call in calls) { + writer.WriteLine (" {0} calls to {1}", call.Calls, call.Item.Name); + } + } + } + } + } + } else { + writer.WriteLine ("No statistical hits reported (on {0} items)", statisticalHitItems.Length); + } + } + + ProfilerEventHandler.GcStatistics[] gcStatistics = data.GarbageCollectioncStatistics; + if (gcStatistics.Length > 0) { + double totalTime = data.clicksToSeconds (data.EndCounter - data.StartCounter); + double gcTime = 0; + double gcMarkTime = 0; + double gcSweepTime = 0; + int collections = 0; + foreach (ProfilerEventHandler.GcStatistics gcs in gcStatistics) { + if (gcs.NewHeapSize == null) { + collections ++; + gcTime += gcs.Duration; + gcMarkTime += gcs.MarkDuration; + gcSweepTime += gcs.SweepDuration; + } + } + + PrintSeparator (writer); + writer.WriteLine ("Reporting GC statistics for {0} collections (total {1:F3}ms, {2,5:F2}% of total time, mark {3,5:F2}%, sweep {4,5:F2}%)", + collections, + gcTime * 1000, + (gcTime / totalTime) * 100, + (gcMarkTime / gcTime) * 100, + (gcSweepTime / gcTime) * 100); + foreach (ProfilerEventHandler.GcStatistics gcs in gcStatistics) { + if (gcs.NewHeapSize == null) { + ulong gcStartClicks = gcs.StartCounter - data.StartCounter; + writer.WriteLine ("[{0}] Collection starting at {1:F3}s (generation {2}): duration {3:F3}ms, mark {4:F3}ms, sweep {5:F3}ms", + gcs.Collection, + data.clicksToSeconds (gcStartClicks), + gcs.Generation, + gcs.Duration * 1000, + gcs.MarkDuration * 1000, + gcs.SweepDuration * 1000); + } else { + writer.WriteLine ("[{0}] Heap resized to {1} bytes", gcs.Collection, gcs.NewHeapSize); + } + } + } + + AllocationSummary [] allocationSummaries = data.AllocationSummaries; + if (allocationSummaries.Length > 0) { + PrintSeparator (writer); + writer.WriteLine ("Reporting allocation summaries for {0} collections", allocationSummaries.Length); + foreach (AllocationSummary allocationSummary in allocationSummaries) { + writer.WriteLine ("Data for collection {0} written {1:F3}s since the application started", + allocationSummary.Collection, ((double) (allocationSummary.StartTime - data.StartTime).Milliseconds) / 1000); + AllocationClassData<LoadedClass>[] classData = allocationSummary.Data; + foreach (AllocationClassData<LoadedClass> cData in classData) { + writer.WriteLine (" Class {0}: {1} bytes in {2} instances (freed: {3} bytes in {4} instances)", + cData.Class.Name, + cData.ReachableBytes, + cData.ReachableInstances, + cData.UnreachableBytes, + cData.UnreachableInstances); + } + } + } + + HeapSnapshot[] heapSnapshots = data.LoadedElements.HeapSnapshots; + if (heapSnapshots.Length > 0) { + PrintSeparator (writer); + writer.WriteLine ("Reporting heap data for {0} collections", heapSnapshots.Length); + foreach (HeapSnapshot heapSnapshot in heapSnapshots) { + HeapSnapshot.AllocationStatisticsPerClass [] allocationStatistics = heapSnapshot.AllocationStatistics; + writer.WriteLine ("Heap data collection {0} started at {1} (duration {2:F3}ms)", + heapSnapshot.Collection, + data.counterToDateTime (heapSnapshot.StartCounter), + data.clicksToSeconds (heapSnapshot.EndCounter - heapSnapshot.StartCounter) * 1000); + if (allocationStatistics.Length > 0) { + Array.Sort (allocationStatistics, HeapSnapshot.AllocationStatisticsPerClass.CompareByAllocatedBytes); + Array.Reverse (allocationStatistics); + uint totalAllocatedBytes = 0; + foreach (HeapSnapshot.AllocationStatisticsPerClass s in allocationStatistics) { + totalAllocatedBytes += s.AllocatedBytes; + } + foreach (HeapSnapshot.AllocationStatisticsPerClass s in allocationStatistics) { + if (s.AllocatedBytes > 0) { + writer.WriteLine (" {0,5:F2}% {1} {2} bytes ({3} freed)", ((((double)s.AllocatedBytes) / totalAllocatedBytes) * 100), s.Class.Name, s.AllocatedBytes, s.FreedBytes); + } + } + } else { + writer.WriteLine ("No allocation statistics for this collection)", statisticalHitItems.Length); + } + } + } + } + + static void Main (string[] argv) { + BlockData.DebugLog = Console.Out; + + if (argv.Length != 1) { + Console.WriteLine ("Please specify one input file"); + return; + } + SyncLogFileReader reader = new SyncLogFileReader (argv [0]); + ProfilerEventHandler data = new ProfilerEventHandler (); + data.LoadedElements.RecordHeapSnapshots = false; + while (! reader.HasEnded) { + try { + reader.ReadBlock ().Decode (data, reader); + } catch (DecodingException e) { + Console.WriteLine ("WARNING: DecodingException in block of code {0}, length {1}, file offset {2}, block offset {3}: {4}", e.FailingData.Code, e.FailingData.Length, e.FailingData.FileOffset, e.OffsetInBlock, e.Message); + break; + } + } + + PrintData (Console.Out, data); + } + } +} diff --git a/Mono.Profiler/profiler-file-decoder/Makefile.am b/Mono.Profiler/profiler-file-decoder/Makefile.am new file mode 100644 index 00000000..0b4f0982 --- /dev/null +++ b/Mono.Profiler/profiler-file-decoder/Makefile.am @@ -0,0 +1,101 @@ + +EXTRA_DIST = + +# Warning: This is an automatically generated file, do not edit! + +if ENABLE_DEBUG +ASSEMBLY_COMPILER_COMMAND = gmcs +ASSEMBLY_COMPILER_FLAGS = -noconfig -codepage:utf8 -warn:4 -optimize+ -debug -define:DEBUG +ASSEMBLY = bin/Debug/mprof-decoder.exe +ASSEMBLY_MDB = $(ASSEMBLY).mdb +COMPILE_TARGET = exe +PROJECT_REFERENCES = \ + ../profiler-decoder-library/bin/Debug/mprof-decoder-library.dll +BUILD_DIR = bin/Debug + +PROFILER_DECODER_LIBRARY_DLL= +MPROF_DECODER_LIBRARY_DLL_MDB_SOURCE=../profiler-decoder-library/bin/Debug/mprof-decoder-library.dll.mdb +MPROF_DECODER_LIBRARY_DLL_MDB=$(BUILD_DIR)/mprof-decoder-library.dll.mdb +MPROF_DECODER_LIBRARY_DLL_SOURCE=../profiler-decoder-library/bin/Debug/mprof-decoder-library.dll +MPROF_DECODER_LIBRARY_DLL=$(BUILD_DIR)/mprof-decoder-library.dll +MPROF_DECODER_1_SOURCE=man/man1/mprof-decoder.1 +MPROF_DECODER_EXE_MDB_SOURCE=bin/Debug/mprof-decoder.exe.mdb +MPROF_DECODER_EXE_MDB=$(BUILD_DIR)/mprof-decoder.exe.mdb + +endif + +if ENABLE_RELEASE +ASSEMBLY_COMPILER_COMMAND = gmcs +ASSEMBLY_COMPILER_FLAGS = -noconfig -codepage:utf8 -warn:4 -optimize+ +ASSEMBLY = bin/Release/mprof-decoder.exe +ASSEMBLY_MDB = +COMPILE_TARGET = exe +PROJECT_REFERENCES = \ + ../profiler-decoder-library/bin/Release/profiler-decoder-library.dll +BUILD_DIR = bin/Release + +PROFILER_DECODER_LIBRARY_DLL_SOURCE=../profiler-decoder-library/bin/Release/profiler-decoder-library.dll +PROFILER_DECODER_LIBRARY_DLL=$(BUILD_DIR)/profiler-decoder-library.dll +MPROF_DECODER_LIBRARY_DLL_MDB= +MPROF_DECODER_LIBRARY_DLL= +MPROF_DECODER_1_SOURCE=man/man1/mprof-decoder.1 +MPROF_DECODER_EXE_MDB= + +endif + +AL=al2 +SATELLITE_ASSEMBLY_NAME=.resources.dll + +PROGRAMFILES = \ + $(PROFILER_DECODER_LIBRARY_DLL) \ + $(MPROF_DECODER_LIBRARY_DLL_MDB) \ + $(MPROF_DECODER_LIBRARY_DLL) \ + $(MPROF_DECODER_EXE_MDB) + +COMMONAPPLICATIONDATAROOT_MAN_MAN1 = \ + $(MPROF_DECODER_1) + +BINARIES = \ + $(MPROF_DECODER) + + +RESGEN=resgen2 + +all: $(ASSEMBLY) $(PROGRAMFILES) $(COMMONAPPLICATIONDATAROOT_MAN_MAN1) $(BINARIES) + +FILES = \ + Main.cs + +DATA_FILES = \ + man/man1/mprof-decoder.1 + +RESOURCES = + +EXTRAS = \ + mprof-decoder.in + +REFERENCES = + +DLL_REFERENCES = + +CLEANFILES = $(PROGRAMFILES) $(COMMONAPPLICATIONDATAROOT_MAN_MAN1) $(BINARIES) + +include $(top_srcdir)/Makefile.include + +MPROF_DECODER_1 = $(BUILD_DIR)/man/man1/mprof-decoder.1 +MPROF_DECODER = $(BUILD_DIR)/mprof-decoder + +$(eval $(call emit-deploy-target,PROFILER_DECODER_LIBRARY_DLL)) +$(eval $(call emit-deploy-target,MPROF_DECODER_LIBRARY_DLL_MDB)) +$(eval $(call emit-deploy-target,MPROF_DECODER_LIBRARY_DLL)) +$(eval $(call emit-deploy-target,MPROF_DECODER_1)) +$(eval $(call emit-deploy-wrapper,MPROF_DECODER,mprof-decoder,x)) + + +$(eval $(call emit_resgen_targets)) +$(build_xamlg_list): %.xaml.g.cs: %.xaml + xamlg '$<' + +$(ASSEMBLY) $(ASSEMBLY_MDB): $(build_sources) $(build_resources) $(build_datafiles) $(DLL_REFERENCES) $(PROJECT_REFERENCES) $(build_xamlg_list) $(build_satellite_assembly_list) + mkdir -p $(shell dirname $(ASSEMBLY)) + $(ASSEMBLY_COMPILER_COMMAND) $(ASSEMBLY_COMPILER_FLAGS) -out:$(ASSEMBLY) -target:$(COMPILE_TARGET) $(build_sources_embed) $(build_resources_embed) $(build_references_ref) diff --git a/Mono.Profiler/profiler-file-decoder/man/man1/mprof-decoder.1 b/Mono.Profiler/profiler-file-decoder/man/man1/mprof-decoder.1 new file mode 100644 index 00000000..9579f88a --- /dev/null +++ b/Mono.Profiler/profiler-file-decoder/man/man1/mprof-decoder.1 @@ -0,0 +1,23 @@ +.TH "mprof-decoder" 1 +.SH NAME +mprof-decoder \- Console decoder for the logging profiler output files +.SH SYNOPSIS +.B mprof-decoder +file +.SH DESCRIPTION +.B mprof-decoder +Console decoder for the logging profiler output files +.PP +Decodes the contents of a logging profiler output file and prints the +data on standard output. +.SH Options +.PP +None +.SH ENVIRONMENT VARIABLES +.PP +None +.SH SEE ALSO +mono(1) +.BR +.SH COPYRIGHT +Copyright (C) 2008 Novell, Inc (http://www.novell.com) diff --git a/Mono.Profiler/profiler-file-decoder/mprof-decoder.in b/Mono.Profiler/profiler-file-decoder/mprof-decoder.in new file mode 100644 index 00000000..38f1ffb0 --- /dev/null +++ b/Mono.Profiler/profiler-file-decoder/mprof-decoder.in @@ -0,0 +1,3 @@ +#!/bin/sh + +exec mono "@expanded_libdir@/@PACKAGE@/mprof-decoder.exe" "$@" diff --git a/Mono.Profiler/profiler-file-decoder/profiler-file-decoder.mdp b/Mono.Profiler/profiler-file-decoder/profiler-file-decoder.mdp new file mode 100644 index 00000000..9d18f7cd --- /dev/null +++ b/Mono.Profiler/profiler-file-decoder/profiler-file-decoder.mdp @@ -0,0 +1,24 @@ +<Project name="mprof-decoder" fileversion="2.0" language="C#" clr-version="Net_2_0" ctype="DotNetProject"> + <Configurations active="Debug"> + <Configuration name="Debug" ctype="DotNetProjectConfiguration"> + <Output directory="bin/Debug" assembly="mprof-decoder" /> + <Build debugmode="True" target="Exe" /> + <Execution runwithwarnings="True" consolepause="True" runtime="MsNet" clr-version="Net_2_0" /> + <CodeGeneration compiler="Mcs" warninglevel="4" optimize="True" unsafecodeallowed="False" generateoverflowchecks="True" generatexmldocumentation="False" ctype="CSharpCompilerParameters" /> + </Configuration> + <Configuration name="Release" ctype="DotNetProjectConfiguration"> + <Output directory="bin/Release" assembly="mprof-decoder" /> + <Build debugmode="False" target="Exe" /> + <Execution runwithwarnings="True" consolepause="True" runtime="MsNet" clr-version="Net_2_0" /> + <CodeGeneration compiler="Mcs" warninglevel="4" optimize="True" unsafecodeallowed="False" generateoverflowchecks="True" generatexmldocumentation="False" ctype="CSharpCompilerParameters" /> + </Configuration> + </Configurations> + <Contents> + <File name="Main.cs" subtype="Code" buildaction="Compile" /> + <File name="man/man1/mprof-decoder.1" subtype="Code" buildaction="FileCopy" DeployService.TargetDirectoryId="CommonApplicationDataRoot" DeployService.UseProjectRelativePath="True" /> + </Contents> + <References> + <ProjectReference type="Project" localcopy="True" refto="mprof-decoder-library" /> + </References> + <Deployment.LinuxDeployData scriptName="mprof-decoder" /> +</Project>
\ No newline at end of file diff --git a/Mono.Profiler/rules.make b/Mono.Profiler/rules.make new file mode 100644 index 00000000..12a84090 --- /dev/null +++ b/Mono.Profiler/rules.make @@ -0,0 +1,32 @@ +clean-local: + make pre-clean-local-hook + -rm -f $(CLEANFILES) + make post-clean-local-hook + +install-local: +uninstall-local: + +dist-local: + make pre-dist-local-hook distdir=$$distdir + list='$(EXTRA_DIST)'; \ + for f in Makefile $$list; do \ + d=`dirname "$$f"`; \ + test -d "$(distdir)/$$d" || \ + mkdir -p "$(distdir)/$$d"; \ + cp -p "$$f" "$(distdir)/$$d" || exit 1; \ + done + make post-dist-local-hook distdir=$$distdir + +dist-local-recursive: + for dir in $(SUBDIRS); do \ + mkdir -p $(distdir)/$$dir || true; \ + case $$dir in \ + .) make dist-local distdir=$(distdir) || exit 1;; \ + *) (cd $$dir; make dist-local distdir=$(distdir)/$$dir) || exit 1; \ + esac \ + done + +#hooks: Available hooks - all, clean, install, uninstall and dist +# and their *-local variants +pre-%-hook: ; @: +post-%-hook: ; @: |