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
137 * [time diff: uleb128] nanoseconds since last timing
138 * if exinfo == TYPE_GC_RESIZE
139 * [heap_size: uleb128] new heap size
140 * if exinfo == TYPE_GC_EVENT
141 * [event type: uleb128] GC event (MONO_GC_EVENT_* from profiler.h)
142 * [generation: uleb128] GC generation event refers to
143 * if exinfo == TYPE_GC_MOVE
144 * [num_objects: uleb128] number of object moves that follow
145 * [objaddr: sleb128]+ num_objects object pointer differences from obj_base
146 * num is always an even number: the even items are the old
147 * addresses, the odd numbers are the respective new object addresses
149 * type metadata format:
150 * type: TYPE_METADATA
151 * exinfo: flags: TYPE_LOAD_ERR
152 * [time diff: uleb128] nanoseconds since last timing
153 * [mtype: byte] metadata type, one of: TYPE_CLASS, TYPE_IMAGE, TYPE_ASSEMBLY, TYPE_DOMAIN
154 * [pointer: sleb128] pointer of the metadata type depending on mtype
155 * if mtype == TYPE_CLASS
156 * [image: sleb128] MonoImage* as a pointer difference from ptr_base
157 * [flags: uleb128] must be 0
158 * [name: string] full class name
159 * if mtype == TYPE_IMAGE
160 * [flags: uleb128] must be 0
161 * [name: string] image file name
163 * type method format:
165 * exinfo: one of: TYPE_LEAVE, TYPE_ENTER, TYPE_EXC_LEAVE, TYPE_JIT
166 * [time diff: uleb128] nanoseconds since last timing
167 * [method: sleb128] MonoMethod* as a pointer difference from the last such
168 * pointer or the buffer method_base
169 * if exinfo == TYPE_JIT
170 * [code address: sleb128] pointer to the native code as a diff from ptr_base
171 * [code size: uleb128] size of the generated code
172 * [name: string] full method name
174 * type exception format:
175 * type: TYPE_EXCEPTION
176 * exinfo: TYPE_EXCEPTION_BT flag and one of: TYPE_THROW, TYPE_CLAUSE
177 * [time diff: uleb128] nanoseconds since last timing
178 * if exinfo.low3bits == TYPE_CLAUSE
179 * [clause type: uleb128] finally/catch/fault/filter
180 * [clause num: uleb128] the clause number in the method header
181 * [method: sleb128] MonoMethod* as a pointer difference from the last such
182 * pointer or the buffer method_base
183 * if exinfo.low3bits == TYPE_THROW
184 * [object: sleb128] the object that was thrown as a difference from obj_base
185 * If the TYPE_EXCEPTION_BT flag is set, a backtrace follows.
187 * type monitor format:
189 * exinfo: TYPE_MONITOR_BT flag and one of: MONO_PROFILER_MONITOR_(CONTENTION|FAIL|DONE)
190 * [time diff: uleb128] nanoseconds since last timing
191 * [object: sleb128] the lock object as a difference from obj_base
192 * if exinfo.low3bits == MONO_PROFILER_MONITOR_CONTENTION
193 * If the TYPE_MONITOR_BT flag is set, a backtrace follows.
197 * exinfo: one of TYPE_HEAP_START, TYPE_HEAP_END, TYPE_HEAP_OBJECT
198 * if exinfo == TYPE_HEAP_START
199 * [time diff: uleb128] nanoseconds since last timing
200 * if exinfo == TYPE_HEAP_END
201 * [time diff: uleb128] nanoseconds since last timing
202 * if exinfo == TYPE_HEAP_OBJECT
203 * [object: sleb128] the object as a difference from obj_base
204 * [class: sleb128] the object MonoClass* as a difference from ptr_base
205 * [size: uleb128] size of the object on the heap
206 * [num_refs: uleb128] number of object references
207 * [objrefs: sleb128]+ object referenced as a difference from obj_base
215 uintptr_t method_base;
216 uintptr_t last_method;
219 unsigned char* data_end;
223 unsigned char buf [1];
226 struct _MonoProfiler {
229 #if defined (HAVE_ZLIB)
233 int last_gc_gen_started;
237 #define TLS_SET(x,y) TlsSetValue(x, y)
238 #define TLS_GET(x) ((LogBuffer *) TlsGetValue(x))
239 #define TLS_INIT(x) x = TlsAlloc ()
240 static int tlsbuffer;
242 #define TLS_SET(x,y) x = y
245 static __thread LogBuffer* tlsbuffer = NULL;
247 #define TLS_SET(x,y) pthread_setspecific(x, y)
248 #define TLS_GET(x) ((LogBuffer *) pthread_getspecific(x))
249 #define TLS_INIT(x) pthread_key_create(&x, NULL)
250 static pthread_key_t tlsbuffer;
256 LogBuffer* buf = alloc_buffer (BUFFER_SIZE);
257 buf->size = BUFFER_SIZE;
258 buf->time_base = current_time ();
259 buf->last_time = buf->time_base;
260 buf->data_end = (unsigned char*)buf + buf->size;
261 buf->data = buf->buf;
268 LogBuffer *logbuffer;
269 if (TLS_GET (tlsbuffer))
271 logbuffer = create_buffer ();
272 TLS_SET (tlsbuffer, logbuffer);
273 logbuffer->thread_id = thread_id ();
274 //printf ("thread %p at time %llu\n", (void*)logbuffer->thread_id, logbuffer->time_base);
278 ensure_logbuf (int bytes)
280 LogBuffer *old = TLS_GET (tlsbuffer);
281 if (old && old->data + bytes + 100 < old->data_end)
283 TLS_SET (tlsbuffer, NULL);
285 TLS_GET (tlsbuffer)->next = old;
287 TLS_GET (tlsbuffer)->call_depth = old->call_depth;
288 //printf ("new logbuffer\n");
289 return TLS_GET (tlsbuffer);
293 emit_byte (LogBuffer *logbuffer, int value)
295 logbuffer->data [0] = value;
297 assert (logbuffer->data <= logbuffer->data_end);
301 emit_value (LogBuffer *logbuffer, int value)
303 encode_uleb128 (value, logbuffer->data, &logbuffer->data);
304 assert (logbuffer->data <= logbuffer->data_end);
308 emit_time (LogBuffer *logbuffer, uint64_t value)
310 uint64_t tdiff = value - logbuffer->last_time;
312 if (value < logbuffer->last_time)
313 printf ("time went backwards\n");
314 //if (tdiff > 1000000)
315 // printf ("large time offset: %llu\n", tdiff);
317 encode_uleb128 (tdiff, logbuffer->data, &logbuffer->data);
318 /*if (tdiff != decode_uleb128 (p, &p))
319 printf ("incorrect encoding: %llu\n", tdiff);*/
320 logbuffer->last_time = value;
321 assert (logbuffer->data <= logbuffer->data_end);
325 emit_svalue (LogBuffer *logbuffer, int64_t value)
327 encode_sleb128 (value, logbuffer->data, &logbuffer->data);
328 assert (logbuffer->data <= logbuffer->data_end);
332 emit_ptr (LogBuffer *logbuffer, void *ptr)
334 if (!logbuffer->ptr_base)
335 logbuffer->ptr_base = (uintptr_t)ptr;
336 emit_svalue (logbuffer, (intptr_t)ptr - logbuffer->ptr_base);
337 assert (logbuffer->data <= logbuffer->data_end);
341 emit_method (LogBuffer *logbuffer, void *method)
343 if (!logbuffer->method_base) {
344 logbuffer->method_base = (intptr_t)method;
345 logbuffer->last_method = (intptr_t)method;
347 encode_sleb128 ((intptr_t)((char*)method - (char*)logbuffer->last_method), logbuffer->data, &logbuffer->data);
348 logbuffer->last_method = (intptr_t)method;
349 assert (logbuffer->data <= logbuffer->data_end);
353 emit_obj (LogBuffer *logbuffer, void *ptr)
355 if (!logbuffer->obj_base)
356 logbuffer->obj_base = (uintptr_t)ptr >> 3;
357 emit_svalue (logbuffer, ((uintptr_t)ptr >> 3) - logbuffer->obj_base);
358 assert (logbuffer->data <= logbuffer->data_end);
362 write_int32 (char *buf, int32_t value)
365 for (i = 0; i < 4; ++i) {
373 write_int64 (char *buf, int64_t value)
376 for (i = 0; i < 8; ++i) {
384 dump_header (MonoProfiler *profiler)
388 p = write_int32 (p, LOG_HEADER_ID);
389 *p++ = LOG_VERSION_MAJOR;
390 *p++ = LOG_VERSION_MINOR;
391 *p++ = LOG_DATA_VERSION;
392 *p++ = sizeof (void*);
393 p = write_int64 (p, ((uint64_t)time (NULL)) * 1000); /* startup time */
394 p = write_int32 (p, get_timer_overhead ()); /* timer overhead */
395 p = write_int32 (p, 0); /* flags */
396 p = write_int32 (p, 0); /* pid */
397 p = write_int32 (p, 0); /* opsystem */
398 #if defined (HAVE_ZLIB)
399 if (profiler->gzfile) {
400 gzwrite (profiler->gzfile, hbuf, p - hbuf);
402 fwrite (hbuf, p - hbuf, 1, profiler->file);
405 fwrite (hbuf, p - hbuf, 1, profiler->file);
410 dump_buffer (MonoProfiler *profiler, LogBuffer *buf)
415 dump_buffer (profiler, buf->next);
416 p = write_int32 (p, BUF_ID);
417 p = write_int32 (p, buf->data - buf->buf);
418 p = write_int64 (p, buf->time_base);
419 p = write_int64 (p, buf->ptr_base);
420 p = write_int64 (p, buf->obj_base);
421 p = write_int64 (p, buf->thread_id);
422 p = write_int64 (p, buf->method_base);
423 #if defined (HAVE_ZLIB)
424 if (profiler->gzfile) {
425 gzwrite (profiler->gzfile, hbuf, p - hbuf);
426 gzwrite (profiler->gzfile, buf->buf, buf->data - buf->buf);
429 fwrite (hbuf, p - hbuf, 1, profiler->file);
430 fwrite (buf->buf, buf->data - buf->buf, 1, profiler->file);
431 #if defined (HAVE_ZLIB)
434 free_buffer (buf, buf->size);
438 runtime_initialized (MonoProfiler *profiler)
444 * Can be called only at safe callback locations.
447 safe_dump (MonoProfiler *profiler, LogBuffer *logbuffer)
449 int cd = logbuffer->call_depth;
451 dump_buffer (profiler, TLS_GET (tlsbuffer));
453 TLS_SET (tlsbuffer, NULL);
455 TLS_GET (tlsbuffer)->call_depth = cd;
459 gc_reference (MonoObject *obj, MonoClass *klass, uintptr_t size, uintptr_t num, MonoObject **refs, void *data)
462 //const char *name = mono_class_get_name (klass);
463 LogBuffer *logbuffer = ensure_logbuf (20 + num * 8);
464 emit_byte (logbuffer, TYPE_HEAP_OBJECT | TYPE_HEAP);
465 emit_obj (logbuffer, obj);
466 emit_ptr (logbuffer, klass);
467 /* account for object alignment in the heap */
470 emit_value (logbuffer, size);
471 emit_value (logbuffer, num);
472 for (i = 0; i < num; ++i)
473 emit_obj (logbuffer, refs [i]);
475 // printf ("obj: %p, klass: %s, refs: %d, size: %d\n", obj, name, (int)num, (int)size);
479 static unsigned int hs_mode_ms = 0;
480 static unsigned int hs_mode_gc = 0;
481 static unsigned int gc_count = 0;
482 static uint64_t last_hs_time = 0;
485 heap_walk (MonoProfiler *profiler)
489 LogBuffer *logbuffer;
492 logbuffer = ensure_logbuf (10);
493 now = current_time ();
494 if (hs_mode_ms && (now - last_hs_time)/1000000 >= hs_mode_ms)
496 else if (hs_mode_gc && (gc_count % hs_mode_gc) == 0)
498 else if (!hs_mode_ms && !hs_mode_gc && profiler->last_gc_gen_started == mono_gc_max_generation ())
503 emit_byte (logbuffer, TYPE_HEAP_START | TYPE_HEAP);
504 emit_time (logbuffer, now);
505 mono_gc_walk_heap (0, gc_reference, NULL);
506 logbuffer = ensure_logbuf (10);
507 now = current_time ();
508 emit_byte (logbuffer, TYPE_HEAP_END | TYPE_HEAP);
509 emit_time (logbuffer, now);
513 gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
515 LogBuffer *logbuffer = ensure_logbuf (10);
516 now = current_time ();
517 emit_byte (logbuffer, TYPE_GC_EVENT | TYPE_GC);
518 emit_time (logbuffer, now);
519 emit_value (logbuffer, ev);
520 emit_value (logbuffer, generation);
521 /* to deal with nested gen1 after gen0 started */
522 if (ev == MONO_GC_EVENT_START) {
523 profiler->last_gc_gen_started = generation;
526 if (ev == MONO_GC_EVENT_PRE_START_WORLD)
527 heap_walk (profiler);
528 if (ev == MONO_GC_EVENT_POST_START_WORLD)
529 safe_dump (profiler, logbuffer);
530 //printf ("gc event %d for generation %d\n", ev, generation);
534 gc_resize (MonoProfiler *profiler, int64_t new_size) {
536 LogBuffer *logbuffer = ensure_logbuf (10);
537 now = current_time ();
538 emit_byte (logbuffer, TYPE_GC_RESIZE | TYPE_GC);
539 emit_time (logbuffer, now);
540 emit_value (logbuffer, new_size);
541 //printf ("gc resized to %lld\n", new_size);
544 #define MAX_FRAMES 16
547 MonoMethod* methods [MAX_FRAMES];
549 static int num_frames = MAX_FRAMES / 2;
552 walk_stack (MonoMethod *method, int32_t native_offset, int32_t il_offset, mono_bool managed, void* data)
554 FrameData *frame = data;
555 if (method && frame->count < num_frames) {
556 frame->methods [frame->count++] = method;
557 //printf ("In %d %s\n", frame->count, mono_method_get_name (method));
559 return frame->count == num_frames;
563 * a note about stack walks: they can cause more profiler events to fire,
564 * so we need to make sure they don't happen after we started emitting an
565 * event, hence the collect_bt/emit_bt split.
568 collect_bt (FrameData *data)
571 mono_stack_walk_no_il (walk_stack, data);
575 emit_bt (LogBuffer *logbuffer, FrameData *data)
577 /* FIXME: this is actually tons of data and we should
578 * just output it the first time and use an id the next
580 if (data->count > num_frames)
581 printf ("bad num frames: %d\n", data->count);
582 emit_value (logbuffer, 0); /* flags */
583 emit_value (logbuffer, data->count);
584 //if (*p != data.count) {
585 // 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);}
586 while (data->count) {
587 emit_ptr (logbuffer, data->methods [--data->count]);
592 gc_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
596 int do_bt = (nocalls && runtime_inited && !notraces)? TYPE_ALLOC_BT: 0;
598 LogBuffer *logbuffer;
599 len = mono_object_get_size (obj);
600 /* account for object alignment in the heap */
605 logbuffer = ensure_logbuf (32 + MAX_FRAMES * 8);
606 now = current_time ();
607 emit_byte (logbuffer, do_bt | TYPE_ALLOC);
608 emit_time (logbuffer, now);
609 emit_ptr (logbuffer, klass);
610 emit_obj (logbuffer, obj);
611 emit_value (logbuffer, len);
613 emit_bt (logbuffer, &data);
615 safe_dump (prof, logbuffer);
616 //printf ("gc alloc %s at %p\n", mono_class_get_name (klass), obj);
620 gc_moves (MonoProfiler *prof, void **objects, int num)
624 LogBuffer *logbuffer = ensure_logbuf (10 + num * 8);
625 now = current_time ();
626 emit_byte (logbuffer, TYPE_GC_MOVE | TYPE_GC);
627 emit_time (logbuffer, now);
628 emit_value (logbuffer, num);
629 for (i = 0; i < num; ++i)
630 emit_obj (logbuffer, objects [i]);
631 //printf ("gc moved %d objects\n", num/2);
635 push_nesting (char *p, MonoClass *klass)
640 nesting = mono_class_get_nesting_type (klass);
642 p = push_nesting (p, nesting);
646 name = mono_class_get_name (klass);
647 nspace = mono_class_get_namespace (klass);
650 p += strlen (nspace);
660 type_name (MonoClass *klass)
664 push_nesting (buf, klass);
665 p = malloc (strlen (buf) + 1);
671 image_loaded (MonoProfiler *prof, MonoImage *image, int result)
676 LogBuffer *logbuffer;
677 if (result != MONO_PROFILE_OK)
679 name = mono_image_get_filename (image);
680 nlen = strlen (name) + 1;
681 logbuffer = ensure_logbuf (16 + nlen);
682 now = current_time ();
683 emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
684 emit_time (logbuffer, now);
685 emit_byte (logbuffer, TYPE_IMAGE);
686 emit_ptr (logbuffer, image);
687 emit_value (logbuffer, 0); /* flags */
688 memcpy (logbuffer->data, name, nlen);
689 logbuffer->data += nlen;
690 //printf ("loaded image %p (%s)\n", image, name);
692 safe_dump (prof, logbuffer);
696 class_loaded (MonoProfiler *prof, MonoClass *klass, int result)
702 LogBuffer *logbuffer;
703 if (result != MONO_PROFILE_OK)
706 name = mono_type_get_name (mono_class_get_type (klass));
708 name = type_name (klass);
709 nlen = strlen (name) + 1;
710 image = mono_class_get_image (klass);
711 logbuffer = ensure_logbuf (24 + nlen);
712 now = current_time ();
713 emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
714 emit_time (logbuffer, now);
715 emit_byte (logbuffer, TYPE_CLASS);
716 emit_ptr (logbuffer, klass);
717 emit_ptr (logbuffer, image);
718 emit_value (logbuffer, 0); /* flags */
719 memcpy (logbuffer->data, name, nlen);
720 logbuffer->data += nlen;
721 //printf ("loaded class %p (%s)\n", klass, name);
727 safe_dump (prof, logbuffer);
731 method_enter (MonoProfiler *prof, MonoMethod *method)
734 LogBuffer *logbuffer = ensure_logbuf (16);
735 if (logbuffer->call_depth++ > max_call_depth)
737 now = current_time ();
738 emit_byte (logbuffer, TYPE_ENTER | TYPE_METHOD);
739 emit_time (logbuffer, now);
740 emit_method (logbuffer, method);
744 method_leave (MonoProfiler *prof, MonoMethod *method)
747 LogBuffer *logbuffer = ensure_logbuf (16);
748 if (--logbuffer->call_depth > max_call_depth)
750 now = current_time ();
751 emit_byte (logbuffer, TYPE_LEAVE | TYPE_METHOD);
752 emit_time (logbuffer, now);
753 emit_method (logbuffer, method);
755 safe_dump (prof, logbuffer);
759 method_exc_leave (MonoProfiler *prof, MonoMethod *method)
762 LogBuffer *logbuffer;
765 logbuffer = ensure_logbuf (16);
766 if (--logbuffer->call_depth > max_call_depth)
768 now = current_time ();
769 emit_byte (logbuffer, TYPE_EXC_LEAVE | TYPE_METHOD);
770 emit_time (logbuffer, now);
771 emit_method (logbuffer, method);
775 method_jitted (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result)
780 LogBuffer *logbuffer;
781 if (result != MONO_PROFILE_OK)
783 name = mono_method_full_name (method, 1);
784 nlen = strlen (name) + 1;
785 logbuffer = ensure_logbuf (32 + nlen);
786 now = current_time ();
787 emit_byte (logbuffer, TYPE_JIT | TYPE_METHOD);
788 emit_time (logbuffer, now);
789 emit_method (logbuffer, method);
790 emit_ptr (logbuffer, mono_jit_info_get_code_start (jinfo));
791 emit_value (logbuffer, mono_jit_info_get_code_size (jinfo));
792 memcpy (logbuffer->data, name, nlen);
793 logbuffer->data += nlen;
796 safe_dump (prof, logbuffer);
800 throw_exc (MonoProfiler *prof, MonoObject *object)
802 int do_bt = (nocalls && runtime_inited && !notraces)? TYPE_EXCEPTION_BT: 0;
805 LogBuffer *logbuffer;
808 logbuffer = ensure_logbuf (16 + MAX_FRAMES * 8);
809 now = current_time ();
810 emit_byte (logbuffer, do_bt | TYPE_EXCEPTION);
811 emit_time (logbuffer, now);
812 emit_obj (logbuffer, object);
814 emit_bt (logbuffer, &data);
818 clause_exc (MonoProfiler *prof, MonoMethod *method, int clause_type, int clause_num)
821 LogBuffer *logbuffer = ensure_logbuf (16);
822 now = current_time ();
823 emit_byte (logbuffer, TYPE_EXCEPTION | TYPE_CLAUSE);
824 emit_time (logbuffer, now);
825 emit_value (logbuffer, clause_type);
826 emit_value (logbuffer, clause_num);
827 emit_method (logbuffer, method);
831 monitor_event (MonoProfiler *profiler, MonoObject *object, MonoProfilerMonitorEvent event)
833 int do_bt = (nocalls && runtime_inited && !notraces && event == MONO_PROFILER_MONITOR_CONTENTION)? TYPE_MONITOR_BT: 0;
836 LogBuffer *logbuffer;
839 logbuffer = ensure_logbuf (16 + MAX_FRAMES * 8);
840 now = current_time ();
841 emit_byte (logbuffer, (event << 4) | do_bt | TYPE_MONITOR);
842 emit_time (logbuffer, now);
843 emit_obj (logbuffer, object);
845 emit_bt (logbuffer, &data);
849 thread_start (MonoProfiler *prof, uintptr_t tid)
851 //printf ("thread start %p\n", (void*)tid);
856 thread_end (MonoProfiler *prof, uintptr_t tid)
859 if (TLS_GET (tlsbuffer))
860 dump_buffer (prof, TLS_GET (tlsbuffer));
862 TLS_SET (tlsbuffer, NULL);
866 log_shutdown (MonoProfiler *prof)
869 if (TLS_GET (tlsbuffer))
870 dump_buffer (prof, TLS_GET (tlsbuffer));
871 TLS_SET (tlsbuffer, NULL);
873 #if defined (HAVE_ZLIB)
875 gzclose (prof->gzfile);
877 if (prof->pipe_output)
885 create_profiler (const char *filename)
888 prof = calloc (1, sizeof (MonoProfiler));
889 if (do_report) /* FIXME: use filename as output */
890 filename = "|mprof-report -";
893 filename = "output.mlpd";
894 if (*filename == '|') {
895 prof->file = popen (filename + 1, "w");
896 prof->pipe_output = 1;
898 prof->file = fopen (filename, "wb");
901 printf ("Cannot create profiler output: %s\n", filename);
904 #if defined (HAVE_ZLIB)
906 prof->gzfile = gzdopen (fileno (prof->file), "wb");
915 printf ("Log profiler version %d.%d (format: %d)\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
916 printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
917 printf ("Options:\n");
918 printf ("\thelp show this usage info\n");
919 printf ("\t[no]alloc enable/disable recording allocation info\n");
920 printf ("\t[no]calls enable/disable recording enter/leave method events\n");
921 printf ("\theapshot record heap shot info (by default at each major collection)\n");
922 printf ("\thsmode=MODE heapshot mode: every XXms milliseconds or every YYgc collections\n");
923 printf ("\ttime=fast use a faster (but more inaccurate) timer\n");
924 printf ("\tmaxframes=NUM collect up to NUM stack frames\n");
925 printf ("\tcalldepth=NUM ignore method events for call chain depth bigger than NUM\n");
926 printf ("\toutput=FILENAME write the data to file FILENAME\n");
927 printf ("\toutput=|PROGRAM write the data to the stdin of PROGRAM\n");
928 printf ("\treport create a report instead of writing the raw data to a file\n");
929 printf ("\tzip compress the output data\n");
935 match_option (const char* p, const char *opt, char **rval)
937 int len = strlen (opt);
938 if (strncmp (p, opt, len) == 0) {
940 if (p [len] == '=' && p [len + 1]) {
941 const char *opt = p + len + 1;
942 const char *end = strchr (opt, ',');
950 val = malloc (l + 1);
951 memcpy (val, opt, l);
968 * declaration to silence the compiler: this is the entry point that
969 * mono will load from the shared library and call.
972 mono_profiler_startup (const char *desc);
975 mono_profiler_startup (const char *desc)
978 char *filename = NULL;
982 int calls_enabled = 0;
983 int allocs_enabled = 0;
984 int events = MONO_PROFILE_GC|MONO_PROFILE_ALLOCATIONS|
985 MONO_PROFILE_GC_MOVES|MONO_PROFILE_CLASS_EVENTS|MONO_PROFILE_THREADS|
986 MONO_PROFILE_ENTER_LEAVE|MONO_PROFILE_JIT_COMPILATION|MONO_PROFILE_EXCEPTIONS|
987 MONO_PROFILE_MONITOR_EVENTS|MONO_PROFILE_MODULE_EVENTS;
990 if (strncmp (p, "log", 3))
995 for (; *p; p = opt) {
1001 if ((opt = match_option (p, "help", NULL)) != p) {
1005 if ((opt = match_option (p, "calls", NULL)) != p) {
1009 if ((opt = match_option (p, "nocalls", NULL)) != p) {
1010 events &= ~MONO_PROFILE_ENTER_LEAVE;
1014 if ((opt = match_option (p, "alloc", NULL)) != p) {
1018 if ((opt = match_option (p, "noalloc", NULL)) != p) {
1019 events &= ~MONO_PROFILE_ALLOCATIONS;
1022 if ((opt = match_option (p, "time", &val)) != p) {
1023 if (strcmp (val, "fast") == 0)
1025 else if (strcmp (val, "null") == 0)
1032 if ((opt = match_option (p, "report", NULL)) != p) {
1036 if ((opt = match_option (p, "heapshot", NULL)) != p) {
1037 events &= ~MONO_PROFILE_ALLOCATIONS;
1038 events &= ~MONO_PROFILE_ENTER_LEAVE;
1043 if ((opt = match_option (p, "hsmode", &val)) != p) {
1045 unsigned int count = strtoul (val, &end, 10);
1048 if (strcmp (end, "ms") == 0)
1050 else if (strcmp (end, "gc") == 0)
1057 if ((opt = match_option (p, "zip", NULL)) != p) {
1061 if ((opt = match_option (p, "output", &val)) != p) {
1065 if ((opt = match_option (p, "maxframes", &val)) != p) {
1067 num_frames = strtoul (val, &end, 10);
1068 if (num_frames > MAX_FRAMES)
1069 num_frames = MAX_FRAMES;
1071 notraces = num_frames == 0;
1074 if ((opt = match_option (p, "calldepth", &val)) != p) {
1076 max_call_depth = strtoul (val, &end, 10);
1085 if (calls_enabled) {
1086 events |= MONO_PROFILE_ENTER_LEAVE;
1090 events |= MONO_PROFILE_ALLOCATIONS;
1091 utils_init (fast_time);
1093 prof = create_profiler (filename);
1096 mono_profiler_install (prof, log_shutdown);
1097 mono_profiler_install_gc (gc_event, gc_resize);
1098 mono_profiler_install_allocation (gc_alloc);
1099 mono_profiler_install_gc_moves (gc_moves);
1100 mono_profiler_install_class (NULL, class_loaded, NULL, NULL);
1101 mono_profiler_install_module (NULL, image_loaded, NULL, NULL);
1102 mono_profiler_install_thread (thread_start, thread_end);
1103 mono_profiler_install_enter_leave (method_enter, method_leave);
1104 mono_profiler_install_jit_end (method_jitted);
1105 mono_profiler_install_exception (throw_exc, method_exc_leave, clause_exc);
1106 mono_profiler_install_monitor (monitor_event);
1107 mono_profiler_install_runtime_initialized (runtime_initialized);
1109 mono_profiler_set_events (events);
1111 TLS_INIT (tlsbuffer);