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