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