2002-05-22 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 static void
27 debug_arg_warning (const char *message)
28 {
29         g_warning ("Error while processing --debug-args arguments: %s", message);
30 }
31
32 MonoDebugHandle*
33 mono_debug_open (MonoAssembly *assembly, MonoDebugFormat format, const char **args)
34 {
35         MonoDebugHandle *debug;
36         const char **ptr;
37         
38         debug = g_new0 (MonoDebugHandle, 1);
39         debug->name = g_strdup (assembly->image->name);
40         debug->format = format;
41         debug->producer_name = g_strdup_printf ("Mono JIT compiler version %s", VERSION);
42         debug->next_idx = 100;
43
44         debug->type_hash = g_hash_table_new (NULL, NULL);
45         debug->methods = g_hash_table_new_full (g_direct_hash, g_direct_equal,
46                                                 NULL, (GDestroyNotify) free_method_info);
47         debug->source_files = g_ptr_array_new ();
48
49         for (ptr = args; ptr && *ptr; ptr++) {
50                 const char *arg = *ptr;
51                 gchar *message;
52
53                 switch (debug->format) {
54                 case MONO_DEBUG_FORMAT_STABS:
55                 case MONO_DEBUG_FORMAT_DWARF2:
56                         if (!strncmp (arg, "filename=", 9)) {
57                                 if (debug->filename)
58                                         debug_arg_warning ("The `filename' argument can be given only once.");
59                                 debug->filename = g_strdup (arg + 9);
60                                 continue;
61                         } else if (!strncmp (arg, "objfile=", 8)) {
62                                 if (debug->objfile)
63                                         debug_arg_warning ("The `objfile' argument can be given only once.");
64                                 debug->objfile = g_strdup (arg + 8);
65                                 continue;
66                         } else if (!strcmp (arg, "install_il_files")) {
67                                 debug->flags |= MONO_DEBUG_FLAGS_INSTALL_IL_FILES;
68                                 continue;
69                         } else if (!strcmp (arg, "dont_update_il_files")) {
70                                 debug->flags |= MONO_DEBUG_FLAGS_DONT_UPDATE_IL_FILES;
71                                 continue;
72                         } else if (!strcmp (arg, "dont_create_il_files")) {
73                                 debug->flags |= MONO_DEBUG_FLAGS_DONT_CREATE_IL_FILES;
74                                 continue;
75                         }
76                         break;
77                 case MONO_DEBUG_FORMAT_DWARF2_PLUS:
78                         if (!strcmp (arg, "dont_fallback")) {
79                                 debug->flags |= MONO_DEBUG_FLAGS_DONT_FALLBACK;
80                                 continue;
81                         } else if (!strcmp (arg, "dont_precompile")) {
82                                 debug->flags |= MONO_DEBUG_FLAGS_DONT_PRECOMPILE;
83                                 continue;
84                         }
85                         break;
86                 default:
87                         break;
88                 }
89
90                 if (!strcmp (arg, "dont_assemble")) {
91                         debug->flags |= MONO_DEBUG_FLAGS_DONT_ASSEMBLE;
92                         continue;
93                 } else if (!strcmp (arg, "update_on_exit")) {
94                         debug->flags |= MONO_DEBUG_FLAGS_UPDATE_ON_EXIT;
95                         continue;
96                 }
97
98                 message = g_strdup_printf ("Unknown argument `%s'.", arg);
99                 debug_arg_warning (message);
100                 g_free (message);
101         }
102
103         switch (debug->format) {
104         case MONO_DEBUG_FORMAT_STABS:
105                 if (!debug->filename)
106                         debug->filename = g_strdup_printf ("%s-stabs.s", g_basename (debug->name));
107                 if (!debug->objfile)
108                         debug->objfile = g_strdup_printf ("%s.o", g_basename (debug->name));
109                 break;
110         case MONO_DEBUG_FORMAT_DWARF2:
111                 if (!debug->filename)
112                         debug->filename = g_strdup_printf ("%s-dwarf.s", g_basename (debug->name));
113                 if (!debug->objfile)
114                         debug->objfile = g_strdup_printf ("%s.o", g_basename (debug->name));
115                 break;
116         case MONO_DEBUG_FORMAT_DWARF2_PLUS:
117                 if (!mono_default_debug_handle && !(debug->flags & MONO_DEBUG_FLAGS_DONT_FALLBACK))
118                         mono_debug_open (assembly, MONO_DEBUG_FORMAT_DWARF2, NULL);
119                 break;
120         default:
121                 g_assert_not_reached ();
122         }
123
124         debug->next = mono_debug_handles;
125         mono_debug_handles = debug;
126
127         if (!mono_default_debug_handle && (debug->format != MONO_DEBUG_FORMAT_DWARF2_PLUS))
128                 mono_default_debug_handle = debug;
129
130         mono_debug_add_image (debug, assembly->image);
131
132         return debug;
133 }
134
135 static void
136 debug_load_method_lines (AssemblyDebugInfo* info)
137 {
138         FILE *f;
139         char buf [1024];
140         int i, mnum;
141         int offset = -1;
142
143         if (!(info->handle->flags & MONO_DEBUG_FLAGS_DONT_UPDATE_IL_FILES)) {
144                 char *command = g_strdup_printf ("monodis --output=%s %s",
145                                                  info->ilfile, info->image->name);
146                 struct stat stata, statb;
147                 int need_update = FALSE;
148
149                 if (stat (info->image->name, &stata)) {
150                         g_warning ("cannot access assembly file (%s): %s",
151                                    info->image->name, g_strerror (errno));
152                         g_free (command);
153                         return;
154                 }
155
156                 /* If the stat() failed or the file is older. */
157                 if (stat (info->ilfile, &statb)) {
158                         /* Don't create any new *.il files if the user told us not to do so. */
159                         if (!(info->handle->flags & MONO_DEBUG_FLAGS_DONT_CREATE_IL_FILES))
160                                 need_update = TRUE;
161                 } else if (statb.st_mtime < stata.st_mtime)
162                         need_update = TRUE;
163
164                 if (need_update) {
165                         g_print ("Recreating %s from %s.\n", info->ilfile, info->image->name);
166                         if (system (command)) {
167                                 g_warning ("cannot create IL assembly file (%s): %s",
168                                            command, g_strerror (errno));
169                                 g_free (command);
170                                 return;
171                         }
172                 }
173         }
174
175         /* use an env var with directories for searching. */
176         if (!(f = fopen (info->ilfile, "r"))) {
177                 g_warning ("cannot open IL assembly file %s", info->ilfile);
178                 return;
179         }
180
181         info->total_lines = 100;
182         info->moffsets = g_malloc (info->total_lines * sizeof (int));
183
184         i = 0;
185         while (fgets (buf, sizeof (buf), f)) {
186                 int pos = i;
187
188                 info->moffsets [i++] = offset;
189                 if (i + 2 >= info->total_lines) {
190                         info->total_lines += 100;
191                         info->moffsets = g_realloc (info->moffsets, info->total_lines * sizeof (int));
192                         g_assert (info->moffsets);
193                 }
194
195                 if (!sscanf (buf, " // method line %d", &mnum))
196                         continue;
197
198                 offset = 0;
199
200                 if (mnum >= info->nmethods)
201                         break;
202
203                 while (fgets (buf, sizeof (buf), f)) {
204                         int newoffset;
205
206                         ++i;
207                         if (i + 2 >= info->total_lines) {
208                                 info->total_lines += 100;
209                                 info->moffsets = g_realloc (info->moffsets, info->total_lines * sizeof (int));
210                                 g_assert (info->moffsets);
211                         }
212
213                         if (strstr (buf, "}")) {
214                                 offset = -1;
215                                 break;
216                         }
217
218                         if (sscanf (buf, " IL_%x:", &newoffset)) {
219                                 offset = newoffset;
220                                 if (!offset)
221                                         pos = i;
222                         }
223
224                         info->moffsets [i] = offset;
225                 }
226                 /* g_print ("method %d found at %d\n", mnum, pos); */
227                 info->mlines [mnum] = pos;
228         }
229         fclose (f);
230 }
231
232 static void
233 record_line_number (DebugMethodInfo *minfo, gpointer address, guint32 line, int is_basic_block)
234 {
235         DebugLineNumberInfo *lni = g_new0 (DebugLineNumberInfo, 1);
236
237         lni->address = address;
238         lni->line = line;
239         lni->is_basic_block = is_basic_block;
240         lni->source_file = minfo->source_file;
241
242         g_ptr_array_add (minfo->line_numbers, lni);
243 }
244
245 static void
246 record_il_offset (GPtrArray *array, guint32 offset, guint32 address)
247 {
248         MonoDebugILOffsetInfo *info = g_new0 (MonoDebugILOffsetInfo, 1);
249
250         info->offset = offset;
251         info->address = address;
252
253         g_ptr_array_add (array, info);
254 }
255
256 static void
257 debug_generate_method_lines (AssemblyDebugInfo *info, DebugMethodInfo *minfo, MonoFlowGraph* cfg)
258 {
259         guint32 st_address, st_line;
260         GPtrArray *il_offsets;
261         int i;
262
263         il_offsets = g_ptr_array_new ();
264         minfo->line_numbers = g_ptr_array_new ();
265
266         st_line = minfo->first_line;
267         st_address = minfo->method_info.prologue_end;
268
269         /* record_line_number takes absolute memory addresses. */
270         record_line_number (minfo, minfo->method_info.code_start, minfo->start_line, FALSE);
271         /* record_il_offsets uses offsets relative to minfo->method_info.code_start. */
272         record_il_offset (il_offsets, 0, st_address);
273
274         /* This is the first actual code line of the method. */
275         record_line_number (minfo, minfo->method_info.code_start + st_address, st_line, TRUE);
276
277         /* start lines of basic blocks */
278         for (i = 0; i < cfg->block_count; ++i) {
279                 int j;
280
281                 for (j = 0; cfg->bblocks [i].forest && (j < cfg->bblocks [i].forest->len); ++j) {
282                         MBTree *t = (MBTree *) g_ptr_array_index (cfg->bblocks [i].forest, j);
283                         gint32 line_inc = 0, addr_inc;
284
285                         if (!i && !j) {
286                                 st_line = minfo->first_line;
287                                 st_address = t->addr - 1;
288
289                                 record_line_number (minfo, cfg->start + st_address, st_line, TRUE);
290                         }
291
292                         addr_inc = t->addr - st_address - 1;
293                         st_address += addr_inc;
294
295                         if (t->cli_addr != -1)
296                                 record_il_offset (il_offsets, t->cli_addr, st_address);
297
298                         if (!info->moffsets)
299                                 continue;
300
301
302                         if (t->cli_addr != -1) {
303                                 int *lines = info->moffsets + st_line;
304                                 int *k = lines;
305
306                                 while ((*k != -1) && (*k < t->cli_addr))
307                                         k++;
308
309                                 line_inc = k - lines;
310                         }
311
312                         st_line += line_inc;
313
314                         record_line_number (minfo, minfo->method_info.code_start + st_address,
315                                             st_line, j == 0);
316                 }
317         }
318
319         minfo->method_info.num_il_offsets = il_offsets->len;
320         minfo->method_info.il_offsets = g_new0 (MonoDebugILOffsetInfo, il_offsets->len);
321         for (i = 0; i < il_offsets->len; i++) {
322                 MonoDebugILOffsetInfo *il = (MonoDebugILOffsetInfo *) g_ptr_array_index (il_offsets, i);
323
324                 minfo->method_info.il_offsets [i] = *il;
325         }
326
327         g_ptr_array_free (il_offsets, TRUE);
328 }
329
330 static AssemblyDebugInfo *
331 mono_debug_get_image (MonoDebugHandle* debug, MonoImage *image)
332 {
333         GList *tmp;
334         AssemblyDebugInfo *info;
335
336         if (debug->format == MONO_DEBUG_FORMAT_NONE)
337                 return NULL;
338
339         for (tmp = debug->info; tmp; tmp = tmp->next) {
340                 info = (AssemblyDebugInfo*)tmp->data;
341
342                 if (info->image == image)
343                         return info;
344         }
345
346         return NULL;
347 }
348
349 static AssemblyDebugInfo *
350 mono_debug_open_image (MonoDebugHandle* debug, MonoImage *image)
351 {
352         AssemblyDebugInfo *info;
353
354         info = mono_debug_get_image (debug, image);
355         if (info != NULL)
356                 return info;
357
358         info = g_new0 (AssemblyDebugInfo, 1);
359         info->image = image;
360         info->image->ref_count++;
361         info->name = g_strdup (image->assembly_name);
362         info->format = debug->format;
363         info->handle = debug;
364
365         info->source_file = debug->source_files->len;
366         g_ptr_array_add (debug->source_files, g_strdup_printf ("%s.il", image->assembly_name));
367
368         debug->info = g_list_prepend (debug->info, info);
369
370         info->nmethods = image->tables [MONO_TABLE_METHOD].rows + 1;
371         info->mlines = g_new0 (int, info->nmethods);
372
373         switch (info->format) {
374         case MONO_DEBUG_FORMAT_STABS:
375         case MONO_DEBUG_FORMAT_DWARF2:
376                 if (debug->flags & MONO_DEBUG_FLAGS_INSTALL_IL_FILES) {
377                         gchar *dirname = g_path_get_dirname (image->name);
378                         info->ilfile = g_strdup_printf ("%s/%s.il", dirname, info->name);
379                         g_free (dirname);
380                 } else
381                         info->ilfile = g_strdup_printf ("%s.il", info->name);
382                 break;
383         case MONO_DEBUG_FORMAT_DWARF2_PLUS:
384                 info->filename = g_strdup_printf ("%s-debug.s", info->name);
385                 info->objfile = g_strdup_printf ("%s-debug.o", info->name);
386                 mono_debug_open_assembly_dwarf2_plus (info);
387                 break;
388         default:
389                 break;
390         }
391
392         if (debug->format != MONO_DEBUG_FORMAT_DWARF2_PLUS)
393                 debug_load_method_lines (info);
394
395         return info;
396 }
397
398 void
399 mono_debug_add_image (MonoDebugHandle* debug, MonoImage *image)
400 {
401         mono_debug_open_image (debug, image);
402 }
403
404 void
405 mono_debug_write_symbols (MonoDebugHandle *debug)
406 {
407         GList *tmp;
408
409         if (!debug)
410                 return;
411
412         switch (debug->format) {
413         case MONO_DEBUG_FORMAT_STABS:
414                 mono_debug_write_stabs (debug);
415                 break;
416         case MONO_DEBUG_FORMAT_DWARF2:
417                 mono_debug_write_dwarf2 (debug);
418                 break;
419         case MONO_DEBUG_FORMAT_DWARF2_PLUS:
420                 for (tmp = debug->info; tmp; tmp = tmp->next) {
421                         AssemblyDebugInfo *info = (AssemblyDebugInfo*)tmp->data;
422
423                         mono_debug_write_assembly_dwarf2_plus (info);
424                 }
425                 break;
426         default:
427                 g_assert_not_reached ();
428         }
429 }
430
431 void
432 mono_debug_make_symbols (void)
433 {
434         MonoDebugHandle *debug;
435
436         for (debug = mono_debug_handles; debug; debug = debug->next)
437                 mono_debug_write_symbols (debug);
438 }
439
440 static void
441 mono_debug_close_assembly (AssemblyDebugInfo* info)
442 {
443         switch (info->format) {
444         case MONO_DEBUG_FORMAT_DWARF2_PLUS:
445                 mono_debug_close_assembly_dwarf2_plus (info);
446                 break;
447         default:
448                 break;
449         }
450         g_free (info->mlines);
451         g_free (info->moffsets);
452         g_free (info->name);
453         g_free (info->ilfile);
454         g_free (info->filename);
455         g_free (info->objfile);
456         g_free (info);
457 }
458
459 void
460 mono_debug_cleanup (void)
461 {
462         MonoDebugHandle *debug, *temp;
463
464         for (debug = mono_debug_handles; debug; debug = temp) {
465                 GList *tmp;
466
467                 if (debug->flags & MONO_DEBUG_FLAGS_UPDATE_ON_EXIT)
468                         mono_debug_write_symbols (debug);
469
470
471                 for (tmp = debug->info; tmp; tmp = tmp->next) {
472                         AssemblyDebugInfo* info = (AssemblyDebugInfo*)tmp->data;
473
474                         mono_debug_close_assembly (info);
475                 }
476
477                 g_ptr_array_free (debug->source_files, TRUE);
478                 g_hash_table_destroy (debug->methods);
479                 g_hash_table_destroy (debug->type_hash);
480                 g_free (debug->producer_name);
481                 g_free (debug->name);
482
483                 temp = debug->next;
484                 g_free (debug);
485         }
486
487         mono_debug_handles = NULL;
488         mono_default_debug_handle = NULL;
489 }
490
491 guint32
492 mono_debug_get_type (MonoDebugHandle *debug, MonoClass *klass)
493 {
494         guint index, i;
495
496         mono_class_init (klass);
497
498         index = GPOINTER_TO_INT (g_hash_table_lookup (debug->type_hash, klass));
499         if (index)
500                 return index;
501
502         index = ++debug->next_klass_idx;
503         g_hash_table_insert (debug->type_hash, klass, GINT_TO_POINTER (index));
504
505         if (klass->enumtype)
506                 return index;
507
508         switch (klass->byval_arg.type) {
509         case MONO_TYPE_CLASS:
510                 if (klass->parent)
511                         mono_debug_get_type (debug, klass->parent);
512
513                 for (i = 0; i < klass->method.count; i++) {
514                         MonoMethod *method = klass->methods [i];
515                         MonoType *ret_type = NULL;
516                         int j;
517
518                         if (method->signature->ret->type != MONO_TYPE_VOID)
519                                 ret_type = method->signature->ret;
520
521                         if (ret_type) {
522                                 MonoClass *ret_klass = mono_class_from_mono_type (ret_type);
523                                 mono_debug_get_type (debug, ret_klass);
524                         }
525
526                         for (j = 0; j < method->signature->param_count; j++) {
527                                 MonoType *sub_type = method->signature->params [j];
528                                 MonoClass *sub_klass = mono_class_from_mono_type (sub_type);
529                                 mono_debug_get_type (debug, sub_klass);
530                         }
531                 }
532                 // fall through
533         case MONO_TYPE_VALUETYPE:
534                 for (i = 0; i < klass->field.count; i++) {
535                         MonoClass *subclass = mono_class_from_mono_type (klass->fields [i].type);
536                         mono_debug_get_type (debug, subclass);
537                 }
538                 break;
539         case MONO_TYPE_ARRAY:
540         case MONO_TYPE_SZARRAY:
541                 mono_debug_get_type (debug, klass->element_class);
542                 break;
543         default:
544                 break;
545         }
546
547         return index;
548 }
549
550 MonoDebugHandle *
551 mono_debug_handle_from_class (MonoClass *klass)
552 {
553         MonoDebugHandle *debug;
554
555         mono_class_init (klass);
556
557         for (debug = mono_debug_handles; debug; debug = debug->next) {
558                 GList *tmp;
559
560                 for (tmp = debug->info; tmp; tmp = tmp->next) {
561                         AssemblyDebugInfo *info = (AssemblyDebugInfo*)tmp->data;
562
563                         if (info->image == klass->image)
564                                 return debug;
565                 }
566         }
567
568         return NULL;
569 }
570
571 void
572 mono_debug_add_type (MonoClass *klass)
573 {
574         MonoDebugHandle *debug = mono_debug_handle_from_class (klass);
575
576         g_assert (debug != NULL);
577
578         mono_debug_get_type (debug, klass);
579 }
580
581 void
582 mono_debug_add_method (MonoFlowGraph *cfg)
583 {
584         MonoMethod *method = cfg->method;
585         MonoClass *klass = method->klass;
586         int method_number = 0, line = 0, start_line = 0, end_line = 0, i;
587         MonoDebugHandle* debug;
588         AssemblyDebugInfo* info;
589         DebugMethodInfo *minfo;
590         char *name;
591
592         mono_class_init (klass);
593
594         debug = mono_debug_handle_from_class (klass);
595         if (!debug) {
596                 if (mono_default_debug_handle)
597                         debug = mono_default_debug_handle;
598                 else
599                         return;
600         }
601
602         info = mono_debug_open_image (debug, klass->image);
603
604         /*
605          * Find the method index in the image.
606          */
607         for (i = 0; klass->methods && i < klass->method.count; ++i) {
608                 if (klass->methods [i] == method) {
609                         method_number = klass->method.first + i + 1;
610                         line = info->mlines [method_number];
611                         break;
612                 }
613         }
614
615         if (g_hash_table_lookup (debug->methods, method))
616                 return;
617
618         if (info->moffsets) {
619                 /* info->moffsets contains -1 "outside" of functions. */
620                 for (i = line; (i > 0) && (info->moffsets [i] == 0); i--)
621                         ;
622                 start_line = i + 1;
623
624                 for (i = start_line; info->moffsets [i] != -1; i++)
625                         ;
626                 end_line = i;
627         }
628
629         name = g_strdup_printf ("%s%s%s.%s", klass->name_space, klass->name_space [0]? ".": "",
630                                 klass->name, method->name);
631
632         minfo = g_new0 (DebugMethodInfo, 1);
633         minfo->name = name;
634         minfo->start_line = start_line;
635         minfo->first_line = line;
636         minfo->last_line = end_line;
637         minfo->source_file = info->source_file;
638         minfo->info = info;
639         minfo->method_info.code_start = cfg->start + 1;
640         minfo->method_info.code_size = cfg->epilogue_end - 1;
641         minfo->method_number = method_number;
642         minfo->method_info.method = method;
643         minfo->method_info.num_params = method->signature->param_count;
644         minfo->method_info.params = g_new0 (MonoDebugVarInfo, minfo->method_info.num_params);
645         minfo->method_info.prologue_end = cfg->prologue_end - 1;
646         minfo->method_info.epilogue_begin = cfg->epilog - 1;
647
648         if (method->signature->hasthis) {
649                 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->args_start_index;
650
651                 minfo->method_info.this_var = g_new0 (MonoDebugVarInfo, 1);
652                 minfo->method_info.this_var->offset = ptr->offset;
653         }
654
655         for (i = 0; i < minfo->method_info.num_params; i++) {
656                 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->args_start_index +
657                         method->signature->hasthis;
658
659                 minfo->method_info.params [i].offset = ptr [i].offset;
660         }
661
662         if (!method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
663                 MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
664                 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->locals_start_index;
665
666                 minfo->method_info.num_locals = header->num_locals;
667                 minfo->method_info.locals = g_new0 (MonoDebugVarInfo, header->num_locals);
668                 for (i = 0; i < minfo->method_info.num_locals; i++) {
669                         minfo->method_info.locals [i].offset = ptr [i].offset;
670                         minfo->method_info.locals [i].begin_scope = minfo->method_info.prologue_end;
671                         minfo->method_info.locals [i].end_scope = minfo->method_info.epilogue_begin;
672                 }
673         }
674
675         debug_generate_method_lines (info, minfo, cfg);
676
677         g_hash_table_insert (debug->methods, method, minfo);
678 }
679
680 static gint32
681 il_offset_from_address (DebugMethodInfo *minfo, guint32 address)
682 {
683         int i;
684
685         for (i = 0; i < minfo->method_info.num_il_offsets; i++) {
686                 MonoDebugILOffsetInfo *ilo = &minfo->method_info.il_offsets [i];
687
688                 if (ilo->address > address)
689                         return ilo->offset;
690         }
691
692         return -1;
693 }
694
695 gchar *
696 mono_debug_source_location_from_address (MonoMethod *method, guint32 address)
697 {
698         MonoDebugHandle *debug;
699         DebugMethodInfo *minfo = NULL;
700         int i;
701
702         for (debug = mono_debug_handles; debug; debug = debug->next) {
703                 minfo = g_hash_table_lookup (debug->methods, method);
704
705                 if (minfo)
706                         break;
707         }
708
709         if (!minfo)
710                 return NULL;
711
712         if (minfo->info->symfile) {
713                 guint32 offset = il_offset_from_address (minfo, address);
714                 
715                 if (offset < 0)
716                         return NULL;
717
718                 return mono_debug_find_source_location (minfo->info->symfile, method, offset);
719         }
720
721         if (!minfo->line_numbers)
722                 return NULL;
723
724         for (i = 0; i < minfo->line_numbers->len; i++) {
725                 DebugLineNumberInfo *lni = g_ptr_array_index (minfo->line_numbers, i);
726
727                 if ((gchar *)lni->address > minfo->method_info.code_start + address) {
728                         gchar *source_file = g_ptr_array_index (debug->source_files, lni->source_file);
729
730                         return g_strdup_printf ("%s:%d", source_file, lni->line);
731                 }
732         }
733
734         return NULL;
735 }
736
737 gint32
738 mono_debug_il_offset_from_address (MonoMethod *method, guint32 address)
739 {
740         MonoDebugHandle *debug;
741         MonoDebugMethodInfo *minfo = NULL;
742         int i;
743
744         for (debug = mono_debug_handles; debug; debug = debug->next) {
745                 minfo = g_hash_table_lookup (debug->methods, method);
746
747                 if (minfo)
748                         break;
749         }
750
751         if (!minfo || !minfo->il_offsets)
752                 return -1;
753
754         for (i = 0; i < minfo->num_il_offsets; i++) {
755                 MonoDebugILOffsetInfo *ilo = &minfo->il_offsets [i];
756                 
757                 if (ilo->address > address)
758                         return ilo->offset;
759         }
760
761         return -1;
762 }