2 * proflog.c: mono log profiler
5 * Paolo Molaro (lupus@ximian.com)
7 * Copyright 2010 Novell, Inc (http://www.novell.com)
11 #include <mono/metadata/profiler.h>
12 #include <mono/metadata/mono-gc.h>
13 #include <mono/metadata/debug-helpers.h>
29 #if defined (HAVE_ZLIB)
33 #define BUFFER_SIZE (4096 * 16)
34 static int nocalls = 0;
35 static int notraces = 0;
36 static int use_zip = 0;
37 static int do_report = 0;
38 static int do_heap_shot = 0;
39 static int max_call_depth = 100;
40 static int runtime_inited = 0;
42 /* For linux compile with:
43 * gcc -fPIC -shared -o libmono-profiler-log.so proflog.c utils.c -Wall -g -lz `pkg-config --cflags --libs mono-2`
44 * gcc -o mprof-report decode.c utils.c -Wall -g -lz -lrt -lpthread `pkg-config --cflags mono-2`
46 * For osx compile with:
47 * gcc -m32 -Dmono_free=free shared -o libmono-profiler-log.dylib proflog.c utils.c -Wall -g -lz `pkg-config --cflags mono-2` -undefined suppress -flat_namespace
48 * gcc -m32 -o mprof-report decode.c utils.c -Wall -g -lz -lrt -lpthread `pkg-config --cflags mono-2`
51 * sudo cp mprof-report /usr/local/bin
52 * sudo cp libmono-profiler-log.so /usr/local/lib
56 typedef struct _LogBuffer LogBuffer;
62 * The file is composed by a header followed by 0 or more buffers.
63 * Each buffer contains events that happened on a thread: for a given thread
64 * buffers that appear later in the file are guaranteed to contain events
65 * that happened later in time. Buffers from separate threads could be interleaved,
67 * Buffers are not required to be aligned.
70 * [id: 4 bytes] constant value: LOG_HEADER_ID
71 * [major: 1 byte] [minor: 1 byte] major and minor version of the log profiler
72 * [format: 1 byte] version of the data format for the rest of the file
73 * [ptrsize: 1 byte] size in bytes of a pointer in the profiled program
74 * [startup time: 8 bytes] time in milliseconds since the unix epoch when the program started
75 * [timer overhead: 4 bytes] approximate overhead in nanoseconds of the timer
76 * [flags: 4 bytes] file format flags, should be 0 for now
77 * [pid: 4 bytes] pid of the profiled process
78 * [sysid: 4 bytes] operating system and architecture identifier
80 * The multiple byte integers are in little-endian format.
83 * [buffer header] [event]*
84 * Buffers have a fixed-size header followed by 0 or more bytes of event data.
85 * Timing information and other values in the event data are usually stored
86 * as uleb128 or sleb128 integers. To save space, as noted for each item below,
87 * some data is represented as a difference between the actual value and
88 * either the last value of the same type (like for timing information) or
89 * as the difference from a value stored in a buffer header.
91 * For timing information the data is stored as uleb128, since timing
92 * increases in a monotonic way in each thread: the value is the number of
93 * nanoseconds to add to the last seen timing data in a buffer. The first value
94 * in a buffer will be calculated from the time_base field in the buffer head.
96 * Object or heap sizes are stored as uleb128.
97 * Pointer differences are stored as sleb128, instead.
99 * If an unexpected value is found, the rest of the buffer should be ignored,
100 * as generally the later values need the former to be interpreted correctly.
102 * buffer header format:
103 * [bufid: 4 bytes] constant value: BUF_ID
104 * [len: 4 bytes] size of the data following the buffer header
105 * [time_base: 8 bytes] time base in nanoseconds since an unspecified epoch
106 * [ptr_base: 8 bytes] base value for pointers
107 * [obj_base: 8 bytes] base value for object addresses
108 * [thread id: 8 bytes] system-specific thread ID (pthread_t for example)
109 * [method_base: 8 bytes] base value for MonoMethod pointers
112 * [extended info: upper 4 bits] [type: lower 4 bits] [data]*
113 * The data that follows depends on type and the extended info.
114 * Type is one of the enum values in proflog.h: TYPE_ALLOC, TYPE_GC,
115 * TYPE_METADATA, TYPE_METHOD, TYPE_EXCEPTION, TYPE_MONITOR, TYPE_HEAP.
116 * The extended info bits are interpreted based on type, see
117 * each individual event description below.
118 * strings are represented as a 0-terminated utf8 sequence.
121 * [flags: uleb128] must be 0
122 * [num: uleb128] number of frames following
123 * [frame: sleb128]* num MonoMethod pointers as differences from ptr_base
127 * exinfo: flags: TYPE_ALLOC_BT
128 * [time diff: uleb128] nanoseconds since last timing
129 * [ptr: sleb128] class as a byte difference from ptr_base
130 * [obj: sleb128] object address as a byte difference from obj_base
131 * [size: uleb128] size of the object in the heap
132 * If the TYPE_ALLOC_BT flag is set, a backtrace follows.
136 * exinfo: one of TYPE_GC_EVENT, TYPE_GC_RESIZE, TYPE_GC_MOVE, TYPE_GC_HANDLE_CREATED,
137 * TYPE_GC_HANDLE_DESTROYED
138 * [time diff: uleb128] nanoseconds since last timing
139 * if exinfo == TYPE_GC_RESIZE
140 * [heap_size: uleb128] new heap size
141 * if exinfo == TYPE_GC_EVENT
142 * [event type: uleb128] GC event (MONO_GC_EVENT_* from profiler.h)
143 * [generation: uleb128] GC generation event refers to
144 * if exinfo == TYPE_GC_MOVE
145 * [num_objects: uleb128] number of object moves that follow
146 * [objaddr: sleb128]+ num_objects object pointer differences from obj_base
147 * num is always an even number: the even items are the old
148 * addresses, the odd numbers are the respective new object addresses
149 * if exinfo == TYPE_GC_HANDLE_CREATED
150 * [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
151 * upper bits reserved as flags
152 * [handle: uleb128] GC handle value
153 * [objaddr: sleb128] object pointer differences from obj_base
154 * if exinfo == TYPE_GC_HANDLE_DESTROYED
155 * [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
156 * upper bits reserved as flags
157 * [handle: uleb128] GC handle value
159 * type metadata format:
160 * type: TYPE_METADATA
161 * exinfo: flags: TYPE_LOAD_ERR
162 * [time diff: uleb128] nanoseconds since last timing
163 * [mtype: byte] metadata type, one of: TYPE_CLASS, TYPE_IMAGE, TYPE_ASSEMBLY, TYPE_DOMAIN,
165 * [pointer: sleb128] pointer of the metadata type depending on mtype
166 * if mtype == TYPE_CLASS
167 * [image: sleb128] MonoImage* as a pointer difference from ptr_base
168 * [flags: uleb128] must be 0
169 * [name: string] full class name
170 * if mtype == TYPE_IMAGE
171 * [flags: uleb128] must be 0
172 * [name: string] image file name
173 * if mtype == TYPE_THREAD
174 * [flags: uleb128] must be 0
175 * [name: string] thread name
177 * type method format:
179 * exinfo: one of: TYPE_LEAVE, TYPE_ENTER, TYPE_EXC_LEAVE, TYPE_JIT
180 * [time diff: uleb128] nanoseconds since last timing
181 * [method: sleb128] MonoMethod* as a pointer difference from the last such
182 * pointer or the buffer method_base
183 * if exinfo == TYPE_JIT
184 * [code address: sleb128] pointer to the native code as a diff from ptr_base
185 * [code size: uleb128] size of the generated code
186 * [name: string] full method name
188 * type exception format:
189 * type: TYPE_EXCEPTION
190 * exinfo: TYPE_EXCEPTION_BT flag and one of: TYPE_THROW, TYPE_CLAUSE
191 * [time diff: uleb128] nanoseconds since last timing
192 * if exinfo.low3bits == TYPE_CLAUSE
193 * [clause type: uleb128] finally/catch/fault/filter
194 * [clause num: uleb128] the clause number in the method header
195 * [method: sleb128] MonoMethod* as a pointer difference from the last such
196 * pointer or the buffer method_base
197 * if exinfo.low3bits == TYPE_THROW
198 * [object: sleb128] the object that was thrown as a difference from obj_base
199 * If the TYPE_EXCEPTION_BT flag is set, a backtrace follows.
201 * type monitor format:
203 * exinfo: TYPE_MONITOR_BT flag and one of: MONO_PROFILER_MONITOR_(CONTENTION|FAIL|DONE)
204 * [time diff: uleb128] nanoseconds since last timing
205 * [object: sleb128] the lock object as a difference from obj_base
206 * if exinfo.low3bits == MONO_PROFILER_MONITOR_CONTENTION
207 * If the TYPE_MONITOR_BT flag is set, a backtrace follows.
211 * exinfo: one of TYPE_HEAP_START, TYPE_HEAP_END, TYPE_HEAP_OBJECT
212 * if exinfo == TYPE_HEAP_START
213 * [time diff: uleb128] nanoseconds since last timing
214 * if exinfo == TYPE_HEAP_END
215 * [time diff: uleb128] nanoseconds since last timing
216 * if exinfo == TYPE_HEAP_OBJECT
217 * [object: sleb128] the object as a difference from obj_base
218 * [class: sleb128] the object MonoClass* as a difference from ptr_base
219 * [size: uleb128] size of the object on the heap
220 * [num_refs: uleb128] number of object references
221 * [objrefs: sleb128]+ object referenced as a difference from obj_base
229 uintptr_t method_base;
230 uintptr_t last_method;
233 unsigned char* data_end;
237 unsigned char buf [1];
240 struct _MonoProfiler {
243 #if defined (HAVE_ZLIB)
247 int last_gc_gen_started;
251 #define TLS_SET(x,y) TlsSetValue(x, y)
252 #define TLS_GET(x) ((LogBuffer *) TlsGetValue(x))
253 #define TLS_INIT(x) x = TlsAlloc ()
254 static int tlsbuffer;
256 #define TLS_SET(x,y) x = y
259 static __thread LogBuffer* tlsbuffer = NULL;
261 #define TLS_SET(x,y) pthread_setspecific(x, y)
262 #define TLS_GET(x) ((LogBuffer *) pthread_getspecific(x))
263 #define TLS_INIT(x) pthread_key_create(&x, NULL)
264 static pthread_key_t tlsbuffer;
270 LogBuffer* buf = alloc_buffer (BUFFER_SIZE);
271 buf->size = BUFFER_SIZE;
272 buf->time_base = current_time ();
273 buf->last_time = buf->time_base;
274 buf->data_end = (unsigned char*)buf + buf->size;
275 buf->data = buf->buf;
282 LogBuffer *logbuffer;
283 if (TLS_GET (tlsbuffer))
285 logbuffer = create_buffer ();
286 TLS_SET (tlsbuffer, logbuffer);
287 logbuffer->thread_id = thread_id ();
288 //printf ("thread %p at time %llu\n", (void*)logbuffer->thread_id, logbuffer->time_base);
292 ensure_logbuf (int bytes)
294 LogBuffer *old = TLS_GET (tlsbuffer);
295 if (old && old->data + bytes + 100 < old->data_end)
297 TLS_SET (tlsbuffer, NULL);
299 TLS_GET (tlsbuffer)->next = old;
301 TLS_GET (tlsbuffer)->call_depth = old->call_depth;
302 //printf ("new logbuffer\n");
303 return TLS_GET (tlsbuffer);
307 emit_byte (LogBuffer *logbuffer, int value)
309 logbuffer->data [0] = value;
311 assert (logbuffer->data <= logbuffer->data_end);
315 emit_value (LogBuffer *logbuffer, int value)
317 encode_uleb128 (value, logbuffer->data, &logbuffer->data);
318 assert (logbuffer->data <= logbuffer->data_end);
322 emit_time (LogBuffer *logbuffer, uint64_t value)
324 uint64_t tdiff = value - logbuffer->last_time;
326 if (value < logbuffer->last_time)
327 printf ("time went backwards\n");
328 //if (tdiff > 1000000)
329 // printf ("large time offset: %llu\n", tdiff);
331 encode_uleb128 (tdiff, logbuffer->data, &logbuffer->data);
332 /*if (tdiff != decode_uleb128 (p, &p))
333 printf ("incorrect encoding: %llu\n", tdiff);*/
334 logbuffer->last_time = value;
335 assert (logbuffer->data <= logbuffer->data_end);
339 emit_svalue (LogBuffer *logbuffer, int64_t value)
341 encode_sleb128 (value, logbuffer->data, &logbuffer->data);
342 assert (logbuffer->data <= logbuffer->data_end);
346 emit_ptr (LogBuffer *logbuffer, void *ptr)
348 if (!logbuffer->ptr_base)
349 logbuffer->ptr_base = (uintptr_t)ptr;
350 emit_svalue (logbuffer, (intptr_t)ptr - logbuffer->ptr_base);
351 assert (logbuffer->data <= logbuffer->data_end);
355 emit_method (LogBuffer *logbuffer, void *method)
357 if (!logbuffer->method_base) {
358 logbuffer->method_base = (intptr_t)method;
359 logbuffer->last_method = (intptr_t)method;
361 encode_sleb128 ((intptr_t)((char*)method - (char*)logbuffer->last_method), logbuffer->data, &logbuffer->data);
362 logbuffer->last_method = (intptr_t)method;
363 assert (logbuffer->data <= logbuffer->data_end);
367 emit_obj (LogBuffer *logbuffer, void *ptr)
369 if (!logbuffer->obj_base)
370 logbuffer->obj_base = (uintptr_t)ptr >> 3;
371 emit_svalue (logbuffer, ((uintptr_t)ptr >> 3) - logbuffer->obj_base);
372 assert (logbuffer->data <= logbuffer->data_end);
376 write_int32 (char *buf, int32_t value)
379 for (i = 0; i < 4; ++i) {
387 write_int64 (char *buf, int64_t value)
390 for (i = 0; i < 8; ++i) {
398 dump_header (MonoProfiler *profiler)
402 p = write_int32 (p, LOG_HEADER_ID);
403 *p++ = LOG_VERSION_MAJOR;
404 *p++ = LOG_VERSION_MINOR;
405 *p++ = LOG_DATA_VERSION;
406 *p++ = sizeof (void*);
407 p = write_int64 (p, ((uint64_t)time (NULL)) * 1000); /* startup time */
408 p = write_int32 (p, get_timer_overhead ()); /* timer overhead */
409 p = write_int32 (p, 0); /* flags */
410 p = write_int32 (p, 0); /* pid */
411 p = write_int32 (p, 0); /* opsystem */
412 #if defined (HAVE_ZLIB)
413 if (profiler->gzfile) {
414 gzwrite (profiler->gzfile, hbuf, p - hbuf);
416 fwrite (hbuf, p - hbuf, 1, profiler->file);
419 fwrite (hbuf, p - hbuf, 1, profiler->file);
424 dump_buffer (MonoProfiler *profiler, LogBuffer *buf)
429 dump_buffer (profiler, buf->next);
430 p = write_int32 (p, BUF_ID);
431 p = write_int32 (p, buf->data - buf->buf);
432 p = write_int64 (p, buf->time_base);
433 p = write_int64 (p, buf->ptr_base);
434 p = write_int64 (p, buf->obj_base);
435 p = write_int64 (p, buf->thread_id);
436 p = write_int64 (p, buf->method_base);
437 #if defined (HAVE_ZLIB)
438 if (profiler->gzfile) {
439 gzwrite (profiler->gzfile, hbuf, p - hbuf);
440 gzwrite (profiler->gzfile, buf->buf, buf->data - buf->buf);
443 fwrite (hbuf, p - hbuf, 1, profiler->file);
444 fwrite (buf->buf, buf->data - buf->buf, 1, profiler->file);
445 #if defined (HAVE_ZLIB)
448 free_buffer (buf, buf->size);
452 runtime_initialized (MonoProfiler *profiler)
458 * Can be called only at safe callback locations.
461 safe_dump (MonoProfiler *profiler, LogBuffer *logbuffer)
463 int cd = logbuffer->call_depth;
465 dump_buffer (profiler, TLS_GET (tlsbuffer));
467 TLS_SET (tlsbuffer, NULL);
469 TLS_GET (tlsbuffer)->call_depth = cd;
473 gc_reference (MonoObject *obj, MonoClass *klass, uintptr_t size, uintptr_t num, MonoObject **refs, void *data)
476 //const char *name = mono_class_get_name (klass);
477 LogBuffer *logbuffer = ensure_logbuf (20 + num * 8);
478 emit_byte (logbuffer, TYPE_HEAP_OBJECT | TYPE_HEAP);
479 emit_obj (logbuffer, obj);
480 emit_ptr (logbuffer, klass);
481 /* account for object alignment in the heap */
484 emit_value (logbuffer, size);
485 emit_value (logbuffer, num);
486 for (i = 0; i < num; ++i)
487 emit_obj (logbuffer, refs [i]);
489 // printf ("obj: %p, klass: %s, refs: %d, size: %d\n", obj, name, (int)num, (int)size);
493 static unsigned int hs_mode_ms = 0;
494 static unsigned int hs_mode_gc = 0;
495 static unsigned int gc_count = 0;
496 static uint64_t last_hs_time = 0;
499 heap_walk (MonoProfiler *profiler)
503 LogBuffer *logbuffer;
506 logbuffer = ensure_logbuf (10);
507 now = current_time ();
508 if (hs_mode_ms && (now - last_hs_time)/1000000 >= hs_mode_ms)
510 else if (hs_mode_gc && (gc_count % hs_mode_gc) == 0)
512 else if (!hs_mode_ms && !hs_mode_gc && profiler->last_gc_gen_started == mono_gc_max_generation ())
517 emit_byte (logbuffer, TYPE_HEAP_START | TYPE_HEAP);
518 emit_time (logbuffer, now);
519 mono_gc_walk_heap (0, gc_reference, NULL);
520 logbuffer = ensure_logbuf (10);
521 now = current_time ();
522 emit_byte (logbuffer, TYPE_HEAP_END | TYPE_HEAP);
523 emit_time (logbuffer, now);
528 gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
530 LogBuffer *logbuffer = ensure_logbuf (10);
531 now = current_time ();
532 emit_byte (logbuffer, TYPE_GC_EVENT | TYPE_GC);
533 emit_time (logbuffer, now);
534 emit_value (logbuffer, ev);
535 emit_value (logbuffer, generation);
536 /* to deal with nested gen1 after gen0 started */
537 if (ev == MONO_GC_EVENT_START) {
538 profiler->last_gc_gen_started = generation;
541 if (ev == MONO_GC_EVENT_PRE_START_WORLD)
542 heap_walk (profiler);
543 if (ev == MONO_GC_EVENT_POST_START_WORLD)
544 safe_dump (profiler, logbuffer);
545 //printf ("gc event %d for generation %d\n", ev, generation);
549 gc_resize (MonoProfiler *profiler, int64_t new_size) {
551 LogBuffer *logbuffer = ensure_logbuf (10);
552 now = current_time ();
553 emit_byte (logbuffer, TYPE_GC_RESIZE | TYPE_GC);
554 emit_time (logbuffer, now);
555 emit_value (logbuffer, new_size);
556 //printf ("gc resized to %lld\n", new_size);
559 #define MAX_FRAMES 16
562 MonoMethod* methods [MAX_FRAMES];
564 static int num_frames = MAX_FRAMES / 2;
567 walk_stack (MonoMethod *method, int32_t native_offset, int32_t il_offset, mono_bool managed, void* data)
569 FrameData *frame = data;
570 if (method && frame->count < num_frames) {
571 frame->methods [frame->count++] = method;
572 //printf ("In %d %s\n", frame->count, mono_method_get_name (method));
574 return frame->count == num_frames;
578 * a note about stack walks: they can cause more profiler events to fire,
579 * so we need to make sure they don't happen after we started emitting an
580 * event, hence the collect_bt/emit_bt split.
583 collect_bt (FrameData *data)
586 mono_stack_walk_no_il (walk_stack, data);
590 emit_bt (LogBuffer *logbuffer, FrameData *data)
592 /* FIXME: this is actually tons of data and we should
593 * just output it the first time and use an id the next
595 if (data->count > num_frames)
596 printf ("bad num frames: %d\n", data->count);
597 emit_value (logbuffer, 0); /* flags */
598 emit_value (logbuffer, data->count);
599 //if (*p != data.count) {
600 // printf ("bad num frames enc at %d: %d -> %d\n", count, data.count, *p); printf ("frames end: %p->%p\n", p, logbuffer->data); exit(0);}
601 while (data->count) {
602 emit_ptr (logbuffer, data->methods [--data->count]);
607 gc_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
611 int do_bt = (nocalls && runtime_inited && !notraces)? TYPE_ALLOC_BT: 0;
613 LogBuffer *logbuffer;
614 len = mono_object_get_size (obj);
615 /* account for object alignment in the heap */
620 logbuffer = ensure_logbuf (32 + MAX_FRAMES * 8);
621 now = current_time ();
622 emit_byte (logbuffer, do_bt | TYPE_ALLOC);
623 emit_time (logbuffer, now);
624 emit_ptr (logbuffer, klass);
625 emit_obj (logbuffer, obj);
626 emit_value (logbuffer, len);
628 emit_bt (logbuffer, &data);
630 safe_dump (prof, logbuffer);
631 //printf ("gc alloc %s at %p\n", mono_class_get_name (klass), obj);
635 gc_moves (MonoProfiler *prof, void **objects, int num)
639 LogBuffer *logbuffer = ensure_logbuf (10 + num * 8);
640 now = current_time ();
641 emit_byte (logbuffer, TYPE_GC_MOVE | TYPE_GC);
642 emit_time (logbuffer, now);
643 emit_value (logbuffer, num);
644 for (i = 0; i < num; ++i)
645 emit_obj (logbuffer, objects [i]);
646 //printf ("gc moved %d objects\n", num/2);
650 gc_handle (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj)
653 LogBuffer *logbuffer = ensure_logbuf (16);
654 now = current_time ();
655 if (op == MONO_PROFILER_GC_HANDLE_CREATED)
656 emit_byte (logbuffer, TYPE_GC_HANDLE_CREATED | TYPE_GC);
657 else if (op == MONO_PROFILER_GC_HANDLE_DESTROYED)
658 emit_byte (logbuffer, TYPE_GC_HANDLE_DESTROYED | TYPE_GC);
661 emit_time (logbuffer, now);
662 emit_value (logbuffer, type);
663 emit_value (logbuffer, handle);
664 if (op == MONO_PROFILER_GC_HANDLE_CREATED)
665 emit_obj (logbuffer, obj);
669 push_nesting (char *p, MonoClass *klass)
674 nesting = mono_class_get_nesting_type (klass);
676 p = push_nesting (p, nesting);
680 name = mono_class_get_name (klass);
681 nspace = mono_class_get_namespace (klass);
684 p += strlen (nspace);
694 type_name (MonoClass *klass)
698 push_nesting (buf, klass);
699 p = malloc (strlen (buf) + 1);
705 image_loaded (MonoProfiler *prof, MonoImage *image, int result)
710 LogBuffer *logbuffer;
711 if (result != MONO_PROFILE_OK)
713 name = mono_image_get_filename (image);
714 nlen = strlen (name) + 1;
715 logbuffer = ensure_logbuf (16 + nlen);
716 now = current_time ();
717 emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
718 emit_time (logbuffer, now);
719 emit_byte (logbuffer, TYPE_IMAGE);
720 emit_ptr (logbuffer, image);
721 emit_value (logbuffer, 0); /* flags */
722 memcpy (logbuffer->data, name, nlen);
723 logbuffer->data += nlen;
724 //printf ("loaded image %p (%s)\n", image, name);
726 safe_dump (prof, logbuffer);
730 class_loaded (MonoProfiler *prof, MonoClass *klass, int result)
736 LogBuffer *logbuffer;
737 if (result != MONO_PROFILE_OK)
740 name = mono_type_get_name (mono_class_get_type (klass));
742 name = type_name (klass);
743 nlen = strlen (name) + 1;
744 image = mono_class_get_image (klass);
745 logbuffer = ensure_logbuf (24 + nlen);
746 now = current_time ();
747 emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
748 emit_time (logbuffer, now);
749 emit_byte (logbuffer, TYPE_CLASS);
750 emit_ptr (logbuffer, klass);
751 emit_ptr (logbuffer, image);
752 emit_value (logbuffer, 0); /* flags */
753 memcpy (logbuffer->data, name, nlen);
754 logbuffer->data += nlen;
755 //printf ("loaded class %p (%s)\n", klass, name);
761 safe_dump (prof, logbuffer);
765 method_enter (MonoProfiler *prof, MonoMethod *method)
768 LogBuffer *logbuffer = ensure_logbuf (16);
769 if (logbuffer->call_depth++ > max_call_depth)
771 now = current_time ();
772 emit_byte (logbuffer, TYPE_ENTER | TYPE_METHOD);
773 emit_time (logbuffer, now);
774 emit_method (logbuffer, method);
778 method_leave (MonoProfiler *prof, MonoMethod *method)
781 LogBuffer *logbuffer = ensure_logbuf (16);
782 if (--logbuffer->call_depth > max_call_depth)
784 now = current_time ();
785 emit_byte (logbuffer, TYPE_LEAVE | TYPE_METHOD);
786 emit_time (logbuffer, now);
787 emit_method (logbuffer, method);
789 safe_dump (prof, logbuffer);
793 method_exc_leave (MonoProfiler *prof, MonoMethod *method)
796 LogBuffer *logbuffer;
799 logbuffer = ensure_logbuf (16);
800 if (--logbuffer->call_depth > max_call_depth)
802 now = current_time ();
803 emit_byte (logbuffer, TYPE_EXC_LEAVE | TYPE_METHOD);
804 emit_time (logbuffer, now);
805 emit_method (logbuffer, method);
809 method_jitted (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result)
814 LogBuffer *logbuffer;
815 if (result != MONO_PROFILE_OK)
817 name = mono_method_full_name (method, 1);
818 nlen = strlen (name) + 1;
819 logbuffer = ensure_logbuf (32 + nlen);
820 now = current_time ();
821 emit_byte (logbuffer, TYPE_JIT | TYPE_METHOD);
822 emit_time (logbuffer, now);
823 emit_method (logbuffer, method);
824 emit_ptr (logbuffer, mono_jit_info_get_code_start (jinfo));
825 emit_value (logbuffer, mono_jit_info_get_code_size (jinfo));
826 memcpy (logbuffer->data, name, nlen);
827 logbuffer->data += nlen;
830 safe_dump (prof, logbuffer);
834 throw_exc (MonoProfiler *prof, MonoObject *object)
836 int do_bt = (nocalls && runtime_inited && !notraces)? TYPE_EXCEPTION_BT: 0;
839 LogBuffer *logbuffer;
842 logbuffer = ensure_logbuf (16 + MAX_FRAMES * 8);
843 now = current_time ();
844 emit_byte (logbuffer, do_bt | TYPE_EXCEPTION);
845 emit_time (logbuffer, now);
846 emit_obj (logbuffer, object);
848 emit_bt (logbuffer, &data);
852 clause_exc (MonoProfiler *prof, MonoMethod *method, int clause_type, int clause_num)
855 LogBuffer *logbuffer = ensure_logbuf (16);
856 now = current_time ();
857 emit_byte (logbuffer, TYPE_EXCEPTION | TYPE_CLAUSE);
858 emit_time (logbuffer, now);
859 emit_value (logbuffer, clause_type);
860 emit_value (logbuffer, clause_num);
861 emit_method (logbuffer, method);
865 monitor_event (MonoProfiler *profiler, MonoObject *object, MonoProfilerMonitorEvent event)
867 int do_bt = (nocalls && runtime_inited && !notraces && event == MONO_PROFILER_MONITOR_CONTENTION)? TYPE_MONITOR_BT: 0;
870 LogBuffer *logbuffer;
873 logbuffer = ensure_logbuf (16 + MAX_FRAMES * 8);
874 now = current_time ();
875 emit_byte (logbuffer, (event << 4) | do_bt | TYPE_MONITOR);
876 emit_time (logbuffer, now);
877 emit_obj (logbuffer, object);
879 emit_bt (logbuffer, &data);
883 thread_start (MonoProfiler *prof, uintptr_t tid)
885 //printf ("thread start %p\n", (void*)tid);
890 thread_end (MonoProfiler *prof, uintptr_t tid)
893 if (TLS_GET (tlsbuffer))
894 dump_buffer (prof, TLS_GET (tlsbuffer));
896 TLS_SET (tlsbuffer, NULL);
900 thread_name (MonoProfiler *prof, uintptr_t tid, const char *name)
902 int len = strlen (name) + 1;
904 LogBuffer *logbuffer;
905 logbuffer = ensure_logbuf (10 + len);
906 now = current_time ();
907 emit_byte (logbuffer, TYPE_METADATA);
908 emit_time (logbuffer, now);
909 emit_byte (logbuffer, TYPE_THREAD);
910 emit_ptr (logbuffer, (void*)tid);
911 emit_value (logbuffer, 0); /* flags */
912 memcpy (logbuffer->data, name, len);
913 logbuffer->data += len;
917 log_shutdown (MonoProfiler *prof)
920 if (TLS_GET (tlsbuffer))
921 dump_buffer (prof, TLS_GET (tlsbuffer));
922 TLS_SET (tlsbuffer, NULL);
924 #if defined (HAVE_ZLIB)
926 gzclose (prof->gzfile);
928 if (prof->pipe_output)
936 create_profiler (const char *filename)
939 prof = calloc (1, sizeof (MonoProfiler));
940 if (do_report) /* FIXME: use filename as output */
941 filename = "|mprof-report -";
944 filename = "output.mlpd";
945 if (*filename == '|') {
946 prof->file = popen (filename + 1, "w");
947 prof->pipe_output = 1;
949 prof->file = fopen (filename, "wb");
952 printf ("Cannot create profiler output: %s\n", filename);
955 #if defined (HAVE_ZLIB)
957 prof->gzfile = gzdopen (fileno (prof->file), "wb");
966 printf ("Log profiler version %d.%d (format: %d)\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
967 printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
968 printf ("Options:\n");
969 printf ("\thelp show this usage info\n");
970 printf ("\t[no]alloc enable/disable recording allocation info\n");
971 printf ("\t[no]calls enable/disable recording enter/leave method events\n");
972 printf ("\theapshot record heap shot info (by default at each major collection)\n");
973 printf ("\thsmode=MODE heapshot mode: every XXms milliseconds or every YYgc collections\n");
974 printf ("\ttime=fast use a faster (but more inaccurate) timer\n");
975 printf ("\tmaxframes=NUM collect up to NUM stack frames\n");
976 printf ("\tcalldepth=NUM ignore method events for call chain depth bigger than NUM\n");
977 printf ("\toutput=FILENAME write the data to file FILENAME\n");
978 printf ("\toutput=|PROGRAM write the data to the stdin of PROGRAM\n");
979 printf ("\treport create a report instead of writing the raw data to a file\n");
980 printf ("\tzip compress the output data\n");
986 match_option (const char* p, const char *opt, char **rval)
988 int len = strlen (opt);
989 if (strncmp (p, opt, len) == 0) {
991 if (p [len] == '=' && p [len + 1]) {
992 const char *opt = p + len + 1;
993 const char *end = strchr (opt, ',');
1001 val = malloc (l + 1);
1002 memcpy (val, opt, l);
1019 * declaration to silence the compiler: this is the entry point that
1020 * mono will load from the shared library and call.
1023 mono_profiler_startup (const char *desc);
1026 mono_profiler_startup (const char *desc)
1029 char *filename = NULL;
1033 int calls_enabled = 0;
1034 int allocs_enabled = 0;
1035 int events = MONO_PROFILE_GC|MONO_PROFILE_ALLOCATIONS|
1036 MONO_PROFILE_GC_MOVES|MONO_PROFILE_CLASS_EVENTS|MONO_PROFILE_THREADS|
1037 MONO_PROFILE_ENTER_LEAVE|MONO_PROFILE_JIT_COMPILATION|MONO_PROFILE_EXCEPTIONS|
1038 MONO_PROFILE_MONITOR_EVENTS|MONO_PROFILE_MODULE_EVENTS|MONO_PROFILE_GC_ROOTS;
1041 if (strncmp (p, "log", 3))
1046 for (; *p; p = opt) {
1052 if ((opt = match_option (p, "help", NULL)) != p) {
1056 if ((opt = match_option (p, "calls", NULL)) != p) {
1060 if ((opt = match_option (p, "nocalls", NULL)) != p) {
1061 events &= ~MONO_PROFILE_ENTER_LEAVE;
1065 if ((opt = match_option (p, "alloc", NULL)) != p) {
1069 if ((opt = match_option (p, "noalloc", NULL)) != p) {
1070 events &= ~MONO_PROFILE_ALLOCATIONS;
1073 if ((opt = match_option (p, "time", &val)) != p) {
1074 if (strcmp (val, "fast") == 0)
1076 else if (strcmp (val, "null") == 0)
1083 if ((opt = match_option (p, "report", NULL)) != p) {
1087 if ((opt = match_option (p, "heapshot", NULL)) != p) {
1088 events &= ~MONO_PROFILE_ALLOCATIONS;
1089 events &= ~MONO_PROFILE_ENTER_LEAVE;
1094 if ((opt = match_option (p, "hsmode", &val)) != p) {
1096 unsigned int count = strtoul (val, &end, 10);
1099 if (strcmp (end, "ms") == 0)
1101 else if (strcmp (end, "gc") == 0)
1108 if ((opt = match_option (p, "zip", NULL)) != p) {
1112 if ((opt = match_option (p, "output", &val)) != p) {
1116 if ((opt = match_option (p, "maxframes", &val)) != p) {
1118 num_frames = strtoul (val, &end, 10);
1119 if (num_frames > MAX_FRAMES)
1120 num_frames = MAX_FRAMES;
1122 notraces = num_frames == 0;
1125 if ((opt = match_option (p, "calldepth", &val)) != p) {
1127 max_call_depth = strtoul (val, &end, 10);
1136 if (calls_enabled) {
1137 events |= MONO_PROFILE_ENTER_LEAVE;
1141 events |= MONO_PROFILE_ALLOCATIONS;
1142 utils_init (fast_time);
1144 prof = create_profiler (filename);
1147 mono_profiler_install (prof, log_shutdown);
1148 mono_profiler_install_gc (gc_event, gc_resize);
1149 mono_profiler_install_allocation (gc_alloc);
1150 mono_profiler_install_gc_moves (gc_moves);
1151 mono_profiler_install_gc_roots (gc_handle);
1152 mono_profiler_install_class (NULL, class_loaded, NULL, NULL);
1153 mono_profiler_install_module (NULL, image_loaded, NULL, NULL);
1154 mono_profiler_install_thread (thread_start, thread_end);
1155 mono_profiler_install_thread_name (thread_name);
1156 mono_profiler_install_enter_leave (method_enter, method_leave);
1157 mono_profiler_install_jit_end (method_jitted);
1158 mono_profiler_install_exception (throw_exc, method_exc_leave, clause_exc);
1159 mono_profiler_install_monitor (monitor_event);
1160 mono_profiler_install_runtime_initialized (runtime_initialized);
1162 mono_profiler_set_events (events);
1164 TLS_INIT (tlsbuffer);