svn path=/trunk/heap-prof/; revision=38579
This commit is contained in:
Ben Maurer 2005-01-10 02:25:13 +00:00
Коммит 3c812ee5e5
29 изменённых файлов: 8516 добавлений и 0 удалений

1
AUTHORS Normal file
Просмотреть файл

@ -0,0 +1 @@
Ben Maurer <bmaurer@ximian.com>

4
ChangeLog Normal file
Просмотреть файл

@ -0,0 +1,4 @@
2005-01-09 Ben Maurer <bmaurer@ximian.com>
* *: Initial Import.

1
INSTALL Symbolic link
Просмотреть файл

@ -0,0 +1 @@
/usr/share/automake-1.8/INSTALL

20
LICENSE Normal file
Просмотреть файл

@ -0,0 +1,20 @@
Copyright (c) 2005 Novell
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.

1
Makefile.am Normal file
Просмотреть файл

@ -0,0 +1 @@
SUBDIRS = src

0
NEWS Normal file
Просмотреть файл

12
README Normal file
Просмотреть файл

@ -0,0 +1,12 @@
This is the Mono Heap Profiler.
To install, you need to do:
patch < ../heap-prof/mono.patch
in your mono dir.
Then do an autogen/make/make install in here.
Run mono --profile=heap:outfile, then run mono-heap-prof-view
to view it.

143
autogen.sh Executable file
Просмотреть файл

@ -0,0 +1,143 @@
#!/bin/sh
# Run this to generate all the initial makefiles, etc.
# Ripped off from GNOME macros version
DIE=0
srcdir=`dirname $0`
test -z "$srcdir" && srcdir=.
if [ -n "$MONO_PATH" ]; then
# from -> /mono/lib:/another/mono/lib
# to -> /mono /another/mono
for i in `echo ${MONO_PATH} | tr ":" " "`; do
i=`dirname ${i}`
if [ -n "{i}" -a -d "${i}/share/aclocal" ]; then
ACLOCAL_FLAGS="-I ${i}/share/aclocal $ACLOCAL_FLAGS"
fi
if [ -n "{i}" -a -d "${i}/bin" ]; then
PATH="${i}/bin:$PATH"
fi
done
export PATH
fi
(autoconf --version) < /dev/null > /dev/null 2>&1 || {
echo
echo "**Error**: You must have \`autoconf' installed to compile Mono."
echo "Download the appropriate package for your distribution,"
echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
DIE=1
}
if [ -z "$LIBTOOL" ]; then
LIBTOOL=`which glibtool 2>/dev/null`
if [ ! -x "$LIBTOOL" ]; then
LIBTOOL=`which libtool`
fi
fi
(grep "^AM_PROG_LIBTOOL" $srcdir/configure.in >/dev/null) && {
($LIBTOOL --version) < /dev/null > /dev/null 2>&1 || {
echo
echo "**Error**: You must have \`libtool' installed to compile Mono."
echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2d.tar.gz"
echo "(or a newer version if it is available)"
DIE=1
}
}
grep "^AM_GNU_GETTEXT" $srcdir/configure.in >/dev/null && {
grep "sed.*POTFILES" $srcdir/configure.in >/dev/null || \
(gettext --version) < /dev/null > /dev/null 2>&1 || {
echo
echo "**Error**: You must have \`gettext' installed to compile Mono."
echo "Get ftp://alpha.gnu.org/gnu/gettext-0.10.35.tar.gz"
echo "(or a newer version if it is available)"
DIE=1
}
}
(automake --version) < /dev/null > /dev/null 2>&1 || {
echo
echo "**Error**: You must have \`automake' installed to compile Mono."
echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz"
echo "(or a newer version if it is available)"
DIE=1
NO_AUTOMAKE=yes
}
# if no automake, don't bother testing for aclocal
test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || {
echo
echo "**Error**: Missing \`aclocal'. The version of \`automake'"
echo "installed doesn't appear recent enough."
echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz"
echo "(or a newer version if it is available)"
DIE=1
}
if test "$DIE" -eq 1; then
exit 1
fi
if test -z "$*"; then
echo "**Warning**: I am going to run \`configure' with no arguments."
echo "If you wish to pass any to it, please specify them on the"
echo \`$0\'" command line."
echo
fi
case $CC in
xlc )
am_opt=--include-deps;;
esac
if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then
if test -z "$NO_LIBTOOLIZE" ; then
echo "Running libtoolize..."
${LIBTOOL}ize --force --copy
#${LIBTOOL}ize --copy
fi
fi
echo "Running aclocal $ACLOCAL_FLAGS ..."
aclocal $ACLOCAL_FLAGS || {
echo
echo "**Error**: aclocal failed. This may mean that you have not"
echo "installed all of the packages you need, or you may need to"
echo "set ACLOCAL_FLAGS to include \"-I \$prefix/share/aclocal\""
echo "for the prefix where you installed the packages whose"
echo "macros were not found"
exit 1
}
if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then
echo "Running autoheader..."
autoheader || { echo "**Error**: autoheader failed."; exit 1; }
fi
echo "Running automake --gnu $am_opt ..."
automake --add-missing --gnu $am_opt ||
{ echo "**Error**: automake failed."; exit 1; }
echo "Running autoconf ..."
autoconf || { echo "**Error**: autoconf failed."; exit 1; }
if test -d $srcdir/libgc; then
echo Running libgc/autogen.sh ...
(cd $srcdir/libgc ; NOCONFIGURE=1 ./autogen.sh "$@")
echo Done running libgc/autogen.sh ...
fi
conf_flags="--enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c
if test x$NOCONFIGURE = x; then
echo Running $srcdir/configure $conf_flags "$@" ...
$srcdir/configure $conf_flags "$@" \
&& echo Now type \`make\' to compile $PKG_NAME || exit 1
else
echo Skipping configure process.
fi

18
configure.in Normal file
Просмотреть файл

@ -0,0 +1,18 @@
AC_INIT(README)
AM_INIT_AUTOMAKE(heap-prof, 0.01)
AC_PROG_CC
AC_PROG_LIBTOOL
PKG_CHECK_MODULES(CCOMPILE, mono glib-2.0)
PKG_CHECK_MODULES(GTKSHARP, gtk-sharp-2.0)
AC_PATH_PROG(MCS, mcs)
AC_PATH_PROG(MONO, mono)
AC_OUTPUT([
Makefile
src/Makefile
src/runtime-profiler/Makefile
src/viewer/Makefile
src/viewer/mono-heap-prof-view
])

6402
ltmain.sh Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

1
missing Symbolic link
Просмотреть файл

@ -0,0 +1 @@
/usr/share/automake-1.8/missing

149
mono.patch Normal file
Просмотреть файл

@ -0,0 +1,149 @@
Index: libgc/include/gc.h
===================================================================
--- libgc/include/gc.h (revision 38557)
+++ libgc/include/gc.h (working copy)
@@ -91,7 +91,19 @@
/* If it returns, it must return 0 or a valid */
/* pointer to a previously allocated heap */
/* object. */
+
+GC_API GC_PTR (*GC_profile_marks_set) GC_PROTO((int col_num));
+ /* Invoked on every collection. At this time mark
+ * bits are set. A profiler would use this to do
+ * a heap profile: so it can see what objects are
+ * alive at a given time.
+ */
+/* Slow/general mark bit manipulation: */
+GC_API int GC_is_marked GC_PROTO((char * p));
+GC_API void GC_clear_mark_bit GC_PROTO((char * p));
+GC_API void GC_set_mark_bit GC_PROTO((char * p));
+
GC_API int GC_find_leak;
/* Do not actually garbage collect, but simply */
/* report inaccessible memory that was not */
Index: libgc/include/private/gc_priv.h
===================================================================
--- libgc/include/private/gc_priv.h (revision 38557)
+++ libgc/include/private/gc_priv.h (working copy)
@@ -1781,10 +1781,7 @@
void GC_dirty_init GC_PROTO((void));
-/* Slow/general mark bit manipulation: */
-GC_API GC_bool GC_is_marked GC_PROTO((ptr_t p));
-void GC_clear_mark_bit GC_PROTO((ptr_t p));
-void GC_set_mark_bit GC_PROTO((ptr_t p));
+
/* Stubborn objects: */
void GC_read_changed GC_PROTO((void)); /* Analogous to GC_read_dirty */
Index: libgc/alloc.c
===================================================================
--- libgc/alloc.c (revision 38557)
+++ libgc/alloc.c (working copy)
@@ -617,6 +617,8 @@
}
}
+GC_PTR (*GC_profile_marks_set) GC_PROTO((int col_num));
+
/* Finish up a collection. Assumes lock is held, signals are disabled, */
/* but the world is otherwise running. */
void GC_finish_collection()
@@ -638,6 +640,11 @@
GC_print_address_map();
}
# endif
+
+
+ if (GC_profile_marks_set)
+ GC_profile_marks_set (GC_gc_no);
+
COND_DUMP;
if (GC_find_leak) {
/* Mark all objects on the free list. All objects should be */
Index: mono/metadata/profiler.c
===================================================================
--- mono/metadata/profiler.c (revision 38558)
+++ mono/metadata/profiler.c (working copy)
@@ -12,6 +12,7 @@
#ifdef HAVE_BACKTRACE_SYMBOLS
#include <execinfo.h>
#endif
+#include <mono/os/gc_wrapper.h>
static MonoProfiler * current_profiler = NULL;
@@ -46,6 +47,8 @@
static MonoProfileThreadFunc thread_start;
static MonoProfileThreadFunc thread_end;
+static MonoProfileGCFunc on_gc;
+
static MonoProfileCoverageFilterFunc coverage_filter_cb;
static MonoProfileFunc shutdown_callback;
@@ -163,7 +166,21 @@
class_end_unload = end_unload;
}
+static void
+mono_profiler_gc (int gc_num)
+{
+ if ((mono_profiler_events & MONO_PROFILE_GC) && on_gc)
+ on_gc (current_profiler, gc_num);
+}
+
void
+mono_profiler_install_gc (MonoProfileGCFunc f)
+{
+ GC_profile_marks_set = mono_profiler_gc;
+ on_gc = f;
+}
+
+void
mono_profiler_method_enter (MonoMethod *method)
{
if ((mono_profiler_events & MONO_PROFILE_ENTER_LEAVE) && method_enter)
@@ -226,6 +243,7 @@
thread_end (current_profiler, tid);
}
+
void
mono_profiler_assembly_event (MonoAssembly *assembly, int code)
{
@@ -357,6 +375,12 @@
shutdown_callback (current_profiler);
}
+gboolean
+mono_profiler_mark_set (MonoObject* o)
+{
+ return GC_is_marked (o);
+}
+
static GHashTable *coverage_hash = NULL;
MonoProfileCoverageInfo*
Index: mono/metadata/profiler.h
===================================================================
--- mono/metadata/profiler.h (revision 38557)
+++ mono/metadata/profiler.h (working copy)
@@ -64,6 +64,7 @@
typedef void (*MonoProfileThreadFunc) (MonoProfiler *prof, guint32 tid);
typedef void (*MonoProfileAllocFunc) (MonoProfiler *prof, MonoObject *obj, MonoClass *klass);
typedef void (*MonoProfileStatFunc) (MonoProfiler *prof, guchar *ip, void *context);
+typedef void (*MonoProfileGCFunc) (MonoProfiler *prof, int gc_num);
typedef gboolean (*MonoProfileCoverageFilterFunc) (MonoProfiler *prof, MonoMethod *method);
@@ -94,6 +95,7 @@
void mono_profiler_install_statistical (MonoProfileStatFunc callback);
void mono_profiler_install_coverage_filter (MonoProfileCoverageFilterFunc callback);
void mono_profiler_coverage_get (MonoProfiler *prof, MonoMethod *method, MonoProfileCoverageFunc func);
+void mono_profiler_install_gc (MonoProfileGCFunc callback);
void mono_profiler_load (const char *desc);

4
src/ChangeLog Normal file
Просмотреть файл

@ -0,0 +1,4 @@
2005-01-09 Ben Maurer <bmaurer@ximian.com>
* *: Initial Import.

1
src/Makefile.am Normal file
Просмотреть файл

@ -0,0 +1 @@
SUBDIRS= viewer runtime-profiler

Просмотреть файл

@ -0,0 +1,4 @@
2005-01-09 Ben Maurer <bmaurer@ximian.com>
* *: Initial Import.

Просмотреть файл

@ -0,0 +1,7 @@
lib_LTLIBRARIES = libmono-profiler-heap.la
libmono_profiler_heap_la_SOURCES = gc-profiler.c
libmono_profiler_heap_la_LIBADD = @CCOMPILE_LIBS@
INCLUDES = @CCOMPILE_CFLAGS@ -Wall

Просмотреть файл

@ -0,0 +1,489 @@
/*
* gc-profiler.c - A heap profiler
*
* Author:
* Ben Maurer <bmaurer@ximian.com>
*
*/
#include <string.h>
#include <glib.h>
#include <mono/io-layer/io-layer.h>
#include <mono/metadata/class.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/object.h>
#include <mono/metadata/profiler.h>
#define leu32 GUINT32_TO_LE
#define leu64 GINT64_TO_LE
typedef struct AllocRec AllocRec;
typedef struct {
guint64 alloc_pos;
guint32 time;
guint32 alloc_ctx;
} HeapProfGcFreedRec;
struct AllocRec {
AllocRec* next;
MonoObject* obj;
HeapProfGcFreedRec rec;
};
struct _MonoProfiler {
FILE* out;
char* file;
GPtrArray* klass_table;
GHashTable* klass_to_table_idx;
GPtrArray* method_table;
GHashTable* method_to_table_idx;
GPtrArray* bt_table;
GHashTable* bt_to_table_idx;
GPtrArray* ctx_table;
GHashTable* ctx_to_table_idx;
AllocRec* live_allocs;
guint32 t_zero;
guint64 foffset;
};
static CRITICAL_SECTION hp_lock;
#define hp_lock_enter() EnterCriticalSection (&hp_lock)
#define hp_lock_leave() LeaveCriticalSection (&hp_lock)
/* binary file format */
static const guint8 heap_prof_dump_sig [] = {
0x68, 0x30, 0xa4, 0x57, 0x18, 0xec, 0xd6, 0xa1,
0x61, 0x9c, 0x1d, 0x43, 0xe1, 0x47, 0x27, 0xb6
};
static const guint8 heap_prof_md_sig [] = {
0xe4, 0x37, 0x29, 0x60, 0x3e, 0x31, 0x89, 0x12,
0xaa, 0x93, 0xc8, 0x76, 0xf4, 0x6a, 0x95, 0x11
};
static const guint32 heap_prof_version = 2;
#define BT_SIZE 5
typedef struct {
guint8 signature [16];
guint32 version;
} HeapProfHeader;
typedef struct {
guint32 time;
guint32 alloc_ctx;
} HeapProfAllocationRec;
typedef struct {
guint32 time;
guint32 gc_num;
HeapProfGcFreedRec freed [MONO_ZERO_LEN_ARRAY];
} HeapProfGCRec;
static guint32
get_delta_t (MonoProfiler *p)
{
return GetTickCount () - p->t_zero;
}
static guint64
write (MonoProfiler* p, gconstpointer data, guint32 size)
{
guint32 offset = p->foffset;
p->foffset += size;
fwrite (data, size, 1, p->out);
return offset;
}
typedef struct {
MonoMethod* methods [BT_SIZE];
} Backtrace;
typedef struct {
guint32 len;
guint32 methods [BT_SIZE];
} IdxBacktrace;
typedef struct {
MonoClass* klass;
guint32 size;
Backtrace bt;
} AllocationCtx;
typedef struct {
guint32 klass;
guint32 size;
guint32 bt;
} IdxAllocationCtx;
static guint32
get_method_idx (MonoProfiler *p, MonoMethod* m)
{
guint32 idx_plus_one;
if (!(idx_plus_one = GPOINTER_TO_UINT (g_hash_table_lookup (p->method_to_table_idx, m)))) {
char* name = mono_method_full_name (m, TRUE);
g_ptr_array_add (p->method_table, name);
idx_plus_one = p->method_table->len;
g_hash_table_insert (p->method_to_table_idx, m, idx_plus_one);
}
return idx_plus_one - 1;
}
static guint32
get_type_idx (MonoProfiler *p, MonoClass* klass)
{
guint32 idx_plus_one;
if (!(idx_plus_one = GPOINTER_TO_UINT (g_hash_table_lookup (p->klass_to_table_idx, klass)))) {
char* name = mono_type_get_full_name (mono_class_get_type (klass));
g_ptr_array_add (p->klass_table, name);
idx_plus_one = p->klass_table->len;
g_hash_table_insert (p->klass_to_table_idx, klass, idx_plus_one);
}
return idx_plus_one - 1;
}
static guint32
get_bt_idx (MonoProfiler *p, Backtrace* bt)
{
guint32 idx_plus_one;
if (!(idx_plus_one = GPOINTER_TO_UINT (g_hash_table_lookup (p->bt_to_table_idx, bt)))) {
IdxBacktrace* ibt = g_new0 (IdxBacktrace, 1);
for (ibt->len = 0; ibt->len < BT_SIZE; ibt->len ++) {
if (! bt->methods [ibt->len])
break;
ibt->methods [ibt->len] = get_method_idx (p, bt->methods [ibt->len]);
}
g_ptr_array_add (p->bt_table, ibt);
idx_plus_one = p->bt_table->len;
g_hash_table_insert (p->bt_to_table_idx, g_memdup (bt, sizeof (*bt)), idx_plus_one);
}
return idx_plus_one - 1;
}
static guint32
get_ctx_idx (MonoProfiler *p, AllocationCtx* ctx)
{
guint32 idx_plus_one;
if (!(idx_plus_one = GPOINTER_TO_UINT (g_hash_table_lookup (p->ctx_to_table_idx, ctx)))) {
IdxAllocationCtx* ictx = g_new0 (IdxAllocationCtx, 1);
ictx->klass = get_type_idx (p, ctx->klass);
ictx->size = ctx->size;
ictx->bt = get_bt_idx (p, &ctx->bt);
g_ptr_array_add (p->ctx_table, ictx);
idx_plus_one = p->ctx_table->len;
g_hash_table_insert (p->ctx_to_table_idx, g_memdup (ctx, sizeof (*ctx)), idx_plus_one);
}
return idx_plus_one - 1;
}
typedef struct {
AllocationCtx* c;
int pos;
} AllocBTData;
static gboolean
get_bt (MonoMethod *m, gint no, gint ilo, gboolean managed, AllocBTData* data)
{
if (!managed)
return FALSE;
data->c->bt.methods [data->pos++] = m;
return data->pos == BT_SIZE;
}
static void
write_allocation (MonoProfiler *p, MonoObject *obj, MonoClass *klass)
{
AllocBTData btd = {0};
AllocationCtx c = {0};
guint32 offset;
HeapProfAllocationRec rec;
AllocRec* arec = g_new0 (AllocRec, 1);
btd.c = &c;
mono_stack_walk_no_il (get_bt, &btd);
c.klass = klass;
c.size = mono_object_get_size (obj);
hp_lock_enter ();
rec.time = leu32 (get_delta_t (p));
rec.alloc_ctx = leu32 (get_ctx_idx (p, &c));
offset = write (p, &rec, sizeof (rec));
arec->rec.time = rec.time;
arec->rec.alloc_ctx = rec.alloc_ctx;
arec->rec.alloc_pos = leu64 (offset);
arec->obj = obj;
arec->next = p->live_allocs;
p->live_allocs = arec;
hp_lock_leave ();
}
static void
prof_marks_set (MonoProfiler *p, int gc_num)
{
HeapProfGCRec rec;
hp_lock_enter ();
rec.time = leu32 (get_delta_t (p) | (1 << 31));
rec.gc_num = leu32 (gc_num);
write (p, &rec, sizeof (rec));
AllocRec *l, *next = NULL, *prev = NULL;
for (l = p->live_allocs; l; l = next) {
next = l->next;
if (! mono_profiler_mark_set (l->obj)) {
write (p, &l->rec, sizeof (l->rec));
if (prev)
prev->next = next;
else
p->live_allocs = next;
g_free (l);
} else
prev = l;
}
{
HeapProfGcFreedRec null = {0};
write (p, &null, sizeof (null));
}
hp_lock_leave ();
}
static void
write_enc_int (MonoProfiler*p, int v)
{
do {
int high = (v >> 7) & 0x01ffffff;
guint8 b = (guint8) (v & 0x7f);
if (high != 0) {
b = (guint8) (b | 0x80);
}
write (p, &b, sizeof (b));
v = high;
} while (v);
}
static void
write_string_table (MonoProfiler* p, GPtrArray* arr)
{
int i;
guint32 size = leu32 (arr->len);
write (p, &size, sizeof (size));
for (i = 0; i < arr->len; i ++) {
char* s = g_ptr_array_index (arr, i);
int l = strlen (s);
write_enc_int (p, l);
write (p, s, l);
}
}
static void
write_bt_table (MonoProfiler* p)
{
GPtrArray* arr = p->bt_table;
int i;
guint32 size = leu32 (arr->len);
write (p, &size, sizeof (size));
for (i = 0; i < arr->len; i ++) {
IdxBacktrace* b = g_ptr_array_index (arr, i);
write (p, b, sizeof (*b));
}
}
static void
write_ctx_table (MonoProfiler* p)
{
GPtrArray* arr = p->ctx_table;
int i;
guint32 size = leu32 (arr->len);
write (p, &size, sizeof (size));
for (i = 0; i < arr->len; i ++) {
IdxAllocationCtx* c = g_ptr_array_index (arr, i);
write (p, c, sizeof (*c));
}
}
static void
write_meta_header (MonoProfiler* p)
{
HeapProfHeader h;
memcpy (h.signature, heap_prof_md_sig, sizeof (heap_prof_md_sig));
h.version = leu32 (heap_prof_version);
write (p, &h, sizeof (h));
}
static void
write_metadata_file (MonoProfiler* p)
{
write_meta_header (p);
write_string_table (p, p->klass_table);
write_string_table (p, p->method_table);
write_bt_table (p);
write_ctx_table (p);
}
static void
mono_heap_prof_shutdown (MonoProfiler *p)
{
guint32 eofevent = -1;
guint64 meta_offset;
meta_offset = write (p, &eofevent, sizeof (eofevent)) + sizeof (eofevent);
write_metadata_file (p);
meta_offset = leu64 (meta_offset);
write (p, &meta_offset, sizeof (meta_offset));
fclose (p->out);
}
static void
write_header (MonoProfiler* p)
{
HeapProfHeader h;
memcpy (h.signature, heap_prof_dump_sig, sizeof (heap_prof_dump_sig));
h.version = leu32 (heap_prof_version);
write (p, &h, sizeof (h));
}
static guint
ctx_hash (const AllocationCtx* c)
{
const guint* x = c;
int i, h = 0;
for (i = 0; i < sizeof (*c) / sizeof (*x); i ++) {
h *= 31;
h += x [i];
}
return h;
}
static gboolean
ctx_eq (const AllocationCtx* a, const AllocationCtx* b)
{
return !memcmp (a, b, sizeof (*a));
}
static guint
bt_hash (const Backtrace* c)
{
const guint* x = c;
int i, h = 0;
for (i = 0; i < sizeof (*c) / sizeof (*x); i ++) {
h *= 31;
h += x [i];
}
return h;
}
static gboolean
bt_eq (const Backtrace* a, const Backtrace* b)
{
return !memcmp (a, b, sizeof (*a));
}
void
mono_profiler_startup (const char *desc)
{
const char* file;
char* dump_file;
MonoProfiler* p = g_new0 (MonoProfiler, 1);
InitializeCriticalSection (&hp_lock);
g_assert (! strncmp (desc, "heap", 4));
if (strncmp (desc, "heap:", 5))
g_error ("You need to specify an output file for the heap profiler with --profile=heap:outfile");
p->file = strdup (desc + 5);
p->klass_to_table_idx = g_hash_table_new (NULL, NULL);
p->method_to_table_idx = g_hash_table_new (NULL, NULL);
p->bt_to_table_idx = g_hash_table_new (bt_hash, bt_eq);
p->ctx_to_table_idx = g_hash_table_new (ctx_hash, ctx_eq);
p->klass_table = g_ptr_array_new ();
p->method_table = g_ptr_array_new ();
p->bt_table = g_ptr_array_new ();
p->ctx_table = g_ptr_array_new ();
p->out = fopen (p->file, "w+");
p->t_zero = GetTickCount ();
write_header (p);
mono_profiler_install_allocation (write_allocation);
mono_profiler_install_gc (prof_marks_set);
mono_profiler_set_events (MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_GC);
mono_profiler_install (p, mono_heap_prof_shutdown);
}

4
src/viewer/ChangeLog Normal file
Просмотреть файл

@ -0,0 +1,4 @@
2005-01-09 Ben Maurer <bmaurer@ximian.com>
* *: Initial Import.

26
src/viewer/Makefile.am Normal file
Просмотреть файл

@ -0,0 +1,26 @@
CLEANFILES = mono-heap-prof-view.exe mono-heap-prof-view
mono_heap_prof_view_SCRIPTS = mono-heap-prof-view.exe
mono_heap_prof_view_DATA = mono-heap-prof-view.exe.config
mono_heap_prof_viewdir = $(prefix)/lib/mono-heap-prof
common_sources = \
$(srcdir)/common/TypeTabulator.cs \
$(srcdir)/common/ProfileReader.cs \
$(srcdir)/common/BacktraceTabulator.cs \
$(srcdir)/common/TypeGraphPlotter.cs
gtk_sources = \
$(srcdir)/gui-gtk/TypeGraphViewer.cs \
$(srcdir)/gui-gtk/BacktraceViewer.cs
mono_heap_prof_view_sources = $(common_sources) $(gtk_sources)
mono-heap-prof-view.exe : $(mono_heap_prof_view_sources)
$(MCS) /out:$@ $(mono_heap_prof_view_sources) /r:System.Drawing.dll @GTKSHARP_LIBS@
scriptdir = $(bindir)
script_SCRIPTS = mono-heap-prof-view

Просмотреть файл

@ -0,0 +1,183 @@
using System;
using System.Collections;
class AllocNode : IComparable {
public int n_allocs;
public int n_bytes;
public ArrayList Children;
public AllocNode Parent;
public int type;
public int [] bt;
public int bt_len;
public BacktraceTabulator tab;
public AllocNode () {}
public AllocNode (int t, int [] bt, int bt_len, BacktraceTabulator tab)
{
this.type = t;
this.bt = bt;
this.bt_len = bt_len;
this.tab = tab;
tab.nodes.Add (this, this);
if (bt_len != 0) {
Parent = tab.LookupNode (t, bt, bt_len - 1);
if (Parent.Children == null)
Parent.Children = new ArrayList ();
Parent.Children.Add (this);
} else {
tab.type_nodes.Add (this);
}
}
public void RecordAlloc (int c, int b)
{
for (AllocNode n = this; n != null; n = n.Parent) {
n.n_allocs += c;
n.n_bytes += b;
}
}
public override bool Equals (object o)
{
AllocNode a = o as AllocNode;
if (a == null)
return false;
if (a.type != type || a.bt_len != bt_len)
return false;
for (int i = 0; i < bt_len; i ++)
if (a.bt [i] != bt [i])
return false;
return true;
}
public override int GetHashCode ()
{
int h = type ^ bt_len;
for (int i = 0; i < bt_len; i ++) {
h ^= bt [i];
h *= 31;
}
return h;
}
public int CompareTo (object o)
{
int nb = ((AllocNode) o).n_bytes;
return nb - n_bytes;
}
}
class BacktraceTabulator {
public Hashtable nodes;
public TypeTabulator t;
public ArrayList type_nodes;
public int total_size;
public BacktraceTabulator (TypeTabulator t, int [] context_data)
{
this.t = t;
nodes = new Hashtable ();
type_nodes = new ArrayList ();
for (int i = 0; i < context_data.Length; i ++) {
if (context_data [i] == 0)
continue;
Context c = t.GetContext (i);
int [] bt = t.GetBacktrace (c.Backtrace);
LookupNode (c.Type, bt, bt.Length).RecordAlloc (context_data [i], context_data [i] * c.Size);
total_size += total_size;
}
SortRecursive (type_nodes);
}
static void SortRecursive (ArrayList ar)
{
if (ar == null)
return;
ar.Sort ();
foreach (AllocNode an in ar)
SortRecursive (an.Children);
}
AllocNode temp_node = new AllocNode ();
public AllocNode LookupNode (int t, int [] bt, int bt_len)
{
temp_node.type = t;
temp_node.bt = bt;
temp_node.bt_len = bt_len;
AllocNode ret = nodes [temp_node] as AllocNode;
if (ret != null)
return ret;
return new AllocNode (t, bt, bt_len, this);
}
public void Dump ()
{
foreach (AllocNode an in type_nodes) {
if (an.n_bytes < total_size * .15)
continue;
Console.WriteLine ("{0} -- {1} bytes, {2} objects", t.GetTypeName (an.type), an.n_bytes, an.n_allocs);
WriteAllocSitesRecursive (an.Children, "\t");
}
}
public void WriteAllocSitesRecursive (ArrayList ar, string pre)
{
if (ar == null)
return;
foreach (AllocNode an in ar) {
Console.WriteLine (pre + "{0} -- {1} bytes, {2} objects", t.GetMethodName (an.bt [an.bt_len - 1]), an.n_bytes, an.n_allocs);
WriteAllocSitesRecursive (an.Children, pre + "\t");
}
}
/*
static void Main (string [] args)
{
TypeTabulator t = new TypeTabulator (args [0]);
t.Read ();
t.Process ();
foreach (TimeData d in t.Data) {
if (d.TotalSize == 0)
continue;
Console.WriteLine ("Heap at {0} ms", d.Time);
Console.WriteLine ("Total heap size {0}", d.TotalSize);
new BacktraceTabulator (t, d.ContextData).Dump ();
}
}*/
}

Просмотреть файл

@ -0,0 +1,4 @@
2005-01-09 Ben Maurer <bmaurer@ximian.com>
* *: Initial Import.

Просмотреть файл

@ -0,0 +1,238 @@
using System;
using System.IO;
public abstract class ProfileReader {
Metadata mr;
BinaryReader br;
string name;
public ProfileReader (string name)
{
this.name = name;
mr = new Metadata (name);
//mr.Dump ();
}
public void Read ()
{
using (br = new BinaryReader (File.OpenRead (name))) {
ProfilerSignature.ReadHeader (br, true);
while (true) {
int time = br.ReadInt32 ();
// end of file
if (time == -1)
return;
if ((time & (int)(1 << 31)) == 0) {
// allocation
int ctx = br.ReadInt32 ();
AllocationSeen (time, GetContext (ctx), br.BaseStream.Position);
} else {
time &= int.MaxValue;
int gc_num = br.ReadInt32 ();
GcSeen (time, gc_num);
}
}
}
}
protected void ReadGcFreed ()
{
while (true) {
long pos = br.ReadInt64 ();
int alloc_time = br.ReadInt32 ();
int alloc_ctx = br.ReadInt32 ();
if (pos == 0 && alloc_time == 0 && alloc_ctx == 0)
return;
GcFreedSeen (alloc_time, GetContext (alloc_ctx), pos);
}
}
protected abstract void AllocationSeen (int time, Context ctx, long pos);
protected abstract void GcSeen (int time, int gc_num);
protected abstract void GcFreedSeen (int time, Context ctx, long pos);
public string GetTypeName (int idx)
{
return mr.GetTypeName (idx);
}
public string GetMethodName (int idx)
{
return mr.GetMethodName (idx);
}
public int [] GetBacktrace (int idx)
{
return mr.GetBacktrace (idx);
}
public Context GetContext (int idx)
{
return mr.GetContext (idx);
}
public int TypeTableSize { get { return mr.TypeTableSize; } }
public int ContextTableSize { get { return mr.ContextTableSize; } }
}
public class Metadata {
const int BacktraceSize = 5;
string [] typeTable;
string [] methodTable;
int [][] backtraceTable;
Context [] contextTable;
public int TypeTableSize { get { return typeTable.Length; } }
public int ContextTableSize { get { return contextTable.Length; } }
public string GetTypeName (int idx)
{
return typeTable [idx];
}
public string GetMethodName (int idx)
{
return methodTable [idx];
}
public int [] GetBacktrace (int idx)
{
return backtraceTable [idx];
}
public Context GetContext (int idx)
{
return contextTable [idx];
}
public Metadata (string name)
{
using (BinaryReader br = new BinaryReader (File.OpenRead (name))) {
br.BaseStream.Seek (-8, SeekOrigin.End);
br.BaseStream.Seek (br.ReadInt64 (), SeekOrigin.Begin);
ProfilerSignature.ReadHeader (br, false);
typeTable = ReadStringTable (br);
methodTable = ReadStringTable (br);
backtraceTable = ReadBacktraceTable (br);
contextTable = ReadContextTable (br);
}
}
public void Dump ()
{
foreach (Context c in contextTable) {
Console.WriteLine ("size {0}, type {1}", c.Size, typeTable [c.Type]);
foreach (int i in backtraceTable [c.Backtrace])
Console.WriteLine (" {0} {1}", i, methodTable [i]);
Console.WriteLine ();
}
}
string [] ReadStringTable (BinaryReader br)
{
int sz = br.ReadInt32 ();
string [] ret = new string [sz];
for (int i = 0; i < sz; i ++)
ret [i] = br.ReadString ();
return ret;
}
int [] [] ReadBacktraceTable (BinaryReader br)
{
int sz = br.ReadInt32 ();
int [][] t = new int [sz] [];
for (int i = 0; i < sz; i ++) {
int szz = br.ReadInt32 ();
t [i] = new int [szz];
for (int j = 0; j < BacktraceSize; j ++) {
int n = br.ReadInt32 ();
if (j < szz)
t [i] [j] = n;
}
}
return t;
}
Context [] ReadContextTable (BinaryReader br)
{
int sz = br.ReadInt32 ();
Context [] d = new Context [sz];
for (int i = 0; i < sz; i ++) {
d [i].Id = i;
d [i].Type = br.ReadInt32 ();
d [i].Size = br.ReadInt32 ();
d [i].Backtrace = br.ReadInt32 ();
}
return d;
}
}
class ProfilerSignature {
static readonly byte [] DumpSignature = {
0x68, 0x30, 0xa4, 0x57, 0x18, 0xec, 0xd6, 0xa1,
0x61, 0x9c, 0x1d, 0x43, 0xe1, 0x47, 0x27, 0xb6
};
static readonly byte [] MetaSignature = {
0xe4, 0x37, 0x29, 0x60, 0x3e, 0x31, 0x89, 0x12,
0xaa, 0x93, 0xc8, 0x76, 0xf4, 0x6a, 0x95, 0x11
};
const int Version = 2;
public static void ReadHeader (BinaryReader br, bool is_dump)
{
byte [] s = is_dump ? DumpSignature : MetaSignature;
byte [] sig = br.ReadBytes (s.Length);
for (int i = 0; i < s.Length; i ++) {
if (sig [i] != s [i])
throw new Exception ("Invalid file format");
}
int ver = br.ReadInt32 ();
if (ver != Version)
throw new Exception (String.Format ("Wrong version: expected {0}, got {1}", Version, ver));
}
}
public struct Context {
public int Id;
public int Type;
public int Size;
public int Backtrace;
}

Просмотреть файл

@ -0,0 +1,175 @@
using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Drawing2D;
class TimePoint {
public int Time;
public int X;
public int OtherSize;
public int [] TypeData;
public TimeData Data;
}
class Plotter {
int xsize, ysize;
TypeTabulator d;
TypeList tl;
public Plotter (int xsize, int ysize, TypeTabulator d, TypeList tl)
{
this.xsize = xsize;
this.ysize = ysize;
this.d = d;
this.tl = tl;
FixupData ();
}
public ArrayList data;
int end_t;
void FixupData ()
{
end_t = ((TimeData) d.Data [d.Data.Count - 1]).Time;
data = new ArrayList ();
int size_threshold = d.MaxSize / ysize;
foreach (TimeData td in d.Data) {
if (td.TotalSize < size_threshold)
continue;
TimePoint p = new TimePoint ();
data.Add (p);
p.Data = td;
p.Time = td.Time;
p.X = td.Time * xsize / end_t;
p.OtherSize = td.OtherSize;
p.TypeData = new int [tl.TypeIndexes.Length];
for (int i = 0; i < tl.TypeIndexes.Length; i ++) {
int ty = tl.TypeIndexes [i];
if (td.TypeData [ty] < size_threshold) {
p.OtherSize += td.TypeData [ty];
continue;
}
p.TypeData [i] = td.TypeData [ty];
}
}
}
public void Draw (Graphics g)
{
int [] offsets = new int [data.Count];
Point [] prev = new Point [data.Count];
for (int i = 0; i < prev.Length; i ++)
prev [i].Y = ysize;
prev [0].X = xsize;
for (int i = -1; i < tl.TypeIndexes.Length; i ++) {
Point [] line = new Point [data.Count];
int j = 0;
foreach (TimePoint tp in data) {
int psize;
if (i == -1)
psize = tp.OtherSize;
else
psize = tp.TypeData [i];
line [j].X = tp.X;
line [j].Y = ysize - checked (offsets [j] + (int)((long)psize * (long) ysize / (long)d.MaxSize));
offsets [j] = ysize - line [j].Y;
j ++;
}
GraphicsPath path = new GraphicsPath ();
path.AddLines (line);
path.AddLine (line [line.Length - 1], prev [0]);
path.AddLines (prev);
//path.CloseFigure ();
Brush b;
if (i == -1)
b = Brushes.DarkGray;
else
b = tl.TypeBrushes [i];
g.FillPath (b, path);
prev = line;
Array.Reverse (prev, 0, prev.Length);
}
}
}
class RandomBrush {
int i;
static Brush [] brushes = {
Brushes.IndianRed,
Brushes.BurlyWood,
Brushes.Chocolate,
Brushes.DarkGoldenrod,
Brushes.PaleGoldenrod,
Brushes.DarkOrange,
Brushes.DarkSalmon,
};
public Brush Next ()
{
return brushes [i ++ % brushes.Length];
}
}
class TypeList {
public long [] Sizes;
public int [] TypeIndexes;
public Brush [] TypeBrushes;
public string [] Names;
public TypeList (TypeTabulator d)
{
int num = 0;
foreach (bool b in d.IsSizeLongEnough)
if (b)
num ++;
Sizes = new long [num];
TypeIndexes = new int [num];
Names = new string [num];
TypeBrushes = new Brush [num];
num = 0;
for (int i = 0; i < d.TotalTypeSizes.Length; i ++) {
if (d.IsSizeLongEnough [i]) {
Sizes [num] = d.TotalTypeSizes [i];
TypeIndexes [num] = i;
num ++;
}
}
Array.Sort (Sizes, TypeIndexes);
RandomBrush rb = new RandomBrush ();
for (int i = 0; i < Sizes.Length; i ++) {
TypeBrushes [i] = rb.Next ();
Names [i] = d.GetTypeName (TypeIndexes [i]);
}
}
}

Просмотреть файл

@ -0,0 +1,141 @@
using System;
using System.Collections;
class TimeData {
public int Time;
public int [] TypeData;
public int [] ContextData;
public int OtherSize;
public int TotalSize;
}
class TypeTabulator : ProfileReader {
const int DeltaT = 50;
const double Threshold = .005;
public ArrayList Data;
public int MaxSize;
public long [] TotalTypeSizes;
public bool [] IsSizeLongEnough;
int [] current_type_data;
int [] current_context_data;
int last_time;
public TypeTabulator (string basename) : base (basename) {
Data = new ArrayList ();
current_type_data = new int [TypeTableSize];
current_context_data = new int [ContextTableSize];
}
void Split (int time)
{
TimeData td = new TimeData ();
td.Time = time - 1;
td.TypeData = (int []) current_type_data.Clone ();
td.ContextData = (int []) current_context_data.Clone ();
foreach (int i in td.TypeData)
td.TotalSize += i;
MaxSize = Math.Max (td.TotalSize, MaxSize);
Data.Add (td);
}
void SplitIfNeeded (int time)
{
if (time < last_time + DeltaT)
return;
Split (time - 1);
last_time = time;
}
protected override void AllocationSeen (int time, Context ctx, long pos)
{
SplitIfNeeded (time);
current_type_data [ctx.Type] += ctx.Size;
current_context_data [ctx.Id] ++;
}
protected override void GcSeen (int time, int gc_num)
{
// Splitting twice here gives nice graphs, since you get a strait line
Split (time);
ReadGcFreed ();
Split (time);
last_time = time;
}
protected override void GcFreedSeen (int time, Context ctx, long pos)
{
current_type_data [ctx.Type] -= ctx.Size;
current_context_data [ctx.Id] --;
}
public void Dump ()
{
long [] sizes = (long []) TotalTypeSizes.Clone ();
int [] indexes = new int [sizes.Length];
for (int i = 0; i < indexes.Length; i ++)
indexes [i] = i;
Array.Sort (sizes, indexes);
Array.Reverse (sizes, 0, sizes.Length);
Array.Reverse (indexes, 0, indexes.Length);
foreach (TimeData d in Data) {
if (d.TotalSize == 0)
continue;
Console.WriteLine ("Heap at {0} ms", d.Time);
Console.WriteLine ("Total heap size {0}", d.TotalSize);
foreach (int ty in indexes) {
if (!IsSizeLongEnough [ty])
continue;
Console.WriteLine ("{0} ({2:p}) -- {1}", d.TypeData [ty], GetTypeName (ty), (double) d.TypeData [ty] / (double) d.TotalSize);
}
Console.WriteLine ();
}
}
public void Process ()
{
int cutoff = (int) (MaxSize * Threshold);
TotalTypeSizes = new long [TypeTableSize];
IsSizeLongEnough = new bool [TypeTableSize];
foreach (TimeData d in Data) {
for (int i = 0; i < d.TypeData.Length; i ++) {
TotalTypeSizes [i] += d.TypeData [i];
if (d.TypeData [i] > cutoff)
IsSizeLongEnough [i] = true;
}
}
foreach (TimeData d in Data) {
for (int i = 0; i < d.TypeData.Length; i ++) {
if (! IsSizeLongEnough [i]) {
d.OtherSize += d.TypeData [i];
d.TypeData [i] = 0;
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,181 @@
using System;
using System.Collections;
using Gtk;
class BacktraceViewerWindow : Window {
TimeData data;
TypeTabulator t;
BacktraceTabulator bt;
BacktraceNodeStore ns;
NodeView nv;
VBox box;
public BacktraceViewerWindow (TimeData data, TypeTabulator t) : base ("")
{
this.data = data;
this.t = t;
this.bt = new BacktraceTabulator (t, data.ContextData);
box = new VBox ();
box.Spacing = 12;
this.Add (box);
box.PackStart (CreateHeader (), false, false, 0);
ns = new BacktraceNodeStore (data, t, bt);
ScrolledWindow sw = new ScrolledWindow ();
sw.Add (ns.GetNodeView ());
box.PackStart (sw, true, true, 0);
Title = string.Format ("Heap at {0} ms", data.Time);
}
Widget CreateHeader ()
{
VBox vb = new VBox ();
vb.Spacing = 12;
vb.BorderWidth = 12;
Label l = new Label (string.Format ("<b>Heap at {0} ms</b>", data.Time));
l.Xalign = 0;
l.UseMarkup = true;
vb.PackStart (l, false, false, 0);
HBox hb = new HBox ();
hb.Spacing = 12;
l = new Label ("Heap Size:");
l.Xalign = 0;
l.Xpad = 12;
hb.PackStart (l, false, false, 0);
l = new Label (FormatHelper.BytesToString (data.TotalSize));
l.Xalign = 0;
hb.PackStart (l, false, false, 0);
vb.PackStart (hb, false, false, 0);
return vb;
}
}
class BacktraceNodeStore : NodeStore {
TimeData data;
TypeTabulator t;
BacktraceTabulator bt;
public BacktraceNodeStore (TimeData data, TypeTabulator t, BacktraceTabulator bt) : base (typeof (BacktraceNode))
{
this.data = data;
this.t = t;
this.bt = bt;
foreach (AllocNode an in bt.type_nodes) {
BacktraceNode n = new BacktraceNode (data, t, an);
ProcessNode (n);
AddNode (n);
}
}
void ProcessNode (BacktraceNode n)
{
AllocNode p = n.an;
if (p.Children == null)
return;
foreach (AllocNode an in p.Children) {
BacktraceNode nn = new BacktraceNode (data, t, an);
ProcessNode (nn);
n.AddChild (nn);
}
}
public NodeView GetNodeView ()
{
NodeView nv = new NodeView (this);
nv.HeadersVisible = false;
nv.AppendColumn ("Size", new CellRendererText (), new NodeCellDataFunc (GetNumBytes));
nv.AppendColumn ("Num objects", new CellRendererText (), new NodeCellDataFunc (GetNumObjects));
nv.AppendColumn ("Percent", new CellRendererText (), new NodeCellDataFunc (GetPercent));
nv.AppendColumn ("Source", new CellRendererText (), "text", 0);
return nv;
}
private void GetNumBytes (TreeViewColumn col, CellRenderer cell, ITreeNode node)
{
CellRendererText c = (CellRendererText) cell;
BacktraceNode n = (BacktraceNode) node;
c.Text = FormatHelper.BytesToString (n.an.n_bytes);
}
private void GetPercent (TreeViewColumn col, CellRenderer cell, ITreeNode node)
{
CellRendererText c = (CellRendererText) cell;
BacktraceNode n = (BacktraceNode) node;
c.Text = String.Format ("{0:p}", (double) n.an.n_bytes / (double) n.data.TotalSize);
}
private void GetNumObjects (TreeViewColumn col, CellRenderer cell, ITreeNode node)
{
CellRendererText c = (CellRendererText) cell;
BacktraceNode n = (BacktraceNode) node;
c.Text = n.an.n_allocs.ToString ();
}
}
[TreeNode (ColumnCount = 1)]
class BacktraceNode : TreeNode {
public TimeData data;
TypeTabulator t;
public AllocNode an;
public BacktraceNode (TimeData data, TypeTabulator t, AllocNode an)
{
this.data = data;
this.t = t;
this.an = an;
}
[TreeNodeValue (Column = 0)]
public string Name {
get {
if (an.bt_len == 0)
return t.GetTypeName (an.type);
else
return t.GetMethodName (an.bt [an.bt_len - 1]);
}
}
}
class FormatHelper {
public static string BytesToString (int cb)
{
const int K = 1024;
const int M = 1024 * K;
if (cb > M)
return String.Format ("{0:0.0} MB", (double) cb / (double) M);
else if (cb > K)
return String.Format ("{0:0.0} KB", (double) cb / (double) K);
else
return String.Format ("{0} bytes", cb);
}
}

Просмотреть файл

@ -0,0 +1,4 @@
2005-01-09 Ben Maurer <bmaurer@ximian.com>
* *: Initial Import.

Просмотреть файл

@ -0,0 +1,295 @@
using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Drawing2D;
using Gtk;
using System.Reflection;
using System.Runtime.InteropServices;
class X {
static DrawingArea d;
static TypeTabulator t;
static TypeList tl;
static HPaned paned;
static void Main (string [] args)
{
int b = Environment.TickCount;
t = new TypeTabulator (args [0]);
t.Read ();
t.Process ();
//t.Dump ();
tl = new TypeList (t);
Console.WriteLine (Environment.TickCount - b);
RunGtk ();
}
static void RunGtk ()
{
Application.Init ();
Gtk.Window w = new Gtk.Window ("Ben's little profiler");
w.DeleteEvent += Window_Delete;
paned = new HPaned ();
d = new PrettyGraphic (t, tl);
ScrolledWindow sw = new ScrolledWindow ();
sw.Add (new TypeListNodeStore (tl).GetNodeView ());
paned.Add1 (d);
paned.Add2 (sw);
w.Add (paned);
w.ShowAll ();
Application.Run ();
}
static void Window_Delete (object obj, DeleteEventArgs args)
{
Application.Quit ();
args.RetVal = true;
}
}
class TypeListNodeStore : NodeStore {
TypeList tl;
static ColorCellRenderer r;
public TypeListNodeStore (TypeList tl) : base (typeof (TypeListTreeNode))
{
this.tl = tl;
for (int i = tl.Sizes.Length - 1; i >= 0; i --)
AddNode (new TypeListTreeNode (tl, i));
}
public NodeView GetNodeView ()
{
r = new ColorCellRenderer ();
NodeView nv = new NodeView (this);
nv.HeadersVisible = false;
nv.AppendColumn ("Color",r, new NodeCellDataFunc (GetColorData));
nv.AppendColumn ("Type", new CellRendererText (), "text", 1);
return nv;
}
private void GetColorData (TreeViewColumn col, CellRenderer cell, ITreeNode node)
{
ColorCellRenderer c = (ColorCellRenderer) cell;
c.Idx = ((TypeListTreeNode) node).idx;
c.List = tl;
}
}
class ColorCellRenderer : CellRenderer {
public int Idx;
public TypeList List;
Gdk.Color ColorFromBrush (Brush b)
{
Color c = ((SolidBrush) b).Color;
return new Gdk.Color (c.R, c.G, c.B);
}
public override void GetSize (Widget widget, ref Gdk.Rectangle cell_area, out int x_offset, out int y_offset, out int width, out int height)
{
int calc_width = (int) this.Xpad * 2 + 10;
int calc_height = (int) this.Ypad * 2 + 10;
width = calc_width;
height = calc_height;
x_offset = 0;
y_offset = 0;
if (!cell_area.Equals (Gdk.Rectangle.Zero)) {
x_offset = (int) (this.Xalign * (cell_area.Width - calc_width));
x_offset = Math.Max (x_offset, 0);
y_offset = (int) (this.Yalign * (cell_area.Height - calc_height));
y_offset = Math.Max (y_offset, 0);
}
}
protected override void Render (Gdk.Drawable window, Widget widget, Gdk.Rectangle background_area,
Gdk.Rectangle cell_area, Gdk.Rectangle expose_area, CellRendererState flags)
{
int width = 0, height = 0, x_offset = 0, y_offset = 0;
GetSize (widget, ref cell_area, out x_offset, out y_offset, out width, out height);
width -= (int) this.Xpad * 2;
height -= (int) this.Ypad * 2;
Gdk.Rectangle clipping_area = new Gdk.Rectangle ((int) (cell_area.X + x_offset + this.Xpad),
(int) (cell_area.Y + y_offset + this.Ypad), width - 1, height - 1);
using (Gdk.GC gc = new Gdk.GC (window)) {
gc.RgbFgColor = ColorFromBrush (List.TypeBrushes [Idx]);
window.DrawRectangle (gc,
true,
clipping_area);
}
}
}
[TreeNode (ColumnCount = 2)]
class TypeListTreeNode : TreeNode {
TypeList tl;
public int idx;
public TypeListTreeNode (TypeList tl, int idx)
{
this.tl = tl;
this.idx = idx;
}
[TreeNodeValue (Column = 0)]
public Brush Color {
get { return tl.TypeBrushes [idx]; }
}
[TreeNodeValue (Column = 1)]
public string Name {
get { return tl.Names [idx]; }
}
}
//
// A sample using inheritance to draw
//
class PrettyGraphic : DrawingArea {
TypeTabulator t;
TypeList tl;
Gdk.Pixmap bitmap_cache;
//System.Drawing.Bitmap bitmap_cache;
Gdk.Rectangle current_allocation; // The current allocation.
bool allocated = false;
Plotter plot;
public PrettyGraphic (TypeTabulator t, TypeList tl)
{
Events |= Gdk.EventMask.ButtonPressMask;
this.t = t;
this.tl = tl;
SetSizeRequest (500, 500);
}
protected override bool OnExposeEvent (Gdk.EventExpose args)
{
if (bitmap_cache == null) {
bitmap_cache = new Gdk.Pixmap (GdkWindow, current_allocation.Width, current_allocation.Height, -1);
bitmap_cache.DrawRectangle (Style.WhiteGC, true, 0, 0,
current_allocation.Width, current_allocation.Height);
using (Graphics g = Gdk.Graphics.FromDrawable (bitmap_cache)) {
plot = new Plotter (current_allocation.Width, current_allocation.Height, t, tl);
plot.Draw (g);
}
}
Gdk.Rectangle area = args.Area;
GdkWindow.DrawDrawable (Style.BlackGC,
bitmap_cache,
area.X, area.Y,
area.X, area.Y,
area.Width, area.Height);
return true;
}
protected override void OnSizeAllocated (Gdk.Rectangle allocation)
{
allocated = true;
current_allocation = allocation;
UpdateCache ();
base.OnSizeAllocated (allocation);
}
void UpdateCache ()
{
if (bitmap_cache != null)
bitmap_cache.Dispose ();
bitmap_cache = null;
}
protected override bool OnButtonPressEvent (Gdk.EventButton e)
{
if (e.Button != 3)
return false;
Console.WriteLine ("Button press at ({0}, {1})", e.X, e.Y);
foreach (TimePoint tp in plot.data) {
if (tp.X >= e.X) {
Console.WriteLine ("Found {0}", tp.Time);
new BacktraceViewerWindow (tp.Data, t).ShowAll ();
break;
}
}
return true;
}
}
namespace Gdk {
public class Graphics {
[DllImport("libgdk-win32-2.0-0.dll")]
internal static extern IntPtr gdk_x11_drawable_get_xdisplay (IntPtr raw);
[DllImport("libgdk-win32-2.0-0.dll")]
internal static extern IntPtr gdk_x11_drawable_get_xid (IntPtr raw);
public static System.Drawing.Graphics FromDrawable (Gdk.Drawable drawable)
{
IntPtr x_drawable;
int x_off = 0, y_off = 0;
if (drawable is Gdk.Window){
((Gdk.Window) drawable).GetInternalPaintInfo(out drawable, out x_off, out y_off);
}
x_drawable = drawable.Handle;
IntPtr display = gdk_x11_drawable_get_xdisplay (x_drawable);
Type graphics = typeof (System.Drawing.Graphics);
MethodInfo mi = graphics.GetMethod ("FromXDrawable", BindingFlags.Static | BindingFlags.NonPublic);
if (mi == null)
throw new NotImplementedException ("In this implementation I can not get a graphics from a drawable");
object [] args = new object [2] { (IntPtr) gdk_x11_drawable_get_xid (drawable.Handle), (IntPtr) display };
object r = mi.Invoke (null, args);
System.Drawing.Graphics g = (System.Drawing.Graphics) r;
g.TranslateTransform (-x_off, -y_off);
return g;
}
}
}

Просмотреть файл

@ -0,0 +1,6 @@
<configuration>
<dllmap dll="libglib-2.0-0.dll" target="libglib-2.0.so.0"/>
<dllmap dll="libgobject-2.0-0.dll" target="libgobject-2.0.so.0"/>
<dllmap dll="libgdk-win32-2.0-0.dll" target="libgdk-x11-2.0.so.0"/>
<dllmap dll="libgdk_pixbuf-2.0-0.dll" target="libgdk_pixbuf-2.0.so.0"/>
</configuration>

Просмотреть файл

@ -0,0 +1,2 @@
#!/bin/sh
exec @MONO@ @prefix@/lib/mono-heap-prof/mono-heap-prof-view.exe "$@"