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 struct MonoSymbolFilePriv
23 guint32 string_table_size;
24 guint32 string_offset_size;
26 GHashTable *method_table;
27 GHashTable *method_hash;
28 MonoSymbolFileOffsetTable *offset_table;
29 GPtrArray *range_table;
35 MonoDebugMethodInfo *minfo;
36 MonoSymbolFileMethodEntry *entry;
37 guint32 method_name_offset;
40 } MonoSymbolFileMethodEntryPriv;
42 static int write_string_table (MonoSymbolFile *symfile);
43 static int create_symfile (MonoSymbolFile *symfile, gboolean emit_warnings);
44 static void close_symfile (MonoSymbolFile *symfile);
47 free_method_info (MonoDebugMethodInfo *minfo)
54 load_symfile (MonoSymbolFile *symfile)
56 MonoSymbolFilePriv *priv = symfile->_priv;
57 MonoSymbolFileMethodEntry *me;
58 const char *ptr, *start;
63 ptr = start = symfile->raw_contents;
65 magic = *((guint64 *) ptr)++;
66 if (magic != MONO_SYMBOL_FILE_MAGIC) {
67 g_warning ("Symbol file %s has is not a mono symbol file", priv->file_name);
71 version = *((guint32 *) ptr)++;
72 if (version != MONO_SYMBOL_FILE_VERSION) {
73 g_warning ("Symbol file %s has incorrect line number table version "
74 "(expected %d, got %ld)", priv->file_name,
75 MONO_SYMBOL_FILE_VERSION, version);
79 priv->offset_table = (MonoSymbolFileOffsetTable *) ptr;
80 symfile->address_table_size = priv->offset_table->address_table_size;
87 priv->method_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
88 (GDestroyNotify) g_free);
89 priv->method_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
90 (GDestroyNotify) free_method_info);
92 ptr = symfile->raw_contents + priv->offset_table->method_table_offset;
93 me = (MonoSymbolFileMethodEntry *) ptr;
95 for (i = 0; i < priv->offset_table->method_count; i++, me++) {
96 MonoMethod *method = mono_get_method (priv->image, me->token, NULL);
97 MonoSymbolFileMethodEntryPriv *mep;
98 MonoDebugMethodInfo *minfo;
103 minfo = g_new0 (MonoDebugMethodInfo, 1);
104 minfo->file_offset = ((const char *) me) - start;
105 minfo->method = method;
106 minfo->symfile = symfile;
107 minfo->num_il_offsets = me->num_line_numbers;
108 minfo->il_offsets = (MonoSymbolFileLineNumberEntry *)
109 (symfile->raw_contents + me->line_number_table_offset);
111 mep = g_new0 (MonoSymbolFileMethodEntryPriv, 1);
112 mep->method = method;
117 mep->method_name_offset = priv->string_table_size;
118 mep->name = g_strdup_printf ("%s%s.%s", method->klass->name_space,
119 method->klass->name, method->name);
120 priv->string_table_size += strlen (mep->name) + 1;
122 g_hash_table_insert (priv->method_table, method, mep);
123 g_hash_table_insert (priv->method_hash, method, minfo);
126 if (!write_string_table (symfile))
133 mono_debug_open_mono_symbol_file (MonoImage *image, const char *filename, gboolean emit_warnings)
135 MonoSymbolFile *symfile;
136 MonoSymbolFilePriv *priv;
141 fd = open (filename, O_RDONLY);
144 g_warning ("Can't open symbol file: %s", filename);
148 file_size = lseek (fd, 0, SEEK_END);
149 lseek (fd, 0, SEEK_SET);
151 if (file_size == (off_t) -1) {
153 g_warning ("Can't get size of symbol file: %s", filename);
157 ptr = mono_raw_buffer_load (fd, FALSE, 0, file_size);
160 g_warning ("Can't read symbol file: %s", filename);
164 symfile = g_new0 (MonoSymbolFile, 1);
165 symfile->magic = MONO_SYMBOL_FILE_MAGIC;
166 symfile->version = MONO_SYMBOL_FILE_VERSION;
167 symfile->image_file = g_strdup (image->name);
168 symfile->raw_contents = ptr;
169 symfile->raw_contents_size = file_size;
171 symfile->_priv = priv = g_new0 (MonoSymbolFilePriv, 1);
175 priv->file_name = g_strdup (filename);
177 if (!load_symfile (symfile)) {
178 mono_debug_close_mono_symbol_file (symfile);
186 close_symfile (MonoSymbolFile *symfile)
188 MonoSymbolFilePriv *priv = symfile->_priv;
190 if (symfile->raw_contents) {
191 mono_raw_buffer_free (symfile->raw_contents);
192 symfile->raw_contents = NULL;
200 if (priv->method_table) {
201 g_hash_table_destroy (priv->method_table);
202 priv->method_table = NULL;
205 if (priv->method_hash) {
206 g_hash_table_destroy (priv->method_hash);
207 priv->method_hash = NULL;
210 if (symfile->is_dynamic)
211 unlink (priv->file_name);
213 if (symfile->image_file) {
214 g_free (symfile->image_file);
215 symfile->image_file = NULL;
218 if (priv->file_name) {
219 g_free (priv->file_name);
220 priv->file_name = NULL;
227 mono_debug_close_mono_symbol_file (MonoSymbolFile *symfile)
232 close_symfile (symfile);
234 g_free (symfile->_priv->source_file);
235 g_free (symfile->_priv);
240 write_string (int fd, const char *string)
242 guint32 length = strlen (string);
244 if (write (fd, &length, sizeof (length)) < 0)
247 if (write (fd, string, strlen (string)) < 0)
254 read_string (const char *ptr)
256 int len = *((guint32 *) ptr)++;
259 return g_filename_from_utf8 (ptr, len, NULL, NULL, NULL);
263 mono_debug_find_source_location (MonoSymbolFile *symfile, MonoMethod *method, guint32 offset,
264 guint32 *line_number)
266 MonoSymbolFilePriv *priv = symfile->_priv;
267 MonoSymbolFileLineNumberEntry *lne;
268 MonoSymbolFileMethodEntryPriv *mep;
269 gchar *source_file = NULL;
273 if (!priv->method_table || symfile->is_dynamic)
276 mep = g_hash_table_lookup (priv->method_table, method);
280 if (mep->entry->source_file_offset)
281 source_file = read_string (symfile->raw_contents + mep->entry->source_file_offset);
283 ptr = symfile->raw_contents + mep->entry->line_number_table_offset;
285 lne = (MonoSymbolFileLineNumberEntry *) ptr;
287 for (i = 0; i < mep->entry->num_line_numbers; i++, lne++) {
288 if (lne->offset < offset)
292 *line_number = lne->row;
297 } else if (source_file) {
298 gchar *retval = g_strdup_printf ("%s:%d", source_file, lne->row);
299 g_free (source_file);
302 return g_strdup_printf ("%d", lne->row);
309 update_method_func (gpointer key, gpointer value, gpointer user_data)
311 MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
312 MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
313 MonoSymbolFileMethodAddress *address;
314 MonoSymbolFileLineNumberEntry *lne;
315 MonoDebugRangeInfo *range;
319 mep->minfo = g_hash_table_lookup (symfile->_priv->method_hash, mep->method);
324 if (!mep->minfo->jit)
327 address = (MonoSymbolFileMethodAddress *)
328 (symfile->address_table + mep->entry->address_table_offset);
330 address->is_valid = TRUE;
331 address->start_address = GPOINTER_TO_UINT (mep->minfo->jit->code_start);
332 address->end_address = GPOINTER_TO_UINT (mep->minfo->jit->code_start + mep->minfo->jit->code_size);
334 range = g_new0 (MonoDebugRangeInfo, 1);
335 range->start_address = address->start_address;
336 range->end_address = address->end_address;
337 range->file_offset = mep->minfo->file_offset;
339 g_ptr_array_add (symfile->_priv->range_table, range);
341 lne = (MonoSymbolFileLineNumberEntry *)
342 (symfile->raw_contents + mep->entry->line_number_table_offset);
344 for (i = 0; i < mep->entry->num_line_numbers; i++, lne++) {
348 address->line_addresses [i] = 0;
350 } else if (lne->offset == 0) {
351 address->line_addresses [i] = mep->minfo->jit->prologue_end;
355 address->line_addresses [i] = mep->minfo->jit->code_size;
357 for (j = 0; j < mep->minfo->num_il_offsets; j++) {
358 MonoSymbolFileLineNumberEntry *il = &mep->minfo->il_offsets [j];
360 if (il->offset >= lne->offset) {
361 address->line_addresses [i] = mep->minfo->jit->il_addresses [j];
369 range_table_compare_func (gconstpointer a, gconstpointer b)
371 const MonoDebugRangeInfo *r1 = (const MonoDebugRangeInfo *) a;
372 const MonoDebugRangeInfo *r2 = (const MonoDebugRangeInfo *) b;
374 if (r1->start_address < r2->start_address)
376 else if (r1->start_address > r2->start_address)
383 mono_debug_update_mono_symbol_file (MonoSymbolFile *symfile)
387 if (!symfile->_priv->method_table)
390 symfile->_priv->range_table = g_ptr_array_new ();
392 if (!symfile->address_table)
393 symfile->address_table = g_malloc0 (symfile->address_table_size);
395 g_hash_table_foreach (symfile->_priv->method_table, update_method_func, symfile);
397 symfile->range_table_size = symfile->_priv->range_table->len * sizeof (MonoDebugRangeInfo);
398 symfile->range_table = g_malloc0 (symfile->range_table_size);
400 g_ptr_array_sort (symfile->_priv->range_table, range_table_compare_func);
402 for (i = 0; i < symfile->_priv->range_table->len; i++) {
403 MonoDebugRangeInfo *range = g_ptr_array_index (symfile->_priv->range_table, i);
405 symfile->range_table [i] = *range;
408 g_ptr_array_free (symfile->_priv->range_table, TRUE);
409 symfile->_priv->range_table = NULL;
413 free_method_entry (MonoSymbolFileMethodEntryPriv *mep)
421 create_method (MonoSymbolFile *symfile, guint32 token, MonoMethod *method)
423 MonoSymbolFileMethodEntryPriv *mep;
424 MonoDebugMethodInfo *minfo;
426 g_assert (method->klass->image == symfile->_priv->image);
428 mep = g_new0 (MonoSymbolFileMethodEntryPriv, 1);
429 mep->entry = g_new0 (MonoSymbolFileMethodEntry, 1);
430 mep->entry->token = token;
431 mep->entry->source_file_offset = symfile->_priv->offset_table->source_table_offset;
433 mep->method_name_offset = symfile->_priv->string_table_size;
434 mep->name = g_strdup_printf ("%s%s.%s", method->klass->name_space,
435 method->klass->name, method->name);
436 symfile->_priv->string_table_size += strlen (mep->name) + 1;
438 minfo = g_new0 (MonoDebugMethodInfo, 1);
439 minfo->method = method;
440 minfo->symfile = symfile;
443 mep->method = method;
445 symfile->_priv->offset_table->method_count++;
447 g_hash_table_insert (symfile->_priv->method_table, method, mep);
448 g_hash_table_insert (symfile->_priv->method_hash, method, minfo);
452 write_method (gpointer key, gpointer value, gpointer user_data)
454 MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
455 MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
457 if (symfile->_priv->error)
460 mep->minfo->file_offset = lseek (symfile->_priv->fd, 0, SEEK_CUR);
462 if (write (symfile->_priv->fd, mep->entry, sizeof (MonoSymbolFileMethodEntry)) < 0) {
463 symfile->_priv->error = TRUE;
469 write_line_numbers (gpointer key, gpointer value, gpointer user_data)
471 MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
472 MonoSymbolFilePriv *priv = symfile->_priv;
473 MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
474 MonoSymbolFileLineNumberEntry lne;
475 const unsigned char *ip, *start, *end;
476 MonoMethodHeader *header;
481 if ((mep->method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
482 (mep->method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
483 (mep->method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
484 g_assert_not_reached ();
486 header = ((MonoMethodNormal *) mep->method)->header;
488 mep->entry->line_number_table_offset = lseek (priv->fd, 0, SEEK_CUR);
489 ++mep->entry->num_line_numbers;
494 if (write (priv->fd, &lne, sizeof (MonoSymbolFileLineNumberEntry)) < 0) {
499 ip = start = header->code;
500 end = ip + header->code_size;
505 ++mep->entry->num_line_numbers;
506 lne.offset = ip - start;
509 if (write (priv->fd, &lne, sizeof (MonoSymbolFileLineNumberEntry)) < 0) {
514 line = mono_disasm_code_one (NULL, mep->method, ip, &ip);
518 mep->entry->address_table_offset = symfile->address_table_size;
519 mep->entry->address_table_size = sizeof (MonoSymbolFileMethodAddress) +
520 mep->entry->num_line_numbers * sizeof (guint32);
522 symfile->address_table_size += mep->entry->address_table_size;
526 create_methods (MonoSymbolFile *symfile)
528 MonoImage *image = symfile->_priv->image;
529 MonoTableInfo *table = &image->tables [MONO_TABLE_METHOD];
532 for (idx = 1; idx <= table->rows; idx++) {
533 guint32 token = mono_metadata_make_token (MONO_TABLE_METHOD, idx);
534 MonoMethod *method = mono_get_method (image, token, NULL);
536 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
537 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
538 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
541 if (method->wrapper_type != MONO_WRAPPER_NONE)
544 create_method (symfile, token, method);
549 load_line_numbers (gpointer key, gpointer value, gpointer user_data)
551 MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
552 MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
554 mep->minfo->num_il_offsets = mep->entry->num_line_numbers;
555 mep->minfo->il_offsets = (MonoSymbolFileLineNumberEntry *)
556 (symfile->raw_contents + mep->entry->line_number_table_offset);
560 create_symfile (MonoSymbolFile *symfile, gboolean emit_warnings)
562 MonoSymbolFilePriv *priv = symfile->_priv;
568 priv->fd = g_file_open_tmp (NULL, &priv->file_name, NULL);
569 if (priv->fd == -1) {
571 g_warning ("Can't create symbol file");
575 magic = MONO_SYMBOL_FILE_MAGIC;
576 if (write (priv->fd, &magic, sizeof (magic)) < 0)
579 version = MONO_SYMBOL_FILE_VERSION;
580 if (write (priv->fd, &version, sizeof (version)) < 0)
583 offset = lseek (priv->fd, 0, SEEK_CUR);
585 priv->offset_table = g_new0 (MonoSymbolFileOffsetTable, 1);
586 if (write (priv->fd, priv->offset_table, sizeof (MonoSymbolFileOffsetTable)) < 0)
590 // Write source file table.
592 if (priv->source_file) {
593 priv->offset_table->source_table_offset = lseek (priv->fd, 0, SEEK_CUR);
594 if (!write_string (priv->fd, priv->source_file))
596 priv->offset_table->source_table_size = lseek (priv->fd, 0, SEEK_CUR) -
597 priv->offset_table->source_table_offset;
601 // Create method table.
604 priv->method_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
605 (GDestroyNotify) free_method_entry);
606 priv->method_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
607 (GDestroyNotify) free_method_info);
609 create_methods (symfile);
612 // Write line numbers.
615 priv->offset_table->line_number_table_offset = lseek (priv->fd, 0, SEEK_CUR);
617 g_hash_table_foreach (priv->method_table, write_line_numbers, symfile);
621 priv->offset_table->line_number_table_size = lseek (priv->fd, 0, SEEK_CUR) -
622 priv->offset_table->line_number_table_offset;
625 // Write method table.
628 priv->offset_table->method_table_offset = lseek (priv->fd, 0, SEEK_CUR);
630 g_hash_table_foreach (priv->method_table, write_method, symfile);
634 priv->offset_table->method_table_size = lseek (priv->fd, 0, SEEK_CUR) -
635 priv->offset_table->method_table_offset;
638 // Write offset table.
641 symfile->raw_contents_size = lseek (priv->fd, 0, SEEK_CUR);
642 priv->offset_table->address_table_size = symfile->address_table_size;
644 lseek (priv->fd, offset, SEEK_SET);
645 if (write (priv->fd, priv->offset_table, sizeof (MonoSymbolFileOffsetTable)) < 0)
648 lseek (priv->fd, symfile->raw_contents_size, SEEK_SET);
650 ptr = mono_raw_buffer_load (priv->fd, TRUE, 0, symfile->raw_contents_size);
654 symfile->raw_contents = ptr;
657 // Load line number table.
659 g_hash_table_foreach (priv->method_table, load_line_numbers, symfile);
663 if (!write_string_table (symfile))
670 mono_debug_create_mono_symbol_file (MonoImage *image)
672 MonoSymbolFile *symfile;
674 symfile = g_new0 (MonoSymbolFile, 1);
675 symfile->magic = MONO_SYMBOL_FILE_MAGIC;
676 symfile->version = MONO_SYMBOL_FILE_VERSION;
677 symfile->is_dynamic = TRUE;
678 symfile->image_file = g_strdup (image->name);
680 symfile->_priv = g_new0 (MonoSymbolFilePriv, 1);
681 symfile->_priv->image = image;
683 if (!create_symfile (symfile, TRUE)) {
684 mono_debug_close_mono_symbol_file (symfile);
691 MonoDebugMethodInfo *
692 mono_debug_find_method (MonoSymbolFile *symfile, MonoMethod *method)
694 return g_hash_table_lookup (symfile->_priv->method_hash, method);
698 write_method_name (gpointer key, gpointer value, gpointer user_data)
700 MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
701 MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
702 MonoSymbolFilePriv *priv = symfile->_priv;
703 guint8 *offset_ptr, *string_ptr;
706 offset = mep->method_name_offset + priv->string_offset_size;
708 offset_ptr = symfile->string_table + mep->index * 4;
709 string_ptr = symfile->string_table + offset;
711 *((guint32 *) offset_ptr) = offset;
712 strcpy (string_ptr, mep->name);
716 write_string_table (MonoSymbolFile *symfile)
718 MonoSymbolFilePriv *priv = symfile->_priv;
720 priv->string_offset_size = priv->offset_table->method_count * 4;
722 symfile->string_table_size = priv->string_table_size + priv->string_offset_size;
723 symfile->string_table = g_malloc0 (symfile->string_table_size);
725 g_hash_table_foreach (symfile->_priv->method_table, write_method_name, symfile);