2002-05-21 Martin Baulig <martin@gnome.org>
[mono.git] / mono / jit / debug.c
1 #include <stdlib.h>
2 #include <string.h>
3 #include <errno.h>
4 #include <sys/stat.h>
5 #include <mono/metadata/class.h>
6 #include <mono/metadata/tabledefs.h>
7 #include <mono/metadata/tokentype.h>
8 #include <mono/jit/codegen.h>
9 #include <mono/jit/debug.h>
10
11 #include "debug-private.h"
12
13 static MonoDebugHandle *mono_debug_handles = NULL;
14 static MonoDebugHandle *mono_default_debug_handle = NULL;
15
16 static void
17 free_method_info (DebugMethodInfo *minfo)
18 {
19         if (minfo->line_numbers)
20                 g_ptr_array_free (minfo->line_numbers, TRUE);
21         g_free (minfo->method_info.params);
22         g_free (minfo->method_info.locals);
23         g_free (minfo);
24 }
25
26 MonoDebugHandle*
27 mono_debug_open_file (const char *filename, MonoDebugFormat format)
28 {
29         MonoDebugHandle *debug;
30         
31         debug = g_new0 (MonoDebugHandle, 1);
32         debug->name = g_strdup (filename);
33         debug->format = format;
34         debug->producer_name = g_strdup_printf ("Mono JIT compiler version %s", VERSION);
35         debug->next_idx = 100;
36
37         debug->type_hash = g_hash_table_new (NULL, NULL);
38         debug->methods = g_hash_table_new_full (g_direct_hash, g_direct_equal,
39                                                 NULL, (GDestroyNotify) free_method_info);
40         debug->source_files = g_ptr_array_new ();
41
42         switch (debug->format) {
43         case MONO_DEBUG_FORMAT_STABS:
44                 debug->filename = g_strdup_printf ("%s-stabs.s", g_basename (debug->name));
45                 debug->objfile = g_strdup_printf ("%s.o", g_basename (debug->name));
46                 break;
47         case MONO_DEBUG_FORMAT_DWARF2:
48                 debug->filename = g_strdup_printf ("%s-dwarf.s", g_basename (debug->name));
49                 debug->objfile = g_strdup_printf ("%s.o", g_basename (debug->name));
50                 break;
51         case MONO_DEBUG_FORMAT_DWARF2_PLUS:
52                 if (!mono_default_debug_handle)
53                         mono_debug_open_file (filename, MONO_DEBUG_FORMAT_DWARF2);
54                 break;
55         default:
56                 g_assert_not_reached ();
57         }
58
59         debug->next = mono_debug_handles;
60         mono_debug_handles = debug;
61
62         if (!mono_default_debug_handle)
63                 mono_default_debug_handle = debug;
64
65         return debug;
66 }
67
68 static void
69 debug_load_method_lines (AssemblyDebugInfo* info)
70 {
71         FILE *f;
72         char buf [1024];
73         int i, mnum;
74         char *name = g_strdup_printf ("%s.il", info->name);
75         char *command = g_strdup_printf ("monodis --output=%s.il %s", info->name, info->image->name);
76         struct stat stata, statb;
77         int offset = -1;
78
79         if (stat (info->image->name, &stata)) {
80                 g_warning ("cannot access assembly file (%s): %s", info->image->name, g_strerror (errno));
81                 g_free (command);
82                 g_free (name);
83                 return;
84         }
85
86         /* If the stat() failed or the file is older. */
87         if (stat (name, &statb) || (statb.st_mtime < stata.st_mtime)) {
88                 g_print ("Recreating %s from %s.\n", name, info->image->name);
89                 if (system (command)) {
90                         g_warning ("cannot create IL assembly file (%s): %s", command, g_strerror (errno));
91                         g_free (command);
92                         g_free (name);
93                         return;
94                 }
95         }
96
97         /* use an env var with directories for searching. */
98         if (!(f = fopen (name, "r"))) {
99                 g_warning ("cannot open IL assembly file %s", name);
100                 g_free (command);
101                 g_free (name);
102                 return;
103         }
104
105         info->total_lines = 100;
106         info->moffsets = g_malloc (info->total_lines * sizeof (int));
107
108         g_free (name);
109         g_free (command);
110         i = 0;
111         while (fgets (buf, sizeof (buf), f)) {
112                 int pos = i;
113
114                 info->moffsets [i++] = offset;
115                 if (i + 2 >= info->total_lines) {
116                         info->total_lines += 100;
117                         info->moffsets = g_realloc (info->moffsets, info->total_lines * sizeof (int));
118                         g_assert (info->moffsets);
119                 }
120
121                 if (!sscanf (buf, " // method line %d", &mnum))
122                         continue;
123
124                 offset = 0;
125
126                 if (mnum >= info->nmethods)
127                         break;
128
129                 while (fgets (buf, sizeof (buf), f)) {
130                         int newoffset;
131
132                         ++i;
133                         if (i + 2 >= info->total_lines) {
134                                 info->total_lines += 100;
135                                 info->moffsets = g_realloc (info->moffsets, info->total_lines * sizeof (int));
136                                 g_assert (info->moffsets);
137                         }
138
139                         if (strstr (buf, "}")) {
140                                 offset = -1;
141                                 break;
142                         }
143
144                         if (sscanf (buf, " IL_%x:", &newoffset)) {
145                                 offset = newoffset;
146                                 if (!offset)
147                                         pos = i;
148                         }
149
150                         info->moffsets [i] = offset;
151                 }
152                 /* g_print ("method %d found at %d\n", mnum, pos); */
153                 info->mlines [mnum] = pos;
154         }
155         fclose (f);
156 }
157
158 static void
159 record_line_number (DebugMethodInfo *minfo, gpointer address, guint32 line, int is_basic_block)
160 {
161         DebugLineNumberInfo *lni = g_new0 (DebugLineNumberInfo, 1);
162
163         lni->address = address;
164         lni->line = line;
165         lni->is_basic_block = is_basic_block;
166         lni->source_file = 0;
167
168         g_ptr_array_add (minfo->line_numbers, lni);
169 }
170
171 static void
172 record_il_offset (GPtrArray *array, guint32 offset, guint32 address)
173 {
174         MonoDebugILOffsetInfo *info = g_new0 (MonoDebugILOffsetInfo, 1);
175
176         info->offset = offset;
177         info->address = address;
178
179         g_ptr_array_add (array, info);
180 }
181
182 static void
183 debug_generate_method_lines (AssemblyDebugInfo *info, DebugMethodInfo *minfo, MonoFlowGraph* cfg)
184 {
185         guint32 st_address, st_line;
186         GPtrArray *il_offsets;
187         int i;
188
189         il_offsets = g_ptr_array_new ();
190         minfo->line_numbers = g_ptr_array_new ();
191
192         st_line = minfo->first_line;
193         st_address = minfo->method_info.prologue_end;
194
195         /* record_line_number takes absolute memory addresses. */
196         record_line_number (minfo, minfo->method_info.code_start, minfo->start_line, FALSE);
197         /* record_il_offsets uses offsets relative to minfo->method_info.code_start. */
198         record_il_offset (il_offsets, 0, st_address);
199
200         /* This is the first actual code line of the method. */
201         record_line_number (minfo, minfo->method_info.code_start + st_address, st_line, TRUE);
202
203         /* start lines of basic blocks */
204         for (i = 0; i < cfg->block_count; ++i) {
205                 int j;
206
207                 for (j = 0; cfg->bblocks [i].forest && (j < cfg->bblocks [i].forest->len); ++j) {
208                         MBTree *t = (MBTree *) g_ptr_array_index (cfg->bblocks [i].forest, j);
209                         gint32 line_inc = 0, addr_inc;
210
211                         if (!i && !j) {
212                                 st_line = minfo->first_line;
213                                 st_address = t->addr - 1;
214
215                                 record_line_number (minfo, cfg->start + st_address, st_line, TRUE);
216                         }
217
218                         addr_inc = t->addr - st_address - 1;
219                         st_address += addr_inc;
220
221                         if (t->cli_addr != -1)
222                                 record_il_offset (il_offsets, t->cli_addr, st_address);
223
224                         if (!info->moffsets)
225                                 continue;
226
227
228                         if (t->cli_addr != -1) {
229                                 int *lines = info->moffsets + st_line;
230                                 int *k = lines;
231
232                                 while ((*k != -1) && (*k < t->cli_addr))
233                                         k++;
234
235                                 line_inc = k - lines;
236                         }
237
238                         st_line += line_inc;
239
240                         record_line_number (minfo, minfo->method_info.code_start + st_address,
241                                             st_line, j == 0);
242                 }
243         }
244
245         minfo->method_info.num_il_offsets = il_offsets->len;
246         minfo->method_info.il_offsets = g_new0 (MonoDebugILOffsetInfo, il_offsets->len);
247         for (i = 0; i < il_offsets->len; i++) {
248                 MonoDebugILOffsetInfo *il = (MonoDebugILOffsetInfo *) g_ptr_array_index (il_offsets, i);
249
250                 minfo->method_info.il_offsets [i] = *il;
251         }
252
253         g_ptr_array_free (il_offsets, TRUE);
254 }
255
256 static AssemblyDebugInfo *
257 mono_debug_get_image (MonoDebugHandle* debug, MonoImage *image)
258 {
259         GList *tmp;
260         AssemblyDebugInfo *info;
261
262         if (debug->format == MONO_DEBUG_FORMAT_NONE)
263                 return NULL;
264
265         for (tmp = debug->info; tmp; tmp = tmp->next) {
266                 info = (AssemblyDebugInfo*)tmp->data;
267
268                 if (info->image == image)
269                         return info;
270         }
271
272         return NULL;
273 }
274
275 static AssemblyDebugInfo *
276 mono_debug_open_image (MonoDebugHandle* debug, MonoImage *image)
277 {
278         AssemblyDebugInfo *info;
279
280         info = mono_debug_get_image (debug, image);
281         if (info != NULL)
282                 return info;
283
284         info = g_new0 (AssemblyDebugInfo, 1);
285         info->image = image;
286         info->image->ref_count++;
287         info->name = g_strdup (image->assembly_name);
288         info->format = debug->format;
289         info->handle = debug;
290
291         info->source_file = debug->source_files->len;
292         g_ptr_array_add (debug->source_files, g_strdup_printf ("%s.il", image->assembly_name));
293
294         debug->info = g_list_prepend (debug->info, info);
295
296         info->nmethods = image->tables [MONO_TABLE_METHOD].rows + 1;
297         info->mlines = g_new0 (int, info->nmethods);
298
299         switch (info->format) {
300         case MONO_DEBUG_FORMAT_DWARF2_PLUS:
301                 info->filename = g_strdup_printf ("%s-debug.s", info->name);
302                 info->objfile = g_strdup_printf ("%s-debug.o", info->name);
303                 mono_debug_open_assembly_dwarf2_plus (info);
304                 break;
305         default:
306                 break;
307         }
308
309         if (debug->format != MONO_DEBUG_FORMAT_DWARF2_PLUS)
310                 debug_load_method_lines (info);
311
312         return info;
313 }
314
315 void
316 mono_debug_add_image (MonoDebugHandle* debug, MonoImage *image)
317 {
318         mono_debug_open_image (debug, image);
319 }
320
321 void
322 mono_debug_write_symbols (MonoDebugHandle *debug)
323 {
324         GList *tmp;
325
326         if (!debug)
327                 return;
328
329         switch (debug->format) {
330         case MONO_DEBUG_FORMAT_STABS:
331                 mono_debug_write_stabs (debug);
332                 break;
333         case MONO_DEBUG_FORMAT_DWARF2:
334                 mono_debug_write_dwarf2 (debug);
335                 break;
336         case MONO_DEBUG_FORMAT_DWARF2_PLUS:
337                 for (tmp = debug->info; tmp; tmp = tmp->next) {
338                         AssemblyDebugInfo *info = (AssemblyDebugInfo*)tmp->data;
339
340                         mono_debug_write_assembly_dwarf2_plus (info);
341                 }
342                 break;
343         default:
344                 g_assert_not_reached ();
345         }
346 }
347
348 void
349 mono_debug_make_symbols (void)
350 {
351         MonoDebugHandle *debug;
352
353         for (debug = mono_debug_handles; debug; debug = debug->next)
354                 mono_debug_write_symbols (debug);
355 }
356
357 static void
358 mono_debug_close_assembly (AssemblyDebugInfo* info)
359 {
360         switch (info->format) {
361         case MONO_DEBUG_FORMAT_DWARF2_PLUS:
362                 mono_debug_close_assembly_dwarf2_plus (info);
363                 break;
364         default:
365                 break;
366         }
367         g_free (info->mlines);
368         g_free (info->moffsets);
369         g_free (info->name);
370         g_free (info->filename);
371         g_free (info->objfile);
372         g_free (info);
373 }
374
375 void
376 mono_debug_cleanup (void)
377 {
378         MonoDebugHandle *debug, *temp;
379
380         mono_debug_make_symbols ();
381
382         for (debug = mono_debug_handles; debug; debug = temp) {
383                 GList *tmp;
384
385                 for (tmp = debug->info; tmp; tmp = tmp->next) {
386                         AssemblyDebugInfo* info = (AssemblyDebugInfo*)tmp->data;
387
388                         mono_debug_close_assembly (info);
389                 }
390
391                 g_ptr_array_free (debug->source_files, TRUE);
392                 g_hash_table_destroy (debug->methods);
393                 g_hash_table_destroy (debug->type_hash);
394                 g_free (debug->producer_name);
395                 g_free (debug->name);
396
397                 temp = debug->next;
398                 g_free (debug);
399         }
400
401         mono_debug_handles = NULL;
402         mono_default_debug_handle = NULL;
403 }
404
405 guint32
406 mono_debug_get_type (MonoDebugHandle *debug, MonoClass *klass)
407 {
408         guint index, i;
409
410         mono_class_init (klass);
411
412         index = GPOINTER_TO_INT (g_hash_table_lookup (debug->type_hash, klass));
413         if (index)
414                 return index;
415
416         index = ++debug->next_klass_idx;
417         g_hash_table_insert (debug->type_hash, klass, GINT_TO_POINTER (index));
418
419         if (klass->enumtype)
420                 return index;
421
422         switch (klass->byval_arg.type) {
423         case MONO_TYPE_CLASS:
424                 if (klass->parent)
425                         mono_debug_get_type (debug, klass->parent);
426
427                 for (i = 0; i < klass->method.count; i++) {
428                         MonoMethod *method = klass->methods [i];
429                         MonoType *ret_type = NULL;
430                         int j;
431
432                         if (method->signature->ret->type != MONO_TYPE_VOID)
433                                 ret_type = method->signature->ret;
434
435                         if (ret_type) {
436                                 MonoClass *ret_klass = mono_class_from_mono_type (ret_type);
437                                 mono_debug_get_type (debug, ret_klass);
438                         }
439
440                         for (j = 0; j < method->signature->param_count; j++) {
441                                 MonoType *sub_type = method->signature->params [j];
442                                 MonoClass *sub_klass = mono_class_from_mono_type (sub_type);
443                                 mono_debug_get_type (debug, sub_klass);
444                         }
445                 }
446                 // fall through
447         case MONO_TYPE_VALUETYPE:
448                 for (i = 0; i < klass->field.count; i++) {
449                         MonoClass *subclass = mono_class_from_mono_type (klass->fields [i].type);
450                         mono_debug_get_type (debug, subclass);
451                 }
452                 break;
453         case MONO_TYPE_ARRAY:
454         case MONO_TYPE_SZARRAY:
455                 mono_debug_get_type (debug, klass->element_class);
456                 break;
457         default:
458                 break;
459         }
460
461         return index;
462 }
463
464 MonoDebugHandle *
465 mono_debug_handle_from_class (MonoClass *klass)
466 {
467         MonoDebugHandle *debug;
468
469         mono_class_init (klass);
470
471         for (debug = mono_debug_handles; debug; debug = debug->next) {
472                 GList *tmp;
473
474                 for (tmp = debug->info; tmp; tmp = tmp->next) {
475                         AssemblyDebugInfo *info = (AssemblyDebugInfo*)tmp->data;
476
477                         if (info->image == klass->image)
478                                 return debug;
479                 }
480         }
481
482         return NULL;
483 }
484
485 void
486 mono_debug_add_type (MonoClass *klass)
487 {
488         MonoDebugHandle *debug = mono_debug_handle_from_class (klass);
489
490         g_assert (debug != NULL);
491
492         mono_debug_get_type (debug, klass);
493 }
494
495 void
496 mono_debug_add_method (MonoFlowGraph *cfg)
497 {
498         MonoMethod *method = cfg->method;
499         MonoClass *klass = method->klass;
500         int method_number = 0, line = 0, start_line = 0, end_line = 0, i;
501         MonoDebugHandle* debug;
502         AssemblyDebugInfo* info;
503         DebugMethodInfo *minfo;
504         char *name;
505
506         mono_class_init (klass);
507
508         debug = mono_debug_handle_from_class (klass);
509         if (!debug) {
510                 if (mono_default_debug_handle)
511                         debug = mono_default_debug_handle;
512                 else
513                         return;
514         }
515
516         info = mono_debug_open_image (debug, klass->image);
517
518         /*
519          * Find the method index in the image.
520          */
521         for (i = 0; klass->methods && i < klass->method.count; ++i) {
522                 if (klass->methods [i] == method) {
523                         method_number = klass->method.first + i + 1;
524                         line = info->mlines [method_number];
525                         break;
526                 }
527         }
528
529         if (g_hash_table_lookup (debug->methods, method))
530                 return;
531
532         if (info->moffsets) {
533                 /* info->moffsets contains -1 "outside" of functions. */
534                 for (i = line; (i > 0) && (info->moffsets [i] == 0); i--)
535                         ;
536                 start_line = i + 1;
537
538                 for (i = start_line; info->moffsets [i] != -1; i++)
539                         ;
540                 end_line = i;
541         }
542
543         name = g_strdup_printf ("%s%s%s.%s", klass->name_space, klass->name_space [0]? ".": "",
544                                 klass->name, method->name);
545
546         minfo = g_new0 (DebugMethodInfo, 1);
547         minfo->name = name;
548         minfo->start_line = start_line;
549         minfo->first_line = line;
550         minfo->last_line = end_line;
551         minfo->source_file = info->source_file;
552         minfo->method_info.code_start = cfg->start + 1;
553         minfo->method_info.code_size = cfg->epilogue_end - 1;
554         minfo->method_number = method_number;
555         minfo->method_info.method = method;
556         minfo->method_info.num_params = method->signature->param_count;
557         minfo->method_info.params = g_new0 (MonoDebugVarInfo, minfo->method_info.num_params);
558         minfo->method_info.prologue_end = cfg->prologue_end - 1;
559         minfo->method_info.epilogue_begin = cfg->epilog - 1;
560
561         if (method->signature->hasthis) {
562                 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->args_start_index;
563
564                 minfo->method_info.this_var = g_new0 (MonoDebugVarInfo, 1);
565                 minfo->method_info.this_var->offset = ptr->offset;
566         }
567
568         for (i = 0; i < minfo->method_info.num_params; i++) {
569                 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->args_start_index +
570                         method->signature->hasthis;
571
572                 minfo->method_info.params [i].offset = ptr [i].offset;
573         }
574
575         if (!method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
576                 MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
577                 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->locals_start_index;
578
579                 minfo->method_info.num_locals = header->num_locals;
580                 minfo->method_info.locals = g_new0 (MonoDebugVarInfo, header->num_locals);
581                 for (i = 0; i < minfo->method_info.num_locals; i++) {
582                         minfo->method_info.locals [i].offset = ptr [i].offset;
583                         minfo->method_info.locals [i].begin_scope = minfo->method_info.prologue_end;
584                         minfo->method_info.locals [i].end_scope = minfo->method_info.epilogue_begin;
585                 }
586         }
587
588         debug_generate_method_lines (info, minfo, cfg);
589
590         g_hash_table_insert (debug->methods, method, minfo);
591 }