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;
};
#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 {
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);
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;
/* Store the context/lmf for the frame above the last frame */
memcpy (&data->ctx, ctx, sizeof (MonoContext));
data->lmf = info->lmf;
-
return TRUE;
}
}
/*
* 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);
+ }
}
/*
/* 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
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;
+ }
}
}
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 ();
/*
*/
sp = find_prev_seq_point_for_native_offset (mono_domain_get (), ji->method, native_offset, &info);
+ 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) {
bp = g_ptr_array_index (breakpoints, 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;
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);
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;
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);
}
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, sig->ret, &res, domain);
} 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)
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));
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;
}
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;
}