5 #include <mono/metadata/metadata.h>
6 #include <mono/metadata/tabledefs.h>
7 #include <mono/metadata/rawbuffer.h>
8 #include <mono/metadata/tokentype.h>
9 #include <mono/metadata/appdomain.h>
10 #include <mono/metadata/exception.h>
11 #include <mono/metadata/debug-helpers.h>
12 #include <mono/metadata/debug-mono-symfile.h>
17 #define RANGE_TABLE_CHUNK_SIZE 256
19 struct MonoSymbolFilePriv
25 guint32 string_table_size;
26 guint32 string_offset_size;
28 GHashTable *method_table;
29 GHashTable *method_hash;
30 MonoSymbolFileOffsetTable *offset_table;
36 MonoDebugMethodInfo *minfo;
37 MonoSymbolFileMethodEntry *entry;
38 guint32 method_name_offset;
41 } MonoSymbolFileMethodEntryPriv;
43 static int write_string_table (MonoSymbolFile *symfile);
44 static int create_symfile (MonoSymbolFile *symfile, gboolean emit_warnings);
45 static void close_symfile (MonoSymbolFile *symfile);
46 static MonoDebugRangeInfo *allocate_range_entry (MonoSymbolFile *symfile);
49 free_method_info (MonoDebugMethodInfo *minfo)
56 load_symfile (MonoSymbolFile *symfile)
58 MonoSymbolFilePriv *priv = symfile->_priv;
59 MonoSymbolFileMethodEntry *me;
60 const char *ptr, *start;
65 ptr = start = symfile->raw_contents;
67 magic = *((guint64 *) ptr)++;
68 if (magic != MONO_SYMBOL_FILE_MAGIC) {
69 g_warning ("Symbol file %s has is not a mono symbol file", priv->file_name);
73 version = *((guint32 *) ptr)++;
74 if (version != MONO_SYMBOL_FILE_VERSION) {
75 g_warning ("Symbol file %s has incorrect line number table version "
76 "(expected %d, got %ld)", priv->file_name,
77 MONO_SYMBOL_FILE_VERSION, version);
81 priv->offset_table = (MonoSymbolFileOffsetTable *) ptr;
88 priv->method_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
89 (GDestroyNotify) g_free);
90 priv->method_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
91 (GDestroyNotify) free_method_info);
93 ptr = symfile->raw_contents + priv->offset_table->method_table_offset;
94 me = (MonoSymbolFileMethodEntry *) ptr;
96 for (i = 0; i < priv->offset_table->method_count; i++, me++) {
97 MonoMethod *method = mono_get_method (priv->image, me->token, NULL);
98 MonoSymbolFileMethodEntryPriv *mep;
99 MonoDebugMethodInfo *minfo;
104 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
105 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
106 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
107 g_assert_not_reached ();
109 if (!((MonoMethodNormal *) method)->header) {
110 g_warning (G_STRLOC ": Internal error: method %s.%s doesn't have a header",
111 method->klass->name, method->name);
115 minfo = g_new0 (MonoDebugMethodInfo, 1);
116 minfo->file_offset = ((const char *) me) - start;
117 minfo->method = method;
118 minfo->symfile = symfile;
119 minfo->num_il_offsets = me->num_line_numbers;
120 minfo->il_offsets = (MonoSymbolFileLineNumberEntry *)
121 (symfile->raw_contents + me->line_number_table_offset);
123 mep = g_new0 (MonoSymbolFileMethodEntryPriv, 1);
124 mep->method = method;
129 mep->method_name_offset = priv->string_table_size;
130 mep->name = g_strdup_printf ("%s%s.%s", method->klass->name_space,
131 method->klass->name, method->name);
132 priv->string_table_size += strlen (mep->name) + 1;
134 g_hash_table_insert (priv->method_table, method, mep);
135 g_hash_table_insert (priv->method_hash, method, minfo);
138 if (!write_string_table (symfile))
145 mono_debug_open_mono_symbol_file (MonoImage *image, const char *filename, gboolean emit_warnings)
147 MonoSymbolFile *symfile;
148 MonoSymbolFilePriv *priv;
153 fd = open (filename, O_RDONLY);
156 g_warning ("Can't open symbol file: %s", filename);
160 file_size = lseek (fd, 0, SEEK_END);
161 lseek (fd, 0, SEEK_SET);
163 if (file_size == (off_t) -1) {
165 g_warning ("Can't get size of symbol file: %s", filename);
169 ptr = mono_raw_buffer_load (fd, FALSE, 0, file_size);
172 g_warning ("Can't read symbol file: %s", filename);
176 symfile = g_new0 (MonoSymbolFile, 1);
177 symfile->magic = MONO_SYMBOL_FILE_MAGIC;
178 symfile->version = MONO_SYMBOL_FILE_VERSION;
179 symfile->dynamic_magic = MONO_SYMBOL_FILE_DYNAMIC_MAGIC;
180 symfile->dynamic_version = MONO_SYMBOL_FILE_DYNAMIC_VERSION;
181 symfile->image_file = g_strdup (image->name);
182 symfile->symbol_file = g_strdup (filename);
183 symfile->raw_contents = ptr;
184 symfile->raw_contents_size = file_size;
186 symfile->_priv = priv = g_new0 (MonoSymbolFilePriv, 1);
190 priv->file_name = g_strdup (filename);
192 if (!load_symfile (symfile)) {
193 mono_debug_close_mono_symbol_file (symfile);
201 close_symfile (MonoSymbolFile *symfile)
203 MonoSymbolFilePriv *priv = symfile->_priv;
205 if (symfile->raw_contents) {
206 mono_raw_buffer_free (symfile->raw_contents);
207 symfile->raw_contents = NULL;
215 if (priv->method_table) {
216 g_hash_table_destroy (priv->method_table);
217 priv->method_table = NULL;
220 if (priv->method_hash) {
221 g_hash_table_destroy (priv->method_hash);
222 priv->method_hash = NULL;
225 if (symfile->is_dynamic)
226 unlink (priv->file_name);
228 if (symfile->image_file) {
229 g_free (symfile->image_file);
230 symfile->image_file = NULL;
233 if (priv->file_name) {
234 g_free (priv->file_name);
235 priv->file_name = NULL;
242 mono_debug_close_mono_symbol_file (MonoSymbolFile *symfile)
247 close_symfile (symfile);
249 g_free (symfile->_priv->source_file);
250 g_free (symfile->_priv);
251 g_free (symfile->image_file);
252 g_free (symfile->symbol_file);
257 write_string (int fd, const char *string)
259 guint32 length = strlen (string);
261 if (write (fd, &length, sizeof (length)) < 0)
264 if (write (fd, string, strlen (string)) < 0)
271 read_string (const char *ptr)
273 int len = *((guint32 *) ptr)++;
275 return g_filename_from_utf8 (ptr, len, NULL, NULL, NULL);
279 mono_debug_find_source_location (MonoSymbolFile *symfile, MonoMethod *method, guint32 offset,
280 guint32 *line_number)
282 MonoSymbolFilePriv *priv = symfile->_priv;
283 MonoSymbolFileLineNumberEntry *lne;
284 MonoSymbolFileMethodEntryPriv *mep;
285 gchar *source_file = NULL;
289 if (!priv->method_table || symfile->is_dynamic)
292 mep = g_hash_table_lookup (priv->method_table, method);
296 if (mep->entry->source_file_offset)
297 source_file = read_string (symfile->raw_contents + mep->entry->source_file_offset);
299 ptr = symfile->raw_contents + mep->entry->line_number_table_offset;
301 lne = (MonoSymbolFileLineNumberEntry *) ptr;
303 for (i = 0; i < mep->entry->num_line_numbers; i++, lne++) {
304 if (lne->offset < offset)
308 *line_number = lne->row;
313 } else if (source_file) {
314 gchar *retval = g_strdup_printf ("%s:%d", source_file, lne->row);
315 g_free (source_file);
318 return g_strdup_printf ("%d", lne->row);
325 mono_debug_symfile_add_method (MonoSymbolFile *symfile, MonoMethod *method)
327 MonoSymbolFileMethodEntryPriv *mep;
328 MonoSymbolFileMethodAddress *address;
329 MonoDebugVarInfo *var_table;
330 MonoSymbolFileLineNumberEntry *lne;
331 MonoDebugRangeInfo *range;
332 guint32 size, line_size, line_offset, num_variables, variable_size, variable_offset;
333 guint32 *line_addresses;
337 mep = g_hash_table_lookup (symfile->_priv->method_table, method);
342 mep->minfo = g_hash_table_lookup (symfile->_priv->method_hash, mep->method);
347 if (!mep->minfo->jit)
350 symfile->generation++;
352 size = sizeof (MonoSymbolFileMethodAddress);
354 line_size = mep->entry->num_line_numbers * sizeof (MonoSymbolFileLineNumberEntry);
358 num_variables = mep->entry->num_parameters + mep->entry->num_locals;
359 if (mep->entry->this_type_index)
362 variable_size = num_variables * sizeof (MonoDebugVarInfo);
363 variable_offset = size;
364 size += variable_size;
366 address = g_malloc0 (size);
367 ptr = (guint8 *) address;
369 address->size = size;
370 address->start_address = GPOINTER_TO_UINT (mep->minfo->jit->code_start);
371 address->end_address = GPOINTER_TO_UINT (mep->minfo->jit->code_start + mep->minfo->jit->code_size);
372 address->line_table_offset = line_offset;
373 address->variable_table_offset = variable_offset;
375 range = allocate_range_entry (symfile);
376 range->file_offset = mep->minfo->file_offset;
377 range->start_address = address->start_address;
378 range->end_address = address->end_address;
379 range->dynamic_data = address;
380 range->dynamic_size = size;
382 var_table = (MonoDebugVarInfo *) (ptr + variable_offset);
384 if (mep->entry->this_type_index) {
385 if (!mep->minfo->jit->this_var) {
386 g_warning (G_STRLOC ": Method %s.%s doesn't have `this'.",
387 mep->method->klass->name, mep->method->name);
390 *var_table++ = *mep->minfo->jit->this_var;
393 if (mep->minfo->jit->num_params != mep->entry->num_parameters) {
394 g_warning (G_STRLOC ": Method %s.%s has %d parameters, but symbol file claims it has %d.",
395 mep->method->klass->name, mep->method->name, mep->minfo->jit->num_params,
396 mep->entry->num_parameters);
397 var_table += mep->entry->num_parameters;
399 for (i = 0; i < mep->minfo->jit->num_params; i++)
400 *var_table++ = mep->minfo->jit->params [i];
402 if (mep->minfo->jit->num_locals != mep->entry->num_locals) {
403 g_warning (G_STRLOC ": Method %s.%s has %d locals, but symbol file claims it has %d.",
404 mep->method->klass->name, mep->method->name, mep->minfo->jit->num_locals,
405 mep->entry->num_locals);
406 var_table += mep->entry->num_locals;
408 for (i = 0; i < mep->minfo->jit->num_locals; i++)
409 *var_table++ = mep->minfo->jit->locals [i];
411 lne = (MonoSymbolFileLineNumberEntry *)
412 (symfile->raw_contents + mep->entry->line_number_table_offset);
414 line_addresses = (guint32 *) (ptr + line_offset);
416 for (i = 0; i < mep->entry->num_line_numbers; i++, lne++) {
420 line_addresses [i] = 0;
422 } else if (lne->offset == 0) {
423 line_addresses [i] = mep->minfo->jit->prologue_end;
427 line_addresses [i] = mep->minfo->jit->code_size;
429 for (j = 0; j < mep->minfo->num_il_offsets; j++) {
430 MonoSymbolFileLineNumberEntry *il = &mep->minfo->il_offsets [j];
432 if (il->offset >= lne->offset) {
433 line_addresses [i] = mep->minfo->jit->il_addresses [j];
441 free_method_entry (MonoSymbolFileMethodEntryPriv *mep)
449 create_method (MonoSymbolFile *symfile, guint32 token, MonoMethod *method)
451 MonoSymbolFileMethodEntryPriv *mep;
452 MonoDebugMethodInfo *minfo;
454 g_assert (method->klass->image == symfile->_priv->image);
456 mep = g_new0 (MonoSymbolFileMethodEntryPriv, 1);
457 mep->entry = g_new0 (MonoSymbolFileMethodEntry, 1);
458 mep->entry->token = token;
459 mep->entry->source_file_offset = symfile->_priv->offset_table->source_table_offset;
461 mep->method_name_offset = symfile->_priv->string_table_size;
462 mep->name = g_strdup_printf ("%s%s.%s", method->klass->name_space,
463 method->klass->name, method->name);
464 symfile->_priv->string_table_size += strlen (mep->name) + 1;
466 minfo = g_new0 (MonoDebugMethodInfo, 1);
467 minfo->method = method;
468 minfo->symfile = symfile;
471 mep->method = method;
473 symfile->_priv->offset_table->method_count++;
475 g_hash_table_insert (symfile->_priv->method_table, method, mep);
476 g_hash_table_insert (symfile->_priv->method_hash, method, minfo);
480 write_method (gpointer key, gpointer value, gpointer user_data)
482 MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
483 MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
485 if (symfile->_priv->error)
488 mep->minfo->file_offset = lseek (symfile->_priv->fd, 0, SEEK_CUR);
490 if (write (symfile->_priv->fd, mep->entry, sizeof (MonoSymbolFileMethodEntry)) < 0) {
491 symfile->_priv->error = TRUE;
497 write_line_numbers (gpointer key, gpointer value, gpointer user_data)
499 MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
500 MonoSymbolFilePriv *priv = symfile->_priv;
501 MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
502 MonoSymbolFileLineNumberEntry lne;
503 const unsigned char *ip, *start, *end;
504 MonoMethodHeader *header;
509 if ((mep->method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
510 (mep->method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
511 (mep->method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
512 g_assert_not_reached ();
514 header = ((MonoMethodNormal *) mep->method)->header;
517 mep->entry->line_number_table_offset = lseek (priv->fd, 0, SEEK_CUR);
518 ++mep->entry->num_line_numbers;
523 if (write (priv->fd, &lne, sizeof (MonoSymbolFileLineNumberEntry)) < 0) {
528 ip = start = header->code;
529 end = ip + header->code_size;
534 ++mep->entry->num_line_numbers;
535 lne.offset = ip - start;
538 if (write (priv->fd, &lne, sizeof (MonoSymbolFileLineNumberEntry)) < 0) {
543 line = mono_disasm_code_one (NULL, mep->method, ip, &ip);
549 create_methods (MonoSymbolFile *symfile)
551 MonoImage *image = symfile->_priv->image;
552 MonoTableInfo *table = &image->tables [MONO_TABLE_METHOD];
555 for (idx = 1; idx <= table->rows; idx++) {
556 guint32 token = mono_metadata_make_token (MONO_TABLE_METHOD, idx);
557 MonoMethod *method = mono_get_method (image, token, NULL);
559 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
560 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
561 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
564 if (method->wrapper_type != MONO_WRAPPER_NONE)
567 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
568 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
569 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
570 g_assert_not_reached ();
572 if (!((MonoMethodNormal *) method)->header) {
573 g_warning (G_STRLOC ": Internal error: method %s.%s doesn't have a header",
574 method->klass->name, method->name);
578 create_method (symfile, token, method);
583 load_line_numbers (gpointer key, gpointer value, gpointer user_data)
585 MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
586 MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
588 mep->minfo->num_il_offsets = mep->entry->num_line_numbers;
589 mep->minfo->il_offsets = (MonoSymbolFileLineNumberEntry *)
590 (symfile->raw_contents + mep->entry->line_number_table_offset);
594 create_symfile (MonoSymbolFile *symfile, gboolean emit_warnings)
596 MonoSymbolFilePriv *priv = symfile->_priv;
602 priv->fd = g_file_open_tmp (NULL, &priv->file_name, NULL);
603 if (priv->fd == -1) {
605 g_warning ("Can't create symbol file");
609 symfile->symbol_file = g_strdup (priv->file_name);
611 magic = MONO_SYMBOL_FILE_MAGIC;
612 if (write (priv->fd, &magic, sizeof (magic)) < 0)
615 version = MONO_SYMBOL_FILE_VERSION;
616 if (write (priv->fd, &version, sizeof (version)) < 0)
619 offset = lseek (priv->fd, 0, SEEK_CUR);
621 priv->offset_table = g_new0 (MonoSymbolFileOffsetTable, 1);
622 if (write (priv->fd, priv->offset_table, sizeof (MonoSymbolFileOffsetTable)) < 0)
626 // Write source file table.
628 if (priv->source_file) {
629 priv->offset_table->source_table_offset = lseek (priv->fd, 0, SEEK_CUR);
630 if (!write_string (priv->fd, priv->source_file))
632 priv->offset_table->source_table_size = lseek (priv->fd, 0, SEEK_CUR) -
633 priv->offset_table->source_table_offset;
637 // Create method table.
640 priv->method_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
641 (GDestroyNotify) free_method_entry);
642 priv->method_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
643 (GDestroyNotify) free_method_info);
645 create_methods (symfile);
648 // Write line numbers.
651 priv->offset_table->line_number_table_offset = lseek (priv->fd, 0, SEEK_CUR);
653 g_hash_table_foreach (priv->method_table, write_line_numbers, symfile);
657 priv->offset_table->line_number_table_size = lseek (priv->fd, 0, SEEK_CUR) -
658 priv->offset_table->line_number_table_offset;
661 // Write method table.
664 priv->offset_table->method_table_offset = lseek (priv->fd, 0, SEEK_CUR);
666 g_hash_table_foreach (priv->method_table, write_method, symfile);
670 priv->offset_table->method_table_size = lseek (priv->fd, 0, SEEK_CUR) -
671 priv->offset_table->method_table_offset;
674 // Write offset table.
677 symfile->raw_contents_size = lseek (priv->fd, 0, SEEK_CUR);
679 lseek (priv->fd, offset, SEEK_SET);
680 if (write (priv->fd, priv->offset_table, sizeof (MonoSymbolFileOffsetTable)) < 0)
683 lseek (priv->fd, symfile->raw_contents_size, SEEK_SET);
685 ptr = mono_raw_buffer_load (priv->fd, TRUE, 0, symfile->raw_contents_size);
689 symfile->raw_contents = ptr;
692 // Load line number table.
694 g_hash_table_foreach (priv->method_table, load_line_numbers, symfile);
698 if (!write_string_table (symfile))
705 mono_debug_create_mono_symbol_file (MonoImage *image)
707 MonoSymbolFile *symfile;
709 symfile = g_new0 (MonoSymbolFile, 1);
710 symfile->magic = MONO_SYMBOL_FILE_MAGIC;
711 symfile->version = MONO_SYMBOL_FILE_VERSION;
712 symfile->dynamic_magic = MONO_SYMBOL_FILE_DYNAMIC_MAGIC;
713 symfile->dynamic_version = MONO_SYMBOL_FILE_DYNAMIC_VERSION;
714 symfile->is_dynamic = TRUE;
715 symfile->image_file = g_strdup (image->name);
717 symfile->_priv = g_new0 (MonoSymbolFilePriv, 1);
718 symfile->_priv->image = image;
720 if (!create_symfile (symfile, TRUE)) {
721 mono_debug_close_mono_symbol_file (symfile);
728 MonoDebugMethodInfo *
729 mono_debug_find_method (MonoSymbolFile *symfile, MonoMethod *method)
731 return g_hash_table_lookup (symfile->_priv->method_hash, method);
735 write_method_name (gpointer key, gpointer value, gpointer user_data)
737 MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
738 MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
739 MonoSymbolFilePriv *priv = symfile->_priv;
740 guint8 *offset_ptr, *string_ptr;
743 offset = mep->method_name_offset + priv->string_offset_size;
745 offset_ptr = symfile->string_table + mep->index * 4;
746 string_ptr = symfile->string_table + offset;
748 *((guint32 *) offset_ptr) = offset;
749 strcpy (string_ptr, mep->name);
753 write_string_table (MonoSymbolFile *symfile)
755 MonoSymbolFilePriv *priv = symfile->_priv;
757 priv->string_offset_size = priv->offset_table->method_count * 4;
759 symfile->string_table_size = priv->string_table_size + priv->string_offset_size;
760 symfile->string_table = g_malloc0 (symfile->string_table_size);
762 g_hash_table_foreach (symfile->_priv->method_table, write_method_name, symfile);
766 MonoReflectionMethod *
767 ves_icall_MonoDebugger_GetMethod (MonoReflectionAssembly *assembly, guint32 token)
771 method = mono_get_method (assembly->assembly->image, token, NULL);
773 return mono_method_get_object (mono_domain_get (), method, NULL);
777 ves_icall_MonoDebugger_GetLocalTypeFromSignature (MonoReflectionAssembly *assembly, MonoArray *signature)
785 MONO_CHECK_ARG_NULL (assembly);
786 MONO_CHECK_ARG_NULL (signature);
788 domain = mono_domain_get();
789 image = assembly->assembly->image;
791 ptr = mono_array_addr (signature, char, 0);
792 g_assert (*ptr++ == 0x07);
793 len = mono_metadata_decode_value (ptr, &ptr);
796 type = mono_metadata_parse_type (image, MONO_PARSE_LOCAL, 0, ptr, &ptr);
798 return mono_type_get_object (domain, type);
801 static MonoDebugRangeInfo *
802 allocate_range_entry (MonoSymbolFile *symfile)
804 MonoDebugRangeInfo *retval;
805 guint32 size, chunks;
807 symfile->range_entry_size = sizeof (MonoDebugRangeInfo);
809 if (!symfile->range_table) {
810 size = sizeof (MonoDebugRangeInfo) * RANGE_TABLE_CHUNK_SIZE;
811 symfile->range_table = g_malloc0 (size);
812 symfile->num_range_entries = 1;
813 return symfile->range_table;
816 if (!((symfile->num_range_entries + 1) % RANGE_TABLE_CHUNK_SIZE)) {
817 chunks = (symfile->num_range_entries + 1) / RANGE_TABLE_CHUNK_SIZE;
818 size = sizeof (MonoDebugRangeInfo) * RANGE_TABLE_CHUNK_SIZE * (chunks + 1);
820 symfile->range_table = g_realloc (symfile->range_table, size);
823 retval = symfile->range_table + symfile->num_range_entries;
824 symfile->num_range_entries++;