7df70371cad895c336b33a28bdcd27ce7ab2bfc4
[mono.git] / mono / metadata / mono-debug.c
1 #include <config.h>
2 #include <mono/metadata/assembly.h>
3 #include <mono/metadata/tabledefs.h>
4 #include <mono/metadata/tokentype.h>
5 #include <mono/metadata/appdomain.h>
6 #include <mono/metadata/class-internals.h>
7 #include <mono/metadata/mono-debug.h>
8 #include <mono/metadata/mono-debug-debugger.h>
9 #include <mono/metadata/mono-endian.h>
10 #include <string.h>
11
12 #define SYMFILE_TABLE_CHUNK_SIZE        16
13 #define DATA_TABLE_PTR_CHUNK_SIZE       256
14 #define DATA_TABLE_CHUNK_SIZE           131072
15
16 MonoSymbolTable *mono_symbol_table = NULL;
17 MonoDebugFormat mono_debug_format = MONO_DEBUG_FORMAT_NONE;
18
19 static gboolean in_the_mono_debugger = FALSE;
20 static gboolean mono_debug_initialized = FALSE;
21 GHashTable *mono_debug_handles = NULL;
22
23 static GHashTable *method_hash = NULL;
24
25 static MonoDebugHandle     *mono_debug_open_image      (MonoImage *image);
26 static void                 mono_debug_close_image     (MonoDebugHandle *debug);
27
28 static MonoDebugHandle     *_mono_debug_get_image      (MonoImage *image);
29 static void                 mono_debug_add_assembly    (MonoAssembly *assembly,
30                                                         gpointer user_data);
31 static void                 mono_debug_start_add_type  (MonoClass *klass);
32 static void                 mono_debug_add_type        (MonoClass *klass);
33
34 extern void (*mono_debugger_class_init_func) (MonoClass *klass);
35 extern void (*mono_debugger_start_class_init_func) (MonoClass *klass);
36
37 typedef struct {
38         guint32 symfile_id;
39         guint32 domain_id;
40         guint32 method_id;
41 } MethodHashEntry;
42
43 static guint
44 method_hash_hash (gconstpointer data)
45 {
46         const MethodHashEntry *entry = (const MethodHashEntry *) data;
47         return entry->symfile_id | (entry->domain_id << 16);
48 }
49
50 static gint
51 method_hash_equal (gconstpointer ka, gconstpointer kb)
52 {
53         const MethodHashEntry *a = (const MethodHashEntry *) ka;
54         const MethodHashEntry *b = (const MethodHashEntry *) kb;
55
56         if ((a->symfile_id != b->symfile_id) || (a->method_id != b->method_id) || (a->domain_id != b->domain_id))
57                 return 0;
58         return 1;
59 }
60
61 /*
62  * Initialize debugging support.
63  *
64  * This method must be called after loading corlib,
65  * but before opening the application's main assembly because we need to set some
66  * callbacks here.
67  */
68 void
69 mono_debug_init (MonoDebugFormat format)
70 {
71         g_assert (!mono_debug_initialized);
72
73         mono_debug_initialized = TRUE;
74         mono_debug_format = format;
75         in_the_mono_debugger = format == MONO_DEBUG_FORMAT_DEBUGGER;
76
77         if (in_the_mono_debugger)
78                 mono_debugger_initialize ();
79
80         mono_debugger_lock ();
81
82         mono_symbol_table = g_new0 (MonoSymbolTable, 1);
83         mono_symbol_table->magic = MONO_DEBUGGER_MAGIC;
84         mono_symbol_table->version = MONO_DEBUGGER_VERSION;
85         mono_symbol_table->total_size = sizeof (MonoSymbolTable);
86
87         mono_debug_handles = g_hash_table_new_full
88                 (NULL, NULL, NULL, (GDestroyNotify) mono_debug_close_image);
89         method_hash = g_hash_table_new (method_hash_hash, method_hash_equal);
90
91         mono_debugger_start_class_init_func = mono_debug_start_add_type;
92         mono_debugger_class_init_func = mono_debug_add_type;
93         mono_install_assembly_load_hook (mono_debug_add_assembly, NULL);
94 }
95
96 void
97 mono_debug_init_1 (MonoDomain *domain)
98 {
99         MonoDebugHandle *handle = mono_debug_open_image (mono_get_corlib ());
100
101         if (in_the_mono_debugger)
102                 mono_debugger_add_builtin_types (handle);
103 }
104
105 /*
106  * Initialize debugging support - part 2.
107  *
108  * This method must be called after loading the application's main assembly.
109  */
110 void
111 mono_debug_init_2 (MonoAssembly *assembly)
112 {
113         mono_debug_open_image (mono_assembly_get_image (assembly));
114 }
115
116 void
117 mono_debug_cleanup (void)
118 {
119         mono_debugger_cleanup ();
120
121         if (mono_debug_handles)
122                 g_hash_table_destroy (mono_debug_handles);
123         mono_debug_handles = NULL;
124 }
125
126 static MonoDebugHandle *
127 _mono_debug_get_image (MonoImage *image)
128 {
129         return g_hash_table_lookup (mono_debug_handles, image);
130 }
131
132 static MonoDebugHandle *
133 allocate_debug_handle (MonoSymbolTable *table)
134 {
135         MonoDebugHandle *handle;
136
137         if (!table->symbol_files)
138                 table->symbol_files = g_new0 (MonoDebugHandle *, SYMFILE_TABLE_CHUNK_SIZE);
139         else if (!((table->num_symbol_files + 1) % SYMFILE_TABLE_CHUNK_SIZE)) {
140                 guint32 chunks = (table->num_symbol_files + 1) / SYMFILE_TABLE_CHUNK_SIZE;
141                 guint32 size = sizeof (MonoDebugHandle *) * SYMFILE_TABLE_CHUNK_SIZE * (chunks + 1);
142
143                 table->symbol_files = g_realloc (table->symbol_files, size);
144         }
145
146         handle = g_new0 (MonoDebugHandle, 1);
147         handle->index = table->num_symbol_files;
148         table->symbol_files [table->num_symbol_files++] = handle;
149         return handle;
150 }
151
152 static MonoDebugHandle *
153 mono_debug_open_image (MonoImage *image)
154 {
155         MonoDebugHandle *handle;
156
157         handle = _mono_debug_get_image (image);
158         if (handle != NULL)
159                 return handle;
160
161         handle = allocate_debug_handle (mono_symbol_table);
162
163         handle->image = image;
164         mono_image_addref (image);
165         handle->image_file = g_strdup (mono_image_get_filename (image));
166
167         g_hash_table_insert (mono_debug_handles, image, handle);
168
169         if (mono_image_is_dynamic (image))
170                 return handle;
171
172         handle->symfile = mono_debug_open_mono_symbol_file (handle, in_the_mono_debugger);
173         if (in_the_mono_debugger)
174                 mono_debugger_add_symbol_file (handle);
175
176         return handle;
177 }
178
179 static void
180 mono_debug_close_image (MonoDebugHandle *handle)
181 {
182         if (handle->symfile)
183                 mono_debug_close_mono_symbol_file (handle->symfile);
184         /* decrease the refcount added with mono_image_addref () */
185         mono_image_close (handle->image);
186         /* FIXME: should also free handle->image_file? */
187         g_free (handle->_priv);
188         g_free (handle);
189 }
190
191 static void
192 mono_debug_add_assembly (MonoAssembly *assembly, gpointer user_data)
193 {
194         mono_debugger_lock ();
195         mono_debug_open_image (mono_assembly_get_image (assembly));
196         mono_debugger_unlock ();
197 }
198
199 /*
200  * Allocate a new data item of size `size'.
201  * Returns the global offset which is to be used to reference this data item and
202  * a pointer (in the `ptr' argument) which is to be used to write it.
203  */
204 static guint32
205 allocate_data_item (MonoSymbolTable *table, MonoDebugDataItemType type, guint32 size, guint8 **ptr)
206 {
207         guint32 retval;
208         guint8 *data;
209
210         g_assert (size + 8 < DATA_TABLE_CHUNK_SIZE);
211         g_assert (ptr != NULL);
212
213         /* Initialize things if necessary. */
214         if (!table->current_data_table) {
215                 table->current_data_table = g_malloc0 (DATA_TABLE_CHUNK_SIZE);
216                 table->data_table_size = DATA_TABLE_CHUNK_SIZE;
217                 table->data_table_chunk_size = DATA_TABLE_CHUNK_SIZE;
218                 table->data_table_offset = 0;
219         }
220
221  again:
222         /* First let's check whether there's still enough room in the current_data_table. */
223         if (table->data_table_offset + size + 8 < table->data_table_size) {
224                 retval = table->data_table_offset;
225                 table->data_table_offset += size + 8;
226                 data = ((guint8 *) table->current_data_table) + retval - table->data_table_start;
227                 * ((guint32 *) data) = size;
228                 data += 4;
229                 * ((guint32 *) data) = type;
230                 data += 4;
231                 *ptr = data;
232                 return retval;
233         }
234
235         /* Add the current_data_table to the data_tables vector and ... */
236         if (!table->data_tables) {
237                 guint32 tsize = sizeof (gpointer) * DATA_TABLE_PTR_CHUNK_SIZE;
238                 table->data_tables = g_malloc0 (tsize);
239         }
240
241         if (!((table->num_data_tables + 1) % DATA_TABLE_PTR_CHUNK_SIZE)) {
242                 guint32 chunks = (table->num_data_tables + 1) / DATA_TABLE_PTR_CHUNK_SIZE;
243                 guint32 tsize = sizeof (gpointer) * DATA_TABLE_PTR_CHUNK_SIZE * (chunks + 1);
244
245                 table->data_tables = g_realloc (table->data_tables, tsize);
246         }
247
248         table->data_tables [table->num_data_tables++] = table->current_data_table;
249
250         /* .... allocate a new current_data_table. */
251         table->current_data_table = g_malloc0 (DATA_TABLE_CHUNK_SIZE);
252         table->data_table_start = table->data_table_offset = table->data_table_size;
253         table->data_table_size += DATA_TABLE_CHUNK_SIZE;
254
255         goto again;
256 }
257
258 struct LookupMethodData
259 {
260         MonoDebugMethodInfo *minfo;
261         MonoMethod *method;
262 };
263
264 static void
265 lookup_method_func (gpointer key, gpointer value, gpointer user_data)
266 {
267         MonoDebugHandle *handle = (MonoDebugHandle *) value;
268         struct LookupMethodData *data = (struct LookupMethodData *) user_data;
269
270         if (data->minfo)
271                 return;
272
273         if (handle->symfile)
274                 data->minfo = mono_debug_find_method (handle, data->method);
275 }
276
277 static MonoDebugMethodInfo *
278 _mono_debug_lookup_method (MonoMethod *method)
279 {
280         struct LookupMethodData data;
281
282         data.minfo = NULL;
283         data.method = method;
284
285         if (!mono_debug_handles)
286                 return NULL;
287
288         g_hash_table_foreach (mono_debug_handles, lookup_method_func, &data);
289         return data.minfo;
290 }
291
292 static inline void
293 write_leb128 (guint32 value, guint8 *ptr, guint8 **rptr)
294 {
295         do {
296                 guint8 byte = value & 0x7f;
297                 value >>= 7;
298                 if (value)
299                         byte |= 0x80;
300                 *ptr++ = byte;
301         } while (value);
302
303         *rptr = ptr;
304 }
305
306 static inline void
307 write_sleb128 (gint32 value, guint8 *ptr, guint8 **rptr)
308 {
309         gboolean more = 1;
310
311         while (more) {
312                 guint8 byte = value & 0x7f;
313                 value >>= 7;
314
315                 if (((value == 0) && ((byte & 0x40) == 0)) || ((value == -1) && (byte & 0x40)))
316                         more = 0;
317                 else
318                         byte |= 0x80;
319                 *ptr++ = byte;
320         }
321
322         *rptr = ptr;
323 }
324
325 static void
326 write_variable (MonoDebugVarInfo *var, guint8 *ptr, guint8 **rptr)
327 {
328         write_leb128 (var->index, ptr, &ptr);
329         write_sleb128 (var->offset, ptr, &ptr);
330         write_leb128 (var->size, ptr, &ptr);
331         write_leb128 (var->begin_scope, ptr, &ptr);
332         write_leb128 (var->end_scope, ptr, &ptr);
333         *rptr = ptr;
334 }
335
336 /*
337  * This is called by the JIT to tell the debugging code about a newly
338  * compiled method.
339  */
340 MonoDebugMethodAddress *
341 mono_debug_add_method (MonoMethod *method, MonoDebugMethodJitInfo *jit, MonoDomain *domain)
342 {
343         MonoDebugMethodAddress *address;
344         char buffer [BUFSIZ];
345         guint8 *ptr, *oldptr;
346         guint32 i, size, total_size, max_size;
347         gint32 last_il_offset = 0, last_native_offset = 0;
348         MonoDebugHandle *handle;
349         MonoDebugMethodInfo *minfo;
350         MethodHashEntry *hash;
351
352         if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
353             (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
354             (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
355             (method->flags & METHOD_ATTRIBUTE_ABSTRACT))
356                 return NULL;
357
358         if (method->wrapper_type != MONO_WRAPPER_NONE)
359                 return NULL;
360
361         mono_debugger_lock ();
362
363         handle = _mono_debug_get_image (method->klass->image);
364         if (!handle || !handle->symfile || !handle->symfile->offset_table) {
365                 mono_debugger_unlock ();
366                 return NULL;
367         }
368
369         minfo = _mono_debug_lookup_method (method);
370         if (!minfo) {
371                 mono_debugger_unlock ();
372                 return NULL;
373         }
374
375         max_size = 24 + 8 * jit->num_line_numbers + 20 * (1 + jit->num_params + jit->num_locals);
376         if (max_size > BUFSIZ)
377                 ptr = oldptr = g_malloc (max_size);
378         else
379                 ptr = oldptr = buffer;
380
381         write_leb128 (jit->prologue_end, ptr, &ptr);
382         write_leb128 (jit->epilogue_begin, ptr, &ptr);
383
384         write_leb128 (jit->num_line_numbers, ptr, &ptr);
385         for (i = 0; i < jit->num_line_numbers; i++) {
386                 MonoDebugLineNumberEntry *lne = &jit->line_numbers [i];
387
388                 write_sleb128 (lne->il_offset - last_il_offset, ptr, &ptr);
389                 write_sleb128 (lne->native_offset - last_native_offset, ptr, &ptr);
390
391                 last_il_offset = lne->il_offset;
392                 last_native_offset = lne->native_offset;
393         }
394
395         *ptr++ = jit->this_var ? 1 : 0;
396         if (jit->this_var)
397                 write_variable (jit->this_var, ptr, &ptr);
398
399         write_leb128 (jit->num_params, ptr, &ptr);
400         for (i = 0; i < jit->num_params; i++)
401                 write_variable (&jit->params [i], ptr, &ptr);
402
403         write_leb128 (jit->num_locals, ptr, &ptr);
404         for (i = 0; i < jit->num_locals; i++)
405                 write_variable (&jit->locals [i], ptr, &ptr);
406
407         size = ptr - oldptr;
408         g_assert (size < max_size);
409         total_size = size + sizeof (MonoDebugMethodAddress);
410
411         if (total_size + 9 >= DATA_TABLE_CHUNK_SIZE) {
412                 // FIXME: Maybe we should print a warning here.
413                 //        This should only happen for very big methods, for instance
414                 //        with more than 40.000 line numbers and more than 5.000
415                 //        local variables.
416                 return NULL;
417         }
418
419         allocate_data_item (mono_symbol_table, MONO_DEBUG_DATA_ITEM_METHOD, total_size, (guint8 **) &address);
420
421         address->size = total_size;
422         address->symfile_id = handle->index;
423         address->domain_id = mono_domain_get_id (domain);
424         address->method_id = minfo->index;
425         address->code_start = jit->code_start;
426         address->code_size = jit->code_size;
427         address->wrapper_addr = jit->wrapper_addr;
428
429         memcpy (&address->data, oldptr, size);
430
431         if (max_size > BUFSIZ)
432                 g_free (oldptr);
433
434         hash = g_new0 (MethodHashEntry, 1);
435         hash->symfile_id = address->symfile_id;
436         hash->domain_id = address->domain_id;
437         hash->method_id = address->method_id;
438
439         g_hash_table_insert (method_hash, hash, address);
440
441         if (in_the_mono_debugger)
442                 mono_debugger_add_method (jit);
443
444         mono_debugger_unlock ();
445
446         return address;
447 }
448
449 static inline guint32
450 read_leb128 (guint8 *ptr, guint8 **rptr)
451 {
452         guint32 result = 0, shift = 0;
453
454         while (TRUE) {
455                 guint8 byte = *ptr++;
456
457                 result |= (byte & 0x7f) << shift;
458                 if ((byte & 0x80) == 0)
459                         break;
460                 shift += 7;
461         }
462
463         *rptr = ptr;
464         return result;
465 }
466
467 static inline gint32
468 read_sleb128 (guint8 *ptr, guint8 **rptr)
469 {
470         gint32 result = 0;
471         guint32 shift = 0;
472
473         while (TRUE) {
474                 guint8 byte = *ptr++;
475
476                 result |= (byte & 0x7f) << shift;
477                 shift += 7;
478
479                 if (byte & 0x80)
480                         continue;
481
482                 if ((shift < 32) && (byte & 0x40))
483                         result |= - (1 << shift);
484                 break;
485         }
486
487         *rptr = ptr;
488         return result;
489 }
490
491 static void
492 read_variable (MonoDebugVarInfo *var, guint8 *ptr, guint8 **rptr)
493 {
494         var->index = read_leb128 (ptr, &ptr);
495         var->offset = read_sleb128 (ptr, &ptr);
496         var->size = read_leb128 (ptr, &ptr);
497         var->begin_scope = read_leb128 (ptr, &ptr);
498         var->end_scope = read_leb128 (ptr, &ptr);
499         *rptr = ptr;
500 }
501
502 MonoDebugMethodJitInfo *
503 mono_debug_read_method (MonoDebugMethodAddress *address)
504 {
505         MonoDebugMethodJitInfo *jit;
506         guint32 i, il_offset = 0, native_offset = 0;
507         guint8 *ptr;
508
509         jit = g_new0 (MonoDebugMethodJitInfo, 1);
510         jit->code_start = address->code_start;
511         jit->code_size = address->code_size;
512         jit->wrapper_addr = address->wrapper_addr;
513
514         ptr = (guint8 *) &address->data;
515
516         jit->prologue_end = read_leb128 (ptr, &ptr);
517         jit->epilogue_begin = read_leb128 (ptr, &ptr);
518
519         jit->num_line_numbers = read_leb128 (ptr, &ptr);
520         jit->line_numbers = g_new0 (MonoDebugLineNumberEntry, jit->num_line_numbers);
521         for (i = 0; i < jit->num_line_numbers; i++) {
522                 MonoDebugLineNumberEntry *lne = &jit->line_numbers [i];
523
524                 il_offset += read_sleb128 (ptr, &ptr);
525                 native_offset += read_sleb128 (ptr, &ptr);
526
527                 lne->il_offset = il_offset;
528                 lne->native_offset = native_offset;
529         }
530
531         if (*ptr++) {
532                 jit->this_var = g_new0 (MonoDebugVarInfo, 1);
533                 read_variable (jit->this_var, ptr, &ptr);
534         }
535
536         jit->num_params = read_leb128 (ptr, &ptr);
537         jit->params = g_new0 (MonoDebugVarInfo, jit->num_params);
538         for (i = 0; i < jit->num_params; i++)
539                 read_variable (&jit->params [i], ptr, &ptr);
540
541         jit->num_locals = read_leb128 (ptr, &ptr);
542         jit->locals = g_new0 (MonoDebugVarInfo, jit->num_locals);
543         for (i = 0; i < jit->num_locals; i++)
544                 read_variable (&jit->locals [i], ptr, &ptr);
545
546         return jit;
547 }
548
549 /*
550  * This is called via the `mono_debugger_class_init_func' from mono_class_init() each time
551  * a new class is initialized.
552  */
553 static void
554 mono_debug_start_add_type (MonoClass *klass)
555 {
556         MonoDebugHandle *handle;
557
558         handle = _mono_debug_get_image (klass->image);
559         if (!handle)
560                 return;
561
562         if (in_the_mono_debugger)
563                 mono_debugger_add_type (handle, klass);
564 }
565
566 static guint32
567 get_token (MonoClass *klass)
568 {
569         while (klass->rank)
570                 klass = klass->element_class;
571
572         return klass->type_token;
573 }
574
575 static void
576 mono_debug_add_type (MonoClass *klass)
577 {
578         MonoDebugHandle *handle;
579         MonoDebugClassEntry *entry;
580         char buffer [BUFSIZ];
581         guint8 *ptr, *oldptr;
582         guint32 token, i, size, total_size, max_size;
583         int base_offset = 0;
584
585         handle = _mono_debug_get_image (klass->image);
586         if (!handle)
587                 return;
588
589         if (klass->generic_class ||
590             (klass->byval_arg.type == MONO_TYPE_VAR) || (klass->byval_arg.type == MONO_TYPE_MVAR))
591                 return;
592
593         max_size = 12 + sizeof (gpointer);
594         if (max_size > BUFSIZ)
595                 ptr = oldptr = g_malloc (max_size);
596         else
597                 ptr = oldptr = buffer;
598
599         token = get_token (klass);
600         g_assert (token);
601
602         if (klass->valuetype)
603                 base_offset = - (int)(sizeof (MonoObject));
604
605         write_leb128 (token, ptr, &ptr);
606         write_leb128 (klass->rank, ptr, &ptr);
607         write_leb128 (klass->instance_size + base_offset, ptr, &ptr);
608         * ((gpointer *) ptr)++ = klass;
609
610         size = ptr - oldptr;
611         g_assert (size < max_size);
612         total_size = size + sizeof (MonoDebugClassEntry);
613
614         if (total_size + 9 >= DATA_TABLE_CHUNK_SIZE) {
615                 // FIXME: Maybe we should print a warning here.
616                 //        This should only happen for very big methods, for instance
617                 //        with more than 40.000 line numbers and more than 5.000
618                 //        local variables.
619                 return NULL;
620         }
621
622         allocate_data_item (mono_symbol_table, MONO_DEBUG_DATA_ITEM_CLASS, total_size, (guint8 **) &entry);
623
624         entry->size = total_size;
625         entry->symfile_id = handle->index;
626
627         memcpy (&entry->data, oldptr, size);
628
629         if (max_size > BUFSIZ)
630                 g_free (oldptr);
631
632         mono_debugger_start_add_type (handle, klass);
633 }
634
635 static MonoDebugMethodJitInfo *
636 find_method (MonoDebugMethodInfo *minfo, MonoDomain *domain)
637 {
638         MethodHashEntry lookup;
639         MonoDebugMethodAddress *address;
640
641         lookup.symfile_id = minfo->handle->index;
642         lookup.domain_id = mono_domain_get_id (domain);
643         lookup.method_id = minfo->index;
644
645         address = g_hash_table_lookup (method_hash, &lookup);
646         if (!address)
647                 return NULL;
648
649         return mono_debug_read_method (address);
650 }
651
652 static gint32
653 il_offset_from_address (MonoDebugMethodInfo *minfo, MonoDomain *domain, guint32 native_offset)
654 {
655         MonoDebugMethodJitInfo *jit;
656         int i;
657
658         jit = find_method (minfo, domain);
659         if (!jit || !jit->line_numbers)
660                 return -1;
661
662         for (i = jit->num_line_numbers - 1; i >= 0; i--) {
663                 MonoDebugLineNumberEntry lne = jit->line_numbers [i];
664
665                 if (lne.native_offset <= native_offset)
666                         return lne.il_offset;
667         }
668
669         return -1;
670 }
671
672 /**
673  * mono_debug_source_location_from_address:
674  * @method:
675  * @address:
676  * @line_number:
677  * @domain:
678  *
679  * Used by the exception code to get a source location from a machine address.
680  *
681  * Returns: a textual representation of the specified address which is suitable to be displayed to
682  * the user (for instance "/home/martin/monocvs/debugger/test/Y.cs:8").
683  *
684  * If the optional @line_number argument is not NULL, the line number is stored there and just the
685  * source file is returned (ie. it'd return "/home/martin/monocvs/debugger/test/Y.cs" and store the
686  * line number 8 in the variable pointed to by @line_number).
687  */
688 gchar *
689 mono_debug_source_location_from_address (MonoMethod *method, guint32 address, guint32 *line_number,
690                                          MonoDomain *domain)
691 {
692         MonoDebugMethodInfo *minfo;
693         char *res = NULL;
694         gint32 offset;
695
696         mono_loader_lock ();
697         minfo = _mono_debug_lookup_method (method);
698         if (!minfo || !minfo->handle || !minfo->handle->symfile || !minfo->handle->symfile->offset_table) {
699                 mono_loader_unlock ();
700                 return NULL;
701         }
702
703         offset = il_offset_from_address (minfo, domain, address);
704                 
705         if (offset >= 0)
706                 res = mono_debug_find_source_location (minfo->handle->symfile, method, offset, line_number);
707
708         mono_loader_unlock ();
709         return res;
710 }
711
712 /**
713  * mono_debug_source_location_from_il_offset:
714  * @method:
715  * @offset:
716  * @line_number:
717  *
718  * Used by the exception code to get a source location from an IL offset.
719  *
720  * Returns a textual representation of the specified address which is suitable to be displayed to
721  * the user (for instance "/home/martin/monocvs/debugger/test/Y.cs:8").
722  *
723  * If the optional @line_number argument is not NULL, the line number is stored there and just the
724  * source file is returned (ie. it'd return "/home/martin/monocvs/debugger/test/Y.cs" and store the
725  * line number 8 in the variable pointed to by @line_number).
726  */
727 gchar *
728 mono_debug_source_location_from_il_offset (MonoMethod *method, guint32 offset, guint32 *line_number)
729 {
730         char *res;
731         MonoDebugMethodInfo *minfo;
732
733         mono_loader_lock ();
734         minfo = _mono_debug_lookup_method (method);
735         if (!minfo || !minfo->handle || !minfo->handle->symfile) {
736                 mono_loader_unlock ();
737                 return NULL;
738         }
739
740         res = mono_debug_find_source_location (minfo->handle->symfile, method, offset, line_number);
741         mono_loader_unlock ();
742         return res;
743 }
744
745 /**
746  * mono_debug_il_offset_from_address:
747  * @method:
748  * @address:
749  * @domain:
750  *
751  * Returns: the IL offset corresponding to machine address @address which is an offset
752  * relative to the beginning of the method @method.
753  */
754 gint32
755 mono_debug_il_offset_from_address (MonoMethod *method, gint32 address, MonoDomain *domain)
756 {
757         MonoDebugMethodInfo *minfo;
758         gint32 res;
759
760         if (address < 0)
761                 return -1;
762
763         mono_loader_lock ();
764         minfo = _mono_debug_lookup_method (method);
765         if (!minfo || !minfo->il_offsets || !minfo->handle || !minfo->handle->symfile ||
766             !minfo->handle->symfile->offset_table) {
767                 mono_loader_unlock ();
768                 return -1;
769         }
770
771         res = il_offset_from_address (minfo, domain, address);
772         mono_loader_unlock ();
773         return res;
774 }
775
776 /**
777  * mono_debug_address_from_il_offset:
778  * @method:
779  * @il_offset:
780  * @domain:
781  *
782  * Returns: the machine address corresponding to IL offset @il_offset.
783  * The returned value is an offset relative to the beginning of the method @method.
784  */
785 gint32
786 mono_debug_address_from_il_offset (MonoMethod *method, gint32 il_offset, MonoDomain *domain)
787 {
788         MonoDebugMethodInfo *minfo;
789         MonoDebugMethodJitInfo *jit;
790         gint32 res;
791
792         if (il_offset < 0)
793                 return -1;
794
795         mono_loader_lock ();
796         minfo = _mono_debug_lookup_method (method);
797         if (!minfo || !minfo->il_offsets || !minfo->handle || !minfo->handle->symfile ||
798             !minfo->handle->symfile->offset_table) {
799                 mono_loader_unlock ();
800                 return -1;
801         }
802
803         jit = find_method (minfo, domain);
804         if (!jit) {
805                 mono_loader_unlock ();
806                 return -1;
807         }
808
809         res = _mono_debug_address_from_il_offset (jit, il_offset);
810         mono_loader_unlock ();
811         return res;
812 }