2004-09-01 Zoltan Varga <vargaz@freemail.hu>
[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
11 struct _MonoDebugHandlePriv
12 {
13         MonoDebuggerSymbolFile *debugger_info;
14         MonoDebugDomainData *domain_table;
15 };
16
17 struct _MonoDebugDomainDataPriv
18 {
19         GHashTable *wrapper_info;
20         MonoDebugDomainData *next;
21 };
22
23 MonoDebugFormat mono_debug_format = MONO_DEBUG_FORMAT_NONE;
24
25 static gboolean in_the_mono_debugger = FALSE;
26 static gboolean mono_debug_initialized = FALSE;
27 GHashTable *mono_debug_handles = NULL;
28
29 static MonoDebugHandle     *mono_debug_open_image      (MonoImage *image);
30 static void                 mono_debug_close_image     (MonoDebugHandle *debug);
31
32 static MonoDebugHandle     *_mono_debug_get_image      (MonoImage *image);
33 static void                 mono_debug_add_assembly    (MonoAssembly *assembly,
34                                                         gpointer user_data);
35 static void                 mono_debug_start_add_type  (MonoClass *klass);
36 static void                 mono_debug_add_type        (MonoClass *klass);
37 static MonoDebugDomainData *mono_debug_get_domain_data (MonoDebugHandle *handle,
38                                                         MonoDomain *domain);
39
40 extern void (*mono_debugger_class_init_func) (MonoClass *klass);
41 extern void (*mono_debugger_start_class_init_func) (MonoClass *klass);
42
43 /*
44  * Initialize debugging support.
45  *
46  * This method must be called after loading corlib,
47  * but before opening the application's main assembly because we need to set some
48  * callbacks here.
49  */
50 void
51 mono_debug_init (MonoDebugFormat format)
52 {
53         g_assert (!mono_debug_initialized);
54
55         mono_debug_initialized = TRUE;
56         mono_debug_format = format;
57         in_the_mono_debugger = format == MONO_DEBUG_FORMAT_DEBUGGER;
58
59         if (in_the_mono_debugger)
60                 mono_debugger_initialize ();
61
62         mono_debugger_lock ();
63
64         mono_debug_handles = g_hash_table_new_full
65                 (NULL, NULL, NULL, (GDestroyNotify) mono_debug_close_image);
66
67         mono_debugger_start_class_init_func = mono_debug_start_add_type;
68         mono_debugger_class_init_func = mono_debug_add_type;
69         mono_install_assembly_load_hook (mono_debug_add_assembly, NULL);
70         mono_debugger_unlock ();
71 }
72
73 void
74 mono_debug_init_1 (MonoDomain *domain)
75 {
76         MonoDebugHandle *handle = mono_debug_open_image (mono_get_corlib ());
77
78         if (in_the_mono_debugger)
79                 mono_debugger_add_builtin_types (handle->_priv->debugger_info);
80 }
81
82 /*
83  * Initialize debugging support - part 2.
84  *
85  * This method must be called after loading the application's main assembly.
86  */
87 void
88 mono_debug_init_2 (MonoAssembly *assembly)
89 {
90         mono_debug_open_image (mono_assembly_get_image (assembly));
91 }
92
93 void
94 mono_debug_cleanup (void)
95 {
96         mono_debugger_cleanup ();
97
98         if (mono_debug_handles)
99                 g_hash_table_destroy (mono_debug_handles);
100         mono_debug_handles = NULL;
101 }
102
103 static MonoDebugHandle *
104 _mono_debug_get_image (MonoImage *image)
105 {
106         return g_hash_table_lookup (mono_debug_handles, image);
107 }
108
109 static MonoDebugHandle *
110 mono_debug_open_image (MonoImage *image)
111 {
112         MonoDebugHandle *handle;
113
114         handle = _mono_debug_get_image (image);
115         if (handle != NULL)
116                 return handle;
117
118         handle = g_new0 (MonoDebugHandle, 1);
119         handle->image = image;
120         mono_image_addref (image);
121         handle->image_file = g_strdup (mono_image_get_filename (image));
122         handle->_priv = g_new0 (MonoDebugHandlePriv, 1);
123
124         g_hash_table_insert (mono_debug_handles, image, handle);
125
126         if (mono_image_is_dynamic (image))
127                 return handle;
128
129         handle->symfile = mono_debug_open_mono_symbol_file (handle, in_the_mono_debugger);
130         if (in_the_mono_debugger)
131                 handle->_priv->debugger_info = mono_debugger_add_symbol_file (handle);
132
133         return handle;
134 }
135
136 static void
137 mono_debug_close_image (MonoDebugHandle *handle)
138 {
139         if (handle->symfile)
140                 mono_debug_close_mono_symbol_file (handle->symfile);
141         /* decrease the refcount added with mono_image_addref () */
142         mono_image_close (handle->image);
143         /* FIXME: should also free handle->image_file? */
144         g_free (handle->_priv);
145         g_free (handle);
146 }
147
148 static void
149 mono_debug_add_assembly (MonoAssembly *assembly, gpointer user_data)
150 {
151         mono_debugger_lock ();
152         mono_debug_open_image (mono_assembly_get_image (assembly));
153         mono_debugger_unlock ();
154 }
155
156 /*
157  * This is called via the `mono_debugger_class_init_func' from mono_class_init() each time
158  * a new class is initialized.
159  */
160 MonoDebuggerSymbolFile *
161 _mono_debugger_get_symfile (MonoImage *image)
162 {
163         MonoDebugHandle *handle = _mono_debug_get_image (image);
164         if (!handle)
165                 return NULL;
166
167         return handle->_priv->debugger_info;
168 }
169
170 static void
171 mono_debug_start_add_type (MonoClass *klass)
172 {
173         MonoDebugHandle *handle;
174
175         handle = _mono_debug_get_image (klass->image);
176         if (!handle)
177                 return;
178
179         if (handle->_priv->debugger_info)
180                 mono_debugger_start_add_type (handle->_priv->debugger_info, klass);
181 }
182
183 static void
184 mono_debug_add_type (MonoClass *klass)
185 {
186         MonoDebugHandle *handle;
187
188         handle = _mono_debug_get_image (klass->image);
189         if (!handle)
190                 return;
191
192         if (handle->_priv->debugger_info)
193                 mono_debugger_add_type (handle->_priv->debugger_info, klass);
194 }
195
196
197 struct LookupMethodData
198 {
199         MonoDebugMethodInfo *minfo;
200         MonoMethod *method;
201 };
202
203 static void
204 lookup_method_func (gpointer key, gpointer value, gpointer user_data)
205 {
206         MonoDebugHandle *handle = (MonoDebugHandle *) value;
207         struct LookupMethodData *data = (struct LookupMethodData *) user_data;
208
209         if (data->minfo)
210                 return;
211
212         if (handle->symfile)
213                 data->minfo = mono_debug_find_method (handle, data->method);
214 }
215
216 static MonoDebugMethodInfo *
217 _mono_debug_lookup_method (MonoMethod *method)
218 {
219         struct LookupMethodData data;
220
221         data.minfo = NULL;
222         data.method = method;
223
224         if (!mono_debug_handles)
225                 return NULL;
226
227         g_hash_table_foreach (mono_debug_handles, lookup_method_func, &data);
228         return data.minfo;
229 }
230
231 /*
232  * This is called by the JIT to tell the debugging code about a newly compiled
233  * wrapper method.
234  */
235 void
236 mono_debug_add_wrapper (MonoMethod *method, gpointer wrapper, MonoDomain *domain)
237 {
238         MonoClass *klass = method->klass;
239         MonoDebugDomainData *domain_data;
240         MonoDebugHandle *handle;
241         MonoDebugMethodJitInfo *jit;
242
243         mono_debugger_lock ();
244
245         mono_class_init (klass);
246
247         handle = _mono_debug_get_image (klass->image);
248         if (!handle || !handle->symfile || !handle->symfile->offset_table) {
249                 mono_debugger_unlock ();
250                 return;
251         }
252
253         domain_data = mono_debug_get_domain_data (handle, domain);
254         jit = g_hash_table_lookup (domain_data->_priv->wrapper_info, method);
255         g_assert (jit);
256
257         mono_debugger_add_wrapper (method, jit, wrapper);
258
259         mono_debugger_unlock ();
260 }
261
262 /*
263  * This is called by the JIT to tell the debugging code about a newly
264  * compiled method.
265  */
266 void
267 mono_debug_add_method (MonoMethod *method, MonoDebugMethodJitInfo *jit, MonoDomain *domain)
268 {
269         MonoClass *klass = method->klass;
270         MonoDebugDomainData *domain_data;
271         MonoDebugHandle *handle;
272         MonoDebugMethodInfo *minfo;
273
274         mono_debugger_lock ();
275
276         mono_class_init (klass);
277
278         if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
279             (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
280             (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
281             (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
282                 mono_debugger_unlock ();
283                 return;
284         }
285
286         handle = _mono_debug_get_image (klass->image);
287         if (!handle || !handle->symfile || !handle->symfile->offset_table) {
288                 mono_debugger_unlock ();
289                 return;
290         }
291
292         domain_data = mono_debug_get_domain_data (handle, domain);
293         if (method->wrapper_type != MONO_WRAPPER_NONE) {
294                 g_hash_table_insert (domain_data->_priv->wrapper_info, method, jit);
295                 mono_debugger_unlock ();
296                 return;
297         }
298
299         minfo = _mono_debug_lookup_method (method);
300         if (!minfo) {
301                 mono_debugger_unlock ();
302                 return;
303         }
304
305         if (domain_data->jit [minfo->index]) {
306                 /* FIXME FIXME FIXME
307                 // This is bug #48591.
308                 */
309                 mono_debugger_unlock ();
310                 return;
311         }
312
313         domain_data->jit [minfo->index] = jit;
314
315         if (handle->_priv->debugger_info && (domain == mono_get_root_domain ()))
316                 mono_debugger_add_method (handle->_priv->debugger_info, minfo, jit);
317
318         mono_debugger_unlock ();
319 }
320
321 static gint32
322 il_offset_from_address (MonoDebugMethodJitInfo *jit, guint32 address)
323 {
324         int i;
325
326         if (!jit || !jit->line_numbers)
327                 return -1;
328
329         for (i = jit->line_numbers->len - 1; i >= 0; i--) {
330                 MonoDebugLineNumberEntry lne = g_array_index (
331                         jit->line_numbers, MonoDebugLineNumberEntry, i);
332
333                 if (lne.address <= address)
334                         return lne.offset;
335         }
336
337         return -1;
338 }
339
340 /*
341  * Used by the exception code to get a source location from a machine address.
342  *
343  * Returns a textual representation of the specified address which is suitable to be displayed to
344  * the user (for instance "/home/martin/monocvs/debugger/test/Y.cs:8").
345  *
346  * If the optional @line_number argument is not NULL, the line number is stored there and just the
347  * source file is returned (ie. it'd return "/home/martin/monocvs/debugger/test/Y.cs" and store the
348  * line number 8 in the variable pointed to by @line_number).
349  */
350 gchar *
351 mono_debug_source_location_from_address (MonoMethod *method, guint32 address, guint32 *line_number,
352                                          MonoDomain *domain)
353 {
354         char *res;
355         MonoDebugMethodInfo *minfo;
356         MonoDebugDomainData *domain_data;
357
358         mono_debugger_lock ();
359         minfo = _mono_debug_lookup_method (method);
360         if (!minfo || !minfo->handle || !minfo->handle->symfile ||
361             !minfo->handle->symfile->offset_table) {
362                 mono_debugger_unlock ();
363                 return NULL;
364         }
365
366         domain_data = mono_debug_get_domain_data (minfo->handle, domain);
367         if (!domain_data->jit [minfo->index]) {
368                 mono_debugger_unlock ();
369                 return NULL;
370         }
371
372         if (minfo->handle && minfo->handle->symfile) {
373                 gint32 offset = il_offset_from_address (domain_data->jit [minfo->index], address);
374                 char *res = NULL;
375                 
376                 if (offset >= 0)
377                         res = mono_debug_find_source_location (minfo->handle->symfile, method, offset, line_number);
378                 mono_debugger_unlock ();
379                 return res;
380         }
381
382         mono_debugger_unlock ();
383         return NULL;
384 }
385
386 /*
387  * Used by the exception code to get a source location from an IL offset.
388  *
389  * Returns a textual representation of the specified address which is suitable to be displayed to
390  * the user (for instance "/home/martin/monocvs/debugger/test/Y.cs:8").
391  *
392  * If the optional @line_number argument is not NULL, the line number is stored there and just the
393  * source file is returned (ie. it'd return "/home/martin/monocvs/debugger/test/Y.cs" and store the
394  * line number 8 in the variable pointed to by @line_number).
395  */
396 gchar *
397 mono_debug_source_location_from_il_offset (MonoMethod *method, guint32 offset, guint32 *line_number)
398 {
399         char *res;
400         MonoDebugMethodInfo *minfo;
401
402         mono_debugger_lock ();
403         minfo = _mono_debug_lookup_method (method);
404         if (!minfo || !minfo->handle || !minfo->handle->symfile) {
405                 mono_debugger_unlock ();
406                 return NULL;
407         }
408
409         res = mono_debug_find_source_location (minfo->handle->symfile, method, offset, line_number);
410         mono_debugger_unlock ();
411         return res;
412 }
413
414 /*
415  * Returns the IL offset corresponding to machine address @address which is an offset
416  * relative to the beginning of the method @method.
417  */
418 gint32
419 mono_debug_il_offset_from_address (MonoMethod *method, gint32 address, MonoDomain *domain)
420 {
421         MonoDebugMethodInfo *minfo;
422         MonoDebugDomainData *domain_data;
423         gint32 res;
424
425         if (address < 0)
426                 return -1;
427
428         mono_debugger_lock ();
429         minfo = _mono_debug_lookup_method (method);
430         if (!minfo || !minfo->il_offsets || !minfo->handle || !minfo->handle->symfile ||
431             !minfo->handle->symfile->offset_table) {
432                 mono_debugger_unlock ();
433                 return -1;
434         }
435
436         domain_data = mono_debug_get_domain_data (minfo->handle, domain);
437
438         res = il_offset_from_address (domain_data->jit [minfo->index], address);
439         mono_debugger_unlock ();
440         return res;
441 }
442
443 /*
444  * Returns the machine address corresponding to IL offset @il_offset.
445  * The returned value is an offset relative to the beginning of the method @method.
446  */
447 gint32
448 mono_debug_address_from_il_offset (MonoMethod *method, gint32 il_offset, MonoDomain *domain)
449 {
450         MonoDebugMethodInfo *minfo;
451         MonoDebugDomainData *domain_data;
452         gint32 res;
453
454         if (il_offset < 0)
455                 return -1;
456
457         mono_debugger_lock ();
458         minfo = _mono_debug_lookup_method (method);
459         if (!minfo || !minfo->il_offsets || !minfo->handle || !minfo->handle->symfile ||
460             !minfo->handle->symfile->offset_table) {
461                 mono_debugger_unlock ();
462                 return -1;
463         }
464
465         domain_data = mono_debug_get_domain_data (minfo->handle, domain);
466
467         res = _mono_debug_address_from_il_offset (domain_data->jit [minfo->index], il_offset);
468         mono_debugger_unlock ();
469         return res;
470 }
471
472 static MonoDebugDomainData *
473 mono_debug_get_domain_data (MonoDebugHandle *handle, MonoDomain *domain)
474 {
475         MonoDebugDomainData *data;
476         int domain_id = mono_domain_get_id (domain);
477
478         /* We checked this earlier. */
479         g_assert (handle->symfile);
480
481         for (data = handle->_priv->domain_table; data; data = data->_priv->next)
482                 if (data->domain_id == domain_id)
483                         return data;
484
485         data = g_new0 (MonoDebugDomainData, 1);
486         data->domain_id = domain_id;
487         data->jit = g_new0 (MonoDebugMethodJitInfo *, read32(&(handle->symfile->offset_table->_method_count)) + 1);
488
489         data->_priv = g_new0 (MonoDebugDomainDataPriv, 1);
490         data->_priv->next = handle->_priv->domain_table;
491         data->_priv->wrapper_info = g_hash_table_new (g_direct_hash, g_direct_equal);
492         handle->_priv->domain_table = data;
493
494         return data;
495 }