#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"
#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>
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 {
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;
#endif
}
- disconnected = !transport_handshake ();
- if (disconnected)
+ if (!transport_handshake ())
exit (1);
}
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;
}
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);
}
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
}
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);
* 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 (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;
}
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);
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);
MonoDebugSourceInfo *sinfo = g_ptr_array_index (source_file_list, source_files [i]);
srcfile = sinfo->source_file;
}
- DEBUG (10, fprintf (log_file, "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;
}