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