2006-06-08 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / metadata / mono-debug.c
index baa8ff2367b67d18fb667a1be6bd6bc1d3714ba6..d4e1ef58e8faf2ed528a92193fcbda6caa950ae3 100644 (file)
@@ -7,25 +7,39 @@
 #include <mono/metadata/mono-debug.h>
 #include <mono/metadata/mono-debug-debugger.h>
 #include <mono/metadata/mono-endian.h>
+#include <string.h>
 
-struct _MonoDebugHandlePriv
-{
-       MonoDebuggerSymbolFile *debugger_info;
-       MonoDebugDomainData *domain_table;
-};
+#define SYMFILE_TABLE_CHUNK_SIZE       16
+#define DATA_TABLE_PTR_CHUNK_SIZE      256
+#define DATA_TABLE_CHUNK_SIZE          32768
 
-struct _MonoDebugDomainDataPriv
-{
-       GHashTable *wrapper_info;
-       MonoDebugDomainData *next;
-};
+#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
 
+#if NO_UNALIGNED_ACCESS
+#define RETURN_UNALIGNED(type, addr) \
+       { \
+               type val; \
+               memcpy(&val, p + offset, sizeof(val)); \
+               return val; \
+       }
+#define WRITE_UNALIGNED(type, addr, val) \
+       memcpy(addr, &val, sizeof(type))
+#else
+#define RETURN_UNALIGNED(type, addr) \
+       return *(type*)(p + offset);
+#define WRITE_UNALIGNED(type, addr, val) \
+       (*(type *)(addr) = (val))
+#endif
+
+MonoSymbolTable *mono_symbol_table = NULL;
 MonoDebugFormat mono_debug_format = MONO_DEBUG_FORMAT_NONE;
 
 static gboolean in_the_mono_debugger = FALSE;
 static gboolean mono_debug_initialized = FALSE;
 GHashTable *mono_debug_handles = NULL;
 
+static GHashTable *method_hash = NULL;
+
 static MonoDebugHandle     *mono_debug_open_image      (MonoImage *image);
 static void                 mono_debug_close_image     (MonoDebugHandle *debug);
 
@@ -34,12 +48,34 @@ static void                 mono_debug_add_assembly    (MonoAssembly *assembly,
                                                        gpointer user_data);
 static void                 mono_debug_start_add_type  (MonoClass *klass);
 static void                 mono_debug_add_type        (MonoClass *klass);
-static MonoDebugDomainData *mono_debug_get_domain_data (MonoDebugHandle *handle,
-                                                       MonoDomain *domain);
 
 extern void (*mono_debugger_class_init_func) (MonoClass *klass);
 extern void (*mono_debugger_start_class_init_func) (MonoClass *klass);
 
+typedef struct {
+       guint32 symfile_id;
+       guint32 domain_id;
+       guint32 method_id;
+} MethodHashEntry;
+
+static guint
+method_hash_hash (gconstpointer data)
+{
+       const MethodHashEntry *entry = (const MethodHashEntry *) data;
+       return entry->symfile_id | (entry->domain_id << 16);
+}
+
+static gint
+method_hash_equal (gconstpointer ka, gconstpointer kb)
+{
+       const MethodHashEntry *a = (const MethodHashEntry *) ka;
+       const MethodHashEntry *b = (const MethodHashEntry *) kb;
+
+       if ((a->symfile_id != b->symfile_id) || (a->method_id != b->method_id) || (a->domain_id != b->domain_id))
+               return 0;
+       return 1;
+}
+
 /*
  * Initialize debugging support.
  *
@@ -56,17 +92,25 @@ mono_debug_init (MonoDebugFormat format)
        mono_debug_format = format;
        in_the_mono_debugger = format == MONO_DEBUG_FORMAT_DEBUGGER;
 
-       if (in_the_mono_debugger)
-               mono_debugger_initialize ();
+       mono_debugger_initialize (in_the_mono_debugger);
 
        mono_debugger_lock ();
 
+       mono_symbol_table = g_new0 (MonoSymbolTable, 1);
+       mono_symbol_table->magic = MONO_DEBUGGER_MAGIC;
+       mono_symbol_table->version = MONO_DEBUGGER_VERSION;
+       mono_symbol_table->total_size = sizeof (MonoSymbolTable);
+
        mono_debug_handles = g_hash_table_new_full
                (NULL, NULL, NULL, (GDestroyNotify) mono_debug_close_image);
+       method_hash = g_hash_table_new (method_hash_hash, method_hash_equal);
 
        mono_debugger_start_class_init_func = mono_debug_start_add_type;
        mono_debugger_class_init_func = mono_debug_add_type;
        mono_install_assembly_load_hook (mono_debug_add_assembly, NULL);
+
+       if (!in_the_mono_debugger)
+               mono_debugger_unlock ();
 }
 
 void
@@ -74,8 +118,7 @@ mono_debug_init_1 (MonoDomain *domain)
 {
        MonoDebugHandle *handle = mono_debug_open_image (mono_get_corlib ());
 
-       if (in_the_mono_debugger)
-               mono_debugger_add_builtin_types (handle->_priv->debugger_info);
+       mono_symbol_table->corlib = handle;
 }
 
 /*
@@ -89,6 +132,12 @@ mono_debug_init_2 (MonoAssembly *assembly)
        mono_debug_open_image (mono_assembly_get_image (assembly));
 }
 
+gboolean
+mono_debug_using_mono_debugger (void)
+{
+       return in_the_mono_debugger;
+}
+
 void
 mono_debug_cleanup (void)
 {
@@ -105,29 +154,49 @@ _mono_debug_get_image (MonoImage *image)
        return g_hash_table_lookup (mono_debug_handles, image);
 }
 
+static MonoDebugHandle *
+allocate_debug_handle (MonoSymbolTable *table)
+{
+       MonoDebugHandle *handle;
+
+       if (!table->symbol_files)
+               table->symbol_files = g_new0 (MonoDebugHandle *, SYMFILE_TABLE_CHUNK_SIZE);
+       else if (!((table->num_symbol_files + 1) % SYMFILE_TABLE_CHUNK_SIZE)) {
+               guint32 chunks = (table->num_symbol_files + 1) / SYMFILE_TABLE_CHUNK_SIZE;
+               guint32 size = sizeof (MonoDebugHandle *) * SYMFILE_TABLE_CHUNK_SIZE * (chunks + 1);
+
+               table->symbol_files = g_realloc (table->symbol_files, size);
+       }
+
+       handle = g_new0 (MonoDebugHandle, 1);
+       handle->index = table->num_symbol_files;
+       table->symbol_files [table->num_symbol_files++] = handle;
+       return handle;
+}
+
 static MonoDebugHandle *
 mono_debug_open_image (MonoImage *image)
 {
        MonoDebugHandle *handle;
 
+       if (mono_image_is_dynamic (image))
+               return NULL;
+
        handle = _mono_debug_get_image (image);
        if (handle != NULL)
                return handle;
 
-       handle = g_new0 (MonoDebugHandle, 1);
+       handle = allocate_debug_handle (mono_symbol_table);
+
        handle->image = image;
        mono_image_addref (image);
        handle->image_file = g_strdup (mono_image_get_filename (image));
-       handle->_priv = g_new0 (MonoDebugHandlePriv, 1);
 
        g_hash_table_insert (mono_debug_handles, image, handle);
 
-       if (mono_image_is_dynamic (image))
-               return handle;
-
        handle->symfile = mono_debug_open_mono_symbol_file (handle, in_the_mono_debugger);
        if (in_the_mono_debugger)
-               handle->_priv->debugger_info = mono_debugger_add_symbol_file (handle);
+               mono_debugger_add_symbol_file (handle);
 
        return handle;
 }
@@ -139,7 +208,7 @@ mono_debug_close_image (MonoDebugHandle *handle)
                mono_debug_close_mono_symbol_file (handle->symfile);
        /* decrease the refcount added with mono_image_addref () */
        mono_image_close (handle->image);
-       /* FIXME: should also free handle->image_file? */
+       g_free (handle->image_file);
        g_free (handle->_priv);
        g_free (handle);
 }
@@ -153,45 +222,70 @@ mono_debug_add_assembly (MonoAssembly *assembly, gpointer user_data)
 }
 
 /*
- * This is called via the `mono_debugger_class_init_func' from mono_class_init() each time
- * a new class is initialized.
+ * Allocate a new data item of size `size'.
+ * Returns the global offset which is to be used to reference this data item and
+ * a pointer (in the `ptr' argument) which is to be used to write it.
  */
-MonoDebuggerSymbolFile *
-_mono_debugger_get_symfile (MonoImage *image)
+static guint8 *
+allocate_data_item (MonoDebugDataItemType type, guint32 size)
 {
-       MonoDebugHandle *handle = _mono_debug_get_image (image);
-       if (!handle)
-               return NULL;
+       guint32 chunk_size;
+       guint8 *data;
 
-       return handle->_priv->debugger_info;
-}
+       g_assert (mono_symbol_table);
 
-static void
-mono_debug_start_add_type (MonoClass *klass)
-{
-       MonoDebugHandle *handle;
+       size = ALIGN_TO (size, sizeof (gpointer));
 
-       handle = _mono_debug_get_image (klass->image);
-       if (!handle)
-               return;
+       if (size + 16 < DATA_TABLE_CHUNK_SIZE)
+               chunk_size = DATA_TABLE_CHUNK_SIZE;
+       else
+               chunk_size = size + 16;
 
-       if (handle->_priv->debugger_info)
-               mono_debugger_start_add_type (handle->_priv->debugger_info, klass);
-}
+       /* Initialize things if necessary. */
+       if (!mono_symbol_table->current_data_table) {
+               mono_symbol_table->current_data_table = g_malloc0 (chunk_size);
+               mono_symbol_table->current_data_table_size = chunk_size;
+               mono_symbol_table->current_data_table_offset = sizeof (gpointer);
 
-static void
-mono_debug_add_type (MonoClass *klass)
-{
-       MonoDebugHandle *handle;
+               * ((guint32 *) mono_symbol_table->current_data_table) = chunk_size;
+       }
 
-       handle = _mono_debug_get_image (klass->image);
-       if (!handle)
-               return;
+ again:
+       /* First let's check whether there's still enough room in the current_data_table. */
+       if (mono_symbol_table->current_data_table_offset + size + 8 < mono_symbol_table->current_data_table_size) {
+               data = ((guint8 *) mono_symbol_table->current_data_table) + mono_symbol_table->current_data_table_offset;
+               mono_symbol_table->current_data_table_offset += size + 8;
+
+               * ((guint32 *) data) = size;
+               data += 4;
+               * ((guint32 *) data) = type;
+               data += 4;
+               return data;
+       }
 
-       if (handle->_priv->debugger_info)
-               mono_debugger_add_type (handle->_priv->debugger_info, klass);
-}
+       /* Add the current_data_table to the data_tables vector and ... */
+       if (!mono_symbol_table->data_tables) {
+               guint32 tsize = sizeof (gpointer) * DATA_TABLE_PTR_CHUNK_SIZE;
+               mono_symbol_table->data_tables = g_malloc0 (tsize);
+       }
+
+       if (!((mono_symbol_table->num_data_tables + 1) % DATA_TABLE_PTR_CHUNK_SIZE)) {
+               guint32 chunks = (mono_symbol_table->num_data_tables + 1) / DATA_TABLE_PTR_CHUNK_SIZE;
+               guint32 tsize = sizeof (gpointer) * DATA_TABLE_PTR_CHUNK_SIZE * (chunks + 1);
+
+               mono_symbol_table->data_tables = g_realloc (mono_symbol_table->data_tables, tsize);
+       }
 
+       mono_symbol_table->data_tables [mono_symbol_table->num_data_tables++] = mono_symbol_table->current_data_table;
+
+       /* .... allocate a new current_data_table. */
+       mono_symbol_table->current_data_table = g_malloc0 (chunk_size);
+       mono_symbol_table->current_data_table_size = chunk_size;
+       mono_symbol_table->current_data_table_offset = sizeof (gpointer);
+       * ((guint32 *) mono_symbol_table->current_data_table) = chunk_size;
+
+       goto again;
+}
 
 struct LookupMethodData
 {
@@ -209,7 +303,7 @@ lookup_method_func (gpointer key, gpointer value, gpointer user_data)
                return;
 
        if (handle->symfile)
-               data->minfo = mono_debug_find_method (handle, data->method);
+               data->minfo = mono_debug_symfile_lookup_method (handle, data->method);
 }
 
 static MonoDebugMethodInfo *
@@ -227,267 +321,608 @@ _mono_debug_lookup_method (MonoMethod *method)
        return data.minfo;
 }
 
-/*
- * This is called by the JIT to tell the debugging code about a newly compiled
- * wrapper method.
+/**
+ * mono_debug_lookup_method:
+ *
+ * Lookup symbol file information for the method @method.  The returned
+ * `MonoDebugMethodInfo' is a private structure, but it can be passed to
+ * mono_debug_symfile_lookup_location().
  */
-void
-mono_debug_add_wrapper (MonoMethod *method, gpointer wrapper, MonoDomain *domain)
+MonoDebugMethodInfo *
+mono_debug_lookup_method (MonoMethod *method)
 {
-       MonoClass *klass = method->klass;
-       MonoDebugDomainData *domain_data;
-       MonoDebugHandle *handle;
-       MonoDebugMethodJitInfo *jit;
+       MonoDebugMethodInfo *minfo;
 
        mono_debugger_lock ();
+       minfo = _mono_debug_lookup_method (method);
+       mono_debugger_unlock ();
+       return minfo;
+}
+
+static inline void
+write_leb128 (guint32 value, guint8 *ptr, guint8 **rptr)
+{
+       do {
+               guint8 byte = value & 0x7f;
+               value >>= 7;
+               if (value)
+                       byte |= 0x80;
+               *ptr++ = byte;
+       } while (value);
+
+       *rptr = ptr;
+}
 
-       mono_class_init (klass);
+static inline void
+write_sleb128 (gint32 value, guint8 *ptr, guint8 **rptr)
+{
+       gboolean more = 1;
 
-       handle = _mono_debug_get_image (klass->image);
-       if (!handle || !handle->symfile || !handle->symfile->offset_table) {
+       while (more) {
+               guint8 byte = value & 0x7f;
+               value >>= 7;
+
+               if (((value == 0) && ((byte & 0x40) == 0)) || ((value == -1) && (byte & 0x40)))
+                       more = 0;
+               else
+                       byte |= 0x80;
+               *ptr++ = byte;
+       }
+
+       *rptr = ptr;
+}
+
+static void
+write_variable (MonoDebugVarInfo *var, guint8 *ptr, guint8 **rptr)
+{
+       write_leb128 (var->index, ptr, &ptr);
+       write_sleb128 (var->offset, ptr, &ptr);
+       write_leb128 (var->size, ptr, &ptr);
+       write_leb128 (var->begin_scope, ptr, &ptr);
+       write_leb128 (var->end_scope, ptr, &ptr);
+       *rptr = ptr;
+}
+
+static MonoDebugWrapperData *
+mono_debug_add_wrapper (MonoMethod *method, MonoDebugMethodJitInfo *jit)
+{
+       MonoMethodHeader *header;
+       MonoDebugWrapperData *wrapper;
+       char buffer [BUFSIZ];
+       guint8 *ptr, *oldptr;
+       guint32 i, size, total_size, max_size;
+       gint32 last_il_offset = 0, last_native_offset = 0;
+       const unsigned char* il_code;
+       guint32 il_codesize;
+
+       if (!in_the_mono_debugger)
+               return NULL;
+
+       mono_debugger_lock ();
+
+       header = mono_method_get_header (method);
+
+       max_size = 28 * jit->num_line_numbers;
+       if (max_size > BUFSIZ)
+               ptr = oldptr = g_malloc (max_size);
+       else
+               ptr = oldptr = buffer;
+
+       write_leb128 (jit->prologue_end, ptr, &ptr);
+       write_leb128 (jit->epilogue_begin, ptr, &ptr);
+       write_leb128 (jit->num_line_numbers, ptr, &ptr);
+       for (i = 0; i < jit->num_line_numbers; i++) {
+               MonoDebugLineNumberEntry *lne = &jit->line_numbers [i];
+
+               write_sleb128 (lne->il_offset - last_il_offset, ptr, &ptr);
+               write_sleb128 (lne->native_offset - last_native_offset, ptr, &ptr);
+
+               last_il_offset = lne->il_offset;
+               last_native_offset = lne->native_offset;
+       }
+
+       write_leb128 (method->wrapper_type, ptr, &ptr);
+
+       size = ptr - oldptr;
+       g_assert (size < max_size);
+       total_size = size + sizeof (MonoDebugWrapperData);
+
+       if (total_size + 9 >= DATA_TABLE_CHUNK_SIZE) {
+               // FIXME: Maybe we should print a warning here.
+               //        This should only happen for very big methods, for instance
+               //        with more than 40.000 line numbers and more than 5.000
+               //        local variables.
                mono_debugger_unlock ();
-               return;
+               return NULL;
        }
 
-       domain_data = mono_debug_get_domain_data (handle, domain);
-       jit = g_hash_table_lookup (domain_data->_priv->wrapper_info, method);
-       g_assert (jit);
+       wrapper = (MonoDebugWrapperData *) allocate_data_item (MONO_DEBUG_DATA_ITEM_WRAPPER, total_size);
+
+       wrapper->method = method;
+       wrapper->size = total_size;
+       wrapper->code_start = jit->code_start;
+       wrapper->code_size = jit->code_size;
+       wrapper->name = mono_method_full_name (method, TRUE);
+
+       il_code = mono_method_header_get_code (header, &il_codesize, NULL);
+       wrapper->cil_code = mono_disasm_code (
+               NULL, method, il_code, il_code + il_codesize);
+
+       memcpy (&wrapper->data, oldptr, size);
 
-       mono_debugger_add_wrapper (method, jit, wrapper);
+       if (max_size > BUFSIZ)
+               g_free (oldptr);
 
        mono_debugger_unlock ();
+
+       return wrapper;
 }
 
 /*
  * This is called by the JIT to tell the debugging code about a newly
  * compiled method.
  */
-void
+MonoDebugMethodAddress *
 mono_debug_add_method (MonoMethod *method, MonoDebugMethodJitInfo *jit, MonoDomain *domain)
 {
-       MonoClass *klass = method->klass;
-       MonoDebugDomainData *domain_data;
+       MonoDebugMethodAddress *address;
+       char buffer [BUFSIZ];
+       guint8 *ptr, *oldptr;
+       guint32 i, size, total_size, max_size;
+       gint32 last_il_offset = 0, last_native_offset = 0;
        MonoDebugHandle *handle;
        MonoDebugMethodInfo *minfo;
-
-       mono_debugger_lock ();
-
-       mono_class_init (klass);
+       MethodHashEntry *hash;
 
        if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
            (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
            (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
-           (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
-               mono_debugger_unlock ();
-               return;
+           (method->flags & METHOD_ATTRIBUTE_ABSTRACT) ||
+           (method->wrapper_type != MONO_WRAPPER_NONE)) {
+               mono_debug_add_wrapper (method, jit);
+               return NULL;
        }
 
-       handle = _mono_debug_get_image (klass->image);
-       if (!handle || !handle->symfile || !handle->symfile->offset_table) {
-               mono_debugger_unlock ();
-               return;
-       }
+       mono_debugger_lock ();
 
-       domain_data = mono_debug_get_domain_data (handle, domain);
-       if (method->wrapper_type != MONO_WRAPPER_NONE) {
-               g_hash_table_insert (domain_data->_priv->wrapper_info, method, jit);
+       handle = _mono_debug_get_image (method->klass->image);
+       if (!handle || !handle->symfile || !handle->symfile->offset_table) {
+               mono_debug_add_wrapper (method, jit);
                mono_debugger_unlock ();
-               return;
+               return NULL;
        }
 
        minfo = _mono_debug_lookup_method (method);
        if (!minfo) {
                mono_debugger_unlock ();
-               return;
+               return NULL;
        }
 
-       if (domain_data->jit [minfo->index]) {
-               /* FIXME FIXME FIXME
-               // This is bug #48591.
-               */
+       max_size = 24 + 8 * jit->num_line_numbers + 16 * minfo->num_lexical_blocks + 20 * (1 + jit->num_params + jit->num_locals);
+       if (max_size > BUFSIZ)
+               ptr = oldptr = g_malloc (max_size);
+       else
+               ptr = oldptr = buffer;
+
+       write_leb128 (jit->prologue_end, ptr, &ptr);
+       write_leb128 (jit->epilogue_begin, ptr, &ptr);
+
+       write_leb128 (jit->num_line_numbers, ptr, &ptr);
+       for (i = 0; i < jit->num_line_numbers; i++) {
+               MonoDebugLineNumberEntry *lne = &jit->line_numbers [i];
+
+               write_sleb128 (lne->il_offset - last_il_offset, ptr, &ptr);
+               write_sleb128 (lne->native_offset - last_native_offset, ptr, &ptr);
+
+               last_il_offset = lne->il_offset;
+               last_native_offset = lne->native_offset;
+       }
+
+       jit->num_lexical_blocks = minfo->num_lexical_blocks;
+       jit->lexical_blocks = g_new0 (MonoDebugLexicalBlockEntry, jit->num_lexical_blocks);
+       for (i = 0; i < jit->num_lexical_blocks; i ++) {
+               MonoDebugLexicalBlockEntry *jit_lbe = &jit->lexical_blocks [i];
+               MonoSymbolFileLexicalBlockEntry *minfo_lbe = &minfo->lexical_blocks [i];
+               jit_lbe->il_start_offset = read32 (&(minfo_lbe->_start_offset));
+               jit_lbe->native_start_offset = _mono_debug_address_from_il_offset (jit, jit_lbe->il_start_offset);
+
+               jit_lbe->il_end_offset = read32 (&(minfo_lbe->_end_offset));
+               jit_lbe->native_end_offset = _mono_debug_address_from_il_offset (jit, jit_lbe->il_end_offset);
+       }
+
+       last_il_offset = 0;
+       last_native_offset = 0;
+       write_leb128 (jit->num_lexical_blocks, ptr, &ptr);
+       for (i = 0; i < jit->num_lexical_blocks; i++) {
+               MonoDebugLexicalBlockEntry *lbe = &jit->lexical_blocks [i];
+
+               write_sleb128 (lbe->il_start_offset - last_il_offset, ptr, &ptr);
+               write_sleb128 (lbe->native_start_offset - last_native_offset, ptr, &ptr);
+
+               last_il_offset = lbe->il_start_offset;
+               last_native_offset = lbe->native_start_offset;
+
+               write_sleb128 (lbe->il_end_offset - last_il_offset, ptr, &ptr);
+               write_sleb128 (lbe->native_end_offset - last_native_offset, ptr, &ptr);
+
+               last_il_offset = lbe->il_end_offset;
+               last_native_offset = lbe->native_end_offset;
+       }
+
+       *ptr++ = jit->this_var ? 1 : 0;
+       if (jit->this_var)
+               write_variable (jit->this_var, ptr, &ptr);
+
+       write_leb128 (jit->num_params, ptr, &ptr);
+       for (i = 0; i < jit->num_params; i++)
+               write_variable (&jit->params [i], ptr, &ptr);
+
+       write_leb128 (jit->num_locals, ptr, &ptr);
+       for (i = 0; i < jit->num_locals; i++)
+               write_variable (&jit->locals [i], ptr, &ptr);
+
+       size = ptr - oldptr;
+       g_assert (size < max_size);
+       total_size = size + sizeof (MonoDebugMethodAddress);
+
+       if (total_size + 9 >= DATA_TABLE_CHUNK_SIZE) {
+               // FIXME: Maybe we should print a warning here.
+               //        This should only happen for very big methods, for instance
+               //        with more than 40.000 line numbers and more than 5.000
+               //        local variables.
                mono_debugger_unlock ();
-               return;
+               return NULL;
        }
 
-       domain_data->jit [minfo->index] = jit;
+       address = (MonoDebugMethodAddress *) allocate_data_item (MONO_DEBUG_DATA_ITEM_METHOD, total_size);
+
+       address->size = total_size;
+       address->symfile_id = handle->index;
+       address->domain_id = mono_domain_get_id (domain);
+       address->method_id = minfo->index;
+       address->code_start = jit->code_start;
+       address->code_size = jit->code_size;
+       address->wrapper_addr = jit->wrapper_addr;
+
+       memcpy (&address->data, oldptr, size);
+
+       if (max_size > BUFSIZ)
+               g_free (oldptr);
+
+       hash = g_new0 (MethodHashEntry, 1);
+       hash->symfile_id = address->symfile_id;
+       hash->domain_id = address->domain_id;
+       hash->method_id = address->method_id;
 
-       if (handle->_priv->debugger_info && (domain == mono_get_root_domain ()))
-               mono_debugger_add_method (handle->_priv->debugger_info, minfo, jit);
+       g_hash_table_insert (method_hash, hash, address);
 
        mono_debugger_unlock ();
+
+       return address;
 }
 
-static gint32
-il_offset_from_address (MonoDebugMethodJitInfo *jit, guint32 address)
+static inline guint32
+read_leb128 (guint8 *ptr, guint8 **rptr)
 {
-       int i;
+       guint32 result = 0, shift = 0;
 
-       if (!jit || !jit->line_numbers)
-               return -1;
+       while (TRUE) {
+               guint8 byte = *ptr++;
+
+               result |= (byte & 0x7f) << shift;
+               if ((byte & 0x80) == 0)
+                       break;
+               shift += 7;
+       }
+
+       *rptr = ptr;
+       return result;
+}
+
+static inline gint32
+read_sleb128 (guint8 *ptr, guint8 **rptr)
+{
+       gint32 result = 0;
+       guint32 shift = 0;
+
+       while (TRUE) {
+               guint8 byte = *ptr++;
+
+               result |= (byte & 0x7f) << shift;
+               shift += 7;
 
-       for (i = jit->line_numbers->len - 1; i >= 0; i--) {
-               MonoDebugLineNumberEntry lne = g_array_index (
-                       jit->line_numbers, MonoDebugLineNumberEntry, i);
+               if (byte & 0x80)
+                       continue;
 
-               if (lne.address <= address)
-                       return lne.offset;
+               if ((shift < 32) && (byte & 0x40))
+                       result |= - (1 << shift);
+               break;
        }
 
-       return -1;
+       *rptr = ptr;
+       return result;
 }
 
-/*
- * Used by the exception code to get a source location from a machine address.
- *
- * Returns a textual representation of the specified address which is suitable to be displayed to
- * the user (for instance "/home/martin/monocvs/debugger/test/Y.cs:8").
- *
- * If the optional @line_number argument is not NULL, the line number is stored there and just the
- * source file is returned (ie. it'd return "/home/martin/monocvs/debugger/test/Y.cs" and store the
- * line number 8 in the variable pointed to by @line_number).
- */
-gchar *
-mono_debug_source_location_from_address (MonoMethod *method, guint32 address, guint32 *line_number,
-                                        MonoDomain *domain)
+static void
+read_variable (MonoDebugVarInfo *var, guint8 *ptr, guint8 **rptr)
 {
-       MonoDebugMethodInfo *minfo;
-       MonoDebugDomainData *domain_data;
+       var->index = read_leb128 (ptr, &ptr);
+       var->offset = read_sleb128 (ptr, &ptr);
+       var->size = read_leb128 (ptr, &ptr);
+       var->begin_scope = read_leb128 (ptr, &ptr);
+       var->end_scope = read_leb128 (ptr, &ptr);
+       *rptr = ptr;
+}
 
-       mono_loader_lock ();
-       minfo = _mono_debug_lookup_method (method);
-       if (!minfo || !minfo->handle || !minfo->handle->symfile ||
-           !minfo->handle->symfile->offset_table) {
-               mono_loader_unlock ();
-               return NULL;
+MonoDebugMethodJitInfo *
+mono_debug_read_method (MonoDebugMethodAddress *address)
+{
+       MonoDebugMethodJitInfo *jit;
+       guint32 i, il_offset = 0, native_offset = 0;
+       guint8 *ptr;
+
+       if (address->jit)
+               return address->jit;
+
+       jit = address->jit = g_new0 (MonoDebugMethodJitInfo, 1);
+       jit->code_start = address->code_start;
+       jit->code_size = address->code_size;
+       jit->wrapper_addr = address->wrapper_addr;
+
+       ptr = (guint8 *) &address->data;
+
+       jit->prologue_end = read_leb128 (ptr, &ptr);
+       jit->epilogue_begin = read_leb128 (ptr, &ptr);
+
+       jit->num_line_numbers = read_leb128 (ptr, &ptr);
+       jit->line_numbers = g_new0 (MonoDebugLineNumberEntry, jit->num_line_numbers);
+       for (i = 0; i < jit->num_line_numbers; i++) {
+               MonoDebugLineNumberEntry *lne = &jit->line_numbers [i];
+
+               il_offset += read_sleb128 (ptr, &ptr);
+               native_offset += read_sleb128 (ptr, &ptr);
+
+               lne->il_offset = il_offset;
+               lne->native_offset = native_offset;
        }
 
-       domain_data = mono_debug_get_domain_data (minfo->handle, domain);
-       if (!domain_data->jit [minfo->index]) {
-               mono_loader_unlock ();
-               return NULL;
+       il_offset = 0;
+       native_offset = 0;
+       jit->num_lexical_blocks = read_leb128 (ptr, &ptr);
+       jit->lexical_blocks = g_new0 (MonoDebugLexicalBlockEntry, jit->num_lexical_blocks);
+       for (i = 0; i < jit->num_lexical_blocks; i ++) {
+               MonoDebugLexicalBlockEntry *lbe = &jit->lexical_blocks [i];
+
+               il_offset += read_sleb128 (ptr, &ptr);
+               native_offset += read_sleb128 (ptr, &ptr);
+
+               lbe->il_start_offset = il_offset;
+               lbe->native_start_offset = native_offset;
+
+               il_offset += read_sleb128 (ptr, &ptr);
+               native_offset += read_sleb128 (ptr, &ptr);
+
+               lbe->il_end_offset = il_offset;
+               lbe->native_end_offset = native_offset;
        }
 
-       if (minfo->handle && minfo->handle->symfile) {
-               gint32 offset = il_offset_from_address (domain_data->jit [minfo->index], address);
-               char *res = NULL;
-               
-               if (offset >= 0)
-                       res = mono_debug_find_source_location (minfo->handle->symfile, method, offset, line_number);
-               mono_loader_unlock ();
-               return res;
+       if (*ptr++) {
+               jit->this_var = g_new0 (MonoDebugVarInfo, 1);
+               read_variable (jit->this_var, ptr, &ptr);
        }
 
-       mono_loader_unlock ();
-       return NULL;
+       jit->num_params = read_leb128 (ptr, &ptr);
+       jit->params = g_new0 (MonoDebugVarInfo, jit->num_params);
+       for (i = 0; i < jit->num_params; i++)
+               read_variable (&jit->params [i], ptr, &ptr);
+
+       jit->num_locals = read_leb128 (ptr, &ptr);
+       jit->locals = g_new0 (MonoDebugVarInfo, jit->num_locals);
+       for (i = 0; i < jit->num_locals; i++)
+               read_variable (&jit->locals [i], ptr, &ptr);
+
+       return jit;
+}
+
+void
+mono_debug_free_method_jit_info (MonoDebugMethodJitInfo *jit)
+{
+       if (jit->address)
+               jit->address->jit = NULL;
+
+       g_free (jit->line_numbers);
+       g_free (jit->this_var);
+       g_free (jit->params);
+       g_free (jit->locals);
+       g_free (jit);
 }
 
 /*
- * Used by the exception code to get a source location from an IL offset.
- *
- * Returns a textual representation of the specified address which is suitable to be displayed to
- * the user (for instance "/home/martin/monocvs/debugger/test/Y.cs:8").
- *
- * If the optional @line_number argument is not NULL, the line number is stored there and just the
- * source file is returned (ie. it'd return "/home/martin/monocvs/debugger/test/Y.cs" and store the
- * line number 8 in the variable pointed to by @line_number).
+ * This is called via the `mono_debugger_class_init_func' from mono_class_init() each time
+ * a new class is initialized.
  */
-gchar *
-mono_debug_source_location_from_il_offset (MonoMethod *method, guint32 offset, guint32 *line_number)
+static void
+mono_debug_start_add_type (MonoClass *klass)
 {
-       char *res;
-       MonoDebugMethodInfo *minfo;
+       MonoDebugHandle *handle;
 
-       mono_loader_lock ();
-       minfo = _mono_debug_lookup_method (method);
-       if (!minfo || !minfo->handle || !minfo->handle->symfile) {
-               mono_loader_unlock ();
+       handle = _mono_debug_get_image (klass->image);
+       if (!handle)
+               return;
+}
+
+static void
+mono_debug_add_type (MonoClass *klass)
+{
+       MonoDebugHandle *handle;
+       MonoDebugClassEntry *entry;
+       char buffer [BUFSIZ];
+       guint8 *ptr, *oldptr;
+       guint32 size, total_size, max_size;
+       int base_offset = 0;
+
+       handle = _mono_debug_get_image (klass->image);
+       if (!handle)
+               return;
+
+       if (klass->generic_class || klass->rank ||
+           (klass->byval_arg.type == MONO_TYPE_VAR) || (klass->byval_arg.type == MONO_TYPE_MVAR))
+               return;
+
+       max_size = 12 + sizeof (gpointer);
+       if (max_size > BUFSIZ)
+               ptr = oldptr = g_malloc (max_size);
+       else
+               ptr = oldptr = buffer;
+
+       if (klass->valuetype)
+               base_offset = - (int)(sizeof (MonoObject));
+
+       write_leb128 (klass->type_token, ptr, &ptr);
+       write_leb128 (klass->instance_size + base_offset, ptr, &ptr);
+       WRITE_UNALIGNED (gpointer, ptr, klass);
+       ptr += sizeof (gpointer);
+
+       size = ptr - oldptr;
+       g_assert (size < max_size);
+       total_size = size + sizeof (MonoDebugClassEntry);
+
+       g_assert (total_size + 9 < DATA_TABLE_CHUNK_SIZE);
+
+       entry = (MonoDebugClassEntry *) allocate_data_item (MONO_DEBUG_DATA_ITEM_CLASS, total_size);
+
+       entry->size = total_size;
+       entry->symfile_id = handle->index;
+
+       memcpy (&entry->data, oldptr, size);
+
+       if (max_size > BUFSIZ)
+               g_free (oldptr);
+
+       mono_debugger_start_add_type (handle, klass);
+}
+
+static MonoDebugMethodJitInfo *
+find_method (MonoDebugMethodInfo *minfo, MonoDomain *domain)
+{
+       MethodHashEntry lookup;
+       MonoDebugMethodAddress *address;
+
+       lookup.symfile_id = minfo->handle->index;
+       lookup.domain_id = mono_domain_get_id (domain);
+       lookup.method_id = minfo->index;
+
+       address = g_hash_table_lookup (method_hash, &lookup);
+       if (!address)
                return NULL;
-       }
 
-       res = mono_debug_find_source_location (minfo->handle->symfile, method, offset, line_number);
-       mono_loader_unlock ();
-       return res;
+       return mono_debug_read_method (address);
 }
 
-/*
- * Returns the IL offset corresponding to machine address @address which is an offset
- * relative to the beginning of the method @method.
- */
-gint32
-mono_debug_il_offset_from_address (MonoMethod *method, gint32 address, MonoDomain *domain)
+static gint32
+il_offset_from_address (MonoDebugMethodInfo *minfo, MonoDomain *domain, guint32 native_offset)
 {
-       MonoDebugMethodInfo *minfo;
-       MonoDebugDomainData *domain_data;
-       gint32 res;
+       MonoDebugMethodJitInfo *jit;
+       int i;
 
-       if (address < 0)
+       jit = find_method (minfo, domain);
+       if (!jit || !jit->line_numbers)
                return -1;
 
-       mono_loader_lock ();
-       minfo = _mono_debug_lookup_method (method);
-       if (!minfo || !minfo->il_offsets || !minfo->handle || !minfo->handle->symfile ||
-           !minfo->handle->symfile->offset_table) {
-               mono_loader_unlock ();
-               return -1;
-       }
+       for (i = jit->num_line_numbers - 1; i >= 0; i--) {
+               MonoDebugLineNumberEntry lne = jit->line_numbers [i];
 
-       domain_data = mono_debug_get_domain_data (minfo->handle, domain);
+               if (lne.native_offset <= native_offset)
+                       return lne.il_offset;
+       }
 
-       res = il_offset_from_address (domain_data->jit [minfo->index], address);
-       mono_loader_unlock ();
-       return res;
+       return -1;
 }
 
-/*
- * Returns the machine address corresponding to IL offset @il_offset.
- * The returned value is an offset relative to the beginning of the method @method.
+/**
+ * mono_debug_lookup_source_location:
+ * @address: Native offset within the @method's machine code.
+ *
+ * Lookup the source code corresponding to the machine instruction located at
+ * native offset @address within @method.
+ *
+ * The returned `MonoDebugSourceLocation' contains both file / line number
+ * information and the corresponding IL offset.  It must be freed by
+ * mono_debug_free_source_location().
  */
-gint32
-mono_debug_address_from_il_offset (MonoMethod *method, gint32 il_offset, MonoDomain *domain)
+MonoDebugSourceLocation *
+mono_debug_lookup_source_location (MonoMethod *method, guint32 address, MonoDomain *domain)
 {
        MonoDebugMethodInfo *minfo;
-       MonoDebugDomainData *domain_data;
-       gint32 res;
+       MonoDebugSourceLocation *location;
+       gint32 offset;
 
-       if (il_offset < 0)
-               return -1;
+       if (mono_debug_format == MONO_DEBUG_FORMAT_NONE)
+               return NULL;
 
-       mono_loader_lock ();
+       mono_debugger_lock ();
        minfo = _mono_debug_lookup_method (method);
-       if (!minfo || !minfo->il_offsets || !minfo->handle || !minfo->handle->symfile ||
-           !minfo->handle->symfile->offset_table) {
-               mono_loader_unlock ();
-               return -1;
+       if (!minfo || !minfo->handle || !minfo->handle->symfile || !minfo->handle->symfile->offset_table) {
+               mono_debugger_unlock ();
+               return NULL;
        }
 
-       domain_data = mono_debug_get_domain_data (minfo->handle, domain);
+       offset = il_offset_from_address (minfo, domain, address);
+       if (offset < 0) {
+               mono_debugger_unlock ();
+               return NULL;
+       }
 
-       res = _mono_debug_address_from_il_offset (domain_data->jit [minfo->index], il_offset);
-       mono_loader_unlock ();
-       return res;
+       location = mono_debug_symfile_lookup_location (minfo, offset);
+       mono_debugger_unlock ();
+       return location;
 }
 
-static MonoDebugDomainData *
-mono_debug_get_domain_data (MonoDebugHandle *handle, MonoDomain *domain)
+/**
+ * mono_debug_free_source_location:
+ * @location: A `MonoDebugSourceLocation'.
+ *
+ * Frees the @location.
+ */
+void
+mono_debug_free_source_location (MonoDebugSourceLocation *location)
 {
-       MonoDebugDomainData *data;
-       int domain_id = mono_domain_get_id (domain);
+       if (location) {
+               g_free (location->source_file);
+               g_free (location);
+       }
+}
 
-       /* We checked this earlier. */
-       g_assert (handle->symfile);
+/**
+ * mono_debug_print_stack_frame:
+ * @native_offset: Native offset within the @method's machine code.
+ *
+ * Conventient wrapper around mono_debug_lookup_source_location() which can be
+ * used if you only want to use the location to print a stack frame.
+ */
+gchar *
+mono_debug_print_stack_frame (MonoMethod *method, guint32 native_offset, MonoDomain *domain)
+{
+       MonoDebugSourceLocation *location;
+       gchar *fname, *ptr, *res;
+
+       fname = mono_method_full_name (method, TRUE);
+       for (ptr = fname; *ptr; ptr++) {
+               if (*ptr == ':') *ptr = '.';
+       }
 
-       for (data = handle->_priv->domain_table; data; data = data->_priv->next)
-               if (data->domain_id == domain_id)
-                       return data;
+       location = mono_debug_lookup_source_location (method, native_offset, domain);
 
-       data = g_new0 (MonoDebugDomainData, 1);
-       data->domain_id = domain_id;
-       data->jit = g_new0 (MonoDebugMethodJitInfo *, read32(&(handle->symfile->offset_table->_method_count)) + 1);
+       if (!location) {
+               res = g_strdup_printf ("at %s <0x%05x>", fname, native_offset);
+               g_free (fname);
+               return res;
+       }
 
-       data->_priv = g_new0 (MonoDebugDomainDataPriv, 1);
-       data->_priv->next = handle->_priv->domain_table;
-       data->_priv->wrapper_info = g_hash_table_new (g_direct_hash, g_direct_equal);
-       handle->_priv->domain_table = data;
+       res = g_strdup_printf ("at %s [0x%05x] in %s:%d", fname, location->il_offset,
+                              location->source_file, location->row);
 
-       return data;
+       g_free (fname);
+       mono_debug_free_source_location (location);
+       return res;
 }