added linear scan to mono (use --ls to enable it)
[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 (const char *filename, MonoDebugFormat format)
13 {
14         MonoDebugHandle *debug;
15         
16         debug = g_new0 (MonoDebugHandle, 1);
17         debug->name = g_strdup (filename);
18         debug->default_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->first_line;
125         st_address = minfo->method_info.prologue_end;
126
127         /* record_line_number takes absolute memory addresses. */
128         record_line_number (minfo, minfo->method_info.code_start, minfo->start_line, FALSE);
129         /* record_il_offsets uses offsets relative to minfo->method_info.code_start. */
130         record_il_offset (il_offsets, 0, st_address);
131
132         /* This is the first actual code line of the method. */
133         record_line_number (minfo, minfo->method_info.code_start + st_address, st_line, TRUE);
134
135         /* start lines of basic blocks */
136         for (i = 0; i < cfg->block_count; ++i) {
137                 int j;
138
139                 for (j = 0; cfg->bblocks [i].forest && (j < cfg->bblocks [i].forest->len); ++j) {
140                         MBTree *t = (MBTree *) g_ptr_array_index (cfg->bblocks [i].forest, j);
141                         gint32 line_inc = 0, addr_inc;
142
143                         if (!i && !j) {
144                                 st_line = minfo->first_line;
145                                 st_address = t->addr - 1;
146
147                                 record_line_number (minfo, cfg->start + st_address, st_line, TRUE);
148                         }
149
150                         addr_inc = t->addr - st_address - 1;
151                         st_address += addr_inc;
152
153                         if (t->cli_addr != -1)
154                                 record_il_offset (il_offsets, t->cli_addr, st_address);
155
156                         if (!info->moffsets)
157                                 continue;
158
159
160                         if (t->cli_addr != -1) {
161                                 int *lines = info->moffsets + st_line;
162                                 int *k = lines;
163
164                                 while ((*k != -1) && (*k < t->cli_addr))
165                                         k++;
166
167                                 line_inc = k - lines;
168                         }
169
170                         st_line += line_inc;
171
172                         record_line_number (minfo, minfo->method_info.code_start + st_address,
173                                             st_line, j == 0);
174                 }
175         }
176
177         minfo->method_info.num_il_offsets = il_offsets->len;
178         minfo->method_info.il_offsets = g_new0 (MonoDebugILOffsetInfo, il_offsets->len);
179         for (i = 0; i < il_offsets->len; i++) {
180                 MonoDebugILOffsetInfo *il = (MonoDebugILOffsetInfo *) g_ptr_array_index (il_offsets, i);
181
182                 minfo->method_info.il_offsets [i] = *il;
183         }
184
185         g_ptr_array_free (il_offsets, TRUE);
186 }
187
188 static void
189 free_method_info (DebugMethodInfo *minfo)
190 {
191         if (minfo->line_numbers)
192                 g_ptr_array_free (minfo->line_numbers, TRUE);
193         g_free (minfo->method_info.params);
194         g_free (minfo->method_info.locals);
195         g_free (minfo);
196 }
197
198 static AssemblyDebugInfo*
199 mono_debug_open_assembly (MonoDebugHandle* handle, MonoImage *image)
200 {
201         GList *tmp;
202         AssemblyDebugInfo* info;
203
204         for (tmp = handle->info; tmp; tmp = tmp->next) {
205                 info = (AssemblyDebugInfo*)tmp->data;
206                 if (strcmp (info->name, image->assembly_name) == 0)
207                         return info;
208         }
209         info = g_new0 (AssemblyDebugInfo, 1);
210         info->format = handle->default_format;
211         switch (handle->default_format) {
212         case MONO_DEBUG_FORMAT_STABS:
213                 info->filename = g_strdup_printf ("%s-stabs.s", image->assembly_name);
214                 break;
215         case MONO_DEBUG_FORMAT_DWARF2:
216                 info->filename = g_strdup_printf ("%s-dwarf.s", image->assembly_name);
217                 break;
218         case MONO_DEBUG_FORMAT_DWARF2_PLUS:
219                 info->filename = g_strdup_printf ("%s-debug.o", image->assembly_name);
220
221                 g_message (G_STRLOC ": %s - %s", image->assembly_name, image->name);
222
223                 /* Fall back to dwarf if we can't find the symbol file. */
224                 if (!g_file_test (info->filename, G_FILE_TEST_EXISTS)) {
225                         gchar *fname = g_strdup_printf ("%s-dwarf.s", image->assembly_name);
226
227                         if (g_file_test (fname, G_FILE_TEST_EXISTS)) {
228                                 g_warning ("Can't open symbol file `%s', falling back to DWARF 2.",
229                                            info->filename);
230                                 g_free (info->filename);
231                                 info->filename = fname;
232                                 info->format = MONO_DEBUG_FORMAT_DWARF2;
233                         } else
234                                 g_free (fname);
235                 }
236
237                 break;
238         }
239         info->image = image;
240         info->image->ref_count++;
241         info->name = g_strdup (image->assembly_name);
242         info->methods = g_hash_table_new_full (g_direct_hash, g_direct_equal,
243                                                NULL, (GDestroyNotify) free_method_info);
244         info->source_files = g_ptr_array_new ();
245         info->type_hash = g_hash_table_new (NULL, NULL);
246
247         g_ptr_array_add (info->source_files, g_strdup_printf ("%s.il", image->assembly_name));
248         info->producer_name = g_strdup_printf ("Mono JIT compiler version %s", VERSION);
249
250         mono_debug_get_type (info, mono_defaults.void_class);
251         mono_debug_get_type (info, mono_defaults.object_class);
252         mono_debug_get_type (info, mono_defaults.void_class);
253         mono_debug_get_type (info, mono_defaults.boolean_class);
254         mono_debug_get_type (info, mono_defaults.char_class);
255         mono_debug_get_type (info, mono_defaults.sbyte_class);
256         mono_debug_get_type (info, mono_defaults.byte_class);
257         mono_debug_get_type (info, mono_defaults.int16_class);
258         mono_debug_get_type (info, mono_defaults.uint16_class);
259         mono_debug_get_type (info, mono_defaults.int32_class);
260         mono_debug_get_type (info, mono_defaults.uint32_class);
261         mono_debug_get_type (info, mono_defaults.int_class);
262         mono_debug_get_type (info, mono_defaults.uint_class);
263         mono_debug_get_type (info, mono_defaults.int64_class);
264         mono_debug_get_type (info, mono_defaults.uint64_class);
265         mono_debug_get_type (info, mono_defaults.single_class);
266         mono_debug_get_type (info, mono_defaults.double_class);
267         mono_debug_get_type (info, mono_defaults.string_class);
268
269         switch (info->format) {
270         case MONO_DEBUG_FORMAT_STABS:
271                 mono_debug_open_assembly_stabs (info);
272                 break;
273         case MONO_DEBUG_FORMAT_DWARF2:
274                 mono_debug_open_assembly_dwarf2 (info);
275                 break;
276         case MONO_DEBUG_FORMAT_DWARF2_PLUS:
277                 mono_debug_open_assembly_dwarf2_plus (info);
278                 break;
279         }
280
281         info->next_idx = 100;
282         handle->info = g_list_prepend (handle->info, info);
283
284         info->nmethods = image->tables [MONO_TABLE_METHOD].rows + 1;
285         info->mlines = g_new0 (int, info->nmethods);
286
287         if (info->format != MONO_DEBUG_FORMAT_DWARF2_PLUS)
288                 debug_load_method_lines (info);
289
290         return info;
291 }
292
293 void
294 mono_debug_make_symbols (void)
295 {
296         GList *tmp;
297         AssemblyDebugInfo* info;
298
299         if (!mono_debug_handle)
300                 return;
301
302         for (tmp = mono_debug_handle->info; tmp; tmp = tmp->next) {
303                 info = (AssemblyDebugInfo*)tmp->data;
304
305                 switch (info->format) {
306                 case MONO_DEBUG_FORMAT_STABS:
307                         mono_debug_write_assembly_stabs (info);
308                         break;
309                 case MONO_DEBUG_FORMAT_DWARF2:
310                         mono_debug_write_assembly_dwarf2 (info);
311                         break;
312                 case MONO_DEBUG_FORMAT_DWARF2_PLUS:
313                         mono_debug_write_assembly_dwarf2_plus (info);
314                         break;
315                 }
316         }
317 }
318
319 static void
320 mono_debug_close_assembly (AssemblyDebugInfo* info)
321 {
322         g_free (info->mlines);
323         g_free (info->moffsets);
324         g_free (info->name);
325         g_free (info->filename);
326         g_ptr_array_free (info->source_files, TRUE);
327         g_hash_table_destroy (info->type_hash);
328         g_hash_table_destroy (info->methods);
329         g_free (info->producer_name);
330         g_free (info);
331 }
332
333 void
334 mono_debug_close (MonoDebugHandle* debug)
335 {
336         GList *tmp;
337         AssemblyDebugInfo* info;
338
339         mono_debug_make_symbols ();
340
341         for (tmp = debug->info; tmp; tmp = tmp->next) {
342                 info = (AssemblyDebugInfo*)tmp->data;
343
344                 switch (info->format) {
345                 case MONO_DEBUG_FORMAT_STABS:
346                         mono_debug_close_assembly_stabs (info);
347                         break;
348                 case MONO_DEBUG_FORMAT_DWARF2:
349                         mono_debug_close_assembly_dwarf2 (info);
350                         break;
351                 case MONO_DEBUG_FORMAT_DWARF2_PLUS:
352                         mono_debug_close_assembly_dwarf2_plus (info);
353                         break;
354                 }
355
356                 mono_debug_close_assembly (info);
357         }
358
359         g_free (debug->name);
360         g_free (debug);
361 }
362
363 guint32
364 mono_debug_get_type (AssemblyDebugInfo* info, MonoClass *klass)
365 {
366         guint index, i;
367
368         mono_class_init (klass);
369
370         index = GPOINTER_TO_INT (g_hash_table_lookup (info->type_hash, klass));
371         if (index)
372                 return index;
373
374         index = ++info->next_klass_idx;
375         g_hash_table_insert (info->type_hash, klass, GINT_TO_POINTER (index));
376
377         if (klass->enumtype)
378                 return index;
379
380         switch (klass->byval_arg.type) {
381         case MONO_TYPE_CLASS:
382                 if (klass->parent)
383                         mono_debug_get_type (info, klass->parent);
384
385                 for (i = 0; i < klass->method.count; i++) {
386                         MonoMethod *method = klass->methods [i];
387                         MonoType *ret_type = NULL;
388                         int j;
389
390                         if (method->signature->ret->type != MONO_TYPE_VOID)
391                                 ret_type = method->signature->ret;
392
393                         if (ret_type) {
394                                 MonoClass *ret_klass = mono_class_from_mono_type (ret_type);
395                                 mono_debug_get_type (info, ret_klass);
396                         }
397
398                         for (j = 0; j < method->signature->param_count; j++) {
399                                 MonoType *sub_type = method->signature->params [j];
400                                 MonoClass *sub_klass = mono_class_from_mono_type (sub_type);
401                                 mono_debug_get_type (info, sub_klass);
402                         }
403                 }
404                 // fall through
405         case MONO_TYPE_VALUETYPE:
406                 for (i = 0; i < klass->field.count; i++) {
407                         MonoClass *subclass = mono_class_from_mono_type (klass->fields [i].type);
408                         mono_debug_get_type (info, subclass);
409                 }
410                 break;
411         case MONO_TYPE_ARRAY:
412         case MONO_TYPE_SZARRAY:
413                 mono_debug_get_type (info, klass->element_class);
414                 break;
415         default:
416                 break;
417         }
418
419         return index;
420 }
421
422 void
423 mono_debug_add_type (MonoDebugHandle* debug, MonoClass *klass)
424 {
425         AssemblyDebugInfo* info = mono_debug_open_assembly (debug, klass->image);
426
427         mono_debug_get_type (info, klass);
428 }
429
430 void
431 mono_debug_add_method (MonoDebugHandle* debug, MonoFlowGraph *cfg)
432 {
433         MonoMethod *method = cfg->method;
434         MonoClass *klass = method->klass;
435         AssemblyDebugInfo* info = mono_debug_open_assembly (debug, klass->image);
436         int method_number = 0, line = 0, start_line = 0, end_line = 0, i;
437         DebugMethodInfo *minfo;
438         char *name;
439
440         mono_class_init (klass);
441         /*
442          * Find the method index in the image.
443          */
444         for (i = 0; klass->methods && i < klass->method.count; ++i) {
445                 if (klass->methods [i] == method) {
446                         method_number = klass->method.first + i + 1;
447                         line = info->mlines [method_number];
448                         break;
449                 }
450         }
451
452         if (g_hash_table_lookup (info->methods, method))
453                 return;
454
455         if (info->moffsets) {
456                 /* info->moffsets contains -1 "outside" of functions. */
457                 for (i = line; (i > 0) && (info->moffsets [i] == 0); i--)
458                         ;
459                 start_line = i + 1;
460
461                 for (i = start_line; info->moffsets [i] != -1; i++)
462                         ;
463                 end_line = i;
464         }
465
466         name = g_strdup_printf ("%s%s%s.%s", klass->name_space, klass->name_space [0]? ".": "",
467                                 klass->name, method->name);
468
469         minfo = g_new0 (DebugMethodInfo, 1);
470         minfo->name = name;
471         minfo->start_line = start_line;
472         minfo->first_line = line;
473         minfo->last_line = end_line;
474         minfo->method_info.code_start = cfg->start + 1;
475         minfo->method_info.code_size = cfg->epilogue_end - 1;
476         minfo->method_number = method_number;
477         minfo->method_info.method = method;
478         minfo->method_info.num_params = method->signature->param_count;
479         minfo->method_info.params = g_new0 (MonoDebugVarInfo, minfo->method_info.num_params);
480         minfo->method_info.prologue_end = cfg->prologue_end - 1;
481         minfo->method_info.epilogue_begin = cfg->epilog - 1;
482
483         if (method->signature->hasthis) {
484                 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->args_start_index;
485
486                 minfo->method_info.this_var = g_new0 (MonoDebugVarInfo, 1);
487                 minfo->method_info.this_var->offset = ptr->offset;
488         }
489
490         for (i = 0; i < minfo->method_info.num_params; i++) {
491                 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->args_start_index +
492                         method->signature->hasthis;
493
494                 minfo->method_info.params [i].offset = ptr [i].offset;
495         }
496
497         if (!method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
498                 MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
499                 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->locals_start_index;
500
501                 minfo->method_info.num_locals = header->num_locals;
502                 minfo->method_info.locals = g_new0 (MonoDebugVarInfo, header->num_locals);
503                 for (i = 0; i < minfo->method_info.num_locals; i++) {
504                         minfo->method_info.locals [i].offset = ptr [i].offset;
505                         minfo->method_info.locals [i].begin_scope = minfo->method_info.prologue_end;
506                         minfo->method_info.locals [i].end_scope = minfo->method_info.epilogue_begin;
507                 }
508         }
509
510         debug_generate_method_lines (info, minfo, cfg);
511
512         g_hash_table_insert (info->methods, method, minfo);
513 }