5 #include <mono/metadata/class.h>
6 #include <mono/metadata/tabledefs.h>
7 #include <mono/metadata/tokentype.h>
8 #include <mono/jit/codegen.h>
9 #include <mono/jit/debug.h>
11 #include "debug-private.h"
13 static MonoDebugHandle *mono_debug_handles = NULL;
14 static MonoDebugHandle *mono_default_debug_handle = NULL;
17 free_method_info (DebugMethodInfo *minfo)
19 if (minfo->line_numbers)
20 g_ptr_array_free (minfo->line_numbers, TRUE);
21 g_free (minfo->method_info.params);
22 g_free (minfo->method_info.locals);
27 debug_arg_warning (const char *message)
29 g_warning ("Error while processing --debug-args arguments: %s", message);
33 mono_debug_open (MonoAssembly *assembly, MonoDebugFormat format, const char **args)
35 MonoDebugHandle *debug;
38 debug = g_new0 (MonoDebugHandle, 1);
39 debug->name = g_strdup (assembly->image->name);
40 debug->format = format;
41 debug->producer_name = g_strdup_printf ("Mono JIT compiler version %s", VERSION);
42 debug->next_idx = 100;
44 debug->type_hash = g_hash_table_new (NULL, NULL);
45 debug->methods = g_hash_table_new_full (g_direct_hash, g_direct_equal,
46 NULL, (GDestroyNotify) free_method_info);
47 debug->source_files = g_ptr_array_new ();
49 for (ptr = args; ptr && *ptr; ptr++) {
50 const char *arg = *ptr;
53 switch (debug->format) {
54 case MONO_DEBUG_FORMAT_STABS:
55 case MONO_DEBUG_FORMAT_DWARF2:
56 if (!strncmp (arg, "filename=", 9)) {
58 debug_arg_warning ("The `filename' argument can be given only once.");
59 debug->filename = g_strdup (arg + 9);
61 } else if (!strncmp (arg, "objfile=", 8)) {
63 debug_arg_warning ("The `objfile' argument can be given only once.");
64 debug->objfile = g_strdup (arg + 8);
66 } else if (!strcmp (arg, "install_il_files")) {
67 debug->flags |= MONO_DEBUG_FLAGS_INSTALL_IL_FILES;
69 } else if (!strcmp (arg, "dont_update_il_files")) {
70 debug->flags |= MONO_DEBUG_FLAGS_DONT_UPDATE_IL_FILES;
72 } else if (!strcmp (arg, "dont_create_il_files")) {
73 debug->flags |= MONO_DEBUG_FLAGS_DONT_CREATE_IL_FILES;
77 case MONO_DEBUG_FORMAT_DWARF2_PLUS:
78 if (!strcmp (arg, "dont_fallback")) {
79 debug->flags |= MONO_DEBUG_FLAGS_DONT_FALLBACK;
81 } else if (!strcmp (arg, "dont_precompile")) {
82 debug->flags |= MONO_DEBUG_FLAGS_DONT_PRECOMPILE;
90 if (!strcmp (arg, "dont_assemble")) {
91 debug->flags |= MONO_DEBUG_FLAGS_DONT_ASSEMBLE;
93 } else if (!strcmp (arg, "update_on_exit")) {
94 debug->flags |= MONO_DEBUG_FLAGS_UPDATE_ON_EXIT;
98 message = g_strdup_printf ("Unknown argument `%s'.", arg);
99 debug_arg_warning (message);
103 switch (debug->format) {
104 case MONO_DEBUG_FORMAT_STABS:
105 if (!debug->filename)
106 debug->filename = g_strdup_printf ("%s-stabs.s", g_basename (debug->name));
108 debug->objfile = g_strdup_printf ("%s.o", g_basename (debug->name));
110 case MONO_DEBUG_FORMAT_DWARF2:
111 if (!debug->filename)
112 debug->filename = g_strdup_printf ("%s-dwarf.s", g_basename (debug->name));
114 debug->objfile = g_strdup_printf ("%s.o", g_basename (debug->name));
116 case MONO_DEBUG_FORMAT_DWARF2_PLUS:
117 if (!mono_default_debug_handle && !(debug->flags & MONO_DEBUG_FLAGS_DONT_FALLBACK))
118 mono_debug_open (assembly, MONO_DEBUG_FORMAT_DWARF2, NULL);
121 g_assert_not_reached ();
124 debug->next = mono_debug_handles;
125 mono_debug_handles = debug;
127 if (!mono_default_debug_handle && (debug->format != MONO_DEBUG_FORMAT_DWARF2_PLUS))
128 mono_default_debug_handle = debug;
130 mono_debug_add_image (debug, assembly->image);
136 debug_load_method_lines (AssemblyDebugInfo* info)
143 if (!(info->handle->flags & MONO_DEBUG_FLAGS_DONT_UPDATE_IL_FILES)) {
144 char *command = g_strdup_printf ("monodis --output=%s %s",
145 info->ilfile, info->image->name);
146 struct stat stata, statb;
147 int need_update = FALSE;
149 if (stat (info->image->name, &stata)) {
150 g_warning ("cannot access assembly file (%s): %s",
151 info->image->name, g_strerror (errno));
156 /* If the stat() failed or the file is older. */
157 if (stat (info->ilfile, &statb)) {
158 /* Don't create any new *.il files if the user told us not to do so. */
159 if (!(info->handle->flags & MONO_DEBUG_FLAGS_DONT_CREATE_IL_FILES))
161 } else if (statb.st_mtime < stata.st_mtime)
165 g_print ("Recreating %s from %s.\n", info->ilfile, info->image->name);
166 if (system (command)) {
167 g_warning ("cannot create IL assembly file (%s): %s",
168 command, g_strerror (errno));
175 /* use an env var with directories for searching. */
176 if (!(f = fopen (info->ilfile, "r"))) {
177 g_warning ("cannot open IL assembly file %s", info->ilfile);
181 info->total_lines = 100;
182 info->moffsets = g_malloc (info->total_lines * sizeof (int));
185 while (fgets (buf, sizeof (buf), f)) {
188 info->moffsets [i++] = offset;
189 if (i + 2 >= info->total_lines) {
190 info->total_lines += 100;
191 info->moffsets = g_realloc (info->moffsets, info->total_lines * sizeof (int));
192 g_assert (info->moffsets);
195 if (!sscanf (buf, " // method line %d", &mnum))
200 if (mnum >= info->nmethods)
203 while (fgets (buf, sizeof (buf), f)) {
207 if (i + 2 >= info->total_lines) {
208 info->total_lines += 100;
209 info->moffsets = g_realloc (info->moffsets, info->total_lines * sizeof (int));
210 g_assert (info->moffsets);
213 if (strstr (buf, "}")) {
218 if (sscanf (buf, " IL_%x:", &newoffset)) {
224 info->moffsets [i] = offset;
226 /* g_print ("method %d found at %d\n", mnum, pos); */
227 info->mlines [mnum] = pos;
233 record_line_number (DebugMethodInfo *minfo, gpointer address, guint32 line, int is_basic_block)
235 DebugLineNumberInfo *lni = g_new0 (DebugLineNumberInfo, 1);
237 lni->address = address;
239 lni->is_basic_block = is_basic_block;
240 lni->source_file = minfo->source_file;
242 g_ptr_array_add (minfo->line_numbers, lni);
246 record_il_offset (GPtrArray *array, guint32 offset, guint32 address)
248 MonoDebugILOffsetInfo *info = g_new0 (MonoDebugILOffsetInfo, 1);
250 info->offset = offset;
251 info->address = address;
253 g_ptr_array_add (array, info);
257 debug_generate_method_lines (AssemblyDebugInfo *info, DebugMethodInfo *minfo, MonoFlowGraph* cfg)
259 guint32 st_address, st_line;
260 GPtrArray *il_offsets;
263 il_offsets = g_ptr_array_new ();
264 minfo->line_numbers = g_ptr_array_new ();
266 st_line = minfo->first_line;
267 st_address = minfo->method_info.prologue_end;
269 /* record_line_number takes absolute memory addresses. */
270 record_line_number (minfo, minfo->method_info.code_start, minfo->start_line, FALSE);
271 /* record_il_offsets uses offsets relative to minfo->method_info.code_start. */
272 record_il_offset (il_offsets, 0, st_address);
274 /* This is the first actual code line of the method. */
275 record_line_number (minfo, minfo->method_info.code_start + st_address, st_line, TRUE);
277 /* start lines of basic blocks */
278 for (i = 0; i < cfg->block_count; ++i) {
281 for (j = 0; cfg->bblocks [i].forest && (j < cfg->bblocks [i].forest->len); ++j) {
282 MBTree *t = (MBTree *) g_ptr_array_index (cfg->bblocks [i].forest, j);
283 gint32 line_inc = 0, addr_inc;
286 st_line = minfo->first_line;
287 st_address = t->addr - 1;
289 record_line_number (minfo, cfg->start + st_address, st_line, TRUE);
292 addr_inc = t->addr - st_address - 1;
293 st_address += addr_inc;
295 if (t->cli_addr != -1)
296 record_il_offset (il_offsets, t->cli_addr, st_address);
302 if (t->cli_addr != -1) {
303 int *lines = info->moffsets + st_line;
306 while ((*k != -1) && (*k < t->cli_addr))
309 line_inc = k - lines;
314 record_line_number (minfo, minfo->method_info.code_start + st_address,
319 minfo->method_info.num_il_offsets = il_offsets->len;
320 minfo->method_info.il_offsets = g_new0 (MonoDebugILOffsetInfo, il_offsets->len);
321 for (i = 0; i < il_offsets->len; i++) {
322 MonoDebugILOffsetInfo *il = (MonoDebugILOffsetInfo *) g_ptr_array_index (il_offsets, i);
324 minfo->method_info.il_offsets [i] = *il;
327 g_ptr_array_free (il_offsets, TRUE);
330 static AssemblyDebugInfo *
331 mono_debug_get_image (MonoDebugHandle* debug, MonoImage *image)
334 AssemblyDebugInfo *info;
336 if (debug->format == MONO_DEBUG_FORMAT_NONE)
339 for (tmp = debug->info; tmp; tmp = tmp->next) {
340 info = (AssemblyDebugInfo*)tmp->data;
342 if (info->image == image)
349 static AssemblyDebugInfo *
350 mono_debug_open_image (MonoDebugHandle* debug, MonoImage *image)
352 AssemblyDebugInfo *info;
354 info = mono_debug_get_image (debug, image);
358 info = g_new0 (AssemblyDebugInfo, 1);
360 info->image->ref_count++;
361 info->name = g_strdup (image->assembly_name);
362 info->format = debug->format;
363 info->handle = debug;
365 info->source_file = debug->source_files->len;
366 g_ptr_array_add (debug->source_files, g_strdup_printf ("%s.il", image->assembly_name));
368 debug->info = g_list_prepend (debug->info, info);
370 info->nmethods = image->tables [MONO_TABLE_METHOD].rows + 1;
371 info->mlines = g_new0 (int, info->nmethods);
373 switch (info->format) {
374 case MONO_DEBUG_FORMAT_STABS:
375 case MONO_DEBUG_FORMAT_DWARF2:
376 if (debug->flags & MONO_DEBUG_FLAGS_INSTALL_IL_FILES) {
377 gchar *dirname = g_path_get_dirname (image->name);
378 info->ilfile = g_strdup_printf ("%s/%s.il", dirname, info->name);
381 info->ilfile = g_strdup_printf ("%s.il", info->name);
383 case MONO_DEBUG_FORMAT_DWARF2_PLUS:
384 info->filename = g_strdup_printf ("%s-debug.s", info->name);
385 info->objfile = g_strdup_printf ("%s-debug.o", info->name);
386 mono_debug_open_assembly_dwarf2_plus (info);
392 if (debug->format != MONO_DEBUG_FORMAT_DWARF2_PLUS)
393 debug_load_method_lines (info);
399 mono_debug_add_image (MonoDebugHandle* debug, MonoImage *image)
401 mono_debug_open_image (debug, image);
405 mono_debug_write_symbols (MonoDebugHandle *debug)
412 switch (debug->format) {
413 case MONO_DEBUG_FORMAT_STABS:
414 mono_debug_write_stabs (debug);
416 case MONO_DEBUG_FORMAT_DWARF2:
417 mono_debug_write_dwarf2 (debug);
419 case MONO_DEBUG_FORMAT_DWARF2_PLUS:
420 for (tmp = debug->info; tmp; tmp = tmp->next) {
421 AssemblyDebugInfo *info = (AssemblyDebugInfo*)tmp->data;
423 mono_debug_write_assembly_dwarf2_plus (info);
427 g_assert_not_reached ();
432 mono_debug_make_symbols (void)
434 MonoDebugHandle *debug;
436 for (debug = mono_debug_handles; debug; debug = debug->next)
437 mono_debug_write_symbols (debug);
441 mono_debug_close_assembly (AssemblyDebugInfo* info)
443 switch (info->format) {
444 case MONO_DEBUG_FORMAT_DWARF2_PLUS:
445 mono_debug_close_assembly_dwarf2_plus (info);
450 g_free (info->mlines);
451 g_free (info->moffsets);
453 g_free (info->ilfile);
454 g_free (info->filename);
455 g_free (info->objfile);
460 mono_debug_cleanup (void)
462 MonoDebugHandle *debug, *temp;
464 for (debug = mono_debug_handles; debug; debug = temp) {
467 if (debug->flags & MONO_DEBUG_FLAGS_UPDATE_ON_EXIT)
468 mono_debug_write_symbols (debug);
471 for (tmp = debug->info; tmp; tmp = tmp->next) {
472 AssemblyDebugInfo* info = (AssemblyDebugInfo*)tmp->data;
474 mono_debug_close_assembly (info);
477 g_ptr_array_free (debug->source_files, TRUE);
478 g_hash_table_destroy (debug->methods);
479 g_hash_table_destroy (debug->type_hash);
480 g_free (debug->producer_name);
481 g_free (debug->name);
487 mono_debug_handles = NULL;
488 mono_default_debug_handle = NULL;
492 mono_debug_get_type (MonoDebugHandle *debug, MonoClass *klass)
496 mono_class_init (klass);
498 index = GPOINTER_TO_INT (g_hash_table_lookup (debug->type_hash, klass));
502 index = ++debug->next_klass_idx;
503 g_hash_table_insert (debug->type_hash, klass, GINT_TO_POINTER (index));
508 switch (klass->byval_arg.type) {
509 case MONO_TYPE_CLASS:
511 mono_debug_get_type (debug, klass->parent);
513 for (i = 0; i < klass->method.count; i++) {
514 MonoMethod *method = klass->methods [i];
515 MonoType *ret_type = NULL;
518 if (method->signature->ret->type != MONO_TYPE_VOID)
519 ret_type = method->signature->ret;
522 MonoClass *ret_klass = mono_class_from_mono_type (ret_type);
523 mono_debug_get_type (debug, ret_klass);
526 for (j = 0; j < method->signature->param_count; j++) {
527 MonoType *sub_type = method->signature->params [j];
528 MonoClass *sub_klass = mono_class_from_mono_type (sub_type);
529 mono_debug_get_type (debug, sub_klass);
533 case MONO_TYPE_VALUETYPE:
534 for (i = 0; i < klass->field.count; i++) {
535 MonoClass *subclass = mono_class_from_mono_type (klass->fields [i].type);
536 mono_debug_get_type (debug, subclass);
539 case MONO_TYPE_ARRAY:
540 case MONO_TYPE_SZARRAY:
541 mono_debug_get_type (debug, klass->element_class);
551 mono_debug_handle_from_class (MonoClass *klass)
553 MonoDebugHandle *debug;
555 mono_class_init (klass);
557 for (debug = mono_debug_handles; debug; debug = debug->next) {
560 for (tmp = debug->info; tmp; tmp = tmp->next) {
561 AssemblyDebugInfo *info = (AssemblyDebugInfo*)tmp->data;
563 if (info->image == klass->image)
572 mono_debug_add_type (MonoClass *klass)
574 MonoDebugHandle *debug = mono_debug_handle_from_class (klass);
576 g_assert (debug != NULL);
578 mono_debug_get_type (debug, klass);
582 mono_debug_add_method (MonoFlowGraph *cfg)
584 MonoMethod *method = cfg->method;
585 MonoClass *klass = method->klass;
586 int method_number = 0, line = 0, start_line = 0, end_line = 0, i;
587 MonoDebugHandle* debug;
588 AssemblyDebugInfo* info;
589 DebugMethodInfo *minfo;
592 mono_class_init (klass);
594 debug = mono_debug_handle_from_class (klass);
596 if (mono_default_debug_handle)
597 debug = mono_default_debug_handle;
602 info = mono_debug_open_image (debug, klass->image);
605 * Find the method index in the image.
607 for (i = 0; klass->methods && i < klass->method.count; ++i) {
608 if (klass->methods [i] == method) {
609 method_number = klass->method.first + i + 1;
610 line = info->mlines [method_number];
615 if (g_hash_table_lookup (debug->methods, method))
618 if (info->moffsets) {
619 /* info->moffsets contains -1 "outside" of functions. */
620 for (i = line; (i > 0) && (info->moffsets [i] == 0); i--)
624 for (i = start_line; info->moffsets [i] != -1; i++)
629 name = g_strdup_printf ("%s%s%s.%s", klass->name_space, klass->name_space [0]? ".": "",
630 klass->name, method->name);
632 minfo = g_new0 (DebugMethodInfo, 1);
634 minfo->start_line = start_line;
635 minfo->first_line = line;
636 minfo->last_line = end_line;
637 minfo->source_file = info->source_file;
639 minfo->method_info.code_start = cfg->start + 1;
640 minfo->method_info.code_size = cfg->epilogue_end - 1;
641 minfo->method_number = method_number;
642 minfo->method_info.method = method;
643 minfo->method_info.num_params = method->signature->param_count;
644 minfo->method_info.params = g_new0 (MonoDebugVarInfo, minfo->method_info.num_params);
645 minfo->method_info.prologue_end = cfg->prologue_end - 1;
646 minfo->method_info.epilogue_begin = cfg->epilog - 1;
648 if (method->signature->hasthis) {
649 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->args_start_index;
651 minfo->method_info.this_var = g_new0 (MonoDebugVarInfo, 1);
652 minfo->method_info.this_var->offset = ptr->offset;
655 for (i = 0; i < minfo->method_info.num_params; i++) {
656 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->args_start_index +
657 method->signature->hasthis;
659 minfo->method_info.params [i].offset = ptr [i].offset;
662 if (!method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
663 MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
664 MonoVarInfo *ptr = ((MonoVarInfo *) cfg->varinfo->data) + cfg->locals_start_index;
666 minfo->method_info.num_locals = header->num_locals;
667 minfo->method_info.locals = g_new0 (MonoDebugVarInfo, header->num_locals);
668 for (i = 0; i < minfo->method_info.num_locals; i++) {
669 minfo->method_info.locals [i].offset = ptr [i].offset;
670 minfo->method_info.locals [i].begin_scope = minfo->method_info.prologue_end;
671 minfo->method_info.locals [i].end_scope = minfo->method_info.epilogue_begin;
675 debug_generate_method_lines (info, minfo, cfg);
677 g_hash_table_insert (debug->methods, method, minfo);
681 il_offset_from_address (DebugMethodInfo *minfo, guint32 address)
685 for (i = 0; i < minfo->method_info.num_il_offsets; i++) {
686 MonoDebugILOffsetInfo *ilo = &minfo->method_info.il_offsets [i];
688 if (ilo->address > address)
696 mono_debug_source_location_from_address (MonoMethod *method, guint32 address)
698 MonoDebugHandle *debug;
699 DebugMethodInfo *minfo = NULL;
702 for (debug = mono_debug_handles; debug; debug = debug->next) {
703 minfo = g_hash_table_lookup (debug->methods, method);
712 if (minfo->info->symfile) {
713 guint32 offset = il_offset_from_address (minfo, address);
718 return mono_debug_find_source_location (minfo->info->symfile, method, offset);
721 if (!minfo->line_numbers)
724 for (i = 0; i < minfo->line_numbers->len; i++) {
725 DebugLineNumberInfo *lni = g_ptr_array_index (minfo->line_numbers, i);
727 if ((gchar *)lni->address > minfo->method_info.code_start + address) {
728 gchar *source_file = g_ptr_array_index (debug->source_files, lni->source_file);
730 return g_strdup_printf ("%s:%d", source_file, lni->line);
738 mono_debug_il_offset_from_address (MonoMethod *method, guint32 address)
740 MonoDebugHandle *debug;
741 MonoDebugMethodInfo *minfo = NULL;
744 for (debug = mono_debug_handles; debug; debug = debug->next) {
745 minfo = g_hash_table_lookup (debug->methods, method);
751 if (!minfo || !minfo->il_offsets)
754 for (i = 0; i < minfo->num_il_offsets; i++) {
755 MonoDebugILOffsetInfo *ilo = &minfo->il_offsets [i];
757 if (ilo->address > address)