in mono/mono/mini:
[mono.git] / mono / metadata / debug-helpers.c
1
2 #include <string.h>
3 #include "mono/metadata/tokentype.h"
4 #include "mono/metadata/opcodes.h"
5 #include "mono/metadata/class-internals.h"
6 #include "mono/metadata/mono-endian.h"
7 #include "mono/metadata/debug-helpers.h"
8 #include "mono/metadata/tabledefs.h"
9 #include "mono/metadata/appdomain.h"
10
11 struct MonoMethodDesc {
12         char *namespace;
13         char *klass;
14         char *name;
15         char *args;
16         guint num_args;
17         gboolean include_namespace;
18 };
19
20 #ifdef HAVE_ARRAY_ELEM_INIT
21 #define MSGSTRFIELD(line) MSGSTRFIELD1(line)
22 #define MSGSTRFIELD1(line) str##line
23 static const struct msgstr_t {
24 #define WRAPPER(a,b) char MSGSTRFIELD(__LINE__) [sizeof (b)];
25 #include "wrapper-types.h"
26 #undef WRAPPER
27 } opstr = {
28 #define WRAPPER(a,b) b,
29 #include "wrapper-types.h"
30 #undef WRAPPER
31 };
32 static const gint16 opidx [] = {
33 #define WRAPPER(a,b) [MONO_WRAPPER_ ## a] = offsetof (struct msgstr_t, MSGSTRFIELD(__LINE__)),
34 #include "wrapper-types.h"
35 #undef WRAPPER
36 };
37
38 static const char*
39 wrapper_type_to_str (guint32 wrapper_type)
40 {
41         g_assert (wrapper_type < MONO_WRAPPER_NUM);
42
43         return (const char*)&opstr + opidx [wrapper_type];
44 }
45
46 #else
47 #define WRAPPER(a,b) b,
48 static const char* const
49 wrapper_type_names [MONO_WRAPPER_NUM + 1] = {
50 #include "wrapper-types.h"
51         NULL
52 };
53
54 static const char*
55 wrapper_type_to_str (guint32 wrapper_type)
56 {
57         g_assert (wrapper_type < MONO_WRAPPER_NUM);
58
59         return wrapper_type_names [wrapper_type];
60 }
61
62 #endif
63
64 static void
65 append_class_name (GString *res, MonoClass *class, gboolean include_namespace)
66 {
67         if (!class) {
68                 g_string_append (res, "Unknown");
69                 return;
70         }
71         if (class->nested_in) {
72                 append_class_name (res, class->nested_in, include_namespace);
73                 g_string_append_c (res, '/');
74         }
75         if (include_namespace && *(class->name_space))
76                 g_string_sprintfa (res, "%s.", class->name_space);
77         g_string_sprintfa (res, "%s", class->name);
78 }
79
80 void
81 mono_type_get_desc (GString *res, MonoType *type, gboolean include_namespace) {
82         switch (type->type) {
83         case MONO_TYPE_VOID:
84                 g_string_append (res, "void"); break;
85         case MONO_TYPE_CHAR:
86                 g_string_append (res, "char"); break;
87         case MONO_TYPE_BOOLEAN:
88                 g_string_append (res, "bool"); break;
89         case MONO_TYPE_U1:
90                 g_string_append (res, "byte"); break;
91         case MONO_TYPE_I1:
92                 g_string_append (res, "sbyte"); break;
93         case MONO_TYPE_U2:
94                 g_string_append (res, "uint16"); break;
95         case MONO_TYPE_I2:
96                 g_string_append (res, "int16"); break;
97         case MONO_TYPE_U4:
98                 g_string_append (res, "uint"); break;
99         case MONO_TYPE_I4:
100                 g_string_append (res, "int"); break;
101         case MONO_TYPE_U8:
102                 g_string_append (res, "ulong"); break;
103         case MONO_TYPE_I8:
104                 g_string_append (res, "long"); break;
105         case MONO_TYPE_FNPTR: /* who cares for the exact signature? */
106                 g_string_append (res, "*()"); break;
107         case MONO_TYPE_U:
108                 g_string_append (res, "uintptr"); break;
109         case MONO_TYPE_I:
110                 g_string_append (res, "intptr"); break;
111         case MONO_TYPE_R4:
112                 g_string_append (res, "single"); break;
113         case MONO_TYPE_R8:
114                 g_string_append (res, "double"); break;
115         case MONO_TYPE_STRING:
116                 g_string_append (res, "string"); break;
117         case MONO_TYPE_OBJECT:
118                 g_string_append (res, "object"); break;
119         case MONO_TYPE_PTR:
120                 mono_type_get_desc (res, type->data.type, include_namespace);
121                 g_string_append_c (res, '*');
122                 break;
123         case MONO_TYPE_ARRAY:
124                 mono_type_get_desc (res, &type->data.array->eklass->byval_arg, include_namespace);
125                 g_string_sprintfa (res, "[%d]", type->data.array->rank);
126                 break;
127         case MONO_TYPE_SZARRAY:
128                 mono_type_get_desc (res, &type->data.klass->byval_arg, include_namespace);
129                 g_string_append (res, "[]");
130                 break;
131         case MONO_TYPE_CLASS:
132         case MONO_TYPE_VALUETYPE:
133                 append_class_name (res, type->data.klass, include_namespace);
134                 break;
135         case MONO_TYPE_GENERICINST:
136                 mono_type_get_desc (res, &type->data.generic_class->container_class->byval_arg, include_namespace);
137                 break;
138         case MONO_TYPE_VAR:
139         case MONO_TYPE_MVAR:
140                 g_string_append (res, type->data.generic_param->name);
141                 break;
142         default:
143                 break;
144         }
145         if (type->byref)
146                 g_string_append_c (res, '&');
147 }
148
149 char*
150 mono_type_full_name (MonoType *type)
151 {
152         GString *str;
153         char *res;
154
155         str = g_string_new ("");
156         mono_type_get_desc (str, type, TRUE);
157
158         res = g_strdup (str->str);
159         g_string_free (str, TRUE);
160         return res;
161 }
162
163 char*
164 mono_signature_get_desc (MonoMethodSignature *sig, gboolean include_namespace)
165 {
166         int i;
167         char *result;
168         GString *res = g_string_new ("");
169
170         for (i = 0; i < sig->param_count; ++i) {
171                 if (i > 0)
172                         g_string_append_c (res, ',');
173                 mono_type_get_desc (res, sig->params [i], include_namespace);
174         }
175         result = res->str;
176         g_string_free (res, FALSE);
177         return result;
178 }
179
180 /**
181  * mono_method_desc_new:
182  * @name: the method name.
183  * @include_namespace: whether the name includes a namespace or not.
184  *
185  * Creates a method description for @name, which conforms to the following
186  * specification:
187  *
188  * [namespace.]classname:methodname[(args...)]
189  *
190  * in all the loaded assemblies.
191  *
192  * Returns: a parsed representation of the method description.
193  */
194 MonoMethodDesc*
195 mono_method_desc_new (const char *name, gboolean include_namespace)
196 {
197         MonoMethodDesc *result;
198         char *class_name, *class_nspace, *method_name, *use_args, *end;
199         int use_namespace;
200         
201         class_nspace = g_strdup (name);
202         use_args = strchr (class_nspace, '(');
203         if (use_args) {
204                 *use_args++ = 0;
205                 end = strchr (use_args, ')');
206                 if (!end) {
207                         g_free (class_nspace);
208                         return NULL;
209                 }
210                 *end = 0;
211         }
212         method_name = strrchr (class_nspace, ':');
213         if (!method_name) {
214                 g_free (class_nspace);
215                 return NULL;
216         }
217         *method_name++ = 0;
218         /* allow two :: to separate the method name */
219         if (*method_name == ':')
220                 method_name++;
221         class_name = strrchr (class_nspace, '.');
222         if (class_name) {
223                 *class_name++ = 0;
224                 use_namespace = 1;
225         } else {
226                 class_name = class_nspace;
227                 use_namespace = 0;
228         }
229         result = g_new0 (MonoMethodDesc, 1);
230         result->include_namespace = include_namespace;
231         result->name = method_name;
232         result->klass = class_name;
233         result->namespace = use_namespace? class_nspace: NULL;
234         result->args = use_args? use_args: NULL;
235         if (use_args) {
236                 end = use_args;
237                 if (*end)
238                         result->num_args = 1;
239                 while (*end) {
240                         if (*end == ',')
241                                 result->num_args++;
242                         ++end;
243                 }
244         }
245
246         return result;
247 }
248
249 MonoMethodDesc*
250 mono_method_desc_from_method (MonoMethod *method)
251 {
252         MonoMethodDesc *result;
253         
254         result = g_new0 (MonoMethodDesc, 1);
255         result->include_namespace = TRUE;
256         result->name = g_strdup (method->name);
257         result->klass = g_strdup (method->klass->name);
258         result->namespace = g_strdup (method->klass->name_space);
259
260         return result;
261 }
262
263 /**
264  * mono_method_desc_free:
265  * @desc: method description to be released
266  *
267  * Releases the MonoMethodDesc object @desc.
268  */
269 void
270 mono_method_desc_free (MonoMethodDesc *desc)
271 {
272         if (desc->namespace)
273                 g_free (desc->namespace);
274         else if (desc->klass)
275                 g_free (desc->klass);
276         g_free (desc);
277 }
278
279 /*
280  * namespace and class are supposed to match already if this function is used.
281  */
282 gboolean
283 mono_method_desc_match (MonoMethodDesc *desc, MonoMethod *method)
284 {
285         char *sig;
286         if (strcmp (desc->name, method->name))
287                 return FALSE;
288         if (!desc->args)
289                 return TRUE;
290         if (desc->num_args != mono_method_signature (method)->param_count)
291                 return FALSE;
292         sig = mono_signature_get_desc (mono_method_signature (method), desc->include_namespace);
293         if (strcmp (sig, desc->args)) {
294                 g_free (sig);
295                 return FALSE;
296         }
297         g_free (sig);
298         return TRUE;
299 }
300
301 static const char *
302 my_strrchr (const char *str, char ch, int *len)
303 {
304         int pos;
305
306         for (pos = (*len)-1; pos >= 0; pos--) {
307                 if (str [pos] != ch)
308                         continue;
309
310                 *len = pos;
311                 return str + pos;
312         }
313
314         return NULL;
315 }
316
317 static gboolean
318 match_class (MonoMethodDesc *desc, int pos, MonoClass *klass)
319 {
320         const char *p;
321
322         p = my_strrchr (desc->klass, '/', &pos);
323         if (!p) {
324                 if (strncmp (desc->klass, klass->name, pos))
325                         return FALSE;
326                 if (desc->namespace && strcmp (desc->namespace, klass->name_space))
327                         return FALSE;
328                 return TRUE;
329         }
330
331         if (strcmp (p+1, klass->name))
332                 return FALSE;
333         if (!klass->nested_in)
334                 return FALSE;
335
336         return match_class (desc, pos, klass->nested_in);
337 }
338
339 gboolean
340 mono_method_desc_full_match (MonoMethodDesc *desc, MonoMethod *method)
341 {
342         if (!match_class (desc, strlen (desc->klass), method->klass))
343                 return FALSE;
344
345         return mono_method_desc_match (desc, method);
346 }
347
348 MonoMethod*
349 mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass)
350 {
351         MonoMethod* m;
352         gpointer iter = NULL;
353         
354         while ((m = mono_class_get_methods (klass, &iter)))
355                 if (mono_method_desc_match (desc, m))
356                         return m;
357         return NULL;
358 }
359
360 MonoMethod*
361 mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image)
362 {
363         MonoClass *klass;
364         const MonoTableInfo *tdef;
365         const MonoTableInfo *methods;
366         MonoMethod *method;
367         int i;
368
369         if (desc->namespace && desc->klass) {
370                 klass = mono_class_from_name (image, desc->namespace, desc->klass);
371                 if (!klass)
372                         return NULL;
373                 return mono_method_desc_search_in_class (desc, klass);
374         }
375
376         tdef = mono_image_get_table_info (image, MONO_TABLE_TYPEDEF);
377         methods = mono_image_get_table_info (image, MONO_TABLE_METHOD);
378         for (i = 0; i < mono_table_info_get_rows (methods); ++i) {
379                 guint32 token = mono_metadata_decode_row_col (methods, i, MONO_METHOD_NAME);
380                 const char *n = mono_metadata_string_heap (image, token);
381
382                 if (strcmp (n, desc->name))
383                         continue;
384                 method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL);
385                 if (mono_method_desc_full_match (desc, method))
386                         return method;
387         }
388         return NULL;
389 }
390
391 static const unsigned char*
392 dis_one (GString *str, MonoDisHelper *dh, MonoMethod *method, const unsigned char *ip, const unsigned char *end)
393 {
394         MonoMethodHeader *header = mono_method_get_header (method);
395         const MonoOpcode *opcode;
396         guint32 label, token;
397         gint32 sval;
398         int i;
399         char *tmp;
400         const unsigned char* il_code = mono_method_header_get_code (header, NULL, NULL);
401
402         label = ip - il_code;
403         if (dh->indenter) {
404                 tmp = dh->indenter (dh, method, label);
405                 g_string_append (str, tmp);
406                 g_free (tmp);
407         }
408         if (dh->label_format)
409                 g_string_sprintfa (str, dh->label_format, label);
410         
411         i = mono_opcode_value (&ip, end);
412         ip++;
413         opcode = &mono_opcodes [i];
414         g_string_sprintfa (str, "%-10s", mono_opcode_name (i));
415
416         switch (opcode->argument) {
417         case MonoInlineNone:
418                 break;
419         case MonoInlineType:
420         case MonoInlineField:
421         case MonoInlineMethod:
422         case MonoInlineTok:
423         case MonoInlineSig:
424                 token = read32 (ip);
425                 if (dh->tokener) {
426                         tmp = dh->tokener (dh, method, token);
427                         g_string_append (str, tmp);
428                         g_free (tmp);
429                 } else {
430                         g_string_sprintfa (str, "0x%08x", token);
431                 }
432                 ip += 4;
433                 break;
434         case MonoInlineString:
435                 /* TODO */
436                 ip += 4;
437                 break;
438         case MonoInlineVar:
439                 g_string_sprintfa (str, "%d", read16 (ip));
440                 ip += 2;
441                 break;
442         case MonoShortInlineVar:
443                 g_string_sprintfa (str, "%d", (*ip));
444                 ip ++;
445                 break;
446         case MonoInlineBrTarget:
447                 sval = read32 (ip);
448                 ip += 4;
449                 if (dh->label_target)
450                         g_string_sprintfa (str, dh->label_target, ip + sval - il_code);
451                 else
452                         g_string_sprintfa (str, "%d", sval);
453                 break;
454         case MonoShortInlineBrTarget:
455                 sval = *(const signed char*)ip;
456                 ip ++;
457                 if (dh->label_target)
458                         g_string_sprintfa (str, dh->label_target, ip + sval - il_code);
459                 else
460                         g_string_sprintfa (str, "%d", sval);
461                 break;
462         case MonoInlineSwitch: {
463                 const unsigned char *end;
464                 sval = read32 (ip);
465                 ip += 4;
466                 end = ip + sval * 4;
467                 g_string_append_c (str, '(');
468                 for (i = 0; i < sval; ++i) {
469                         if (i > 0)
470                                 g_string_append (str, ", ");
471                         label = read32 (ip);
472                         if (dh->label_target)
473                                 g_string_sprintfa (str, dh->label_target, end + label - il_code);
474                         else
475                                 g_string_sprintfa (str, "%d", label);
476                         ip += 4;
477                 }
478                 g_string_append_c (str, ')');
479                 break;
480         }
481         case MonoInlineR: {
482                 double r;
483                 readr8 (ip, &r);
484                 g_string_sprintfa (str, "%g", r);
485                 ip += 8;
486                 break;
487         }
488         case MonoShortInlineR: {
489                 float r;
490                 readr4 (ip, &r);
491                 g_string_sprintfa (str, "%g", r);
492                 ip += 4;
493                 break;
494         }
495         case MonoInlineI:
496                 g_string_sprintfa (str, "%d", (gint32)read32 (ip));
497                 ip += 4;
498                 break;
499         case MonoShortInlineI:
500                 g_string_sprintfa (str, "%d", *(const signed char*)ip);
501                 ip ++;
502                 break;
503         case MonoInlineI8:
504                 ip += 8;
505                 break;
506         default:
507                 g_assert_not_reached ();
508         }
509         if (dh->newline)
510                 g_string_append (str, dh->newline);
511
512         return ip;
513 }
514
515 static MonoDisHelper
516 default_dh = {
517         "\n",
518         "IL_%04x: ", /* label_format */
519         "IL_%04x", /* label_target */
520         NULL, /* indenter */
521         NULL, /* tokener */
522         NULL  /* user data */
523 };
524
525 char*
526 mono_disasm_code_one (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar **endp)
527 {
528         char *result;
529         GString *res = g_string_new ("");
530
531         if (!dh)
532                 dh = &default_dh;
533         /* set ip + 2 as the end: this is just a debugging method */
534         ip = dis_one (res, dh, method, ip, ip + 2);
535         if (endp)
536                 *endp = ip;
537         
538         result = res->str;
539         g_string_free (res, FALSE);
540         return result;
541 }
542
543 char*
544 mono_disasm_code (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar* end)
545 {
546         char *result;
547         GString *res = g_string_new ("");
548
549         if (!dh)
550                 dh = &default_dh;
551         while (ip < end) {
552                 ip = dis_one (res, dh, method, ip, end);
553         }
554         
555         result = res->str;
556         g_string_free (res, FALSE);
557         return result;
558 }
559
560 char *
561 mono_field_full_name (MonoClassField *field)
562 {
563         char *res;
564         const char *nspace = field->parent->name_space;
565
566         res = g_strdup_printf ("%s%s%s:%s", nspace, *nspace ? "." : "",
567                                                    field->parent->name, field->name);
568
569         return res;
570 }
571
572 char *
573 mono_method_full_name (MonoMethod *method, gboolean signature)
574 {
575         char *res;
576         char wrapper [64];
577         const char *nspace = method->klass->name_space;
578
579         if (signature) {
580                 char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE);
581
582                 if (method->wrapper_type != MONO_WRAPPER_NONE)
583                         sprintf (wrapper, "(wrapper %s) ", wrapper_type_to_str (method->wrapper_type));
584                 else
585                         strcpy (wrapper, "");
586                 res = g_strdup_printf ("%s%s%s%s:%s (%s)", wrapper, 
587                                                            nspace, *nspace ? "." : "",
588                                                            method->klass->name, method->name, tmpsig);
589                 g_free (tmpsig);
590         } else {
591
592                 res = g_strdup_printf ("%02d %s%s%s:%s", method->wrapper_type,
593                                                            nspace, *nspace ? "." : "",
594                                                            method->klass->name, method->name);
595         }
596
597         return res;
598 }
599
600 static const char*
601 print_name_space (MonoClass *klass)
602 {
603         if (klass->nested_in) {
604                 print_name_space (klass->nested_in);
605                 g_print (klass->nested_in->name);
606                 return "/";
607         }
608         if (klass->name_space [0]) {
609                 g_print (klass->name_space);
610                 return ".";
611         }
612         return "";
613 }
614
615 /**
616  * mono_object_describe:
617  *
618  * Prints to stdout a small description of the object @obj.
619  * For use in a debugger.
620  */
621 void
622 mono_object_describe (MonoObject *obj)
623 {
624         MonoClass* klass;
625         const char* sep;
626         if (!obj) {
627                 g_print ("(null)\n");
628                 return;
629         }
630         klass = mono_object_class (obj);
631         if (klass == mono_defaults.string_class) {
632                 char *utf8 = mono_string_to_utf8 ((MonoString*)obj);
633                 if (strlen (utf8) > 60) {
634                         utf8 [57] = '.';
635                         utf8 [58] = '.';
636                         utf8 [59] = '.';
637                         utf8 [60] = 0;
638                 }
639                 g_print ("String at %p, length: %d, '%s'\n", obj, mono_string_length ((MonoString*) obj), utf8);
640                 g_free (utf8);
641         } else if (klass->rank) {
642                 MonoArray *array = (MonoArray*)obj;
643                 sep = print_name_space (klass);
644                 g_print ("%s%s", sep, klass->name);
645                 g_print (" at %p, rank: %d, length: %d\n", obj, klass->rank, mono_array_length (array));
646         } else {
647                 sep = print_name_space (klass);
648                 g_print ("%s%s", sep, klass->name);
649                 g_print (" object at %p (klass: %p)\n", obj, klass);
650         }
651
652 }
653
654 static void
655 print_field_value (const char *field_ptr, MonoClassField *field, int type_offset)
656 {
657         MonoType *type;
658         g_print ("At %p (ofs: %2d) %s: ", field_ptr, field->offset + type_offset, field->name);
659         type = mono_type_get_underlying_type (field->type);
660
661         switch (type->type) {
662         case MONO_TYPE_I:
663         case MONO_TYPE_U:
664         case MONO_TYPE_PTR:
665         case MONO_TYPE_FNPTR:
666                 g_print ("%p\n", *(const void**)field_ptr);
667                 break;
668         case MONO_TYPE_STRING:
669         case MONO_TYPE_SZARRAY:
670         case MONO_TYPE_CLASS:
671         case MONO_TYPE_OBJECT:
672         case MONO_TYPE_ARRAY:
673                 mono_object_describe (*(MonoObject**)field_ptr);
674                 break;
675         case MONO_TYPE_GENERICINST:
676                 if (!mono_type_generic_inst_is_valuetype (type)) {
677                         mono_object_describe (*(MonoObject**)field_ptr);
678                         break;
679                 } else {
680                         /* fall through */
681                 }
682         case MONO_TYPE_VALUETYPE: {
683                 MonoClass *k = mono_class_from_mono_type (type);
684                 g_print ("%s ValueType (type: %p) at %p\n", k->name, k, field_ptr);
685                 break;
686         }
687         case MONO_TYPE_I1:
688                 g_print ("%d\n", *(gint8*)field_ptr);
689                 break;
690         case MONO_TYPE_U1:
691                 g_print ("%d\n", *(guint8*)field_ptr);
692                 break;
693         case MONO_TYPE_I2:
694                 g_print ("%d\n", *(gint16*)field_ptr);
695                 break;
696         case MONO_TYPE_U2:
697                 g_print ("%d\n", *(guint16*)field_ptr);
698                 break;
699         case MONO_TYPE_I4:
700                 g_print ("%d\n", *(gint32*)field_ptr);
701                 break;
702         case MONO_TYPE_U4:
703                 g_print ("%u\n", *(guint32*)field_ptr);
704                 break;
705         case MONO_TYPE_I8:
706                 g_print ("%lld\n", *(gint64*)field_ptr);
707                 break;
708         case MONO_TYPE_U8:
709                 g_print ("%llu\n", *(guint64*)field_ptr);
710                 break;
711         case MONO_TYPE_R4:
712                 g_print ("%f\n", *(gfloat*)field_ptr);
713                 break;
714         case MONO_TYPE_R8:
715                 g_print ("%f\n", *(gdouble*)field_ptr);
716                 break;
717         case MONO_TYPE_BOOLEAN:
718                 g_print ("%s (%d)\n", *(guint8*)field_ptr? "True": "False", *(guint8*)field_ptr);
719                 break;
720         case MONO_TYPE_CHAR:
721                 g_print ("'%c' (%d 0x%04x)\n", *(guint16*)field_ptr, *(guint16*)field_ptr, *(guint16*)field_ptr);
722                 break;
723         default:
724                 g_assert_not_reached ();
725                 break;
726         }
727 }
728
729 static void
730 objval_describe (MonoClass *class, const char *addr)
731 {
732         MonoClassField *field;
733         MonoClass *p;
734         const char *field_ptr;
735         int type_offset = 0;
736         if (class->valuetype)
737                 type_offset = -sizeof (MonoObject);
738
739         for (p = class; p != NULL; p = p->parent) {
740                 gpointer iter = NULL;
741                 int printed_header = FALSE;
742                 while ((field = mono_class_get_fields (p, &iter))) {
743                         if (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA))
744                                 continue;
745
746                         if (p != class && !printed_header) {
747                                 const char *sep;
748                                 g_print ("In class ");
749                                 sep = print_name_space (p);
750                                 g_print ("%s%s:\n", sep, p->name);
751                                 printed_header = TRUE;
752                         }
753                         field_ptr = (const char*)addr + field->offset + type_offset;
754
755                         print_field_value (field_ptr, field, type_offset);
756                 }
757         }
758 }
759
760 /**
761  * mono_object_describe_fields:
762  *
763  * Prints to stdout a small description of each field of the object @obj.
764  * For use in a debugger.
765  */
766 void
767 mono_object_describe_fields (MonoObject *obj)
768 {
769         MonoClass *class = mono_object_class (obj);
770         objval_describe (class, (char*)obj);
771 }
772
773 /**
774  * mono_value_describe_fields:
775  *
776  * Prints to stdout a small description of each field of the value type
777  * stored at @addr of type @klass.
778  * For use in a debugger.
779  */
780 void
781 mono_value_describe_fields (MonoClass* klass, const char* addr)
782 {
783         objval_describe (klass, addr);
784 }
785
786 /**
787  * mono_class_describe_statics:
788  *
789  * Prints to stdout a small description of each static field of the type @klass
790  * in the current application domain.
791  * For use in a debugger.
792  */
793 void
794 mono_class_describe_statics (MonoClass* klass)
795 {
796         MonoClassField *field;
797         MonoClass *p;
798         const char *field_ptr;
799         const char *addr = mono_class_vtable (mono_domain_get (), klass)->data;
800         if (!addr)
801                 return;
802
803         for (p = klass; p != NULL; p = p->parent) {
804                 gpointer iter = NULL;
805                 while ((field = mono_class_get_fields (p, &iter))) {
806                         if (field->type->attrs & FIELD_ATTRIBUTE_LITERAL)
807                                 continue;
808                         if (!(field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA)))
809                                 continue;
810
811                         field_ptr = (const char*)addr + field->offset;
812
813                         print_field_value (field_ptr, field, 0);
814                 }
815         }
816 }
817