Log profiler: removed the hsmode option, use heapshot=MODE instead.
[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  *      if (format version > 1) each referenced objref is preceded by a
219  *      uleb128 encoded offset: the first offset is from the object address
220  *      and each next offset is relative to the previous one
221  *      [objrefs: sleb128]+ object referenced as a difference from obj_base
222  *
223  */
224 struct _LogBuffer {
225         LogBuffer *next;
226         uint64_t time_base;
227         uint64_t last_time;
228         uintptr_t ptr_base;
229         uintptr_t method_base;
230         uintptr_t last_method;
231         uintptr_t obj_base;
232         uintptr_t thread_id;
233         unsigned char* data_end;
234         unsigned char* data;
235         int locked;
236         int size;
237         int call_depth;
238         unsigned char buf [1];
239 };
240
241 #define ENTER_LOG(lb,str) if ((lb)->locked) {write(2, str, strlen(str)); write(2, "\n", 1);return;} else {(lb)->locked++;}
242 #define EXIT_LOG(lb) (lb)->locked--;
243
244 struct _MonoProfiler {
245         LogBuffer *buffers;
246         FILE* file;
247 #if defined (HAVE_SYS_ZLIB)
248         gzFile *gzfile;
249 #endif
250         int pipe_output;
251         int last_gc_gen_started;
252 };
253
254 #ifdef HOST_WIN32
255 #define TLS_SET(x,y) TlsSetValue(x, y)
256 #define TLS_GET(x) ((LogBuffer *) TlsGetValue(x))
257 #define TLS_INIT(x) x = TlsAlloc ()
258 static int tlsbuffer;
259 #elif HAVE_KW_THREAD
260 #define TLS_SET(x,y) x = y
261 #define TLS_GET(x) x
262 #define TLS_INIT(x)
263 static __thread LogBuffer* tlsbuffer = NULL;
264 #else
265 #define TLS_SET(x,y) pthread_setspecific(x, y)
266 #define TLS_GET(x) ((LogBuffer *) pthread_getspecific(x))
267 #define TLS_INIT(x) pthread_key_create(&x, NULL)
268 static pthread_key_t tlsbuffer;
269 #endif
270
271 static char*
272 pstrdup (const char *s)
273 {
274         int len = strlen (s) + 1;
275         char *p = malloc (len);
276         memcpy (p, s, len);
277         return p;
278 }
279
280 static LogBuffer*
281 create_buffer (void)
282 {
283         LogBuffer* buf = alloc_buffer (BUFFER_SIZE);
284         buf->size = BUFFER_SIZE;
285         buf->time_base = current_time ();
286         buf->last_time = buf->time_base;
287         buf->data_end = (unsigned char*)buf + buf->size;
288         buf->data = buf->buf;
289         return buf;
290 }
291
292 static void
293 init_thread (void)
294 {
295         LogBuffer *logbuffer;
296         if (TLS_GET (tlsbuffer))
297                 return;
298         logbuffer = create_buffer ();
299         TLS_SET (tlsbuffer, logbuffer);
300         logbuffer->thread_id = thread_id ();
301         //printf ("thread %p at time %llu\n", (void*)logbuffer->thread_id, logbuffer->time_base);
302 }
303
304 static LogBuffer*
305 ensure_logbuf (int bytes)
306 {
307         LogBuffer *old = TLS_GET (tlsbuffer);
308         if (old && old->data + bytes + 100 < old->data_end)
309                 return old;
310         TLS_SET (tlsbuffer, NULL);
311         init_thread ();
312         TLS_GET (tlsbuffer)->next = old;
313         if (old)
314                 TLS_GET (tlsbuffer)->call_depth = old->call_depth;
315         //printf ("new logbuffer\n");
316         return TLS_GET (tlsbuffer);
317 }
318
319 static void
320 emit_byte (LogBuffer *logbuffer, int value)
321 {
322         logbuffer->data [0] = value;
323         logbuffer->data++;
324         assert (logbuffer->data <= logbuffer->data_end);
325 }
326
327 static void
328 emit_value (LogBuffer *logbuffer, int value)
329 {
330         encode_uleb128 (value, logbuffer->data, &logbuffer->data);
331         assert (logbuffer->data <= logbuffer->data_end);
332 }
333
334 static void
335 emit_time (LogBuffer *logbuffer, uint64_t value)
336 {
337         uint64_t tdiff = value - logbuffer->last_time;
338         unsigned char *p;
339         if (value < logbuffer->last_time)
340                 printf ("time went backwards\n");
341         //if (tdiff > 1000000)
342         //      printf ("large time offset: %llu\n", tdiff);
343         p = logbuffer->data;
344         encode_uleb128 (tdiff, logbuffer->data, &logbuffer->data);
345         /*if (tdiff != decode_uleb128 (p, &p))
346                 printf ("incorrect encoding: %llu\n", tdiff);*/
347         logbuffer->last_time = value;
348         assert (logbuffer->data <= logbuffer->data_end);
349 }
350
351 static void
352 emit_svalue (LogBuffer *logbuffer, int64_t value)
353 {
354         encode_sleb128 (value, logbuffer->data, &logbuffer->data);
355         assert (logbuffer->data <= logbuffer->data_end);
356 }
357
358 static void
359 emit_ptr (LogBuffer *logbuffer, void *ptr)
360 {
361         if (!logbuffer->ptr_base)
362                 logbuffer->ptr_base = (uintptr_t)ptr;
363         emit_svalue (logbuffer, (intptr_t)ptr - logbuffer->ptr_base);
364         assert (logbuffer->data <= logbuffer->data_end);
365 }
366
367 static void
368 emit_method (LogBuffer *logbuffer, void *method)
369 {
370         if (!logbuffer->method_base) {
371                 logbuffer->method_base = (intptr_t)method;
372                 logbuffer->last_method = (intptr_t)method;
373         }
374         encode_sleb128 ((intptr_t)((char*)method - (char*)logbuffer->last_method), logbuffer->data, &logbuffer->data);
375         logbuffer->last_method = (intptr_t)method;
376         assert (logbuffer->data <= logbuffer->data_end);
377 }
378
379 static void
380 emit_obj (LogBuffer *logbuffer, void *ptr)
381 {
382         if (!logbuffer->obj_base)
383                 logbuffer->obj_base = (uintptr_t)ptr >> 3;
384         emit_svalue (logbuffer, ((uintptr_t)ptr >> 3) - logbuffer->obj_base);
385         assert (logbuffer->data <= logbuffer->data_end);
386 }
387
388 static char*
389 write_int32 (char *buf, int32_t value)
390 {
391         int i;
392         for (i = 0; i < 4; ++i) {
393                 buf [i] = value;
394                 value >>= 8;
395         }
396         return buf + 4;
397 }
398
399 static char*
400 write_int64 (char *buf, int64_t value)
401 {
402         int i;
403         for (i = 0; i < 8; ++i) {
404                 buf [i] = value;
405                 value >>= 8;
406         }
407         return buf + 8;
408 }
409
410 static void
411 dump_header (MonoProfiler *profiler)
412 {
413         char hbuf [128];
414         char *p = hbuf;
415         p = write_int32 (p, LOG_HEADER_ID);
416         *p++ = LOG_VERSION_MAJOR;
417         *p++ = LOG_VERSION_MINOR;
418         *p++ = LOG_DATA_VERSION;
419         *p++ = sizeof (void*);
420         p = write_int64 (p, ((uint64_t)time (NULL)) * 1000); /* startup time */
421         p = write_int32 (p, get_timer_overhead ()); /* timer overhead */
422         p = write_int32 (p, 0); /* flags */
423         p = write_int32 (p, process_id ()); /* pid */
424         p = write_int32 (p, 0); /* opsystem */
425 #if defined (HAVE_SYS_ZLIB)
426         if (profiler->gzfile) {
427                 gzwrite (profiler->gzfile, hbuf, p - hbuf);
428         } else {
429                 fwrite (hbuf, p - hbuf, 1, profiler->file);
430         }
431 #else
432         fwrite (hbuf, p - hbuf, 1, profiler->file);
433 #endif
434 }
435
436 static void
437 dump_buffer (MonoProfiler *profiler, LogBuffer *buf)
438 {
439         char hbuf [128];
440         char *p = hbuf;
441         if (buf->next)
442                 dump_buffer (profiler, buf->next);
443         p = write_int32 (p, BUF_ID);
444         p = write_int32 (p, buf->data - buf->buf);
445         p = write_int64 (p, buf->time_base);
446         p = write_int64 (p, buf->ptr_base);
447         p = write_int64 (p, buf->obj_base);
448         p = write_int64 (p, buf->thread_id);
449         p = write_int64 (p, buf->method_base);
450 #if defined (HAVE_SYS_ZLIB)
451         if (profiler->gzfile) {
452                 gzwrite (profiler->gzfile, hbuf, p - hbuf);
453                 gzwrite (profiler->gzfile, buf->buf, buf->data - buf->buf);
454         } else {
455 #endif
456                 fwrite (hbuf, p - hbuf, 1, profiler->file);
457                 fwrite (buf->buf, buf->data - buf->buf, 1, profiler->file);
458 #if defined (HAVE_SYS_ZLIB)
459         }
460 #endif
461         free_buffer (buf, buf->size);
462 }
463
464 static void
465 runtime_initialized (MonoProfiler *profiler)
466 {
467         runtime_inited = 1;
468 }
469
470 /*
471  * Can be called only at safe callback locations.
472  */
473 static void
474 safe_dump (MonoProfiler *profiler, LogBuffer *logbuffer)
475 {
476         int cd = logbuffer->call_depth;
477         take_lock ();
478         dump_buffer (profiler, TLS_GET (tlsbuffer));
479         release_lock ();
480         TLS_SET (tlsbuffer, NULL);
481         init_thread ();
482         TLS_GET (tlsbuffer)->call_depth = cd;
483 }
484
485 static int
486 gc_reference (MonoObject *obj, MonoClass *klass, uintptr_t size, uintptr_t num, MonoObject **refs, uintptr_t *offsets, void *data)
487 {
488         int i;
489         uintptr_t last_offset = 0;
490         //const char *name = mono_class_get_name (klass);
491         LogBuffer *logbuffer = ensure_logbuf (20 + num * 8);
492         emit_byte (logbuffer, TYPE_HEAP_OBJECT | TYPE_HEAP);
493         emit_obj (logbuffer, obj);
494         emit_ptr (logbuffer, klass);
495         /* account for object alignment in the heap */
496         size += 7;
497         size &= ~7;
498         emit_value (logbuffer, size);
499         emit_value (logbuffer, num);
500         for (i = 0; i < num; ++i) {
501                 emit_value (logbuffer, offsets [i] - last_offset);
502                 last_offset = offsets [i];
503                 emit_obj (logbuffer, refs [i]);
504         }
505         //if (num)
506         //      printf ("obj: %p, klass: %s, refs: %d, size: %d\n", obj, name, (int)num, (int)size);
507         return 0;
508 }
509
510 static unsigned int hs_mode_ms = 0;
511 static unsigned int hs_mode_gc = 0;
512 static unsigned int gc_count = 0;
513 static uint64_t last_hs_time = 0;
514
515 static void
516 heap_walk (MonoProfiler *profiler)
517 {
518         int do_walk = 0;
519         uint64_t now;
520         LogBuffer *logbuffer;
521         if (!do_heap_shot)
522                 return;
523         logbuffer = ensure_logbuf (10);
524         now = current_time ();
525         if (hs_mode_ms && (now - last_hs_time)/1000000 >= hs_mode_ms)
526                 do_walk = 1;
527         else if (hs_mode_gc && (gc_count % hs_mode_gc) == 0)
528                 do_walk = 1;
529         else if (!hs_mode_ms && !hs_mode_gc && profiler->last_gc_gen_started == mono_gc_max_generation ())
530                 do_walk = 1;
531
532         if (!do_walk)
533                 return;
534         emit_byte (logbuffer, TYPE_HEAP_START | TYPE_HEAP);
535         emit_time (logbuffer, now);
536         mono_gc_walk_heap (0, gc_reference, NULL);
537         logbuffer = ensure_logbuf (10);
538         now = current_time ();
539         emit_byte (logbuffer, TYPE_HEAP_END | TYPE_HEAP);
540         emit_time (logbuffer, now);
541         last_hs_time = now;
542 }
543
544 static void
545 gc_event (MonoProfiler *profiler, MonoGCEvent ev, int generation) {
546         uint64_t now;
547         LogBuffer *logbuffer = ensure_logbuf (10);
548         now = current_time ();
549         ENTER_LOG (logbuffer, "gcevent");
550         emit_byte (logbuffer, TYPE_GC_EVENT | TYPE_GC);
551         emit_time (logbuffer, now);
552         emit_value (logbuffer, ev);
553         emit_value (logbuffer, generation);
554         /* to deal with nested gen1 after gen0 started */
555         if (ev == MONO_GC_EVENT_START) {
556                 profiler->last_gc_gen_started = generation;
557                 gc_count++;
558         }
559         if (ev == MONO_GC_EVENT_PRE_START_WORLD)
560                 heap_walk (profiler);
561         EXIT_LOG (logbuffer);
562         if (ev == MONO_GC_EVENT_POST_START_WORLD)
563                 safe_dump (profiler, logbuffer);
564         //printf ("gc event %d for generation %d\n", ev, generation);
565 }
566
567 static void
568 gc_resize (MonoProfiler *profiler, int64_t new_size) {
569         uint64_t now;
570         LogBuffer *logbuffer = ensure_logbuf (10);
571         now = current_time ();
572         ENTER_LOG (logbuffer, "gcresize");
573         emit_byte (logbuffer, TYPE_GC_RESIZE | TYPE_GC);
574         emit_time (logbuffer, now);
575         emit_value (logbuffer, new_size);
576         //printf ("gc resized to %lld\n", new_size);
577         EXIT_LOG (logbuffer);
578 }
579
580 #define MAX_FRAMES 16
581 typedef struct {
582         int count;
583         MonoMethod* methods [MAX_FRAMES];
584 } FrameData;
585 static int num_frames = MAX_FRAMES / 2;
586
587 static mono_bool
588 walk_stack (MonoMethod *method, int32_t native_offset, int32_t il_offset, mono_bool managed, void* data)
589 {
590         FrameData *frame = data;
591         if (method && frame->count < num_frames) {
592                 frame->methods [frame->count++] = method;
593                 //printf ("In %d %s\n", frame->count, mono_method_get_name (method));
594         }
595         return frame->count == num_frames;
596 }
597
598 /*
599  * a note about stack walks: they can cause more profiler events to fire,
600  * so we need to make sure they don't happen after we started emitting an
601  * event, hence the collect_bt/emit_bt split.
602  */
603 static void
604 collect_bt (FrameData *data)
605 {
606         data->count = 0;
607         mono_stack_walk_no_il (walk_stack, data);
608 }
609
610 static void
611 emit_bt (LogBuffer *logbuffer, FrameData *data)
612 {
613         /* FIXME: this is actually tons of data and we should
614          * just output it the first time and use an id the next
615          */
616         if (data->count > num_frames)
617                 printf ("bad num frames: %d\n", data->count);
618         emit_value (logbuffer, 0); /* flags */
619         emit_value (logbuffer, data->count);
620         //if (*p != data.count) {
621         //      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);}
622         while (data->count) {
623                 emit_ptr (logbuffer, data->methods [--data->count]);
624         }
625 }
626
627 static void
628 gc_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
629 {
630         uint64_t now;
631         uintptr_t len;
632         int do_bt = (nocalls && runtime_inited && !notraces)? TYPE_ALLOC_BT: 0;
633         FrameData data;
634         LogBuffer *logbuffer;
635         len = mono_object_get_size (obj);
636         /* account for object alignment in the heap */
637         len += 7;
638         len &= ~7;
639         if (do_bt)
640                 collect_bt (&data);
641         logbuffer = ensure_logbuf (32 + MAX_FRAMES * 8);
642         now = current_time ();
643         ENTER_LOG (logbuffer, "gcalloc");
644         emit_byte (logbuffer, do_bt | TYPE_ALLOC);
645         emit_time (logbuffer, now);
646         emit_ptr (logbuffer, klass);
647         emit_obj (logbuffer, obj);
648         emit_value (logbuffer, len);
649         if (do_bt)
650                 emit_bt (logbuffer, &data);
651         EXIT_LOG (logbuffer);
652         if (logbuffer->next)
653                 safe_dump (prof, logbuffer);
654         //printf ("gc alloc %s at %p\n", mono_class_get_name (klass), obj);
655 }
656
657 static void
658 gc_moves (MonoProfiler *prof, void **objects, int num)
659 {
660         int i;
661         uint64_t now;
662         LogBuffer *logbuffer = ensure_logbuf (10 + num * 8);
663         now = current_time ();
664         ENTER_LOG (logbuffer, "gcmove");
665         emit_byte (logbuffer, TYPE_GC_MOVE | TYPE_GC);
666         emit_time (logbuffer, now);
667         emit_value (logbuffer, num);
668         for (i = 0; i < num; ++i)
669                 emit_obj (logbuffer, objects [i]);
670         //printf ("gc moved %d objects\n", num/2);
671         EXIT_LOG (logbuffer);
672 }
673
674 static void
675 gc_handle (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj)
676 {
677         uint64_t now;
678         LogBuffer *logbuffer = ensure_logbuf (16);
679         now = current_time ();
680         ENTER_LOG (logbuffer, "gchandle");
681         if (op == MONO_PROFILER_GC_HANDLE_CREATED)
682                 emit_byte (logbuffer, TYPE_GC_HANDLE_CREATED | TYPE_GC);
683         else if (op == MONO_PROFILER_GC_HANDLE_DESTROYED)
684                 emit_byte (logbuffer, TYPE_GC_HANDLE_DESTROYED | TYPE_GC);
685         else
686                 return;
687         emit_time (logbuffer, now);
688         emit_value (logbuffer, type);
689         emit_value (logbuffer, handle);
690         if (op == MONO_PROFILER_GC_HANDLE_CREATED)
691                 emit_obj (logbuffer, obj);
692         EXIT_LOG (logbuffer);
693 }
694
695 static char*
696 push_nesting (char *p, MonoClass *klass)
697 {
698         MonoClass *nesting;
699         const char *name;
700         const char *nspace;
701         nesting = mono_class_get_nesting_type (klass);
702         if (nesting) {
703                 p = push_nesting (p, nesting);
704                 *p++ = '/';
705                 *p = 0;
706         }
707         name = mono_class_get_name (klass);
708         nspace = mono_class_get_namespace (klass);
709         if (*nspace) {
710                 strcpy (p, nspace);
711                 p += strlen (nspace);
712                 *p++ = '.';
713                 *p = 0;
714         }
715         strcpy (p, name);
716         p += strlen (name);
717         return p;
718 }
719
720 static char*
721 type_name (MonoClass *klass)
722 {
723         char buf [1024];
724         char *p;
725         push_nesting (buf, klass);
726         p = malloc (strlen (buf) + 1);
727         strcpy (p, buf);
728         return p;
729 }
730
731 static void
732 image_loaded (MonoProfiler *prof, MonoImage *image, int result)
733 {
734         uint64_t now;
735         const char *name;
736         int nlen;
737         LogBuffer *logbuffer;
738         if (result != MONO_PROFILE_OK)
739                 return;
740         name = mono_image_get_filename (image);
741         nlen = strlen (name) + 1;
742         logbuffer = ensure_logbuf (16 + nlen);
743         now = current_time ();
744         ENTER_LOG (logbuffer, "image");
745         emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
746         emit_time (logbuffer, now);
747         emit_byte (logbuffer, TYPE_IMAGE);
748         emit_ptr (logbuffer, image);
749         emit_value (logbuffer, 0); /* flags */
750         memcpy (logbuffer->data, name, nlen);
751         logbuffer->data += nlen;
752         //printf ("loaded image %p (%s)\n", image, name);
753         EXIT_LOG (logbuffer);
754         if (logbuffer->next)
755                 safe_dump (prof, logbuffer);
756 }
757
758 static void
759 class_loaded (MonoProfiler *prof, MonoClass *klass, int result)
760 {
761         uint64_t now;
762         char *name;
763         int nlen;
764         MonoImage *image;
765         LogBuffer *logbuffer;
766         if (result != MONO_PROFILE_OK)
767                 return;
768         if (runtime_inited)
769                 name = mono_type_get_name (mono_class_get_type (klass));
770         else
771                 name = type_name (klass);
772         nlen = strlen (name) + 1;
773         image = mono_class_get_image (klass);
774         logbuffer = ensure_logbuf (24 + nlen);
775         now = current_time ();
776         ENTER_LOG (logbuffer, "class");
777         emit_byte (logbuffer, TYPE_END_LOAD | TYPE_METADATA);
778         emit_time (logbuffer, now);
779         emit_byte (logbuffer, TYPE_CLASS);
780         emit_ptr (logbuffer, klass);
781         emit_ptr (logbuffer, image);
782         emit_value (logbuffer, 0); /* flags */
783         memcpy (logbuffer->data, name, nlen);
784         logbuffer->data += nlen;
785         //printf ("loaded class %p (%s)\n", klass, name);
786         if (runtime_inited)
787                 mono_free (name);
788         else
789                 free (name);
790         EXIT_LOG (logbuffer);
791         if (logbuffer->next)
792                 safe_dump (prof, logbuffer);
793 }
794
795 static void
796 method_enter (MonoProfiler *prof, MonoMethod *method)
797 {
798         uint64_t now;
799         LogBuffer *logbuffer = ensure_logbuf (16);
800         if (logbuffer->call_depth++ > max_call_depth)
801                 return;
802         now = current_time ();
803         ENTER_LOG (logbuffer, "enter");
804         emit_byte (logbuffer, TYPE_ENTER | TYPE_METHOD);
805         emit_time (logbuffer, now);
806         emit_method (logbuffer, method);
807         EXIT_LOG (logbuffer);
808 }
809
810 static void
811 method_leave (MonoProfiler *prof, MonoMethod *method)
812 {
813         uint64_t now;
814         LogBuffer *logbuffer = ensure_logbuf (16);
815         if (--logbuffer->call_depth > max_call_depth)
816                 return;
817         now = current_time ();
818         ENTER_LOG (logbuffer, "leave");
819         emit_byte (logbuffer, TYPE_LEAVE | TYPE_METHOD);
820         emit_time (logbuffer, now);
821         emit_method (logbuffer, method);
822         EXIT_LOG (logbuffer);
823         if (logbuffer->next)
824                 safe_dump (prof, logbuffer);
825 }
826
827 static void
828 method_exc_leave (MonoProfiler *prof, MonoMethod *method)
829 {
830         uint64_t now;
831         LogBuffer *logbuffer;
832         if (nocalls)
833                 return;
834         logbuffer = ensure_logbuf (16);
835         if (--logbuffer->call_depth > max_call_depth)
836                 return;
837         now = current_time ();
838         ENTER_LOG (logbuffer, "eleave");
839         emit_byte (logbuffer, TYPE_EXC_LEAVE | TYPE_METHOD);
840         emit_time (logbuffer, now);
841         emit_method (logbuffer, method);
842         EXIT_LOG (logbuffer);
843 }
844
845 static void
846 method_jitted (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result)
847 {
848         uint64_t now;
849         char *name;
850         int nlen;
851         LogBuffer *logbuffer;
852         if (result != MONO_PROFILE_OK)
853                 return;
854         name = mono_method_full_name (method, 1);
855         nlen = strlen (name) + 1;
856         logbuffer = ensure_logbuf (32 + nlen);
857         now = current_time ();
858         ENTER_LOG (logbuffer, "jit");
859         emit_byte (logbuffer, TYPE_JIT | TYPE_METHOD);
860         emit_time (logbuffer, now);
861         emit_method (logbuffer, method);
862         emit_ptr (logbuffer, mono_jit_info_get_code_start (jinfo));
863         emit_value (logbuffer, mono_jit_info_get_code_size (jinfo));
864         memcpy (logbuffer->data, name, nlen);
865         logbuffer->data += nlen;
866         mono_free (name);
867         EXIT_LOG (logbuffer);
868         if (logbuffer->next)
869                 safe_dump (prof, logbuffer);
870 }
871
872 static void
873 throw_exc (MonoProfiler *prof, MonoObject *object)
874 {
875         int do_bt = (nocalls && runtime_inited && !notraces)? TYPE_EXCEPTION_BT: 0;
876         uint64_t now;
877         FrameData data;
878         LogBuffer *logbuffer;
879         if (do_bt)
880                 collect_bt (&data);
881         logbuffer = ensure_logbuf (16 + MAX_FRAMES * 8);
882         now = current_time ();
883         ENTER_LOG (logbuffer, "throw");
884         emit_byte (logbuffer, do_bt | TYPE_EXCEPTION);
885         emit_time (logbuffer, now);
886         emit_obj (logbuffer, object);
887         if (do_bt)
888                 emit_bt (logbuffer, &data);
889         EXIT_LOG (logbuffer);
890 }
891
892 static void
893 clause_exc (MonoProfiler *prof, MonoMethod *method, int clause_type, int clause_num)
894 {
895         uint64_t now;
896         LogBuffer *logbuffer = ensure_logbuf (16);
897         now = current_time ();
898         ENTER_LOG (logbuffer, "clause");
899         emit_byte (logbuffer, TYPE_EXCEPTION | TYPE_CLAUSE);
900         emit_time (logbuffer, now);
901         emit_value (logbuffer, clause_type);
902         emit_value (logbuffer, clause_num);
903         emit_method (logbuffer, method);
904         EXIT_LOG (logbuffer);
905 }
906
907 static void
908 monitor_event (MonoProfiler *profiler, MonoObject *object, MonoProfilerMonitorEvent event)
909 {
910         int do_bt = (nocalls && runtime_inited && !notraces && event == MONO_PROFILER_MONITOR_CONTENTION)? TYPE_MONITOR_BT: 0;
911         uint64_t now;
912         FrameData data;
913         LogBuffer *logbuffer;
914         if (do_bt)
915                 collect_bt (&data);
916         logbuffer = ensure_logbuf (16 + MAX_FRAMES * 8);
917         now = current_time ();
918         ENTER_LOG (logbuffer, "monitor");
919         emit_byte (logbuffer, (event << 4) | do_bt | TYPE_MONITOR);
920         emit_time (logbuffer, now);
921         emit_obj (logbuffer, object);
922         if (do_bt)
923                 emit_bt (logbuffer, &data);
924         EXIT_LOG (logbuffer);
925 }
926
927 static void
928 thread_start (MonoProfiler *prof, uintptr_t tid)
929 {
930         //printf ("thread start %p\n", (void*)tid);
931         init_thread ();
932 }
933
934 static void
935 thread_end (MonoProfiler *prof, uintptr_t tid)
936 {
937         take_lock ();
938         if (TLS_GET (tlsbuffer))
939                 dump_buffer (prof, TLS_GET (tlsbuffer));
940         release_lock ();
941         TLS_SET (tlsbuffer, NULL);
942 }
943
944 static void
945 thread_name (MonoProfiler *prof, uintptr_t tid, const char *name)
946 {
947         int len = strlen (name) + 1;
948         uint64_t now;
949         LogBuffer *logbuffer;
950         logbuffer = ensure_logbuf (10 + len);
951         now = current_time ();
952         ENTER_LOG (logbuffer, "tname");
953         emit_byte (logbuffer, TYPE_METADATA);
954         emit_time (logbuffer, now);
955         emit_byte (logbuffer, TYPE_THREAD);
956         emit_ptr (logbuffer, (void*)tid);
957         emit_value (logbuffer, 0); /* flags */
958         memcpy (logbuffer->data, name, len);
959         logbuffer->data += len;
960         EXIT_LOG (logbuffer);
961 }
962
963 static void
964 log_shutdown (MonoProfiler *prof)
965 {
966         take_lock ();
967         if (TLS_GET (tlsbuffer))
968                 dump_buffer (prof, TLS_GET (tlsbuffer));
969         TLS_SET (tlsbuffer, NULL);
970         release_lock ();
971 #if defined (HAVE_SYS_ZLIB)
972         if (prof->gzfile)
973                 gzclose (prof->gzfile);
974 #endif
975         if (prof->pipe_output)
976                 pclose (prof->file);
977         else
978                 fclose (prof->file);
979         free (prof);
980 }
981
982 static char*
983 new_filename (const char* filename)
984 {
985         time_t t = time (NULL);
986         int pid = process_id ();
987         char pid_buf [16];
988         char time_buf [16];
989         char *res, *d;
990         const char *p;
991         int count_dates = 0;
992         int count_pids = 0;
993         int s_date, s_pid;
994         struct tm *ts;
995         for (p = filename; *p; p++) {
996                 if (*p != '%')
997                         continue;
998                 p++;
999                 if (*p == 't')
1000                         count_dates++;
1001                 else if (*p == 'p')
1002                         count_pids++;
1003                 else if (*p == 0)
1004                         break;
1005         }
1006         if (!count_dates && !count_pids)
1007                 return pstrdup (filename);
1008         snprintf (pid_buf, sizeof (pid_buf), "%d", pid);
1009         ts = gmtime (&t);
1010         snprintf (time_buf, sizeof (time_buf), "%d%02d%02d%02d%02d%02d",
1011                 1900 + ts->tm_year, 1 + ts->tm_mon, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec);
1012         s_date = strlen (time_buf);
1013         s_pid = strlen (pid_buf);
1014         d = res = malloc (strlen (filename) + s_date * count_dates + s_pid * count_pids);
1015         for (p = filename; *p; p++) {
1016                 if (*p != '%') {
1017                         *d++ = *p;
1018                         continue;
1019                 }
1020                 p++;
1021                 if (*p == 't') {
1022                         strcpy (d, time_buf);
1023                         d += s_date;
1024                         continue;
1025                 } else if (*p == 'p') {
1026                         strcpy (d, pid_buf);
1027                         d += s_pid;
1028                         continue;
1029                 } else if (*p == '%') {
1030                         *d++ = '%';
1031                         continue;
1032                 } else if (*p == 0)
1033                         break;
1034                 *d++ = '%';
1035                 *d++ = *p;
1036         }
1037         *d = 0;
1038         return res;
1039 }
1040
1041 static MonoProfiler*
1042 create_profiler (const char *filename)
1043 {
1044         MonoProfiler *prof;
1045         char *nf;
1046         int force_delete = 0;
1047         prof = calloc (1, sizeof (MonoProfiler));
1048
1049         if (filename && *filename == '-') {
1050                 force_delete = 1;
1051                 filename++;
1052         }
1053         if (!filename) {
1054                 if (do_report)
1055                         filename = "|mprof-report -";
1056                 else
1057                         filename = "output.mlpd";
1058                 nf = filename;
1059         } else {
1060                 nf = new_filename (filename);
1061                 if (do_report) {
1062                         int s = strlen (nf) + 32;
1063                         char *p = malloc (s);
1064                         snprintf (p, s, "|mprof-report '--out=%s' -", nf);
1065                         free (nf);
1066                         nf = p;
1067                 }
1068         }
1069         if (*nf == '|') {
1070                 prof->file = popen (nf + 1, "w");
1071                 prof->pipe_output = 1;
1072         } else {
1073                 FILE *f;
1074                 if (force_delete)
1075                         unlink (nf);
1076                 if ((f = fopen (nf, "r"))) {
1077                         fclose (f);
1078                         fprintf (stderr, "The Mono profiler won't overwrite existing filename: %s.\n", nf);
1079                         fprintf (stderr, "Profiling disabled: use a different name or -FILENAME to force overwrite.\n");
1080                         free (prof);
1081                         return NULL;
1082                 }
1083                 prof->file = fopen (nf, "wb");
1084         }
1085         if (!prof->file) {
1086                 printf ("Cannot create profiler output: %s\n", nf);
1087                 exit (1);
1088         }
1089 #if defined (HAVE_SYS_ZLIB)
1090         if (use_zip)
1091                 prof->gzfile = gzdopen (fileno (prof->file), "wb");
1092 #endif
1093         dump_header (prof);
1094         return prof;
1095 }
1096
1097 static void
1098 usage (int do_exit)
1099 {
1100         printf ("Log profiler version %d.%d (format: %d)\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
1101         printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
1102         printf ("Options:\n");
1103         printf ("\thelp             show this usage info\n");
1104         printf ("\t[no]alloc        enable/disable recording allocation info\n");
1105         printf ("\t[no]calls        enable/disable recording enter/leave method events\n");
1106         printf ("\theapshot[=MODE]  record heap shot info (by default at each major collection)\n");
1107         printf ("\t                 heapshot MODE: every XXms milliseconds or every YYgc collections\n");
1108         printf ("\ttime=fast        use a faster (but more inaccurate) timer\n");
1109         printf ("\tmaxframes=NUM    collect up to NUM stack frames\n");
1110         printf ("\tcalldepth=NUM    ignore method events for call chain depth bigger than NUM\n");
1111         printf ("\toutput=FILENAME  write the data to file FILENAME (-FILENAME to overwrite)\n");
1112         printf ("\toutput=|PROGRAM  write the data to the stdin of PROGRAM\n");
1113         printf ("\t                 %%t is subtituted with date and time, %%p with the pid\n");
1114         printf ("\treport           create a report instead of writing the raw data to a file\n");
1115         printf ("\tzip              compress the output data\n");
1116         if (do_exit)
1117                 exit (1);
1118 }
1119
1120 static const char*
1121 match_option (const char* p, const char *opt, char **rval)
1122 {
1123         int len = strlen (opt);
1124         if (strncmp (p, opt, len) == 0) {
1125                 if (rval) {
1126                         if (p [len] == '=' && p [len + 1]) {
1127                                 const char *opt = p + len + 1;
1128                                 const char *end = strchr (opt, ',');
1129                                 char *val;
1130                                 int l;
1131                                 if (end == NULL) {
1132                                         l = strlen (opt);
1133                                 } else {
1134                                         l = end - opt;
1135                                 }
1136                                 val = malloc (l + 1);
1137                                 memcpy (val, opt, l);
1138                                 val [l] = 0;
1139                                 *rval = val;
1140                                 return opt + l;
1141                         }
1142                         if (p [len] == 0 || p [len] == ',') {
1143                                 *rval = NULL;
1144                                 return p + len + (p [len] == ',');
1145                         }
1146                         usage (1);
1147                 } else {
1148                         if (p [len] == 0)
1149                                 return p + len;
1150                         if (p [len] == ',')
1151                                 return p + len + 1;
1152                 }
1153         }
1154         return p;
1155 }
1156
1157 static void
1158 set_hsmode (char* val, int allow_empty)
1159 {
1160         char *end;
1161         unsigned int count;
1162         if (allow_empty && !val)
1163                 return;
1164         count = strtoul (val, &end, 10);
1165         if (val == end)
1166                 usage (1);
1167         if (strcmp (end, "ms") == 0)
1168                 hs_mode_ms = count;
1169         else if (strcmp (end, "gc") == 0)
1170                 hs_mode_gc = count;
1171         else
1172                 usage (1);
1173         free (val);
1174 }
1175
1176 /* 
1177  * declaration to silence the compiler: this is the entry point that
1178  * mono will load from the shared library and call.
1179  */
1180 extern void
1181 mono_profiler_startup (const char *desc);
1182
1183 void
1184 mono_profiler_startup (const char *desc)
1185 {
1186         MonoProfiler *prof;
1187         char *filename = NULL;
1188         const char *p;
1189         const char *opt;
1190         int fast_time = 0;
1191         int calls_enabled = 0;
1192         int allocs_enabled = 0;
1193         int events = MONO_PROFILE_GC|MONO_PROFILE_ALLOCATIONS|
1194                 MONO_PROFILE_GC_MOVES|MONO_PROFILE_CLASS_EVENTS|MONO_PROFILE_THREADS|
1195                 MONO_PROFILE_ENTER_LEAVE|MONO_PROFILE_JIT_COMPILATION|MONO_PROFILE_EXCEPTIONS|
1196                 MONO_PROFILE_MONITOR_EVENTS|MONO_PROFILE_MODULE_EVENTS|MONO_PROFILE_GC_ROOTS;
1197
1198         p = desc;
1199         if (strncmp (p, "log", 3))
1200                 usage (1);
1201         p += 3;
1202         if (*p == ':')
1203                 p++;
1204         for (; *p; p = opt) {
1205                 char *val;
1206                 if (*p == ',') {
1207                         opt = p + 1;
1208                         continue;
1209                 }
1210                 if ((opt = match_option (p, "help", NULL)) != p) {
1211                         usage (0);
1212                         continue;
1213                 }
1214                 if ((opt = match_option (p, "calls", NULL)) != p) {
1215                         calls_enabled = 1;
1216                         continue;
1217                 }
1218                 if ((opt = match_option (p, "nocalls", NULL)) != p) {
1219                         events &= ~MONO_PROFILE_ENTER_LEAVE;
1220                         nocalls = 1;
1221                         continue;
1222                 }
1223                 if ((opt = match_option (p, "alloc", NULL)) != p) {
1224                         allocs_enabled = 1;
1225                         continue;
1226                 }
1227                 if ((opt = match_option (p, "noalloc", NULL)) != p) {
1228                         events &= ~MONO_PROFILE_ALLOCATIONS;
1229                         continue;
1230                 }
1231                 if ((opt = match_option (p, "time", &val)) != p) {
1232                         if (strcmp (val, "fast") == 0)
1233                                 fast_time = 1;
1234                         else if (strcmp (val, "null") == 0)
1235                                 fast_time = 2;
1236                         else
1237                                 usage (1);
1238                         free (val);
1239                         continue;
1240                 }
1241                 if ((opt = match_option (p, "report", NULL)) != p) {
1242                         do_report = 1;
1243                         continue;
1244                 }
1245                 if ((opt = match_option (p, "heapshot", &val)) != p) {
1246                         events &= ~MONO_PROFILE_ALLOCATIONS;
1247                         events &= ~MONO_PROFILE_ENTER_LEAVE;
1248                         nocalls = 1;
1249                         do_heap_shot = 1;
1250                         set_hsmode (val, 1);
1251                         continue;
1252                 }
1253                 if ((opt = match_option (p, "hsmode", &val)) != p) {
1254                         fprintf (stderr, "The hsmode profiler option is obsolete, use heapshot=MODE.\n");
1255                         set_hsmode (val, 0);
1256                         continue;
1257                 }
1258                 if ((opt = match_option (p, "zip", NULL)) != p) {
1259                         use_zip = 1;
1260                         continue;
1261                 }
1262                 if ((opt = match_option (p, "output", &val)) != p) {
1263                         filename = val;
1264                         continue;
1265                 }
1266                 if ((opt = match_option (p, "maxframes", &val)) != p) {
1267                         char *end;
1268                         num_frames = strtoul (val, &end, 10);
1269                         if (num_frames > MAX_FRAMES)
1270                                 num_frames = MAX_FRAMES;
1271                         free (val);
1272                         notraces = num_frames == 0;
1273                         continue;
1274                 }
1275                 if ((opt = match_option (p, "calldepth", &val)) != p) {
1276                         char *end;
1277                         max_call_depth = strtoul (val, &end, 10);
1278                         free (val);
1279                         continue;
1280                 }
1281                 if (opt == p) {
1282                         usage (0);
1283                         exit (0);
1284                 }
1285         }
1286         if (calls_enabled) {
1287                 events |= MONO_PROFILE_ENTER_LEAVE;
1288                 nocalls = 0;
1289         }
1290         if (allocs_enabled)
1291                 events |= MONO_PROFILE_ALLOCATIONS;
1292         utils_init (fast_time);
1293
1294         prof = create_profiler (filename);
1295         if (!prof)
1296                 return;
1297         init_thread ();
1298
1299         mono_profiler_install (prof, log_shutdown);
1300         mono_profiler_install_gc (gc_event, gc_resize);
1301         mono_profiler_install_allocation (gc_alloc);
1302         mono_profiler_install_gc_moves (gc_moves);
1303         mono_profiler_install_gc_roots (gc_handle);
1304         mono_profiler_install_class (NULL, class_loaded, NULL, NULL);
1305         mono_profiler_install_module (NULL, image_loaded, NULL, NULL);
1306         mono_profiler_install_thread (thread_start, thread_end);
1307         mono_profiler_install_thread_name (thread_name);
1308         mono_profiler_install_enter_leave (method_enter, method_leave);
1309         mono_profiler_install_jit_end (method_jitted);
1310         mono_profiler_install_exception (throw_exc, method_exc_leave, clause_exc);
1311         mono_profiler_install_monitor (monitor_event);
1312         mono_profiler_install_runtime_initialized (runtime_initialized);
1313
1314         mono_profiler_set_events (events);
1315
1316         TLS_INIT (tlsbuffer);
1317 }
1318