3c916a941b3ddf62af09f0ed8db444584e76c84e
[mono.git] / mono / mini / trace.c
1 /**
2  * \file
3  * Tracing facilities for the Mono Runtime.
4  *
5  * Author:
6  *   Paolo Molaro (lupus@ximian.com)
7  *   Dietmar Maurer (dietmar@ximian.com)
8  *
9  * (C) 2002 Ximian, Inc.
10  * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
11  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12  */
13
14 #include <config.h>
15 #ifdef HAVE_ALLOCA_H
16 #include <alloca.h>
17 #endif
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21 #include <string.h>
22 #include "mini.h"
23 #include <mono/metadata/debug-helpers.h>
24 #include <mono/metadata/assembly.h>
25 #include <mono/utils/mono-time.h>
26 #include <mono/utils/mono-memory-model.h>
27 #include "trace.h"
28
29 #if defined (HOST_ANDROID) || (defined (TARGET_IOS) && defined (TARGET_IOS))
30 #  undef printf
31 #  define printf(...) g_log("mono", G_LOG_LEVEL_MESSAGE, __VA_ARGS__)
32 #  undef fprintf
33 #  define fprintf(__ignore, ...) g_log ("mono-gc", G_LOG_LEVEL_MESSAGE, __VA_ARGS__)
34 #endif
35
36 static MonoTraceSpec trace_spec;
37
38 static volatile gint32 output_lock = 0;
39
40 gboolean
41 mono_trace_eval_exception (MonoClass *klass)
42 {
43         int include = 0;
44         int i;
45
46         if (!klass)
47                 return FALSE;
48
49         for (i = 0; i < trace_spec.len; i++) {
50                 MonoTraceOperation *op = &trace_spec.ops [i];
51                 int inc = 0;
52                 
53                 switch (op->op){
54                 case MONO_TRACEOP_EXCEPTION:
55                         if (strcmp ("", op->data) == 0 && strcmp ("all", op->data2) == 0)
56                                 inc = 1;
57                         else if (strcmp ("", op->data) == 0 || strcmp (klass->name_space, op->data) == 0)
58                                 if (strcmp (klass->name, op->data2) == 0)
59                                         inc = 1;
60                         break;
61                 default:
62                         break;
63                 }
64                 if (op->exclude){
65                         if (inc)
66                                 include = 0;
67                 } else if (inc)
68                         include = 1;
69         }
70
71         return include;
72 }
73
74 gboolean
75 mono_trace_eval (MonoMethod *method)
76 {
77         int include = 0;
78         int i;
79
80         for (i = 0; i < trace_spec.len; i++){
81                 MonoTraceOperation *op = &trace_spec.ops [i];
82                 int inc = 0;
83                 
84                 switch (op->op){
85                 case MONO_TRACEOP_ALL:
86                         inc = 1;
87                         break;
88                 case MONO_TRACEOP_PROGRAM:
89                         if (trace_spec.assembly && (method->klass->image == mono_assembly_get_image (trace_spec.assembly)))
90                                 inc = 1;
91                         break;
92                 case MONO_TRACEOP_WRAPPER:
93                         if ((method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) ||
94                                 (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE))
95                                 inc = 1;
96                         break;
97                 case MONO_TRACEOP_METHOD:
98                         if (mono_method_desc_full_match ((MonoMethodDesc *) op->data, method))
99                                 inc = 1;
100                         break;
101                 case MONO_TRACEOP_CLASS:
102                         if (strcmp (method->klass->name_space, op->data) == 0)
103                                 if (strcmp (method->klass->name, op->data2) == 0)
104                                         inc = 1;
105                         break;
106                 case MONO_TRACEOP_ASSEMBLY:
107                         if (strcmp (mono_image_get_name (method->klass->image), op->data) == 0)
108                                 inc = 1;
109                         break;
110                 case MONO_TRACEOP_NAMESPACE:
111                         if (strcmp (method->klass->name_space, op->data) == 0)
112                                 inc = 1;
113                         break;
114                 case MONO_TRACEOP_EXCEPTION:
115                         break;
116                 }
117                 if (op->exclude) {
118                         if (inc)
119                                 include = 0;
120                 } else if (inc) {
121                         include = 1;
122                 }
123         }
124         return include;
125 }
126
127 static int is_filenamechar (char p)
128 {
129         if (p >= 'A' && p <= 'Z')
130                 return TRUE;
131         if (p >= 'a' && p <= 'z')
132                 return TRUE;
133         if (p >= '0' && p <= '9')
134                 return TRUE;
135         if (p == '.' || p == ':' || p == '_' || p == '-' || p == '`')
136                 return TRUE;
137         return FALSE;
138 }
139
140 static char *input;
141 static char *value;
142
143 static void get_string (void)
144 {
145         char *start = input;
146         while (is_filenamechar (*input)){
147                 input++;
148         }
149         if (value != NULL)
150                 g_free (value);
151         size_t len = input - start;
152         value = (char *)g_malloc (len + 1);
153         memcpy (value, start, len);
154         value [len] = 0;
155 }
156
157 enum Token {
158         TOKEN_METHOD,
159         TOKEN_CLASS,
160         TOKEN_ALL,
161         TOKEN_PROGRAM,
162         TOKEN_EXCEPTION,
163         TOKEN_NAMESPACE,
164         TOKEN_WRAPPER,
165         TOKEN_STRING,
166         TOKEN_EXCLUDE,
167         TOKEN_DISABLED,
168         TOKEN_SEPARATOR,
169         TOKEN_END,
170         TOKEN_ERROR
171 };
172
173 static int
174 get_token (void)
175 {
176         while (input [0] == '+')
177                 input++;
178
179         if (input [0] == '\0') {
180                 return TOKEN_END;
181         }
182         if (input [0] == 'M' && input [1] == ':'){
183                 input += 2;
184                 get_string ();
185                 return TOKEN_METHOD;
186         }
187         if (input [0] == 'N' && input [1] == ':'){
188                 input += 2;
189                 get_string ();
190                 return TOKEN_NAMESPACE;
191         }
192         if (input [0] == 'T' && input [1] == ':'){
193                 input += 2;
194                 get_string ();
195                 return TOKEN_CLASS;
196         }
197         if (input [0] == 'E' && input [1] == ':'){
198                 input += 2;
199                 get_string ();
200                 return TOKEN_EXCEPTION;
201         }
202         if (*input == '-'){
203                 input++;
204                 return TOKEN_EXCLUDE;
205         }
206         if (is_filenamechar (*input)){
207                 get_string ();
208                 if (strcmp (value, "all") == 0)
209                         return TOKEN_ALL;
210                 if (strcmp (value, "program") == 0)
211                         return TOKEN_PROGRAM;
212                 if (strcmp (value, "wrapper") == 0)
213                         return TOKEN_WRAPPER;
214                 if (strcmp (value, "disabled") == 0)
215                         return TOKEN_DISABLED;
216                 return TOKEN_STRING;
217         }
218         if (*input == ','){
219                 input++;
220                 return TOKEN_SEPARATOR;
221         }
222
223         fprintf (stderr, "Syntax error at or around '%s'\n", input);    
224         return TOKEN_ERROR;
225 }
226
227 static void
228 cleanup (void)
229 {
230         if (value != NULL)
231                 g_free (value);
232 }
233
234 static int
235 get_spec (int *last)
236 {
237         int token = get_token ();
238         if (token == TOKEN_EXCLUDE){
239                 token = get_spec (last);
240                 if (token == TOKEN_EXCLUDE){
241                         fprintf (stderr, "Expecting an expression");
242                         return TOKEN_ERROR;
243                 }
244                 if (token == TOKEN_ERROR)
245                         return token;
246                 trace_spec.ops [(*last)-1].exclude = 1;
247                 return TOKEN_SEPARATOR;
248         }
249         if (token == TOKEN_END || token == TOKEN_SEPARATOR || token == TOKEN_ERROR)
250                 return token;
251         
252         if (token == TOKEN_METHOD){
253                 MonoMethodDesc *desc = mono_method_desc_new (value, TRUE);
254                 if (desc == NULL){
255                         fprintf (stderr, "Invalid method name: %s\n", value);
256                         return TOKEN_ERROR;
257                 }
258                 trace_spec.ops [*last].op = MONO_TRACEOP_METHOD;
259                 trace_spec.ops [*last].data = desc;
260         } else if (token == TOKEN_ALL)
261                 trace_spec.ops [*last].op = MONO_TRACEOP_ALL;
262         else if (token == TOKEN_PROGRAM)
263                 trace_spec.ops [*last].op = MONO_TRACEOP_PROGRAM;
264         else if (token == TOKEN_WRAPPER)
265                 trace_spec.ops [*last].op = MONO_TRACEOP_WRAPPER;
266         else if (token == TOKEN_NAMESPACE){
267                 trace_spec.ops [*last].op = MONO_TRACEOP_NAMESPACE;
268                 trace_spec.ops [*last].data = g_strdup (value);
269         } else if (token == TOKEN_CLASS || token == TOKEN_EXCEPTION){
270                 char *p = strrchr (value, '.');
271                 if (p) {
272                         *p++ = 0;
273                         trace_spec.ops [*last].data = g_strdup (value);
274                         trace_spec.ops [*last].data2 = g_strdup (p);
275                 }
276                 else {
277                         trace_spec.ops [*last].data = g_strdup ("");
278                         trace_spec.ops [*last].data2 = g_strdup (value);
279                 }
280                 trace_spec.ops [*last].op = token == TOKEN_CLASS ? MONO_TRACEOP_CLASS : MONO_TRACEOP_EXCEPTION;
281         } else if (token == TOKEN_STRING){
282                 trace_spec.ops [*last].op = MONO_TRACEOP_ASSEMBLY;
283                 trace_spec.ops [*last].data = g_strdup (value);
284         } else if (token == TOKEN_DISABLED) {
285                 trace_spec.enabled = FALSE;
286         } else {
287                 fprintf (stderr, "Syntax error in trace option specification\n");
288                 return TOKEN_ERROR;
289         }
290         (*last)++;
291         return TOKEN_SEPARATOR;
292 }
293
294 MonoTraceSpec *
295 mono_trace_parse_options (const char *options)
296 {
297         char *p = (char*)options;
298         int size = 1;
299         int last_used;
300         int token;
301
302         trace_spec.enabled = TRUE;
303         if (*p == 0){
304                 trace_spec.len = 1;
305                 trace_spec.ops = g_new0 (MonoTraceOperation, 1);
306                 trace_spec.ops [0].op = MONO_TRACEOP_ALL;
307                 return &trace_spec;
308         }
309                 
310         for (p = (char*)options; *p != 0; p++)
311                 if (*p == ',')
312                         size++;
313         
314         trace_spec.ops = g_new0 (MonoTraceOperation, size);
315
316         input = (char*)options;
317         last_used = 0;
318         
319         while ((token = (get_spec (&last_used))) != TOKEN_END){
320                 if (token == TOKEN_ERROR)
321                         return NULL;
322                 if (token == TOKEN_SEPARATOR)
323                         continue;
324         }
325         trace_spec.len = last_used;
326         cleanup ();
327         return &trace_spec;
328 }
329
330 void
331 mono_trace_set_assembly (MonoAssembly *assembly)
332 {
333         trace_spec.assembly = assembly;
334 }
335
336 static
337 #ifdef HAVE_KW_THREAD
338 __thread 
339 #endif
340 int indent_level = 0;
341 static guint64 start_time = 0;
342
343 static double seconds_since_start (void)
344 {
345         guint64 diff = mono_100ns_ticks () - start_time;
346         return diff/10000000.0;
347 }
348
349 static void indent (int diff) {
350         if (diff < 0)
351                 indent_level += diff;
352         if (start_time == 0)
353                 start_time = mono_100ns_ticks ();
354         printf ("[%p: %.5f %d] ", (void*)mono_native_thread_id_get (), seconds_since_start (), indent_level);
355         if (diff > 0)
356                 indent_level += diff;
357 }
358
359 static char *
360 string_to_utf8 (MonoString *s)
361 {
362         char *as;
363         GError *error = NULL;
364
365         g_assert (s);
366
367         if (!s->length)
368                 return g_strdup ("");
369
370         as = g_utf16_to_utf8 (mono_string_chars (s), s->length, NULL, NULL, &error);
371         if (error) {
372                 /* Happens with StringBuilders */
373                 g_error_free (error);
374                 return g_strdup ("<INVALID UTF8>");
375         }
376         else
377                 return as;
378 }
379
380 /*
381  * cpos (ebp + arg_info[n].offset) points to the beginning of the
382  * stack slot for this argument.  On little-endian systems, we can
383  * simply dereference it. On big-endian systems, we need to adjust
384  * cpos upward first if the datatype we're referencing is smaller than
385  * a stack slot. Also - one can't assume that gpointer is also the
386  * size of a stack slot - use SIZEOF_REGISTER instead. The following
387  * helper macro tries to keep down the mess of all the pointer
388  * calculations.
389  */
390 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
391 #define arg_in_stack_slot(cpos, type) ((type *)(cpos))
392 #else
393 #define arg_in_stack_slot(cpos, type) ((type *)((sizeof(type) < SIZEOF_REGISTER) ? (((gssize)(cpos)) + SIZEOF_REGISTER - sizeof(type)) : (gssize)(cpos)))
394 #endif
395
396 void
397 mono_trace_enter_method (MonoMethod *method, char *ebp)
398 {
399         int i, j;
400         MonoClass *klass;
401         MonoObject *o;
402         MonoJitArgumentInfo *arg_info;
403         MonoMethodSignature *sig;
404         char *fname;
405         MonoGenericSharingContext *gsctx = NULL;
406
407         if (!trace_spec.enabled)
408                 return;
409
410         while (output_lock != 0 || InterlockedCompareExchange (&output_lock, 1, 0) != 0)
411                 mono_thread_info_yield ();
412
413         fname = mono_method_full_name (method, TRUE);
414         indent (1);
415         printf ("ENTER: %s(", fname);
416         g_free (fname);
417
418         if (!ebp) {
419                 printf (") ip: %p\n", MONO_RETURN_ADDRESS_N (1));
420                 goto unlock;
421         }
422
423         sig = mono_method_signature (method);
424
425         arg_info = (MonoJitArgumentInfo *)alloca (sizeof (MonoJitArgumentInfo) * (sig->param_count + 1));
426
427         if (method->is_inflated) {
428                 /* FIXME: Might be better to pass the ji itself */
429                 MonoJitInfo *ji = mini_jit_info_table_find (mono_domain_get (), (char *)MONO_RETURN_ADDRESS (), NULL);
430                 if (ji) {
431                         gsctx = mono_jit_info_get_generic_sharing_context (ji);
432                         if (gsctx && gsctx->is_gsharedvt) {
433                                 /* Needs a ctx to get precise method */
434                                 printf (") <gsharedvt>\n");
435                                 goto unlock;
436                         }
437                 }
438         }
439
440         mono_arch_get_argument_info (sig, sig->param_count, arg_info);
441
442         if (MONO_TYPE_ISSTRUCT (mono_method_signature (method)->ret)) {
443                 g_assert (!mono_method_signature (method)->ret->byref);
444
445                 printf ("VALUERET:%p, ", *((gpointer *)(ebp + 8)));
446         }
447
448         if (mono_method_signature (method)->hasthis) {
449                 gpointer *this_obj = (gpointer *)(ebp + arg_info [0].offset);
450                 if (method->klass->valuetype) {
451                         printf ("value:%p, ", *arg_in_stack_slot(this_obj, gpointer *));
452                 } else {
453                         o = *arg_in_stack_slot(this_obj, MonoObject *);
454
455                         if (o) {
456                                 klass = o->vtable->klass;
457
458                                 if (klass == mono_defaults.string_class) {
459                                         MonoString *s = (MonoString*)o;
460                                         char *as = string_to_utf8 (s);
461
462                                         printf ("this:[STRING:%p:%s], ", o, as);
463                                         g_free (as);
464                                 } else {
465                                         printf ("this:%p[%s.%s %s], ", o, klass->name_space, klass->name, o->vtable->domain->friendly_name);
466                                 }
467                         } else 
468                                 printf ("this:NULL, ");
469                 }
470         }
471
472         for (i = 0; i < mono_method_signature (method)->param_count; ++i) {
473                 gpointer *cpos = (gpointer *)(ebp + arg_info [i + 1].offset);
474                 int size = arg_info [i + 1].size;
475
476                 MonoType *type = mono_method_signature (method)->params [i];
477                 
478                 if (type->byref) {
479                         printf ("[BYREF:%p], ", *arg_in_stack_slot(cpos, gpointer *));
480                 } else switch (mini_get_underlying_type (type)->type) {
481                         
482                 case MONO_TYPE_I:
483                 case MONO_TYPE_U:
484                         printf ("%p, ", *arg_in_stack_slot(cpos, gpointer *));
485                         break;
486                 case MONO_TYPE_BOOLEAN:
487                 case MONO_TYPE_CHAR:
488                 case MONO_TYPE_I1:
489                 case MONO_TYPE_U1:
490                         printf ("%d, ", *arg_in_stack_slot(cpos, gint8));
491                         break;
492                 case MONO_TYPE_I2:
493                 case MONO_TYPE_U2:
494                         printf ("%d, ", *arg_in_stack_slot(cpos, gint16));
495                         break;
496                 case MONO_TYPE_I4:
497                 case MONO_TYPE_U4:
498                         printf ("%d, ", *arg_in_stack_slot(cpos, int));
499                         break;
500                 case MONO_TYPE_STRING: {
501                         MonoString *s = *arg_in_stack_slot(cpos, MonoString *);
502                         if (s) {
503                                 char *as;
504
505                                 g_assert (((MonoObject *)s)->vtable->klass == mono_defaults.string_class);
506                                 as = string_to_utf8 (s);
507
508                                 printf ("[STRING:%p:%s], ", s, as);
509                                 g_free (as);
510                         } else 
511                                 printf ("[STRING:null], ");
512                         break;
513                 }
514                 case MONO_TYPE_CLASS:
515                 case MONO_TYPE_OBJECT: {
516                         o = *arg_in_stack_slot(cpos, MonoObject *);
517                         if (o) {
518                                 klass = o->vtable->klass;
519                     
520                                 if (klass == mono_defaults.string_class) {
521                                         char *as = string_to_utf8 ((MonoString*)o);
522
523                                         printf ("[STRING:%p:%s], ", o, as);
524                                         g_free (as);
525                                 } else if (klass == mono_defaults.int32_class) {
526                                         printf ("[INT32:%p:%d], ", o, *(gint32 *)((char *)o + sizeof (MonoObject)));
527                                 } else if (klass == mono_defaults.runtimetype_class) {
528                                         printf ("[TYPE:%s], ", mono_type_full_name (((MonoReflectionType*)o)->type));
529                                 } else
530                                         printf ("[%s.%s:%p], ", klass->name_space, klass->name, o);
531                         } else {
532                                 printf ("%p, ", *arg_in_stack_slot(cpos, gpointer));
533                         }
534                         break;
535                 }
536                 case MONO_TYPE_PTR:
537                 case MONO_TYPE_FNPTR:
538                 case MONO_TYPE_ARRAY:
539                 case MONO_TYPE_SZARRAY:
540                         printf ("%p, ", *arg_in_stack_slot(cpos, gpointer));
541                         break;
542                 case MONO_TYPE_I8:
543                 case MONO_TYPE_U8:
544                         printf ("0x%016llx, ", (long long)*arg_in_stack_slot(cpos, gint64));
545                         break;
546                 case MONO_TYPE_R4:
547                         printf ("%f, ", *arg_in_stack_slot(cpos, float));
548                         break;
549                 case MONO_TYPE_R8:
550                         printf ("%f, ", *arg_in_stack_slot(cpos, double));
551                         break;
552                 case MONO_TYPE_VALUETYPE: 
553                         printf ("[");
554                         for (j = 0; j < size; j++)
555                                 printf ("%02x,", *((guint8*)cpos +j));
556                         printf ("], ");
557                         break;
558                 default:
559                         printf ("XX, ");
560                 }
561         }
562
563         printf (")\n");
564         fflush (stdout);
565
566 unlock:
567         mono_atomic_store_release (&output_lock, 0);
568 }
569
570 void
571 mono_trace_leave_method (MonoMethod *method, ...)
572 {
573         MonoType *type;
574         char *fname;
575         va_list ap;
576         MonoGenericSharingContext *gsctx;
577
578         if (!trace_spec.enabled)
579                 return;
580
581         while (output_lock != 0 || InterlockedCompareExchange (&output_lock, 1, 0) != 0)
582                 mono_thread_info_yield ();
583
584         va_start(ap, method);
585
586         fname = mono_method_full_name (method, TRUE);
587         indent (-1);
588         printf ("LEAVE: %s", fname);
589         g_free (fname);
590
591         if (method->is_inflated) {
592                 /* FIXME: Might be better to pass the ji itself */
593                 MonoJitInfo *ji = mini_jit_info_table_find (mono_domain_get (), (char *)MONO_RETURN_ADDRESS (), NULL);
594                 if (ji) {
595                         gsctx = mono_jit_info_get_generic_sharing_context (ji);
596                         if (gsctx && gsctx->is_gsharedvt) {
597                                 /* Needs a ctx to get precise method */
598                                 printf (") <gsharedvt>\n");
599                                 goto unlock;
600                         }
601                 }
602         }
603
604         type = mini_get_underlying_type (mono_method_signature (method)->ret);
605
606         switch (type->type) {
607         case MONO_TYPE_VOID:
608                 break;
609         case MONO_TYPE_BOOLEAN: {
610                 int eax = va_arg (ap, int);
611                 if (eax)
612                         printf ("TRUE:%d", eax);
613                 else 
614                         printf ("FALSE");
615                         
616                 break;
617         }
618         case MONO_TYPE_CHAR:
619         case MONO_TYPE_I1:
620         case MONO_TYPE_U1:
621         case MONO_TYPE_I2:
622         case MONO_TYPE_U2:
623         case MONO_TYPE_I4:
624         case MONO_TYPE_U4:
625         case MONO_TYPE_I:
626         case MONO_TYPE_U: {
627                 int eax = va_arg (ap, int);
628                 printf ("result=%d", eax);
629                 break;
630         }
631         case MONO_TYPE_STRING: {
632                 MonoString *s = va_arg (ap, MonoString *);
633 ;
634                 if (s) {
635                         char *as;
636
637                         g_assert (((MonoObject *)s)->vtable->klass == mono_defaults.string_class);
638                         as = string_to_utf8 (s);
639                         printf ("[STRING:%p:%s]", s, as);
640                         g_free (as);
641                 } else 
642                         printf ("[STRING:null], ");
643                 break;
644         }
645         case MONO_TYPE_CLASS: 
646         case MONO_TYPE_OBJECT: {
647                 MonoObject *o = va_arg (ap, MonoObject *);
648
649                 if (o) {
650                         if (o->vtable->klass == mono_defaults.boolean_class) {
651                                 printf ("[BOOLEAN:%p:%d]", o, *((guint8 *)o + sizeof (MonoObject)));            
652                         } else if  (o->vtable->klass == mono_defaults.int32_class) {
653                                 printf ("[INT32:%p:%d]", o, *((gint32 *)((char *)o + sizeof (MonoObject))));    
654                         } else if  (o->vtable->klass == mono_defaults.int64_class) {
655                                 printf ("[INT64:%p:%lld]", o, (long long)*((gint64 *)((char *)o + sizeof (MonoObject))));       
656                         } else
657                                 printf ("[%s.%s:%p]", o->vtable->klass->name_space, o->vtable->klass->name, o);
658                 } else
659                         printf ("[OBJECT:%p]", o);
660                
661                 break;
662         }
663         case MONO_TYPE_PTR:
664         case MONO_TYPE_FNPTR:
665         case MONO_TYPE_ARRAY:
666         case MONO_TYPE_SZARRAY: {
667                 gpointer p = va_arg (ap, gpointer);
668                 printf ("result=%p", p);
669                 break;
670         }
671         case MONO_TYPE_I8: {
672                 gint64 l =  va_arg (ap, gint64);
673                 printf ("lresult=0x%16llx", (long long)l);
674                 break;
675         }
676         case MONO_TYPE_U8: {
677                 gint64 l =  va_arg (ap, gint64);
678                 printf ("lresult=0x%16llx", (long long)l);
679                 break;
680         }
681         case MONO_TYPE_R4:
682         case MONO_TYPE_R8: {
683                 double f = va_arg (ap, double);
684                 printf ("FP=%f", f);
685                 break;
686         }
687         case MONO_TYPE_VALUETYPE:  {
688                 guint8 *p = (guint8 *)va_arg (ap, gpointer);
689                 int j, size, align;
690                 size = mono_type_size (type, &align);
691                 printf ("[");
692                 for (j = 0; p && j < size; j++)
693                         printf ("%02x,", p [j]);
694                 printf ("]");
695                 break;
696         }
697         default:
698                 printf ("(unknown return type %x)", mono_method_signature (method)->ret->type);
699         }
700
701         //printf (" ip: %p\n", MONO_RETURN_ADDRESS_N (1));
702         printf ("\n");
703         fflush (stdout);
704
705 unlock:
706         mono_atomic_store_release (&output_lock, 0);
707 }
708
709 void
710 mono_trace_enable (gboolean enable)
711 {
712         trace_spec.enabled = enable;
713 }
714
715 gboolean
716 mono_trace_is_enabled ()
717 {
718         return trace_spec.enabled;
719 }