2005-11-27 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / mini / trace.c
1 /*
2  * trace.c: Tracing facilities for the Mono Runtime.
3  *
4  * Author:
5  *   Paolo Molaro (lupus@ximian.com)
6  *   Dietmar Maurer (dietmar@ximian.com)
7  *
8  * (C) 2002 Ximian, Inc.
9  */
10
11 #include <config.h>
12 #include <signal.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include "mini.h"
16 #include <mono/metadata/debug-helpers.h>
17 #include <mono/metadata/assembly.h>
18 #include "trace.h"
19
20 static MonoTraceSpec trace_spec;
21
22 gboolean
23 mono_trace_eval (MonoMethod *method)
24 {
25         int include = 0;
26         int i;
27
28         for (i = 0; i < trace_spec.len; i++){
29                 MonoTraceOperation *op = &trace_spec.ops [i];
30                 int inc = 0;
31                 
32                 switch (op->op){
33                 case MONO_TRACEOP_ALL:
34                         inc = 1; break;
35                 case MONO_TRACEOP_PROGRAM:
36                         if (trace_spec.assembly && (method->klass->image == mono_assembly_get_image (trace_spec.assembly)))
37                                 inc = 1; break;
38                 case MONO_TRACEOP_METHOD:
39                         if (mono_method_desc_full_match ((MonoMethodDesc *) op->data, method))
40                                 inc = 1; break;
41                 case MONO_TRACEOP_CLASS:
42                         if (strcmp (method->klass->name_space, op->data) == 0)
43                                 if (strcmp (method->klass->name, op->data2) == 0)
44                                         inc = 1;
45                         break;
46                 case MONO_TRACEOP_ASSEMBLY:
47                         if (strcmp (mono_image_get_name (method->klass->image), op->data) == 0)
48                                 inc = 1; break;
49                 case MONO_TRACEOP_NAMESPACE:
50                         if (strcmp (method->klass->name_space, op->data) == 0)
51                                 inc = 1;
52                 }
53                 if (op->exclude){
54                         if (inc)
55                                 include = 0;
56                 } else if (inc)
57                         include = 1;
58         }
59         return include;
60 }
61
62 static int is_filenamechar (char p)
63 {
64         if (p >= 'A' && p <= 'Z')
65                 return TRUE;
66         if (p >= 'a' && p <= 'z')
67                 return TRUE;
68         if (p >= '0' && p <= '9')
69                 return TRUE;
70         if (p == '.' || p == ':' || p == '_')
71                 return TRUE;
72         return FALSE;
73 }
74
75 static char *input;
76 static char *value;
77
78 static void get_string (void)
79 {
80         char *start = input;
81         while (is_filenamechar (*input)){
82                 input++;
83         }
84         if (value != NULL)
85                 g_free (value);
86         value = g_malloc (input - start + 1);
87         strncpy (value, start, input-start);
88         value [input-start] = 0;
89 }
90
91 enum Token {
92         TOKEN_METHOD,
93         TOKEN_CLASS,
94         TOKEN_ALL,
95         TOKEN_PROGRAM,
96         TOKEN_NAMESPACE,
97         TOKEN_STRING,
98         TOKEN_EXCLUDE,
99         TOKEN_DISABLED,
100         TOKEN_SEPARATOR,
101         TOKEN_END,
102         TOKEN_ERROR
103 };
104
105 static int
106 get_token (void)
107 {
108         if (input [0] == '\0') {
109                 return TOKEN_END;
110         }
111         if (input [0] == 'M' && input [1] == ':'){
112                 input += 2;
113                 get_string ();
114                 return TOKEN_METHOD;
115         }
116         if (input [0] == 'N' && input [1] == ':'){
117                 input += 2;
118                 get_string ();
119                 return TOKEN_NAMESPACE;
120         }
121         if (input [0] == 'T' && input [1] == ':'){
122                 input += 2;
123                 get_string ();
124                 return TOKEN_CLASS;
125         }
126         if (is_filenamechar (*input)){
127                 get_string ();
128                 if (strcmp (value, "all") == 0)
129                         return TOKEN_ALL;
130                 if (strcmp (value, "program") == 0)
131                         return TOKEN_PROGRAM;
132                 if (strcmp (value, "disabled") == 0)
133                         return TOKEN_DISABLED;
134                 return TOKEN_STRING;
135         }
136         if (*input == '-'){
137                 input++;
138                 return TOKEN_EXCLUDE;
139         }
140         if (*input == ','){
141                 input++;
142                 return TOKEN_SEPARATOR;
143         }
144
145         fprintf (stderr, "Syntax error at or around '%s'\n", input);    
146         return TOKEN_ERROR;
147 }
148
149 static void
150 cleanup (void)
151 {
152         if (value != NULL)
153                 g_free (value);
154 }
155
156 static int
157 get_spec (int *last)
158 {
159         int token = get_token ();
160         if (token == TOKEN_EXCLUDE){
161                 token = get_spec (last);
162                 if (token == TOKEN_EXCLUDE){
163                         fprintf (stderr, "Expecting an expression");
164                         return TOKEN_ERROR;
165                 }
166                 if (token == TOKEN_ERROR)
167                         return token;
168                 trace_spec.ops [(*last)-1].exclude = 1;
169                 return TOKEN_SEPARATOR;
170         }
171         if (token == TOKEN_END || token == TOKEN_SEPARATOR || token == TOKEN_ERROR)
172                 return token;
173         
174         if (token == TOKEN_METHOD){
175                 MonoMethodDesc *desc = mono_method_desc_new (value, TRUE);
176                 if (desc == NULL){
177                         fprintf (stderr, "Invalid method name: %s\n", value);
178                         return TOKEN_ERROR;
179                 }
180                 trace_spec.ops [*last].op = MONO_TRACEOP_METHOD;
181                 trace_spec.ops [*last].data = desc;
182         } else if (token == TOKEN_ALL)
183                 trace_spec.ops [*last].op = MONO_TRACEOP_ALL;
184         else if (token == TOKEN_PROGRAM)
185                 trace_spec.ops [*last].op = MONO_TRACEOP_PROGRAM;
186         else if (token == TOKEN_NAMESPACE){
187                 trace_spec.ops [*last].op = MONO_TRACEOP_NAMESPACE;
188                 trace_spec.ops [*last].data = g_strdup (value);
189         } else if (token == TOKEN_CLASS){
190                 char *p = strrchr (value, '.');
191                 if (p) {
192                         *p++ = 0;
193                         trace_spec.ops [*last].data = g_strdup (value);
194                         trace_spec.ops [*last].data2 = g_strdup (p);
195                 }
196                 else {
197                         trace_spec.ops [*last].data = g_strdup ("");
198                         trace_spec.ops [*last].data2 = g_strdup (value);
199                 }
200                 trace_spec.ops [*last].op = MONO_TRACEOP_CLASS;
201         } else if (token == TOKEN_STRING){
202                 trace_spec.ops [*last].op = MONO_TRACEOP_ASSEMBLY;
203                 trace_spec.ops [*last].data = g_strdup (value);
204         } else if (token == TOKEN_DISABLED) {
205                 trace_spec.enabled = FALSE;
206         } else {
207                 fprintf (stderr, "Syntax error in trace option specification\n");
208                 return TOKEN_ERROR;
209         }
210         (*last)++;
211         return TOKEN_SEPARATOR;
212 }
213
214 MonoTraceSpec *
215 mono_trace_parse_options (char *options)
216 {
217         char *p = options;
218         int size = 1;
219         int last_used;
220         int token;
221
222         trace_spec.enabled = TRUE;
223         if (*p == 0){
224                 trace_spec.len = 1;
225                 trace_spec.ops = g_new0 (MonoTraceOperation, 1);
226                 trace_spec.ops [0].op = MONO_TRACEOP_ALL;
227                 return &trace_spec;
228         }
229                 
230         for (p = options; *p != 0; p++)
231                 if (*p == ',')
232                         size++;
233         
234         trace_spec.ops = g_new0 (MonoTraceOperation, size);
235
236         input = options;
237         last_used = 0;
238         
239         while ((token = (get_spec (&last_used))) != TOKEN_END){
240                 if (token == TOKEN_ERROR)
241                         return NULL;
242                 if (token == TOKEN_SEPARATOR)
243                         continue;
244         }
245         trace_spec.len = last_used;
246         cleanup ();
247         return &trace_spec;
248 }
249
250 void
251 mono_trace_set_assembly (MonoAssembly *assembly)
252 {
253         trace_spec.assembly = assembly;
254 }
255
256 static int indent_level = 0;
257
258 static void indent (int diff) {
259         int v;
260         if (diff < 0)
261                 indent_level += diff;
262         v = indent_level;
263         while (v-- > 0) {
264                 printf (". ");
265         }
266         if (diff > 0)
267                 indent_level += diff;
268 }
269
270 void
271 mono_trace_enter_method (MonoMethod *method, char *ebp)
272 {
273         int i, j;
274         MonoClass *class;
275         MonoObject *o;
276         MonoJitArgumentInfo *arg_info;
277         MonoMethodSignature *sig;
278         char *fname;
279
280         if (!trace_spec.enabled)
281                 return;
282
283         fname = mono_method_full_name (method, TRUE);
284         indent (1);
285         printf ("ENTER: %s(", fname);
286         g_free (fname);
287
288         if (!ebp) {
289                 printf (") ip: %p\n", __builtin_return_address (1));
290                 return;
291         }       
292         if ((GPOINTER_TO_INT (ebp) & (MONO_ARCH_FRAME_ALIGNMENT - 1)) != 0) {
293                 g_error ("unaligned stack detected (%p)", ebp);
294         }
295
296         sig = mono_method_signature (method);
297
298         arg_info = alloca (sizeof (MonoJitArgumentInfo) * (sig->param_count + 1));
299
300         mono_arch_get_argument_info (sig, sig->param_count, arg_info);
301
302         if (MONO_TYPE_ISSTRUCT (mono_method_signature (method)->ret)) {
303                 g_assert (!mono_method_signature (method)->ret->byref);
304
305                 printf ("VALUERET:%p, ", *((gpointer *)(ebp + 8)));
306         }
307
308         if (mono_method_signature (method)->hasthis) {
309                 gpointer *this = (gpointer *)(ebp + arg_info [0].offset);
310                 if (method->klass->valuetype) {
311                         printf ("value:%p, ", *this);
312                 } else {
313                         o = *((MonoObject **)this);
314
315                         if (o) {
316                                 class = o->vtable->klass;
317
318                                 if (class == mono_defaults.string_class) {
319                                         printf ("this:[STRING:%p:%s], ", o, mono_string_to_utf8 ((MonoString *)o));
320                                 } else {
321                                         printf ("this:%p[%s.%s %s], ", o, class->name_space, class->name, o->vtable->domain->friendly_name);
322                                 }
323                         } else 
324                                 printf ("this:NULL, ");
325                 }
326         }
327
328         for (i = 0; i < mono_method_signature (method)->param_count; ++i) {
329                 gpointer *cpos = (gpointer *)(ebp + arg_info [i + 1].offset);
330                 int size = arg_info [i + 1].size;
331
332                 MonoType *type = mono_method_signature (method)->params [i];
333                 
334                 if (type->byref) {
335                         printf ("[BYREF:%p], ", *cpos); 
336                 } else switch (mono_type_get_underlying_type (type)->type) {
337                         
338                 case MONO_TYPE_I:
339                 case MONO_TYPE_U:
340                         printf ("%p, ", *((gpointer **)(cpos)));
341                         break;
342                 case MONO_TYPE_BOOLEAN:
343                 case MONO_TYPE_CHAR:
344                 case MONO_TYPE_I1:
345                 case MONO_TYPE_U1:
346                         printf ("%d, ", *((gint8 *)(cpos)));
347                         break;
348                 case MONO_TYPE_I2:
349                 case MONO_TYPE_U2:
350                         printf ("%d, ", *((gint16 *)(cpos)));
351                         break;
352                 case MONO_TYPE_I4:
353                 case MONO_TYPE_U4:
354                         printf ("%d, ", *((int *)(cpos)));
355                         break;
356                 case MONO_TYPE_STRING: {
357                         MonoString *s = *((MonoString **)cpos);
358                         if (s) {
359                                 g_assert (((MonoObject *)s)->vtable->klass == mono_defaults.string_class);
360                                 printf ("[STRING:%p:%s], ", s, mono_string_to_utf8 (s));
361                         } else 
362                                 printf ("[STRING:null], ");
363                         break;
364                 }
365                 case MONO_TYPE_CLASS:
366                 case MONO_TYPE_OBJECT: {
367                         o = *((MonoObject **)cpos);
368                         if (o) {
369                                 class = o->vtable->klass;
370                     
371                                 if (class == mono_defaults.string_class) {
372                                         printf ("[STRING:%p:%s], ", o, mono_string_to_utf8 ((MonoString *)o));
373                                 } else if (class == mono_defaults.int32_class) {
374                                         printf ("[INT32:%p:%d], ", o, *(gint32 *)((char *)o + sizeof (MonoObject)));
375                                 } else
376                                         printf ("[%s.%s:%p], ", class->name_space, class->name, o);
377                         } else {
378                                 printf ("%p, ", *((gpointer *)(cpos)));                         
379                         }
380                         break;
381                 }
382                 case MONO_TYPE_PTR:
383                 case MONO_TYPE_FNPTR:
384                 case MONO_TYPE_ARRAY:
385                 case MONO_TYPE_SZARRAY:
386                         printf ("%p, ", *((gpointer *)(cpos)));
387                         break;
388                 case MONO_TYPE_I8:
389                 case MONO_TYPE_U8:
390                         printf ("0x%016llx, ", (long long)*((gint64 *)(cpos)));
391                         break;
392                 case MONO_TYPE_R4:
393                         printf ("%f, ", *((float *)(cpos)));
394                         break;
395                 case MONO_TYPE_R8:
396                         printf ("%f, ", *((double *)(cpos)));
397                         break;
398                 case MONO_TYPE_VALUETYPE: 
399                         printf ("[");
400                         for (j = 0; j < size; j++)
401                                 printf ("%02x,", *((guint8*)cpos +j));
402                         printf ("], ");
403                         break;
404                 default:
405                         printf ("XX, ");
406                 }
407         }
408
409         printf (")\n");
410 }
411
412 void
413 mono_trace_leave_method (MonoMethod *method, ...)
414 {
415         MonoType *type;
416         char *fname;
417         va_list ap;
418
419         if (!trace_spec.enabled)
420                 return;
421
422         va_start(ap, method);
423
424         fname = mono_method_full_name (method, TRUE);
425         indent (-1);
426         printf ("LEAVE: %s", fname);
427         g_free (fname);
428
429         type = mono_method_signature (method)->ret;
430
431 handle_enum:
432         switch (type->type) {
433         case MONO_TYPE_VOID:
434                 break;
435         case MONO_TYPE_BOOLEAN: {
436                 int eax = va_arg (ap, int);
437                 if (eax)
438                         printf ("TRUE:%d", eax);
439                 else 
440                         printf ("FALSE");
441                         
442                 break;
443         }
444         case MONO_TYPE_CHAR:
445         case MONO_TYPE_I1:
446         case MONO_TYPE_U1:
447         case MONO_TYPE_I2:
448         case MONO_TYPE_U2:
449         case MONO_TYPE_I4:
450         case MONO_TYPE_U4:
451         case MONO_TYPE_I:
452         case MONO_TYPE_U: {
453                 int eax = va_arg (ap, int);
454                 printf ("result=%d", eax);
455                 break;
456         }
457         case MONO_TYPE_STRING: {
458                 MonoString *s = va_arg (ap, MonoString *);
459 ;
460                 if (s) {
461                         g_assert (((MonoObject *)s)->vtable->klass == mono_defaults.string_class);
462                         printf ("[STRING:%p:%s]", s, mono_string_to_utf8 (s));
463                 } else 
464                         printf ("[STRING:null], ");
465                 break;
466         }
467         case MONO_TYPE_CLASS: 
468         case MONO_TYPE_OBJECT: {
469                 MonoObject *o = va_arg (ap, MonoObject *);
470
471                 if (o) {
472                         if (o->vtable->klass == mono_defaults.boolean_class) {
473                                 printf ("[BOOLEAN:%p:%d]", o, *((guint8 *)o + sizeof (MonoObject)));            
474                         } else if  (o->vtable->klass == mono_defaults.int32_class) {
475                                 printf ("[INT32:%p:%d]", o, *((gint32 *)((char *)o + sizeof (MonoObject))));    
476                         } else if  (o->vtable->klass == mono_defaults.int64_class) {
477                                 printf ("[INT64:%p:%lld]", o, (long long)*((gint64 *)((char *)o + sizeof (MonoObject))));       
478                         } else
479                                 printf ("[%s.%s:%p]", o->vtable->klass->name_space, o->vtable->klass->name, o);
480                 } else
481                         printf ("[OBJECT:%p]", o);
482                
483                 break;
484         }
485         case MONO_TYPE_PTR:
486         case MONO_TYPE_FNPTR:
487         case MONO_TYPE_ARRAY:
488         case MONO_TYPE_SZARRAY: {
489                 gpointer p = va_arg (ap, gpointer);
490                 printf ("result=%p", p);
491                 break;
492         }
493         case MONO_TYPE_I8: {
494                 gint64 l =  va_arg (ap, gint64);
495                 printf ("lresult=0x%16llx", (long long)l);
496                 break;
497         }
498         case MONO_TYPE_U8: {
499                 gint64 l =  va_arg (ap, gint64);
500                 printf ("lresult=0x%16llx", (long long)l);
501                 break;
502         }
503         case MONO_TYPE_R4:
504         case MONO_TYPE_R8: {
505                 double f = va_arg (ap, double);
506                 printf ("FP=%f\n", f);
507                 break;
508         }
509         case MONO_TYPE_VALUETYPE: 
510                 if (type->data.klass->enumtype) {
511                         type = type->data.klass->enum_basetype;
512                         goto handle_enum;
513                 } else {
514                         guint8 *p = va_arg (ap, gpointer);
515                         int j, size, align;
516                         size = mono_type_size (type, &align);
517                         printf ("[");
518                         for (j = 0; p && j < size; j++)
519                                 printf ("%02x,", p [j]);
520                         printf ("]");
521                 }
522                 break;
523         default:
524                 printf ("(unknown return type %x)", mono_method_signature (method)->ret->type);
525         }
526
527         //printf (" ip: %p\n", __builtin_return_address (1));
528         printf ("\n");
529 }
530
531 void
532 mono_trace_enable (gboolean enable)
533 {
534         trace_spec.enabled = enable;
535 }
536
537 gboolean
538 mono_trace_is_enabled ()
539 {
540         return trace_spec.enabled;
541 }
542