Log profiler: allow overwriting data files and name them from date and pid.
[mono.git] / mono / profiler / proflog.c
1 /*
2  * proflog.c: mono log profiler
3  *
4  * Author:
5  *   Paolo Molaro (lupus@ximian.com)
6  *
7  * Copyright 2010 Novell, Inc (http://www.novell.com)
8  */
9
10 #include <config.h>
11 #include <mono/metadata/profiler.h>
12 #include <mono/metadata/mono-gc.h>
13 #include <mono/metadata/debug-helpers.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <assert.h>
17 #ifdef HOST_WIN32
18 #include <windows.h>
19 #else
20 #include <pthread.h>
21 #endif
22
23 #include "utils.c"
24 #include "proflog.h"
25
26 #if defined (HAVE_SYS_ZLIB)
27 #include <zlib.h>
28 #endif
29
30 #define BUFFER_SIZE (4096 * 16)
31 static int nocalls = 0;
32 static int notraces = 0;
33 static int use_zip = 0;
34 static int do_report = 0;
35 static int do_heap_shot = 0;
36 static int max_call_depth = 100;
37 static int runtime_inited = 0;
38
39 /* For linux compile with:
40  * gcc -fPIC -shared -o libmono-profiler-log.so proflog.c utils.c -Wall -g -lz `pkg-config --cflags --libs mono-2`
41  * gcc -o mprof-report decode.c utils.c -Wall -g -lz -lrt -lpthread `pkg-config --cflags mono-2`
42  *
43  * For osx compile with:
44  * 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
45  * gcc -m32 -o mprof-report decode.c utils.c -Wall -g -lz -lrt -lpthread `pkg-config --cflags mono-2`
46  *
47  * Install with:
48  * sudo cp mprof-report /usr/local/bin
49  * sudo cp libmono-profiler-log.so /usr/local/lib
50  * sudo ldconfig
51  */
52
53 typedef struct _LogBuffer LogBuffer;
54
55 /*
56  * file format:
57  * [header] [buffer]*
58  *
59  * The file is composed by a header followed by 0 or more buffers.
60  * Each buffer contains events that happened on a thread: for a given thread
61  * buffers that appear later in the file are guaranteed to contain events
62  * that happened later in time. Buffers from separate threads could be interleaved,
63  * though.
64  * Buffers are not required to be aligned.
65  *
66  * header format:
67  * [id: 4 bytes] constant value: LOG_HEADER_ID
68  * [major: 1 byte] [minor: 1 byte] major and minor version of the log profiler
69  * [format: 1 byte] version of the data format for the rest of the file
70  * [ptrsize: 1 byte] size in bytes of a pointer in the profiled program
71  * [startup time: 8 bytes] time in milliseconds since the unix epoch when the program started
72  * [timer overhead: 4 bytes] approximate overhead in nanoseconds of the timer
73  * [flags: 4 bytes] file format flags, should be 0 for now
74  * [pid: 4 bytes] pid of the profiled process
75  * [sysid: 4 bytes] operating system and architecture identifier
76  *
77  * The multiple byte integers are in little-endian format.
78  *
79  * buffer format:
80  * [buffer header] [event]*
81  * Buffers have a fixed-size header followed by 0 or more bytes of event data.
82  * Timing information and other values in the event data are usually stored
83  * as uleb128 or sleb128 integers. To save space, as noted for each item below,
84  * some data is represented as a difference between the actual value and
85  * either the last value of the same type (like for timing information) or
86  * as the difference from a value stored in a buffer header.
87  *
88  * For timing information the data is stored as uleb128, since timing
89  * increases in a monotonic way in each thread: the value is the number of
90  * nanoseconds to add to the last seen timing data in a buffer. The first value
91  * in a buffer will be calculated from the time_base field in the buffer head.
92  *
93  * Object or heap sizes are stored as uleb128.
94  * Pointer differences are stored as sleb128, instead.
95  *
96  * If an unexpected value is found, the rest of the buffer should be ignored,
97  * as generally the later values need the former to be interpreted correctly.
98  *
99  * buffer header format:
100  * [bufid: 4 bytes] constant value: BUF_ID
101  * [len: 4 bytes] size of the data following the buffer header
102  * [time_base: 8 bytes] time base in nanoseconds since an unspecified epoch
103  * [ptr_base: 8 bytes] base value for pointers
104  * [obj_base: 8 bytes] base value for object addresses
105  * [thread id: 8 bytes] system-specific thread ID (pthread_t for example)
106  * [method_base: 8 bytes] base value for MonoMethod pointers
107  *
108  * event format:
109  * [extended info: upper 4 bits] [type: lower 4 bits] [data]*
110  * The data that follows depends on type and the extended info.
111  * Type is one of the enum values in proflog.h: TYPE_ALLOC, TYPE_GC,
112  * TYPE_METADATA, TYPE_METHOD, TYPE_EXCEPTION, TYPE_MONITOR, TYPE_HEAP.
113  * The extended info bits are interpreted based on type, see
114  * each individual event description below.
115  * strings are represented as a 0-terminated utf8 sequence.
116  *
117  * backtrace format:
118  * [flags: uleb128] must be 0
119  * [num: uleb128] number of frames following
120  * [frame: sleb128]* num MonoMethod pointers as differences from ptr_base
121  *
122  * type alloc format:
123  * type: TYPE_ALLOC
124  * exinfo: flags: TYPE_ALLOC_BT
125  * [time diff: uleb128] nanoseconds since last timing
126  * [ptr: sleb128] class as a byte difference from ptr_base
127  * [obj: sleb128] object address as a byte difference from obj_base
128  * [size: uleb128] size of the object in the heap
129  * If the TYPE_ALLOC_BT flag is set, a backtrace follows.
130  *
131  * type GC format:
132  * type: TYPE_GC
133  * exinfo: one of TYPE_GC_EVENT, TYPE_GC_RESIZE, TYPE_GC_MOVE, TYPE_GC_HANDLE_CREATED,
134  * TYPE_GC_HANDLE_DESTROYED
135  * [time diff: uleb128] nanoseconds since last timing
136  * if exinfo == TYPE_GC_RESIZE
137  *      [heap_size: uleb128] new heap size
138  * if exinfo == TYPE_GC_EVENT
139  *      [event type: uleb128] GC event (MONO_GC_EVENT_* from profiler.h)
140  *      [generation: uleb128] GC generation event refers to
141  * if exinfo == TYPE_GC_MOVE
142  *      [num_objects: uleb128] number of object moves that follow
143  *      [objaddr: sleb128]+ num_objects object pointer differences from obj_base
144  *      num is always an even number: the even items are the old
145  *      addresses, the odd numbers are the respective new object addresses
146  * if exinfo == TYPE_GC_HANDLE_CREATED
147  *      [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
148  *      upper bits reserved as flags
149  *      [handle: uleb128] GC handle value
150  *      [objaddr: sleb128] object pointer differences from obj_base
151  * if exinfo == TYPE_GC_HANDLE_DESTROYED
152  *      [handle_type: uleb128] GC handle type (System.Runtime.InteropServices.GCHandleType)
153  *      upper bits reserved as flags
154  *      [handle: uleb128] GC handle value
155  *
156  * type metadata format:
157  * type: TYPE_METADATA
158  * exinfo: flags: TYPE_LOAD_ERR
159  * [time diff: uleb128] nanoseconds since last timing
160  * [mtype: byte] metadata type, one of: TYPE_CLASS, TYPE_IMAGE, TYPE_ASSEMBLY, TYPE_DOMAIN,
161  * TYPE_THREAD
162  * [pointer: sleb128] pointer of the metadata type depending on mtype
163  * if mtype == TYPE_CLASS
164  *      [image: sleb128] MonoImage* as a pointer difference from ptr_base
165  *      [flags: uleb128] must be 0
166  *      [name: string] full class name
167  * if mtype == TYPE_IMAGE
168  *      [flags: uleb128] must be 0
169  *      [name: string] image file name
170  * if mtype == TYPE_THREAD
171  *      [flags: uleb128] must be 0
172  *      [name: string] thread name
173  *
174  * type method format:
175  * type: TYPE_METHOD
176  * exinfo: one of: TYPE_LEAVE, TYPE_ENTER, TYPE_EXC_LEAVE, TYPE_JIT
177  * [time diff: uleb128] nanoseconds since last timing
178  * [method: sleb128] MonoMethod* as a pointer difference from the last such
179  * pointer or the buffer method_base
180  * if exinfo == TYPE_JIT
181  *      [code address: sleb128] pointer to the native code as a diff from ptr_base
182  *      [code size: uleb128] size of the generated code
183  *      [name: string] full method name
184  *
185  * type exception format:
186  * type: TYPE_EXCEPTION
187  * exinfo: TYPE_EXCEPTION_BT flag and one of: TYPE_THROW, TYPE_CLAUSE
188  * [time diff: uleb128] nanoseconds since last timing
189  * if exinfo.low3bits == TYPE_CLAUSE
190  *      [clause type: uleb128] finally/catch/fault/filter
191  *      [clause num: uleb128] the clause number in the method header
192  *      [method: sleb128] MonoMethod* as a pointer difference from the last such
193  *      pointer or the buffer method_base
194  * if exinfo.low3bits == TYPE_THROW
195  *      [object: sleb128] the object that was thrown as a difference from obj_base
196  *      If the TYPE_EXCEPTION_BT flag is set, a backtrace follows.
197  *
198  * type monitor format:
199  * type: TYPE_MONITOR
200  * exinfo: TYPE_MONITOR_BT flag and one of: MONO_PROFILER_MONITOR_(CONTENTION|FAIL|DONE)
201  * [time diff: uleb128] nanoseconds since last timing
202  * [object: sleb128] the lock object as a difference from obj_base
203  * if exinfo.low3bits == MONO_PROFILER_MONITOR_CONTENTION
204  *      If the TYPE_MONITOR_BT flag is set, a backtrace follows.
205  *
206  * type heap format
207  * type: TYPE_HEAP
208  * exinfo: one of TYPE_HEAP_START, TYPE_HEAP_END, TYPE_HEAP_OBJECT
209  * if exinfo == TYPE_HEAP_START
210  *      [time diff: uleb128] nanoseconds since last timing
211  * if exinfo == TYPE_HEAP_END
212  *      [time diff: uleb128] nanoseconds since last timing
213  * if exinfo == TYPE_HEAP_OBJECT
214  *      [object: sleb128] the object as a difference from obj_base
215  *      [class: sleb128] the object MonoClass* as a difference from ptr_base
216  *      [size: uleb128] size of the object on the heap
217  *      [num_refs: uleb128] number of object references
218  *      [objrefs: sleb128]+ object referenced as a difference from obj_base
219  *
220  */
221 struct _LogBuffer {
222         LogBuffer *next;
223         uint64_t time_base;
224         uint64_t last_time;
225         uintptr_t ptr_base;
226         uintptr_t method_base;
227         uintptr_t last_method;
228         uintptr_t obj_base;
229         uintptr_t thread_id;
230         unsigned char* data_end;
231         unsigned char* data;
232         int locked;
233         int size;
234         int call_depth;
235         unsigned char buf [1];
236 };
237
238 #define ENTER_LOG(lb,str) if ((lb)->locked) {write(2, str, strlen(str)); write(2, "\n", 1);return;} else {(lb)->locked++;}
239 #define EXIT_LOG(lb) (lb)->locked--;
240
241 struct _MonoProfiler {
242         LogBuffer *buffers;
243         FILE* file;
244 #if defined (HAVE_SYS_ZLIB)
245         gzFile *gzfile;
246 #endif
247         int pipe_output;
248         int last_gc_gen_started;
249 };
250
251 #ifdef HOST_WIN32
252 #define TLS_SET(x,y) TlsSetValue(x, y)
253 #define TLS_GET(x) ((LogBuffer *) TlsGetValue(x))
254 #define TLS_INIT(x) x = TlsAlloc ()
255 static int tlsbuffer;
256 #elif HAVE_KW_THREAD
257 #define TLS_SET(x,y) x = y
258 #define TLS_GET(x) x
259 #define TLS_INIT(x)
260 static __thread LogBuffer* tlsbuffer = NULL;
261 #else
262 #define TLS_SET(x,y) pthread_setspecific(x, y)
263 #define TLS_GET(x) ((LogBuffer *) pthread_getspecific(x))
264 #define TLS_INIT(x) pthread_key_create(&x, NULL)
265 static pthread_key_t tlsbuffer;
266 #endif
267
268 static char*
269 pstrdup (const char *s)
270 {
271         int len = strlen (s) + 1;
272         char *p = malloc (len);
273         memcpy (p, s, len);
274         return p;
275 }
276
277 static LogBuffer*
278 create_buffer (void)
279 {
280         LogBuffer* buf = alloc_buffer (BUFFER_SIZE);
281         buf->size = BUFFER_SIZE;
282         buf->time_base = current_time ();
283         buf->last_time = buf->time_base;
284         buf->data_end = (unsigned char*)buf + buf->size;
285         buf->data = buf->buf;
286         return buf;
287 }
288
289 static void
290 init_thread (void)
291 {
292         LogBuffer *logbuffer;
293         if (TLS_GET (tlsbuffer))
294                 return;
295         logbuffer = create_buffer ();
296         TLS_SET (tlsbuffer, logbuffer);
297         logbuffer->thread_id = thread_id ();
298         //printf ("thread %p at time %llu\n", (void*)logbuffer->thread_id, logbuffer->time_base);
299 }
300
301 static LogBuffer*
302 ensure_logbuf (int bytes)
303 {
304         LogBuffer *old = TLS_GET (tlsbuffer);
305         if (old && old->data + bytes + 100 < old->data_end)
306                 return old;
307         TLS_SET (tlsbuffer, NULL);
308         init_thread ();
309         TLS_GET (tlsbuffer)->next = old;
310         if (old)
311                 TLS_GET (tlsbuffer)->call_depth = old->call_depth;
312         //printf ("new logbuffer\n");
313         return TLS_GET (tlsbuffer);
314 }
315
316 static void
317 emit_byte (LogBuffer *logbuffer, int value)
318 {
319         logbuffer->data [0] = value;
320         logbuffer->data++;
321         assert (logbuffer->data <= logbuffer->data_end);
322 }
323
324 static void
325 emit_value (LogBuffer *logbuffer, int value)
326 {
327         encode_uleb128 (value, logbuffer->data, &logbuffer->data);
328         assert (logbuffer->data <= logbuffer->data_end);
329 }
330
331 static void
332 emit_time (LogBuffer *logbuffer, uint64_t value)
333 {
334         uint64_t tdiff = value - logbuffer->last_time;
335         unsigned char *p;
336         if (value < logbuffer->last_time)
337                 printf ("time went backwards\n");
338         //if (tdiff > 1000000)
339         //      printf ("large time offset: %llu\n", tdiff);
340         p = logbuffer->data;
341         encode_uleb128 (tdiff, logbuffer->data, &logbuffer->data);
342         /*if (tdiff != decode_uleb128 (p, &p))
343                 printf ("incorrect encoding: %llu\n", tdiff);*/
344         logbuffer->last_time = value;
345         assert (logbuffer->data <= logbuffer->data_end);
346 }
347
348 static void
349 emit_svalue (LogBuffer *logbuffer, int64_t value)
350 {
351         encode_sleb128 (value, logbuffer->data, &logbuffer->data);
352         assert (logbuffer->data <= logbuffer->data_end);
353 }
354
355 static void
356 emit_ptr (LogBuffer *logbuffer, void *ptr)
357 {
358         if (!logbuffer->ptr_base)
359                 logbuffer->ptr_base = (uintptr_t)ptr;
360         emit_svalue (logbuffer, (intptr_t)ptr - logbuffer->ptr_base);
361         assert (logbuffer->data <= logbuffer->data_end);
362 }
363
364 static void
365 emit_method (LogBuffer *logbuffer, void *method)
366 {
367         if (!logbuffer->method_base) {
368                 logbuffer->method_base = (intptr_t)method;
369                 logbuffer->last_method = (intptr_t)method;
370         }
371         encode_sleb128 ((intptr_t)((char*)method - (char*)logbuffer->last_method), logbuffer->data, &logbuffer->data);
372         logbuffer->last_method = (intptr_t)method;
373         assert (logbuffer->data <= logbuffer->data_end);
374 }
375
376 static void
377 emit_obj (LogBuffer *logbuffer, void *ptr)
378 {
379         if (!logbuffer->obj_base)
380                 logbuffer->obj_base = (uintptr_t)ptr >> 3;
381         emit_svalue (logbuffer, ((uintptr_t)ptr >> 3) - logbuffer->obj_base);
382         assert (logbuffer->data <= logbuffer->data_end);
383 }
384
385 static char*
386 write_int32 (char *buf, int32_t value)
387 {
388         int i;
389         for (i = 0; i < 4; ++i) {
390                 buf [i] = value;
391                 value >>= 8;
392         }
393         return buf + 4;
394 }
395
396 static char*
397 write_int64 (char *buf, int64_t value)
398 {
399         int i;
400         for (i = 0; i < 8; ++i) {
401                 buf [i] = value;
402                 value >>= 8;
403         }
404         return buf + 8;
405 }
406
407 static void
408 dump_header (MonoProfiler *profiler)
409 {
410         char hbuf [128];
411         char *p = hbuf;
412         p = write_int32 (p, LOG_HEADER_ID);
413         *p++ = LOG_VERSION_MAJOR;
414         *p++ = LOG_VERSION_MINOR;
415         *p++ = LOG_DATA_VERSION;
416         *p++ = sizeof (void*);
417         p = write_int64 (p, ((uint64_t)time (NULL)) * 1000); /* startup time */
418         p = write_int32 (p, get_timer_overhead ()); /* timer overhead */
419         p = write_int32 (p, 0); /* flags */
420         p = write_int32 (p, process_id ()); /* pid */
421         p = write_int32 (p, 0); /* opsystem */
422 #if defined (HAVE_SYS_ZLIB)
423         if (profiler->gzfile) {
424                 gzwrite (profiler->gzfile, hbuf, p - hbuf);
425         } else {
426                 fwrite (hbuf, p - hbuf, 1, profiler->file);
427         }
428 #else
429         fwrite (hbuf, p - hbuf, 1, profiler->file);
430 #endif
431 }
432
433 static void
434 dump_buffer (MonoProfiler *profiler, LogBuffer *buf)
435 {
436         char hbuf [128];
437         char *p = hbuf;
438         if (buf->next)
439                 dump_buffer (profiler, buf->next);
440         p = write_int32 (p, BUF_ID);
441         p = write_int32 (p, buf->data - buf->buf);
442         p = write_int64 (p, buf->time_base);
443         p = write_int64 (p, buf->ptr_base);
444         p = write_int64 (p, buf->obj_base);
445         p = write_int64 (p, buf->thread_id);
446         p = write_int64 (p, buf->method_base);
447 #if defined (HAVE_SYS_ZLIB)
448         if (profiler->gzfile) {
449                 gzwrite (profiler->gzfile, hbuf, p - hbuf);
450                 gzwrite (profiler->gzfile, buf->buf, buf->data - buf->buf);
451         } else {
452 #endif
453                 fwrite (hbuf, p - hbuf, 1, profiler->file);
454                 fwrite (buf->buf, buf->data - buf->buf, 1, profiler->file);
455 #if defined (HAVE_SYS_ZLIB)
456         }
457 #endif
458         free_buffer (buf, buf->size);
459 }
460
461 static void
462 runtime_initialized (MonoProfiler *profiler)
463 {
464         runtime_inited = 1;
465 }
466
467 /*
468  * Can be called only at safe callback locations.
469  */
470 static void
471 safe_dump (MonoProfiler *profiler, LogBuffer *logbuffer)
472 {
473         int cd = logbuffer->call_depth;
474         take_lock ();
475         dump_buffer (profiler, TLS_GET (tlsbuffer));
476         release_lock ();
477         TLS_SET (tlsbuffer, NULL);
478         init_thread ();
479         TLS_GET (tlsbuffer)->call_depth = cd;
480 }
481
482 static int
483 gc_reference (MonoObject *obj, MonoClass *klass, uintptr_t size, uintptr_t num, MonoObject **refs, void *data)
484 {
485         int i;
486         //const char *name = mono_class_get_name (klass);
487         LogBuffer *logbuffer = ensure_logbuf (20 + num * 8);
488         emit_byte (logbuffer, TYPE_HEAP_OBJECT | TYPE_HEAP);
489         emit_obj (logbuffer, obj);
490         emit_ptr (logbuffer, klass);
491         /* account for object alignment in the heap */
492         size += 7;
493         size &= ~7;
494         emit_value (logbuffer, size);
495         emit_value (logbuffer, num);
496         for (i = 0; i < num; ++i)
497                 emit_obj (logbuffer, refs [i]);
498         //if (num)
499         //      printf ("obj: %p, klass: %s, refs: %d, size: %d\n", obj, name, (int)num, (int)size);
500         return 0;
501 }
502
503 static unsigned int hs_mode_ms = 0;
504 static unsigned int hs_mode_gc = 0;
505 static unsigned int gc_count = 0;
506 static uint64_t last_hs_time = 0;
507
508 static void
509 heap_walk (MonoProfiler *profiler)
510 {
511         int do_walk = 0;
512         uint64_t now;
513         LogBuffer *logbuffer;
514         if (!do_heap_shot)
515                 return;
516         logbuffer = ensure_logbuf (10);
517         now = current_time ();
518         if (hs_mode_ms && (now - last_hs_time)/1000000 >= hs_mode_ms)
519                 do_walk = 1;
520         else if (hs_mode_gc && (gc_count % hs_mode_gc) == 0)
521                 do_walk = 1;
522         else if (!hs_mode_ms && !hs_mode_gc && profiler->last_gc_gen_started == mono_gc_max_generation ())
523                 do_walk = 1;
524
525         if (!do_walk)
526                 return;
527         emit_byte (logbuffer, TYPE_HEAP_START | TYPE_HEAP);
528         emit_time (logbuffer, now);
529         mono_gc_walk_heap (0, gc_reference, NULL);
530         logbuffer = ensure_logbuf (10);
531         now = current_time ();
532         emit_byte (logbuffer, TYPE_HEAP_END | TYPE_HEAP);
533         emit_time (logbuffer, now);
534         last_hs_time = now;
535 }
536
537 static void
538 gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
539         uint64_t now;
540         LogBuffer *logbuffer = ensure_logbuf (10);
541         now = current_time ();
542         ENTER_LOG (logbuffer, "gcevent");
543         emit_byte (logbuffer, TYPE_GC_EVENT | TYPE_GC);
544         emit_time (logbuffer, now);
545         emit_value (logbuffer, ev);
546         emit_value (logbuffer, generation);
547         /* to deal with nested gen1 after gen0 started */
548         if (ev == MONO_GC_EVENT_START) {
549                 profiler->last_gc_gen_started = generation;
550                 gc_count++;
551         }
552         if (ev == MONO_GC_EVENT_PRE_START_WORLD)
553                 heap_walk (profiler);
554         EXIT_LOG (logbuffer);
555         if (ev == MONO_GC_EVENT_POST_START_WORLD)
556                 safe_dump (profiler, logbuffer);
557         //printf ("gc event %d for generation %d\n", ev, generation);
558 }
559
560 static void
561 gc_resize (MonoProfiler *profiler, int64_t new_size) {
562         uint64_t now;
563         LogBuffer *logbuffer = ensure_logbuf (10);
564         now = current_time ();
565         ENTER_LOG (logbuffer, "gcresize");
566         emit_byte (logbuffer, TYPE_GC_RESIZE | TYPE_GC);
567         emit_time (logbuffer, now);
568         emit_value (logbuffer, new_size);
569         //printf ("gc resized to %lld\n", new_size);
570         EXIT_LOG (logbuffer);
571 }
572
573 #define MAX_FRAMES 16
574 typedef struct {
575         int count;
576         MonoMethod* methods [MAX_FRAMES];
577 } FrameData;
578 static int num_frames = MAX_FRAMES / 2;
579
580 static mono_bool
581 walk_stack (MonoMethod *method, int32_t native_offset, int32_t il_offset, mono_bool managed, void* data)
582 {
583         FrameData *frame = data;
584         if (method && frame->count < num_frames) {
585                 frame->methods [frame->count++] = method;
586                 //printf ("In %d %s\n", frame->count, mono_method_get_name (method));
587         }
588         return frame->count == num_frames;
589 }
590
591 /*
592  * a note about stack walks: they can cause more profiler events to fire,
593  * so we need to make sure they don't happen after we started emitting an
594  * event, hence the collect_bt/emit_bt split.
595  */
596 static void
597 collect_bt (FrameData *data)
598 {
599         data->count = 0;
600         mono_stack_walk_no_il (walk_stack, data);
601 }
602
603 static void
604 emit_bt (LogBuffer *logbuffer, FrameData *data)
605 {
606         /* FIXME: this is actually tons of data and we should
607          * just output it the first time and use an id the next
608          */
609         if (data->count > num_frames)
610                 printf ("bad num frames: %d\n", data->count);
611         emit_value (logbuffer, 0); /* flags */
612         emit_value (logbuffer, data->count);
613         //if (*p != data.count) {
614         //      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);}
615         while (data->count) {
616                 emit_ptr (logbuffer, data->methods [--data->count]);
617         }
618 }
619
620 static void
621 gc_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
622 {
623         uint64_t now;
624         uintptr_t len;
625         int do_bt = (nocalls && runtime_inited && !notraces)? TYPE_ALLOC_BT: 0;
626         FrameData data;
627         LogBuffer *logbuffer;
628         len = mono_object_get_size (obj);
629         /* account for object alignment in the heap */
630         len += 7;
631         len &= ~7;
632         if (do_bt)
633                 collect_bt (&data);
634         logbuffer = ensure_logbuf (32 + MAX_FRAMES * 8);
635         now = current_time ();
636         ENTER_LOG (logbuffer, "gcalloc");
637         emit_byte (logbuffer, do_bt | TYPE_ALLOC);
638         emit_time (logbuffer, now);
639         emit_ptr (logbuffer, klass);
640         emit_obj (logbuffer, obj);
641         emit_value (logbuffer, len);
642         if (do_bt)
643                 emit_bt (logbuffer, &data);
644         EXIT_LOG (logbuffer);
645         if (logbuffer->next)
646                 safe_dump (prof, logbuffer);
647         //printf ("gc alloc %s at %p\n", mono_class_get_name (klass), obj);
648 }
649
650 static void
651 gc_moves (MonoProfiler *prof, void **objects, int num)
652 {
653         int i;
654         uint64_t now;
655         LogBuffer *logbuffer = ensure_logbuf (10 + num * 8);
656         now = current_time ();
657         ENTER_LOG (logbuffer, "gcmove");
658         emit_byte (logbuffer, TYPE_GC_MOVE | TYPE_GC);
659         emit_time (logbuffer, now);
660         emit_value (logbuffer, num);
661         for (i = 0; i < num; ++i)
662                 emit_obj (logbuffer, objects [i]);
663         //printf ("gc moved %d objects\n", num/2);
664         EXIT_LOG (logbuffer);
665 }
666
667 static void
668 gc_handle (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj)
669 {
670         uint64_t now;
671         LogBuffer *logbuffer = ensure_logbuf (16);
672         now = current_time ();
673         ENTER_LOG (logbuffer, "gchandle");
674         if (op == MONO_PROFILER_GC_HANDLE_CREATED)
675                 emit_byte (logbuffer, TYPE_GC_HANDLE_CREATED | TYPE_GC);
676         else if (op == MONO_PROFILER_GC_HANDLE_DESTROYED)
677                 emit_byte (logbuffer, TYPE_GC_HANDLE_DESTROYED | TYPE_GC);
678         else
679                 return;
680         emit_time (logbuffer, now);
681         emit_value (logbuffer, type);
682         emit_value (logbuffer, handle);
683         if (op == MONO_PROFILER_GC_HANDLE_CREATED)
684                 emit_obj (logbuffer, obj);
685         EXIT_LOG (logbuffer);
686 }
687
688 static char*
689 push_nesting (char *p, MonoClass *klass)
690 {
691         MonoClass *nesting;
692         const char *name;
693         const char *nspace;
694         nesting = mono_class_get_nesting_type (klass);
695         if (nesting) {
696                 p = push_nesting (p, nesting);
697                 *p++ = '/';
698                 *p = 0;
699         }
700         name = mono_class_get_name (klass);
701         nspace = mono_class_get_namespace (klass);
702         if (*nspace) {
703                 strcpy (p, nspace);
704                 p += strlen (nspace);
705                 *p++ = '.';
706                 *p = 0;
707         }
708         strcpy (p, name);
709         p += strlen (name);
710         return p;
711 }
712
713 static char*
714 type_name (MonoClass *klass)
715 {
716         char buf [1024];
717         char *p;
718         push_nesting (buf, klass);
719         p = malloc (strlen (buf) + 1);
720         strcpy (p, buf);
721         return p;
722 }
723
724 static void
725 image_loaded (MonoProfiler *prof, MonoImage *image, int result)
726 {
727         uint64_t now;
728         const char *name;
729         int nlen;
730         LogBuffer *logbuffer;
731         if (result != MONO_PROFILE_OK)
732                 return;
733         name = mono_image_get_filename (image);
734         nlen = strlen (name) + 1;
735         logbuffer = ensure_logbuf (16 + nlen);
736         now = current_time ();
737         ENTER_LOG (logbuffer, "image");
738         emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
739         emit_time (logbuffer, now);
740         emit_byte (logbuffer, TYPE_IMAGE);
741         emit_ptr (logbuffer, image);
742         emit_value (logbuffer, 0); /* flags */
743         memcpy (logbuffer->data, name, nlen);
744         logbuffer->data += nlen;
745         //printf ("loaded image %p (%s)\n", image, name);
746         EXIT_LOG (logbuffer);
747         if (logbuffer->next)
748                 safe_dump (prof, logbuffer);
749 }
750
751 static void
752 class_loaded (MonoProfiler *prof, MonoClass *klass, int result)
753 {
754         uint64_t now;
755         char *name;
756         int nlen;
757         MonoImage *image;
758         LogBuffer *logbuffer;
759         if (result != MONO_PROFILE_OK)
760                 return;
761         if (runtime_inited)
762                 name = mono_type_get_name (mono_class_get_type (klass));
763         else
764                 name = type_name (klass);
765         nlen = strlen (name) + 1;
766         image = mono_class_get_image (klass);
767         logbuffer = ensure_logbuf (24 + nlen);
768         now = current_time ();
769         ENTER_LOG (logbuffer, "class");
770         emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
771         emit_time (logbuffer, now);
772         emit_byte (logbuffer, TYPE_CLASS);
773         emit_ptr (logbuffer, klass);
774         emit_ptr (logbuffer, image);
775         emit_value (logbuffer, 0); /* flags */
776         memcpy (logbuffer->data, name, nlen);
777         logbuffer->data += nlen;
778         //printf ("loaded class %p (%s)\n", klass, name);
779         if (runtime_inited)
780                 mono_free (name);
781         else
782                 free (name);
783         EXIT_LOG (logbuffer);
784         if (logbuffer->next)
785                 safe_dump (prof, logbuffer);
786 }
787
788 static void
789 method_enter (MonoProfiler *prof, MonoMethod *method)
790 {
791         uint64_t now;
792         LogBuffer *logbuffer = ensure_logbuf (16);
793         if (logbuffer->call_depth++ > max_call_depth)
794                 return;
795         now = current_time ();
796         ENTER_LOG (logbuffer, "enter");
797         emit_byte (logbuffer, TYPE_ENTER | TYPE_METHOD);
798         emit_time (logbuffer, now);
799         emit_method (logbuffer, method);
800         EXIT_LOG (logbuffer);
801 }
802
803 static void
804 method_leave (MonoProfiler *prof, MonoMethod *method)
805 {
806         uint64_t now;
807         LogBuffer *logbuffer = ensure_logbuf (16);
808         if (--logbuffer->call_depth > max_call_depth)
809                 return;
810         now = current_time ();
811         ENTER_LOG (logbuffer, "leave");
812         emit_byte (logbuffer, TYPE_LEAVE | TYPE_METHOD);
813         emit_time (logbuffer, now);
814         emit_method (logbuffer, method);
815         EXIT_LOG (logbuffer);
816         if (logbuffer->next)
817                 safe_dump (prof, logbuffer);
818 }
819
820 static void
821 method_exc_leave (MonoProfiler *prof, MonoMethod *method)
822 {
823         uint64_t now;
824         LogBuffer *logbuffer;
825         if (nocalls)
826                 return;
827         logbuffer = ensure_logbuf (16);
828         if (--logbuffer->call_depth > max_call_depth)
829                 return;
830         now = current_time ();
831         ENTER_LOG (logbuffer, "eleave");
832         emit_byte (logbuffer, TYPE_EXC_LEAVE | TYPE_METHOD);
833         emit_time (logbuffer, now);
834         emit_method (logbuffer, method);
835         EXIT_LOG (logbuffer);
836 }
837
838 static void
839 method_jitted (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result)
840 {
841         uint64_t now;
842         char *name;
843         int nlen;
844         LogBuffer *logbuffer;
845         if (result != MONO_PROFILE_OK)
846                 return;
847         name = mono_method_full_name (method, 1);
848         nlen = strlen (name) + 1;
849         logbuffer = ensure_logbuf (32 + nlen);
850         now = current_time ();
851         ENTER_LOG (logbuffer, "jit");
852         emit_byte (logbuffer, TYPE_JIT | TYPE_METHOD);
853         emit_time (logbuffer, now);
854         emit_method (logbuffer, method);
855         emit_ptr (logbuffer, mono_jit_info_get_code_start (jinfo));
856         emit_value (logbuffer, mono_jit_info_get_code_size (jinfo));
857         memcpy (logbuffer->data, name, nlen);
858         logbuffer->data += nlen;
859         mono_free (name);
860         EXIT_LOG (logbuffer);
861         if (logbuffer->next)
862                 safe_dump (prof, logbuffer);
863 }
864
865 static void
866 throw_exc (MonoProfiler *prof, MonoObject *object)
867 {
868         int do_bt = (nocalls && runtime_inited && !notraces)? TYPE_EXCEPTION_BT: 0;
869         uint64_t now;
870         FrameData data;
871         LogBuffer *logbuffer;
872         if (do_bt)
873                 collect_bt (&data);
874         logbuffer = ensure_logbuf (16 + MAX_FRAMES * 8);
875         now = current_time ();
876         ENTER_LOG (logbuffer, "throw");
877         emit_byte (logbuffer, do_bt | TYPE_EXCEPTION);
878         emit_time (logbuffer, now);
879         emit_obj (logbuffer, object);
880         if (do_bt)
881                 emit_bt (logbuffer, &data);
882         EXIT_LOG (logbuffer);
883 }
884
885 static void
886 clause_exc (MonoProfiler *prof, MonoMethod *method, int clause_type, int clause_num)
887 {
888         uint64_t now;
889         LogBuffer *logbuffer = ensure_logbuf (16);
890         now = current_time ();
891         ENTER_LOG (logbuffer, "clause");
892         emit_byte (logbuffer, TYPE_EXCEPTION | TYPE_CLAUSE);
893         emit_time (logbuffer, now);
894         emit_value (logbuffer, clause_type);
895         emit_value (logbuffer, clause_num);
896         emit_method (logbuffer, method);
897         EXIT_LOG (logbuffer);
898 }
899
900 static void
901 monitor_event (MonoProfiler *profiler, MonoObject *object, MonoProfilerMonitorEvent event)
902 {
903         int do_bt = (nocalls && runtime_inited && !notraces && event == MONO_PROFILER_MONITOR_CONTENTION)? TYPE_MONITOR_BT: 0;
904         uint64_t now;
905         FrameData data;
906         LogBuffer *logbuffer;
907         if (do_bt)
908                 collect_bt (&data);
909         logbuffer = ensure_logbuf (16 + MAX_FRAMES * 8);
910         now = current_time ();
911         ENTER_LOG (logbuffer, "monitor");
912         emit_byte (logbuffer, (event << 4) | do_bt | TYPE_MONITOR);
913         emit_time (logbuffer, now);
914         emit_obj (logbuffer, object);
915         if (do_bt)
916                 emit_bt (logbuffer, &data);
917         EXIT_LOG (logbuffer);
918 }
919
920 static void
921 thread_start (MonoProfiler *prof, uintptr_t tid)
922 {
923         //printf ("thread start %p\n", (void*)tid);
924         init_thread ();
925 }
926
927 static void
928 thread_end (MonoProfiler *prof, uintptr_t tid)
929 {
930         take_lock ();
931         if (TLS_GET (tlsbuffer))
932                 dump_buffer (prof, TLS_GET (tlsbuffer));
933         release_lock ();
934         TLS_SET (tlsbuffer, NULL);
935 }
936
937 static void
938 thread_name (MonoProfiler *prof, uintptr_t tid, const char *name)
939 {
940         int len = strlen (name) + 1;
941         uint64_t now;
942         LogBuffer *logbuffer;
943         logbuffer = ensure_logbuf (10 + len);
944         now = current_time ();
945         ENTER_LOG (logbuffer, "tname");
946         emit_byte (logbuffer, TYPE_METADATA);
947         emit_time (logbuffer, now);
948         emit_byte (logbuffer, TYPE_THREAD);
949         emit_ptr (logbuffer, (void*)tid);
950         emit_value (logbuffer, 0); /* flags */
951         memcpy (logbuffer->data, name, len);
952         logbuffer->data += len;
953         EXIT_LOG (logbuffer);
954 }
955
956 static void
957 log_shutdown (MonoProfiler *prof)
958 {
959         take_lock ();
960         if (TLS_GET (tlsbuffer))
961                 dump_buffer (prof, TLS_GET (tlsbuffer));
962         TLS_SET (tlsbuffer, NULL);
963         release_lock ();
964 #if defined (HAVE_SYS_ZLIB)
965         if (prof->gzfile)
966                 gzclose (prof->gzfile);
967 #endif
968         if (prof->pipe_output)
969                 pclose (prof->file);
970         else
971                 fclose (prof->file);
972         free (prof);
973 }
974
975 static char*
976 new_filename (const char* filename)
977 {
978         time_t t = time (NULL);
979         int pid = process_id ();
980         char pid_buf [16];
981         char time_buf [16];
982         char *res, *d;
983         const char *p;
984         int count_dates = 0;
985         int count_pids = 0;
986         int s_date, s_pid;
987         struct tm *ts;
988         for (p = filename; *p; p++) {
989                 if (*p != '%')
990                         continue;
991                 p++;
992                 if (*p == 't')
993                         count_dates++;
994                 else if (*p == 'p')
995                         count_pids++;
996                 else if (*p == 0)
997                         break;
998         }
999         if (!count_dates && !count_pids)
1000                 return pstrdup (filename);
1001         snprintf (pid_buf, sizeof (pid_buf), "%d", pid);
1002         ts = gmtime (&t);
1003         snprintf (time_buf, sizeof (time_buf), "%d%02d%02d%02d%02d%02d",
1004                 1900 + ts->tm_year, 1 + ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec);
1005         s_date = strlen (time_buf);
1006         s_pid = strlen (pid_buf);
1007         d = res = malloc (strlen (filename) + s_date * count_dates + s_pid * count_pids);
1008         for (p = filename; *p; p++) {
1009                 if (*p != '%') {
1010                         *d++ = *p;
1011                         continue;
1012                 }
1013                 p++;
1014                 if (*p == 't') {
1015                         strcpy (d, time_buf);
1016                         d += s_date;
1017                         continue;
1018                 } else if (*p == 'p') {
1019                         strcpy (d, pid_buf);
1020                         d += s_pid;
1021                         continue;
1022                 } else if (*p == '%') {
1023                         *d++ = '%';
1024                         continue;
1025                 } else if (*p == 0)
1026                         break;
1027                 *d++ = '%';
1028                 *d++ = *p;
1029         }
1030         *d = 0;
1031         return res;
1032 }
1033
1034 static MonoProfiler*
1035 create_profiler (const char *filename)
1036 {
1037         MonoProfiler *prof;
1038         char *nf;
1039         int force_delete = 0;
1040         prof = calloc (1, sizeof (MonoProfiler));
1041         if (do_report) /* FIXME: use filename as output */
1042                 filename = "|mprof-report -";
1043
1044         if (!filename)
1045                 filename = "output.mlpd";
1046         if (*filename == '-') {
1047                 force_delete = 1;
1048                 filename++;
1049         }
1050         nf = new_filename (filename);
1051         if (*nf == '|') {
1052                 prof->file = popen (nf + 1, "w");
1053                 prof->pipe_output = 1;
1054         } else {
1055                 FILE *f;
1056                 if (force_delete)
1057                         unlink (nf);
1058                 if ((f = fopen (nf, "r"))) {
1059                         fclose (f);
1060                         fprintf (stderr, "The Mono profiler won't overwrite existing filename: %s.\n", nf);
1061                         fprintf (stderr, "Profiling disabled: use a different name or -FILENAME to force overwrite.\n");
1062                         free (prof);
1063                         return NULL;
1064                 }
1065                 prof->file = fopen (nf, "wb");
1066         }
1067         if (!prof->file) {
1068                 printf ("Cannot create profiler output: %s\n", nf);
1069                 exit (1);
1070         }
1071 #if defined (HAVE_SYS_ZLIB)
1072         if (use_zip)
1073                 prof->gzfile = gzdopen (fileno (prof->file), "wb");
1074 #endif
1075         dump_header (prof);
1076         return prof;
1077 }
1078
1079 static void
1080 usage (int do_exit)
1081 {
1082         printf ("Log profiler version %d.%d (format: %d)\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
1083         printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
1084         printf ("Options:\n");
1085         printf ("\thelp             show this usage info\n");
1086         printf ("\t[no]alloc        enable/disable recording allocation info\n");
1087         printf ("\t[no]calls        enable/disable recording enter/leave method events\n");
1088         printf ("\theapshot         record heap shot info (by default at each major collection)\n");
1089         printf ("\thsmode=MODE      heapshot mode: every XXms milliseconds or every YYgc collections\n");
1090         printf ("\ttime=fast        use a faster (but more inaccurate) timer\n");
1091         printf ("\tmaxframes=NUM    collect up to NUM stack frames\n");
1092         printf ("\tcalldepth=NUM    ignore method events for call chain depth bigger than NUM\n");
1093         printf ("\toutput=FILENAME  write the data to file FILENAME (-FILENAME to overwrite)\n");
1094         printf ("\toutput=|PROGRAM  write the data to the stdin of PROGRAM\n");
1095         printf ("\t                 %%t is subtituted with date and time, %%p with the pid\n");
1096         printf ("\treport           create a report instead of writing the raw data to a file\n");
1097         printf ("\tzip              compress the output data\n");
1098         if (do_exit)
1099                 exit (1);
1100 }
1101
1102 static const char*
1103 match_option (const char* p, const char *opt, char **rval)
1104 {
1105         int len = strlen (opt);
1106         if (strncmp (p, opt, len) == 0) {
1107                 if (rval) {
1108                         if (p [len] == '=' && p [len + 1]) {
1109                                 const char *opt = p + len + 1;
1110                                 const char *end = strchr (opt, ',');
1111                                 char *val;
1112                                 int l;
1113                                 if (end == NULL) {
1114                                         l = strlen (opt);
1115                                 } else {
1116                                         l = end - opt;
1117                                 }
1118                                 val = malloc (l + 1);
1119                                 memcpy (val, opt, l);
1120                                 val [l] = 0;
1121                                 *rval = val;
1122                                 return opt + l;
1123                         }
1124                         usage (1);
1125                 } else {
1126                         if (p [len] == 0)
1127                                 return p + len;
1128                         if (p [len] == ',')
1129                                 return p + len + 1;
1130                 }
1131         }
1132         return p;
1133 }
1134
1135 /* 
1136  * declaration to silence the compiler: this is the entry point that
1137  * mono will load from the shared library and call.
1138  */
1139 extern void
1140 mono_profiler_startup (const char *desc);
1141
1142 void
1143 mono_profiler_startup (const char *desc)
1144 {
1145         MonoProfiler *prof;
1146         char *filename = NULL;
1147         const char *p;
1148         const char *opt;
1149         int fast_time = 0;
1150         int calls_enabled = 0;
1151         int allocs_enabled = 0;
1152         int events = MONO_PROFILE_GC|MONO_PROFILE_ALLOCATIONS|
1153                 MONO_PROFILE_GC_MOVES|MONO_PROFILE_CLASS_EVENTS|MONO_PROFILE_THREADS|
1154                 MONO_PROFILE_ENTER_LEAVE|MONO_PROFILE_JIT_COMPILATION|MONO_PROFILE_EXCEPTIONS|
1155                 MONO_PROFILE_MONITOR_EVENTS|MONO_PROFILE_MODULE_EVENTS|MONO_PROFILE_GC_ROOTS;
1156
1157         p = desc;
1158         if (strncmp (p, "log", 3))
1159                 usage (1);
1160         p += 3;
1161         if (*p == ':')
1162                 p++;
1163         for (; *p; p = opt) {
1164                 char *val;
1165                 if (*p == ',') {
1166                         opt = p + 1;
1167                         continue;
1168                 }
1169                 if ((opt = match_option (p, "help", NULL)) != p) {
1170                         usage (0);
1171                         continue;
1172                 }
1173                 if ((opt = match_option (p, "calls", NULL)) != p) {
1174                         calls_enabled = 1;
1175                         continue;
1176                 }
1177                 if ((opt = match_option (p, "nocalls", NULL)) != p) {
1178                         events &= ~MONO_PROFILE_ENTER_LEAVE;
1179                         nocalls = 1;
1180                         continue;
1181                 }
1182                 if ((opt = match_option (p, "alloc", NULL)) != p) {
1183                         allocs_enabled = 1;
1184                         continue;
1185                 }
1186                 if ((opt = match_option (p, "noalloc", NULL)) != p) {
1187                         events &= ~MONO_PROFILE_ALLOCATIONS;
1188                         continue;
1189                 }
1190                 if ((opt = match_option (p, "time", &val)) != p) {
1191                         if (strcmp (val, "fast") == 0)
1192                                 fast_time = 1;
1193                         else if (strcmp (val, "null") == 0)
1194                                 fast_time = 2;
1195                         else
1196                                 usage (1);
1197                         free (val);
1198                         continue;
1199                 }
1200                 if ((opt = match_option (p, "report", NULL)) != p) {
1201                         do_report = 1;
1202                         continue;
1203                 }
1204                 if ((opt = match_option (p, "heapshot", NULL)) != p) {
1205                         events &= ~MONO_PROFILE_ALLOCATIONS;
1206                         events &= ~MONO_PROFILE_ENTER_LEAVE;
1207                         nocalls = 1;
1208                         do_heap_shot = 1;
1209                         continue;
1210                 }
1211                 if ((opt = match_option (p, "hsmode", &val)) != p) {
1212                         char *end;
1213                         unsigned int count = strtoul (val, &end, 10);
1214                         if (val == end)
1215                                 usage (1);
1216                         if (strcmp (end, "ms") == 0)
1217                                 hs_mode_ms = count;
1218                         else if (strcmp (end, "gc") == 0)
1219                                 hs_mode_gc = count;
1220                         else
1221                                 usage (1);
1222                         free (val);
1223                         continue;
1224                 }
1225                 if ((opt = match_option (p, "zip", NULL)) != p) {
1226                         use_zip = 1;
1227                         continue;
1228                 }
1229                 if ((opt = match_option (p, "output", &val)) != p) {
1230                         filename = val;
1231                         continue;
1232                 }
1233                 if ((opt = match_option (p, "maxframes", &val)) != p) {
1234                         char *end;
1235                         num_frames = strtoul (val, &end, 10);
1236                         if (num_frames > MAX_FRAMES)
1237                                 num_frames = MAX_FRAMES;
1238                         free (val);
1239                         notraces = num_frames == 0;
1240                         continue;
1241                 }
1242                 if ((opt = match_option (p, "calldepth", &val)) != p) {
1243                         char *end;
1244                         max_call_depth = strtoul (val, &end, 10);
1245                         free (val);
1246                         continue;
1247                 }
1248                 if (opt == p) {
1249                         usage (0);
1250                         exit (0);
1251                 }
1252         }
1253         if (calls_enabled) {
1254                 events |= MONO_PROFILE_ENTER_LEAVE;
1255                 nocalls = 0;
1256         }
1257         if (allocs_enabled)
1258                 events |= MONO_PROFILE_ALLOCATIONS;
1259         utils_init (fast_time);
1260
1261         prof = create_profiler (filename);
1262         if (!prof)
1263                 return;
1264         init_thread ();
1265
1266         mono_profiler_install (prof, log_shutdown);
1267         mono_profiler_install_gc (gc_event, gc_resize);
1268         mono_profiler_install_allocation (gc_alloc);
1269         mono_profiler_install_gc_moves (gc_moves);
1270         mono_profiler_install_gc_roots (gc_handle);
1271         mono_profiler_install_class (NULL, class_loaded, NULL, NULL);
1272         mono_profiler_install_module (NULL, image_loaded, NULL, NULL);
1273         mono_profiler_install_thread (thread_start, thread_end);
1274         mono_profiler_install_thread_name (thread_name);
1275         mono_profiler_install_enter_leave (method_enter, method_leave);
1276         mono_profiler_install_jit_end (method_jitted);
1277         mono_profiler_install_exception (throw_exc, method_exc_leave, clause_exc);
1278         mono_profiler_install_monitor (monitor_event);
1279         mono_profiler_install_runtime_initialized (runtime_initialized);
1280
1281         mono_profiler_set_events (events);
1282
1283         TLS_INIT (tlsbuffer);
1284 }
1285