Missing test.
[mono.git] / mono / jit / debug.c
1 #include <stdlib.h>
2 #include <string.h>
3 #include <mono/metadata/class.h>
4 #include <mono/metadata/tabledefs.h>
5 #include <mono/metadata/tokentype.h>
6 #include <mono/jit/codegen.h>
7
8 typedef struct {
9         FILE *f;
10         char *filename;
11         char *name;
12         int *mlines;
13         int nmethods;
14         int next_idx;
15 } AssemblyDebugInfo;
16
17 struct _MonoDebugHandle {
18         char *name;
19         GList *info;
20 };
21
22 #include "debug.h"
23
24 typedef struct {
25         char *name;
26         char *spec;
27 } BaseTypes;
28
29 /*
30  * Not 64 bit clean.
31  * Note: same order of MonoTypeEnum.
32  */
33 static BaseTypes
34 base_types[] = {
35         {"", NULL},
36         {"Void", "(0,1)"},
37         {"Boolean", ";0;255;"},
38         {"Char", ";0;65535;"},
39         {"SByte", ";-128;127;"},
40         {"Byte", ";0;255;"},
41         {"Int16", ";-32768;32767;"},
42         {"UInt16", ";0;65535;"},
43         {"Int32", ";0020000000000;0017777777777;"},
44         {"UInt32", ";0000000000000;0037777777777;"},
45         {"Int64", ";01000000000000000000000;0777777777777777777777;"},
46         {"UInt64", ";0000000000000;01777777777777777777777;"},
47         {"Single", "r(0,8);4;0;"},
48         {"Double", "r(0,8);8;0;"},
49         {"String", "(0,41)=*(0,42)=xsMonoString:"}, /*string*/
50         {"", }, /*ptr*/
51         {"", }, /*byref*/
52         {"", }, /*valuetype*/
53         {"Class", "(0,44)=*(0,45)=xsMonoObject:"}, /*class*/
54         {"", }, /*unused*/
55         {"Array", }, /*array*/
56         {"", }, /*typedbyref*/
57         {"", }, /*unused*/
58         {"", }, /*unused*/
59         {"IntPtr", ";0020000000000;0017777777777;"},
60         {"UIntPtr", ";0000000000000;0037777777777;"},
61         {"", }, /*unused*/
62         {"FnPtr", "*(0,1)"}, /*fnptr*/
63         {"Object", "(0,47)=*(0,48)=xsMonoObject:"}, /*object*/
64         {"SzArray", "(0,50)=*(0,51))=xsMonoArray:"}, /*szarray*/
65         {NULL, NULL}
66 };
67
68 static void
69 output_std_stuff (AssemblyDebugInfo* debug)
70 {
71         int i;
72         for (i = 0; base_types [i].name; ++i) {
73                 if (! base_types [i].spec)
74                         continue;
75                 fprintf (debug->f, ".stabs \"%s:t(0,%d)=", base_types [i].name, i);
76                 if (base_types [i].spec [0] == ';') {
77                         fprintf (debug->f, "r(0,%d)%s\"", i, base_types [i].spec);
78                 } else {
79                         fprintf (debug->f, "%s\"", base_types [i].spec);
80                 }
81                 fprintf (debug->f, ",128,0,0,0\n");
82         }
83 }
84
85 MonoDebugHandle*
86 mono_debug_open_file (char *filename)
87 {
88         MonoDebugHandle *debug;
89         
90         debug = g_new0 (MonoDebugHandle, 1);
91         debug->name = g_strdup (filename);
92         return debug;
93 }
94
95 static void
96 debug_load_method_lines (AssemblyDebugInfo* info)
97 {
98         FILE *f;
99         char buf [1024];
100         int i, mnum;
101         char *name = g_strdup_printf ("%s.il", info->name);
102
103         /* use an env var with directories for searching. */
104         if (!(f = fopen (name, "r"))) {
105                 g_warning ("cannot open IL assembly file %s", name);
106                 g_free (name);
107                 return;
108         }
109         g_free (name);
110         i = 0;
111         while (fgets (buf, sizeof (buf), f)) {
112                 i++;
113                 if (sscanf (buf, " // method line %d", &mnum) && mnum < info->nmethods) {
114                         while (fgets (buf, sizeof (buf), f)) {
115                                 ++i;
116                                 if (strstr (buf, "}"))
117                                         break; /* internalcall or runtime method */
118                                 if (strstr (buf, "IL_0000:"))
119                                         break;
120                         }
121                         /* g_print ("method %d found at %d\n", mnum, i); */
122                         info->mlines [mnum] = i;
123                 }
124         }
125         fclose (f);
126 }
127
128 static AssemblyDebugInfo*
129 mono_debug_open_ass (MonoDebugHandle* handle, MonoImage *image)
130 {
131         GList *tmp;
132         AssemblyDebugInfo* info;
133
134         for (tmp = handle->info; tmp; tmp = tmp->next) {
135                 info = (AssemblyDebugInfo*)tmp->data;
136                 if (strcmp (info->name, image->assembly_name) == 0)
137                         return info;
138         }
139         info = g_new0 (AssemblyDebugInfo, 1);
140         info->filename = g_strdup_printf ("%s-stabs.s", image->assembly_name);
141         if (!(info->f = fopen (info->filename, "w"))) {
142                 g_free (info->filename);
143                 g_free (info);
144                 return NULL;
145         }
146         info->name = g_strdup (image->assembly_name);
147
148         fprintf (info->f, ".stabs \"%s.il\",100,0,0,0\n", image->assembly_name);
149         output_std_stuff (info);
150         info->next_idx = 100;
151         handle->info = g_list_prepend (handle->info, info);
152
153         info->nmethods = image->tables [MONO_TABLE_METHOD].rows + 1;
154         info->mlines = g_new0 (int, info->nmethods);
155         debug_load_method_lines (info);
156         return info;
157 }
158
159 void
160 mono_debug_make_symbols (MonoDebugHandle* debug)
161 {
162         GList *tmp;
163         char *buf;
164         AssemblyDebugInfo* info;
165
166         for (tmp = debug->info; tmp; tmp = tmp->next) {
167                 info = (AssemblyDebugInfo*)tmp->data;
168                 /* yes, it's completely unsafe */
169                 buf = g_strdup_printf ("as %s -o /tmp/%s.o", info->filename, info->name);
170                 fflush (info->f);
171                 system (buf);
172                 g_free (buf);
173         }
174 }
175
176 static void
177 mono_debug_close_ass (AssemblyDebugInfo* debug)
178 {
179         fclose (debug->f);
180         g_free (debug->mlines);
181         g_free (debug->name);
182         g_free (debug->filename);
183         g_free (debug);
184 }
185
186 void
187 mono_debug_close (MonoDebugHandle* debug)
188 {
189         GList *tmp;
190         AssemblyDebugInfo* info;
191
192         for (tmp = debug->info; tmp; tmp = tmp->next) {
193                 info = (AssemblyDebugInfo*)tmp->data;
194                 mono_debug_close_ass (info);
195         }
196         g_free (debug->name);
197         g_free (debug);
198 }
199
200 void
201 mono_debug_add_method (MonoDebugHandle* debug, MonoFlowGraph *cfg)
202 {
203         char *name;
204         int line = 0;
205         int i;
206         MonoMethod *method = cfg->method;
207         MonoClass *klass = method->klass;
208         MonoMethodSignature *sig = method->signature;
209         char **names = g_new (char*, sig->param_count);
210         AssemblyDebugInfo* info = mono_debug_open_ass (debug, klass->image);
211
212         /* FIXME: we should mangle the name better */
213         name = g_strdup_printf ("%s%s%s__%s_%p", klass->name_space, klass->name_space [0]? "_": "",
214                         klass->name, method->name, method);
215
216         for (i = 0; name [i]; ++i)
217                 if (name [i] == '.') name [i] = '_';
218
219         mono_class_init (klass);
220         /*
221          * Find the method index in the image.
222          */
223         for (i = 0; klass->methods && i < klass->method.count; ++i) {
224                 if (klass->methods [i] == method) {
225                         line = info->mlines [klass->method.first + i + 1];
226                         /*g_print ("method %d found at line %d\n", klass->method.first + i + 1, line);*/
227                         break;
228                 }
229         }
230
231         /*
232          * We need to output all the basic info, if we change filename...
233          * fprintf (info->f, ".stabs \"%s.il\",100,0,0,0\n", klass->image->assembly_name);
234          */
235         fprintf (info->f, ".stabs \"%s:F(0,%d)\",36,0,%d,%p\n", name, sig->ret->type, line, cfg->start);
236
237         /* params */
238         mono_method_get_param_names (cfg->method, (const char **)names);
239         if (sig->hasthis)
240                 fprintf (info->f, ".stabs \"this:p(0,%d)=(0,%d)\",160,0,%d,%d\n", info->next_idx++, klass->byval_arg.type, line, 8); /* FIXME */
241         for (i = 0; i < sig->param_count; ++i) {
242                 int stack_offset = g_array_index (cfg->varinfo, MonoVarInfo, cfg->args_start_index + i + sig->hasthis).offset;
243                 fprintf (info->f, ".stabs \"%s:p(0,%d)=(0,%d)\",160,0,%d,%d\n", names [i], info->next_idx++, sig->params [i]->type, line, stack_offset);
244         }
245         /* local vars */
246         if (!method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
247                 MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
248                 for (i = 0; i < header->num_locals; ++i) {
249                         int stack_offset = g_array_index (cfg->varinfo, MonoVarInfo, cfg->locals_start_index + i).offset;
250                         fprintf (info->f, ".stabs \"local_%d:(0,%d)=(0,%d)\",128,0,%d,%d\n", i, info->next_idx++, header->locals [i]->type, line, stack_offset);
251                 }
252         }
253         /* start lines of basic blocks */
254         for (i = 0; i < cfg->block_count; ++i) {
255                 int j;
256                 for (j = 0; j < cfg->bblocks [i].forest->len; ++j) {
257                         MBTree *t = (MBTree *) g_ptr_array_index (cfg->bblocks [i].forest, j);
258                         fprintf (info->f, ".stabn 68,0,%d,%d\n", line + t->cli_addr, t->addr);
259                 }
260         }
261
262         /* end of function */
263         fprintf (info->f, ".stabs \"\",36,0,0,%d\n", cfg->code - cfg->start);
264         g_free (name);
265         g_free (names);
266         fflush (info->f);
267 }
268
269 static void
270 get_enumvalue (MonoClass *klass, int index, char *buf)
271 {
272         guint32 const_cols [MONO_CONSTANT_SIZE];
273         const char *ptr;
274         guint32 crow = mono_metadata_get_constant_index (klass->image, MONO_TOKEN_FIELD_DEF | (index + 1));
275
276         if (!crow) {
277                 buf [0] = '0';
278                 buf [1] = 0;
279                 return;
280         }
281         mono_metadata_decode_row (&klass->image->tables [MONO_TABLE_CONSTANT], crow-1, const_cols, MONO_CONSTANT_SIZE);
282         ptr = mono_metadata_blob_heap (klass->image, const_cols [MONO_CONSTANT_VALUE]);
283         switch (const_cols [MONO_CONSTANT_TYPE]) {
284         case MONO_TYPE_U4:
285         case MONO_TYPE_I4:
286                 /* FIXME: add other types... */
287         default:
288                 g_snprintf (buf, 64, "%d", *(gint32*)ptr);
289         }
290 }
291
292 void
293 mono_debug_add_type (MonoDebugHandle* debug, MonoClass *klass)
294 {
295         char *name;
296         int i;
297         char buf [64];
298         AssemblyDebugInfo* info = mono_debug_open_ass (debug, klass->image);
299
300         mono_class_init (klass);
301
302         /* output enums ...*/
303         if (klass->enumtype) {
304                 name = g_strdup_printf ("%s%s%s", klass->name_space, klass->name_space [0]? "_": "", klass->name);
305                 fprintf (info->f, ".stabs \"%s:T%d=e", name, ++info->next_idx);
306                 g_free (name);
307                 for (i = 0; i < klass->field.count; ++i) {
308                         if (klass->fields [i].type->attrs & FIELD_ATTRIBUTE_LITERAL) {
309                                 get_enumvalue (klass, klass->field.first + i, buf);
310                                 fprintf (info->f, "%s_%s=%s,", klass->name, klass->fields [i].name, buf);
311                         }
312                 }
313                 fprintf (info->f, ";\",128,0,0,0\n");
314         }
315         fflush (info->f);
316
317 }
318