Merge pull request #260 from pcc/topmost
[mono.git] / mono / metadata / debug-mono-symfile.c
index 8d3f6f1460909c3cd25a34198a29dd7449d5e6f6..9832418bec12990296a243288a4958fdba0f801b 100644 (file)
@@ -5,6 +5,7 @@
  *     Mono Project (http://www.mono-project.com)
  *
  * Copyright (C) 2005-2008 Novell, Inc. (http://www.novell.com)
+ * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
  */
 
 #include <config.h>
@@ -29,6 +30,7 @@
 #include <mono/metadata/metadata-internals.h>
 #include <mono/metadata/class-internals.h>
 #include <mono/utils/mono-mmap.h>
+#include <mono/utils/bsearch.h>
 
 #include <fcntl.h>
 #ifdef HAVE_UNISTD_H
 #define TYPE_TABLE_PTR_CHUNK_SIZE      256
 #define TYPE_TABLE_CHUNK_SIZE          65536
 
+struct _MonoSymbolFile {
+       const uint8_t *raw_contents;
+       int raw_contents_size;
+       void *raw_contents_handle;
+       int major_version;
+       int minor_version;
+       char *filename;
+       GHashTable *method_hash;
+       GHashTable *source_hash;
+       MonoSymbolFileOffsetTable *offset_table;
+       gboolean was_loaded_from_memory;
+};
+
 static void
 free_method_info (MonoDebugMethodInfo *minfo)
 {
        g_free (minfo);
 }
 
+static void
+free_source_info (MonoDebugSourceInfo *sinfo)
+{
+       g_free (sinfo->source_file);
+       g_free (sinfo->guid);
+       g_free (sinfo->hash);
+       g_free (sinfo);
+}
+
 static int
-load_symfile (MonoDebugHandle *handle, MonoSymbolFile *symfile, gboolean in_the_debugger)
+load_symfile (MonoDebugHandle *handle, MonoSymbolFile *symfile, mono_bool in_the_debugger)
 {
        const char *ptr, *start;
        gchar *guid;
-       guint64 magic;
+       uint64_t magic;
        int minor, major;
 
        ptr = start = (const char*)symfile->raw_contents;
@@ -59,7 +83,7 @@ load_symfile (MonoDebugHandle *handle, MonoSymbolFile *symfile, gboolean in_the_
                return FALSE;
 
        magic = read64(ptr);
-       ptr += sizeof(guint64);
+       ptr += sizeof(uint64_t);
        if (magic != MONO_SYMBOL_FILE_MAGIC) {
                if (!in_the_debugger)
                        g_warning ("Symbol file %s is not a mono symbol file", symfile->filename);
@@ -67,9 +91,9 @@ load_symfile (MonoDebugHandle *handle, MonoSymbolFile *symfile, gboolean in_the_
        }
 
        major = read32(ptr);
-       ptr += sizeof(guint32);
+       ptr += sizeof(uint32_t);
        minor = read32(ptr);
-       ptr += sizeof(guint32);
+       ptr += sizeof(uint32_t);
 
        /*
         * 50.0 is the frozen version for Mono 2.0.
@@ -84,7 +108,7 @@ load_symfile (MonoDebugHandle *handle, MonoSymbolFile *symfile, gboolean in_the_
                return FALSE;
        }
 
-       guid = mono_guid_to_string ((const guint8 *) ptr);
+       guid = mono_guid_to_string ((const uint8_t *) ptr);
        ptr += 16;
 
        if (strcmp (handle->image->guid, guid)) {
@@ -102,14 +126,17 @@ load_symfile (MonoDebugHandle *handle, MonoSymbolFile *symfile, gboolean in_the_
        symfile->offset_table = (MonoSymbolFileOffsetTable *) ptr;
 
        symfile->method_hash = g_hash_table_new_full (
-               g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) free_method_info);
+               NULL, NULL, NULL, (GDestroyNotify) free_method_info);
+
+       symfile->source_hash = g_hash_table_new_full (
+               NULL, NULL, NULL, (GDestroyNotify) free_source_info);
 
        g_free (guid);
        return TRUE;
 }
 
 MonoSymbolFile *
-mono_debug_open_mono_symbols (MonoDebugHandle *handle, const guint8 *raw_contents,
+mono_debug_open_mono_symbols (MonoDebugHandle *handle, const uint8_t *raw_contents,
                              int size, gboolean in_the_debugger)
 {
        MonoSymbolFile *symfile;
@@ -178,8 +205,15 @@ mono_debug_close_mono_symbol_file (MonoSymbolFile *symfile)
        mono_debugger_unlock ();
 }
 
+mono_bool
+mono_debug_symfile_is_loaded (MonoSymbolFile *symfile)
+{
+       return symfile && symfile->offset_table;
+}
+
+
 static int
-read_leb128 (const guint8 *ptr, const guint8 **rptr)
+read_leb128 (const uint8_t *ptr, const uint8_t **rptr)
 {
        int ret = 0;
        int shift = 0;
@@ -199,18 +233,24 @@ read_leb128 (const guint8 *ptr, const guint8 **rptr)
 }
 
 static gchar *
-read_string (const guint8 *ptr)
+read_string (const uint8_t *ptr, const uint8_t **endp)
 {
+       gchar *s;
        int len = read_leb128 (ptr, &ptr);
-       return g_filename_from_utf8 ((const char *) ptr, len, NULL, NULL, NULL);
+
+       s = g_filename_from_utf8 ((const char *) ptr, len, NULL, NULL, NULL);
+       ptr += len;
+       if (endp)
+               *endp = ptr;
+       return s;
 }
 
 typedef struct {
        MonoSymbolFile *symfile;
        int line_base, line_range, max_address_incr;
-       guint8 opcode_base;
-       guint32 last_line, last_file, last_offset;
-       guint32 first_file;
+       uint8_t opcode_base;
+       uint32_t last_line, last_file, last_offset;
+       uint32_t first_file;
        int line, file, offset;
        gboolean is_hidden;
 } StatementMachine;
@@ -234,7 +274,7 @@ check_line (StatementMachine *stm, int offset, MonoDebugSourceLocation **locatio
                MonoSymbolFileSourceEntry *se = (MonoSymbolFileSourceEntry *)
                        (stm->symfile->raw_contents + offset);
 
-               source_file = read_string (stm->symfile->raw_contents + read32(&(se->_data_offset)));
+               source_file = read_string (stm->symfile->raw_contents + read32(&(se->_data_offset)), NULL);
        }
 
        if (stm->last_line == 0) {
@@ -264,7 +304,7 @@ check_line (StatementMachine *stm, int offset, MonoDebugSourceLocation **locatio
  * `native address -> IL offset' mapping.
  */
 MonoDebugSourceLocation *
-mono_debug_symfile_lookup_location (MonoDebugMethodInfo *minfo, guint32 offset)
+mono_debug_symfile_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset)
 {
        MonoDebugSourceLocation *location = NULL;
        MonoSymbolFile *symfile;
@@ -288,7 +328,7 @@ mono_debug_symfile_lookup_location (MonoDebugMethodInfo *minfo, guint32 offset)
 
        stm.line_base = read32 (&symfile->offset_table->_line_number_table_line_base);
        stm.line_range = read32 (&symfile->offset_table->_line_number_table_line_range);
-       stm.opcode_base = (guint8) read32 (&symfile->offset_table->_line_number_table_opcode_base);
+       stm.opcode_base = (uint8_t) read32 (&symfile->offset_table->_line_number_table_opcode_base);
        stm.max_address_incr = (255 - stm.opcode_base) / stm.line_range;
 
        mono_debugger_lock ();
@@ -305,10 +345,10 @@ mono_debug_symfile_lookup_location (MonoDebugMethodInfo *minfo, guint32 offset)
        stm.is_hidden = FALSE;
 
        while (TRUE) {
-               guint8 opcode = *ptr++;
+               uint8_t opcode = *ptr++;
 
                if (opcode == 0) {
-                       guint8 size = *ptr++;
+                       uint8_t size = *ptr++;
                        const unsigned char *end_ptr = ptr + size;
 
                        opcode = *ptr++;
@@ -371,11 +411,12 @@ mono_debug_symfile_lookup_location (MonoDebugMethodInfo *minfo, guint32 offset)
 }
 
 static void
-add_line (StatementMachine *stm, GPtrArray *il_offset_array, GPtrArray *line_number_array)
+add_line (StatementMachine *stm, GPtrArray *il_offset_array, GPtrArray *line_number_array, GPtrArray *source_file_array)
 {
        if (stm->line > 0) {
                g_ptr_array_add (il_offset_array, GUINT_TO_POINTER (stm->offset));
                g_ptr_array_add (line_number_array, GUINT_TO_POINTER (stm->line));
+               g_ptr_array_add (source_file_array, GUINT_TO_POINTER (stm->file));
        }
 
        if (!stm->is_hidden && !stm->first_file)
@@ -396,34 +437,105 @@ mono_debug_symfile_free_location   (MonoDebugSourceLocation  *location)
 }
 
 /*
- * mono_debug_symfile_get_line_numbers:
+ * LOCKING: Assumes the debugger lock is held.
+ */
+static MonoDebugSourceInfo*
+get_source_info (MonoSymbolFile *symfile, int index)
+{
+       MonoDebugSourceInfo *info;
+
+       info = g_hash_table_lookup (symfile->source_hash, GUINT_TO_POINTER (index));
+       if (!info) {
+               int offset = read32(&(symfile->offset_table->_source_table_offset)) +
+                       (index - 1) * sizeof (MonoSymbolFileSourceEntry);
+               MonoSymbolFileSourceEntry *se = (MonoSymbolFileSourceEntry *)
+                       (symfile->raw_contents + offset);
+               const uint8_t *ptr = symfile->raw_contents + read32(&(se->_data_offset));
+
+               info = g_new0 (MonoDebugSourceInfo, 1);
+               info->source_file = read_string (ptr, &ptr);
+               info->guid = g_malloc0 (16);
+               memcpy (info->guid, ptr, 16);
+               ptr += 16;
+               info->hash = g_malloc0 (16);
+               memcpy (info->hash, ptr, 16);
+               ptr += 16;
+               g_hash_table_insert (symfile->source_hash, GUINT_TO_POINTER (index), info);
+       }
+       return info;
+}
+
+static gboolean
+method_has_column_info (MonoDebugMethodInfo *minfo)
+{
+       MonoSymbolFile *symfile;
+       const unsigned char *ptr;
+       guint32 flags;
+
+       if ((symfile = minfo->handle->symfile) == NULL)
+               return FALSE;
+
+       ptr = symfile->raw_contents + minfo->data_offset;
+
+       /* Has to read 'flags' which is preceeded by a bunch of other data */
+       /* compile_unit_index */
+       read_leb128 (ptr, &ptr);
+       /* local variable table offset */
+       read_leb128 (ptr, &ptr);
+       /* namespace id */
+       read_leb128 (ptr, &ptr);
+       /* code block table offset */
+       read_leb128 (ptr, &ptr);
+       /* scope variable table offset */
+       read_leb128 (ptr, &ptr);
+       /* real name offset */
+       read_leb128 (ptr, &ptr);
+
+       flags = read_leb128 (ptr, &ptr);
+       return (flags & 2) > 0;
+}
+
+/*
+ * mono_debug_symfile_get_line_numbers_full:
  *
- *   All the output parameters can be NULL.
- */ 
+ * On return, SOURCE_FILE_LIST will point to a GPtrArray of MonoDebugSourceFile
+ * structures, and SOURCE_FILES will contain indexes into this array.
+ * The MonoDebugSourceFile structures are owned by this module.
+ */
 void
-mono_debug_symfile_get_line_numbers (MonoDebugMethodInfo *minfo, char **source_file, int *n_il_offsets, int **il_offsets, int **line_numbers)
+mono_debug_symfile_get_line_numbers_full (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int *n_il_offsets, int **il_offsets, int **line_numbers, int **column_numbers, int **source_files)
 {
        // FIXME: Unify this with mono_debug_symfile_lookup_location
        MonoSymbolFile *symfile;
        const unsigned char *ptr;
        StatementMachine stm;
-       guint32 i;
-       GPtrArray *il_offset_array, *line_number_array;
+       uint32_t i;
+       GPtrArray *il_offset_array, *line_number_array, *source_file_array;
+       gboolean has_column_info;
 
-       if (source_file)
-               *source_file = NULL;
+       if (source_file_list)
+               *source_file_list = NULL;
        if (n_il_offsets)
                *n_il_offsets = 0;
+       if (source_files)
+               *source_files = NULL;
+       if (source_file)
+               *source_file = NULL;
+       if (column_numbers)
+               *column_numbers = NULL;
 
        if ((symfile = minfo->handle->symfile) == NULL)
                return;
 
+       has_column_info = method_has_column_info (minfo);
+
        il_offset_array = g_ptr_array_new ();
        line_number_array = g_ptr_array_new ();
+       source_file_array = g_ptr_array_new ();
 
        stm.line_base = read32 (&symfile->offset_table->_line_number_table_line_base);
        stm.line_range = read32 (&symfile->offset_table->_line_number_table_line_range);
-       stm.opcode_base = (guint8) read32 (&symfile->offset_table->_line_number_table_opcode_base);
+       stm.opcode_base = (uint8_t) read32 (&symfile->offset_table->_line_number_table_opcode_base);
        stm.max_address_incr = (255 - stm.opcode_base) / stm.line_range;
 
        mono_debugger_lock ();
@@ -440,16 +552,19 @@ mono_debug_symfile_get_line_numbers (MonoDebugMethodInfo *minfo, char **source_f
        stm.is_hidden = FALSE;
 
        while (TRUE) {
-               guint8 opcode = *ptr++;
+               uint8_t opcode = *ptr++;
 
                if (opcode == 0) {
-                       guint8 size = *ptr++;
+                       uint8_t size = *ptr++;
                        const unsigned char *end_ptr = ptr + size;
 
                        opcode = *ptr++;
 
                        if (opcode == DW_LNE_end_sequence) {
-                               add_line (&stm, il_offset_array, line_number_array);
+                               if (il_offset_array->len == 0)
+                                       /* Empty table */
+                                       break;
+                               add_line (&stm, il_offset_array, line_number_array, source_file_array);
                                break;
                        } else if (opcode == DW_LNE_MONO_negate_is_hidden) {
                                stm.is_hidden = !stm.is_hidden;
@@ -465,7 +580,7 @@ mono_debug_symfile_get_line_numbers (MonoDebugMethodInfo *minfo, char **source_f
                } else if (opcode < stm.opcode_base) {
                        switch (opcode) {
                        case DW_LNS_copy:
-                               add_line (&stm, il_offset_array, line_number_array);
+                               add_line (&stm, il_offset_array, line_number_array, source_file_array);
                                break;
                        case DW_LNS_advance_pc:
                                stm.offset += read_leb128 (ptr, &ptr);
@@ -489,23 +604,48 @@ mono_debug_symfile_get_line_numbers (MonoDebugMethodInfo *minfo, char **source_f
                        stm.offset += opcode / stm.line_range;
                        stm.line += stm.line_base + (opcode % stm.line_range);
 
-                       add_line (&stm, il_offset_array, line_number_array);
+                       add_line (&stm, il_offset_array, line_number_array, source_file_array);
                }
        }
 
        if (!stm.file && stm.first_file)
                stm.file = stm.first_file;
 
-       if (stm.file) {
+       if (stm.file && source_file) {
                int offset = read32(&(stm.symfile->offset_table->_source_table_offset)) +
                        (stm.file - 1) * sizeof (MonoSymbolFileSourceEntry);
                MonoSymbolFileSourceEntry *se = (MonoSymbolFileSourceEntry *)
                        (stm.symfile->raw_contents + offset);
 
                if (source_file)
-                       *source_file = read_string (stm.symfile->raw_contents + read32(&(se->_data_offset)));
+                       *source_file = read_string (stm.symfile->raw_contents + read32(&(se->_data_offset)), NULL);
        }
 
+       if (source_file_list) {
+               int file, last_file = 0;
+
+               *source_file_list = g_ptr_array_new ();
+               if (source_files)
+                       *source_files = g_malloc (il_offset_array->len * sizeof (int));
+
+               for (i = 0; i < il_offset_array->len; ++i) {
+                       file = GPOINTER_TO_UINT (g_ptr_array_index (source_file_array, i));
+                       if (file && file != last_file) {
+                               MonoDebugSourceInfo *info = get_source_info (symfile, file);
+
+                               g_ptr_array_add (*source_file_list, info);
+                       }
+                       last_file = file;
+                       if (source_files)
+                               (*source_files) [i] = (*source_file_list)->len - 1;
+               }
+               if ((*source_file_list)->len == 0 && stm.file) {
+                       MonoDebugSourceInfo *info = get_source_info (symfile, stm.file);
+
+                       g_ptr_array_add (*source_file_list, info);
+               }
+       }                               
+
        if (n_il_offsets)
                *n_il_offsets = il_offset_array->len;
        if (il_offsets && line_numbers) {
@@ -516,6 +656,13 @@ mono_debug_symfile_get_line_numbers (MonoDebugMethodInfo *minfo, char **source_f
                        (*line_numbers) [i] = GPOINTER_TO_UINT (g_ptr_array_index (line_number_array, i));
                }
        }
+
+       if (column_numbers && has_column_info) {
+               *column_numbers = g_malloc (il_offset_array->len * sizeof (int));
+               for (i = 0; i < il_offset_array->len; ++i)
+                       (*column_numbers) [i] = read_leb128 (ptr, &ptr);
+       }
+
        g_ptr_array_free (il_offset_array, TRUE);
        g_ptr_array_free (line_number_array, TRUE);
 
@@ -523,8 +670,19 @@ mono_debug_symfile_get_line_numbers (MonoDebugMethodInfo *minfo, char **source_f
        return;
 }
 
-gint32
-_mono_debug_address_from_il_offset (MonoDebugMethodJitInfo *jit, guint32 il_offset)
+/*
+ * mono_debug_symfile_get_line_numbers:
+ *
+ *   All the output parameters can be NULL.
+ */ 
+void
+mono_debug_symfile_get_line_numbers (MonoDebugMethodInfo *minfo, char **source_file, int *n_il_offsets, int **il_offsets, int **line_numbers)
+{
+       mono_debug_symfile_get_line_numbers_full (minfo, source_file, NULL, n_il_offsets, il_offsets, line_numbers, NULL, NULL);
+}
+       
+int32_t
+_mono_debug_address_from_il_offset (MonoDebugMethodJitInfo *jit, uint32_t il_offset)
 {
        int i;
 
@@ -534,8 +692,6 @@ _mono_debug_address_from_il_offset (MonoDebugMethodJitInfo *jit, guint32 il_offs
        for (i = jit->num_line_numbers - 1; i >= 0; i--) {
                MonoDebugLineNumberEntry lne = jit->line_numbers [i];
 
-               if (lne.il_offset < 0)
-                       continue;
                if (lne.il_offset <= il_offset)
                        return lne.native_offset;
        }
@@ -546,7 +702,7 @@ _mono_debug_address_from_il_offset (MonoDebugMethodJitInfo *jit, guint32 il_offs
 static int
 compare_method (const void *key, const void *object)
 {
-       guint32 token = GPOINTER_TO_UINT (key);
+       uint32_t token = GPOINTER_TO_UINT (key);
        MonoSymbolFileMethodEntry *me = (MonoSymbolFileMethodEntry*)object;
 
        return token - read32(&(me->_token));
@@ -576,7 +732,7 @@ mono_debug_symfile_lookup_method (MonoDebugHandle *handle, MonoMethod *method)
        first_ie = (MonoSymbolFileMethodEntry *)
                (symfile->raw_contents + read32(&(symfile->offset_table->_method_table_offset)));
 
-       ie = bsearch (GUINT_TO_POINTER (mono_method_get_token (method)), first_ie,
+       ie = mono_binary_search (GUINT_TO_POINTER (mono_method_get_token (method)), first_ie,
                                   read32(&(symfile->offset_table->_method_count)),
                                   sizeof (MonoSymbolFileMethodEntry), compare_method);
 
@@ -610,7 +766,7 @@ MonoDebugLocalsInfo*
 mono_debug_symfile_lookup_locals (MonoDebugMethodInfo *minfo)
 {
        MonoSymbolFile *symfile = minfo->handle->symfile;
-       const guint8 *p;
+       const uint8_t *p;
        int i, len, compile_unit_index, locals_offset, num_locals, block_index;
        int namespace_id, code_block_table_offset;
        MonoDebugLocalsInfo *res;