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;
82 symfile->type_table_size = symfile->_priv->offset_table->type_count * sizeof (guint8 *);
83 symfile->type_table = g_malloc0 (symfile->type_table_size);
90 priv->method_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
91 (GDestroyNotify) g_free);
92 priv->method_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
93 (GDestroyNotify) free_method_info);
95 ptr = symfile->raw_contents + priv->offset_table->method_table_offset;
96 me = (MonoSymbolFileMethodEntry *) ptr;
98 for (i = 0; i < priv->offset_table->method_count; i++, me++) {
99 MonoMethod *method = mono_get_method (priv->image, me->token, NULL);
100 MonoSymbolFileMethodEntryPriv *mep;
101 MonoDebugMethodInfo *minfo;
106 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
107 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
108 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
109 g_assert_not_reached ();
111 if (!((MonoMethodNormal *) method)->header) {
112 g_warning (G_STRLOC ": Internal error: method %s.%s doesn't have a header",
113 method->klass->name, method->name);
117 minfo = g_new0 (MonoDebugMethodInfo, 1);
118 minfo->file_offset = ((const char *) me) - start;
119 minfo->method = method;
120 minfo->symfile = symfile;
121 minfo->num_il_offsets = me->num_line_numbers;
122 minfo->il_offsets = (MonoSymbolFileLineNumberEntry *)
123 (symfile->raw_contents + me->line_number_table_offset);
125 mep = g_new0 (MonoSymbolFileMethodEntryPriv, 1);
126 mep->method = method;
131 mep->method_name_offset = priv->string_table_size;
132 mep->name = g_strdup_printf ("%s%s.%s", method->klass->name_space,
133 method->klass->name, method->name);
134 priv->string_table_size += strlen (mep->name) + 1;
136 g_hash_table_insert (priv->method_table, method, mep);
137 g_hash_table_insert (priv->method_hash, method, minfo);
140 if (!write_string_table (symfile))
147 mono_debug_open_mono_symbol_file (MonoImage *image, const char *filename, gboolean emit_warnings)
149 MonoSymbolFile *symfile;
150 MonoSymbolFilePriv *priv;
155 fd = open (filename, O_RDONLY);
158 g_warning ("Can't open symbol file: %s", filename);
162 file_size = lseek (fd, 0, SEEK_END);
163 lseek (fd, 0, SEEK_SET);
165 if (file_size == (off_t) -1) {
167 g_warning ("Can't get size of symbol file: %s", filename);
171 ptr = mono_raw_buffer_load (fd, FALSE, 0, file_size);
174 g_warning ("Can't read symbol file: %s", filename);
178 symfile = g_new0 (MonoSymbolFile, 1);
179 symfile->magic = MONO_SYMBOL_FILE_MAGIC;
180 symfile->version = MONO_SYMBOL_FILE_VERSION;
181 symfile->image_file = g_strdup (image->name);
182 symfile->raw_contents = ptr;
183 symfile->raw_contents_size = file_size;
185 symfile->_priv = priv = g_new0 (MonoSymbolFilePriv, 1);
189 priv->file_name = g_strdup (filename);
191 if (!load_symfile (symfile)) {
192 mono_debug_close_mono_symbol_file (symfile);
200 close_symfile (MonoSymbolFile *symfile)
202 MonoSymbolFilePriv *priv = symfile->_priv;
204 if (symfile->raw_contents) {
205 mono_raw_buffer_free (symfile->raw_contents);
206 symfile->raw_contents = NULL;
214 if (priv->method_table) {
215 g_hash_table_destroy (priv->method_table);
216 priv->method_table = NULL;
219 if (priv->method_hash) {
220 g_hash_table_destroy (priv->method_hash);
221 priv->method_hash = NULL;
224 if (symfile->is_dynamic)
225 unlink (priv->file_name);
227 if (symfile->image_file) {
228 g_free (symfile->image_file);
229 symfile->image_file = NULL;
232 if (priv->file_name) {
233 g_free (priv->file_name);
234 priv->file_name = NULL;
241 mono_debug_close_mono_symbol_file (MonoSymbolFile *symfile)
246 close_symfile (symfile);
248 g_free (symfile->_priv->source_file);
249 g_free (symfile->_priv);
254 write_string (int fd, const char *string)
256 guint32 length = strlen (string);
258 if (write (fd, &length, sizeof (length)) < 0)
261 if (write (fd, string, strlen (string)) < 0)
268 read_string (const char *ptr)
270 int len = *((guint32 *) ptr)++;
272 return g_filename_from_utf8 (ptr, len, NULL, NULL, NULL);
276 mono_debug_find_source_location (MonoSymbolFile *symfile, MonoMethod *method, guint32 offset,
277 guint32 *line_number)
279 MonoSymbolFilePriv *priv = symfile->_priv;
280 MonoSymbolFileLineNumberEntry *lne;
281 MonoSymbolFileMethodEntryPriv *mep;
282 gchar *source_file = NULL;
286 if (!priv->method_table || symfile->is_dynamic)
289 mep = g_hash_table_lookup (priv->method_table, method);
293 if (mep->entry->source_file_offset)
294 source_file = read_string (symfile->raw_contents + mep->entry->source_file_offset);
296 ptr = symfile->raw_contents + mep->entry->line_number_table_offset;
298 lne = (MonoSymbolFileLineNumberEntry *) ptr;
300 for (i = 0; i < mep->entry->num_line_numbers; i++, lne++) {
301 if (lne->offset < offset)
305 *line_number = lne->row;
310 } else if (source_file) {
311 gchar *retval = g_strdup_printf ("%s:%d", source_file, lne->row);
312 g_free (source_file);
315 return g_strdup_printf ("%d", lne->row);
322 update_method_func (gpointer key, gpointer value, gpointer user_data)
324 MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
325 MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
326 MonoSymbolFileMethodAddress *address;
327 MonoDebugVarInfo *var_table;
328 MonoSymbolFileLineNumberEntry *lne;
329 MonoDebugRangeInfo *range;
333 mep->minfo = g_hash_table_lookup (symfile->_priv->method_hash, mep->method);
338 if (!mep->minfo->jit)
341 address = (MonoSymbolFileMethodAddress *)
342 (symfile->address_table + mep->entry->address_table_offset);
344 address->is_valid = TRUE;
345 address->start_address = GPOINTER_TO_UINT (mep->minfo->jit->code_start);
346 address->end_address = GPOINTER_TO_UINT (mep->minfo->jit->code_start + mep->minfo->jit->code_size);
348 range = g_new0 (MonoDebugRangeInfo, 1);
349 range->start_address = address->start_address;
350 range->end_address = address->end_address;
351 range->file_offset = mep->minfo->file_offset;
353 var_table = (MonoDebugVarInfo *)
354 (symfile->address_table + mep->entry->variable_table_offset);
356 if (mep->entry->this_type_index) {
357 if (!mep->minfo->jit->this_var) {
358 g_warning (G_STRLOC ": Method %s.%s doesn't have `this'.",
359 mep->method->klass->name, mep->method->name);
362 *var_table++ = *mep->minfo->jit->this_var;
365 if (mep->minfo->jit->num_params != mep->entry->num_parameters) {
366 g_warning (G_STRLOC ": Method %s.%s has %d parameters, but symbol file claims it has %d.",
367 mep->method->klass->name, mep->method->name, mep->minfo->jit->num_params,
368 mep->entry->num_parameters);
369 var_table += mep->entry->num_parameters;
371 for (i = 0; i < mep->minfo->jit->num_params; i++)
372 *var_table++ = mep->minfo->jit->params [i];
374 if (mep->minfo->jit->num_locals != mep->entry->num_locals) {
375 g_warning (G_STRLOC ": Method %s.%s has %d locals, but symbol file claims it has %d.",
376 mep->method->klass->name, mep->method->name, mep->minfo->jit->num_locals,
377 mep->entry->num_locals);
378 var_table += mep->entry->num_locals;
380 for (i = 0; i < mep->minfo->jit->num_locals; i++)
381 *var_table++ = mep->minfo->jit->locals [i];
383 g_ptr_array_add (symfile->_priv->range_table, range);
385 lne = (MonoSymbolFileLineNumberEntry *)
386 (symfile->raw_contents + mep->entry->line_number_table_offset);
388 for (i = 0; i < mep->entry->num_line_numbers; i++, lne++) {
392 address->line_addresses [i] = 0;
394 } else if (lne->offset == 0) {
395 address->line_addresses [i] = mep->minfo->jit->prologue_end;
399 address->line_addresses [i] = mep->minfo->jit->code_size;
401 for (j = 0; j < mep->minfo->num_il_offsets; j++) {
402 MonoSymbolFileLineNumberEntry *il = &mep->minfo->il_offsets [j];
404 if (il->offset >= lne->offset) {
405 address->line_addresses [i] = mep->minfo->jit->il_addresses [j];
413 range_table_compare_func (gconstpointer a, gconstpointer b)
415 const MonoDebugRangeInfo *r1 = (const MonoDebugRangeInfo *) a;
416 const MonoDebugRangeInfo *r2 = (const MonoDebugRangeInfo *) b;
418 if (r1->start_address < r2->start_address)
420 else if (r1->start_address > r2->start_address)
427 mono_debug_update_mono_symbol_file (MonoSymbolFile *symfile)
431 if (!symfile->_priv->method_table)
434 symfile->_priv->range_table = g_ptr_array_new ();
436 if (!symfile->address_table)
437 symfile->address_table = g_malloc0 (symfile->address_table_size);
439 g_hash_table_foreach (symfile->_priv->method_table, update_method_func, symfile);
441 symfile->range_table_size = symfile->_priv->range_table->len * sizeof (MonoDebugRangeInfo);
442 symfile->range_table = g_malloc0 (symfile->range_table_size);
444 g_ptr_array_sort (symfile->_priv->range_table, range_table_compare_func);
446 for (i = 0; i < symfile->_priv->range_table->len; i++) {
447 MonoDebugRangeInfo *range = g_ptr_array_index (symfile->_priv->range_table, i);
449 symfile->range_table [i] = *range;
452 g_ptr_array_free (symfile->_priv->range_table, TRUE);
453 symfile->_priv->range_table = NULL;
457 free_method_entry (MonoSymbolFileMethodEntryPriv *mep)
465 create_method (MonoSymbolFile *symfile, guint32 token, MonoMethod *method)
467 MonoSymbolFileMethodEntryPriv *mep;
468 MonoDebugMethodInfo *minfo;
470 g_assert (method->klass->image == symfile->_priv->image);
472 mep = g_new0 (MonoSymbolFileMethodEntryPriv, 1);
473 mep->entry = g_new0 (MonoSymbolFileMethodEntry, 1);
474 mep->entry->token = token;
475 mep->entry->source_file_offset = symfile->_priv->offset_table->source_table_offset;
477 mep->method_name_offset = symfile->_priv->string_table_size;
478 mep->name = g_strdup_printf ("%s%s.%s", method->klass->name_space,
479 method->klass->name, method->name);
480 symfile->_priv->string_table_size += strlen (mep->name) + 1;
482 minfo = g_new0 (MonoDebugMethodInfo, 1);
483 minfo->method = method;
484 minfo->symfile = symfile;
487 mep->method = method;
489 symfile->_priv->offset_table->method_count++;
491 g_hash_table_insert (symfile->_priv->method_table, method, mep);
492 g_hash_table_insert (symfile->_priv->method_hash, method, minfo);
496 write_method (gpointer key, gpointer value, gpointer user_data)
498 MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
499 MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
501 if (symfile->_priv->error)
504 mep->minfo->file_offset = lseek (symfile->_priv->fd, 0, SEEK_CUR);
506 if (write (symfile->_priv->fd, mep->entry, sizeof (MonoSymbolFileMethodEntry)) < 0) {
507 symfile->_priv->error = TRUE;
513 write_line_numbers (gpointer key, gpointer value, gpointer user_data)
515 MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
516 MonoSymbolFilePriv *priv = symfile->_priv;
517 MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
518 MonoSymbolFileLineNumberEntry lne;
519 const unsigned char *ip, *start, *end;
520 MonoMethodHeader *header;
525 if ((mep->method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
526 (mep->method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
527 (mep->method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
528 g_assert_not_reached ();
530 header = ((MonoMethodNormal *) mep->method)->header;
533 mep->entry->line_number_table_offset = lseek (priv->fd, 0, SEEK_CUR);
534 ++mep->entry->num_line_numbers;
539 if (write (priv->fd, &lne, sizeof (MonoSymbolFileLineNumberEntry)) < 0) {
544 ip = start = header->code;
545 end = ip + header->code_size;
550 ++mep->entry->num_line_numbers;
551 lne.offset = ip - start;
554 if (write (priv->fd, &lne, sizeof (MonoSymbolFileLineNumberEntry)) < 0) {
559 line = mono_disasm_code_one (NULL, mep->method, ip, &ip);
563 mep->entry->address_table_offset = symfile->address_table_size;
564 mep->entry->address_table_size = sizeof (MonoSymbolFileMethodAddress) +
565 mep->entry->num_line_numbers * sizeof (guint32);
567 symfile->address_table_size += mep->entry->address_table_size;
571 create_methods (MonoSymbolFile *symfile)
573 MonoImage *image = symfile->_priv->image;
574 MonoTableInfo *table = &image->tables [MONO_TABLE_METHOD];
577 for (idx = 1; idx <= table->rows; idx++) {
578 guint32 token = mono_metadata_make_token (MONO_TABLE_METHOD, idx);
579 MonoMethod *method = mono_get_method (image, token, NULL);
581 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
582 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
583 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
586 if (method->wrapper_type != MONO_WRAPPER_NONE)
589 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
590 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
591 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
592 g_assert_not_reached ();
594 if (!((MonoMethodNormal *) method)->header) {
595 g_warning (G_STRLOC ": Internal error: method %s.%s doesn't have a header",
596 method->klass->name, method->name);
600 create_method (symfile, token, method);
605 load_line_numbers (gpointer key, gpointer value, gpointer user_data)
607 MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
608 MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
610 mep->minfo->num_il_offsets = mep->entry->num_line_numbers;
611 mep->minfo->il_offsets = (MonoSymbolFileLineNumberEntry *)
612 (symfile->raw_contents + mep->entry->line_number_table_offset);
616 create_symfile (MonoSymbolFile *symfile, gboolean emit_warnings)
618 MonoSymbolFilePriv *priv = symfile->_priv;
624 priv->fd = g_file_open_tmp (NULL, &priv->file_name, NULL);
625 if (priv->fd == -1) {
627 g_warning ("Can't create symbol file");
631 magic = MONO_SYMBOL_FILE_MAGIC;
632 if (write (priv->fd, &magic, sizeof (magic)) < 0)
635 version = MONO_SYMBOL_FILE_VERSION;
636 if (write (priv->fd, &version, sizeof (version)) < 0)
639 offset = lseek (priv->fd, 0, SEEK_CUR);
641 priv->offset_table = g_new0 (MonoSymbolFileOffsetTable, 1);
642 if (write (priv->fd, priv->offset_table, sizeof (MonoSymbolFileOffsetTable)) < 0)
646 // Write source file table.
648 if (priv->source_file) {
649 priv->offset_table->source_table_offset = lseek (priv->fd, 0, SEEK_CUR);
650 if (!write_string (priv->fd, priv->source_file))
652 priv->offset_table->source_table_size = lseek (priv->fd, 0, SEEK_CUR) -
653 priv->offset_table->source_table_offset;
657 // Create method table.
660 priv->method_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
661 (GDestroyNotify) free_method_entry);
662 priv->method_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
663 (GDestroyNotify) free_method_info);
665 create_methods (symfile);
668 // Write line numbers.
671 priv->offset_table->line_number_table_offset = lseek (priv->fd, 0, SEEK_CUR);
673 g_hash_table_foreach (priv->method_table, write_line_numbers, symfile);
677 priv->offset_table->line_number_table_size = lseek (priv->fd, 0, SEEK_CUR) -
678 priv->offset_table->line_number_table_offset;
681 // Write method table.
684 priv->offset_table->method_table_offset = lseek (priv->fd, 0, SEEK_CUR);
686 g_hash_table_foreach (priv->method_table, write_method, symfile);
690 priv->offset_table->method_table_size = lseek (priv->fd, 0, SEEK_CUR) -
691 priv->offset_table->method_table_offset;
694 // Write offset table.
697 symfile->raw_contents_size = lseek (priv->fd, 0, SEEK_CUR);
698 priv->offset_table->address_table_size = symfile->address_table_size;
700 lseek (priv->fd, offset, SEEK_SET);
701 if (write (priv->fd, priv->offset_table, sizeof (MonoSymbolFileOffsetTable)) < 0)
704 lseek (priv->fd, symfile->raw_contents_size, SEEK_SET);
706 ptr = mono_raw_buffer_load (priv->fd, TRUE, 0, symfile->raw_contents_size);
710 symfile->raw_contents = ptr;
712 symfile->type_table_size = symfile->_priv->offset_table->type_count * sizeof (guint8 *);
713 symfile->type_table = g_malloc0 (symfile->type_table_size);
716 // Load line number table.
718 g_hash_table_foreach (priv->method_table, load_line_numbers, symfile);
722 if (!write_string_table (symfile))
729 mono_debug_create_mono_symbol_file (MonoImage *image)
731 MonoSymbolFile *symfile;
733 symfile = g_new0 (MonoSymbolFile, 1);
734 symfile->magic = MONO_SYMBOL_FILE_MAGIC;
735 symfile->version = MONO_SYMBOL_FILE_VERSION;
736 symfile->is_dynamic = TRUE;
737 symfile->image_file = g_strdup (image->name);
739 symfile->_priv = g_new0 (MonoSymbolFilePriv, 1);
740 symfile->_priv->image = image;
742 if (!create_symfile (symfile, TRUE)) {
743 mono_debug_close_mono_symbol_file (symfile);
750 MonoDebugMethodInfo *
751 mono_debug_find_method (MonoSymbolFile *symfile, MonoMethod *method)
753 return g_hash_table_lookup (symfile->_priv->method_hash, method);
757 write_method_name (gpointer key, gpointer value, gpointer user_data)
759 MonoSymbolFile *symfile = (MonoSymbolFile *) user_data;
760 MonoSymbolFileMethodEntryPriv *mep = (MonoSymbolFileMethodEntryPriv *) value;
761 MonoSymbolFilePriv *priv = symfile->_priv;
762 guint8 *offset_ptr, *string_ptr;
765 offset = mep->method_name_offset + priv->string_offset_size;
767 offset_ptr = symfile->string_table + mep->index * 4;
768 string_ptr = symfile->string_table + offset;
770 *((guint32 *) offset_ptr) = offset;
771 strcpy (string_ptr, mep->name);
775 write_string_table (MonoSymbolFile *symfile)
777 MonoSymbolFilePriv *priv = symfile->_priv;
779 priv->string_offset_size = priv->offset_table->method_count * 4;
781 symfile->string_table_size = priv->string_table_size + priv->string_offset_size;
782 symfile->string_table = g_malloc0 (symfile->string_table_size);
784 g_hash_table_foreach (symfile->_priv->method_table, write_method_name, symfile);
788 MonoReflectionMethod *
789 ves_icall_MonoDebugger_GetMethod (MonoReflectionAssembly *assembly, guint32 token)
793 method = mono_get_method (assembly->assembly->image, token, NULL);
795 return mono_method_get_object (mono_domain_get (), method, NULL);