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