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