2003-12-20 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/mono-debug.h>
7 #include <mono/metadata/mono-debug-debugger.h>
8
9 struct _MonoDebugHandlePriv
10 {
11         MonoDebuggerSymbolFile *debugger_info;
12         MonoDebugDomainData *domain_table;
13 };
14
15 struct _MonoDebugDomainDataPriv
16 {
17         GHashTable *wrapper_info;
18         MonoDebugDomainData *next;
19 };
20
21 MonoDebugFormat mono_debug_format = MONO_DEBUG_FORMAT_NONE;
22
23 static gboolean in_the_mono_debugger = FALSE;
24 static gboolean mono_debug_initialized = FALSE;
25 GHashTable *mono_debug_handles = NULL;
26
27 static MonoDebugHandle *mono_debug_open_image    (MonoImage *image);
28 static void             mono_debug_close_image   (MonoDebugHandle *debug);
29
30 static MonoDebugHandle *_mono_debug_get_image    (MonoImage *image);
31 static void             mono_debug_add_assembly  (MonoAssembly *assembly, gpointer user_data);
32 static void             mono_debug_add_type      (MonoClass *klass);
33
34 extern void (*mono_debugger_class_init_func) (MonoClass *klass);
35
36 /*
37  * Initialize debugging support.
38  *
39  * This method must be called after loading corlib,
40  * but before opening the application's main assembly because we need to set some
41  * callbacks here.
42  */
43 void
44 mono_debug_init (MonoDomain *domain, MonoDebugFormat format)
45 {
46         MonoAssembly **ass;
47
48         g_assert (!mono_debug_initialized);
49
50         mono_debug_initialized = TRUE;
51         mono_debug_format = format;
52         in_the_mono_debugger = format == MONO_DEBUG_FORMAT_DEBUGGER;
53
54         if (in_the_mono_debugger)
55                 mono_debugger_initialize (domain);
56
57         mono_debugger_lock ();
58
59         mono_debug_handles = g_hash_table_new_full
60                 (NULL, NULL, NULL, (GDestroyNotify) mono_debug_close_image);
61
62         mono_debugger_class_init_func = mono_debug_add_type;
63         mono_install_assembly_load_hook (mono_debug_add_assembly, NULL);
64
65         mono_debug_open_image (mono_defaults.corlib);
66         for (ass = mono_defaults.corlib->references; ass && *ass; ass++)
67                 mono_debug_open_image ((*ass)->image);
68 }
69
70 /*
71  * Initialize debugging support - part 2.
72  *
73  * This method must be called after loading the application's main assembly.
74  */
75 void
76 mono_debug_init_2 (MonoAssembly *assembly)
77 {
78         MonoDebugHandle *handle;
79
80         mono_debug_open_image (assembly->image);
81
82         handle = _mono_debug_get_image (mono_defaults.corlib);
83         g_assert (handle);
84
85         mono_debugger_unlock ();
86 }
87
88 void
89 mono_debug_cleanup (void)
90 {
91         mono_debugger_cleanup ();
92
93         if (mono_debug_handles)
94                 g_hash_table_destroy (mono_debug_handles);
95         mono_debug_handles = NULL;
96 }
97
98 static MonoDebugHandle *
99 _mono_debug_get_image (MonoImage *image)
100 {
101         return g_hash_table_lookup (mono_debug_handles, image);
102 }
103
104 static MonoDebugHandle *
105 mono_debug_open_image (MonoImage *image)
106 {
107         MonoDebugHandle *handle;
108
109         handle = _mono_debug_get_image (image);
110         if (handle != NULL)
111                 return handle;
112
113         handle = g_new0 (MonoDebugHandle, 1);
114         handle->image = image;
115         handle->image->ref_count++;
116         handle->image_file = g_strdup (image->name);
117         handle->_priv = g_new0 (MonoDebugHandlePriv, 1);
118
119         g_hash_table_insert (mono_debug_handles, image, handle);
120
121         if (image->assembly->dynamic)
122                 return handle;
123
124         handle->symfile = mono_debug_open_mono_symbol_file (handle, in_the_mono_debugger);
125         if (in_the_mono_debugger) {
126                 handle->_priv->debugger_info = mono_debugger_add_symbol_file (handle);
127                 if (image == mono_defaults.corlib)
128                         mono_debugger_add_builtin_types (handle->_priv->debugger_info);
129         }
130
131         return handle;
132 }
133
134 static void
135 mono_debug_close_image (MonoDebugHandle *handle)
136 {
137         if (handle->symfile)
138                 mono_debug_close_mono_symbol_file (handle->symfile);
139         handle->image->ref_count--;
140         g_free (handle->_priv);
141         g_free (handle);
142 }
143
144 static void
145 mono_debug_add_assembly (MonoAssembly *assembly, gpointer user_data)
146 {
147         mono_debugger_lock ();
148         mono_debug_open_image (assembly->image);
149         mono_debugger_unlock ();
150 }
151
152 /*
153  * This is called via the `mono_debugger_class_init_func' from mono_class_init() each time
154  * a new class is initialized.
155  */
156 static void
157 mono_debug_add_type (MonoClass *klass)
158 {
159         MonoDebugHandle *handle;
160
161         handle = _mono_debug_get_image (klass->image);
162         if (!handle)
163                 return;
164
165         if (handle->_priv->debugger_info)
166                 mono_debugger_add_type (handle->_priv->debugger_info, klass);
167 }
168
169 struct LookupMethodData
170 {
171         MonoDebugMethodInfo *minfo;
172         MonoMethod *method;
173 };
174
175 static void
176 lookup_method_func (gpointer key, gpointer value, gpointer user_data)
177 {
178         MonoDebugHandle *handle = (MonoDebugHandle *) value;
179         struct LookupMethodData *data = (struct LookupMethodData *) user_data;
180
181         if (data->minfo)
182                 return;
183
184         if (handle->symfile)
185                 data->minfo = mono_debug_find_method (handle->symfile, data->method);
186 }
187
188 static MonoDebugMethodInfo *
189 _mono_debug_lookup_method (MonoMethod *method)
190 {
191         struct LookupMethodData data;
192
193         data.minfo = NULL;
194         data.method = method;
195
196         if (!mono_debug_handles)
197                 return NULL;
198
199         g_hash_table_foreach (mono_debug_handles, lookup_method_func, &data);
200         return data.minfo;
201 }
202
203 /*
204  * This is called by the JIT to tell the debugging code about a newly compiled
205  * wrapper method.
206  */
207 void
208 mono_debug_add_wrapper (MonoMethod *method, MonoMethod *wrapper_method, MonoDomain *domain)
209 {
210         MonoClass *klass = method->klass;
211         MonoDebugHandle *handle;
212         MonoDebugMethodInfo *minfo;
213         MonoDebugMethodJitInfo *jit;
214         MonoDebugDomainData *domain_data;
215
216         if (!(method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL))
217                 return;
218
219         mono_class_init (klass);
220
221         handle = _mono_debug_get_image (klass->image);
222         g_assert (handle);
223
224         minfo = _mono_debug_lookup_method (method);
225         if (!minfo)
226                 return;
227
228         domain_data = mono_debug_get_domain_data (handle, domain);
229         if (domain_data->jit [minfo->index]) {
230                 // FIXME FIXME FIXME
231                 // This is bug #48591.
232                 return;
233         }
234
235         jit = g_hash_table_lookup (domain_data->_priv->wrapper_info, wrapper_method);
236         g_assert (jit);
237
238         mono_debugger_lock ();
239
240         domain_data->jit [minfo->index] = jit;
241         jit->wrapper_addr = method->addr;
242
243         if (handle->_priv->debugger_info && (domain == mono_root_domain))
244                 mono_debugger_add_method (handle->_priv->debugger_info, minfo, jit);
245
246         mono_debugger_unlock ();
247 }
248
249 /*
250  * This is called by the JIT to tell the debugging code about a newly
251  * compiled method.
252  */
253 void
254 mono_debug_add_method (MonoMethod *method, MonoDebugMethodJitInfo *jit, MonoDomain *domain)
255 {
256         MonoClass *klass = method->klass;
257         MonoDebugDomainData *domain_data;
258         MonoDebugHandle *handle;
259         MonoDebugMethodInfo *minfo;
260
261         mono_class_init (klass);
262
263         if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
264             (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
265             (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
266             (method->flags & METHOD_ATTRIBUTE_ABSTRACT))
267                 return;
268
269         handle = _mono_debug_get_image (klass->image);
270         if (!handle)
271                 return;
272
273         minfo = _mono_debug_lookup_method (method);
274         if (!minfo)
275                 return;
276
277         mono_debugger_lock ();
278
279         domain_data = mono_debug_get_domain_data (handle, domain);
280         if (domain_data->jit [minfo->index]) {
281                 // FIXME FIXME FIXME
282                 // This is bug #48591.
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 *, 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 }