HeapClassRevRef *rev_hash;
int rev_hash_size;
int rev_count;
+ uintptr_t pinned_references;
+ uintptr_t root_references;
};
static int
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;
i = 0;
} while (i != start_pos);
/* should not happen */
- printf ("failed heap obj update\n");
+ //printf ("failed heap obj slot\n");
return -1;
}
}
}
+#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)
{
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
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)
{
}
}
+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)
{
}
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);
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) {
}
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");
}
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);
}
*
* 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
* 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 {
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)
{
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);