Grasshopper project system now uses csproj extension
[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                 append_class_name (res, type->data.array->eklass, 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 gboolean
302 mono_method_desc_full_match (MonoMethodDesc *desc, MonoMethod *method)
303 {
304         if (strcmp (desc->klass, method->klass->name))
305                 return FALSE;
306         if (desc->namespace && strcmp (desc->namespace, method->klass->name_space))
307                 return FALSE;
308         return mono_method_desc_match (desc, method);
309 }
310
311 MonoMethod*
312 mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass)
313 {
314         MonoMethod* m;
315         gpointer iter = NULL;
316         
317         while ((m = mono_class_get_methods (klass, &iter)))
318                 if (mono_method_desc_match (desc, m))
319                         return m;
320         return NULL;
321 }
322
323 MonoMethod*
324 mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image)
325 {
326         MonoClass *klass;
327         const MonoTableInfo *tdef;
328         const MonoTableInfo *methods;
329         MonoMethod *method;
330         int i;
331
332         if (desc->namespace && desc->klass) {
333                 klass = mono_class_from_name (image, desc->namespace, desc->klass);
334                 if (!klass)
335                         return NULL;
336                 return mono_method_desc_search_in_class (desc, klass);
337         }
338
339         tdef = mono_image_get_table_info (image, MONO_TABLE_TYPEDEF);
340         methods = mono_image_get_table_info (image, MONO_TABLE_METHOD);
341         for (i = 0; i < mono_table_info_get_rows (methods); ++i) {
342                 guint32 token = mono_metadata_decode_row_col (methods, i, MONO_METHOD_NAME);
343                 const char *n = mono_metadata_string_heap (image, token);
344
345                 if (strcmp (n, desc->name))
346                         continue;
347                 method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL);
348                 if (mono_method_desc_full_match (desc, method))
349                         return method;
350         }
351         return NULL;
352 }
353
354 static const unsigned char*
355 dis_one (GString *str, MonoDisHelper *dh, MonoMethod *method, const unsigned char *ip, const unsigned char *end)
356 {
357         MonoMethodHeader *header = mono_method_get_header (method);
358         const MonoOpcode *opcode;
359         guint32 label, token;
360         gint32 sval;
361         int i;
362         char *tmp;
363         const unsigned char* il_code = mono_method_header_get_code (header, NULL, NULL);
364
365         label = ip - il_code;
366         if (dh->indenter) {
367                 tmp = dh->indenter (dh, method, label);
368                 g_string_append (str, tmp);
369                 g_free (tmp);
370         }
371         if (dh->label_format)
372                 g_string_sprintfa (str, dh->label_format, label);
373         
374         i = mono_opcode_value (&ip, end);
375         ip++;
376         opcode = &mono_opcodes [i];
377         g_string_sprintfa (str, "%-10s", mono_opcode_name (i));
378
379         switch (opcode->argument) {
380         case MonoInlineNone:
381                 break;
382         case MonoInlineType:
383         case MonoInlineField:
384         case MonoInlineMethod:
385         case MonoInlineTok:
386         case MonoInlineSig:
387                 token = read32 (ip);
388                 if (dh->tokener) {
389                         tmp = dh->tokener (dh, method, token);
390                         g_string_append (str, tmp);
391                         g_free (tmp);
392                 } else {
393                         g_string_sprintfa (str, "0x%08x", token);
394                 }
395                 ip += 4;
396                 break;
397         case MonoInlineString:
398                 /* TODO */
399                 ip += 4;
400                 break;
401         case MonoInlineVar:
402                 g_string_sprintfa (str, "%d", read16 (ip));
403                 ip += 2;
404                 break;
405         case MonoShortInlineVar:
406                 g_string_sprintfa (str, "%d", (*ip));
407                 ip ++;
408                 break;
409         case MonoInlineBrTarget:
410                 sval = read32 (ip);
411                 ip += 4;
412                 if (dh->label_target)
413                         g_string_sprintfa (str, dh->label_target, ip + sval - il_code);
414                 else
415                         g_string_sprintfa (str, "%d", sval);
416                 break;
417         case MonoShortInlineBrTarget:
418                 sval = *(const signed char*)ip;
419                 ip ++;
420                 if (dh->label_target)
421                         g_string_sprintfa (str, dh->label_target, ip + sval - il_code);
422                 else
423                         g_string_sprintfa (str, "%d", sval);
424                 break;
425         case MonoInlineSwitch: {
426                 const unsigned char *end;
427                 sval = read32 (ip);
428                 ip += 4;
429                 end = ip + sval * 4;
430                 g_string_append_c (str, '(');
431                 for (i = 0; i < sval; ++i) {
432                         if (i > 0)
433                                 g_string_append (str, ", ");
434                         label = read32 (ip);
435                         if (dh->label_target)
436                                 g_string_sprintfa (str, dh->label_target, end + label - il_code);
437                         else
438                                 g_string_sprintfa (str, "%d", label);
439                         ip += 4;
440                 }
441                 g_string_append_c (str, ')');
442                 break;
443         }
444         case MonoInlineR: {
445                 double r;
446                 readr8 (ip, &r);
447                 g_string_sprintfa (str, "%g", r);
448                 ip += 8;
449                 break;
450         }
451         case MonoShortInlineR: {
452                 float r;
453                 readr4 (ip, &r);
454                 g_string_sprintfa (str, "%g", r);
455                 ip += 4;
456                 break;
457         }
458         case MonoInlineI:
459                 g_string_sprintfa (str, "%d", (gint32)read32 (ip));
460                 ip += 4;
461                 break;
462         case MonoShortInlineI:
463                 g_string_sprintfa (str, "%d", *(const signed char*)ip);
464                 ip ++;
465                 break;
466         case MonoInlineI8:
467                 ip += 8;
468                 break;
469         default:
470                 g_assert_not_reached ();
471         }
472         if (dh->newline)
473                 g_string_append (str, dh->newline);
474
475         return ip;
476 }
477
478 static MonoDisHelper
479 default_dh = {
480         "\n",
481         "IL_%04x: ", /* label_format */
482         "IL_%04x", /* label_target */
483         NULL, /* indenter */
484         NULL, /* tokener */
485         NULL  /* user data */
486 };
487
488 char*
489 mono_disasm_code_one (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar **endp)
490 {
491         char *result;
492         GString *res = g_string_new ("");
493
494         if (!dh)
495                 dh = &default_dh;
496         /* set ip + 2 as the end: this is just a debugging method */
497         ip = dis_one (res, dh, method, ip, ip + 2);
498         if (endp)
499                 *endp = ip;
500         
501         result = res->str;
502         g_string_free (res, FALSE);
503         return result;
504 }
505
506 char*
507 mono_disasm_code (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar* end)
508 {
509         char *result;
510         GString *res = g_string_new ("");
511
512         if (!dh)
513                 dh = &default_dh;
514         while (ip < end) {
515                 ip = dis_one (res, dh, method, ip, end);
516         }
517         
518         result = res->str;
519         g_string_free (res, FALSE);
520         return result;
521 }
522
523 char *
524 mono_method_full_name (MonoMethod *method, gboolean signature)
525 {
526         char *res;
527         char wrapper [64];
528         const char *nspace = method->klass->name_space;
529
530         if (signature) {
531                 char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE);
532
533                 if (method->wrapper_type != MONO_WRAPPER_NONE)
534                         sprintf (wrapper, "(wrapper %s) ", wrapper_type_to_str (method->wrapper_type));
535                 else
536                         strcpy (wrapper, "");
537                 res = g_strdup_printf ("%s%s%s%s:%s (%s)", wrapper, 
538                                                            nspace, *nspace ? "." : "",
539                                                            method->klass->name, method->name, tmpsig);
540                 g_free (tmpsig);
541         } else {
542
543                 res = g_strdup_printf ("%02d %s%s%s:%s", method->wrapper_type,
544                                                            nspace, *nspace ? "." : "",
545                                                            method->klass->name, method->name);
546         }
547
548         return res;
549 }
550
551 static const char*
552 print_name_space (MonoClass *klass)
553 {
554         if (klass->nested_in) {
555                 print_name_space (klass->nested_in);
556                 g_print (klass->nested_in->name);
557                 return "/";
558         }
559         if (klass->name_space [0]) {
560                 g_print (klass->name_space);
561                 return ".";
562         }
563         return "";
564 }
565
566 /**
567  * mono_object_describe:
568  *
569  * Prints to stdout a small description of the object @obj.
570  * For use in a debugger.
571  */
572 void
573 mono_object_describe (MonoObject *obj)
574 {
575         MonoClass* klass;
576         const char* sep;
577         if (!obj) {
578                 g_print ("(null)\n");
579                 return;
580         }
581         klass = mono_object_class (obj);
582         if (klass == mono_defaults.string_class) {
583                 char *utf8 = mono_string_to_utf8 ((MonoString*)obj);
584                 if (strlen (utf8) > 60) {
585                         utf8 [57] = '.';
586                         utf8 [58] = '.';
587                         utf8 [59] = '.';
588                         utf8 [60] = 0;
589                 }
590                 g_print ("String at %p, length: %d, '%s'\n", obj, mono_string_length ((MonoString*) obj), utf8);
591                 g_free (utf8);
592         } else if (klass->rank) {
593                 MonoArray *array = (MonoArray*)obj;
594                 sep = print_name_space (klass);
595                 g_print ("%s%s", sep, klass->name);
596                 g_print (" at %p, rank: %d, length: %d\n", obj, klass->rank, mono_array_length (array));
597         } else {
598                 sep = print_name_space (klass);
599                 g_print ("%s%s", sep, klass->name);
600                 g_print (" object at %p (klass: %p)\n", obj, klass);
601         }
602
603 }
604
605 static void
606 print_field_value (const char *field_ptr, MonoClassField *field, int type_offset)
607 {
608         MonoType *type;
609         g_print ("At %p (ofs: %2d) %s: ", field_ptr, field->offset + type_offset, field->name);
610         type = mono_type_get_underlying_type (field->type);
611
612         switch (type->type) {
613         case MONO_TYPE_I:
614         case MONO_TYPE_U:
615         case MONO_TYPE_PTR:
616         case MONO_TYPE_FNPTR:
617                 g_print ("%p\n", *(const void**)field_ptr);
618                 break;
619         case MONO_TYPE_STRING:
620         case MONO_TYPE_SZARRAY:
621         case MONO_TYPE_CLASS:
622         case MONO_TYPE_OBJECT:
623         case MONO_TYPE_ARRAY:
624                 mono_object_describe (*(MonoObject**)field_ptr);
625                 break;
626         case MONO_TYPE_GENERICINST:
627                 if (!mono_type_generic_inst_is_valuetype (type)) {
628                         mono_object_describe (*(MonoObject**)field_ptr);
629                         break;
630                 } else {
631                         /* fall through */
632                 }
633         case MONO_TYPE_VALUETYPE: {
634                 MonoClass *k = mono_class_from_mono_type (type);
635                 g_print ("%s ValueType (type: %p) at %p\n", k->name, k, field_ptr);
636                 break;
637         }
638         case MONO_TYPE_I1:
639                 g_print ("%d\n", *(gint8*)field_ptr);
640                 break;
641         case MONO_TYPE_U1:
642                 g_print ("%d\n", *(guint8*)field_ptr);
643                 break;
644         case MONO_TYPE_I2:
645                 g_print ("%d\n", *(gint16*)field_ptr);
646                 break;
647         case MONO_TYPE_U2:
648                 g_print ("%d\n", *(guint16*)field_ptr);
649                 break;
650         case MONO_TYPE_I4:
651                 g_print ("%d\n", *(gint32*)field_ptr);
652                 break;
653         case MONO_TYPE_U4:
654                 g_print ("%u\n", *(guint32*)field_ptr);
655                 break;
656         case MONO_TYPE_I8:
657                 g_print ("%lld\n", *(gint64*)field_ptr);
658                 break;
659         case MONO_TYPE_U8:
660                 g_print ("%llu\n", *(guint64*)field_ptr);
661                 break;
662         case MONO_TYPE_R4:
663                 g_print ("%f\n", *(gfloat*)field_ptr);
664                 break;
665         case MONO_TYPE_R8:
666                 g_print ("%f\n", *(gdouble*)field_ptr);
667                 break;
668         case MONO_TYPE_BOOLEAN:
669                 g_print ("%s (%d)\n", *(guint8*)field_ptr? "True": "False", *(guint8*)field_ptr);
670                 break;
671         case MONO_TYPE_CHAR:
672                 g_print ("'%c' (%d 0x%04x)\n", *(guint16*)field_ptr, *(guint16*)field_ptr, *(guint16*)field_ptr);
673                 break;
674         default:
675                 g_assert_not_reached ();
676                 break;
677         }
678 }
679
680 static void
681 objval_describe (MonoClass *class, const char *addr)
682 {
683         MonoClassField *field;
684         MonoClass *p;
685         const char *field_ptr;
686         int type_offset = 0;
687         if (class->valuetype)
688                 type_offset = -sizeof (MonoObject);
689
690         for (p = class; p != NULL; p = p->parent) {
691                 gpointer iter = NULL;
692                 int printed_header = FALSE;
693                 while ((field = mono_class_get_fields (p, &iter))) {
694                         if (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA))
695                                 continue;
696
697                         if (p != class && !printed_header) {
698                                 const char *sep;
699                                 g_print ("In class ");
700                                 sep = print_name_space (p);
701                                 g_print ("%s%s:\n", sep, p->name);
702                                 printed_header = TRUE;
703                         }
704                         field_ptr = (const char*)addr + field->offset + type_offset;
705
706                         print_field_value (field_ptr, field, type_offset);
707                 }
708         }
709 }
710
711 /**
712  * mono_object_describe_fields:
713  *
714  * Prints to stdout a small description of each field of the object @obj.
715  * For use in a debugger.
716  */
717 void
718 mono_object_describe_fields (MonoObject *obj)
719 {
720         MonoClass *class = mono_object_class (obj);
721         objval_describe (class, (char*)obj);
722 }
723
724 /**
725  * mono_value_describe_fields:
726  *
727  * Prints to stdout a small description of each field of the value type
728  * stored at @addr of type @klass.
729  * For use in a debugger.
730  */
731 void
732 mono_value_describe_fields (MonoClass* klass, const char* addr)
733 {
734         objval_describe (klass, addr);
735 }
736
737 /**
738  * mono_class_describe_statics:
739  *
740  * Prints to stdout a small description of each static field of the type @klass
741  * in the current application domain.
742  * For use in a debugger.
743  */
744 void
745 mono_class_describe_statics (MonoClass* klass)
746 {
747         MonoClassField *field;
748         MonoClass *p;
749         const char *field_ptr;
750         const char *addr = mono_class_vtable (mono_domain_get (), klass)->data;
751         if (!addr)
752                 return;
753
754         for (p = klass; p != NULL; p = p->parent) {
755                 gpointer iter = NULL;
756                 while ((field = mono_class_get_fields (p, &iter))) {
757                         if (field->type->attrs & FIELD_ATTRIBUTE_LITERAL)
758                                 continue;
759                         if (!(field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA)))
760                                 continue;
761
762                         field_ptr = (const char*)addr + field->offset;
763
764                         print_field_value (field_ptr, field, 0);
765                 }
766         }
767 }
768