diff --git a/src/runtime-profiler/ChangeLog b/src/runtime-profiler/ChangeLog index 93c9bdb..93a2688 100644 --- a/src/runtime-profiler/ChangeLog +++ b/src/runtime-profiler/ChangeLog @@ -1,3 +1,9 @@ +2005-01-16 Ben Maurer + + * gc-profiler.c: Emit dumps of the heap about every MB of + data. This will allow the reader to do stuff faster. Also, emit + tables of the size of the heap over time + 2005-01-15 Ben Maurer * gc-profiler.c: Emit heap size info. File format updates. diff --git a/src/runtime-profiler/gc-profiler.c b/src/runtime-profiler/gc-profiler.c index c59b40a..2889d76 100644 --- a/src/runtime-profiler/gc-profiler.c +++ b/src/runtime-profiler/gc-profiler.c @@ -14,8 +14,14 @@ #include #define leu32 GUINT32_TO_LE +#define lnatu32 GUINT32_FROM_LE #define leu64 GINT64_TO_LE +typedef enum { + HEAP_PROF_EVENT_GC = 0, + HEAP_PROF_EVENT_RESIZE_HEAP = 1, + HEAP_PROF_EVENT_CHECKPOINT = 2 +} HeapProfEvent; typedef struct AllocRec AllocRec; @@ -51,6 +57,18 @@ struct _MonoProfiler { AllocRec* live_allocs; guint32 t_zero; guint64 foffset; + + int context_live_objects_size; + int* context_live_objects; + + int type_live_data_size; + int* type_live_data; + + int total_live_bytes; + + GPtrArray* timeline; + + guint64 last_checkpoint; }; @@ -70,10 +88,12 @@ static const guint8 heap_prof_md_sig [] = { 0xaa, 0x93, 0xc8, 0x76, 0xf4, 0x6a, 0x95, 0x11 }; -static const guint32 heap_prof_version = 3; +static const guint32 heap_prof_version = 4; #define BT_SIZE 5 +#define CHECKPOINT_SPACING (1024*1024) /* 1 MB */ + typedef struct { guint8 signature [16]; guint32 version; @@ -85,17 +105,39 @@ typedef struct { } HeapProfAllocationRec; +typedef struct { + guint32 time; + HeapProfEvent event; + guint32 event_num; + + guint32 context_size; + guint32 type_size; +} HeapProfCheckpointRec; typedef struct { guint32 time; - guint32 gc_num; + HeapProfEvent event; + guint32 event_num; + HeapProfGcFreedRec freed [MONO_ZERO_LEN_ARRAY]; } HeapProfGCRec; typedef struct { guint32 time; - guint32 new_size; /* high bit is set */ -} HeapProfGCHeapResize; + HeapProfEvent event; + guint32 event_num; + + guint32 new_size; +} HeapProfHeapResizeRec; + +typedef struct { + guint32 time; + HeapProfEvent event; + guint32 size_high; + guint32 size_low; + + guint64 file_pos; +} HeapProfTimelineRec; static guint32 get_delta_t (MonoProfiler *p) @@ -133,7 +175,15 @@ typedef struct { guint32 bt; } IdxAllocationCtx; +#define resize_array(arr, old_size, new_size) do { \ + gpointer __x = g_malloc0 ((new_size) * sizeof (*arr)); \ + if (arr) \ + memcpy (__x, arr, (old_size) * sizeof (*arr)); \ + arr = __x; \ + old_size = (new_size); \ +} while (0) + static guint32 get_method_idx (MonoProfiler *p, MonoMethod* m) { @@ -161,6 +211,9 @@ get_type_idx (MonoProfiler *p, MonoClass* klass) idx_plus_one = p->klass_table->len; g_hash_table_insert (p->klass_to_table_idx, klass, idx_plus_one); + + if (idx_plus_one > p->type_live_data_size) + resize_array (p->type_live_data, p->type_live_data_size, MAX (p->type_live_data_size << 1, idx_plus_one)); } return leu32 (idx_plus_one - 1); @@ -210,11 +263,34 @@ get_ctx_idx (MonoProfiler *p, AllocationCtx* ctx) idx_plus_one = p->ctx_table->len; g_hash_table_insert (p->ctx_to_table_idx, g_memdup (ctx, sizeof (*ctx)), idx_plus_one); + + + if (idx_plus_one > p->context_live_objects_size) + resize_array (p->context_live_objects, p->context_live_objects_size, MAX (p->context_live_objects_size << 1, idx_plus_one)); } return leu32 (idx_plus_one - 1); } +static void +record_obj (MonoProfiler* p, guint32 ctx_idx, gboolean is_alloc) +{ + guint32 cidx = lnatu32 (ctx_idx); + IdxAllocationCtx* ctx = g_ptr_array_index (p->ctx_table, cidx); + guint32 tidx = lnatu32 (ctx->klass); + guint32 size = lnatu32 (ctx->size); + + if (is_alloc) { + p->total_live_bytes += size; + p->type_live_data [tidx] += size; + p->context_live_objects [cidx] ++; + } else { + p->total_live_bytes -= size; + p->type_live_data [tidx] -= size; + p->context_live_objects [cidx] --; + } +} + typedef struct { AllocationCtx* c; int pos; @@ -231,6 +307,52 @@ get_bt (MonoMethod *m, gint no, gint ilo, gboolean managed, AllocBTData* data) return data->pos == BT_SIZE; } + +static void +write_checkpoint_if_needed (MonoProfiler* p) +{ + HeapProfCheckpointRec rec; + HeapProfTimelineRec* trec; + + guint32* ctx_rec; + guint32* type_rec; + + guint64 pos; + guint32 time = get_delta_t (p); + int i; + + if (p->last_checkpoint + CHECKPOINT_SPACING > p->foffset) + return; + + trec = g_new0 (HeapProfTimelineRec, 1); + rec.time = leu32 (time | (1 << 31)); + rec.event = leu32 (HEAP_PROF_EVENT_CHECKPOINT); + rec.event_num = p->timeline->len + 1; + rec.context_size = leu32 (p->ctx_table->len); + rec.type_size = leu32 (p->klass_table->len); + + ctx_rec = g_newa (guint32, p->ctx_table->len); + type_rec = g_newa (guint32, p->klass_table->len); + + for (i = 0; i < p->ctx_table->len; i ++) + ctx_rec [i] = leu32 (p->context_live_objects [i]); + + for (i = 0; i < p->klass_table->len; i ++) + type_rec [i] = leu32 (p->type_live_data [i]); + + pos = prof_write (p, &rec, sizeof (rec)); + prof_write (p, ctx_rec, sizeof (*ctx_rec) * p->ctx_table->len); + prof_write (p, type_rec, sizeof (*type_rec) * p->klass_table->len); + + trec->time = leu32 (time); + trec->event = leu32 (HEAP_PROF_EVENT_CHECKPOINT); + trec->file_pos = leu64 (pos); + + g_ptr_array_add (p->timeline, trec); + + p->last_checkpoint = p->foffset; +} + static void write_allocation (MonoProfiler *p, MonoObject *obj, MonoClass *klass) { @@ -238,6 +360,7 @@ write_allocation (MonoProfiler *p, MonoObject *obj, MonoClass *klass) AllocationCtx c = {0}; guint32 offset; HeapProfAllocationRec rec; + guint32 ctx_idx; AllocRec* arec = g_new0 (AllocRec, 1); btd.c = &c; @@ -248,9 +371,10 @@ write_allocation (MonoProfiler *p, MonoObject *obj, MonoClass *klass) c.size = mono_object_get_size (obj); hp_lock_enter (); + ctx_idx = get_ctx_idx (p, &c); rec.time = leu32 (get_delta_t (p)); - rec.alloc_ctx = get_ctx_idx (p, &c); + rec.alloc_ctx = ctx_idx; offset = prof_write (p, &rec, sizeof (rec)); @@ -261,6 +385,10 @@ write_allocation (MonoProfiler *p, MonoObject *obj, MonoClass *klass) arec->next = p->live_allocs; p->live_allocs = arec; + record_obj (p, ctx_idx, TRUE); + + write_checkpoint_if_needed (p); + hp_lock_leave (); } @@ -268,13 +396,21 @@ static void prof_marks_set (MonoProfiler *p, int gc_num) { HeapProfGCRec rec; - + HeapProfTimelineRec* trec = g_new0 (HeapProfTimelineRec, 1); + + guint64 pos; + guint32 time = get_delta_t (p); + guint32 old_size; + hp_lock_enter (); - rec.time = leu32 (get_delta_t (p) | (1 << 31)); - rec.gc_num = leu32 (gc_num); + old_size = p->total_live_bytes; + + rec.time = leu32 (time | (1 << 31)); + rec.event = leu32 (HEAP_PROF_EVENT_GC); + rec.event_num = p->timeline->len + 1; - prof_write (p, &rec, sizeof (rec)); + pos = prof_write (p, &rec, sizeof (rec)); AllocRec *l, *next = NULL, *prev = NULL; for (l = p->live_allocs; l; l = next) { @@ -283,6 +419,8 @@ prof_marks_set (MonoProfiler *p, int gc_num) if (! mono_profiler_mark_set (l->obj)) { prof_write (p, &l->rec, sizeof (l->rec)); + record_obj (p, l->rec.alloc_ctx, FALSE); + if (prev) prev->next = next; else @@ -298,20 +436,41 @@ prof_marks_set (MonoProfiler *p, int gc_num) prof_write (p, &null, sizeof (null)); } + trec->time = leu32 (time); + trec->event = leu32 (HEAP_PROF_EVENT_RESIZE_HEAP); + trec->size_high = leu32 (old_size); + trec->size_low = leu32 (p->total_live_bytes); + trec->file_pos = leu64 (pos); + + g_ptr_array_add (p->timeline, trec); + hp_lock_leave (); } static void prof_heap_resize (MonoProfiler *p, int new_size) { - HeapProfGCHeapResize rec; + HeapProfHeapResizeRec rec; + HeapProfTimelineRec* trec = g_new0 (HeapProfTimelineRec, 1); + guint64 pos; + guint32 time = get_delta_t (p); + hp_lock_enter (); - rec.time = leu32 (get_delta_t (p) | (1 << 31)); - rec.new_size = leu32 (new_size | (1 << 31)); + rec.time = leu32 (time | (1 << 31)); + rec.event = leu32 (HEAP_PROF_EVENT_RESIZE_HEAP); + rec.new_size = leu32 (new_size); + rec.event_num = p->timeline->len + 1; - prof_write (p, &rec, sizeof (rec)); + pos = prof_write (p, &rec, sizeof (rec)); + + trec->time = leu32 (time); + trec->event = leu32 (HEAP_PROF_EVENT_RESIZE_HEAP); + trec->size_high = leu32 (new_size); + trec->file_pos = leu64 (pos); + + g_ptr_array_add (p->timeline, trec); hp_lock_leave (); } @@ -349,34 +508,14 @@ write_string_table (MonoProfiler* p, GPtrArray* arr) } static void -write_bt_table (MonoProfiler* p) +write_data_table (MonoProfiler* p, GPtrArray* arr, guint32 elesz) { - GPtrArray* arr = p->bt_table; int i; guint32 size = leu32 (arr->len); prof_write (p, &size, sizeof (size)); - for (i = 0; i < arr->len; i ++) { - IdxBacktrace* b = g_ptr_array_index (arr, i); - prof_write (p, b, sizeof (*b)); - } -} - -static void -write_ctx_table (MonoProfiler* p) -{ - GPtrArray* arr = p->ctx_table; - int i; - guint32 size = leu32 (arr->len); - - prof_write (p, &size, sizeof (size)); - - for (i = 0; i < arr->len; i ++) { - IdxAllocationCtx* c = g_ptr_array_index (arr, i); - - - prof_write (p, c, sizeof (*c)); - } + for (i = 0; i < arr->len; i ++) + prof_write (p, g_ptr_array_index (arr, i), elesz); } static void @@ -397,8 +536,9 @@ write_metadata_file (MonoProfiler* p) write_string_table (p, p->klass_table); write_string_table (p, p->method_table); - write_bt_table (p); - write_ctx_table (p); + write_data_table (p, p->bt_table, sizeof (IdxBacktrace)); + write_data_table (p, p->ctx_table, sizeof (IdxAllocationCtx)); + write_data_table (p, p->timeline, sizeof (HeapProfTimelineRec)); } @@ -472,8 +612,6 @@ bt_eq (const Backtrace* a, const Backtrace* b) void mono_profiler_startup (const char *desc) { - const char* file; - char* dump_file; MonoProfiler* p = g_new0 (MonoProfiler, 1); InitializeCriticalSection (&hp_lock); @@ -494,6 +632,7 @@ mono_profiler_startup (const char *desc) p->method_table = g_ptr_array_new (); p->bt_table = g_ptr_array_new (); p->ctx_table = g_ptr_array_new (); + p->timeline = g_ptr_array_new (); p->out = fopen (p->file, "w+"); diff --git a/src/viewer/common/ChangeLog b/src/viewer/common/ChangeLog index 778b76d..734357d 100644 --- a/src/viewer/common/ChangeLog +++ b/src/viewer/common/ChangeLog @@ -1,5 +1,9 @@ 2005-01-16 Ben Maurer + * ProfileReader.cs: Update file format + + * TypeTabulator.cs: cope with changes + * *.cs: Factor Profile out. * Profile.cs: New file. diff --git a/src/viewer/common/ProfileReader.cs b/src/viewer/common/ProfileReader.cs index 1371484..d33ab47 100644 --- a/src/viewer/common/ProfileReader.cs +++ b/src/viewer/common/ProfileReader.cs @@ -32,12 +32,25 @@ public abstract class ProfileReader { } else { time &= int.MaxValue; - int data = br.ReadInt32 (); + EventType event_type = (EventType) br.ReadInt32 (); + int event_num = br.ReadInt32 (); - if ((data & (int)(1 << 31)) == 0) - GcSeen (time, data); - else - GcHeapResize (time, (data & int.MaxValue)); + switch (event_type) { + case EventType.GC: + GcSeen (time, event_num); + break; + case EventType.HeapResize: + GcHeapResize (time, event_num, br.ReadInt32 ()); + break; + case EventType.Checkpoint: + int context_size = br.ReadInt32 (); + int type_size = br.ReadInt32 (); + + int cb = (context_size + type_size) * 4; + + br.BaseStream.Seek (cb, SeekOrigin.Current); + break; + } } } @@ -59,10 +72,10 @@ public abstract class ProfileReader { } protected abstract void AllocationSeen (int time, Context ctx, long pos); - protected abstract void GcSeen (int time, int gc_num); + protected abstract void GcSeen (int time, int event_num); protected abstract void GcFreedSeen (int time, Context ctx, long pos); - protected virtual void GcHeapResize (int time, int new_size) + protected virtual void GcHeapResize (int time, int event_num, int new_size) { } @@ -105,6 +118,7 @@ public class Metadata { string [] methodTable; int [][] backtraceTable; Context [] contextTable; + Timeline [] timeline; public int TypeTableSize { get { return typeTable.Length; } } public int ContextTableSize { get { return contextTable.Length; } } @@ -143,6 +157,7 @@ public class Metadata { methodTable = ReadStringTable (br); backtraceTable = ReadBacktraceTable (br); contextTable = ReadContextTable (br); + timeline = ReadTimeline (br); } } @@ -203,6 +218,24 @@ public class Metadata { return d; } + + Timeline [] ReadTimeline (BinaryReader br) + { + + int sz = br.ReadInt32 (); + Timeline [] d = new Timeline [sz]; + + for (int i = 0; i < sz; i ++) { + d [i].Id = i; + d [i].Time = br.ReadInt32 (); + d [i].Event = (EventType) br.ReadInt32 (); + d [i].SizeHigh = br.ReadInt32 (); + d [i].SizeLow = br.ReadInt32 (); + d [i].FilePos = br.ReadInt64 (); + } + + return d; + } } class ProfilerSignature { @@ -216,7 +249,7 @@ class ProfilerSignature { 0xaa, 0x93, 0xc8, 0x76, 0xf4, 0x6a, 0x95, 0x11 }; - const int Version = 3; + const int Version = 4; public static void ReadHeader (BinaryReader br, bool is_dump) { @@ -235,6 +268,20 @@ class ProfilerSignature { } +public enum EventType { + GC = 0, + HeapResize = 1, + Checkpoint = 2 +} + +public struct Timeline { + public int Id; + public int Time; + public EventType Event; + public int SizeHigh, SizeLow; + public long FilePos; +} + public struct Context { public int Id; public int Type; diff --git a/src/viewer/common/TypeTabulator.cs b/src/viewer/common/TypeTabulator.cs index ea839fe..c26cbcf 100644 --- a/src/viewer/common/TypeTabulator.cs +++ b/src/viewer/common/TypeTabulator.cs @@ -78,7 +78,7 @@ class TypeTabulator : ProfileReader { last_time = time; } - protected override void GcHeapResize (int time, int new_size) + protected override void GcHeapResize (int time, int event_num, int new_size) { // Splitting twice here gives nice graphs, since you get a strait line Split (time);