static uint64_t max_heap_size;
static uint64_t gc_object_moves;
static int gc_resizes;
+typedef struct {
+ uint64_t created;
+ uint64_t destroyed;
+ uint64_t max_live;
+ TraceDesc traces;
+} HandleInfo;
+static HandleInfo handle_info [4];
static const char*
gc_event_name (int ev)
}
}
+static const char*
+get_handle_name (int htype)
+{
+ switch (htype) {
+ case 0: return "weak";
+ case 1: return "weaktrack";
+ case 2: return "normal";
+ case 3: return "pinned";
+ default: return "unknown";
+ }
+}
+
static MethodDesc**
decode_bt (MethodDesc** sframes, int *size, unsigned char *p, unsigned char **endp, intptr_t ptr_base)
{
}
}
+static void
+track_handle (uintptr_t obj, int htype, uint32_t handle)
+{
+ int i;
+ for (i = 0; i < num_tracked_objects; ++i) {
+ if (tracked_objects [i] == obj)
+ fprintf (outfile, "Object %p referenced from handle %u\n", (void*)obj, handle);
+ }
+}
+
static void
track_move (uintptr_t src, uintptr_t dst)
{
fprintf (outfile, "moved obj %p to %p\n", (void*)OBJ_ADDR (obj1diff), (void*)OBJ_ADDR (obj2diff));
}
}
+ } else if (subtype == TYPE_GC_HANDLE_CREATED) {
+ int htype = decode_uleb128 (p, &p);
+ uint32_t handle = decode_uleb128 (p, &p);
+ intptr_t objdiff = decode_sleb128 (p, &p);
+ if (htype > 3)
+ return 0;
+ handle_info [htype].created++;
+ add_trace_thread (thread, &handle_info [htype].traces, 1);
+ /* FIXME: we don't take into account timing here */
+ if (handle_info [htype].created > handle_info [htype].max_live)
+ handle_info [htype].max_live = handle_info [htype].created;
+ if (num_tracked_objects)
+ track_handle (OBJ_ADDR (objdiff), htype, handle);
+ if (debug)
+ fprintf (outfile, "handle (%s) %u created for object %p\n", get_handle_name (htype), handle, (void*)OBJ_ADDR (objdiff));
+ } else if (subtype == TYPE_GC_HANDLE_DESTROYED) {
+ int htype = decode_uleb128 (p, &p);
+ uint32_t handle = decode_uleb128 (p, &p);
+ if (htype > 3)
+ return 0;
+ handle_info [htype].created--;
+ if (debug)
+ fprintf (outfile, "handle (%s) %u destroyed\n", get_handle_name (htype), handle);
}
break;
}
i, gc_info [i].count, gc_info [i].max_time / 1000, gc_info [i].total_time / 1000,
gc_info [i].total_time / gc_info [i].count / 1000);
}
+ for (i = 0; i < 3; ++i) {
+ if (!handle_info [i].max_live)
+ continue;
+ fprintf (outfile, "\tGC handles %s: created: %llu, destroyed: %llu, max: %llu\n",
+ get_handle_name (i), handle_info [i].created, handle_info [i].destroyed, handle_info [i].max_live);
+ dump_traces (&handle_info [i].traces, "created");
+ }
}
static void
*
* type GC format:
* type: TYPE_GC
- * exinfo: one of TYPE_GC_EVENT, TYPE_GC_RESIZE, TYPE_GC_MOVE
+ * exinfo: one of TYPE_GC_EVENT, TYPE_GC_RESIZE, TYPE_GC_MOVE, TYPE_GC_HANDLE_CREATED,
+ * TYPE_GC_HANDLE_DESTROYED
* [time diff: uleb128] nanoseconds since last timing
* if exinfo == TYPE_GC_RESIZE
* [heap_size: uleb128] new heap size
* [objaddr: sleb128]+ num_objects object pointer differences from obj_base
* num is always an even number: the even items are the old
* addresses, the odd numbers are the respective new object addresses
+ * if exinfo == TYPE_GC_HANDLE_CREATED
+ * [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
+ * upper bits reserved as flags
+ * [handle: uleb128] GC handle value
+ * [objaddr: sleb128] object pointer differences from obj_base
+ * if exinfo == TYPE_GC_HANDLE_DESTROYED
+ * [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
+ * upper bits reserved as flags
+ * [handle: uleb128] GC handle value
*
* type metadata format:
* type: TYPE_METADATA
//printf ("gc moved %d objects\n", num/2);
}
+static void
+gc_handle (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj)
+{
+ uint64_t now;
+ LogBuffer *logbuffer = ensure_logbuf (16);
+ now = current_time ();
+ if (op == MONO_PROFILER_GC_HANDLE_CREATED)
+ emit_byte (logbuffer, TYPE_GC_HANDLE_CREATED | TYPE_GC);
+ else if (op == MONO_PROFILER_GC_HANDLE_DESTROYED)
+ emit_byte (logbuffer, TYPE_GC_HANDLE_DESTROYED | TYPE_GC);
+ else
+ return;
+ emit_time (logbuffer, now);
+ emit_value (logbuffer, type);
+ emit_value (logbuffer, handle);
+ if (op == MONO_PROFILER_GC_HANDLE_CREATED)
+ emit_obj (logbuffer, obj);
+}
+
static char*
push_nesting (char *p, MonoClass *klass)
{
int events = MONO_PROFILE_GC|MONO_PROFILE_ALLOCATIONS|
MONO_PROFILE_GC_MOVES|MONO_PROFILE_CLASS_EVENTS|MONO_PROFILE_THREADS|
MONO_PROFILE_ENTER_LEAVE|MONO_PROFILE_JIT_COMPILATION|MONO_PROFILE_EXCEPTIONS|
- MONO_PROFILE_MONITOR_EVENTS|MONO_PROFILE_MODULE_EVENTS;
+ MONO_PROFILE_MONITOR_EVENTS|MONO_PROFILE_MODULE_EVENTS|MONO_PROFILE_GC_ROOTS;
p = desc;
if (strncmp (p, "log", 3))
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_class (NULL, class_loaded, NULL, NULL);
mono_profiler_install_module (NULL, image_loaded, NULL, NULL);
mono_profiler_install_thread (thread_start, thread_end);
#define BUF_ID 0x4D504C01
#define LOG_HEADER_ID 0x4D505A01
#define LOG_VERSION_MAJOR 0
-#define LOG_VERSION_MINOR 1
+#define LOG_VERSION_MINOR 2
#define LOG_DATA_VERSION 1
enum {
TYPE_GC_EVENT = 1 << 4,
TYPE_GC_RESIZE = 2 << 4,
TYPE_GC_MOVE = 3 << 4,
+ TYPE_GC_HANDLE_CREATED = 4 << 4,
+ TYPE_GC_HANDLE_DESTROYED = 5 << 4,
/* extended type for TYPE_METHOD */
TYPE_LEAVE = 1 << 4,
TYPE_ENTER = 2 << 4,