2008-10-07 Bill Holmes <billholmes54@gmail.com>
[mono.git] / mono / metadata / mono-debug-debugger.c
1 #include <config.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <mono/metadata/assembly.h>
5 #include <mono/metadata/metadata.h>
6 #include <mono/metadata/tabledefs.h>
7 #include <mono/metadata/tokentype.h>
8 #include <mono/metadata/appdomain.h>
9 #include <mono/metadata/gc-internal.h>
10 #include <mono/metadata/threads.h>
11 #include <mono/metadata/gc-internal.h>
12 #include <mono/metadata/object-internals.h>
13 #include <mono/metadata/class-internals.h>
14 #include <mono/metadata/exception.h>
15 #include <mono/metadata/mono-debug.h>
16 #include <mono/metadata/mono-debug-debugger.h>
17 #include <mono/metadata/mono-endian.h>
18
19 static guint32 debugger_lock_level = 0;
20 static CRITICAL_SECTION debugger_lock_mutex;
21 static gboolean mono_debugger_use_debugger = FALSE;
22 static MonoObject *last_exception = NULL;
23 volatile gint32 _mono_debugger_interruption_request = 0;
24
25 void (*mono_debugger_event_handler) (MonoDebuggerEvent event, guint64 data, guint64 arg) = NULL;
26
27 typedef struct {
28         gpointer stack_pointer;
29         MonoObject *exception_obj;
30         guint32 stop;
31 } MonoDebuggerExceptionInfo;
32
33 typedef struct
34 {
35         guint32 index;
36         MonoMethod *method;
37         MonoDebugMethodAddressList *address_list;
38 } MethodBreakpointInfo;
39
40 typedef struct {
41         MonoImage *image;
42         guint64 index;
43         guint32 token;
44         gchar *name_space;
45         gchar *name;
46 } ClassInitCallback;
47
48 static GPtrArray *class_init_callbacks = NULL;
49
50 static int initialized = 0;
51
52 void
53 mono_debugger_lock (void)
54 {
55         g_assert (initialized);
56         EnterCriticalSection (&debugger_lock_mutex);
57         debugger_lock_level++;
58 }
59
60 void
61 mono_debugger_unlock (void)
62 {
63         g_assert (initialized);
64         debugger_lock_level--;
65         LeaveCriticalSection (&debugger_lock_mutex);
66 }
67
68 void
69 mono_debugger_initialize (gboolean use_debugger)
70 {
71         MONO_GC_REGISTER_ROOT (last_exception);
72         
73         g_assert (!mono_debugger_use_debugger);
74
75         InitializeCriticalSection (&debugger_lock_mutex);
76         mono_debugger_use_debugger = use_debugger;
77         initialized = 1;
78 }
79
80 void
81 mono_debugger_event (MonoDebuggerEvent event, guint64 data, guint64 arg)
82 {
83         if (mono_debugger_event_handler)
84                 (* mono_debugger_event_handler) (event, data, arg);
85 }
86
87 void
88 mono_debugger_cleanup (void)
89 {
90         mono_debugger_event (MONO_DEBUGGER_EVENT_FINALIZE_MANAGED_CODE, 0, 0);
91         mono_debugger_event_handler = NULL;
92 }
93
94 gboolean
95 mono_debugger_unhandled_exception (gpointer addr, gpointer stack, MonoObject *exc)
96 {
97         const gchar *name;
98
99         if (!mono_debugger_use_debugger)
100                 return FALSE;
101
102         name = mono_class_get_name (mono_object_get_class (exc));
103         if (!strcmp (name, "ThreadAbortException"))
104                 return FALSE;
105
106         // Prevent the object from being finalized.
107         last_exception = exc;
108
109         mono_debugger_event (MONO_DEBUGGER_EVENT_UNHANDLED_EXCEPTION,
110                              (guint64) (gsize) exc, (guint64) (gsize) addr);
111         return TRUE;
112 }
113
114 void
115 mono_debugger_handle_exception (gpointer addr, gpointer stack, MonoObject *exc)
116 {
117         MonoDebuggerExceptionInfo info;
118
119         if (!mono_debugger_use_debugger)
120                 return;
121
122         // Prevent the object from being finalized.
123         last_exception = exc;
124
125         info.stack_pointer = stack;
126         info.exception_obj = exc;
127         info.stop = 0;
128
129         mono_debugger_event (MONO_DEBUGGER_EVENT_HANDLE_EXCEPTION, (guint64) (gsize) &info,
130                              (guint64) (gsize) addr);
131 }
132
133 gboolean
134 mono_debugger_throw_exception (gpointer addr, gpointer stack, MonoObject *exc)
135 {
136         MonoDebuggerExceptionInfo info;
137
138         if (!mono_debugger_use_debugger)
139                 return FALSE;
140
141         // Prevent the object from being finalized.
142         last_exception = exc;
143
144         info.stack_pointer = stack;
145         info.exception_obj = exc;
146         info.stop = 0;
147
148         mono_debugger_event (MONO_DEBUGGER_EVENT_THROW_EXCEPTION, (guint64) (gsize) &info,
149                              (guint64) (gsize) addr);
150         return info.stop != 0;
151 }
152
153 static gchar *
154 get_exception_message (MonoObject *exc)
155 {
156         char *message = NULL;
157         MonoString *str; 
158         MonoMethod *method;
159         MonoClass *klass;
160         gint i;
161
162         if (mono_object_isinst (exc, mono_defaults.exception_class)) {
163                 klass = exc->vtable->klass;
164                 method = NULL;
165                 while (klass && method == NULL) {
166                         for (i = 0; i < klass->method.count; ++i) {
167                                 method = klass->methods [i];
168                                 if (!strcmp ("ToString", method->name) &&
169                                     mono_method_signature (method)->param_count == 0 &&
170                                     method->flags & METHOD_ATTRIBUTE_VIRTUAL &&
171                                     method->flags & METHOD_ATTRIBUTE_PUBLIC) {
172                                         break;
173                                 }
174                                 method = NULL;
175                         }
176                         
177                         if (method == NULL)
178                                 klass = klass->parent;
179                 }
180
181                 g_assert (method);
182
183                 str = (MonoString *) mono_runtime_invoke (method, exc, NULL, NULL);
184                 if (str)
185                         message = mono_string_to_utf8 (str);
186         }
187
188         return message;
189 }
190
191 MonoObject *
192 mono_debugger_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc)
193 {
194         MonoObject *retval;
195         gchar *message;
196
197         if (!strcmp (method->name, ".ctor")) {
198                 retval = obj = mono_object_new (mono_domain_get (), method->klass);
199
200                 mono_runtime_invoke (method, obj, params, exc);
201         } else
202                 retval = mono_runtime_invoke (method, obj, params, exc);
203
204         if (!exc || (*exc == NULL))
205                 return retval;
206
207         message = get_exception_message (*exc);
208         if (message) {
209                 *exc = (MonoObject *) mono_string_new_wrapper (message);
210                 g_free (message);
211         }
212
213         return retval;
214 }
215
216 void
217 mono_debugger_check_interruption (void)
218 {
219         if (!_mono_debugger_interruption_request)
220                 return;
221
222         mono_debugger_lock ();
223         mono_debugger_event (MONO_DEBUGGER_EVENT_INTERRUPTION_REQUEST, 0, 0);
224         mono_debugger_unlock ();
225 }
226
227 /*
228  * Debugger breakpoint interface.
229  *
230  * This interface is used to insert breakpoints on methods which are not yet JITed.
231  * The debugging code keeps a list of all such breakpoints and automatically inserts the
232  * breakpoint when the method is JITed.
233  */
234
235 static GPtrArray *method_breakpoints = NULL;
236
237 MonoDebugMethodAddressList *
238 mono_debugger_insert_method_breakpoint (MonoMethod *method, guint64 index)
239 {
240         MethodBreakpointInfo *info;
241
242         info = g_new0 (MethodBreakpointInfo, 1);
243         info->method = method;
244         info->index = index;
245
246         info->address_list = mono_debug_lookup_method_addresses (method);
247
248         if (!method_breakpoints)
249                 method_breakpoints = g_ptr_array_new ();
250
251         g_ptr_array_add (method_breakpoints, info);
252
253         return info->address_list;
254 }
255
256 int
257 mono_debugger_remove_method_breakpoint (guint64 index)
258 {
259         int i;
260
261         if (!method_breakpoints)
262                 return 0;
263
264         for (i = 0; i < method_breakpoints->len; i++) {
265                 MethodBreakpointInfo *info = g_ptr_array_index (method_breakpoints, i);
266
267                 if (info->index != index)
268                         continue;
269
270                 g_ptr_array_remove (method_breakpoints, info);
271                 g_free (info->address_list);
272                 g_free (info);
273                 return 1;
274         }
275
276         return 0;
277 }
278
279 void
280 mono_debugger_check_breakpoints (MonoMethod *method, MonoDebugMethodAddress *debug_info)
281 {
282         int i;
283
284         if (!method_breakpoints)
285                 return;
286
287         if (method->is_inflated)
288                 method = ((MonoMethodInflated *) method)->declaring;
289
290         for (i = 0; i < method_breakpoints->len; i++) {
291                 MethodBreakpointInfo *info = g_ptr_array_index (method_breakpoints, i);
292
293                 if (method != info->method)
294                         continue;
295
296                 mono_debugger_event (MONO_DEBUGGER_EVENT_JIT_BREAKPOINT,
297                                      (guint64) (gsize) debug_info, info->index);
298         }
299
300         if (!class_init_callbacks)
301                 return;
302
303         for (i = 0; i < class_init_callbacks->len; i++) {
304                 ClassInitCallback *info = g_ptr_array_index (class_init_callbacks, i);
305
306                 if ((method->token != info->token) || (method->klass->image != info->image))
307                         continue;
308
309                 mono_debugger_event (MONO_DEBUGGER_EVENT_JIT_BREAKPOINT,
310                                      (guint64) (gsize) debug_info, info->index);
311         }
312 }
313
314 MonoClass *
315 mono_debugger_register_class_init_callback (MonoImage *image, const gchar *full_name,
316                                             guint32 method_token, guint32 index)
317 {
318         ClassInitCallback *info;
319         MonoClass *klass;
320         gchar *name_space, *name, *pos;
321
322         name = g_strdup (full_name);
323
324         pos = strrchr (name, '.');
325         if (pos) {
326                 name_space = name;
327                 *pos = 0;
328                 name = pos + 1;
329         } else {
330                 name_space = NULL;
331         }
332
333         mono_loader_lock ();
334
335         klass = mono_class_from_name (image, name_space ? name_space : "", name);
336         if (klass && klass->inited && klass->methods) {
337                 mono_loader_unlock ();
338                 return klass;
339         }
340
341         info = g_new0 (ClassInitCallback, 1);
342         info->image = image;
343         info->index = index;
344         info->token = method_token;
345         info->name_space = name_space;
346         info->name = name;
347
348         if (!class_init_callbacks)
349                 class_init_callbacks = g_ptr_array_new ();
350
351         g_ptr_array_add (class_init_callbacks, info);
352         mono_loader_unlock ();
353         return NULL;
354 }
355
356 void
357 mono_debugger_remove_class_init_callback (int index)
358 {
359         int i;
360
361         if (!class_init_callbacks)
362                 return;
363
364         for (i = 0; i < class_init_callbacks->len; i++) {
365                 ClassInitCallback *info = g_ptr_array_index (class_init_callbacks, i);
366
367                 if (info->index != index)
368                         continue;
369
370                 g_ptr_array_remove (class_init_callbacks, info);
371                 if (info->name_space)
372                         g_free (info->name_space);
373                 else
374                         g_free (info->name);
375                 g_free (info);
376         }
377 }
378
379 void
380 mono_debugger_class_initialized (MonoClass *klass)
381 {
382         int i;
383
384         if (!class_init_callbacks)
385                 return;
386
387  again:
388         for (i = 0; i < class_init_callbacks->len; i++) {
389                 ClassInitCallback *info = g_ptr_array_index (class_init_callbacks, i);
390
391                 if (info->name_space && strcmp (info->name_space, klass->name_space))
392                         continue;
393                 if (strcmp (info->name, klass->name))
394                         continue;
395
396                 mono_debugger_event (MONO_DEBUGGER_EVENT_CLASS_INITIALIZED,
397                                      (guint64) (gsize) klass, info->index);
398
399                 if (info->token) {
400                         int j;
401
402                         for (j = 0; j < klass->method.count; j++) {
403                                 if (klass->methods [j]->token != info->token)
404                                         continue;
405
406                                 mono_debugger_insert_method_breakpoint (klass->methods [j], info->index);
407                         }
408                 }
409
410                 g_ptr_array_remove (class_init_callbacks, info);
411                 if (info->name_space)
412                         g_free (info->name_space);
413                 else
414                         g_free (info->name);
415                 g_free (info);
416                 goto again;
417         }
418 }