#include <mono/utils/mono-error-internals.h>
#include <mono/utils/mono-stack-unwinding.h>
#include <mono/utils/mono-time.h>
+#include <mono/utils/mono-threads.h>
#include "debugger-agent.h"
#include "mini.h"
+/*
+On iOS we can't use System.Environment.Exit () as it will do the wrong
+shutdown sequence.
+*/
+#if !defined (TARGET_IOS)
+#define TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT
+#endif
+
+
#ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED
#define DISABLE_DEBUGGER_AGENT 1
#endif
#define DISABLE_DEBUGGER_AGENT 1
#endif
-#if defined(__MACH__)
-#include <mono/utils/mono-threads.h>
-#endif
-
#ifndef DISABLE_DEBUGGER_AGENT
#include <mono/io-layer/mono-mutex.h>
* method.
*/
MonoMethod *actual_method;
+ /*
+ * This is the method which is visible to debugger clients. Same as method,
+ * except for native-to-managed wrappers.
+ */
+ MonoMethod *api_method;
MonoContext ctx;
MonoDebugMethodJitInfo *jit;
+ MonoJitInfo *ji;
int flags;
mgreg_t *reg_locations [MONO_MAX_IREGS];
/*
MonoMethod *method;
gpointer *args;
guint32 suspend_count;
+ int nmethods;
InvokeData *last_invoke;
};
typedef struct {
MonoThreadUnwindState context;
- gpointer resume_event;
/* This is computed on demand when it is requested using the wire protocol */
/* It is freed up when the thread is resumed */
int frame_count;
#define HEADER_LENGTH 11
#define MAJOR_VERSION 2
-#define MINOR_VERSION 17
+#define MINOR_VERSION 22
typedef enum {
CMD_SET_VM = 1,
typedef enum {
STEP_FILTER_NONE = 0,
- STEP_FILTER_STATIC_CTOR = 1
+ STEP_FILTER_STATIC_CTOR = 1,
+ STEP_FILTER_DEBUGGER_HIDDEN = 2
} StepFilter;
typedef enum {
CMD_VM_ABORT_INVOKE = 9,
CMD_VM_SET_KEEPALIVE = 10,
CMD_VM_GET_TYPES_FOR_SOURCE_FILE = 11,
- CMD_VM_GET_TYPES = 12
+ CMD_VM_GET_TYPES = 12,
+ CMD_VM_INVOKE_METHODS = 13
} CmdVM;
typedef enum {
CMD_METHOD_GET_INFO = 6,
CMD_METHOD_GET_BODY = 7,
CMD_METHOD_RESOLVE_TOKEN = 8,
+ CMD_METHOD_GET_CATTRS = 9,
} CmdMethod;
typedef enum {
/* A hash table containing all active domains */
static GHashTable *domains;
+/* The number of times the runtime is suspended */
+static gint32 suspend_count;
+
static void transport_init (void);
static void transport_connect (const char *address);
static gboolean transport_handshake (void);
static void process_profiler_event (EventKind event, gpointer arg);
+static void invalidate_frames (DebuggerTlsData *tls);
+
#ifndef DISABLE_SOCKET_TRANSPORT
static void
register_socket_transport (void);
if (!agent_config.onuncaught && !agent_config.onthrow)
finish_agent_init (TRUE);
-
- /* FIXME: Is this still needed ? */
-#if defined(__MACH__)
- mono_thread_info_disable_new_interrupt (TRUE);
-#endif
}
/*
int total = 0;
int fd = conn_fd;
int flags = 0;
+ static gint32 last_keepalive;
+ gint32 msecs;
do {
again:
res = recv (fd, (char *) buf + total, len - total, flags);
if (res > 0)
total += res;
- if (agent_config.keepalive && res == -1 && get_last_sock_error () == MONO_EWOULDBLOCK) {
- process_profiler_event (EVENT_KIND_KEEPALIVE, NULL);
- goto again;
+ if (agent_config.keepalive) {
+ gboolean need_keepalive = FALSE;
+ if (res == -1 && get_last_sock_error () == MONO_EWOULDBLOCK) {
+ need_keepalive = TRUE;
+ } else if (res == -1) {
+ /* This could happen if recv () is interrupted repeatedly */
+ msecs = mono_msec_ticks ();
+ if (msecs - last_keepalive >= agent_config.keepalive) {
+ need_keepalive = TRUE;
+ last_keepalive = msecs;
+ }
+ }
+ if (need_keepalive) {
+ process_profiler_event (EVENT_KIND_KEEPALIVE, NULL);
+ goto again;
+ }
}
} while ((res > 0 && total < len) || (res == -1 && get_last_sock_error () == MONO_EINTR));
return total;
struct timeval tv;
int result;
- if (!agent_config.keepalive)
+ if (!agent_config.keepalive || !conn_fd)
return;
tv.tv_sec = agent_config.keepalive / 1000;
addrlen = sizeof (addr);
memset (&addr, 0, sizeof (addr));
- res = getsockname (sfd, &addr, &addrlen);
+ res = getsockname (sfd, (struct sockaddr*)&addr, &addrlen);
g_assert (res == 0);
host = (char*)"127.0.0.1";
#endif
}
- disconnected = !transport_handshake ();
- if (disconnected)
+ if (!transport_handshake ())
exit (1);
}
static DebuggerTransport transports [MAX_TRANSPORTS];
static int ntransports;
+void
+mono_debugger_agent_register_transport (DebuggerTransport *trans);
+
+void
+mono_debugger_agent_register_transport (DebuggerTransport *trans)
+{
+ register_transport (trans);
+}
+
static void
register_transport (DebuggerTransport *trans)
{
return transport->recv (buf, len);
}
+gboolean
+mono_debugger_agent_transport_handshake (void)
+{
+ return transport_handshake ();
+}
+
static gboolean
transport_handshake (void)
{
guint8 buf [128];
int res;
+ disconnected = TRUE;
+
/* Write handshake message */
sprintf (handshake_msg, "DWP-Handshake");
do {
* Set TCP_NODELAY on the socket so the client receives events/command
* results immediately.
*/
- {
+ if (conn_fd) {
int flag = 1;
int result = setsockopt (conn_fd,
IPPROTO_TCP,
set_keepalive ();
#endif
+ disconnected = FALSE;
return TRUE;
}
}
static GHashTable *obj_to_objref;
+static MonoGHashTable *suspended_objs;
/*
* Return an ObjRef for OBJ.
mono_loader_lock ();
- if (!obj_to_objref)
+ if (!obj_to_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_GC_REGISTER_ROOT_FIXED (suspended_objs);
+ }
+
+ if (suspend_count) {
+ /*
+ * Have to keep object refs created during suspensions alive for the duration of the suspension, so GCs during invokes don't collect them.
+ */
+ mono_g_hash_table_insert (suspended_objs, obj, NULL);
+ }
/* FIXME: The tables can grow indefinitely */
return ref;
}
+static gboolean
+true_pred (gpointer key, gpointer value, gpointer user_data)
+{
+ return TRUE;
+}
+
+static void
+clear_suspended_objs (void)
+{
+ mono_loader_lock ();
+ mono_g_hash_table_foreach_remove (suspended_objs, true_pred, NULL);
+ mono_loader_unlock ();
+}
+
static inline int
get_objid (MonoObject *obj)
{
mono_thread_state_init_from_current (&tls->context);
}
-/* The number of times the runtime is suspended */
-static gint32 suspend_count;
-
/* Number of threads suspended */
/*
* If this is equal to the size of thread_to_tls, the runtime is considered
/* Store the context/lmf for the frame above the last frame */
memcpy (&data->ctx, ctx, sizeof (MonoContext));
data->lmf = info->lmf;
-
return TRUE;
}
}
if (info)
tid = mono_thread_info_get_tid (info);
else
- tid = GetCurrentThreadId ();
+ tid = (MonoNativeThreadId)GetCurrentThreadId ();
// FIXME: Races when the thread leaves managed code before hitting a single step
// event.
return FALSE;
tls = mono_native_tls_get_value (debugger_tls_id);
- if (!tls)
- return FALSE;
+ if (!tls) {
+ DEBUG (1, fprintf (log_file, "[%p] Received interrupt with no TLS, continuing.\n", (gpointer)GetCurrentThreadId ()));
+ return FALSE;
+ }
return thread_interrupt (tls, NULL, sigctx, ji);
}
/*
* reset_native_thread_suspend_state:
*
- * Reset the suspended flag on native threads
+ * Reset the suspended flag and state on native threads
*/
static void
reset_native_thread_suspend_state (gpointer key, gpointer value, gpointer user_data)
{
DebuggerTlsData *tls = value;
- if (!tls->really_suspended && tls->suspended)
+ if (!tls->really_suspended && tls->suspended) {
tls->suspended = FALSE;
+ /*
+ * The thread might still be running if it was executing native code, so the state won't be invalided by
+ * suspend_current ().
+ */
+ tls->context.valid = FALSE;
+ tls->async_state.valid = FALSE;
+ invalidate_frames (tls);
+ }
}
/*
MonoJitInfo *ji;
info = mono_thread_info_safe_suspend_sync ((MonoNativeThreadId)(gpointer)(gsize)thread->tid, FALSE);
- g_assert (info);
-
- ji = mono_jit_info_table_find (info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
+ if (!info) {
+ DEBUG(1, fprintf (log_file, "[%p] mono_thread_info_suspend_sync () failed for %p...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid));
+ /*
+ * Attached thread which died without detaching.
+ */
+ tls->terminated = TRUE;
+ } else {
+ ji = mono_jit_info_table_find (info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
- thread_interrupt (tls, info, NULL, ji);
+ thread_interrupt (tls, info, NULL, ji);
- mono_thread_info_resume (mono_thread_info_get_tid (info));
+ mono_thread_info_resume (mono_thread_info_get_tid (info));
+ }
} else {
res = mono_thread_kill (thread, mono_thread_get_abort_signal ());
- if (res)
+ if (res) {
+ DEBUG(1, fprintf (log_file, "[%p] mono_thread_kill () failed for %p: %d...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid, res));
/*
* Attached thread which died without detaching.
*/
tls->terminated = TRUE;
+ }
}
#endif
}
/* The frame info becomes invalid after a resume */
tls->context.valid = FALSE;
tls->async_state.valid = FALSE;
- invalidate_frames (NULL);
+ invalidate_frames (tls);
}
static void
int i;
seq_points = find_seq_points (domain, method);
+ g_assert (seq_points);
if (info)
*info = seq_points;
MonoSeqPointInfo *seq_points;
int i;
- seq_points = find_seq_points (domain, method);
+ seq_points = get_seq_points (domain, method);
if (info)
*info = seq_points;
+ if (!seq_points)
+ return NULL;
for (i = seq_points->len - 1; i >= 0; --i) {
if (seq_points->seq_points [i].native_offset <= native_offset)
{
ComputeFramesUserData *ud = user_data;
StackFrame *frame;
- MonoMethod *method, *actual_method;
+ MonoMethod *method, *actual_method, *api_method;
SeqPoint *sp;
int flags = 0;
else
method = info->method;
actual_method = info->actual_method;
+ api_method = method;
if (!method)
return FALSE;
if (!CHECK_PROTOCOL_VERSION (2, 17))
/* Older clients can't handle this flag */
return FALSE;
- method = mono_marshal_method_from_wrapper (method);
- if (!method)
+ api_method = mono_marshal_method_from_wrapper (method);
+ if (!api_method)
return FALSE;
- actual_method = method;
+ actual_method = api_method;
flags |= FRAME_FLAG_NATIVE_TRANSITION;
}
frame = g_new0 (StackFrame, 1);
frame->method = method;
frame->actual_method = actual_method;
+ frame->api_method = api_method;
frame->il_offset = info->il_offset;
frame->native_offset = info->native_offset;
frame->flags = flags;
+ frame->ji = info->ji;
if (info->reg_locations)
memcpy (frame->reg_locations, info->reg_locations, MONO_MAX_IREGS * sizeof (mgreg_t*));
if (ctx) {
MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method);
if (minfo) {
- mono_debug_symfile_get_line_numbers_full (minfo, &source_file, &source_file_list, NULL, NULL, NULL, NULL);
+ mono_debug_symfile_get_line_numbers_full (minfo, &source_file, &source_file_list, NULL, NULL, NULL, NULL, NULL);
for (i = 0; i < source_file_list->len; ++i) {
sinfo = g_ptr_array_index (source_file_list, i);
/*
(ji->method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) &&
!strcmp (ji->method->name, ".cctor"))
filtered = TRUE;
+ if ((mod->data.filter & STEP_FILTER_DEBUGGER_HIDDEN) && ji) {
+ MonoCustomAttrInfo *ainfo;
+ static MonoClass *klass;
+
+ if (!klass) {
+ klass = mono_class_from_name (mono_defaults.corlib, "System.Diagnostics", "DebuggerHiddenAttribute");
+ g_assert (klass);
+ }
+ if (!ji->dbg_hidden_inited) {
+ ainfo = mono_custom_attrs_from_method (ji->method);
+ if (ainfo) {
+ if (mono_custom_attrs_has_attr (ainfo, klass))
+ ji->dbg_hidden = TRUE;
+ mono_custom_attrs_free (ainfo);
+ }
+ ji->dbg_hidden_inited = TRUE;
+ }
+ if (ji->dbg_hidden)
+ filtered = TRUE;
+ }
}
}
g_assert (!tls);
// FIXME: Free this somewhere
tls = g_new0 (DebuggerTlsData, 1);
- tls->resume_event = CreateEvent (NULL, FALSE, FALSE, NULL);
MONO_GC_REGISTER_ROOT_SINGLE (tls->thread);
tls->thread = thread;
mono_native_tls_set_value (debugger_tls_id, tls);
char *s = g_strdup_printf ("Unable to insert breakpoint at %s:%d, seq_points=%d\n", mono_method_full_name (ji->method, TRUE), bp->il_offset, seq_points->len);
for (i = 0; i < seq_points->len; ++i)
- printf ("%d\n", seq_points->seq_points [i].il_offset);
+ DEBUG (1, fprintf (log_file, "%d\n", seq_points->seq_points [i].il_offset));
if (error) {
mono_error_set_error (error, MONO_ERROR_GENERIC, "%s", s);
ss_reqs = g_ptr_array_new ();
ss_reqs_orig = g_ptr_array_new ();
- DEBUG(1, fprintf (log_file, "[%p] Breakpoint hit, method=%s, ip=%p, offset=0x%x.\n", (gpointer)GetCurrentThreadId (), ji->method->name, ip, native_offset));
-
mono_loader_lock ();
/*
* the offset recorded in the seq point map, so find the prev seq point before ip.
*/
sp = find_prev_seq_point_for_native_offset (mono_domain_get (), ji->method, native_offset, &info);
+ g_assert (sp);
+
+ DEBUG(1, fprintf (log_file, "[%p] Breakpoint hit, method=%s, ip=%p, offset=0x%x, sp il offset=0x%x.\n", (gpointer)GetCurrentThreadId (), ji->method->name, ip, native_offset, sp ? sp->il_offset : -1));
bp = NULL;
for (i = 0; i < breakpoints->len; ++i) {
if (minfo)
loc = mono_debug_symfile_lookup_location (minfo, sp->il_offset);
- if (!loc || (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line))
+ if (!loc || (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line)) {
/* Have to continue single stepping */
+ DEBUG(1, fprintf (log_file, "[%p] Same source line, continuing single stepping.\n", (gpointer)GetCurrentThreadId ()));
hit = FALSE;
+ }
if (loc) {
ss_req->last_method = ji->method;
* problems, like the original signal is disabled, libgc can't handle altstack, etc.
* So set up the signal context to return to the real breakpoint handler function.
*/
-
resume_from_signal_handler (sigctx, process_breakpoint);
}
static void
ss_start (SingleStepReq *ss_req, MonoMethod *method, SeqPoint *sp, MonoSeqPointInfo *info, MonoContext *ctx, DebuggerTlsData *tls, gboolean step_to_catch)
{
- int i, frame_index;
+ int i, j, frame_index;
SeqPoint *next_sp;
MonoBreakpoint *bp;
gboolean enable_global = FALSE;
} else {
frame_index = 1;
- if ((!sp || sp->next_len == 0 || ss_req->depth == STEP_DEPTH_OUT) && ctx) {
+ if ((!sp || sp->next_len == 0 || ss_req->depth == STEP_DEPTH_OUT || ss_req->depth == STEP_DEPTH_OVER) && ctx) {
/* Need parent frames */
if (!tls->context.valid)
mono_thread_state_init_from_monoctx (&tls->context, ctx);
if (sp && sp->next_len != 0)
break;
}
+ // There could be method calls before the next seq point in the caller when using nested calls
+ //enable_global = TRUE;
} else {
while (sp && sp->next_len == 0) {
sp = NULL;
}
}
+ if (ss_req->depth == STEP_DEPTH_OVER) {
+ /* Need to stop in catch clauses as well */
+ for (i = 0; i < tls->frame_count; ++i) {
+ StackFrame *frame = tls->frames [i];
+
+ if (frame->ji) {
+ MonoJitInfo *jinfo = frame->ji;
+ for (j = 0; j < jinfo->num_clauses; ++j) {
+ MonoJitExceptionInfo *ei = &jinfo->clauses [j];
+
+ sp = find_next_seq_point_for_native_offset (frame->domain, frame->method, (char*)ei->handler_start - (char*)jinfo->code_start, NULL);
+ if (sp) {
+ bp = set_breakpoint (frame->method, sp->il_offset, ss_req->req, NULL);
+ ss_req->bps = g_slist_append (ss_req->bps, bp);
+ }
+ }
+ }
+ }
+ }
+
+
if (ss_req->depth == STEP_DEPTH_INTO) {
/* Enable global stepping so we stop at method entry too */
enable_global = TRUE;
return agent_config.enabled;
}
+#ifdef PLATFORM_ANDROID
+void
+mono_debugger_agent_unhandled_exception (MonoException *exc)
+{
+ int suspend_policy;
+ GSList *events;
+ EventInfo ei;
+
+ if (!inited)
+ return;
+
+ memset (&ei, 0, sizeof (EventInfo));
+ ei.exc = (MonoObject*)exc;
+
+ mono_loader_lock ();
+ events = create_event_list (EVENT_KIND_EXCEPTION, NULL, NULL, &ei, &suspend_policy);
+ mono_loader_unlock ();
+
+ process_event (EVENT_KIND_EXCEPTION, &ei, 0, NULL, events, suspend_policy);
+}
+#endif
+
void
mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *throw_ctx,
MonoContext *catch_ctx)
{
- int suspend_policy;
+ int i, j, suspend_policy;
GSList *events;
- MonoJitInfo *ji;
+ MonoJitInfo *ji, *catch_ji;
EventInfo ei;
DebuggerTlsData *tls = NULL;
return;
ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (throw_ctx), NULL);
+ if (catch_ctx)
+ catch_ji = mini_jit_info_table_find (mono_domain_get (), MONO_CONTEXT_GET_IP (catch_ctx), NULL);
+ else
+ catch_ji = NULL;
ei.exc = (MonoObject*)exc;
ei.caught = catch_ctx != NULL;
mono_loader_lock ();
+
+ /* Treat exceptions which are caught in non-user code as unhandled */
+ for (i = 0; i < event_requests->len; ++i) {
+ EventRequest *req = g_ptr_array_index (event_requests, i);
+ if (req->event_kind != EVENT_KIND_EXCEPTION)
+ continue;
+
+ for (j = 0; j < req->nmodifiers; ++j) {
+ Modifier *mod = &req->modifiers [j];
+
+ if (mod->kind == MOD_KIND_ASSEMBLY_ONLY && catch_ji) {
+ int k;
+ gboolean found = FALSE;
+ MonoAssembly **assemblies = mod->data.assemblies;
+
+ if (assemblies) {
+ for (k = 0; assemblies [k]; ++k)
+ if (assemblies [k] == catch_ji->method->klass->image->assembly)
+ found = TRUE;
+ }
+ if (!found)
+ ei.caught = FALSE;
+ }
+ }
+ }
+
events = create_event_list (EVENT_KIND_EXCEPTION, NULL, ji, &ei, &suspend_policy);
mono_loader_unlock ();
- if (tls && catch_ctx) {
+ if (tls && ei.caught && catch_ctx) {
tls->catch_ctx = *catch_ctx;
tls->has_catch_ctx = TRUE;
}
buffer_add_value_full (buf, t, addr, domain, FALSE);
}
+static gboolean
+obj_is_of_type (MonoObject *obj, MonoType *t)
+{
+ MonoClass *klass = obj->vtable->klass;
+ if (!mono_class_is_assignable_from (mono_class_from_mono_type (t), klass)) {
+ if (klass == mono_defaults.transparent_proxy_class) {
+ klass = ((MonoTransparentProxy *)obj)->remote_class->proxy_class;
+ if (mono_class_is_assignable_from (mono_class_from_mono_type (t), klass)) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+ return TRUE;
+}
+
static ErrorCode
-decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
+decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit);
+
+static ErrorCode
+decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
{
int err;
- int type = decode_byte (buf, &buf, limit);
if (type != t->type && !MONO_TYPE_IS_REFERENCE (t) &&
!(t->type == MONO_TYPE_I && type == MONO_TYPE_VALUETYPE) &&
if (err)
return err;
- if (obj && !mono_class_is_assignable_from (mono_class_from_mono_type (t), obj->vtable->klass))
- return ERR_INVALID_ARGUMENT;
+ if (obj) {
+ if (!obj_is_of_type (obj, t)) {
+ DEBUG (1, fprintf (log_file, "Expected type '%s', got '%s'\n", mono_type_full_name (t), obj->vtable->klass->name));
+ return ERR_INVALID_ARGUMENT;
+ }
+ }
if (obj && obj->vtable->domain != domain)
return ERR_INVALID_ARGUMENT;
return 0;
}
+static ErrorCode
+decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
+{
+ int err;
+ int type = decode_byte (buf, &buf, limit);
+
+ if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) {
+ MonoType *targ = t->data.generic_class->context.class_inst->type_argv [0];
+ guint8 *nullable_buf;
+
+ /*
+ * First try decoding it as a Nullable`1
+ */
+ err = decode_value_internal (t, type, domain, addr, buf, endbuf, limit);
+ if (!err)
+ return err;
+
+ /*
+ * Then try decoding as a primitive value or null.
+ */
+ if (targ->type == type) {
+ nullable_buf = g_malloc (mono_class_instance_size (mono_class_from_mono_type (targ)));
+ err = decode_value_internal (targ, type, domain, nullable_buf, buf, endbuf, limit);
+ if (err) {
+ g_free (nullable_buf);
+ return err;
+ }
+ mono_nullable_init (addr, mono_value_box (domain, mono_class_from_mono_type (targ), nullable_buf), mono_class_from_mono_type (t));
+ g_free (nullable_buf);
+ *endbuf = buf;
+ return ERR_NONE;
+ } else if (type == VALUE_TYPE_ID_NULL) {
+ mono_nullable_init (addr, NULL, mono_class_from_mono_type (t));
+ *endbuf = buf;
+ return ERR_NONE;
+ }
+ }
+
+ return decode_value_internal (t, type, domain, addr, buf, endbuf, limit);
+}
+
static void
add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, gboolean as_vtype)
{
g_assert_not_reached ();
}
+ if (t->byref)
+ NOT_IMPLEMENTED;
+
/* Set value on the stack or in the return ctx */
if (reg_locations [reg]) {
/* Saved on the stack */
//printf ("[R%d+%d] = %p\n", reg, var->offset, addr);
+ if (t->byref) {
+ addr = *(guint8**)addr;
+
+ if (!addr)
+ break;
+ }
+
// FIXME: Write barriers
- memcpy (addr, val, size);
+ mono_gc_memmove (addr, val, size);
break;
case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
NOT_IMPLEMENTED;
}
static ErrorCode
-do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke)
+do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8 *p, guint8 **endp)
{
- guint8 *p = invoke->p;
guint8 *end = invoke->endp;
MonoMethod *m;
int i, err, nargs;
}
}
- if (this && !mono_class_is_assignable_from (m->klass, this->vtable->klass))
+ if (this && !obj_is_of_type (this, &m->klass->byval_arg))
return ERR_INVALID_ARGUMENT;
nargs = decode_int (p, &p, end);
buffer_add_value (buf, &mono_defaults.void_class->byval_arg, NULL, domain);
} else if (MONO_TYPE_IS_REFERENCE (sig->ret)) {
buffer_add_value (buf, sig->ret, &res, domain);
- } else if (mono_class_from_mono_type (sig->ret)->valuetype) {
+ } else if (mono_class_from_mono_type (sig->ret)->valuetype || sig->ret->type == MONO_TYPE_PTR || sig->ret->type == MONO_TYPE_FNPTR) {
if (mono_class_is_nullable (mono_class_from_mono_type (sig->ret))) {
- if (!res)
- buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &res, domain);
- else
- buffer_add_value (buf, sig->ret, mono_object_unbox (res), domain);
+ MonoClass *k = mono_class_from_mono_type (sig->ret);
+ guint8 *nullable_buf = g_alloca (mono_class_value_size (k, NULL));
+
+ g_assert (nullable_buf);
+ mono_nullable_init (nullable_buf, res, k);
+ buffer_add_value (buf, sig->ret, nullable_buf, domain);
} else {
g_assert (res);
buffer_add_value (buf, sig->ret, mono_object_unbox (res), domain);
mono_set_lmf ((gpointer)(((gssize)ext.lmf.previous_lmf) & ~3));
#endif
+ *endp = p;
// FIXME: byref arguments
// FIXME: varargs
return ERR_NONE;
DebuggerTlsData *tls;
InvokeData *invoke;
int id;
- int i, err;
+ int i, err, mindex;
Buffer buf;
static void (*restore_context) (void *);
MonoContext restore_ctx;
+ guint8 *p;
if (!restore_context)
restore_context = mono_get_restore_context ();
id = invoke->id;
- buffer_init (&buf, 128);
+ p = invoke->p;
+ err = 0;
+ for (mindex = 0; mindex < invoke->nmethods; ++mindex) {
+ buffer_init (&buf, 128);
- err = do_invoke_method (tls, &buf, invoke);
+ if (err) {
+ /* Fail the other invokes as well */
+ } else {
+ err = do_invoke_method (tls, &buf, invoke, p, &p);
+ }
- /* Start suspending before sending the reply */
- if (!(invoke->flags & INVOKE_FLAG_SINGLE_THREADED)) {
- for (i = 0; i < invoke->suspend_count; ++i)
- suspend_vm ();
- }
+ /* Start suspending before sending the reply */
+ if (mindex == invoke->nmethods - 1) {
+ if (!(invoke->flags & INVOKE_FLAG_SINGLE_THREADED)) {
+ for (i = 0; i < invoke->suspend_count; ++i)
+ suspend_vm ();
+ }
+ }
- send_reply_packet (id, err, &buf);
+ send_reply_packet (id, err, &buf);
- buffer_free (&buf);
+ buffer_free (&buf);
+ }
memcpy (&restore_ctx, &invoke->ctx, sizeof (MonoContext));
GPtrArray *source_file_list;
if (minfo) {
- mono_debug_symfile_get_line_numbers_full (minfo, NULL, &source_file_list, NULL, NULL, NULL, NULL);
+ mono_debug_symfile_get_line_numbers_full (minfo, NULL, &source_file_list, NULL, NULL, NULL, NULL, NULL);
for (j = 0; j < source_file_list->len; ++j) {
sinfo = g_ptr_array_index (source_file_list, j);
for (i = 0; i < files->len; ++i)
if (suspend_count == 0)
return ERR_NOT_SUSPENDED;
resume_vm ();
+ clear_suspended_objs ();
break;
case CMD_VM_DISPOSE:
/* Clear all event requests */
suspend_vm ();
wait_for_suspend ();
+#ifdef TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT
env_class = mono_class_from_name (mono_defaults.corlib, "System", "Environment");
if (env_class)
exit_method = mono_class_get_method_from_name (env_class, "Exit", 1);
+#endif
mono_loader_lock ();
thread = mono_g_hash_table_find (tid_to_thread, is_really_suspended, NULL);
tls->pending_invoke = g_new0 (InvokeData, 1);
tls->pending_invoke->method = exit_method;
tls->pending_invoke->args = args;
+ tls->pending_invoke->nmethods = 1;
while (suspend_count > 0)
resume_vm ();
}
break;
}
- case CMD_VM_INVOKE_METHOD: {
+ case CMD_VM_INVOKE_METHOD:
+ case CMD_VM_INVOKE_METHODS: {
int objid = decode_objid (p, &p, end);
MonoThread *thread;
DebuggerTlsData *tls;
- int i, count, err, flags;
+ int i, count, err, flags, nmethods;
err = get_object (objid, (MonoObject**)&thread);
if (err)
flags = decode_int (p, &p, end);
+ if (command == CMD_VM_INVOKE_METHODS)
+ nmethods = decode_int (p, &p, end);
+ else
+ nmethods = 1;
+
// Wait for suspending if it already started
if (suspend_count)
wait_for_suspend ();
memcpy (tls->pending_invoke->p, p, end - p);
tls->pending_invoke->endp = tls->pending_invoke->p + (end - p);
tls->pending_invoke->suspend_count = suspend_count;
+ tls->pending_invoke->nmethods = nmethods;
if (flags & INVOKE_FLAG_SINGLE_THREADED) {
resume_thread (THREAD_TO_INTERNAL (thread));
break;
}
case CMD_ASSEMBLY_GET_OBJECT: {
- MonoObject *o = (MonoObject*)mono_assembly_get_object (mono_domain_get (), ass);
+ MonoObject *o = (MonoObject*)mono_assembly_get_object (domain, ass);
buffer_add_objid (buf, o);
break;
}
break;
}
case CMD_TYPE_GET_OBJECT: {
- MonoObject *o = (MonoObject*)mono_type_get_object (mono_domain_get (), &klass->byval_arg);
+ MonoObject *o = (MonoObject*)mono_type_get_object (domain, &klass->byval_arg);
buffer_add_objid (buf, o);
break;
}
method_commands_internal (int command, MonoMethod *method, MonoDomain *domain, guint8 *p, guint8 *end, Buffer *buf)
{
MonoMethodHeader *header;
+ int err;
switch (command) {
case CMD_METHOD_GET_NAME: {
int i, j, n_il_offsets;
int *il_offsets;
int *line_numbers;
+ int *column_numbers;
int *source_files;
GPtrArray *source_file_list;
break;
}
- mono_debug_symfile_get_line_numbers_full (minfo, &source_file, &source_file_list, &n_il_offsets, &il_offsets, &line_numbers, &source_files);
+ mono_debug_symfile_get_line_numbers_full (minfo, &source_file, &source_file_list, &n_il_offsets, &il_offsets, &line_numbers, &column_numbers, &source_files);
buffer_add_int (buf, header->code_size);
if (CHECK_PROTOCOL_VERSION (2, 13)) {
buffer_add_int (buf, source_file_list->len);
buffer_add_string (buf, source_file);
}
buffer_add_int (buf, n_il_offsets);
- DEBUG (10, printf ("Line number table for method %s:\n", mono_method_full_name (method, TRUE)));
+ DEBUG (10, fprintf (log_file, "Line number table for method %s:\n", mono_method_full_name (method, TRUE)));
for (i = 0; i < n_il_offsets; ++i) {
const char *srcfile = "";
MonoDebugSourceInfo *sinfo = g_ptr_array_index (source_file_list, source_files [i]);
srcfile = sinfo->source_file;
}
- DEBUG (10, printf ("IL%x -> %s:%d\n", il_offsets [i], srcfile, line_numbers [i]));
+ DEBUG (10, fprintf (log_file, "IL%x -> %s:%d %d\n", il_offsets [i], srcfile, line_numbers [i], column_numbers ? column_numbers [i] : -1));
buffer_add_int (buf, il_offsets [i]);
buffer_add_int (buf, line_numbers [i]);
if (CHECK_PROTOCOL_VERSION (2, 13))
buffer_add_int (buf, source_files [i]);
+ if (CHECK_PROTOCOL_VERSION (2, 19))
+ buffer_add_int (buf, column_numbers ? column_numbers [i] : -1);
}
g_free (source_file);
g_free (il_offsets);
header = mono_method_get_header (method);
if (!header) {
buffer_add_int (buf, 0);
+
+ if (CHECK_PROTOCOL_VERSION (2, 18))
+ buffer_add_int (buf, 0);
} else {
buffer_add_int (buf, header->code_size);
for (i = 0; i < header->code_size; ++i)
buffer_add_byte (buf, header->code [i]);
+
+ if (CHECK_PROTOCOL_VERSION (2, 18)) {
+ buffer_add_int (buf, header->num_clauses);
+ for (i = 0; i < header->num_clauses; ++i) {
+ MonoExceptionClause *clause = &header->clauses [i];
+
+ buffer_add_int (buf, clause->flags);
+ buffer_add_int (buf, clause->try_offset);
+ buffer_add_int (buf, clause->try_len);
+ buffer_add_int (buf, clause->handler_offset);
+ buffer_add_int (buf, clause->handler_len);
+ if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE)
+ buffer_add_typeid (buf, domain, clause->data.catch_class);
+ else if (clause->flags == MONO_EXCEPTION_CLAUSE_FILTER)
+ buffer_add_int (buf, clause->data.filter_offset);
+ }
+ }
+
+ mono_metadata_free_mh (header);
}
- mono_metadata_free_mh (header);
+
break;
}
case CMD_METHOD_RESOLVE_TOKEN: {
}
break;
}
+ case CMD_METHOD_GET_CATTRS: {
+ MonoClass *attr_klass;
+ MonoCustomAttrInfo *cinfo;
+
+ attr_klass = decode_typeid (p, &p, end, NULL, &err);
+ /* attr_klass can be NULL */
+ if (err)
+ return err;
+
+ cinfo = mono_custom_attrs_from_method (method);
+
+ buffer_add_cattrs (buf, domain, method->klass->image, attr_klass, cinfo);
+ break;
+ }
default:
return ERR_NOT_IMPLEMENTED;
}
return ERR_INVALID_FRAMEID;
if (!frame->jit) {
- frame->jit = mono_debug_find_method (frame->method, frame->domain);
- if (!frame->jit && frame->method->is_inflated)
- frame->jit = mono_debug_find_method (mono_method_get_declaring_generic_method (frame->method), frame->domain);
+ 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->method, TRUE);
+ s = mono_method_full_name (frame->api_method, TRUE);
DEBUG (1, fprintf (log_file, "[dbg] No debug information found for '%s'.\n", s));
g_free (s);
return ERR_ABSENT_INFORMATION;
break;
}
case CMD_STACK_FRAME_GET_THIS: {
- if (frame->method->klass->valuetype) {
+ if (frame->api_method->klass->valuetype) {
if (!sig->hasthis) {
MonoObject *p = NULL;
buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &p, frame->domain);
MonoObject *p = NULL;
buffer_add_value (buf, &frame->actual_method->klass->byval_arg, &p, frame->domain);
} else {
- add_var (buf, &frame->method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
+ add_var (buf, &frame->api_method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
}
}
break;
switch (command) {
case CMD_OBJECT_REF_GET_TYPE:
- buffer_add_typeid (buf, obj->vtable->domain, obj->vtable->klass);
+ /* This handles transparent proxies too */
+ buffer_add_typeid (buf, obj->vtable->domain, mono_class_from_mono_type (((MonoReflectionType*)obj->vtable->type)->type));
break;
case CMD_OBJECT_REF_GET_VALUES:
len = decode_int (p, &p, end);
buffer_add_domainid (buf, obj->vtable->domain);
break;
case CMD_OBJECT_REF_GET_INFO:
- buffer_add_typeid (buf, obj->vtable->domain, obj->vtable->klass);
+ buffer_add_typeid (buf, obj->vtable->domain, mono_class_from_mono_type (((MonoReflectionType*)obj->vtable->type)->type));
buffer_add_domainid (buf, obj->vtable->domain);
break;
default:
return "SET_PROTOCOL_VERSION";
case CMD_VM_ABORT_INVOKE:
return "ABORT_INVOKE";
+ case CMD_VM_SET_KEEPALIVE:
+ return "SET_KEEPALIVE";
default:
break;
}
res = transport_recv (header, HEADER_LENGTH);
/* This will break if the socket is closed during shutdown too */
- if (res != HEADER_LENGTH)
+ if (res != HEADER_LENGTH) {
+ DEBUG (1, fprintf (log_file, "[dbg] transport_recv () returned %d, expected %d.\n", res, HEADER_LENGTH));
break;
+ }
p = header;
end = header + HEADER_LENGTH;
if (len - HEADER_LENGTH > 0)
{
res = transport_recv (data, len - HEADER_LENGTH);
- if (res != len - HEADER_LENGTH)
+ if (res != len - HEADER_LENGTH) {
+ DEBUG (1, fprintf (log_file, "[dbg] transport_recv () returned %d, expected %d.\n", res, len - HEADER_LENGTH));
break;
+ }
}
p = data;