From c910858923122b51bca13e313c8902753ecf042e Mon Sep 17 00:00:00 2001 From: Paolo Molaro Date: Tue, 16 Nov 2010 16:02:34 +0100 Subject: [PATCH] Log profiler: collect root information during GC. --- mono/profiler/decode.c | 160 ++++++++++++++++++++++++++++++++++++++-- mono/profiler/proflog.c | 28 ++++++- mono/profiler/proflog.h | 7 +- 3 files changed, 187 insertions(+), 8 deletions(-) diff --git a/mono/profiler/decode.c b/mono/profiler/decode.c index e707f0d6339..d3cb8985d9d 100644 --- a/mono/profiler/decode.c +++ b/mono/profiler/decode.c @@ -239,6 +239,8 @@ struct _HeapClassDesc { HeapClassRevRef *rev_hash; int rev_hash_size; int rev_count; + uintptr_t pinned_references; + uintptr_t root_references; }; static int @@ -312,6 +314,10 @@ struct _HeapShot { HeapObjectDesc **objects_hash; uintptr_t objects_count; uintptr_t objects_hash_size; + uintptr_t num_roots; + uintptr_t *roots; + uintptr_t *roots_extra; + int *roots_types; }; static HeapShot *heap_shots = NULL; @@ -441,7 +447,7 @@ heap_shot_find_obj_slot (HeapShot *hs, uintptr_t objaddr) i = 0; } while (i != start_pos); /* should not happen */ - printf ("failed heap obj update\n"); + //printf ("failed heap obj slot\n"); return -1; } @@ -526,6 +532,81 @@ heap_shot_resolve_reverse_refs (HeapShot *hs) } } +#define MARK_GRAY 1 +#define MARK_BLACK 2 + +static void +heap_shot_mark_objects (HeapShot *hs) +{ + uintptr_t i, oi, r; + unsigned char *marks; + HeapObjectDesc *obj, *ref; + int marked_some; + uintptr_t num_marked = 0, num_unmarked; + for (i = 0; i < hs->num_roots; ++i) { + HeapClassDesc *cd; + oi = heap_shot_find_obj_slot (hs, hs->roots [i]); + if (oi == -1) { + continue; + } + obj = hs->objects_hash [oi]; + cd = obj->hklass; + if (hs->roots_types [i] & MONO_PROFILE_GC_ROOT_PINNING) + cd->pinned_references++; + cd->root_references++; + } + if (!debug) + return; + /* consistency checks: it seems not all the objects are walked in the heap in some cases */ + marks = calloc (hs->objects_hash_size, 1); + if (!marks) + return; + for (i = 0; i < hs->num_roots; ++i) { + oi = heap_shot_find_obj_slot (hs, hs->roots [i]); + if (oi == -1) { + fprintf (outfile, "root type 0x%x for obj %p (%s) not found in heap\n", hs->roots_types [i], (void*)hs->roots [i], lookup_class (hs->roots_extra [i])->name); + continue; + } + obj = hs->objects_hash [oi]; + if (!marks [oi]) { + marks [oi] = obj->num_refs? MARK_GRAY: MARK_BLACK; + num_marked++; + } + } + marked_some = 1; + while (marked_some) { + marked_some = 0; + for (i = 0; i < hs->objects_hash_size; ++i) { + if (marks [i] != MARK_GRAY) + continue; + marks [i] = MARK_BLACK; + obj = hs->objects_hash [i]; + for (r = 0; r < obj->num_refs; ++r) { + oi = heap_shot_find_obj_slot (hs, obj->refs [r]); + if (oi == -1) { + fprintf (outfile, "referenced obj %p not found in heap\n", (void*)obj->refs [r]); + continue; + } + ref = hs->objects_hash [oi]; + if (!marks [oi]) { + marks [oi] = ref->num_refs? MARK_GRAY: MARK_BLACK; + } + } + marked_some++; + } + } + + num_unmarked = 0; + for (i = 0; i < hs->objects_hash_size; ++i) { + if (hs->objects_hash [i] && !marks [i]) { + num_unmarked++; + fprintf (outfile, "object %p (%s) unmarked\n", (void*)hs->objects_hash [i], hs->objects_hash [i]->hklass->klass->name); + } + } + fprintf (outfile, "Total unmarked: %d/%d\n", num_unmarked, hs->objects_count); + free (marks); +} + static void heap_shot_free_objects (HeapShot *hs) { @@ -642,6 +723,11 @@ struct _ThreadContext { int stack_size; int stack_id; HeapShot *current_heap_shot; + uintptr_t num_roots; + uintptr_t size_roots; + uintptr_t *roots; + uintptr_t *roots_extra; + int *roots_types; }; static void @@ -794,6 +880,23 @@ add_trace_methods (MethodDesc **methods, int count, TraceDesc *trace, uint64_t v return bt; } +static void +thread_add_root (ThreadContext *ctx, uintptr_t obj, int root_type, uintptr_t extra_info) +{ + if (ctx->num_roots == ctx->size_roots) { + int new_size = ctx->size_roots * 2; + if (!new_size) + new_size = 4; + ctx->roots = realloc (ctx->roots, new_size * sizeof (uintptr_t)); + ctx->roots_extra = realloc (ctx->roots_extra, new_size * sizeof (uintptr_t)); + ctx->roots_types = realloc (ctx->roots_types, new_size * sizeof (int)); + ctx->size_roots = new_size; + } + ctx->roots_types [ctx->num_roots] = root_type; + ctx->roots_extra [ctx->num_roots] = extra_info; + ctx->roots [ctx->num_roots++] = obj; +} + static int compare_callc (const void *a, const void *b) { @@ -962,6 +1065,19 @@ get_handle_name (int htype) } } +static const char* +get_root_name (int rtype) +{ + switch (rtype & MONO_PROFILE_GC_ROOT_TYPEMASK) { + case MONO_PROFILE_GC_ROOT_STACK: return "stack"; + case MONO_PROFILE_GC_ROOT_FINALIZER: return "finalizer"; + case MONO_PROFILE_GC_ROOT_HANDLE: return "handle"; + case MONO_PROFILE_GC_ROOT_OTHER: return "other"; + case MONO_PROFILE_GC_ROOT_MISC: return "misc"; + default: return "unknown"; + } +} + static MethodDesc** decode_bt (MethodDesc** sframes, int *size, unsigned char *p, unsigned char **endp, intptr_t ptr_base) { @@ -1327,6 +1443,20 @@ decode_buffer (ProfContext *ctx) } if (debug && size) fprintf (outfile, "traced object %p, size %llu (%s), refs: %d\n", (void*)OBJ_ADDR (objdiff), size, cd->name, num); + } else if (subtype == TYPE_HEAP_ROOT) { + uintptr_t num = decode_uleb128 (p + 1, &p); + uintptr_t gc_num = decode_uleb128 (p, &p); + int i; + for (i = 0; i < num; ++i) { + intptr_t objdiff = decode_sleb128 (p, &p); + int root_type = decode_uleb128 (p, &p); + /* we just discard the extra info for now */ + uintptr_t extra_info = decode_uleb128 (p, &p); + if (debug) + fprintf (outfile, "object %p is a %s root\n", (void*)OBJ_ADDR (objdiff), get_root_name (root_type)); + if (collect_traces) + thread_add_root (thread, OBJ_ADDR (objdiff), root_type, extra_info); + } } else if (subtype == TYPE_HEAP_END) { uint64_t tdiff = decode_uleb128 (p + 1, &p); LOG_TIME (time_base, tdiff); @@ -1334,8 +1464,26 @@ decode_buffer (ProfContext *ctx) if (debug) fprintf (outfile, "heap shot end\n"); if (collect_traces) { - heap_shot_resolve_reverse_refs (thread->current_heap_shot); - heap_shot_free_objects (thread->current_heap_shot); + HeapShot *hs = thread->current_heap_shot; + if (hs && thread->num_roots) { + /* transfer the root ownershipt to the heapshot */ + hs->num_roots = thread->num_roots; + hs->roots = thread->roots; + hs->roots_extra = thread->roots_extra; + hs->roots_types = thread->roots_types; + } else { + free (thread->roots); + free (thread->roots_extra); + free (thread->roots_types); + } + thread->num_roots = 0; + thread->size_roots = 0; + thread->roots = NULL; + thread->roots_extra = NULL; + thread->roots_types = NULL; + heap_shot_resolve_reverse_refs (hs); + heap_shot_mark_objects (hs); + heap_shot_free_objects (hs); } thread->current_heap_shot = NULL; } else if (subtype == TYPE_HEAP_START) { @@ -1843,8 +1991,8 @@ heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs) } hs->sorted = sorted; qsort (sorted, ccount, sizeof (void*), compare_heap_class); - fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d\n", - hs_num, (hs->timestamp - startup_time)/1000000000.0, size, count, ccount); + fprintf (outfile, "\n\tHeap shot %d at %.3f secs: size: %llu, object count: %llu, class count: %d, roots: %d\n", + hs_num, (hs->timestamp - startup_time)/1000000000.0, size, count, ccount, hs->num_roots); if (!verbose && ccount > 30) ccount = 30; fprintf (outfile, "\t%10s %10s %8s Class name\n", "Bytes", "Count", "Average"); @@ -1873,6 +2021,8 @@ heap_shot_summary (HeapShot *hs, int hs_num, HeapShot *last_hs) } assert (cd->rev_count == k); qsort (rev_sorted, cd->rev_count, sizeof (HeapClassRevRef), compare_rev_class); + if (cd->root_references) + fprintf (outfile, "\t\t%d root references (%d pinning)\n", cd->root_references, cd->pinned_references); dump_rev_claases (rev_sorted, cd->rev_count); free (rev_sorted); } diff --git a/mono/profiler/proflog.c b/mono/profiler/proflog.c index b92088fc020..ca6a1db0859 100644 --- a/mono/profiler/proflog.c +++ b/mono/profiler/proflog.c @@ -205,7 +205,7 @@ typedef struct _LogBuffer LogBuffer; * * type heap format * type: TYPE_HEAP - * exinfo: one of TYPE_HEAP_START, TYPE_HEAP_END, TYPE_HEAP_OBJECT + * exinfo: one of TYPE_HEAP_START, TYPE_HEAP_END, TYPE_HEAP_OBJECT, TYPE_HEAP_ROOT * if exinfo == TYPE_HEAP_START * [time diff: uleb128] nanoseconds since last timing * if exinfo == TYPE_HEAP_END @@ -219,6 +219,13 @@ typedef struct _LogBuffer LogBuffer; * uleb128 encoded offset: the first offset is from the object address * and each next offset is relative to the previous one * [objrefs: sleb128]+ object referenced as a difference from obj_base + * if exinfo == TYPE_HEAP_ROOT + * [num_roots: uleb128] number of root references + * [num_gc: uleb128] number of major gcs + * [object: sleb128] the object as a difference from obj_base + * [root_type: uleb128] the root_type + * [extra_info: uleb128] the extra_info value + * object, root_type_extra_info are repeated num_roots times * */ struct _LogBuffer { @@ -671,6 +678,23 @@ gc_moves (MonoProfiler *prof, void **objects, int num) EXIT_LOG (logbuffer); } +static void +gc_roots (MonoProfiler *prof, int num, void **objects, int *root_types, uintptr_t *extra_info) +{ + int i; + LogBuffer *logbuffer = ensure_logbuf (5 + num * 18); + ENTER_LOG (logbuffer, "gcroots"); + emit_byte (logbuffer, TYPE_HEAP_ROOT | TYPE_HEAP); + emit_value (logbuffer, num); + emit_value (logbuffer, mono_gc_collection_count (mono_gc_max_generation ())); + for (i = 0; i < num; ++i) { + emit_obj (logbuffer, objects [i]); + emit_value (logbuffer, root_types [i]); + emit_value (logbuffer, extra_info [i]); + } + EXIT_LOG (logbuffer); +} + static void gc_handle (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj) { @@ -1300,7 +1324,7 @@ mono_profiler_startup (const char *desc) mono_profiler_install_gc (gc_event, gc_resize); mono_profiler_install_allocation (gc_alloc); mono_profiler_install_gc_moves (gc_moves); - mono_profiler_install_gc_roots (gc_handle); + mono_profiler_install_gc_roots (gc_handle, gc_roots); mono_profiler_install_class (NULL, class_loaded, NULL, NULL); mono_profiler_install_module (NULL, image_loaded, NULL, NULL); mono_profiler_install_thread (thread_start, thread_end); diff --git a/mono/profiler/proflog.h b/mono/profiler/proflog.h index 86124a0e2c0..5011eab9a44 100644 --- a/mono/profiler/proflog.h +++ b/mono/profiler/proflog.h @@ -5,7 +5,11 @@ #define LOG_HEADER_ID 0x4D505A01 #define LOG_VERSION_MAJOR 0 #define LOG_VERSION_MINOR 2 -#define LOG_DATA_VERSION 2 +#define LOG_DATA_VERSION 3 +/* + * version 2: added offsets in heap walk + * version 3: added GC roots + */ enum { TYPE_ALLOC, @@ -20,6 +24,7 @@ enum { TYPE_HEAP_START = 0 << 4, TYPE_HEAP_END = 1 << 4, TYPE_HEAP_OBJECT = 2 << 4, + TYPE_HEAP_ROOT = 3 << 4, /* extended type for TYPE_METADATA */ TYPE_START_LOAD = 1 << 4, TYPE_END_LOAD = 2 << 4, -- 2.25.1