2002-03-24 Martin Baulig <martin@gnome.org>
[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 #include <mono/jit/debug.h>
8
9 #include "debug-private.h"
10
11 MonoDebugHandle*
12 mono_debug_open_file (char *filename, MonoDebugFormat format)
13 {
14         MonoDebugHandle *debug;
15         
16         debug = g_new0 (MonoDebugHandle, 1);
17         debug->name = g_strdup (filename);
18         debug->format = format;
19         return debug;
20 }
21
22 static void
23 debug_load_method_lines (AssemblyDebugInfo* info)
24 {
25         FILE *f;
26         char buf [1024];
27         int i, mnum;
28         char *name = g_strdup_printf ("%s.il", info->name);
29         int offset = -1;
30
31         /* use an env var with directories for searching. */
32         if (!(f = fopen (name, "r"))) {
33                 g_warning ("cannot open IL assembly file %s", name);
34                 g_free (name);
35                 return;
36         }
37
38         info->total_lines = 100;
39         info->moffsets = g_malloc (info->total_lines * sizeof (int));
40
41         g_free (name);
42         i = 0;
43         while (fgets (buf, sizeof (buf), f)) {
44                 int pos = i;
45
46                 info->moffsets [i++] = offset;
47                 if (i + 2 >= info->total_lines) {
48                         info->total_lines += 100;
49                         info->moffsets = g_realloc (info->moffsets, info->total_lines * sizeof (int));
50                         g_assert (info->moffsets);
51                 }
52
53                 if (!sscanf (buf, " // method line %d", &mnum))
54                         continue;
55
56                 offset = 0;
57
58                 if (mnum >= info->nmethods)
59                         break;
60
61                 while (fgets (buf, sizeof (buf), f)) {
62                         int newoffset;
63
64                         ++i;
65                         if (i + 2 >= info->total_lines) {
66                                 info->total_lines += 100;
67                                 info->moffsets = g_realloc (info->moffsets, info->total_lines * sizeof (int));
68                                 g_assert (info->moffsets);
69                         }
70
71                         if (strstr (buf, "}")) {
72                                 offset = -1;
73                                 break;
74                         }
75
76                         if (sscanf (buf, " IL_%x:", &newoffset)) {
77                                 offset = newoffset;
78                                 if (!offset)
79                                         pos = i;
80                         }
81
82                         info->moffsets [i] = offset;
83                 }
84                 /* g_print ("method %d found at %d\n", mnum, pos); */
85                 info->mlines [mnum] = pos;
86         }
87         fclose (f);
88 }
89
90 static void
91 record_line_number (DebugMethodInfo *minfo, gpointer address, guint32 line, int is_basic_block)
92 {
93         DebugLineNumberInfo *lni = g_new0 (DebugLineNumberInfo, 1);
94
95         lni->address = address;
96         lni->line = line;
97         lni->is_basic_block = is_basic_block;
98         lni->source_file = 0;
99
100         g_ptr_array_add (minfo->line_numbers, lni);
101 }
102
103 static void
104 record_il_offset (GPtrArray *array, guint32 offset, guint32 address)
105 {
106         MonoDebugILOffsetInfo *info = g_new0 (MonoDebugILOffsetInfo, 1);
107
108         info->offset = offset;
109         info->address = address;
110
111         g_ptr_array_add (array, info);
112 }
113
114 static void
115 debug_generate_method_lines (AssemblyDebugInfo *info, DebugMethodInfo *minfo, MonoFlowGraph* cfg)
116 {
117         guint32 st_address, st_line;
118         GPtrArray *il_offsets;
119         int i;
120
121         il_offsets = g_ptr_array_new ();
122         minfo->line_numbers = g_ptr_array_new ();
123
124         st_line = minfo->start_line;
125         st_address = 1;
126
127         record_line_number (minfo, cfg->start + st_address, st_line, FALSE);
128         record_il_offset (il_offsets, 0, 0);
129
130         /* start lines of basic blocks */
131         for (i = 0; i < cfg->block_count; ++i) {
132                 int j;
133
134                 for (j = 0; j < cfg->bblocks [i].forest->len; ++j) {
135                         MBTree *t = (MBTree *) g_ptr_array_index (cfg->bblocks [i].forest, j);
136                         gint32 line_inc = 0, addr_inc;
137
138                         if (!i && !j) {
139                                 st_line = minfo->first_line;
140                                 st_address = t->addr;
141
142                                 minfo->frame_start_offset = st_address;
143
144                                 record_line_number (minfo, cfg->start + st_address, st_line, TRUE);
145                         }
146
147                         if (t->cli_addr != -1) {
148                                 int *lines = info->moffsets + st_line;
149                                 int *k = lines;
150
151                                 while ((*k != -1) && (*k < t->cli_addr))
152                                         k++;
153
154                                 line_inc = k - lines;
155                         }
156                         addr_inc = t->addr - st_address;
157
158                         st_line += line_inc;
159                         st_address += addr_inc;
160
161                         record_line_number (minfo, cfg->start + st_address, st_line, j == 0);
162
163                         if (t->cli_addr != -1)
164                                 record_il_offset (il_offsets, t->cli_addr, st_address + 1);
165                 }
166         }
167
168         minfo->method_info.num_il_offsets = il_offsets->len;
169         minfo->method_info.il_offsets = g_new0 (MonoDebugILOffsetInfo, il_offsets->len);
170         for (i = 0; i < il_offsets->len; i++) {
171                 MonoDebugILOffsetInfo *il = (MonoDebugILOffsetInfo *) g_ptr_array_index (il_offsets, i);
172
173                 minfo->method_info.il_offsets [i] = *il;
174         }
175
176         g_ptr_array_free (il_offsets, TRUE);
177 }
178
179 static void
180 free_method_info (DebugMethodInfo *minfo)
181 {
182         if (minfo->line_numbers)
183                 g_ptr_array_free (minfo->line_numbers, TRUE);
184         g_free (minfo->method_info.param_offsets);
185         g_free (minfo->method_info.local_offsets);
186         g_free (minfo);
187 }
188
189 static AssemblyDebugInfo*
190 mono_debug_open_assembly (MonoDebugHandle* handle, MonoImage *image)
191 {
192         GList *tmp;
193         AssemblyDebugInfo* info;
194
195         for (tmp = handle->info; tmp; tmp = tmp->next) {
196                 info = (AssemblyDebugInfo*)tmp->data;
197                 if (strcmp (info->name, image->assembly_name) == 0)
198                         return info;
199         }
200         info = g_new0 (AssemblyDebugInfo, 1);
201         switch (handle->format) {
202         case MONO_DEBUG_FORMAT_STABS:
203                 info->filename = g_strdup_printf ("%s-stabs.s", image->assembly_name);
204                 break;
205         case MONO_DEBUG_FORMAT_DWARF2:
206                 info->filename = g_strdup_printf ("%s-dwarf.s", image->assembly_name);
207                 break;
208         case MONO_DEBUG_FORMAT_DWARF2_PLUS:
209                 info->filename = g_strdup_printf ("%s-debug.o", image->assembly_name);
210                 break;
211         }
212         info->image = image;
213         info->name = g_strdup (image->assembly_name);
214         info->methods = g_hash_table_new_full (g_direct_hash, g_direct_equal,
215                                                NULL, (GDestroyNotify) free_method_info);
216         info->source_files = g_ptr_array_new ();
217         info->type_hash = g_hash_table_new (NULL, NULL);
218
219         g_ptr_array_add (info->source_files, g_strdup_printf ("%s.il", image->assembly_name));
220         info->producer_name = g_strdup_printf ("Mono JIT compiler version %s", VERSION);
221
222         mono_debug_get_type (info, mono_defaults.void_class);
223         mono_debug_get_type (info, mono_defaults.object_class);
224         mono_debug_get_type (info, mono_defaults.void_class);
225         mono_debug_get_type (info, mono_defaults.boolean_class);
226         mono_debug_get_type (info, mono_defaults.char_class);
227         mono_debug_get_type (info, mono_defaults.sbyte_class);
228         mono_debug_get_type (info, mono_defaults.byte_class);
229         mono_debug_get_type (info, mono_defaults.int16_class);
230         mono_debug_get_type (info, mono_defaults.uint16_class);
231         mono_debug_get_type (info, mono_defaults.int32_class);
232         mono_debug_get_type (info, mono_defaults.uint32_class);
233         mono_debug_get_type (info, mono_defaults.int_class);
234         mono_debug_get_type (info, mono_defaults.uint_class);
235         mono_debug_get_type (info, mono_defaults.int64_class);
236         mono_debug_get_type (info, mono_defaults.uint64_class);
237         mono_debug_get_type (info, mono_defaults.single_class);
238         mono_debug_get_type (info, mono_defaults.double_class);
239         mono_debug_get_type (info, mono_defaults.string_class);
240
241         switch (handle->format) {
242         case MONO_DEBUG_FORMAT_STABS:
243                 mono_debug_open_assembly_stabs (info);
244                 break;
245         case MONO_DEBUG_FORMAT_DWARF2:
246                 mono_debug_open_assembly_dwarf2 (info);
247                 break;
248         case MONO_DEBUG_FORMAT_DWARF2_PLUS:
249                 mono_debug_open_assembly_dwarf2_plus (info);
250                 break;
251         }
252
253         info->next_idx = 100;
254         handle->info = g_list_prepend (handle->info, info);
255
256         info->nmethods = image->tables [MONO_TABLE_METHOD].rows + 1;
257         info->mlines = g_new0 (int, info->nmethods);
258         debug_load_method_lines (info);
259         return info;
260 }
261
262 void
263 mono_debug_make_symbols (void)
264 {
265         GList *tmp;
266         AssemblyDebugInfo* info;
267
268         if (!mono_debug_handle)
269                 return;
270
271         for (tmp = mono_debug_handle->info; tmp; tmp = tmp->next) {
272                 info = (AssemblyDebugInfo*)tmp->data;
273
274                 switch (mono_debug_handle->format) {
275                 case MONO_DEBUG_FORMAT_STABS:
276                         mono_debug_write_assembly_stabs (info);
277                         break;
278                 case MONO_DEBUG_FORMAT_DWARF2:
279                         mono_debug_write_assembly_dwarf2 (info);
280                         break;
281                 case MONO_DEBUG_FORMAT_DWARF2_PLUS:
282                         mono_debug_write_assembly_dwarf2_plus (info);
283                         break;
284                 }
285         }
286 }
287
288 static void
289 mono_debug_close_assembly (AssemblyDebugInfo* info)
290 {
291         g_free (info->mlines);
292         g_free (info->moffsets);
293         g_free (info->name);
294         g_free (info->filename);
295         g_ptr_array_free (info->source_files, TRUE);
296         g_hash_table_destroy (info->type_hash);
297         g_hash_table_destroy (info->methods);
298         g_free (info->producer_name);
299         g_free (info);
300 }
301
302 void
303 mono_debug_close (MonoDebugHandle* debug)
304 {
305         GList *tmp;
306         AssemblyDebugInfo* info;
307
308         mono_debug_make_symbols ();
309
310         for (tmp = debug->info; tmp; tmp = tmp->next) {
311                 info = (AssemblyDebugInfo*)tmp->data;
312
313                 switch (debug->format) {
314                 case MONO_DEBUG_FORMAT_STABS:
315                         mono_debug_close_assembly_stabs (info);
316                         break;
317                 case MONO_DEBUG_FORMAT_DWARF2:
318                         mono_debug_close_assembly_dwarf2 (info);
319                         break;
320                 case MONO_DEBUG_FORMAT_DWARF2_PLUS:
321                         mono_debug_close_assembly_dwarf2_plus (info);
322                         break;
323                 }
324
325                 mono_debug_close_assembly (info);
326         }
327
328         g_free (debug->name);
329         g_free (debug);
330 }
331
332 guint32
333 mono_debug_get_type (AssemblyDebugInfo* info, MonoClass *klass)
334 {
335         guint index, i;
336
337         mono_class_init (klass);
338
339         index = GPOINTER_TO_INT (g_hash_table_lookup (info->type_hash, klass));
340         if (index)
341                 return index;
342
343         index = ++info->next_klass_idx;
344         g_hash_table_insert (info->type_hash, klass, GINT_TO_POINTER (index));
345
346         if (klass->enumtype)
347                 return index;
348
349         switch (klass->byval_arg.type) {
350         case MONO_TYPE_CLASS:
351                 if (klass->parent)
352                         mono_debug_get_type (info, klass->parent);
353
354                 for (i = 0; i < klass->method.count; i++) {
355                         MonoMethod *method = klass->methods [i];
356                         MonoType *ret_type = NULL;
357                         int j;
358
359                         if (method->signature->ret->type != MONO_TYPE_VOID)
360                                 ret_type = method->signature->ret;
361
362                         if (ret_type) {
363                                 MonoClass *ret_klass = mono_class_from_mono_type (ret_type);
364                                 mono_debug_get_type (info, ret_klass);
365                         }
366
367                         for (j = 0; j < method->signature->param_count; j++) {
368                                 MonoType *sub_type = method->signature->params [j];
369                                 MonoClass *sub_klass = mono_class_from_mono_type (sub_type);
370                                 mono_debug_get_type (info, sub_klass);
371                         }
372                 }
373                 // fall through
374         case MONO_TYPE_VALUETYPE:
375                 for (i = 0; i < klass->field.count; i++) {
376                         MonoClass *subclass = mono_class_from_mono_type (klass->fields [i].type);
377                         mono_debug_get_type (info, subclass);
378                 }
379                 break;
380         case MONO_TYPE_ARRAY:
381         case MONO_TYPE_SZARRAY:
382                 mono_debug_get_type (info, klass->element_class);
383                 break;
384         default:
385                 break;
386         }
387
388         return index;
389 }
390
391 void
392 mono_debug_add_type (MonoDebugHandle* debug, MonoClass *klass)
393 {
394         AssemblyDebugInfo* info = mono_debug_open_assembly (debug, klass->image);
395
396         mono_debug_get_type (info, klass);
397 }
398
399 void
400 mono_debug_add_method (MonoDebugHandle* debug, MonoFlowGraph *cfg)
401 {
402         MonoMethod *method = cfg->method;
403         MonoClass *klass = method->klass;
404         AssemblyDebugInfo* info = mono_debug_open_assembly (debug, klass->image);
405         int method_number = 0, line = 0, start_line, i;
406         DebugMethodInfo *minfo;
407         char *name;
408
409         mono_class_init (klass);
410         /*
411          * Find the method index in the image.
412          */
413         for (i = 0; klass->methods && i < klass->method.count; ++i) {
414                 if (klass->methods [i] == method) {
415                         method_number = klass->method.first + i + 1;
416                         line = info->mlines [method_number];
417                         break;
418                 }
419         }
420
421         if (g_hash_table_lookup (info->methods, method))
422                 return;
423
424         /* info->moffsets contains -1 "outside" of functions. */
425         for (i = line; (i > 0) && (info->moffsets [i] == 0); i--)
426                 ;
427         start_line = i + 1;
428
429         name = g_strdup_printf ("%s%s%s.%s", klass->name_space, klass->name_space [0]? ".": "",
430                                 klass->name, method->name);
431
432         minfo = g_new0 (DebugMethodInfo, 1);
433         minfo->name = name;
434         minfo->start_line = start_line;
435         minfo->first_line = line;
436         minfo->method_info.code_start = cfg->start + 1;
437         minfo->method_info.code_size = cfg->code_size;
438         minfo->method_number = method_number;
439         minfo->method_info.method = method;
440         minfo->method_info.num_params = method->signature->param_count + method->signature->hasthis;
441         minfo->method_info.param_offsets = g_new0 (guint32, minfo->method_info.num_params + 1);
442
443         for (i = 0; i < minfo->method_info.num_params; i++) {
444                 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->args_start_index;
445
446                 minfo->method_info.param_offsets [i] = ptr [i].offset;
447         }
448
449         if (!method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
450                 MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
451                 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->locals_start_index;
452
453                 minfo->method_info.num_locals = header->num_locals;
454                 minfo->method_info.local_offsets = g_new0 (guint32, header->num_locals);
455                 for (i = 0; i < minfo->method_info.num_locals; i++)
456                         minfo->method_info.local_offsets [i] = ptr [i].offset;
457         }
458
459         debug_generate_method_lines (info, minfo, cfg);
460
461         g_hash_table_insert (info->methods, method, minfo);
462 }