#include <string.h>
#define SYMFILE_TABLE_CHUNK_SIZE 16
+
#define DATA_TABLE_PTR_CHUNK_SIZE 256
-#define DATA_TABLE_CHUNK_SIZE 32768
+#define DATA_TABLE_CHUNK_SIZE 4096
#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
(*(type *)(addr) = (val))
#endif
+typedef enum {
+ MONO_DEBUG_DATA_ITEM_UNKNOWN = 0,
+ MONO_DEBUG_DATA_ITEM_CLASS,
+ MONO_DEBUG_DATA_ITEM_METHOD
+} MonoDebugDataItemType;
+
+struct _MonoDebugDataTable {
+ guint32 total_size;
+ guint32 allocated_size;
+ guint32 current_offset;
+ guint32 dummy;
+ MonoDebugDataTable *next;
+ guint8 data [MONO_ZERO_LEN_ARRAY];
+};
+
+typedef struct {
+ const gchar *method_name;
+ const gchar *cil_code;
+ guint32 wrapper_type;
+} MonoDebugWrapperData;
+
+typedef struct {
+ guint32 size;
+ guint32 symfile_id;
+ guint32 domain_id;
+ guint32 method_id;
+ MonoDebugWrapperData *wrapper_data;
+ MonoMethod *method;
+ GSList *address_list;
+} MonoDebugMethodHeader;
+
+struct _MonoDebugMethodAddress {
+ MonoDebugMethodHeader header;
+ const guint8 *code_start;
+ const guint8 *wrapper_addr;
+ guint32 code_size;
+ guint8 data [MONO_ZERO_LEN_ARRAY];
+};
+
+typedef struct {
+ MonoMethod *method;
+ guint32 domain_id;
+} MonoDebugMethodHash;
+
MonoSymbolTable *mono_symbol_table = NULL;
+MonoDebugDataTable *mono_debug_data_table = NULL;
MonoDebugFormat mono_debug_format = MONO_DEBUG_FORMAT_NONE;
+gint32 mono_debug_debugger_version = 2;
static gboolean in_the_mono_debugger = FALSE;
static gboolean mono_debug_initialized = FALSE;
GHashTable *mono_debug_handles = NULL;
+static MonoDebugDataTable *current_data_table = NULL;
+
+static GHashTable *method_address_hash = NULL;
static GHashTable *method_hash = NULL;
static MonoDebugHandle *mono_debug_open_image (MonoImage *image, const guint8 *raw_contents, int size);
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);
+ const MonoDebugMethodHash *hash = (const MonoDebugMethodHash *) data;
+ return hash->method->token | (hash->domain_id << 16);
}
static gint
method_hash_equal (gconstpointer ka, gconstpointer kb)
{
- const MethodHashEntry *a = (const MethodHashEntry *) ka;
- const MethodHashEntry *b = (const MethodHashEntry *) kb;
+ const MonoDebugMethodHash *a = (const MonoDebugMethodHash *) ka;
+ const MonoDebugMethodHash *b = (const MonoDebugMethodHash *) kb;
- if ((a->symfile_id != b->symfile_id) || (a->method_id != b->method_id) || (a->domain_id != b->domain_id))
+ if ((a->method != b->method) || (a->domain_id != b->domain_id))
return 0;
return 1;
}
+
/*
* Initialize debugging support.
*
mono_symbol_table->version = MONO_DEBUGGER_VERSION;
mono_symbol_table->total_size = sizeof (MonoSymbolTable);
+ mono_debug_data_table = g_malloc0 (sizeof (MonoDebugDataTable) + DATA_TABLE_CHUNK_SIZE);
+ mono_debug_data_table->total_size = DATA_TABLE_CHUNK_SIZE;
+
+ current_data_table = mono_debug_data_table;
+
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);
+ method_address_hash = g_hash_table_new (method_hash_hash, method_hash_equal);
+ method_hash = g_hash_table_new (NULL, NULL);
mono_debugger_start_class_init_func = mono_debug_start_add_type;
mono_debugger_class_init_func = mono_debug_add_type;
void
mono_debug_cleanup (void)
{
+ MonoDebugDataTable *table, *next_table;
+
mono_debugger_cleanup ();
if (mono_debug_handles)
g_hash_table_destroy (mono_debug_handles);
mono_debug_handles = NULL;
+
+ table = mono_debug_data_table;
+ while (table) {
+ next_table = table->next;
+ g_free (table);
+ table = next_table;
+ }
+
+ mono_debug_data_table = current_data_table = NULL;
}
static MonoDebugHandle *
mono_debugger_unlock ();
}
-/*
- * 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.
- */
static guint8 *
allocate_data_item (MonoDebugDataItemType type, guint32 size)
{
guint32 chunk_size;
guint8 *data;
- g_assert (mono_symbol_table);
-
size = ALIGN_TO (size, sizeof (gpointer));
if (size + 16 < DATA_TABLE_CHUNK_SIZE)
else
chunk_size = size + 16;
- /* 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);
+ g_assert (current_data_table);
+ g_assert (current_data_table->current_offset == current_data_table->allocated_size);
- * ((guint32 *) mono_symbol_table->current_data_table) = chunk_size;
- }
+ if (current_data_table->allocated_size + size + 8 >= current_data_table->total_size) {
+ MonoDebugDataTable *new_table;
- 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;
- }
+ new_table = g_malloc0 (sizeof (MonoDebugDataTable) + chunk_size);
+ new_table->total_size = chunk_size;
- /* 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);
+ current_data_table->next = new_table;
+ current_data_table = new_table;
}
- 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);
- }
+ data = ¤t_data_table->data [current_data_table->allocated_size];
+ current_data_table->allocated_size += size + 8;
- mono_symbol_table->data_tables [mono_symbol_table->num_data_tables++] = mono_symbol_table->current_data_table;
+ * ((guint32 *) data) = size;
+ data += 4;
+ * ((guint32 *) data) = type;
+ data += 4;
+ return data;
+}
- /* .... 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;
+static void
+write_data_item (const guint8 *data)
+{
+ guint32 size = * ((guint32 *) (data - 8));
- goto again;
+ g_assert (current_data_table->current_offset + size + 8 == current_data_table->allocated_size);
+ current_data_table->current_offset = current_data_table->allocated_size;
}
struct LookupMethodData
*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 NULL;
- }
-
- 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);
-
- 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.
- */
MonoDebugMethodAddress *
mono_debug_add_method (MonoMethod *method, MonoDebugMethodJitInfo *jit, MonoDomain *domain)
{
+ MonoMethod *declaring;
+ MonoDebugMethodHash *hash;
+ MonoDebugMethodHeader *header;
MonoDebugMethodAddress *address;
- char buffer [BUFSIZ];
+ MonoDebugMethodInfo *minfo;
+ MonoDebugHandle *handle;
+ guint8 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;
- MethodHashEntry *hash;
+ gboolean is_wrapper = FALSE;
+
+ mono_debugger_lock ();
- if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
+ handle = _mono_debug_get_image (method->klass->image);
+ minfo = _mono_debug_lookup_method (method);
+
+ if (!minfo || (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
(method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
(method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
(method->flags & METHOD_ATTRIBUTE_ABSTRACT) ||
(method->wrapper_type != MONO_WRAPPER_NONE)) {
- mono_debug_add_wrapper (method, jit);
- return NULL;
+ is_wrapper = TRUE;
}
- mono_debugger_lock ();
-
- 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 NULL;
- }
+ jit->num_lexical_blocks = minfo ? minfo->num_lexical_blocks : 0;
- minfo = _mono_debug_lookup_method (method);
- if (!minfo) {
- mono_debugger_unlock ();
- return NULL;
- }
+ max_size = 24 + 8 * jit->num_line_numbers + 16 * jit->num_lexical_blocks +
+ 20 * (1 + jit->num_params + jit->num_locals);
- 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
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];
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 NULL;
- }
-
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->header.size = total_size;
+ address->header.symfile_id = handle ? handle->index : 0;
+ address->header.domain_id = mono_domain_get_id (domain);
+ address->header.method_id = is_wrapper ? 0 : minfo->index;
+ address->header.method = method;
+
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;
+ declaring = method->is_inflated ? ((MonoMethodInflated *) method)->declaring : method;
+ header = g_hash_table_lookup (method_hash, declaring);
- g_hash_table_insert (method_hash, hash, address);
+ if (!header) {
+ header = &address->header;
+ g_hash_table_insert (method_hash, declaring, header);
- mono_debugger_unlock ();
+ if (is_wrapper) {
+ const unsigned char* il_code;
+ MonoMethodHeader *mheader;
+ MonoDebugWrapperData *wrapper;
+ guint32 il_codesize;
+
+ mheader = mono_method_get_header (declaring);
+ il_code = mono_method_header_get_code (mheader, &il_codesize, NULL);
+ header->wrapper_data = wrapper = g_new0 (MonoDebugWrapperData, 1);
+
+ wrapper->wrapper_type = method->wrapper_type;
+ wrapper->method_name = mono_method_full_name (declaring, TRUE);
+ wrapper->cil_code = mono_disasm_code (
+ NULL, declaring, il_code, il_code + il_codesize);
+ }
+ } else {
+ address->header.wrapper_data = header->wrapper_data;
+ header->address_list = g_slist_prepend (header->address_list, address);
+ }
+
+ hash = g_new0 (MonoDebugMethodHash, 1);
+ hash->method = method;
+ hash->domain_id = mono_domain_get_id (domain);
+
+ g_hash_table_insert (method_address_hash, hash, address);
+
+ write_data_item ((guint8 *) address);
+
+ mono_debugger_unlock ();
return address;
}
*rptr = ptr;
}
-MonoDebugMethodJitInfo *
+static 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 = g_new0 (MonoDebugMethodJitInfo, 1);
jit->code_start = address->code_start;
jit->code_size = address->code_size;
jit->wrapper_addr = address->wrapper_addr;
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);
-}
-
/*
* This is called via the `mono_debugger_class_init_func' from mono_class_init() each time
* a new class is initialized.
{
MonoDebugHandle *handle;
MonoDebugClassEntry *entry;
- char buffer [BUFSIZ];
+ guint8 buffer [BUFSIZ];
guint8 *ptr, *oldptr;
guint32 size, total_size, max_size;
int base_offset = 0;
(klass->byval_arg.type == MONO_TYPE_VAR) || (klass->byval_arg.type == MONO_TYPE_MVAR))
return;
+ mono_debugger_lock ();
+
max_size = 12 + sizeof (gpointer);
if (max_size > BUFSIZ)
ptr = oldptr = g_malloc (max_size);
memcpy (&entry->data, oldptr, size);
+ if (mono_debug_debugger_version >= 2)
+ write_data_item ((guint8 *) entry);
+
if (max_size > BUFSIZ)
g_free (oldptr);
- mono_debugger_start_add_type (handle, klass);
+ mono_debugger_add_type (handle, klass);
+
+ mono_debugger_unlock ();
}
static MonoDebugMethodJitInfo *
-find_method (MonoDebugMethodInfo *minfo, MonoDomain *domain)
+find_method (MonoMethod *method, MonoDomain *domain)
{
- MethodHashEntry lookup;
+ MonoDebugMethodHash lookup;
MonoDebugMethodAddress *address;
- lookup.symfile_id = minfo->handle->index;
+ lookup.method = method;
lookup.domain_id = mono_domain_get_id (domain);
- lookup.method_id = minfo->index;
- address = g_hash_table_lookup (method_hash, &lookup);
+ address = g_hash_table_lookup (method_address_hash, &lookup);
if (!address)
return NULL;
}
MonoDebugMethodJitInfo *
-mono_debug_find_method (MonoDebugMethodInfo *minfo, MonoDomain *domain)
+mono_debug_find_method (MonoMethod *method, MonoDomain *domain)
{
MonoDebugMethodJitInfo *res;
mono_debugger_lock ();
- res = find_method (minfo, domain);
+ res = find_method (method, domain);
mono_debugger_unlock ();
return res;
}
+MonoDebugMethodAddressList *
+mono_debug_lookup_method_addresses (MonoMethod *method)
+{
+ MonoDebugMethodAddressList *info;
+ MonoDebugMethodHeader *header;
+ MonoMethod *declaring;
+ int count, size;
+ GSList *list;
+ guint8 *ptr;
+
+ g_assert (mono_debug_debugger_version == 2);
+
+ mono_debugger_lock ();
+
+ declaring = method->is_inflated ? ((MonoMethodInflated *) method)->declaring : method;
+ header = g_hash_table_lookup (method_hash, declaring);
+
+ if (!header) {
+ mono_debugger_unlock ();
+ return NULL;
+ }
+
+ count = g_slist_length (header->address_list) + 1;
+ size = sizeof (MonoDebugMethodAddressList) + count * sizeof (gpointer);
+
+ info = g_malloc0 (size);
+ info->size = size;
+ info->count = count;
+
+ ptr = info->data;
+
+ WRITE_UNALIGNED (gpointer, ptr, header);
+ ptr += sizeof (gpointer);
+
+ for (list = header->address_list; list; list = list->next) {
+ WRITE_UNALIGNED (gpointer, ptr, list->data);
+ ptr += sizeof (gpointer);
+ }
+
+ mono_debugger_unlock ();
+ return info;
+}
+
static gint32
-il_offset_from_address (MonoDebugMethodInfo *minfo, MonoDomain *domain, guint32 native_offset)
+il_offset_from_address (MonoMethod *method, MonoDomain *domain, guint32 native_offset)
{
MonoDebugMethodJitInfo *jit;
int i;
- jit = find_method (minfo, domain);
+ jit = find_method (method, domain);
if (!jit || !jit->line_numbers)
return -1;
return NULL;
}
- offset = il_offset_from_address (minfo, domain, address);
+ offset = il_offset_from_address (method, domain, address);
if (offset < 0) {
mono_debugger_unlock ();
return NULL;