#include <mono/metadata/mono-debug-debugger.h>
#include <mono/metadata/debug-mono-symfile.h>
#include <mono/metadata/gc-internal.h>
+#include <mono/metadata/environment.h>
#include <mono/metadata/threads-types.h>
#include <mono/metadata/socket-io.h>
#include <mono/metadata/assembly.h>
gboolean embedding;
gboolean defer;
int keepalive;
+ gboolean setpgid;
} AgentConfig;
typedef struct
#define HEADER_LENGTH 11
#define MAJOR_VERSION 2
-#define MINOR_VERSION 26
+#define MINOR_VERSION 29
typedef enum {
CMD_SET_VM = 1,
CMD_SET_METHOD = 22,
CMD_SET_TYPE = 23,
CMD_SET_MODULE = 24,
+ CMD_SET_FIELD = 25,
CMD_SET_EVENT = 64
} CommandSet;
CMD_THREAD_GET_STATE = 3,
CMD_THREAD_GET_INFO = 4,
CMD_THREAD_GET_ID = 5,
- CMD_THREAD_GET_TID = 6
+ CMD_THREAD_GET_TID = 6,
+ CMD_THREAD_SET_IP = 7
} CmdThread;
typedef enum {
CMD_MODULE_GET_INFO = 1,
} CmdModule;
+typedef enum {
+ CMD_FIELD_GET_INFO = 1,
+} CmdField;
+
typedef enum {
CMD_METHOD_GET_NAME = 1,
CMD_METHOD_GET_DECLARING_TYPE = 2,
gboolean global;
/* The list of breakpoints used to implement step-over */
GSList *bps;
+ /* The number of frames at the start of a step-over */
+ int nframes;
} SingleStepReq;
/*
fprintf (stderr, " timeout=<n>\t\t\tTimeout for connecting in milliseconds.\n");
fprintf (stderr, " server=y/n\t\t\tWhether to listen for a client connection.\n");
fprintf (stderr, " keepalive=<n>\t\t\tSend keepalive events every n milliseconds.\n");
+ fprintf (stderr, " setpgid=y/n\t\t\tWhether to call setpid(0, 0) after startup.\n");
fprintf (stderr, " help\t\t\t\tPrint this help.\n");
}
agent_config.embedding = atoi (arg + 10) == 1;
} else if (strncmp (arg, "keepalive=", 10) == 0) {
agent_config.keepalive = atoi (arg + 10);
+ } else if (strncmp (arg, "setpgid=", 8) == 0) {
+ agent_config.setpgid = parse_flag ("setpgid", arg + 8);
} else {
print_usage ();
exit (1);
/* Waiting for deferred attachment */
agent_config.defer = TRUE;
if (agent_config.address == NULL) {
- agent_config.address = g_strdup_printf ("0.0.0.0:%u", 56000 + (GetCurrentProcessId () % 1000));
+ agent_config.address = g_strdup_printf ("0.0.0.0:%u", 56000 + (getpid () % 1000));
}
}
*/
mini_get_debug_options ()->load_aot_jit_info_eagerly = TRUE;
+#ifdef HAVE_SETPGID
+ if (agent_config.setpgid)
+ setpgid (0, 0);
+#endif
+
if (!agent_config.onuncaught && !agent_config.onthrow)
finish_agent_init (TRUE);
}
static void
start_debugger_thread (void)
{
- gsize tid;
-
- debugger_thread_handle = mono_create_thread (NULL, 0, debugger_thread, NULL, 0, &tid);
+ debugger_thread_handle = mono_threads_create_thread (debugger_thread, NULL, 0, 0, NULL);
g_assert (debugger_thread_handle);
}
mono_loader_unlock ();
if (res->domain == NULL) {
+ DEBUG (0, fprintf (log_file, "ERR_UNLOADED, id=%d, type=%d.\n", id, type));
*err = ERR_UNLOADED;
return NULL;
}
g_assert (suspend_count > 0);
- DEBUG(1, fprintf (log_file, "[%p] Resuming thread...\n", (gpointer)(gssize)thread->tid));
+ DEBUG(1, fprintf (log_file, "[sdb] Resuming thread %p...\n", (gpointer)(gssize)thread->tid));
tls->resume_count += suspend_count;
buffer_add_domainid (&buf, mono_get_root_domain ());
break;
case EVENT_KIND_VM_DEATH:
+ if (CHECK_PROTOCOL_VERSION (2, 27))
+ buffer_add_int (&buf, mono_environment_exitcode_get ());
break;
case EVENT_KIND_EXCEPTION: {
EventInfo *ei = arg;
* Return FALSE if single stepping needs to continue.
*/
static gboolean
-ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp)
+ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *tls, MonoContext *ctx)
{
MonoDebugMethodInfo *minfo;
MonoDebugSourceLocation *loc = NULL;
return FALSE;
}
+ if (req->depth == STEP_DEPTH_OVER && hit) {
+ if (!tls->context.valid)
+ mono_thread_state_init_from_monoctx (&tls->context, ctx);
+ compute_frame_info (tls->thread, tls);
+ if (req->nframes && tls->frame_count && tls->frame_count > req->nframes) {
+ /* Hit the breakpoint in a recursive call */
+ DEBUG (1, fprintf (log_file, "[%p] Breakpoint at lower frame while stepping over, continuing single stepping.\n", (gpointer)GetCurrentThreadId ()));
+ return FALSE;
+ }
+ }
+
if (req->size != STEP_SIZE_LINE)
return TRUE;
if (mono_thread_internal_current () != ss_req->thread)
continue;
- hit = ss_update (ss_req, ji, sp);
+ hit = ss_update (ss_req, ji, sp, tls, ctx);
if (hit)
g_ptr_array_add (ss_reqs, req);
return;
il_offset = sp->il_offset;
- if (!ss_update (ss_req, ji, sp))
+ if (!ss_update (ss_req, ji, sp, tls, ctx))
return;
/* Start single stepping again from the current sequence point */
}
if (ss_req->depth == STEP_DEPTH_OVER) {
+ if (ss_req->nframes == 0)
+ ss_req->nframes = tls->frame_count;
/* Need to stop in catch clauses as well */
for (i = 0; i < tls->frame_count; ++i) {
StackFrame *frame = tls->frames [i];
mgreg_t v;
gboolean is_signed = FALSE;
+ if (t->byref) {
+ addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
+
+ if (addr) {
+ // FIXME: Write barriers
+ mono_gc_memmove_atomic (addr, val, size);
+ }
+ break;
+ }
+
if (!t->byref && (t->type == MONO_TYPE_I1 || t->type == MONO_TYPE_I2 || t->type == MONO_TYPE_I4 || t->type == MONO_TYPE_I8))
is_signed = TRUE;
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 */
}
// FIXME: Write barriers
- mono_gc_memmove (addr, val, size);
+ mono_gc_memmove_atomic (addr, val, size);
break;
case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR:
/* Same as regoffset, but with an indirection */
gaddr = *(gpointer*)addr;
g_assert (gaddr);
// FIXME: Write barriers
- mono_gc_memmove (gaddr, val, size);
+ mono_gc_memmove_atomic (gaddr, val, size);
break;
case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
NOT_IMPLEMENTED;
/* Setup our lmf */
memset (&ext, 0, sizeof (ext));
-#ifdef TARGET_AMD64
- ext.lmf.previous_lmf = *(lmf_addr);
- /* Mark that this is a MonoLMFExt */
- ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
- ext.lmf.rsp = (gssize)&ext;
-#elif defined(TARGET_X86)
- ext.lmf.previous_lmf = (gsize)*(lmf_addr);
- /* Mark that this is a MonoLMFExt */
- ext.lmf.previous_lmf = (gsize)(gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
- ext.lmf.ebp = (gssize)&ext;
-#elif defined(TARGET_ARM)
- ext.lmf.previous_lmf = *(lmf_addr);
- /* Mark that this is a MonoLMFExt */
- ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
- ext.lmf.sp = (gssize)&ext;
-#elif defined(TARGET_POWERPC)
- ext.lmf.previous_lmf = *(lmf_addr);
- /* Mark that this is a MonoLMFExt */
- ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
- ext.lmf.ebp = (gssize)&ext;
-#elif defined(TARGET_S390X)
- ext.lmf.previous_lmf = *(lmf_addr);
- /* Mark that this is a MonoLMFExt */
- ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
- ext.lmf.ebp = (gssize)&ext;
-#elif defined(TARGET_MIPS)
- ext.lmf.previous_lmf = *(lmf_addr);
- /* Mark that this is a MonoLMFExt */
- ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
- ext.lmf.iregs [mips_sp] = (gssize)&ext;
-#else
- g_assert_not_reached ();
-#endif
+ mono_arch_init_lmf_ext (&ext, *lmf_addr);
ext.debugger_invoke = TRUE;
memcpy (&ext.ctx, &invoke->ctx, sizeof (MonoContext));
if (!mono_runtime_try_shutdown ())
break;
+ mono_environment_exitcode_set (exit_code);
+
/* Suspend all managed threads since the runtime is going away */
DEBUG(1, fprintf (log_file, "Suspending all threads...\n"));
mono_thread_suspend_all_other_threads ();
return ERR_NONE;
}
+static ErrorCode
+field_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+{
+ int err;
+ MonoDomain *domain;
+
+ switch (command) {
+ case CMD_FIELD_GET_INFO: {
+ MonoClassField *f = decode_fieldid (p, &p, end, &domain, &err);
+
+ buffer_add_string (buf, f->name);
+ buffer_add_typeid (buf, domain, f->parent);
+ buffer_add_typeid (buf, domain, mono_class_from_mono_type (f->type));
+ buffer_add_int (buf, f->type->attrs);
+ break;
+ }
+ default:
+ return ERR_NOT_IMPLEMENTED;
+ }
+
+ return ERR_NONE;
+}
+
static void
buffer_add_cattr_arg (Buffer *buf, MonoType *t, MonoDomain *domain, MonoObject *val)
{
case CMD_THREAD_GET_TID:
buffer_add_long (buf, (guint64)thread->tid);
break;
+ case CMD_THREAD_SET_IP: {
+ DebuggerTlsData *tls;
+ MonoMethod *method;
+ MonoDomain *domain;
+ MonoSeqPointInfo *seq_points;
+ SeqPoint *sp = NULL;
+ gint64 il_offset;
+ int i;
+
+ method = decode_methodid (p, &p, end, &domain, &err);
+ if (err)
+ return err;
+ il_offset = decode_long (p, &p, end);
+
+ while (!is_suspended ()) {
+ if (suspend_count)
+ wait_for_suspend ();
+ }
+
+ mono_loader_lock ();
+ tls = mono_g_hash_table_lookup (thread_to_tls, thread);
+ mono_loader_unlock ();
+ g_assert (tls);
+
+ compute_frame_info (thread, tls);
+ if (tls->frame_count == 0 || tls->frames [0]->actual_method != method)
+ return ERR_INVALID_ARGUMENT;
+
+ seq_points = get_seq_points (domain, method);
+ g_assert (seq_points);
+
+ for (i = 0; i < seq_points->len; ++i) {
+ sp = &seq_points->seq_points [i];
+
+ if (sp->il_offset == il_offset)
+ break;
+ }
+ if (i == seq_points->len)
+ return ERR_INVALID_ARGUMENT;
+
+ // FIXME: Check that the ip change is safe
+
+ DEBUG (1, fprintf (log_file, "[dbg] Setting IP to %s:0x%0x(0x%0x)\n", tls->frames [0]->actual_method->name, (int)sp->il_offset, (int)sp->native_offset));
+ MONO_CONTEXT_SET_IP (&tls->restore_ctx, (guint8*)tls->frames [0]->ji->code_start + sp->native_offset);
+ break;
+ }
default:
return ERR_NOT_IMPLEMENTED;
}
case CMD_SET_OBJECT_REF:
return "OBJECT_REF";
case CMD_SET_STRING_REF:
- return "STRING_REF";
+ return "STRING_REF";
case CMD_SET_THREAD:
- return "THREAD";
+ return "THREAD";
case CMD_SET_ARRAY_REF:
- return "ARRAY_REF";
+ return "ARRAY_REF";
case CMD_SET_EVENT_REQUEST:
- return "EVENT_REQUEST";
+ return "EVENT_REQUEST";
case CMD_SET_STACK_FRAME:
- return "STACK_FRAME";
+ return "STACK_FRAME";
case CMD_SET_APPDOMAIN:
- return "APPDOMAIN";
+ return "APPDOMAIN";
case CMD_SET_ASSEMBLY:
- return "ASSEMBLY";
+ return "ASSEMBLY";
case CMD_SET_METHOD:
- return "METHOD";
+ return "METHOD";
case CMD_SET_TYPE:
- return "TYPE";
+ return "TYPE";
case CMD_SET_MODULE:
- return "MODULE";
+ return "MODULE";
+ case CMD_SET_FIELD:
+ return "FIELD";
case CMD_SET_EVENT:
- return "EVENT";
+ return "EVENT";
default:
return "";
}
"GET_STATE",
"GET_INFO",
"GET_ID",
- "GET_TID"
+ "GET_TID",
+ "SET_IP"
};
static const char* event_cmds_str[] = {
"GET_INFO",
};
+static const char* field_cmds_str[] = {
+ "GET_INFO",
+};
+
static const char* method_cmds_str[] = {
"GET_NAME",
"GET_DECLARING_TYPE",
cmds = module_cmds_str;
cmds_len = G_N_ELEMENTS (module_cmds_str);
break;
+ case CMD_SET_FIELD:
+ cmds = field_cmds_str;
+ cmds_len = G_N_ELEMENTS (field_cmds_str);
+ break;
case CMD_SET_EVENT:
cmds = event_cmds_str;
cmds_len = G_N_ELEMENTS (event_cmds_str);
case CMD_SET_MODULE:
err = module_commands (command, p, end, &buf);
break;
+ case CMD_SET_FIELD:
+ err = field_commands (command, p, end, &buf);
+ break;
case CMD_SET_TYPE:
err = type_commands (command, p, end, &buf);
break;