2003-08-27 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         g_assert (handle);
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->symfile, 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         g_assert (!domain_data->jit [minfo->index]);
229
230         jit = g_hash_table_lookup (domain_data->_priv->wrapper_info, wrapper_method);
231         g_assert (jit);
232
233         mono_debugger_lock ();
234
235         domain_data->jit [minfo->index] = jit;
236         jit->wrapper_addr = method->addr;
237
238         if (handle->_priv->debugger_info && (domain == mono_root_domain))
239                 mono_debugger_add_method (handle->_priv->debugger_info, minfo, jit);
240
241         mono_debugger_unlock ();
242 }
243
244 /*
245  * This is called by the JIT to tell the debugging code about a newly
246  * compiled method.
247  */
248 void
249 mono_debug_add_method (MonoMethod *method, MonoDebugMethodJitInfo *jit, MonoDomain *domain)
250 {
251         MonoClass *klass = method->klass;
252         MonoDebugDomainData *domain_data;
253         MonoDebugHandle *handle;
254         MonoDebugMethodInfo *minfo;
255
256         mono_class_init (klass);
257
258         if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
259             (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
260             (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
261             (method->flags & METHOD_ATTRIBUTE_ABSTRACT))
262                 return;
263
264         handle = _mono_debug_get_image (klass->image);
265         g_assert (handle);
266
267         minfo = _mono_debug_lookup_method (method);
268         if (!minfo)
269                 return;
270
271         mono_debugger_lock ();
272
273         domain_data = mono_debug_get_domain_data (handle, domain);
274         g_assert (!domain_data->jit [minfo->index]);
275
276         if (method->wrapper_type != MONO_WRAPPER_NONE) {
277                 g_hash_table_insert (domain_data->_priv->wrapper_info, method, jit);
278                 mono_debugger_unlock ();
279                 return;
280         }
281
282         domain_data->jit [minfo->index] = jit;
283
284         if (handle->_priv->debugger_info && (domain == mono_root_domain))
285                 mono_debugger_add_method (handle->_priv->debugger_info, minfo, jit);
286
287         mono_debugger_unlock ();
288 }
289
290 static gint32
291 il_offset_from_address (MonoDebugMethodJitInfo *jit, guint32 address)
292 {
293         int i;
294
295         if (!jit || !jit->line_numbers)
296                 return -1;
297
298         for (i = jit->line_numbers->len - 1; i >= 0; i--) {
299                 MonoDebugLineNumberEntry lne = g_array_index (
300                         jit->line_numbers, MonoDebugLineNumberEntry, i);
301
302                 if (lne.address <= address)
303                         return lne.offset;
304         }
305
306         return -1;
307 }
308
309 /*
310  * Used by the exception code to get a source location from a machine address.
311  *
312  * Returns a textual representation of the specified address which is suitable to be displayed to
313  * the user (for instance "/home/martin/monocvs/debugger/test/Y.cs:8").
314  *
315  * If the optional @line_number argument is not NULL, the line number is stored there and just the
316  * source file is returned (ie. it'd return "/home/martin/monocvs/debugger/test/Y.cs" and store the
317  * line number 8 in the variable pointed to by @line_number).
318  */
319 gchar *
320 mono_debug_source_location_from_address (MonoMethod *method, guint32 address, guint32 *line_number,
321                                          MonoDomain *domain)
322 {
323         MonoDebugMethodInfo *minfo = _mono_debug_lookup_method (method);
324         MonoDebugDomainData *domain_data;
325
326         if (!minfo)
327                 return NULL;
328
329         domain_data = mono_debug_get_domain_data (minfo->handle, domain);
330         if (!domain_data->jit [minfo->index])
331                 return NULL;
332
333         if (minfo->handle) {
334                 gint32 offset = il_offset_from_address (domain_data->jit [minfo->index], address);
335                 
336                 if (offset < 0)
337                         return NULL;
338
339                 return mono_debug_find_source_location (minfo->handle->symfile, method, offset, line_number);
340         }
341
342         return NULL;
343 }
344
345 /*
346  * Used by the exception code to get a source location from an IL offset.
347  *
348  * Returns a textual representation of the specified address which is suitable to be displayed to
349  * the user (for instance "/home/martin/monocvs/debugger/test/Y.cs:8").
350  *
351  * If the optional @line_number argument is not NULL, the line number is stored there and just the
352  * source file is returned (ie. it'd return "/home/martin/monocvs/debugger/test/Y.cs" and store the
353  * line number 8 in the variable pointed to by @line_number).
354  */
355 gchar *
356 mono_debug_source_location_from_il_offset (MonoMethod *method, guint32 offset, guint32 *line_number)
357 {
358         MonoDebugMethodInfo *minfo = _mono_debug_lookup_method (method);
359
360         if (!minfo || !minfo->handle)
361                 return NULL;
362
363         return mono_debug_find_source_location (minfo->handle->symfile, method, offset, line_number);
364 }
365
366 /*
367  * Returns the IL offset corresponding to machine address @address which is an offset
368  * relative to the beginning of the method @method.
369  */
370 gint32
371 mono_debug_il_offset_from_address (MonoMethod *method, gint32 address, MonoDomain *domain)
372 {
373         MonoDebugMethodInfo *minfo;
374         MonoDebugDomainData *domain_data;
375
376         if (address < 0)
377                 return -1;
378
379         minfo = _mono_debug_lookup_method (method);
380         if (!minfo || !minfo->il_offsets)
381                 return -1;
382
383         domain_data = mono_debug_get_domain_data (minfo->handle, domain);
384
385         return il_offset_from_address (domain_data->jit [minfo->index], address);
386 }
387
388 /*
389  * Returns the machine address corresponding to IL offset @il_offset.
390  * The returned value is an offset relative to the beginning of the method @method.
391  */
392 gint32
393 mono_debug_address_from_il_offset (MonoMethod *method, gint32 il_offset, MonoDomain *domain)
394 {
395         MonoDebugMethodInfo *minfo;
396         MonoDebugDomainData *domain_data;
397
398         if (il_offset < 0)
399                 return -1;
400
401         minfo = _mono_debug_lookup_method (method);
402         if (!minfo || !minfo->il_offsets)
403                 return -1;
404
405         domain_data = mono_debug_get_domain_data (minfo->handle, domain);
406
407         return _mono_debug_address_from_il_offset (domain_data->jit [minfo->index], il_offset);
408 }
409
410 MonoDebugDomainData *
411 mono_debug_get_domain_data (MonoDebugHandle *handle, MonoDomain *domain)
412 {
413         MonoDebugDomainData *data;
414
415         for (data = handle->_priv->domain_table; data; data = data->_priv->next)
416                 if (data->domain_id == domain->domain_id)
417                         return data;
418
419         data = g_new0 (MonoDebugDomainData, 1);
420         data->domain_id = domain->domain_id;
421         data->jit = g_new0 (MonoDebugMethodJitInfo *, handle->symfile->offset_table->method_count + 1);
422
423         data->_priv = g_new0 (MonoDebugDomainDataPriv, 1);
424         data->_priv->next = handle->_priv->domain_table;
425         data->_priv->wrapper_info = g_hash_table_new (g_direct_hash, g_direct_equal);
426         handle->_priv->domain_table = data;
427
428         return data;
429 }