/*
- * debugger-agent.c: Debugger back-end module
+ * debugger-agent.c: Soft Debugger back-end module
*
* Author:
* Zoltan Varga (vargaz@gmail.com)
*
- * (C) 2009 Novell, Inc.
+ * Copyright 2009-2010 Novell, Inc.
*/
#include <config.h>
#include <mono/metadata/gc-internal.h>
#include <mono/metadata/threads-types.h>
#include <mono/metadata/socket-io.h>
+#include <mono/metadata/assembly.h>
#include <mono/utils/mono-semaphore.h>
#include "debugger-agent.h"
#include "mini.h"
GSList *onthrow;
int timeout;
char *launch;
+ gboolean embedding;
} AgentConfig;
typedef struct
#define HEADER_LENGTH 11
#define MAJOR_VERSION 2
-#define MINOR_VERSION 1
+#define MINOR_VERSION 2
typedef enum {
CMD_SET_VM = 1,
static int log_level;
+static gboolean embedding;
+
static FILE *log_file;
/* Classes whose class load event has been sent */
static void runtime_shutdown (MonoProfiler *prof);
-static void thread_startup (MonoProfiler *prof, intptr_t tid);
+static void thread_startup (MonoProfiler *prof, uintptr_t tid);
-static void thread_end (MonoProfiler *prof, intptr_t tid);
+static void thread_end (MonoProfiler *prof, uintptr_t tid);
static void appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result);
static void clear_event_requests_for_assembly (MonoAssembly *assembly);
+static void clear_breakpoints_for_domain (MonoDomain *domain);
+
+static void clear_types_for_assembly (MonoAssembly *assembly);
+
/* Submodule init/cleanup */
static void breakpoints_init (void);
static void breakpoints_cleanup (void);
agent_config.timeout = atoi (arg + 8);
} else if (strncmp (arg, "launch=", 7) == 0) {
agent_config.launch = g_strdup (arg + 7);
+ } else if (strncmp (arg, "embedding=", 10) == 0) {
+ agent_config.embedding = atoi (arg + 10) == 1;
} else {
print_usage ();
exit (1);
debugger_tls_id = TlsAlloc ();
thread_to_tls = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC);
- MONO_GC_REGISTER_ROOT (thread_to_tls);
+ MONO_GC_REGISTER_ROOT_FIXED (thread_to_tls);
tid_to_thread = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC);
- MONO_GC_REGISTER_ROOT (tid_to_thread);
+ MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread);
tid_to_thread_obj = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC);
- MONO_GC_REGISTER_ROOT (tid_to_thread_obj);
+ MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread_obj);
loaded_classes = g_hash_table_new (mono_aligned_addr_hash, NULL);
pending_assembly_loads = g_ptr_array_new ();
log_level = agent_config.log_level;
+ embedding = agent_config.embedding;
+
if (agent_config.log_file) {
log_file = fopen (agent_config.log_file, "w+");
if (!log_file) {
//WaitForSingleObject (debugger_thread_handle, INFINITE);
if (GetCurrentThreadId () != debugger_thread_id) {
mono_mutex_lock (&debugger_thread_exited_mutex);
- if (!debugger_thread_exited) {
+ while (!debugger_thread_exited) {
#ifdef HOST_WIN32
if (WAIT_TIMEOUT == WaitForSingleObject(debugger_thread_exited_cond, 0)) {
mono_mutex_unlock (&debugger_thread_exited_mutex);
get_objref (MonoObject *obj)
{
ObjRef *ref;
+ GSList *reflist = NULL, *l;
+ int hash = 0;
if (obj == NULL)
return 0;
-#ifdef HAVE_SGEN_GC
- NOT_IMPLEMENTED;
-#endif
-
- /* Use a hash table with masked pointers to internalize object references */
- /* FIXME: This can grow indefinitely */
mono_loader_lock ();
if (!obj_to_objref)
obj_to_objref = g_hash_table_new (NULL, NULL);
+
+ /* FIXME: The tables can grow indefinitely */
- ref = g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)));
- /* ref might refer to a different object with the same addr which was GCd */
- if (ref && mono_gchandle_get_target (ref->handle) == obj) {
- mono_loader_unlock ();
- return ref;
+ if (mono_gc_is_moving ()) {
+ /*
+ * Objects can move, so use a hash table mapping hash codes to lists of
+ * ObjRef structures.
+ */
+ hash = mono_object_hash (obj);
+
+ reflist = g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (hash));
+ for (l = reflist; l; l = l->next) {
+ ref = l->data;
+ if (ref && mono_gchandle_get_target (ref->handle) == obj) {
+ mono_loader_unlock ();
+ return ref;
+ }
+ }
+ } else {
+ /* Use a hash table with masked pointers to internalize object references */
+ ref = g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)));
+ /* ref might refer to a different object with the same addr which was GCd */
+ if (ref && mono_gchandle_get_target (ref->handle) == obj) {
+ mono_loader_unlock ();
+ return ref;
+ }
}
ref = g_new0 (ObjRef, 1);
ref->handle = mono_gchandle_new_weakref (obj, FALSE);
g_hash_table_insert (objrefs, GINT_TO_POINTER (ref->id), ref);
- g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref);
+
+ if (mono_gc_is_moving ()) {
+ reflist = g_slist_append (reflist, ref);
+ g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (hash), reflist);
+ } else {
+ g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref);
+ }
mono_loader_unlock ();
}
static void
-thread_startup (MonoProfiler *prof, intptr_t tid)
+thread_startup (MonoProfiler *prof, uintptr_t tid)
{
MonoInternalThread *thread = mono_thread_internal_current ();
MonoInternalThread *old_thread;
// FIXME: Free this somewhere
tls = g_new0 (DebuggerTlsData, 1);
tls->resume_event = CreateEvent (NULL, FALSE, FALSE, NULL);
- MONO_GC_REGISTER_ROOT (tls->thread);
+ MONO_GC_REGISTER_ROOT_SINGLE (tls->thread);
tls->thread = thread;
TlsSetValue (debugger_tls_id, tls);
}
static void
-thread_end (MonoProfiler *prof, intptr_t tid)
+thread_end (MonoProfiler *prof, uintptr_t tid)
{
MonoInternalThread *thread;
DebuggerTlsData *tls = NULL;
{
/* Invalidate each thread's frame stack */
mono_g_hash_table_foreach (thread_to_tls, invalidate_each_thread, NULL);
+ clear_breakpoints_for_domain (domain);
process_profiler_event (EVENT_KIND_APPDOMAIN_UNLOAD, domain);
}
process_profiler_event (EVENT_KIND_ASSEMBLY_UNLOAD, assembly);
clear_event_requests_for_assembly (assembly);
+ clear_types_for_assembly (assembly);
}
static void
gpointer stackptr = __builtin_frame_address (1);
#endif
- if (ss_req == NULL || stackptr != ss_invoke_addr || ss_req->thread != mono_thread_internal_current ())
+ if (!embedding || ss_req == NULL || stackptr != ss_invoke_addr || ss_req->thread != mono_thread_internal_current ())
return;
/*
long il_offset, native_offset;
guint8 *ip;
MonoJitInfo *ji;
+ MonoDomain *domain;
} BreakpointInstance;
/*
bp_locs = g_hash_table_new (NULL, NULL);
}
-static void
-breakpoints_cleanup (void)
-{
- int i;
-
- mono_loader_lock ();
-
- for (i = 0; i < breakpoints->len; ++i)
- g_free (g_ptr_array_index (breakpoints, i));
-
- g_ptr_array_free (breakpoints, TRUE);
- g_hash_table_destroy (bp_locs);
-
- mono_loader_unlock ();
-}
-
/*
* insert_breakpoint:
*
* JI.
*/
static void
-insert_breakpoint (MonoSeqPointInfo *seq_points, MonoJitInfo *ji, MonoBreakpoint *bp)
+insert_breakpoint (MonoSeqPointInfo *seq_points, MonoDomain *domain, MonoJitInfo *ji, MonoBreakpoint *bp)
{
int i, count;
gint32 il_offset = -1, native_offset;
inst->native_offset = native_offset;
inst->ip = (guint8*)ji->code_start + native_offset;
inst->ji = ji;
+ inst->domain = domain;
mono_loader_lock ();
continue;
g_assert (seq_points);
- insert_breakpoint (seq_points, ji, bp);
+ insert_breakpoint (seq_points, domain, ji, bp);
}
}
}
g_assert (code);
- insert_breakpoint (seq_points, ji, bp);
+ insert_breakpoint (seq_points, domain, ji, bp);
}
typedef struct
g_free (bp);
}
+static void
+breakpoints_cleanup (void)
+{
+ int i;
+
+ mono_loader_lock ();
+ i = 0;
+ while (i < event_requests->len) {
+ EventRequest *req = g_ptr_array_index (event_requests, i);
+
+ if (req->event_kind == EVENT_KIND_BREAKPOINT) {
+ clear_breakpoint (req->info);
+ g_ptr_array_remove_index_fast (event_requests, i);
+ g_free (req);
+ } else {
+ i ++;
+ }
+ }
+
+ for (i = 0; i < breakpoints->len; ++i)
+ g_free (g_ptr_array_index (breakpoints, i));
+
+ g_ptr_array_free (breakpoints, TRUE);
+ g_hash_table_destroy (bp_locs);
+
+ breakpoints = NULL;
+ bp_locs = NULL;
+
+ mono_loader_unlock ();
+}
+
+/*
+ * clear_breakpoints_for_domain:
+ *
+ * Clear breakpoint instances which reference DOMAIN.
+ */
+static void
+clear_breakpoints_for_domain (MonoDomain *domain)
+{
+ int i, j;
+
+ /* This could be called after shutdown */
+ if (!breakpoints)
+ return;
+
+ mono_loader_lock ();
+ for (i = 0; i < breakpoints->len; ++i) {
+ MonoBreakpoint *bp = g_ptr_array_index (breakpoints, i);
+
+ j = 0;
+ while (j < bp->children->len) {
+ BreakpointInstance *inst = g_ptr_array_index (bp->children, j);
+
+ if (inst->domain == domain) {
+ remove_breakpoint (inst);
+
+ g_free (inst);
+
+ g_ptr_array_remove_index_fast (bp->children, j);
+ } else {
+ j ++;
+ }
+ }
+ }
+ mono_loader_unlock ();
+}
+
static gboolean
breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly)
{
mono_loader_unlock ();
}
+/*
+ * type_comes_from_assembly:
+ *
+ * GHRFunc that returns TRUE if klass comes from assembly
+ */
+static gboolean
+type_comes_from_assembly (gpointer klass, gpointer also_klass, gpointer assembly)
+{
+ return (mono_class_get_image ((MonoClass*)klass) == mono_assembly_get_image ((MonoAssembly*)assembly));
+}
+
+/*
+ * clear_types_for_assembly:
+ *
+ * Clears types from loaded_classes for a given assembly
+ */
+static void
+clear_types_for_assembly (MonoAssembly *assembly)
+{
+ mono_loader_lock ();
+ g_hash_table_foreach_remove (loaded_classes, type_comes_from_assembly, assembly);
+ mono_loader_unlock ();
+}
+
static void
add_thread (gpointer key, gpointer value, gpointer user_data)
{
}
case CMD_METHOD_GET_LOCALS_INFO: {
int i, j, num_locals;
- char **local_names;
- int *local_indexes;
+ MonoDebugLocalsInfo *locals;
header = mono_method_get_header (method);
g_assert (header);
buffer_add_typeid (buf, domain, mono_class_from_mono_type (header->locals [i]));
/* Names */
- num_locals = mono_debug_lookup_locals (method, &local_names, &local_indexes);
+ locals = mono_debug_lookup_locals (method);
+ if (locals)
+ num_locals = locals->num_locals;
+ else
+ num_locals = 0;
for (i = 0; i < header->num_locals; ++i) {
for (j = 0; j < num_locals; ++j)
- if (local_indexes [j] == i)
+ if (locals->locals [j].index == i)
break;
if (j < num_locals)
- buffer_add_string (buf, local_names [j]);
+ buffer_add_string (buf, locals->locals [j].name);
else
buffer_add_string (buf, "");
}
- g_free (local_names);
- g_free (local_indexes);
- /* Live ranges */
- /* FIXME: This works because we set debug_options.mdb_optimizations */
+ /* Scopes */
for (i = 0; i < header->num_locals; ++i) {
- buffer_add_int (buf, 0);
- buffer_add_int (buf, header->code_size);
+ for (j = 0; j < num_locals; ++j)
+ if (locals->locals [j].index == i)
+ break;
+ if (j < num_locals && locals->locals [j].block) {
+ buffer_add_int (buf, locals->locals [j].block->start_offset);
+ buffer_add_int (buf, locals->locals [j].block->end_offset);
+ } else {
+ buffer_add_int (buf, 0);
+ buffer_add_int (buf, header->code_size);
+ }
}
mono_metadata_free_mh (header);
+ if (locals)
+ mono_debug_symfile_free_locals (locals);
+
break;
}
case CMD_METHOD_GET_INFO: