-/*
- * debugger-agent.c: Soft Debugger back-end module
+/**
+ * \file
+ * Soft Debugger back-end module
*
* Author:
* Zoltan Varga (vargaz@gmail.com)
#endif
#include <mono/metadata/mono-debug.h>
-#include <mono/metadata/mono-debug-debugger.h>
-#include <mono/metadata/debug-mono-symfile.h>
+#include <mono/metadata/debug-internals.h>
#include <mono/metadata/gc-internals.h>
#include <mono/metadata/environment.h>
#include <mono/metadata/threads-types.h>
-#include <mono/metadata/threadpool-ms.h>
-#include <mono/metadata/socket-io.h>
+#include <mono/metadata/threadpool.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/runtime.h>
#include <mono/metadata/verify-internals.h>
#include <mono/metadata/reflection-internals.h>
+#include <mono/metadata/w32socket.h>
#include <mono/utils/mono-coop-mutex.h>
#include <mono/utils/mono-coop-semaphore.h>
#include <mono/utils/mono-error-internals.h>
#include "debugger-agent.h"
#include "mini.h"
#include "seq-points.h"
+#include <mono/utils/w32api.h>
/*
* On iOS we can't use System.Environment.Exit () as it will do the wrong
#define HEADER_LENGTH 11
#define MAJOR_VERSION 2
-#define MINOR_VERSION 44
+#define MINOR_VERSION 45
typedef enum {
CMD_SET_VM = 1,
CMD_ASSEMBLY_GET_MANIFEST_MODULE = 3,
CMD_ASSEMBLY_GET_OBJECT = 4,
CMD_ASSEMBLY_GET_TYPE = 5,
- CMD_ASSEMBLY_GET_NAME = 6
+ CMD_ASSEMBLY_GET_NAME = 6,
+ CMD_ASSEMBLY_GET_DOMAIN = 7
} CmdAssembly;
typedef enum {
GSList *bps;
/* The number of frames at the start of a step-over */
int nframes;
+ /* If set, don't stop in methods that are not part of user assemblies */
+ MonoAssembly** user_assemblies;
+ /* Used to distinguish stepping breakpoint hits in parallel tasks executions */
+ int async_id;
+ /* Used to know if we are in process of async step-out and distishing from exception breakpoints */
+ MonoMethod* async_stepout_method;
} SingleStepReq;
/*
static inline gboolean
is_debugger_thread (void)
{
- return mono_native_thread_id_equals (mono_native_thread_id_get (), debugger_thread_id);
+ MonoInternalThread *internal;
+
+ internal = mono_thread_internal_current ();
+ if (!internal)
+ return FALSE;
+
+ return internal->debugger_thread;
}
static int
if (pos == NULL || pos == address)
return 1;
- *host = (char *)g_malloc (pos - address + 1);
- strncpy (*host, address, pos - address);
- (*host) [pos - address] = '\0';
+ size_t len = pos - address;
+ *host = (char *)g_malloc (len + 1);
+ memcpy (*host, address, len);
+ (*host) [len] = '\0';
*port = atoi (pos + 1);
char **args, **ptr;
char *host;
int port;
- const char *extra;
+ char *extra;
#ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED
fprintf (stderr, "--debugger-agent is not supported on this platform.\n");
#endif
extra = g_getenv ("MONO_SDB_ENV_OPTIONS");
- if (extra)
+ if (extra) {
options = g_strdup_printf ("%s,%s", options, extra);
+ g_free (extra);
+ }
agent_config.enabled = TRUE;
agent_config.suspend = TRUE;
/* Needed by the hash_table_new_type () call below */
mono_gc_base_init ();
- thread_to_tls = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, "thread-to-tls table");
- MONO_GC_REGISTER_ROOT_FIXED (thread_to_tls, MONO_ROOT_SOURCE_DEBUGGER, "thread-to-tls table");
+ thread_to_tls = mono_g_hash_table_new_type ((GHashFunc)mono_object_hash, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, "thread-to-tls table");
tid_to_thread = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread table");
- MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread table");
tid_to_thread_obj = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread object table");
- MONO_GC_REGISTER_ROOT_FIXED (tid_to_thread_obj, MONO_ROOT_SOURCE_DEBUGGER, "tid-to-thread object table");
pending_assembly_loads = g_ptr_array_new ();
domains = g_hash_table_new (mono_aligned_addr_hash, NULL);
static void
start_debugger_thread (void)
{
- debugger_thread_handle = mono_threads_create_thread (debugger_thread, NULL, NULL, NULL);
+ MonoError error;
+ MonoInternalThread *thread;
+
+ thread = mono_thread_create_internal (mono_get_root_domain (), debugger_thread, NULL, MONO_THREAD_CREATE_FLAGS_DEBUGGER, &error);
+ mono_error_assert_ok (&error);
+
+ debugger_thread_handle = mono_threads_open_thread_handle (thread->handle);
g_assert (debugger_thread_handle);
}
{
objrefs = g_hash_table_new_full (NULL, NULL, NULL, free_objref);
obj_to_objref = g_hash_table_new (NULL, NULL);
- suspended_objs = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, "suspended objects table");
- MONO_GC_REGISTER_ROOT_FIXED (suspended_objs, MONO_ROOT_SOURCE_DEBUGGER, "suspended objects table");
+ suspended_objs = mono_g_hash_table_new_type ((GHashFunc)mono_object_hash, NULL, MONO_HASH_KEY_GC, MONO_ROOT_SOURCE_DEBUGGER, "suspended objects table");
}
static void
res = (Id *)g_ptr_array_index (ids [type], GPOINTER_TO_INT (id - 1));
dbg_unlock ();
- if (res->domain == NULL) {
+ if (res->domain == NULL || res->domain->state == MONO_APPDOMAIN_UNLOADED) {
DEBUG_PRINTF (1, "ERR_UNLOADED, id=%d, type=%d.\n", id, type);
*err = ERR_UNLOADED;
return NULL;
DebuggerTlsData *tls = (DebuggerTlsData *)value;
MonoNativeThreadId tid = MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid);
- if (mono_native_thread_id_equals (mono_native_thread_id_get (), tid) || tls->terminated)
+ if (mono_thread_internal_is_current (thread) || tls->terminated)
return;
DEBUG_PRINTF (1, "[%p] Interrupting %p...\n", (gpointer) (gsize) mono_native_thread_id_get (), (gpointer)tid);
/*
* Suspend creation of new threadpool threads, since they cannot run
*/
- mono_threadpool_ms_suspend ();
+ mono_threadpool_suspend ();
mono_loader_unlock ();
}
//g_assert (err == 0);
if (suspend_count == 0)
- mono_threadpool_ms_resume ();
+ mono_threadpool_resume ();
mono_loader_unlock ();
}
{
DebuggerTlsData *tls = (DebuggerTlsData *)value;
- if (!tls->suspended && !tls->terminated)
+ if (!tls->suspended && !tls->terminated && !mono_thread_internal_is_current (tls->thread))
*(int*)user_data = *(int*)user_data + 1;
}
static void
emit_thread_start (gpointer key, gpointer value, gpointer user_data)
{
- if (!mono_native_thread_id_equals (MONO_UINT_TO_NATIVE_THREAD_ID (GPOINTER_TO_UINT (key)), debugger_thread_id))
- process_profiler_event (EVENT_KIND_THREAD_START, value);
+ g_assert (!mono_native_thread_id_equals (MONO_UINT_TO_NATIVE_THREAD_ID (GPOINTER_TO_UINT (key)), debugger_thread_id));
+ process_profiler_event (EVENT_KIND_THREAD_START, value);
}
/*
MonoInternalThread *old_thread;
DebuggerTlsData *tls;
- if (mono_native_thread_id_equals (MONO_UINT_TO_NATIVE_THREAD_ID (tid), debugger_thread_id))
+ if (is_debugger_thread ())
return;
g_assert (mono_native_thread_id_equals (MONO_UINT_TO_NATIVE_THREAD_ID (tid), MONO_UINT_TO_NATIVE_THREAD_ID (thread->tid)));
if (thread) {
DEBUG_PRINTF (1, "[%p] Thread terminated, obj=%p, tls=%p.\n", (gpointer)tid, thread, tls);
- if (mono_native_thread_id_equals (mono_native_thread_id_get (), MONO_UINT_TO_NATIVE_THREAD_ID (tid))
- && !mono_native_tls_get_value (debugger_tls_id)
+ if (mono_thread_internal_is_current (thread) && !mono_native_tls_get_value (debugger_tls_id)
) {
/*
* This can happen on darwin since we deregister threads using pthread dtors.
static void
send_types_for_domain (MonoDomain *domain, void *user_data)
{
+ MonoDomain* old_domain;
AgentDomainInfo *info = NULL;
info = get_agent_domain_info (domain);
g_assert (info);
+
+ old_domain = mono_domain_get ();
+
+ mono_domain_set (domain, TRUE);
mono_loader_lock ();
g_hash_table_foreach (info->loaded_classes, emit_type_load, NULL);
mono_loader_unlock ();
+
+ mono_domain_set (old_domain, TRUE);
+}
+
+static void
+send_assemblies_for_domain (MonoDomain *domain, void *user_data)
+{
+ GSList *tmp;
+ MonoDomain* old_domain;
+
+ old_domain = mono_domain_get ();
+
+ mono_domain_set (domain, TRUE);
+
+ mono_domain_assemblies_lock (domain);
+ for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
+ MonoAssembly* ass = (MonoAssembly *)tmp->data;
+ emit_assembly_load (ass, NULL);
+ }
+ mono_domain_assemblies_unlock (domain);
+
+ mono_domain_set (old_domain, TRUE);
}
static void
gboolean it_has_sp = FALSE;
if (error)
- mono_error_init (error);
+ error_init (error);
mono_seq_point_iterator_init (&it, seq_points);
while (mono_seq_point_iterator_next (&it)) {
MonoJitInfo *ji;
if (error)
- mono_error_init (error);
+ error_init (error);
code = mono_jit_find_compiled_method_with_jit_info (domain, method, &ji);
if (!code) {
int i;
if (error)
- mono_error_init (error);
+ error_init (error);
// FIXME:
// - suspend/resume the vm to prevent code patching problems
compute_frame_info (tls->thread, tls);
}
+static gboolean
+ensure_jit (StackFrame* frame)
+{
+ if (!frame->jit) {
+ frame->jit = mono_debug_find_method (frame->api_method, frame->domain);
+ if (!frame->jit && frame->api_method->is_inflated)
+ frame->jit = mono_debug_find_method(mono_method_get_declaring_generic_method (frame->api_method), frame->domain);
+ if (!frame->jit) {
+ char *s;
+
+ /* This could happen for aot images with no jit debug info */
+ s = mono_method_full_name (frame->api_method, TRUE);
+ DEBUG_PRINTF(1, "[dbg] No debug information found for '%s'.\n", s);
+ g_free (s);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
/*
* ss_update:
*
* Return FALSE if single stepping needs to continue.
*/
static gboolean
-ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *tls, MonoContext *ctx)
+ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *tls, MonoContext *ctx, MonoMethod* method)
{
MonoDebugMethodInfo *minfo;
MonoDebugSourceLocation *loc = NULL;
gboolean hit = TRUE;
- MonoMethod *method;
+
+ if (req->async_stepout_method == method) {
+ DEBUG_PRINTF (1, "[%p] Breakpoint hit during async step-out at %s hit, continuing stepping out.\n", (gpointer)(gsize)mono_native_thread_id_get (), method->name);
+ return FALSE;
+ }
if (req->depth == STEP_DEPTH_OVER && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK)) {
/*
return FALSE;
}
- if ((req->depth == STEP_DEPTH_OVER || req->depth == STEP_DEPTH_OUT) && hit) {
+ if ((req->depth == STEP_DEPTH_OVER || req->depth == STEP_DEPTH_OUT) && hit && !req->async_stepout_method) {
gboolean is_step_out = req->depth == STEP_DEPTH_OUT;
ss_calculate_framecount (tls, ctx);
}
if (req->depth == STEP_DEPTH_INTO && req->size == STEP_SIZE_MIN && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK) && ss_req->start_method){
- method = jinfo_get_method (ji);
ss_calculate_framecount (tls, ctx);
if (ss_req->start_method == method && req->nframes && tls->frame_count == req->nframes) {//Check also frame count(could be recursion)
DEBUG_PRINTF (1, "[%p] Seq point at nonempty stack %x while stepping in, continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), sp->il_offset);
}
}
+ MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
+ if (asyncMethod) {
+ for (int i = 0; i < asyncMethod->num_awaits; i++)
+ {
+ if (asyncMethod->yield_offsets[i] == sp->il_offset || asyncMethod->resume_offsets[i] == sp->il_offset) {
+ mono_debug_free_method_async_debug_info (asyncMethod);
+ return FALSE;
+ }
+ }
+ mono_debug_free_method_async_debug_info (asyncMethod);
+ }
+
if (req->size != STEP_SIZE_LINE)
return TRUE;
/* Have to check whenever a different source line was reached */
- method = jinfo_get_method (ji);
minfo = mono_debug_lookup_method (method);
if (minfo)
return bp->method && bp->method->klass->image->assembly == assembly;
}
+static MonoObject*
+get_this (StackFrame *frame)
+{
+ //Logic inspiered by "add_var" method and took out path that happens in async method for getting this
+ MonoDebugVarInfo *var = frame->jit->this_var;
+ if ((var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS) != MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET)
+ return NULL;
+
+ guint8 * addr = (guint8 *)mono_arch_context_get_int_reg (&frame->ctx, var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS);
+ addr += (gint32)var->offset;
+ return *(MonoObject**)addr;
+}
+
+//This ID is used to figure out if breakpoint hit on resumeOffset belongs to us or not
+//since thread probably changed...
+static int
+get_this_async_id (StackFrame *frame)
+{
+ return get_objid (get_this (frame));
+}
+
+static MonoMethod*
+get_set_notification_method (MonoClass* async_builder_class)
+{
+ MonoError error;
+ GPtrArray* array = mono_class_get_methods_by_name (async_builder_class, "SetNotificationForWaitCompletion", 0x24, FALSE, FALSE, &error);
+ mono_error_assert_ok (&error);
+ g_assert (array->len == 1);
+ MonoMethod* set_notification_method = (MonoMethod *)g_ptr_array_index (array, 0);
+ g_ptr_array_free (array, TRUE);
+ return set_notification_method;
+}
+
+static void
+set_set_notification_for_wait_completion_flag (StackFrame *frame)
+{
+ MonoObject* obj = get_this (frame);
+ g_assert (obj);
+ MonoClassField *builder_field = mono_class_get_field_from_name (obj->vtable->klass, "<>t__builder");
+ g_assert (builder_field);
+ MonoObject* builder;
+ MonoError error;
+ builder = mono_field_get_value_object_checked (frame->domain, builder_field, obj, &error);
+ mono_error_assert_ok (&error);
+ g_assert (builder);
+
+ void* args [1];
+ gboolean arg = TRUE;
+ args [0] = &arg;
+ mono_runtime_invoke_checked (get_set_notification_method (builder->vtable->klass), mono_object_unbox (builder), args, &error);
+ mono_error_assert_ok (&error);
+ mono_field_set_value (obj, builder_field, mono_object_unbox (builder));
+}
+
+static MonoMethod* notify_debugger_of_wait_completion_method_cache = NULL;
+
+static MonoMethod*
+get_notify_debugger_of_wait_completion_method ()
+{
+ if (notify_debugger_of_wait_completion_method_cache != NULL)
+ return notify_debugger_of_wait_completion_method_cache;
+ MonoError error;
+ MonoClass* task_class = mono_class_load_from_name (mono_defaults.corlib, "System.Threading.Tasks", "Task");
+ GPtrArray* array = mono_class_get_methods_by_name (task_class, "NotifyDebuggerOfWaitCompletion", 0x24, FALSE, FALSE, &error);
+ mono_error_assert_ok (&error);
+ g_assert (array->len == 1);
+ notify_debugger_of_wait_completion_method_cache = (MonoMethod *)g_ptr_array_index (array, 0);
+ g_ptr_array_free (array, TRUE);
+ return notify_debugger_of_wait_completion_method_cache;
+}
+
static void
process_breakpoint_inner (DebuggerTlsData *tls, gboolean from_signal)
{
SingleStepReq *ss_req = (SingleStepReq *)req->info;
gboolean hit;
- if (mono_thread_internal_current () != ss_req->thread)
- continue;
+ //if we hit async_stepout_method, it's our no matter which thread
+ if ((ss_req->async_stepout_method != method) && (ss_req->async_id || mono_thread_internal_current () != ss_req->thread)) {
+ //We have different thread and we don't have async stepping in progress
+ //it's breakpoint in parallel thread, ignore it
+ if (ss_req->async_id == 0)
+ continue;
+
+ tls->context.valid = FALSE;
+ tls->async_state.valid = FALSE;
+ invalidate_frames (tls);
+ ss_calculate_framecount(tls, ctx);
+ //make sure we have enough data to get current async method instance id
+ if (tls->frame_count == 0 || !ensure_jit (tls->frames [0]))
+ continue;
+
+ //Check method is async before calling get_this_async_id
+ MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
+ if (!asyncMethod)
+ continue;
+ else
+ mono_debug_free_method_async_debug_info (asyncMethod);
+
+ //breakpoint was hit in parallelly executing async method, ignore it
+ if (ss_req->async_id != get_this_async_id (tls->frames [0]))
+ continue;
+ }
+
+ //Update stepping request to new thread/frame_count that we are continuing on
+ //so continuing with normal stepping works as expected
+ if (ss_req->async_stepout_method || ss_req->async_id) {
+ tls->context.valid = FALSE;
+ tls->async_state.valid = FALSE;
+ invalidate_frames (tls);
+ ss_calculate_framecount (tls, ctx);
+ ss_req->thread = mono_thread_internal_current ();
+ ss_req->nframes = tls->frame_count;
+ }
- hit = ss_update (ss_req, ji, &sp, tls, ctx);
+ hit = ss_update (ss_req, ji, &sp, tls, ctx, method);
if (hit)
g_ptr_array_add (ss_reqs, req);
if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy")))
return;
+ /*
+ * This could be in ss_update method, but mono_find_next_seq_point_for_native_offset is pretty expensive method,
+ * hence we prefer this check here.
+ */
+ if (ss_req->user_assemblies) {
+ gboolean found = FALSE;
+ for (int k = 0; ss_req->user_assemblies[k]; k++)
+ if (ss_req->user_assemblies[k] == method->klass->image->assembly) {
+ found = TRUE;
+ break;
+ }
+ if (!found)
+ return;
+ }
+
+
/*
* The ip points to the instruction causing the single step event, which is before
* the offset recorded in the seq point map, so find the next seq point after ip.
il_offset = sp.il_offset;
- if (!ss_update (ss_req, ji, &sp, tls, ctx))
+ if (!ss_update (ss_req, ji, &sp, tls, ctx, method))
return;
/* Start single stepping again from the current sequence point */
ss_req->bps = NULL;
}
+ ss_req->async_id = 0;
+ ss_req->async_stepout_method = NULL;
if (ss_req->global) {
stop_single_stepping ();
ss_req->global = FALSE;
}
}
+static gboolean
+is_last_non_empty (SeqPoint* sp, MonoSeqPointInfo *info)
+{
+ if (!sp->next_len)
+ return TRUE;
+ SeqPoint* next = g_new (SeqPoint, sp->next_len);
+ mono_seq_point_init_next (info, *sp, next);
+ for (int i = 0; i < sp->next_len; i++) {
+ if (next [i].flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK) {
+ if (!is_last_non_empty (&next [i], info)) {
+ g_free (next);
+ return FALSE;
+ }
+ } else {
+ g_free (next);
+ return FALSE;
+ }
+ }
+ g_free (next);
+ return TRUE;
+}
+
/*
* ss_start:
*
nframes = tls->frame_count;
}
+ MonoDebugMethodAsyncInfo* asyncMethod = mono_debug_lookup_method_async_debug_info (method);
+
+ /* Need to stop in catch clauses as well */
+ for (i = ss_req->depth == STEP_DEPTH_OUT ? 1 : 0; i < nframes; ++i) {
+ StackFrame *frame = frames [i];
+
+ if (frame->ji) {
+ MonoJitInfo *jinfo = frame->ji;
+ for (j = 0; j < jinfo->num_clauses; ++j) {
+ // In case of async method we don't want to place breakpoint on last catch handler(which state machine added for whole method)
+ if (asyncMethod && asyncMethod->num_awaits && i == 0 && j + 1 == jinfo->num_clauses)
+ break;
+ MonoJitExceptionInfo *ei = &jinfo->clauses [j];
+
+ if (mono_find_next_seq_point_for_native_offset (frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL, &local_sp))
+ ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, frame->method, local_sp.il_offset);
+ }
+ }
+ }
+
+ if (asyncMethod && asyncMethod->num_awaits && nframes && ensure_jit (frames [0])) {
+ //asyncMethod has value and num_awaits > 0, this means we are inside async method with awaits
+
+ // Check if we hit yield_offset during normal stepping, because if we did...
+ // Go into special async stepping mode which places breakpoint on resumeOffset
+ // of this await call and sets async_id so we can distinguish it from parallel executions
+ for (i = 0; i < asyncMethod->num_awaits; i++) {
+ if (sp->il_offset == asyncMethod->yield_offsets [i]) {
+ ss_req->async_id = get_this_async_id (frames [0]);
+ ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, method, asyncMethod->resume_offsets [i]);
+ if (ss_req_bp_cache)
+ g_hash_table_destroy (ss_req_bp_cache);
+ mono_debug_free_method_async_debug_info (asyncMethod);
+ return;
+ }
+ }
+ //If we are at end of async method and doing step-in or step-over...
+ //Switch to step-out, so whole NotifyDebuggerOfWaitCompletion magic happens...
+ if (is_last_non_empty (sp, info)) {
+ ss_req->depth = STEP_DEPTH_OUT;//setting depth to step-out is important, don't inline IF, because code later depends on this
+ }
+ if (ss_req->depth == STEP_DEPTH_OUT) {
+ set_set_notification_for_wait_completion_flag (frames [0]);
+ ss_req->async_id = get_this_async_id (frames [0]);
+ ss_req->async_stepout_method = get_notify_debugger_of_wait_completion_method ();
+ ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, ss_req->async_stepout_method, 0);
+ if (ss_req_bp_cache)
+ g_hash_table_destroy (ss_req_bp_cache);
+ mono_debug_free_method_async_debug_info (asyncMethod);
+ return;
+ }
+ }
+
+ if (asyncMethod)
+ mono_debug_free_method_async_debug_info (asyncMethod);
+
/*
- * Find the first sequence point in the current or in a previous frame which
- * is not the last in its method.
- */
+ * Find the first sequence point in the current or in a previous frame which
+ * is not the last in its method.
+ */
if (ss_req->depth == STEP_DEPTH_OUT) {
/* Ignore seq points in current method */
while (frame_index < nframes) {
ss_req->depth = STEP_DEPTH_INTO;
}
- if (ss_req->depth == STEP_DEPTH_OVER) {
- /* Need to stop in catch clauses as well */
- for (i = 0; i < nframes; ++i) {
- StackFrame *frame = frames [i];
-
- if (frame->ji) {
- MonoJitInfo *jinfo = frame->ji;
- for (j = 0; j < jinfo->num_clauses; ++j) {
- MonoJitExceptionInfo *ei = &jinfo->clauses [j];
-
- found_sp = mono_find_next_seq_point_for_native_offset (frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL, &local_sp);
- sp = (found_sp)? &local_sp : NULL;
-
- if (found_sp)
- ss_bp_add_one (ss_req, &ss_req_bp_count, &ss_req_bp_cache, frame->method, sp->il_offset);
- }
- }
- }
- }
-
if (ss_req->depth == STEP_DEPTH_INTO) {
/* Enable global stepping so we stop at method entry too */
enable_global = TRUE;
ss_req->filter = filter;
req->info = ss_req;
+ for (int i = 0; i < req->nmodifiers; i++) {
+ if (req->modifiers[i].kind == MOD_KIND_ASSEMBLY_ONLY) {
+ ss_req->user_assemblies = req->modifiers[i].data.assemblies;
+ break;
+ }
+ }
+
mono_loader_lock ();
tls = (DebuggerTlsData *)mono_g_hash_table_lookup (thread_to_tls, thread);
mono_loader_unlock ();
g_assert (tls);
- g_assert (tls->context.valid);
+ if (!tls->context.valid) {
+ DEBUG_PRINTF (1, "Received a single step request on a thread with no managed frames.");
+ return ERR_INVALID_ARGUMENT;
+ }
if (tls->restore_state.valid && MONO_CONTEXT_GET_IP (&tls->context.ctx) != MONO_CONTEXT_GET_IP (&tls->restore_state.ctx)) {
/*
}
if (match_count) {
- newassemblies = g_new0 (MonoAssembly*, count - match_count);
+ // +1 because we don't know length and we use last element to check for end
+ newassemblies = g_new0 (MonoAssembly*, count - match_count + 1);
pos = 0;
for (i = 0; i < count; ++i)
if (!m->klass->valuetype && !(m->flags & METHOD_ATTRIBUTE_STATIC) && !this_arg) {
if (!strcmp (m->name, ".ctor")) {
- if (m->klass->flags & TYPE_ATTRIBUTE_ABSTRACT)
+ if (mono_class_is_abstract (m->klass))
return ERR_INVALID_ARGUMENT;
else {
MonoError error;
int n = decode_int (p, &p, end);
int j;
- req->modifiers [i].data.assemblies = g_new0 (MonoAssembly*, n);
+ // +1 because we don't know length and we use last element to check for end
+ req->modifiers [i].data.assemblies = g_new0 (MonoAssembly*, n + 1);
for (j = 0; j < n; ++j) {
req->modifiers [i].data.assemblies [j] = decode_assemblyid (p, &p, end, &domain, &err);
if (err != ERR_NONE) {
break;
case EVENT_KIND_ASSEMBLY_LOAD:
/* Emit load events for currently loaded assemblies */
- mono_assembly_foreach (emit_assembly_load, NULL);
+ mono_domain_foreach (send_assemblies_for_domain, NULL);
break;
case EVENT_KIND_THREAD_START:
/* Emit start events for currently started threads */
case CMD_APPDOMAIN_CREATE_STRING: {
char *s;
MonoString *o;
+ MonoError error;
domain = decode_domainid (p, &p, end, NULL, &err);
if (err != ERR_NONE)
return err;
s = decode_string (p, &p, end);
- o = mono_string_new (domain, s);
+ o = mono_string_new_checked (domain, s, &error);
+ if (!is_ok (&error)) {
+ DEBUG_PRINTF (1, "[dbg] Failed to allocate String object '%s': %s\n", s, mono_error_get_message (&error));
+ mono_error_cleanup (&error);
+ return ERR_INVALID_OBJECT;
+ }
buffer_add_objid (buf, (MonoObject*)o);
break;
}
return ERR_NONE;
}
+static ErrorCode
+get_assembly_object_command (MonoDomain *domain, MonoAssembly *ass, Buffer *buf, MonoError *error)
+{
+ HANDLE_FUNCTION_ENTER();
+ ErrorCode err = ERR_NONE;
+ error_init (error);
+ MonoReflectionAssemblyHandle o = mono_assembly_get_object_handle (domain, ass, error);
+ if (MONO_HANDLE_IS_NULL (o)) {
+ err = ERR_INVALID_OBJECT;
+ goto leave;
+ }
+ buffer_add_objid (buf, MONO_HANDLE_RAW (MONO_HANDLE_CAST (MonoObject, o)));
+leave:
+ HANDLE_FUNCTION_RETURN_VAL (err);
+}
+
+
static ErrorCode
assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
{
}
case CMD_ASSEMBLY_GET_OBJECT: {
MonoError error;
- MonoObject *o = (MonoObject*)mono_assembly_get_object_checked (domain, ass, &error);
- if (!o) {
- mono_error_cleanup (&error); /* FIXME don't swallow the error */
- return ERR_INVALID_OBJECT;
- }
- buffer_add_objid (buf, o);
+ err = get_assembly_object_command (domain, ass, buf, &error);
+ mono_error_cleanup (&error);
+ return err;
+ }
+ case CMD_ASSEMBLY_GET_DOMAIN: {
+ buffer_add_domainid (buf, domain);
break;
}
case CMD_ASSEMBLY_GET_TYPE: {
buffer_add_id (buf, 0);
buffer_add_int (buf, klass->type_token);
buffer_add_byte (buf, klass->rank);
- buffer_add_int (buf, klass->flags);
+ buffer_add_int (buf, mono_class_get_flags (klass));
b = 0;
type = &klass->byval_arg;
// FIXME: Can't decide whenever a class represents a byref type
b |= (1 << 3);
if (klass->enumtype)
b |= (1 << 4);
- if (klass->generic_container)
+ if (mono_class_is_gtd (klass))
b |= (1 << 5);
- if (klass->generic_container || mono_class_is_ginst (klass))
+ if (mono_class_is_gtd (klass) || mono_class_is_ginst (klass))
b |= (1 << 6);
buffer_add_byte (buf, b);
nnested = 0;
while ((nested = mono_class_get_nested_types (klass, &iter)))
buffer_add_typeid (buf, domain, nested);
if (CHECK_PROTOCOL_VERSION (2, 12)) {
- if (klass->generic_container)
+ if (mono_class_is_gtd (klass))
buffer_add_typeid (buf, domain, klass);
else if (mono_class_is_ginst (klass))
buffer_add_typeid (buf, domain, mono_class_get_generic_class (klass)->container_class);
buffer_add_int (buf, count);
for (i = 0; i < count; i++)
buffer_add_typeid (buf, domain, mono_class_from_mono_type (inst->type_argv [i]));
- } else if (klass->generic_container) {
- MonoGenericContainer *container = klass->generic_container;
+ } else if (mono_class_is_gtd (klass)) {
+ MonoGenericContainer *container = mono_class_get_generic_container (klass);
MonoClass *pklass;
count = container->type_argc;
MonoError error;
GPtrArray *array;
- mono_error_init (&error);
+ error_init (&error);
array = mono_class_get_methods_by_name (klass, name, flags & ~BINDING_FLAGS_IGNORE_CASE, (flags & BINDING_FLAGS_IGNORE_CASE) != 0, TRUE, &error);
if (!is_ok (&error)) {
mono_error_cleanup (&error);
if (!frame->has_ctx)
return ERR_ABSENT_INFORMATION;
- if (!frame->jit) {
- frame->jit = mono_debug_find_method (frame->api_method, frame->domain);
- if (!frame->jit && frame->api_method->is_inflated)
- frame->jit = mono_debug_find_method (mono_method_get_declaring_generic_method (frame->api_method), frame->domain);
- if (!frame->jit) {
- char *s;
+ if (!ensure_jit (frame))
+ return ERR_ABSENT_INFORMATION;
- /* This could happen for aot images with no jit debug info */
- s = mono_method_full_name (frame->api_method, TRUE);
- DEBUG_PRINTF (1, "[dbg] No debug information found for '%s'.\n", s);
- g_free (s);
- return ERR_ABSENT_INFORMATION;
- }
- }
jit = frame->jit;
sig = mono_method_signature (frame->actual_method);
"GET_MANIFEST_MODULE",
"GET_OBJECT",
"GET_TYPE",
- "GET_NAME"
+ "GET_NAME",
+ "GET_DOMAIN"
};
static const char* module_cmds_str[] = {
debugger_thread_id = mono_native_thread_id_get ();
- MonoThread *thread = mono_thread_attach (mono_get_root_domain ());
- mono_thread_set_name_internal (thread->internal_thread, mono_string_new (mono_get_root_domain (), "Debugger agent"), TRUE, &error);
+ MonoInternalThread *internal = mono_thread_internal_current ();
+ MonoString *str = mono_string_new_checked (mono_domain_get (), "Debugger agent", &error);
+ mono_error_assert_ok (&error);
+ mono_thread_set_name_internal (internal, str, TRUE, FALSE, &error);
mono_error_assert_ok (&error);
- thread->internal_thread->state |= ThreadState_Background;
- thread->internal_thread->flags |= MONO_THREAD_FLAG_DONT_MANAGE;
+ internal->state |= ThreadState_Background;
+ internal->flags |= MONO_THREAD_FLAG_DONT_MANAGE;
if (agent_config.defer) {
if (!wait_for_attach ()) {