summaryrefslogtreecommitdiff
path: root/mono/mini/debug-debugger.c
blob: a103595c0bf72426da06cef43e283fb219b22e1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
/*
 * debug-debugger.c: Hard debugger support (mdb)
 *
 * Author:
 *
 * Copyright 2006-2010 Novell, Inc.
 */
#if MONO_DEBUGGER_SUPPORTED

#include <config.h>
#include <mono/io-layer/io-layer.h>
#include <mono/metadata/threads.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/mono-debug.h>
#include <mono/metadata/mono-config.h>
#define _IN_THE_MONO_DEBUGGER
#include "debug-debugger.h"
#include "debug-mini.h"
#include <libgc/include/libgc-mono-debugger.h>
#include "mini.h"
#include <unistd.h>
#include <locale.h>
#include <string.h>

/*
 * This file is only compiled on platforms where the debugger is supported - see the conditional
 * definition of `debugger_sources' in Makefile.am.
 *
 * configure.in checks whether we're using the included libgc and disables the debugger if not.
 */

#if !defined(USE_INCLUDED_LIBGC)
#error "Inconsistency detected: #defined MONO_DEBUGGER_SUPPORTED without USE_INCLUDED_GC - fix configure.in!"
#endif

static guint64 debugger_compile_method (guint64 method_arg);
static guint64 debugger_get_virtual_method (guint64 class_arg, guint64 method_arg);
static guint64 debugger_get_boxed_object (guint64 klass_arg, guint64 val_arg);
static guint64 debugger_class_get_static_field_data (guint64 klass);

static guint64 debugger_run_finally (guint64 argument1, guint64 argument2);
static void debugger_initialize (void);
static guint64 debugger_init_code_buffer (void);

static void debugger_event_handler (MonoDebuggerEvent event, guint64 data, guint64 arg);

static guint64 debugger_create_string (G_GNUC_UNUSED guint64 dummy, G_GNUC_UNUSED guint64 dummy2,
				       G_GNUC_UNUSED guint64 dummy3, const gchar *string_argument);
static gint64 debugger_lookup_class (guint64 image_argument, G_GNUC_UNUSED guint64 dummy,
				     G_GNUC_UNUSED guint64 dummy2, gchar *full_name);
static guint64 debugger_insert_method_breakpoint (guint64 method_argument, guint64 index);
static guint64 debugger_insert_source_breakpoint (guint64 image_argument, guint64 token,
						  guint64 index, const gchar *class_name);
static void debugger_remove_breakpoint (guint64 index, G_GNUC_UNUSED guint64 dummy);
static guint64 debugger_register_class_init_callback (guint64 image_argument, guint64 token,
						      guint64 index, const gchar *class_name);
static void debugger_remove_class_init_callback (guint64 index, G_GNUC_UNUSED guint64 dummy);
static guint64 debugger_get_method_signature (guint64 argument1, G_GNUC_UNUSED guint64 argument2);

static guint64 debugger_abort_runtime_invoke (G_GNUC_UNUSED guint64 dummy1, G_GNUC_UNUSED guint64 dummy2);

#define EXECUTABLE_CODE_BUFFER_SIZE 4096
static guint8 *debugger_executable_code_buffer = NULL;

static GCThreadFunctions debugger_thread_vtable;

static guint32 debugger_thread_abort_signal = 0;

static MonoDebuggerMetadataInfo debugger_metadata_info = {
	sizeof (MonoDebuggerMetadataInfo),
	sizeof (MonoDefaults),
	&mono_defaults,
	MONO_SIZEOF_TYPE,
	sizeof (MonoArrayType),
	sizeof (MonoClass),
	sizeof (MonoInternalThread),
	G_STRUCT_OFFSET (MonoInternalThread, tid),
	G_STRUCT_OFFSET (MonoInternalThread, stack_ptr),
	G_STRUCT_OFFSET (MonoInternalThread, end_stack),
	G_STRUCT_OFFSET (MonoClass, image),
	G_STRUCT_OFFSET (MonoClass, instance_size),
	G_STRUCT_OFFSET (MonoClass, parent),
	G_STRUCT_OFFSET (MonoClass, type_token),
	G_STRUCT_OFFSET (MonoClass, fields),
	G_STRUCT_OFFSET (MonoClass, methods),
	G_STRUCT_OFFSET (MonoClass, method.count),
	G_STRUCT_OFFSET (MonoClass, this_arg),
	G_STRUCT_OFFSET (MonoClass, byval_arg),
	G_STRUCT_OFFSET (MonoClass, generic_class),
	G_STRUCT_OFFSET (MonoClass, generic_container),
	G_STRUCT_OFFSET (MonoClass, vtable),
	sizeof (MonoClassField),
	G_STRUCT_OFFSET (MonoClassField, type),
	G_STRUCT_OFFSET (MonoClassField, offset),
	G_STRUCT_OFFSET (MonoDefaults, corlib),
	G_STRUCT_OFFSET (MonoDefaults, object_class),
	G_STRUCT_OFFSET (MonoDefaults, byte_class),
	G_STRUCT_OFFSET (MonoDefaults, void_class),
	G_STRUCT_OFFSET (MonoDefaults, boolean_class),
	G_STRUCT_OFFSET (MonoDefaults, sbyte_class),
	G_STRUCT_OFFSET (MonoDefaults, int16_class),
	G_STRUCT_OFFSET (MonoDefaults, uint16_class),
	G_STRUCT_OFFSET (MonoDefaults, int32_class),
	G_STRUCT_OFFSET (MonoDefaults, uint32_class),
	G_STRUCT_OFFSET (MonoDefaults, int_class),
	G_STRUCT_OFFSET (MonoDefaults, uint_class),
	G_STRUCT_OFFSET (MonoDefaults, int64_class),
	G_STRUCT_OFFSET (MonoDefaults, uint64_class),
	G_STRUCT_OFFSET (MonoDefaults, single_class),
	G_STRUCT_OFFSET (MonoDefaults, double_class),
	G_STRUCT_OFFSET (MonoDefaults, char_class),
	G_STRUCT_OFFSET (MonoDefaults, string_class),
	G_STRUCT_OFFSET (MonoDefaults, enum_class),
	G_STRUCT_OFFSET (MonoDefaults, array_class),
	G_STRUCT_OFFSET (MonoDefaults, delegate_class),
	G_STRUCT_OFFSET (MonoDefaults, exception_class),
	G_STRUCT_OFFSET (MonoMethod, klass),
	G_STRUCT_OFFSET (MonoMethod, token),
	G_STRUCT_OFFSET (MonoMethod, name) + sizeof (void *),
	G_STRUCT_OFFSET (MonoMethodInflated, declaring),
	G_STRUCT_OFFSET (MonoVTable, klass),
	G_STRUCT_OFFSET (MonoVTable, vtable)
};

extern void MONO_DEBUGGER__notification_function (guint64 command, guint64 data, guint64 data2);

/*
 * This is a global data symbol which is read by the debugger.
 */
MonoDebuggerInfo MONO_DEBUGGER__debugger_info = {
	MONO_DEBUGGER_MAGIC,
	MONO_DEBUGGER_MAJOR_VERSION,
	MONO_DEBUGGER_MINOR_VERSION,
	0, /* runtime_flags */
	sizeof (MonoDebuggerInfo),
	sizeof (MonoSymbolTable),
	MONO_TRAMPOLINE_NUM,
	mono_trampoline_code,
	&MONO_DEBUGGER__notification_function,
	&mono_symbol_table,
	&debugger_metadata_info,
	&mono_debug_debugger_version,

	&debugger_compile_method,
	&debugger_get_virtual_method,
	&debugger_get_boxed_object,
	&mono_debugger_runtime_invoke,
	&debugger_class_get_static_field_data,
	&debugger_run_finally,
	&debugger_initialize,

	&debugger_create_string,
	&debugger_lookup_class,

	&debugger_insert_method_breakpoint,
	&debugger_insert_source_breakpoint,
	&debugger_remove_breakpoint,

	&debugger_register_class_init_callback,
	&debugger_remove_class_init_callback,

	&mono_debugger_thread_table,

	&debugger_executable_code_buffer,
	mono_breakpoint_info,
	mono_breakpoint_info_index,

	EXECUTABLE_CODE_BUFFER_SIZE,
	MONO_BREAKPOINT_ARRAY_SIZE,

	debugger_get_method_signature,
	debugger_init_code_buffer,

	&gc_thread_vtable,
	&debugger_thread_vtable,

	&mono_debugger_event_handler,
	debugger_event_handler,

	&_mono_debug_using_mono_debugger,
	(gint32*)&_mono_debugger_interruption_request,

	&debugger_abort_runtime_invoke,

	&debugger_thread_abort_signal
};

static guint64
debugger_abort_runtime_invoke (G_GNUC_UNUSED guint64 dummy1, G_GNUC_UNUSED guint64 dummy2)
{
	return mono_debugger_abort_runtime_invoke ();
}

static guint64
debugger_compile_method (guint64 method_arg)
{
	MonoMethod *method = (MonoMethod *) GUINT_TO_POINTER ((gsize) method_arg);
	gpointer addr;

	mono_debugger_lock ();
	addr = mono_compile_method (method);
	mono_debugger_unlock ();

	return (guint64) (gsize) addr;
}

static guint64
debugger_get_virtual_method (guint64 object_arg, guint64 method_arg)
{
	MonoObject *object = (MonoObject *) GUINT_TO_POINTER ((gsize) object_arg);
	MonoMethod *method = (MonoMethod *) GUINT_TO_POINTER ((gsize) method_arg);

	if (mono_class_is_valuetype (mono_method_get_class (method)))
		return method_arg;

	return (guint64) (gsize) mono_object_get_virtual_method (object, method);
}

static guint64
debugger_get_boxed_object (guint64 klass_arg, guint64 val_arg)
{
	static MonoObject *last_boxed_object = NULL;
	MonoClass *klass = (MonoClass *) GUINT_TO_POINTER ((gsize) klass_arg);
	gpointer val = (gpointer) GUINT_TO_POINTER ((gsize) val_arg);
	MonoObject *boxed;

	if (!mono_class_is_valuetype (klass))
		return val_arg;

	boxed = mono_value_box (mono_domain_get (), klass, val);
	last_boxed_object = boxed; // Protect the object from being garbage collected

	return (guint64) (gsize) boxed;
}

static guint64
debugger_create_string (G_GNUC_UNUSED guint64 dummy, G_GNUC_UNUSED guint64 dummy2,
			G_GNUC_UNUSED guint64 dummy3, const gchar *string_argument)
{
	return (guint64) (gsize) mono_string_new_wrapper (string_argument);
}

static gint64
debugger_lookup_class (guint64 image_argument, G_GNUC_UNUSED guint64 dummy,
		       G_GNUC_UNUSED guint64 dummy2, gchar *full_name)
{
	MonoImage *image = (MonoImage *) GUINT_TO_POINTER ((gsize) image_argument);
	gchar *name_space, *name, *pos;
	MonoClass *klass;

	pos = strrchr (full_name, '.');
	if (pos) {
		name_space = full_name;
		*pos = 0;
		name = pos + 1;
	} else {
		name = full_name;
		name_space = NULL;
	}

	klass = mono_class_from_name (image, name_space ? name_space : "", name);
	if (!klass)
		return -1;

	mono_class_init (klass);
	mono_class_setup_methods (klass);
	return (gint64) (gssize) klass;
}

static guint64
debugger_run_finally (guint64 context_argument, G_GNUC_UNUSED guint64 dummy)
{
	mono_debugger_run_finally (GUINT_TO_POINTER ((gsize)context_argument));
	return 0;
}

static guint64
debugger_class_get_static_field_data (guint64 value)
{
	MonoClass *klass = GUINT_TO_POINTER ((gsize) value);
	MonoVTable *vtable = mono_class_vtable (mono_domain_get (), klass);
	return (guint64) (gsize) mono_vtable_get_static_field_data (vtable);
}

static guint64
debugger_insert_method_breakpoint (guint64 method_argument, guint64 index)
{
	MonoMethod *method = GUINT_TO_POINTER ((gsize) method_argument);
	MonoDebugMethodAddressList *info;

	mono_debugger_lock ();

	if (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) {
		const char *name = method->name;
		MonoMethod *nm = NULL;

		if (method->klass->parent == mono_defaults.multicastdelegate_class) {
			if (*name == 'I' && (strcmp (name, "Invoke") == 0))
			        nm = mono_marshal_get_delegate_invoke (method, NULL);
			else if (*name == 'B' && (strcmp (name, "BeginInvoke") == 0))
				nm = mono_marshal_get_delegate_begin_invoke (method);
			else if (*name == 'E' && (strcmp (name, "EndInvoke") == 0))
				nm = mono_marshal_get_delegate_end_invoke (method);
		}

		if (!nm) {
			mono_debugger_unlock ();
			return 0;
		}

		method = nm;
	}

	info = mono_debugger_insert_method_breakpoint (method, index);

	mono_debugger_unlock ();
	return (guint64) (gsize) info;
}

static guint64
debugger_insert_source_breakpoint (guint64 image_argument, guint64 token, guint64 index,
				   const gchar *class_name)
{
	MonoImage *image = GUINT_TO_POINTER ((gsize) image_argument);
	MonoDebugMethodAddressList *info;
	MonoClass *klass;
	int i;

	mono_debugger_lock ();

	klass = mono_debugger_register_class_init_callback (image, class_name, token, index);
	if (!klass || !klass->inited || !klass->methods) {
		mono_debugger_unlock ();
		return 0;
	}

	for (i = 0; i < klass->method.count; i++) {
		MonoMethod *method = klass->methods [i];

		if (method->token != token)
			continue;

		if (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) {
			const char *name = method->name;
			MonoMethod *nm = NULL;

			if (method->klass->parent == mono_defaults.multicastdelegate_class) {
				if (*name == 'I' && (strcmp (name, "Invoke") == 0))
					nm = mono_marshal_get_delegate_invoke (method, NULL);
				else if (*name == 'B' && (strcmp (name, "BeginInvoke") == 0))
					nm = mono_marshal_get_delegate_begin_invoke (method);
				else if (*name == 'E' && (strcmp (name, "EndInvoke") == 0))
					nm = mono_marshal_get_delegate_end_invoke (method);
			}

			if (!nm) {
				mono_debugger_unlock ();
				return 0;
			}

			method = nm;
		}

		info = mono_debug_lookup_method_addresses (method);
		mono_debugger_unlock ();
		return (guint64) (gsize) info;
	}

	mono_debugger_unlock ();
	return 0;
}

static void
debugger_remove_breakpoint (guint64 index, G_GNUC_UNUSED guint64 dummy)
{
	mono_debugger_lock ();
	mono_debugger_remove_method_breakpoint (index);
	mono_debugger_remove_class_init_callback (index);
	mono_debugger_unlock ();
}

static guint64
debugger_register_class_init_callback (guint64 image_argument, guint64 token, guint64 index,
				       const gchar *class_name)
{
	MonoImage *image = GUINT_TO_POINTER ((gsize) image_argument);
	MonoClass *klass;

	mono_debugger_lock ();
	klass = mono_debugger_register_class_init_callback (image, class_name, token, index);
	mono_debugger_unlock ();
	return (guint64) (gsize) klass;
}

static void
debugger_remove_class_init_callback (guint64 index, G_GNUC_UNUSED guint64 dummy)
{
	mono_debugger_lock ();
	mono_debugger_remove_class_init_callback (index);
	mono_debugger_unlock ();
}

static guint64
debugger_get_method_signature (guint64 method_arg, G_GNUC_UNUSED guint64 dummy)
{
	MonoMethod *method = (MonoMethod *) GUINT_TO_POINTER ((gsize) method_arg);
	MonoMethodSignature *sig;

	sig = mono_method_signature (method);
	return (guint64) (gsize) sig;
}

static void
debugger_event_handler (MonoDebuggerEvent event, guint64 data, guint64 arg)
{
	MONO_DEBUGGER__notification_function (event, data, arg);
}

static void
debugger_gc_thread_created (pthread_t thread, void *stack_ptr)
{
	mono_debugger_event (MONO_DEBUGGER_EVENT_GC_THREAD_CREATED,
			     (guint64) (gsize) stack_ptr, thread);
}

static void
debugger_gc_thread_exited (pthread_t thread, void *stack_ptr)
{
	mono_debugger_event (MONO_DEBUGGER_EVENT_GC_THREAD_EXITED,
			     (guint64) (gsize) stack_ptr, thread);
}

static void
debugger_gc_stop_world (void)
{
	mono_debugger_event (MONO_DEBUGGER_EVENT_ACQUIRE_GLOBAL_THREAD_LOCK, 0, 0);
}

static void
debugger_gc_start_world (void)
{
	mono_debugger_event (MONO_DEBUGGER_EVENT_RELEASE_GLOBAL_THREAD_LOCK, 0, 0);
}

static GCThreadFunctions debugger_thread_vtable = {
	NULL,

	debugger_gc_thread_created,
	debugger_gc_thread_exited,

	debugger_gc_stop_world,
	debugger_gc_start_world
};

static void
debugger_init_threads (void)
{
	gc_thread_vtable = &debugger_thread_vtable;
}

#if 0

static void
debugger_finalize_threads (void)
{
	gc_thread_vtable = NULL;
}

#endif

static guint64
debugger_init_code_buffer (void)
{
	if (!debugger_executable_code_buffer)
		debugger_executable_code_buffer = mono_global_codeman_reserve (EXECUTABLE_CODE_BUFFER_SIZE);
	return (guint64) (gsize) debugger_executable_code_buffer;
}

extern MonoDebuggerInfo *MONO_DEBUGGER__debugger_info_ptr;
extern long MONO_DEBUGGER__using_debugger;

static void
debugger_initialize (void)
{
}

/**
 * Check whether we're running inside the debugger.
 *
 * There seems to be a bug in some versions of glibc which causes _dl_debug_state() being called with
 * RT_CONSISTENT before relocations are done.
 *
 * If that happens, the debugger cannot read the `MONO_DEBUGGER__debugger_info' structure at the time
 * the `libmono.so' library is loaded.
 *
 * As a workaround, the `mdb_debug_info' now also contains a global variable called
 * `MONO_DEBUGGER__using_debugger' which may we set to 1 by the debugger to tell us that we're running
 * inside the debugger.
 *
 * mini_init() checks this and calls mini_debugger_init() if necessary.
 *
 */

gboolean
mini_debug_running_inside_mdb (void)
{
	return MONO_DEBUGGER__using_debugger || mono_debug_using_mono_debugger ();
}

void
mini_debugger_init (void)
{
	if (mono_debugger_event_handler) {
		g_warning (G_STRLOC ": duplicate call to mono_debugger_init()!");
		return;
	}

	debugger_executable_code_buffer = mono_global_codeman_reserve (EXECUTABLE_CODE_BUFFER_SIZE);
	mono_debugger_event_handler = debugger_event_handler;

	debugger_thread_abort_signal = mono_thread_get_abort_signal ();

	/*
	 * Use an indirect call so gcc can't optimize it away.
	 */
	MONO_DEBUGGER__debugger_info.initialize ();

	debugger_init_threads ();

	/*
	 * Initialize the thread manager.
	 *
	 * NOTE: We only reference the `MONO_DEBUGGER__debugger_info_ptr' here to prevent the
	 * linker from removing the .mdb_debug_info section.
	 */

	mono_debugger_event (MONO_DEBUGGER_EVENT_INITIALIZE_THREAD_MANAGER,
			     (guint64) (gssize) MONO_DEBUGGER__debugger_info_ptr, 0);
}

void
mini_debugger_set_attach_ok (void)
{
	debugger_thread_abort_signal = mono_thread_get_abort_signal ();
	MONO_DEBUGGER__debugger_info.runtime_flags |= DEBUGGER_RUNTIME_FLAGS_ATTACH_OK;
}

typedef struct 
{
	MonoDomain *domain;
	const char *file;
} DebuggerThreadArgs;

typedef struct
{
	MonoDomain *domain;
	MonoMethod *method;
	int argc;
	char **argv;
} MainThreadArgs;

static guint32
main_thread_handler (gpointer user_data)
{
	MainThreadArgs *main_args = (MainThreadArgs *) user_data;

	return mono_runtime_run_main (main_args->method, main_args->argc, main_args->argv, NULL);
}

int
mini_debugger_main (MonoDomain *domain, MonoAssembly *assembly, int argc, char **argv)
{
	MainThreadArgs main_args;
	MonoImage *image;
	MonoMethod *main_method;

	/*
	 * Get and compile the main function.
	 */

	image = mono_assembly_get_image (assembly);
	main_method = mono_get_method (image, mono_image_get_entry_point (image), NULL);

	/*
	 * Initialize managed code.
	 */
	mono_debugger_event (MONO_DEBUGGER_EVENT_INITIALIZE_MANAGED_CODE,
			     (guint64) (gssize) main_method, 0);

	/*
	 * Start the main thread and wait until it's ready.
	 */

	main_args.domain = domain;
	main_args.method = main_method;
	main_args.argc = argc;
	main_args.argv = argv;

#if RUN_IN_SUBTHREAD
	mono_thread_create (domain, main_thread_handler, &main_args);
#else
	main_thread_handler (&main_args);
#endif

	mono_thread_manage ();

	/*
	 * This will never return.
	 */
	mono_debugger_event (MONO_DEBUGGER_EVENT_WRAPPER_MAIN, 0, 0);

	return 0;
}
#endif /* MONO_DEBUGGER_SUPPORTED */