#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>
+#include <mono/metadata/runtime.h>
+#include <mono/metadata/threadpool.h>
+#include <mono/metadata/verify-internals.h>
#include <mono/utils/mono-semaphore.h>
#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>
+#include <mono/utils/mono-mutex.h>
/* Definitions to make backporting to 2.6 easier */
//#define MonoInternalThread MonoThread
gboolean embedding;
gboolean defer;
int keepalive;
+ gboolean setpgid;
} AgentConfig;
typedef struct
* 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;
* The context where single stepping should resume while the thread is suspended because
* of an EXCEPTION event.
*/
- MonoContext catch_ctx;
-
- gboolean has_catch_ctx;
+ MonoThreadUnwindState catch_state;
/*
* The context which needs to be restored after handling a single step/breakpoint
#define HEADER_LENGTH 11
#define MAJOR_VERSION 2
-#define MINOR_VERSION 17
+#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;
typedef enum {
STEP_FILTER_NONE = 0,
- STEP_FILTER_STATIC_CTOR = 1
+ STEP_FILTER_STATIC_CTOR = 1,
+ STEP_FILTER_DEBUGGER_HIDDEN = 2,
+ STEP_FILTER_DEBUGGER_STEP_THROUGH = 4
} 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_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,
CMD_METHOD_GET_INFO = 6,
CMD_METHOD_GET_BODY = 7,
CMD_METHOD_RESOLVE_TOKEN = 8,
+ CMD_METHOD_GET_CATTRS = 9,
+ CMD_METHOD_MAKE_GENERIC_METHOD = 10
} CmdMethod;
typedef enum {
CMD_TYPE_GET_METHODS_BY_NAME_FLAGS = 15,
CMD_TYPE_GET_INTERFACES = 16,
CMD_TYPE_GET_INTERFACE_MAP = 17,
+ CMD_TYPE_IS_INITIALIZED = 18
} CmdType;
typedef enum {
GHashTable *type_names; /* For kind == MONO_KIND_TYPE_NAME_ONLY */
StepFilter filter; /* For kind == MOD_KIND_STEP */
} data;
- gboolean caught, uncaught; /* For kind == MOD_KIND_EXCEPTION_ONLY */
+ gboolean caught, uncaught, subclasses; /* For kind == MOD_KIND_EXCEPTION_ONLY */
} Modifier;
typedef struct{
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;
/*
/* 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);
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");
}
char **args, **ptr;
char *host;
int port;
- char *extra;
+ const char *extra;
#ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED
fprintf (stderr, "--debugger-agent is not supported on this platform.\n");
exit (1);
#endif
- extra = getenv ("MONO_SDB_ENV_OPTIONS");
+ extra = g_getenv ("MONO_SDB_ENV_OPTIONS");
if (extra)
options = g_strdup_printf ("%s,%s", options, extra);
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));
}
}
event_requests = g_ptr_array_new ();
- mono_mutex_init (&debugger_thread_exited_mutex, NULL);
+ mono_mutex_init (&debugger_thread_exited_mutex);
mono_cond_init (&debugger_thread_exited_cond, NULL);
mono_profiler_install ((MonoProfiler*)&debugger_profiler, runtime_shutdown);
*/
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);
-
- /* 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);
}
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 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);
}
}
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_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;
}
static inline MonoClass*
decode_typeid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
{
- return decode_ptr_id (buf, endbuf, limit, ID_TYPE, domain, err);
+ MonoClass *klass;
+
+ klass = decode_ptr_id (buf, endbuf, limit, ID_TYPE, domain, err);
+ if (G_UNLIKELY (log_level >= 2) && klass) {
+ char *s;
+
+ s = mono_type_full_name (&klass->byval_arg);
+ DEBUG(2, fprintf (log_file, "[dbg] recv class [%s]\n", s));
+ g_free (s);
+ }
+ return klass;
}
static inline MonoAssembly*
static inline MonoMethod*
decode_methodid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
{
- return decode_ptr_id (buf, endbuf, limit, ID_METHOD, domain, err);
+ MonoMethod *m;
+
+ m = decode_ptr_id (buf, endbuf, limit, ID_METHOD, domain, err);
+ if (G_UNLIKELY (log_level >= 2) && m) {
+ char *s;
+
+ s = mono_method_full_name (m, TRUE);
+ DEBUG(2, fprintf (log_file, "[dbg] recv method [%s]\n", s));
+ g_free (s);
+ }
+ return m;
}
static inline MonoClassField*
buffer_add_typeid (Buffer *buf, MonoDomain *domain, MonoClass *klass)
{
buffer_add_ptr_id (buf, domain, ID_TYPE, klass);
+ if (G_UNLIKELY (log_level >= 2) && klass) {
+ char *s;
+
+ s = mono_type_full_name (&klass->byval_arg);
+ if (GetCurrentThreadId () == debugger_thread_id)
+ DEBUG(2, fprintf (log_file, "[dbg] send class [%s]\n", s));
+ else
+ DEBUG(2, fprintf (log_file, "[%p] send class [%s]\n", (gpointer)GetCurrentThreadId (), s));
+ g_free (s);
+ }
}
static inline void
buffer_add_methodid (Buffer *buf, MonoDomain *domain, MonoMethod *method)
{
buffer_add_ptr_id (buf, domain, ID_METHOD, method);
+ if (G_UNLIKELY (log_level >= 2) && method) {
+ char *s;
+
+ s = mono_method_full_name (method, 1);
+ DEBUG(2, fprintf (log_file, "[dbg] send method [%s]\n", s));
+ g_free (s);
+ }
}
static inline void
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
static void
suspend_init (void)
{
- mono_mutex_init (&suspend_mutex, NULL);
+ mono_mutex_init (&suspend_mutex);
mono_cond_init (&suspend_cond, NULL);
MONO_SEM_INIT (&suspend_sem, 0);
}
/* Store the context/lmf for the frame above the last frame */
memcpy (&data->ctx, ctx, sizeof (MonoContext));
data->lmf = info->lmf;
-
return TRUE;
}
}
if (ji) {
/* Running managed code, will be suspended by the single step code */
- DEBUG (1, fprintf (log_file, "[%p] Received interrupt while at %s(%p), continuing.\n", (gpointer)(gsize)tid, ji->method->name, ip));
+ DEBUG (1, fprintf (log_file, "[%p] Received interrupt while at %s(%p), continuing.\n", (gpointer)(gsize)tid, jinfo_get_method (ji)->name, ip));
return TRUE;
} else {
/*
/* Already terminated */
return TRUE;
- tls->context.valid = FALSE;
-
/*
* We are in a difficult position: we want to be able to provide stack
* traces for this thread, but we can't use the current ctx+lmf, since
tls->async_state.unwind_data [MONO_UNWIND_DATA_LMF] = data.lmf;
tls->async_state.unwind_data [MONO_UNWIND_DATA_JIT_TLS] = tls->thread->jit_data;
} else {
- /* No managed frames */
tls->async_state.valid = FALSE;
}
+
mono_memory_barrier ();
tls->suspended = 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);
}
/*
* 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
}
{
guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
MonoJitInfo *ji;
+ MonoMethod *method;
if (mono_loader_lock_is_owned_by_self ()) {
/*
ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL);
/* Can't suspend in these methods */
- if (ji->method->klass == mono_defaults.string_class && (!strcmp (ji->method->name, "memset") || strstr (ji->method->name, "memcpy")))
+ method = jinfo_get_method (ji);
+ if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy")))
return;
save_thread_context (ctx);
mono_mutex_unlock (&suspend_mutex);
+ if (suspend_count == 1)
+ /*
+ * Suspend creation of new threadpool threads, since they cannot run
+ */
+ mono_thread_pool_suspend ();
+
mono_loader_unlock ();
}
mono_mutex_unlock (&suspend_mutex);
//g_assert (err == 0);
+ if (suspend_count == 0)
+ mono_thread_pool_resume ();
+
mono_loader_unlock ();
}
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;
/* 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
return seq_points;
}
-static MonoSeqPointInfo*
-find_seq_points (MonoDomain *domain, MonoMethod *method)
+static void
+no_seq_points_found (MonoMethod *method)
{
- MonoSeqPointInfo *seq_points = get_seq_points (domain, method);
-
- if (!seq_points)
- printf ("Unable to find seq points for method '%s'.\n", mono_method_full_name (method, TRUE));
- g_assert (seq_points);
-
- return seq_points;
+ /*
+ * This can happen in full-aot mode with assemblies AOTed without the 'soft-debug' option to save space.
+ */
+ printf ("Unable to find seq points for method '%s'.\n", mono_method_full_name (method, TRUE));
}
/*
MonoSeqPointInfo *seq_points;
int i;
- seq_points = find_seq_points (domain, method);
+ seq_points = get_seq_points (domain, method);
+ if (!seq_points) {
+ if (info)
+ *info = NULL;
+ return NULL;
+ }
+ 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;
}
if (info->ji)
- method = info->ji->method;
+ method = jinfo_get_method (info->ji);
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) {
if (mod->data.thread != mono_thread_internal_current ())
filtered = TRUE;
} else if (mod->kind == MOD_KIND_EXCEPTION_ONLY && ei) {
- if (mod->data.exc_class && !mono_class_is_assignable_from (mod->data.exc_class, ei->exc->vtable->klass))
+ if (mod->data.exc_class && mod->subclasses && !mono_class_is_assignable_from (mod->data.exc_class, ei->exc->vtable->klass))
+ filtered = TRUE;
+ if (mod->data.exc_class && !mod->subclasses && mod->data.exc_class != ei->exc->vtable->klass)
filtered = TRUE;
if (ei->caught && !mod->caught)
filtered = TRUE;
if (assemblies) {
for (k = 0; assemblies [k]; ++k)
- if (assemblies [k] == ji->method->klass->image->assembly)
+ if (assemblies [k] == jinfo_get_method (ji)->klass->image->assembly)
found = TRUE;
}
if (!found)
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);
/*
s = strdup_tolower (sinfo->source_file);
if (g_hash_table_lookup (mod->data.source_files, s))
found = TRUE;
+ else {
+ char *s2 = g_path_get_basename (sinfo->source_file);
+ char *s3 = strdup_tolower (s2);
+
+ if (g_hash_table_lookup (mod->data.source_files, s3))
+ found = TRUE;
+ g_free (s2);
+ g_free (s3);
+ }
g_free (s);
}
g_ptr_array_free (source_file_list, TRUE);
g_free (s);
} else if (mod->kind == MOD_KIND_STEP) {
if ((mod->data.filter & STEP_FILTER_STATIC_CTOR) && ji &&
- (ji->method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) &&
- !strcmp (ji->method->name, ".cctor"))
+ (jinfo_get_method (ji)->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) &&
+ !strcmp (jinfo_get_method (ji)->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 (jinfo_get_method (ji));
+ 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;
+ }
+ if ((mod->data.filter & STEP_FILTER_DEBUGGER_STEP_THROUGH) && ji) {
+ MonoCustomAttrInfo *ainfo;
+ static MonoClass *klass;
+
+ if (!klass) {
+ klass = mono_class_from_name (mono_defaults.corlib, "System.Diagnostics", "DebuggerStepThroughAttribute");
+ g_assert (klass);
+ }
+ if (!ji->dbg_step_through_inited) {
+ ainfo = mono_custom_attrs_from_method (jinfo_get_method (ji));
+ if (ainfo) {
+ if (mono_custom_attrs_has_attr (ainfo, klass))
+ ji->dbg_step_through = TRUE;
+ mono_custom_attrs_free (ainfo);
+ }
+ ainfo = mono_custom_attrs_from_class (jinfo_get_method (ji)->klass);
+ if (ainfo) {
+ if (mono_custom_attrs_has_attr (ainfo, klass))
+ ji->dbg_step_through = TRUE;
+ mono_custom_attrs_free (ainfo);
+ }
+ ji->dbg_step_through_inited = TRUE;
+ }
+ if (ji->dbg_step_through)
+ filtered = TRUE;
+ }
}
}
case EVENT_KIND_USER_LOG: return "USER_LOG";
default:
g_assert_not_reached ();
+ return "";
}
}
GSList *l;
MonoDomain *domain = mono_domain_get ();
MonoThread *thread = NULL;
+ MonoObject *keepalive_obj = NULL;
gboolean send_success = FALSE;
static int ecount;
int nevents;
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;
buffer_add_objid (&buf, ei->exc);
+ /*
+ * We are not yet suspending, so get_objref () will not keep this object alive. So we need to do it
+ * later after the suspension. (#12494).
+ */
+ keepalive_obj = ei->exc;
break;
}
case EVENT_KIND_USER_BREAK:
*/
save_thread_context (ctx);
suspend_vm ();
+
+ if (keepalive_obj)
+ /* This will keep this object alive */
+ get_objref (keepalive_obj);
}
send_success = send_packet (CMD_SET_EVENT, CMD_COMPOSITE, &buf);
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);
}
if (i == seq_points->len) {
- 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);
+ char *s = g_strdup_printf ("Unable to insert breakpoint at %s:%d, seq_points=%d\n", mono_method_full_name (jinfo_get_method (ji), 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);
+ g_warning ("%s", s);
g_free (s);
return;
} else {
- g_error ("%s", s);
+ g_warning ("%s", s);
g_free (s);
+ return;
}
}
g_hash_table_insert (bp_locs, inst->ip, GINT_TO_POINTER (count + 1));
mono_loader_unlock ();
- if (count == 0) {
+ if (sp->native_offset == SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) {
+ DEBUG (1, fprintf (log_file, "[dbg] Attempting to insert seq point at dead IL offset %d, ignoring.\n", (int)bp->il_offset));
+ } else if (count == 0) {
#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
mono_arch_set_breakpoint (ji, inst->ip);
#else
#endif
}
- DEBUG(1, fprintf (log_file, "[dbg] Inserted breakpoint at %s:0x%x.\n", mono_method_full_name (ji->method, TRUE), (int)sp->il_offset));
+ DEBUG(1, fprintf (log_file, "[dbg] Inserted breakpoint at %s:0x%x.\n", mono_method_full_name (jinfo_get_method (ji), TRUE), (int)sp->il_offset));
}
static void
g_assert (count > 0);
- if (count == 1) {
+ if (count == 1 && inst->native_offset != SEQ_POINT_NATIVE_OFFSET_DEAD_CODE) {
mono_arch_clear_breakpoint (ji, ip);
}
#else
int i, j;
MonoSeqPointInfo *seq_points;
MonoDomain *domain;
+ MonoMethod *jmethod;
if (!breakpoints)
return;
}
if (!found) {
+ jmethod = jinfo_get_method (ji);
mono_domain_lock (domain);
- seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, ji->method);
- if (!seq_points && ji->method->is_inflated)
- seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mono_method_get_declaring_generic_method (ji->method));
+ seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, jmethod);
+ if (!seq_points && jmethod->is_inflated)
+ seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, mono_method_get_declaring_generic_method (jmethod));
mono_domain_unlock (domain);
if (!seq_points)
/* Could be AOT code */
mono_loader_unlock ();
}
+/*
+ * ss_update:
+ *
+ * Return FALSE if single stepping needs to continue.
+ */
+static gboolean
+ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, DebuggerTlsData *tls, MonoContext *ctx)
+{
+ MonoDebugMethodInfo *minfo;
+ MonoDebugSourceLocation *loc = NULL;
+ gboolean hit = TRUE;
+ MonoMethod *method;
+
+ if (req->depth == STEP_DEPTH_OVER && (sp->flags & MONO_SEQ_POINT_FLAG_NONEMPTY_STACK)) {
+ /*
+ * These seq points are inserted by the JIT after calls, step over needs to skip them.
+ */
+ DEBUG (1, fprintf (log_file, "[%p] Seq point at nonempty stack %x while stepping over, continuing single stepping.\n", (gpointer)GetCurrentThreadId (), sp->il_offset));
+ 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;
+
+ /* Have to check whenever a different source line was reached */
+ method = jinfo_get_method (ji);
+ minfo = mono_debug_lookup_method (method);
+
+ if (minfo)
+ loc = mono_debug_symfile_lookup_location (minfo, sp->il_offset);
+
+ if (!loc || (loc && method == ss_req->last_method && loc->row == ss_req->last_line)) {
+ /* Have to continue single stepping */
+ if (!loc)
+ DEBUG(1, fprintf (log_file, "[%p] No line number info for il offset %x, continuing single stepping.\n", (gpointer)GetCurrentThreadId (), sp->il_offset));
+ else
+ DEBUG(1, fprintf (log_file, "[%p] Same source line (%d), continuing single stepping.\n", (gpointer)GetCurrentThreadId (), loc->row));
+ hit = FALSE;
+ }
+
+ if (loc) {
+ ss_req->last_method = method;
+ ss_req->last_line = loc->row;
+ mono_debug_free_source_location (loc);
+ }
+
+ return hit;
+}
+
static gboolean
breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly)
{
GSList *bp_events = NULL, *ss_events = NULL, *enter_leave_events = NULL;
EventKind kind = EVENT_KIND_BREAKPOINT;
MonoContext *ctx = &tls->restore_ctx;
+ MonoMethod *method;
MonoSeqPointInfo *info;
SeqPoint *sp;
ip = MONO_CONTEXT_GET_IP (ctx);
ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, NULL);
g_assert (ji);
- g_assert (ji->method);
+ method = jinfo_get_method (ji);
/* Compute the native offset of the breakpoint from the ip */
native_offset = ip - (guint8*)ji->code_start;
*/
mono_arch_skip_breakpoint (ctx, ji);
- if (ji->method->wrapper_type || tls->disable_breakpoints)
+ if (method->wrapper_type || tls->disable_breakpoints)
return;
bp_reqs = g_ptr_array_new ();
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 ip points to the instruction causing the breakpoint event, which is after
* 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);
+ sp = find_prev_seq_point_for_native_offset (mono_domain_get (), method, native_offset, &info);
+ if (!sp)
+ no_seq_points_found (method);
+ 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 (), method->name, ip, native_offset, sp ? sp->il_offset : -1));
bp = NULL;
for (i = 0; i < breakpoints->len; ++i) {
for (i = 0; i < ss_reqs_orig->len; ++i) {
EventRequest *req = g_ptr_array_index (ss_reqs_orig, i);
SingleStepReq *ss_req = req->info;
- gboolean hit = TRUE;
-
- if (ss_req->size == STEP_SIZE_LINE) {
- /* Have to check whenever a different source line was reached */
- MonoDebugMethodInfo *minfo;
- MonoDebugSourceLocation *loc = NULL;
-
- minfo = mono_debug_lookup_method (ji->method);
-
- if (minfo)
- loc = mono_debug_symfile_lookup_location (minfo, sp->il_offset);
+ gboolean hit;
- if (!loc || (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line))
- /* Have to continue single stepping */
- hit = FALSE;
-
- if (loc) {
- ss_req->last_method = ji->method;
- ss_req->last_line = loc->row;
- mono_debug_free_source_location (loc);
- }
- }
+ if (mono_thread_internal_current () != ss_req->thread)
+ continue;
+ hit = ss_update (ss_req, ji, sp, tls, ctx);
if (hit)
g_ptr_array_add (ss_reqs, req);
/* Start single stepping again from the current sequence point */
- ss_start (ss_req, ji->method, sp, info, ctx, tls, FALSE);
+ ss_start (ss_req, method, sp, info, ctx, tls, FALSE);
}
if (ss_reqs->len > 0)
* resume.
*/
if (ss_events)
- process_event (EVENT_KIND_STEP, ji->method, 0, ctx, ss_events, suspend_policy);
+ process_event (EVENT_KIND_STEP, method, 0, ctx, ss_events, suspend_policy);
if (bp_events)
- process_event (kind, ji->method, 0, ctx, bp_events, suspend_policy);
+ process_event (kind, method, 0, ctx, bp_events, suspend_policy);
if (enter_leave_events)
- process_event (kind, ji->method, 0, ctx, enter_leave_events, suspend_policy);
+ process_event (kind, method, 0, ctx, enter_leave_events, suspend_policy);
}
/* Process a breakpoint/single step event after resuming from a signal handler */
{
DebuggerTlsData *tls;
MonoContext orig_restore_ctx, ctx;
- static void (*restore_context) (void *);
-
- if (!restore_context)
- restore_context = mono_get_restore_context ();
tls = mono_native_tls_get_value (debugger_tls_id);
/* Have to save/restore the restore_ctx as we can be called recursively during invokes etc. */
/* This is called when resuming from a signal handler, so it shouldn't return */
memcpy (&ctx, &tls->restore_ctx, sizeof (MonoContext));
memcpy (&tls->restore_ctx, &orig_restore_ctx, sizeof (MonoContext));
- restore_context (&ctx);
+ mono_restore_context (&ctx);
g_assert_not_reached ();
}
/* Save the original context in TLS */
// FIXME: This might not work on an altstack ?
tls = mono_native_tls_get_value (debugger_tls_id);
+ if (!tls)
+ fprintf (stderr, "Thread %p is not attached to the JIT.\n", (gpointer)GetCurrentThreadId ());
g_assert (tls);
// FIXME: MonoContext usually doesn't include the fp registers, so these are
* 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);
}
MonoDomain *domain;
GSList *events;
MonoContext *ctx = &tls->restore_ctx;
+ MonoMethod *method;
SeqPoint *sp;
MonoSeqPointInfo *info;
if (log_level > 0) {
ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain);
- DEBUG (1, fprintf (log_file, "[%p] Single step event (depth=%s) at %s (%p), sp %p, last sp %p\n", (gpointer)GetCurrentThreadId (), ss_depth_to_string (ss_req->depth), mono_method_full_name (ji->method, TRUE), MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), ss_req->last_sp));
+ DEBUG (1, fprintf (log_file, "[%p] Single step event (depth=%s) at %s (%p), sp %p, last sp %p\n", (gpointer)GetCurrentThreadId (), ss_depth_to_string (ss_req->depth), mono_method_full_name (jinfo_get_method (ji), TRUE), MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), ss_req->last_sp));
}
ji = mini_jit_info_table_find (mono_domain_get (), (char*)ip, &domain);
g_assert (ji);
- g_assert (ji->method);
+ method = jinfo_get_method (ji);
+ g_assert (method);
- if (ji->method->wrapper_type && ji->method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
+ if (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
return;
/*
- * FIXME:
+ * FIXME:
* Stopping in memset makes half-initialized vtypes visible.
* Stopping in memcpy makes half-copied vtypes visible.
*/
- if (ji->method->klass == mono_defaults.string_class && (!strcmp (ji->method->name, "memset") || strstr (ji->method->name, "memcpy")))
+ if (method->klass == mono_defaults.string_class && (!strcmp (method->name, "memset") || strstr (method->name, "memcpy")))
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.
*/
- sp = find_next_seq_point_for_native_offset (domain, ji->method, (guint8*)ip - (guint8*)ji->code_start, &info);
+ sp = find_next_seq_point_for_native_offset (domain, method, (guint8*)ip - (guint8*)ji->code_start, &info);
if (!sp)
return;
il_offset = sp->il_offset;
- // FIXME: No tests fail if this is disabled
-#if 0
- if (ss_req->size == STEP_SIZE_LINE) {
- // FIXME:
- NOT_IMPLEMENTED;
-
- /* Step until a different source line is reached */
- MonoDebugMethodInfo *minfo;
-
- minfo = mono_debug_lookup_method (ji->method);
-
- if (minfo) {
- MonoDebugSourceLocation *loc = mono_debug_symfile_lookup_location (minfo, il_offset);
-
- if (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line) {
- mono_debug_free_source_location (loc);
- return;
- }
- if (!loc)
- /*
- * Step until we reach a location with line number info,
- * otherwise the client can't show a location.
- * This can happen for example with statics initialized inline
- * outside of a cctor.
- */
- return;
-
- if (loc) {
- ss_req->last_method = ji->method;
- ss_req->last_line = loc->row;
- mono_debug_free_source_location (loc);
- }
- }
- }
-#endif
+ if (!ss_update (ss_req, ji, sp, tls, ctx))
+ return;
/* Start single stepping again from the current sequence point */
- ss_start (ss_req, ji->method, sp, info, ctx, tls, FALSE);
+ ss_start (ss_req, method, sp, info, ctx, tls, FALSE);
if ((ss_req->filter & STEP_FILTER_STATIC_CTOR) &&
- (ji->method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) &&
- !strcmp (ji->method->name, ".cctor"))
+ (method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) &&
+ !strcmp (method->name, ".cctor"))
return;
// FIXME: Has to lock earlier
mono_loader_unlock ();
- process_event (EVENT_KIND_STEP, ji->method, il_offset, ctx, events, suspend_policy);
+ process_event (EVENT_KIND_STEP, jinfo_get_method (ji), il_offset, ctx, events, suspend_policy);
}
static void
if (val == 0)
mono_arch_stop_single_stepping ();
+ if (ss_req != NULL)
+ ss_invoke_addr = NULL;
#else
g_assert_not_reached ();
#endif
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) {
+ if (sp && sp->next_len == 0) {
sp = NULL;
- if (frame_index < tls->frame_count) {
+ while (frame_index < tls->frame_count) {
StackFrame *frame = tls->frames [frame_index];
method = frame->method;
sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info);
+ if (sp && sp->next_len != 0)
+ break;
+ sp = NULL;
frame_index ++;
}
}
}
}
+ 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];
+
+ 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;
g_assert (tls->context.valid);
ss_req->start_sp = ss_req->last_sp = MONO_CONTEXT_GET_SP (&tls->context.ctx);
- if (tls->has_catch_ctx) {
+ if (tls->catch_state.valid) {
gboolean res;
StackFrameInfo frame;
MonoContext new_ctx;
*/
/* Find the the jit info for the catch context */
- res = mono_find_jit_info_ext (mono_domain_get (), thread->jit_data, NULL, &tls->catch_ctx, &new_ctx, NULL, &lmf, NULL, &frame);
+ res = mono_find_jit_info_ext (tls->catch_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], thread->jit_data, NULL, &tls->catch_state.ctx, &new_ctx, NULL, &lmf, NULL, &frame);
g_assert (res);
g_assert (frame.type == FRAME_TYPE_MANAGED);
* point after ip.
*/
sp = find_next_seq_point_for_native_offset (frame.domain, frame.method, frame.native_offset, &info);
+ if (!sp)
+ no_seq_points_found (frame.method);
g_assert (sp);
method = frame.method;
if (!method && frame->il_offset != -1) {
/* FIXME: Sort the table and use a binary search */
sp = find_prev_seq_point_for_native_offset (frame->domain, frame->method, frame->native_offset, &info);
+ if (!sp)
+ no_seq_points_found (frame->method);
g_assert (sp);
method = frame->method;
}
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] == jinfo_get_method (catch_ji)->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) {
- tls->catch_ctx = *catch_ctx;
- tls->has_catch_ctx = TRUE;
+ if (tls && ei.caught && catch_ctx) {
+ memset (&tls->catch_state, 0, sizeof (tls->catch_state));
+ tls->catch_state.ctx = *catch_ctx;
+ tls->catch_state.unwind_data [MONO_UNWIND_DATA_DOMAIN] = mono_domain_get ();
+ tls->catch_state.valid = TRUE;
}
process_event (EVENT_KIND_EXCEPTION, &ei, 0, throw_ctx, events, suspend_policy);
if (tls)
- tls->has_catch_ctx = FALSE;
+ tls->catch_state.valid = FALSE;
}
void
MonoObject *obj;
if (t->byref) {
+ if (!(*(void**)addr)) {
+ /* This can happen with compiler generated locals */
+ //printf ("%s\n", mono_type_full_name (t));
+ buffer_add_byte (buf, VALUE_TYPE_ID_NULL);
+ return;
+ }
g_assert (*(void**)addr);
addr = *(void**)addr;
}
buffer_add_value_full (buf, t, addr, domain, FALSE);
}
-static ErrorCode
-decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
+static gboolean
+obj_is_of_type (MonoObject *obj, MonoType *t)
{
- int err;
- int type = decode_byte (buf, &buf, limit);
+ MonoClass *klass = obj->vtable->klass;
+ if (!mono_class_is_assignable_from (mono_class_from_mono_type (t), klass)) {
+ if (mono_class_is_transparent_proxy (klass)) {
+ 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);
+
+static ErrorCode
+decode_vtype (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
+{
+ gboolean is_enum;
+ MonoClass *klass;
+ MonoClassField *f;
+ int nfields;
+ gpointer iter = NULL;
+ MonoDomain *d;
+ int err;
+
+ is_enum = decode_byte (buf, &buf, limit);
+ /* Enums are sent as a normal vtype */
+ if (is_enum)
+ return ERR_NOT_IMPLEMENTED;
+ klass = decode_typeid (buf, &buf, limit, &d, &err);
+ if (err)
+ return err;
+
+ if (t && klass != mono_class_from_mono_type (t)) {
+ char *name = mono_type_full_name (t);
+ char *name2 = mono_type_full_name (&klass->byval_arg);
+ DEBUG(1, fprintf (log_file, "[%p] Expected value of type %s, got %s.\n", (gpointer)GetCurrentThreadId (), name, name2));
+ g_free (name);
+ g_free (name2);
+ return ERR_INVALID_ARGUMENT;
+ }
+
+ nfields = decode_int (buf, &buf, limit);
+ while ((f = mono_class_get_fields (klass, &iter))) {
+ if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
+ continue;
+ if (mono_field_is_deleted (f))
+ continue;
+ err = decode_value (f->type, domain, (guint8*)addr + f->offset - sizeof (MonoObject), buf, &buf, limit);
+ if (err)
+ return err;
+ nfields --;
+ }
+ g_assert (nfields == 0);
+
+ *endbuf = buf;
+
+ return 0;
+}
+
+static ErrorCode
+decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
+{
+ int err;
if (type != t->type && !MONO_TYPE_IS_REFERENCE (t) &&
!(t->type == MONO_TYPE_I && type == MONO_TYPE_VALUETYPE) &&
g_assert (type == MONO_TYPE_VALUETYPE);
/* Fall through */
handle_vtype:
- case MONO_TYPE_VALUETYPE: {
- gboolean is_enum = decode_byte (buf, &buf, limit);
- MonoClass *klass;
- MonoClassField *f;
- int nfields;
- gpointer iter = NULL;
- MonoDomain *d;
-
- /* Enums are sent as a normal vtype */
- if (is_enum)
- return ERR_NOT_IMPLEMENTED;
- klass = decode_typeid (buf, &buf, limit, &d, &err);
+ case MONO_TYPE_VALUETYPE:
+ err = decode_vtype (t, domain, addr,buf, &buf, limit);
if (err)
return err;
-
- if (klass != mono_class_from_mono_type (t))
- return ERR_INVALID_ARGUMENT;
-
- nfields = decode_int (buf, &buf, limit);
- while ((f = mono_class_get_fields (klass, &iter))) {
- if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
- continue;
- if (mono_field_is_deleted (f))
- continue;
- err = decode_value (f->type, domain, (guint8*)addr + f->offset - sizeof (MonoObject), buf, &buf, limit);
- if (err)
- return err;
- nfields --;
- }
- g_assert (nfields == 0);
break;
- }
handle_ref:
default:
if (MONO_TYPE_IS_REFERENCE (t)) {
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;
mono_gc_wbarrier_generic_store (addr, obj);
} else if (type == VALUE_TYPE_ID_NULL) {
*(MonoObject**)addr = NULL;
+ } else if (type == MONO_TYPE_VALUETYPE) {
+ guint8 *buf2;
+ gboolean is_enum;
+ MonoClass *klass;
+ MonoDomain *d;
+ guint8 *vtype_buf;
+ int vtype_buf_size;
+
+ /* This can happen when round-tripping boxed vtypes */
+ /*
+ * Obtain vtype class.
+ * Same as the beginning of the handle_vtype case above.
+ */
+ buf2 = buf;
+ is_enum = decode_byte (buf, &buf, limit);
+ if (is_enum)
+ return ERR_NOT_IMPLEMENTED;
+ klass = decode_typeid (buf, &buf, limit, &d, &err);
+ if (err)
+ return err;
+
+ /* Decode the vtype into a temporary buffer, then box it. */
+ vtype_buf_size = mono_class_value_size (klass, NULL);
+ vtype_buf = g_malloc0 (vtype_buf_size);
+ g_assert (vtype_buf);
+
+ buf = buf2;
+ err = decode_vtype (NULL, domain, vtype_buf, buf, &buf, limit);
+ if (err) {
+ g_free (vtype_buf);
+ return err;
+ }
+ *(MonoObject**)addr = mono_value_box (d, klass, vtype_buf);
+ g_free (vtype_buf);
} else {
+ char *name = mono_type_full_name (t);
+ DEBUG(1, fprintf (log_file, "[%p] Expected value of type %s, got 0x%0x.\n", (gpointer)GetCurrentThreadId (), name, type));
+ g_free (name);
return ERR_INVALID_ARGUMENT;
}
} else {
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)
+add_var (Buffer *buf, MonoDebugMethodJitInfo *jit, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, gboolean as_vtype)
{
guint32 flags;
int reg;
- guint8 *addr;
+ guint8 *addr, *gaddr;
mgreg_t reg_val;
flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
NOT_IMPLEMENTED;
break;
+ case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET_INDIR:
+ case MONO_DEBUG_VAR_ADDRESS_MODE_VTADDR:
+ /* Same as regoffset, but with an indirection */
+ addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
+ addr += (gint32)var->offset;
+
+ gaddr = *(gpointer*)addr;
+ g_assert (gaddr);
+ buffer_add_value_full (buf, t, gaddr, domain, as_vtype);
+ break;
+ case MONO_DEBUG_VAR_ADDRESS_MODE_GSHAREDVT_LOCAL: {
+ MonoDebugVarInfo *info_var = jit->gsharedvt_info_var;
+ MonoDebugVarInfo *locals_var = jit->gsharedvt_locals_var;
+ MonoGSharedVtMethodRuntimeInfo *info;
+ guint8 *locals;
+ int idx;
+
+ idx = reg;
+
+ g_assert (info_var);
+ g_assert (locals_var);
+
+ flags = info_var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
+ reg = info_var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
+ if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET) {
+ addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
+ addr += (gint32)info_var->offset;
+ info = *(gpointer*)addr;
+ } else if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER) {
+ info = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
+ } else {
+ g_assert_not_reached ();
+ }
+ g_assert (info);
+
+ flags = locals_var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
+ reg = locals_var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
+ if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET) {
+ addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
+ addr += (gint32)locals_var->offset;
+ locals = *(gpointer*)addr;
+ } else if (flags == MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER) {
+ locals = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
+ } else {
+ g_assert_not_reached ();
+ }
+ g_assert (locals);
+
+ addr = locals + GPOINTER_TO_INT (info->entries [idx]);
+
+ buffer_add_value_full (buf, t, addr, domain, as_vtype);
+ break;
+ }
+
default:
g_assert_not_reached ();
}
{
guint32 flags;
int reg, size;
- guint8 *addr;
+ guint8 *addr, *gaddr;
flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
reg = var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
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
- memcpy (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 */
+ addr = (gpointer)mono_arch_context_get_int_reg (ctx, reg);
+ addr += (gint32)var->offset;
+
+ gaddr = *(gpointer*)addr;
+ g_assert (gaddr);
+ // FIXME: Write barriers
+ mono_gc_memmove_atomic (gaddr, 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 (m->klass->valuetype && (m->flags & METHOD_ATTRIBUTE_STATIC)) {
/* Should be null */
int type = decode_byte (p, &p, end);
- if (type != VALUE_TYPE_ID_NULL)
+ if (type != VALUE_TYPE_ID_NULL) {
+ DEBUG (1, fprintf (log_file, "[%p] Error: Static vtype method invoked with this argument.\n", (gpointer)GetCurrentThreadId ()));
return ERR_INVALID_ARGUMENT;
+ }
memset (this_buf, 0, mono_class_instance_size (m->klass));
} else {
err = decode_value (&m->klass->byval_arg, domain, this_buf, p, &p, end);
else
this = NULL;
+ if (MONO_CLASS_IS_INTERFACE (m->klass)) {
+ if (!this) {
+ DEBUG (1, fprintf (log_file, "[%p] Error: Interface method invoked without this argument.\n", (gpointer)GetCurrentThreadId ()));
+ return ERR_INVALID_ARGUMENT;
+ }
+ m = mono_object_get_virtual_method (this, m);
+ }
+
DEBUG (1, fprintf (log_file, "[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer)GetCurrentThreadId (), mono_method_full_name (m, TRUE), this ? this->vtable->klass->name : "<null>"));
if (this && this->vtable->domain != domain)
}
}
- 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);
/* 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));
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;
-
- if (!restore_context)
- restore_context = mono_get_restore_context ();
+ guint8 *p;
tls = mono_native_tls_get_value (debugger_tls_id);
g_assert (tls);
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));
tls->resume_count -= invoke->suspend_count;
}
- DEBUG (1, fprintf (log_file, "[%p] Invoke finished, resume_count = %d.\n", (gpointer)GetCurrentThreadId (), tls->resume_count));
+ DEBUG (1, fprintf (log_file, "[%p] Invoke finished (%d), resume_count = %d.\n", (gpointer)GetCurrentThreadId (), err, tls->resume_count));
/*
* Take the loader lock to avoid race conditions with CMD_VM_ABORT_INVOKE:
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 */
case CMD_VM_EXIT: {
MonoInternalThread *thread;
DebuggerTlsData *tls;
+#ifdef TRY_MANAGED_SYSTEM_ENVIRONMENT_EXIT
MonoClass *env_class;
+#endif
MonoMethod *exit_method = NULL;
gpointer *args;
int exit_code;
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 ();
while (suspend_count > 0)
resume_vm ();
- mono_runtime_set_shutting_down ();
+ if (!mono_runtime_try_shutdown ())
+ break;
- mono_threads_set_shutting_down ();
+ 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"));
}
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));
if (CHECK_PROTOCOL_VERSION (2, 16))
filter = decode_int (p, &p, end);
req->modifiers [i].data.filter = filter;
+ if (!CHECK_PROTOCOL_VERSION (2, 26) && (req->modifiers [i].data.filter & STEP_FILTER_DEBUGGER_HIDDEN))
+ /* Treat STEP_THOUGH the same as HIDDEN */
+ req->modifiers [i].data.filter |= STEP_FILTER_DEBUGGER_STEP_THROUGH;
} else if (mod == MOD_KIND_THREAD_ONLY) {
int id = decode_id (p, &p, end);
return err;
req->modifiers [i].caught = decode_byte (p, &p, end);
req->modifiers [i].uncaught = decode_byte (p, &p, end);
- DEBUG(1, fprintf (log_file, "[dbg] \tEXCEPTION_ONLY filter (%s%s%s).\n", exc_class ? exc_class->name : "all", req->modifiers [i].caught ? ", caught" : "", req->modifiers [i].uncaught ? ", uncaught" : ""));
+ if (CHECK_PROTOCOL_VERSION (2, 25))
+ req->modifiers [i].subclasses = decode_byte (p, &p, end);
+ else
+ req->modifiers [i].subclasses = TRUE;
+ DEBUG(1, fprintf (log_file, "[dbg] \tEXCEPTION_ONLY filter (%s%s%s%s).\n", exc_class ? exc_class->name : "all", req->modifiers [i].caught ? ", caught" : "", req->modifiers [i].uncaught ? ", uncaught" : "", req->modifiers [i].subclasses ? ", include-subclasses" : ""));
if (exc_class) {
req->modifiers [i].data.exc_class = exc_class;
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)
{
if (!attr_klass || mono_class_has_parent (attr->ctor->klass, attr_klass)) {
MonoArray *typed_args, *named_args;
MonoType *t;
- CattrNamedArg *arginfo;
+ CattrNamedArg *arginfo = NULL;
+ MonoError error;
- mono_reflection_create_custom_attr_data_args (image, attr->ctor, attr->data, attr->data_size, &typed_args, &named_args, &arginfo);
+ mono_reflection_create_custom_attr_data_args (image, attr->ctor, attr->data, attr->data_size, &typed_args, &named_args, &arginfo, &error);
+ g_assert (mono_error_ok (&error));
buffer_add_methodid (buf, domain, attr->ctor);
} else {
buffer_add_int (buf, 0);
}
+ g_free (arginfo);
}
}
}
}
break;
}
+ case CMD_TYPE_IS_INITIALIZED: {
+ MonoVTable *vtable = mono_class_vtable (domain, klass);
+
+ if (vtable)
+ buffer_add_int (buf, (vtable->initialized || vtable->init_failed) ? 1 : 0);
+ else
+ buffer_add_int (buf, 0);
+ break;
+ }
default:
return ERR_NOT_IMPLEMENTED;
}
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;
+ }
+ case CMD_METHOD_MAKE_GENERIC_METHOD: {
+ MonoType **type_argv;
+ int i, type_argc;
+ MonoDomain *d;
+ MonoClass *klass;
+ MonoGenericInst *ginst;
+ MonoGenericContext tmp_context;
+ MonoMethod *inflated;
+
+ type_argc = decode_int (p, &p, end);
+ type_argv = g_new0 (MonoType*, type_argc);
+ for (i = 0; i < type_argc; ++i) {
+ klass = decode_typeid (p, &p, end, &d, &err);
+ if (err) {
+ g_free (type_argv);
+ return err;
+ }
+ if (domain != d) {
+ g_free (type_argv);
+ return ERR_INVALID_ARGUMENT;
+ }
+ type_argv [i] = &klass->byval_arg;
+ }
+ ginst = mono_metadata_get_generic_inst (type_argc, type_argv);
+ g_free (type_argv);
+ tmp_context.class_inst = method->klass->generic_class ? method->klass->generic_class->context.class_inst : NULL;
+ tmp_context.method_inst = ginst;
+
+ inflated = mono_class_inflate_generic_method (method, &tmp_context);
+ if (!mono_verifier_is_method_valid_generic_instantiation (inflated))
+ return ERR_INVALID_ARGUMENT;
+ buffer_add_methodid (buf, domain, inflated);
+ break;
+ }
default:
return ERR_NOT_IMPLEMENTED;
}
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;
}
frame = tls->frames [frame_idx];
if (!frame->has_ctx)
- // FIXME:
- return ERR_INVALID_FRAMEID;
+ return ERR_ABSENT_INFORMATION;
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;
var = &jit->params [pos];
- add_var (buf, sig->params [pos], &jit->params [pos], &frame->ctx, frame->domain, FALSE);
+ add_var (buf, jit, sig->params [pos], &jit->params [pos], &frame->ctx, frame->domain, FALSE);
} else {
g_assert (pos >= 0 && pos < jit->num_locals);
var = &jit->locals [pos];
- add_var (buf, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE);
+ add_var (buf, jit, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain, FALSE);
}
}
mono_metadata_free_mh (header);
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);
} else {
- add_var (buf, &frame->actual_method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
+ add_var (buf, jit, &frame->actual_method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain, TRUE);
}
} else {
if (!sig->hasthis) {
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, jit, &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:
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 "";
}
}
+static const char* vm_cmds_str [] = {
+ "VERSION",
+ "ALL_THREADS",
+ "SUSPEND",
+ "RESUME",
+ "EXIT",
+ "DISPOSE",
+ "INVOKE_METHOD",
+ "SET_PROTOCOL_VERSION",
+ "ABORT_INVOKE",
+ "SET_KEEPALIVE"
+ "GET_TYPES_FOR_SOURCE_FILE",
+ "GET_TYPES",
+ "INVOKE_METHODS"
+};
+
+static const char* thread_cmds_str[] = {
+ "GET_FRAME_INFO",
+ "GET_NAME",
+ "GET_STATE",
+ "GET_INFO",
+ "GET_ID",
+ "GET_TID",
+ "SET_IP"
+};
+
+static const char* event_cmds_str[] = {
+ "REQUEST_SET",
+ "REQUEST_CLEAR",
+ "REQUEST_CLEAR_ALL_BREAKPOINTS"
+};
+
+static const char* appdomain_cmds_str[] = {
+ "GET_ROOT_DOMAIN",
+ "GET_FRIENDLY_NAME",
+ "GET_ASSEMBLIES",
+ "GET_ENTRY_ASSEMBLY",
+ "CREATE_STRING",
+ "GET_CORLIB",
+ "CREATE_BOXED_VALUE"
+};
+
+static const char* assembly_cmds_str[] = {
+ "GET_LOCATION",
+ "GET_ENTRY_POINT",
+ "GET_MANIFEST_MODULE",
+ "GET_OBJECT",
+ "GET_TYPE",
+ "GET_NAME"
+};
+
+static const char* module_cmds_str[] = {
+ "GET_INFO",
+};
+
+static const char* field_cmds_str[] = {
+ "GET_INFO",
+};
+
+static const char* method_cmds_str[] = {
+ "GET_NAME",
+ "GET_DECLARING_TYPE",
+ "GET_DEBUG_INFO",
+ "GET_PARAM_INFO",
+ "GET_LOCALS_INFO",
+ "GET_INFO",
+ "GET_BODY",
+ "RESOLVE_TOKEN",
+ "GET_CATTRS ",
+ "MAKE_GENERIC_METHOD"
+};
+
+static const char* type_cmds_str[] = {
+ "GET_INFO",
+ "GET_METHODS",
+ "GET_FIELDS",
+ "GET_VALUES",
+ "GET_OBJECT",
+ "GET_SOURCE_FILES",
+ "SET_VALUES",
+ "IS_ASSIGNABLE_FROM",
+ "GET_PROPERTIES ",
+ "GET_CATTRS",
+ "GET_FIELD_CATTRS",
+ "GET_PROPERTY_CATTRS",
+ "GET_SOURCE_FILES_2",
+ "GET_VALUES_2",
+ "GET_METHODS_BY_NAME_FLAGS",
+ "GET_INTERFACES",
+ "GET_INTERFACE_MAP",
+ "IS_INITIALIZED"
+};
+
+static const char* stack_frame_cmds_str[] = {
+ "GET_VALUES",
+ "GET_THIS",
+ "SET_VALUES"
+};
+
+static const char* array_cmds_str[] = {
+ "GET_LENGTH",
+ "GET_VALUES",
+ "SET_VALUES",
+};
+
+static const char* string_cmds_str[] = {
+ "GET_VALUE",
+ "GET_LENGTH",
+ "GET_CHARS"
+};
+
+static const char* object_cmds_str[] = {
+ "GET_TYPE",
+ "GET_VALUES",
+ "IS_COLLECTED",
+ "GET_ADDRESS",
+ "GET_DOMAIN",
+ "SET_VALUES",
+ "GET_INFO",
+};
+
static const char*
cmd_to_string (CommandSet set, int command)
{
+ const char **cmds;
+ int cmds_len = 0;
+
switch (set) {
- case CMD_SET_VM: {
- switch (command) {
- case CMD_VM_VERSION:
- return "VERSION";
- case CMD_VM_ALL_THREADS:
- return "ALL_THREADS";
- case CMD_VM_SUSPEND:
- return "SUSPEND";
- case CMD_VM_RESUME:
- return "RESUME";
- case CMD_VM_EXIT:
- return "EXIT";
- case CMD_VM_DISPOSE:
- return "DISPOSE";
- case CMD_VM_INVOKE_METHOD:
- return "INVOKE_METHOD";
- case CMD_VM_SET_PROTOCOL_VERSION:
- return "SET_PROTOCOL_VERSION";
- case CMD_VM_ABORT_INVOKE:
- return "ABORT_INVOKE";
- default:
- break;
- }
+ case CMD_SET_VM:
+ cmds = vm_cmds_str;
+ cmds_len = G_N_ELEMENTS (vm_cmds_str);
break;
- }
- default:
+ case CMD_SET_OBJECT_REF:
+ cmds = object_cmds_str;
+ cmds_len = G_N_ELEMENTS (object_cmds_str);
+ break;
+ case CMD_SET_STRING_REF:
+ cmds = string_cmds_str;
+ cmds_len = G_N_ELEMENTS (string_cmds_str);
+ break;
+ case CMD_SET_THREAD:
+ cmds = thread_cmds_str;
+ cmds_len = G_N_ELEMENTS (thread_cmds_str);
+ break;
+ case CMD_SET_ARRAY_REF:
+ cmds = array_cmds_str;
+ cmds_len = G_N_ELEMENTS (array_cmds_str);
+ break;
+ case CMD_SET_EVENT_REQUEST:
+ cmds = event_cmds_str;
+ cmds_len = G_N_ELEMENTS (event_cmds_str);
+ break;
+ case CMD_SET_STACK_FRAME:
+ cmds = stack_frame_cmds_str;
+ cmds_len = G_N_ELEMENTS (stack_frame_cmds_str);
+ break;
+ case CMD_SET_APPDOMAIN:
+ cmds = appdomain_cmds_str;
+ cmds_len = G_N_ELEMENTS (appdomain_cmds_str);
break;
+ case CMD_SET_ASSEMBLY:
+ cmds = assembly_cmds_str;
+ cmds_len = G_N_ELEMENTS (assembly_cmds_str);
+ break;
+ case CMD_SET_METHOD:
+ cmds = method_cmds_str;
+ cmds_len = G_N_ELEMENTS (method_cmds_str);
+ break;
+ case CMD_SET_TYPE:
+ cmds = type_cmds_str;
+ cmds_len = G_N_ELEMENTS (type_cmds_str);
+ break;
+ case CMD_SET_MODULE:
+ 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);
+ break;
+ default:
+ return NULL;
}
- return NULL;
+ if (command > 0 && command <= cmds_len)
+ return cmds [command - 1];
+ else
+ return NULL;
}
static gboolean
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;
cmd_str = cmd_num;
}
- DEBUG (1, fprintf (log_file, "[dbg] Received command %s(%s), id=%d.\n", command_set_to_string (command_set), cmd_str, id));
+ DEBUG (1, fprintf (log_file, "[dbg] Command %s(%s) [%d].\n", command_set_to_string (command_set), cmd_str, id));
}
data = g_malloc (len - 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;
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;
g_free (data);
buffer_free (&buf);
- if (command_set == CMD_SET_VM && command == CMD_VM_DISPOSE)
+ if (command_set == CMD_SET_VM && (command == CMD_VM_DISPOSE || command == CMD_VM_EXIT))
break;
}