* [mixed] Add beginnings of unwinding support for interp->jit transitions by pushing an LMF frame on the stack when exiting interpreted code.
* [runtime] Add a mono_debug_lookup_source_location_by_il () helper function to lookup a source location using an IL offset.
* [mixed] Add support for mixed mode managed stack walks.
* [interp] Generate line number info for interpreted code, using the same MonoDebugMethodJitInfo structure used by the JIT.
* [interp] Pass the clause index to the MINT_ENDFINALLY opcode. Not yet used.
* [interp] Create a MonoJitInfo structure for each interpreted method. Not yet used.
* [interp] Add a mono_interp_set_resume_state () function which can be used to set the frame/ip the interpreter will resume execution from when execution returns to it.
* [mixed] Add support for mixed mode exception handling by extending the normal JIT exception handling code in mini-exceptions.c to handle interpreted frames as well.
* [jit] Add an Unwinder type to reduce the amount of code duplication when unwinding though interpreter frames.
* [interp] Fix the !ENABLE_INTERPRETER build.
* [interp] Disable an assert which is hit even when running without --interpreter.
* Fix a comment.
* [interp] Fix an assertion. Fix/add comments.
gboolean
mono_debug_image_has_debug_info (MonoImage *image);
+MonoDebugSourceLocation *
+mono_debug_lookup_source_location_by_il (MonoMethod *method, guint32 il_offset, MonoDomain *domain);
+
#endif /* __DEBUG_INTERNALS_H__ */
return location;
}
+/**
+ * mono_debug_lookup_source_location_by_il:
+ *
+ * Same as mono_debug_lookup_source_location but take an IL_OFFSET argument.
+ */
+MonoDebugSourceLocation *
+mono_debug_lookup_source_location_by_il (MonoMethod *method, guint32 il_offset, MonoDomain *domain)
+{
+ MonoDebugMethodInfo *minfo;
+ MonoDebugSourceLocation *location;
+
+ if (mono_debug_format == MONO_DEBUG_FORMAT_NONE)
+ return NULL;
+
+ mono_debugger_lock ();
+ minfo = mono_debug_lookup_method_internal (method);
+ if (!minfo || !minfo->handle) {
+ mono_debugger_unlock ();
+ return NULL;
+ }
+
+ if (!minfo->handle->ppdb && (!minfo->handle->symfile || !mono_debug_symfile_is_loaded (minfo->handle->symfile))) {
+ mono_debugger_unlock ();
+ return NULL;
+ }
+
+ if (minfo->handle->ppdb)
+ location = mono_ppdb_lookup_location (minfo, il_offset);
+ else
+ location = mono_debug_symfile_lookup_location (minfo, il_offset);
+ mono_debugger_unlock ();
+ return location;
+}
+
MonoDebugSourceLocation *
mono_debug_method_lookup_location (MonoDebugMethodInfo *minfo, int il_offset)
{
g_assert (method);
method = mono_marshal_get_remoting_invoke (method);
#ifdef ENABLE_INTERPRETER
- g_error ("need RuntimeMethod in method_ptr when using interpreter");
+ //g_error ("need RuntimeMethod in method_ptr when using interpreter");
#endif
delegate->method_ptr = mono_compile_method_checked (method, error);
return_val_if_nok (error, FALSE);
guint64 rip;
if (((guint64)(*lmf)->previous_lmf) & 2) {
- /*
- * This LMF entry is created by the soft debug code to mark transitions to
- * managed code done during invokes.
- */
MonoLMFExt *ext = (MonoLMFExt*)(*lmf);
- g_assert (ext->debugger_invoke);
-
- memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
+ if (ext->debugger_invoke) {
+ /*
+ * This LMF entry is created by the soft debug code to mark transitions to
+ * managed code done during invokes.
+ */
+ frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
+ memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
+ } else if (ext->interp_exit) {
+ frame->type = FRAME_TYPE_INTERP_TO_MANAGED;
+ frame->interp_exit_data = ext->interp_exit_data;
+ } else {
+ g_assert_not_reached ();
+ }
*lmf = (MonoLMF *)(((guint64)(*lmf)->previous_lmf) & ~7);
- frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
-
return TRUE;
}
#include <mono/metadata/object.h>
#include <mono/metadata/domain-internals.h>
#include <mono/metadata/class-internals.h>
+#include <mono/metadata/debug-internals.h>
#include "config.h"
enum {
gpointer jit_entry;
MonoType *rtype;
MonoType **param_types;
+ MonoJitInfo *jinfo;
} RuntimeMethod;
struct _MonoInvocation {
jmp_buf *current_env;
unsigned char search_for_handler;
unsigned char managed_code;
+
+ /* Resume state for resuming execution in mixed mode */
+ gboolean has_resume_state;
+ /* Frame to resume execution at */
+ MonoInvocation *handler_frame;
+ /* IP to resume execution at */
+ gpointer handler_ip;
} ThreadContext;
extern int mono_interp_traceopt;
static char* dump_stack (stackval *stack, stackval *sp);
static char* dump_frame (MonoInvocation *inv);
static MonoArray *get_trace_ips (MonoDomain *domain, MonoInvocation *top);
-static void ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, unsigned short *start_with_ip, MonoException *filter_exception);
+static void ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, unsigned short *start_with_ip, MonoException *filter_exception, int exit_at_finally);
typedef void (*ICallMethod) (MonoInvocation *frame);
break_on_method = 1;
}
-static void debug_enter (MonoInvocation *frame, int *tracing)
+static void
+debug_enter (MonoInvocation *frame, int *tracing)
{
if (db_methods) {
g_list_foreach (db_methods, db_match_method, (gpointer)frame->runtime_method->method);
#endif
+/* Set the current execution state to the resume state in context */
+#define SET_RESUME_STATE(context) do { \
+ ip = (context)->handler_ip; \
+ sp->data.p = frame->ex; \
+ ++sp; \
+ frame->ex = NULL; \
+ (context)->has_resume_state = 0; \
+ (context)->handler_frame = NULL; \
+ goto main_loop; \
+ } while (0)
+
static void
interp_ex_handler (MonoException *ex) {
MonoError error;
MonoInvocation *old_frame = context->current_frame;
MonoInvocation *old_env_frame = context->env_frame;
jmp_buf *old_env = context->current_env;
+ MonoLMFExt ext;
if (setjmp (env)) {
context->current_frame = old_frame;
context->current_frame = frame;
context->managed_code = 0;
+ /*
+ * Push an LMF frame on the LMF stack
+ * to mark the transition to native code.
+ */
+ memset (&ext, 0, sizeof (ext));
+ ext.interp_exit = TRUE;
+ ext.interp_exit_data = frame;
+
+ mono_push_lmf (&ext);
+
mono_interp_enter_icall_trampoline (addr, margs);
+ mono_pop_lmf (&ext.lmf);
+
context->managed_code = 1;
/* domain can only be changed by native code */
context->domain = mono_domain_get ();
if (context == NULL) {
context = &context_struct;
+ memset (context, 0, sizeof (ThreadContext));
context_struct.base_frame = &frame;
- context_struct.current_frame = NULL;
context_struct.env_frame = &frame;
context_struct.current_env = &env;
- context_struct.search_for_handler = 0;
- context_struct.managed_code = 0;
mono_native_tls_set_value (thread_context_id, context);
}
else
if (exc)
frame.invoke_trap = 1;
context->managed_code = 1;
- ves_exec_method_with_context (&frame, context, NULL, NULL);
+ ves_exec_method_with_context (&frame, context, NULL, NULL, -1);
context->managed_code = 0;
if (context == &context_struct)
mono_native_tls_set_value (thread_context_id, NULL);
}
}
- init_frame (&frame, context->current_frame, data->rmethod, args, &result);
+ init_frame (&frame, NULL, data->rmethod, args, &result);
context->managed_code = 1;
type = rmethod->rtype;
break;
}
- ves_exec_method_with_context (&frame, context, NULL, NULL);
+ ves_exec_method_with_context (&frame, context, NULL, NULL, -1);
context->managed_code = 0;
if (context == &context_struct)
mono_native_tls_set_value (thread_context_id, NULL);
#define MINT_IN_DEFAULT default:
#endif
+/*
+ * If EXIT_AT_FINALLY is not -1, exit after exiting the finally clause with that index.
+ */
static void
-ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, unsigned short *start_with_ip, MonoException *filter_exception)
+ves_exec_method_with_context (MonoInvocation *frame, ThreadContext *context, unsigned short *start_with_ip, MonoException *filter_exception, int exit_at_finally)
{
MonoInvocation child_frame;
GSList *finally_ips = NULL;
}
}
- ves_exec_method_with_context (&child_frame, context, NULL, NULL);
+ ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
context->current_frame = frame;
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
if (child_frame.ex) {
/*
* An exception occurred, need to run finally, fault and catch handlers..
context->current_frame = frame;
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
if (child_frame.ex) {
/*
* An exception occurred, need to run finally, fault and catch handlers..
mono_error_cleanup (&error); /* FIXME: don't swallow the error */
}
- ves_exec_method_with_context (&child_frame, context, NULL, NULL);
+ ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
context->current_frame = frame;
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
if (child_frame.ex) {
/*
* An exception occurred, need to run finally, fault and catch handlers..
mono_error_cleanup (&error); /* FIXME: don't swallow the error */
}
- ves_exec_method_with_context (&child_frame, context, NULL, NULL);
+ ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
context->current_frame = frame;
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
if (child_frame.ex) {
/*
* An exception occurred, need to run finally, fault and catch handlers..
MonoFtnDesc ftndesc;
guint8 res_buf [256];
MonoType *type;
+ MonoLMFExt ext;
//printf ("%s\n", mono_method_full_name (rmethod->method, 1));
}
}
+ /*
+ * Push an LMF frame on the LMF stack
+ * to mark the transition to compiled code.
+ */
+ memset (&ext, 0, sizeof (ext));
+ ext.interp_exit = TRUE;
+ ext.interp_exit_data = frame;
+
+ mono_push_lmf (&ext);
+
switch (pindex) {
case 0: {
void (*func)(gpointer) = rmethod->jit_wrapper;
break;
}
+ mono_pop_lmf (&ext.lmf);
+
+ if (context->has_resume_state) {
+ /*
+ * If this bit is set, it means the call has thrown the exception, and we
+ * reached this point because the EH code in mono_handle_exception ()
+ * unwound all the JITted frames below us. mono_interp_set_resume_state ()
+ * has set the fields in context to indicate where we have to resume execution.
+ */
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
MonoType *rtype = rmethod->rtype;
switch (rtype->type) {
case MONO_TYPE_VOID:
sp [0].data.p = unboxed;
}
- ves_exec_method_with_context (&child_frame, context, NULL, NULL);
+ ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
context->current_frame = frame;
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
if (child_frame.ex) {
/*
* An exception occurred, need to run finally, fault and catch handlers..
sp [0].data.p = unboxed;
}
- ves_exec_method_with_context (&child_frame, context, NULL, NULL);
+ ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
context->current_frame = frame;
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
if (child_frame.ex) {
/*
* An exception occurred, need to run finally, fault and catch handlers..
g_assert (csig->call_convention == MONO_CALL_DEFAULT);
- ves_exec_method_with_context (&child_frame, context, NULL, NULL);
+ ves_exec_method_with_context (&child_frame, context, NULL, NULL, -1);
context->current_frame = frame;
+ if (context->has_resume_state) {
+ if (frame == context->handler_frame)
+ SET_RESUME_STATE (context);
+ else
+ goto exit_frame;
+ }
+
if (child_frame.ex) {
/*
* An exception occurred, need to run finally, fault and catch handlers..
BINOP_CAST(l, -, guint64);
MINT_IN_BREAK;
MINT_IN_CASE(MINT_ENDFINALLY)
+ ip ++;
+ int clause_index = *ip;
+ if (clause_index == exit_at_finally)
+ goto exit_frame;
while (sp > frame->stack) {
--sp;
}
stackval retval;
memcpy (&dup_frame, inv, sizeof (MonoInvocation));
dup_frame.retval = &retval;
- ves_exec_method_with_context (&dup_frame, context, inv->runtime_method->code + clause->data.filter_offset, frame->ex);
+ ves_exec_method_with_context (&dup_frame, context, inv->runtime_method->code + clause->data.filter_offset, frame->ex, -1);
if (dup_frame.retval->data.i) {
#if DEBUG_INTERP
if (tracing)
frame->runtime_method = mono_interp_get_runtime_method (context->domain, frame->method, &error);
mono_error_cleanup (&error); /* FIXME: don't swallow the error */
context->managed_code = 1;
- ves_exec_method_with_context (frame, context, NULL, NULL);
+ ves_exec_method_with_context (frame, context, NULL, NULL, -1);
context->managed_code = 0;
if (frame->ex) {
if (context != &context_struct && context->current_env) {
return total;
}
+/*
+ * mono_interp_set_resume_state:
+ *
+ * Set the state the interpeter will continue to execute from after execution returns to the interpreter.
+ */
+void
+mono_interp_set_resume_state (MonoException *ex, StackFrameInfo *frame, gpointer handler_ip)
+{
+ ThreadContext *context = mono_native_tls_get_value (thread_context_id);
+
+ context->has_resume_state = TRUE;
+ context->handler_frame = frame->interp_frame;
+ /* This is on the stack, so it doesn't need a wbarrier */
+ context->handler_frame->ex = ex;
+ context->handler_ip = handler_ip;
+}
+
+/*
+ * mono_interp_run_finally:
+ *
+ * Run the finally clause identified by CLAUSE_INDEX in the intepreter frame given by
+ * frame->interp_frame.
+ */
+void
+mono_interp_run_finally (StackFrameInfo *frame, int clause_index, gpointer handler_ip)
+{
+ MonoInvocation *iframe = frame->interp_frame;
+ ThreadContext *context = mono_native_tls_get_value (thread_context_id);
+
+ ves_exec_method_with_context (iframe, context, handler_ip, NULL, clause_index);
+}
+
+typedef struct {
+ MonoInvocation *current;
+} StackIter;
+
+/*
+ * mono_interp_frame_iter_init:
+ *
+ * Initialize an iterator for iterating through interpreted frames.
+ */
+void
+mono_interp_frame_iter_init (MonoInterpStackIter *iter, gpointer interp_exit_data)
+{
+ StackIter *stack_iter = (StackIter*)iter;
+
+ stack_iter->current = (MonoInvocation*)interp_exit_data;
+}
+
+gboolean
+mono_interp_frame_iter_next (MonoInterpStackIter *iter, StackFrameInfo *frame)
+{
+ StackIter *stack_iter = (StackIter*)iter;
+ MonoInvocation *iframe = stack_iter->current;
+
+ memset (frame, 0, sizeof (StackFrameInfo));
+ /* pinvoke frames doesn't have runtime_method set */
+ while (iframe && !iframe->runtime_method)
+ iframe = iframe->parent;
+ if (!iframe)
+ return FALSE;
+
+ frame->type = FRAME_TYPE_INTERP;
+ frame->interp_frame = iframe;
+ frame->method = iframe->runtime_method->method;
+ frame->actual_method = frame->method;
+ /* This is the offset in the interpreter IR */
+ frame->native_offset = iframe->ip - iframe->runtime_method->code;
+ frame->ji = iframe->runtime_method->jinfo;
+
+ stack_iter->current = iframe->parent;
+
+ return TRUE;
+}
#define __MONO_MINI_INTERPRETER_H__
#include <mono/mini/mini.h>
+typedef struct _MonoInterpStackIter MonoInterpStackIter;
+
+/* Needed for stack allocation */
+struct _MonoInterpStackIter {
+ gpointer dummy [8];
+};
+
int
mono_interp_regression_list (int verbose, int count, char *images []);
void
interp_walk_stack_with_ctx (MonoInternalStackWalk func, MonoContext *ctx, MonoUnwindOptions options, void *user_data);
+
+void
+mono_interp_set_resume_state (MonoException *ex, StackFrameInfo *frame, gpointer handler_ip);
+
+void
+mono_interp_run_finally (StackFrameInfo *frame, int clause_index, gpointer handler_ip);
+
+void
+mono_interp_frame_iter_init (MonoInterpStackIter *iter, gpointer interp_exit_data);
+
+gboolean
+mono_interp_frame_iter_next (MonoInterpStackIter *iter, StackFrameInfo *frame);
+
#endif /* __MONO_MINI_INTERPRETER_H__ */
OPDEF(MINT_THROW, "throw", 1, MintOpNoArgs)
OPDEF(MINT_RETHROW, "rethrow", 1, MintOpNoArgs)
-OPDEF(MINT_ENDFINALLY, "endfinally", 1, MintOpNoArgs)
+OPDEF(MINT_ENDFINALLY, "endfinally", 2, MintOpNoArgs)
OPDEF(MINT_BRFALSE_I4, "brfalse.i4", 3, MintOpBranch)
OPDEF(MINT_BRFALSE_I8, "brfalse.i8", 3, MintOpBranch)
int max_data_items;
void **data_items;
GHashTable *data_hash;
+ int *clause_indexes;
} TransformData;
#define MINT_TYPE_I1 0
return field;
}
+static void
+interp_save_debug_info (RuntimeMethod *rtm, MonoMethodHeader *header, TransformData *td, GArray *line_numbers)
+{
+ MonoDebugMethodJitInfo *dinfo;
+ int i;
+
+ if (!mono_debug_enabled ())
+ return;
+
+ /*
+ * We save the debug info in the same way the JIT does it, treating the interpreter IR as the native code.
+ */
+
+ dinfo = g_new0 (MonoDebugMethodJitInfo, 1);
+ dinfo->num_locals = header->num_locals;
+ dinfo->locals = g_new0 (MonoDebugVarInfo, header->num_locals);
+ dinfo->code_start = (guint8*)rtm->code;
+ dinfo->code_size = td->new_ip - td->new_code;
+ dinfo->epilogue_begin = 0;
+ dinfo->has_var_info = FALSE;
+ dinfo->num_line_numbers = line_numbers->len;
+ dinfo->line_numbers = g_new0 (MonoDebugLineNumberEntry, dinfo->num_line_numbers);
+ for (i = 0; i < dinfo->num_line_numbers; i++)
+ dinfo->line_numbers [i] = g_array_index (line_numbers, MonoDebugLineNumberEntry, i);
+ mono_debug_add_method (rtm->method, dinfo, mono_domain_get ());
+
+ mono_debug_free_method_jit_info (dinfo);
+}
+
static void
generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, MonoGenericContext *generic_context)
{
guint32 token;
TransformData td;
int generating_code = 1;
+ GArray *line_numbers;
memset(&td, 0, sizeof(td));
td.method = method;
td.max_data_items = 0;
td.data_items = NULL;
td.data_hash = g_hash_table_new (NULL, NULL);
+ td.clause_indexes = g_malloc (header->code_size * sizeof (int));
rtm->data_items = td.data_items;
for (i = 0; i < header->code_size; i++) {
td.forward_refs [i] = -1;
td.stack_height [i] = -1;
+ td.clause_indexes [i] = -1;
}
td.new_ip = td.new_code;
td.last_new_ip = NULL;
td.sp = td.stack;
td.max_stack_height = 0;
+ line_numbers = g_array_new (FALSE, TRUE, sizeof (MonoDebugLineNumberEntry));
+
for (i = 0; i < header->num_clauses; i++) {
MonoExceptionClause *c = header->clauses + i;
td.stack_height [c->handler_offset] = 0;
td.stack_state [c->data.filter_offset][0].type = STACK_TYPE_O;
td.stack_state [c->data.filter_offset][0].klass = NULL; /*FIX*/
}
+
+ if ((c->flags & MONO_EXCEPTION_CLAUSE_FINALLY) || (c->flags & MONO_EXCEPTION_CLAUSE_FAULT)) {
+ for (int j = c->handler_offset; j < c->handler_offset + c->handler_len; ++j) {
+ if (td.clause_indexes [j] == -1)
+ td.clause_indexes [j] = i;
+ }
+ }
}
td.ip = header->code;
td.in_offsets [in_offset] = td.new_ip - td.new_code;
new_in_start_offset = td.new_ip - td.new_code;
td.in_start = td.ip;
+
+ MonoDebugLineNumberEntry lne;
+ lne.native_offset = td.new_ip - td.new_code;
+ lne.il_offset = td.ip - header->code;
+ g_array_append_val (line_numbers, lne);
+
while (td.forward_refs [in_offset] >= 0) {
int j = td.forward_refs [in_offset];
int slot;
++td.ip;
break;
case CEE_ENDFINALLY:
+ g_assert (td.clause_indexes [in_offset] != -1);
td.sp = td.stack;
SIMPLE_OP (td, MINT_ENDFINALLY);
+ ADD_CODE (&td, td.clause_indexes [in_offset]);
generating_code = 0;
break;
case CEE_LEAVE:
}
g_assert (td.max_stack_height <= (header->max_stack + 1));
+ int code_len = td.new_ip - td.new_code;
+
rtm->clauses = mono_domain_alloc0 (domain, header->num_clauses * sizeof (MonoExceptionClause));
memcpy (rtm->clauses, header->clauses, header->num_clauses * sizeof(MonoExceptionClause));
rtm->code = mono_domain_alloc0 (domain, (td.new_ip - td.new_code) * sizeof (gushort));
rtm->alloca_size = rtm->locals_size + rtm->args_size + rtm->vt_stack_size + rtm->stack_size;
rtm->data_items = mono_domain_alloc0 (domain, td.n_data_items * sizeof (td.data_items [0]));
memcpy (rtm->data_items, td.data_items, td.n_data_items * sizeof (td.data_items [0]));
+
+ /* Save debug info */
+ interp_save_debug_info (rtm, header, &td, line_numbers);
+
+ /* Create a MonoJitInfo for the interpreted method by creating the interpreter IR as the native code. */
+ int jinfo_len = mono_jit_info_size (0, header->num_clauses, 0);
+ MonoJitInfo *jinfo = (MonoJitInfo *)mono_domain_alloc0 (domain, jinfo_len);
+ rtm->jinfo = jinfo;
+ mono_jit_info_init (jinfo, method, (guint8*)rtm->code, code_len, 0, header->num_clauses, 0);
+ for (i = 0; i < jinfo->num_clauses; ++i) {
+ MonoJitExceptionInfo *ei = &jinfo->clauses [i];
+ MonoExceptionClause *c = rtm->clauses + i;
+
+ ei->flags = c->flags;
+ ei->try_start = rtm->code + c->try_offset;
+ ei->try_end = rtm->code + c->try_offset + c->try_len;
+ ei->handler_start = rtm->code + c->handler_offset;
+ if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER || ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
+ } else {
+ ei->data.catch_class = c->data.catch_class;
+ }
+ }
+
g_free (td.in_offsets);
g_free (td.forward_refs);
for (i = 0; i < header->code_size; ++i)
g_free (td.data_items);
g_free (td.stack);
g_hash_table_destroy (td.data_hash);
+ g_free (td.clause_indexes);
+ g_array_free (line_numbers, TRUE);
}
static mono_mutex_t calc_section;
#define MONO_ARCH_HAVE_PATCH_CODE_NEW 1
#define MONO_ARCH_HAVE_OP_GENERIC_CLASS_INIT 1
#define MONO_ARCH_HAVE_GENERAL_RGCTX_LAZY_FETCH_TRAMPOLINE 1
+#define MONO_ARCH_HAVE_INIT_LMF_EXT 1
#if defined(TARGET_OSX) || defined(__linux__)
#define MONO_ARCH_HAVE_UNWIND_BACKTRACE 1
#include <mono/metadata/object-internals.h>
#include <mono/metadata/reflection-internals.h>
#include <mono/metadata/gc-internals.h>
+#include <mono/metadata/debug-internals.h>
#include <mono/metadata/mono-debug.h>
#include <mono/metadata/profiler.h>
#include <mono/metadata/mono-endian.h>
if (!err)
return FALSE;
- if (*lmf && ((*lmf) != jit_tls->first_lmf) && ((gpointer)MONO_CONTEXT_GET_SP (new_ctx) >= (gpointer)(*lmf))) {
+ if (frame->type != FRAME_TYPE_INTERP_TO_MANAGED && *lmf && ((*lmf) != jit_tls->first_lmf) && ((gpointer)MONO_CONTEXT_GET_SP (new_ctx) >= (gpointer)(*lmf))) {
/*
* Remove any unused lmf.
* Mask out the lower bits which might be used to hold additional information.
return TRUE;
}
+typedef struct {
+ gboolean in_interp;
+ MonoInterpStackIter interp_iter;
+} Unwinder;
+
+static void
+unwinder_init (Unwinder *unwinder)
+{
+ memset (unwinder, 0, sizeof (Unwinder));
+}
+
+static gboolean
+unwinder_unwind_frame (Unwinder *unwinder,
+ MonoDomain *domain, MonoJitTlsData *jit_tls,
+ MonoJitInfo *prev_ji, MonoContext *ctx,
+ MonoContext *new_ctx, char **trace, MonoLMF **lmf,
+ mgreg_t **save_locations,
+ StackFrameInfo *frame)
+{
+ if (unwinder->in_interp) {
+ unwinder->in_interp = mono_interp_frame_iter_next (&unwinder->interp_iter, frame);
+ if (!unwinder->in_interp) {
+ return unwinder_unwind_frame (unwinder, domain, jit_tls, prev_ji, ctx, new_ctx, trace, lmf, save_locations, frame);
+ }
+ return TRUE;
+ } else {
+ gboolean res = mono_find_jit_info_ext (domain, jit_tls, prev_ji, ctx, new_ctx, trace, lmf,
+ save_locations, frame);
+ if (!res)
+ return FALSE;
+ if (frame->type == FRAME_TYPE_INTERP_TO_MANAGED) {
+ unwinder->in_interp = TRUE;
+ mono_interp_frame_iter_init (&unwinder->interp_iter, frame->interp_exit_data);
+ }
+ return TRUE;
+ }
+}
+
/*
* This function is async-safe.
*/
MonoMethod *jmethod = NULL, *actual_method;
StackFrameInfo frame;
gboolean res;
+ Unwinder unwinder;
+ int il_offset = -1;
MONO_ARCH_CONTEXT_DEF;
MONO_INIT_CONTEXT_FROM_FUNC (&ctx, ves_icall_get_frame_info);
#endif
+ unwinder_init (&unwinder);
+
new_ctx = ctx;
do {
ctx = new_ctx;
- res = mono_find_jit_info_ext (domain, jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, NULL, &frame);
+ res = unwinder_unwind_frame (&unwinder, domain, jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, NULL, &frame);
if (!res)
return FALSE;
-
- if (frame.type == FRAME_TYPE_MANAGED_TO_NATIVE ||
- frame.type == FRAME_TYPE_DEBUGGER_INVOKE ||
- frame.type == FRAME_TYPE_TRAMPOLINE)
+ switch (frame.type) {
+ case FRAME_TYPE_MANAGED_TO_NATIVE:
+ case FRAME_TYPE_DEBUGGER_INVOKE:
+ case FRAME_TYPE_TRAMPOLINE:
+ case FRAME_TYPE_INTERP_TO_MANAGED:
continue;
+ case FRAME_TYPE_INTERP:
+ skip--;
+ break;
+ default:
+ ji = frame.ji;
+ *native_offset = frame.native_offset;
- ji = frame.ji;
- *native_offset = frame.native_offset;
-
- /* The skip count passed by the caller depends on us not filtering out MANAGED_TO_NATIVE */
- jmethod = jinfo_get_method (ji);
- if (jmethod->wrapper_type != MONO_WRAPPER_NONE && jmethod->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD && jmethod->wrapper_type != MONO_WRAPPER_MANAGED_TO_NATIVE)
- continue;
- skip--;
+ /* The skip count passed by the caller depends on us not filtering out MANAGED_TO_NATIVE */
+ jmethod = jinfo_get_method (ji);
+ if (jmethod->wrapper_type != MONO_WRAPPER_NONE && jmethod->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD && jmethod->wrapper_type != MONO_WRAPPER_MANAGED_TO_NATIVE)
+ continue;
+ skip--;
+ break;
+ }
} while (skip >= 0);
- actual_method = get_method_from_stack_frame (ji, get_generic_info_from_stack_frame (ji, &ctx));
+ if (frame.type == FRAME_TYPE_INTERP) {
+ jmethod = frame.method;
+ actual_method = frame.actual_method;
+ *native_offset = frame.native_offset;
+ } else {
+ actual_method = get_method_from_stack_frame (ji, get_generic_info_from_stack_frame (ji, &ctx));
+ }
}
MonoReflectionMethod *rm = mono_method_get_object_checked (domain, actual_method, NULL, &error);
}
mono_gc_wbarrier_generic_store (method, (MonoObject*) rm);
- location = mono_debug_lookup_source_location (jmethod, *native_offset, domain);
+ if (il_offset != -1) {
+ location = mono_debug_lookup_source_location_by_il (jmethod, il_offset, domain);
+ } else {
+ location = mono_debug_lookup_source_location (jmethod, *native_offset, domain);
+ }
if (location)
*iloffset = location->il_offset;
else
gint32 filter_idx;
int i;
MonoObject *ex_obj;
+ Unwinder unwinder;
+ gboolean in_interp;
g_assert (ctx != NULL);
filter_idx = 0;
initial_ctx = *ctx;
+ unwinder_init (&unwinder);
+
while (1) {
MonoContext new_ctx;
guint32 free_stack;
if (out_prev_ji)
*out_prev_ji = ji;
- unwind_res = mono_find_jit_info_ext (domain, jit_tls, NULL, ctx, &new_ctx, NULL, &lmf, NULL, &frame);
- if (unwind_res) {
- if (frame.type == FRAME_TYPE_DEBUGGER_INVOKE ||
- frame.type == FRAME_TYPE_MANAGED_TO_NATIVE ||
- frame.type == FRAME_TYPE_TRAMPOLINE) {
- *ctx = new_ctx;
- continue;
- }
- g_assert (frame.type == FRAME_TYPE_MANAGED);
- ji = frame.ji;
- }
-
+ unwind_res = unwinder_unwind_frame (&unwinder, domain, jit_tls, NULL, ctx, &new_ctx, NULL, &lmf, NULL, &frame);
if (!unwind_res) {
setup_stack_trace (mono_ex, dynamic_methods, &trace_ips);
g_slist_free (dynamic_methods);
return FALSE;
}
+ switch (frame.type) {
+ case FRAME_TYPE_DEBUGGER_INVOKE:
+ case FRAME_TYPE_MANAGED_TO_NATIVE:
+ case FRAME_TYPE_TRAMPOLINE:
+ case FRAME_TYPE_INTERP_TO_MANAGED:
+ *ctx = new_ctx;
+ continue;
+ case FRAME_TYPE_INTERP:
+ case FRAME_TYPE_MANAGED:
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ in_interp = frame.type == FRAME_TYPE_INTERP;
+ ji = frame.ji;
+
+ gpointer ip;
+ if (in_interp)
+ ip = (guint16*)ji->code_start + frame.native_offset;
+ else
+ ip = MONO_CONTEXT_GET_IP (ctx);
+
frame_count ++;
method = jinfo_get_method (ji);
//printf ("M: %s %d.\n", mono_method_full_name (method, TRUE), frame_count);
if (free_stack <= (64 * 1024))
continue;
- if (is_address_protected (ji, ei, MONO_CONTEXT_GET_IP (ctx))) {
+ if (is_address_protected (ji, ei, ip)) {
/* catch block */
MonoClass *catch_class = get_exception_catch_class (ei, ji, ctx);
*out_ji = ji;
/* mono_debugger_agent_handle_exception () needs this */
- MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
+ if (!in_interp)
+ MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
return TRUE;
}
mono_error_cleanup (&isinst_error);
int i;
MonoObject *ex_obj;
MonoObject *non_exception = NULL;
+ Unwinder unwinder;
+ gboolean in_interp;
g_assert (ctx != NULL);
if (!obj) {
filter_idx = 0;
initial_ctx = *ctx;
+ unwinder_init (&unwinder);
+
while (1) {
MonoContext new_ctx;
guint32 free_stack;
int clause_index_start = 0;
gboolean unwind_res = TRUE;
+ StackFrameInfo frame;
+ gpointer ip;
if (resume) {
resume = FALSE;
first_filter_idx = jit_tls->resume_state.first_filter_idx;
filter_idx = jit_tls->resume_state.filter_idx;
} else {
- StackFrameInfo frame;
-
- unwind_res = mono_find_jit_info_ext (domain, jit_tls, NULL, ctx, &new_ctx, NULL, &lmf, NULL, &frame);
- if (unwind_res) {
- if (frame.type == FRAME_TYPE_DEBUGGER_INVOKE ||
- frame.type == FRAME_TYPE_MANAGED_TO_NATIVE ||
- frame.type == FRAME_TYPE_TRAMPOLINE) {
- *ctx = new_ctx;
- continue;
- }
- g_assert (frame.type == FRAME_TYPE_MANAGED);
- ji = frame.ji;
+ unwind_res = unwinder_unwind_frame (&unwinder, domain, jit_tls, NULL, ctx, &new_ctx, NULL, &lmf, NULL, &frame);
+ if (!unwind_res) {
+ *(mono_get_lmf_addr ()) = lmf;
+
+ jit_tls->abort_func (obj);
+ g_assert_not_reached ();
+ }
+ switch (frame.type) {
+ case FRAME_TYPE_DEBUGGER_INVOKE:
+ case FRAME_TYPE_MANAGED_TO_NATIVE:
+ case FRAME_TYPE_TRAMPOLINE:
+ *ctx = new_ctx;
+ continue;
+ case FRAME_TYPE_INTERP_TO_MANAGED:
+ continue;
+ case FRAME_TYPE_INTERP:
+ case FRAME_TYPE_MANAGED:
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
}
+ in_interp = frame.type == FRAME_TYPE_INTERP;
+ ji = frame.ji;
}
- if (!unwind_res) {
- *(mono_get_lmf_addr ()) = lmf;
-
- jit_tls->abort_func (obj);
- g_assert_not_reached ();
- }
+ if (in_interp)
+ ip = (guint16*)ji->code_start + frame.native_offset;
+ else
+ ip = MONO_CONTEXT_GET_IP (ctx);
method = jinfo_get_method (ji);
frame_count ++;
if (free_stack <= (64 * 1024))
continue;
- if (is_address_protected (ji, ei, MONO_CONTEXT_GET_IP (ctx))) {
+ if (is_address_protected (ji, ei, ip)) {
/* catch block */
MonoClass *catch_class = get_exception_catch_class (ei, ji, ctx);
mono_profiler_exception_clause_handler (method, ei->flags, i);
jit_tls->orig_ex_ctx_set = FALSE;
mini_set_abort_threshold (ctx);
- MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
+
+ if (in_interp) {
+ /*
+ * ctx->pc points into the interpreter, after the call which transitioned to
+ * JITted code. Store the unwind state into the
+ * interpeter state, then resume, the interpreter will unwind itself until
+ * it reaches the target frame and will continue execution from there.
+ * The resuming is kinda hackish, from the native code standpoint, it looks
+ * like the call which transitioned to JITted code has succeeded, but the
+ * return value register etc. is not set, so we have to be careful.
+ */
+ mono_interp_set_resume_state (mono_ex, &frame, ei->handler_start);
+ /* Undo the IP adjustment done by mono_arch_unwind_frame () */
+#ifdef TARGET_AMD64
+ ctx->gregs [AMD64_RIP] ++;
+#else
+ NOT_IMPLEMENTED;
+#endif
+ } else {
+ MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
+ }
mono_set_lmf (lmf);
#ifndef DISABLE_PERFCOUNTERS
mono_perfcounters->exceptions_depth += frame_count;
return 0;
} else {
mini_set_abort_threshold (ctx);
- call_filter (ctx, ei->handler_start);
+ if (in_interp)
+ mono_interp_run_finally (&frame, i, ei->handler_start);
+ else
+ call_filter (ctx, ei->handler_start);
}
}
}
g_assert_not_reached ();
}
#endif
+
+#ifndef ENABLE_INTERPRETER
+/* Stubs of interpreter functions */
+void
+mono_interp_set_resume_state (MonoException *ex, StackFrameInfo *frame, gpointer handler_ip)
+{
+ g_assert_not_reached ();
+}
+
+void
+mono_interp_run_finally (StackFrameInfo *frame, int clause_index, gpointer handler_ip)
+{
+ g_assert_not_reached ();
+}
+
+void
+mono_interp_frame_iter_init (MonoInterpStackIter *iter, gpointer interp_exit_data)
+{
+ g_assert_not_reached ();
+}
+
+gboolean
+mono_interp_frame_iter_next (MonoInterpStackIter *iter, StackFrameInfo *frame)
+{
+ g_assert_not_reached ();
+ return FALSE;
+}
+#endif
mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_UNKNOWN);
- // FIXME: save lmf
+ /* This is needed to be able to unwind out of interpreted code */
+ mb->method->save_lmf = 1;
#ifndef DISABLE_JIT
if (sig->ret->type != MONO_TYPE_VOID)
mono_thread_info_tls_set (info, TLS_KEY_LMF_ADDR, lmf_addr);
}
+/*
+ * mono_push_lmf:
+ *
+ * Push an MonoLMFExt frame on the LMF stack.
+ */
+void
+mono_push_lmf (MonoLMFExt *ext)
+{
+#ifdef MONO_ARCH_HAVE_INIT_LMF_EXT
+ MonoLMF **lmf_addr;
+
+ lmf_addr = mono_get_lmf_addr ();
+
+ mono_arch_init_lmf_ext (ext, *lmf_addr);
+
+ mono_set_lmf ((MonoLMF*)ext);
+#else
+ NOT_IMPLEMENTED;
+#endif
+}
+
+/*
+ * mono_push_lmf:
+ *
+ * Pop the last frame from the LMF stack.
+ */
+void
+mono_pop_lmf (MonoLMF *lmf)
+{
+ mono_set_lmf ((MonoLMF *)(((gssize)lmf->previous_lmf) & ~3));
+}
+
/*
* mono_jit_thread_attach:
*
typedef struct {
struct MonoLMF lmf;
gboolean debugger_invoke;
+ gboolean interp_exit;
MonoContext ctx; /* if debugger_invoke is TRUE */
+ /* If interp_exit is TRUE */
+ gpointer interp_exit_data;
} MonoLMFExt;
/* Generic sharing */
MonoLMF * mono_get_lmf (void);
MonoLMF** mono_get_lmf_addr (void);
void mono_set_lmf (MonoLMF *lmf);
+void mono_push_lmf (MonoLMFExt *ext);
+void mono_pop_lmf (MonoLMF *lmf);
MonoJitTlsData* mono_get_jit_tls (void);
MONO_API MonoDomain* mono_jit_thread_attach (MonoDomain *domain);
MONO_API void mono_jit_set_domain (MonoDomain *domain);
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
+using System.Diagnostics;
/*
* Regression tests for the mixed-mode execution.
return i;
}
+ [MethodImplAttribute (MethodImplOptions.NoInlining)]
+ public static StackTrace get_stacktrace_interp () {
+ var o = new object ();
+ return new StackTrace (true);
+ }
+
+ [MethodImplAttribute (MethodImplOptions.NoInlining)]
+ public static void throw_ex () {
+ JitClass.throw_ex ();
+ }
}
/* The methods in this class will always be JITted */
public static void exit_byref (ref int i) {
i += 1;
}
+
+ [MethodImplAttribute (MethodImplOptions.NoInlining)]
+ public static void throw_ex () {
+ throw new Exception ();
+ }
+
+ [MethodImplAttribute (MethodImplOptions.NoInlining)]
+ public static StackTrace get_stacktrace_jit () {
+ return InterpClass.get_stacktrace_interp ();
+ }
}
#if __MOBILE__
return 4;
return 0;
}
+
+ public static int test_0_throw () {
+ // Throw an exception from jitted code, catch it in interpreted code
+ try {
+ JitClass.throw_ex ();
+ } catch {
+ return 0;
+ }
+ return 1;
+ }
+
+ public static int test_0_throw_child () {
+ try {
+ InterpClass.throw_ex ();
+ } catch {
+ return 0;
+ }
+ return 1;
+ }
+
+ static bool finally_called;
+
+ public static void call_finally () {
+ try {
+ JitClass.throw_ex ();
+ } finally {
+ finally_called = true;
+ }
+ }
+
+ public static int test_0_eh2 () {
+ finally_called = false;
+
+ // Throw an exception from jitted code, execute finally in interpreted code
+ try {
+ call_finally ();
+ } catch {
+ return 0;
+ }
+ if (!finally_called)
+ return 2;
+ return 1;
+ }
+
+ public static int test_0_stack_traces () {
+ //
+ // Get a stacktrace for an interp->jit->interp call stack
+ //
+ StackTrace st = JitClass.get_stacktrace_jit ();
+ var frame = st.GetFrame (0);
+ if (frame.GetMethod ().Name != "get_stacktrace_interp")
+ return 1;
+ frame = st.GetFrame (1);
+ if (frame.GetMethod ().Name != "get_stacktrace_jit")
+ return 2;
+ frame = st.GetFrame (2);
+ if (frame.GetMethod ().Name != "test_0_stack_traces")
+ return 3;
+ return 0;
+ }
}
\ No newline at end of file
/* Frame for transitioning to native code */
FRAME_TYPE_MANAGED_TO_NATIVE = 2,
FRAME_TYPE_TRAMPOLINE = 3,
- FRAME_TYPE_NUM = 4
+ /* Interpreter frame */
+ FRAME_TYPE_INTERP = 4,
+ /* Frame for transitioning from interpreter to managed code */
+ FRAME_TYPE_INTERP_TO_MANAGED = 5,
+ FRAME_TYPE_NUM = 6
} MonoStackFrameType;
typedef enum {
*/
int il_offset;
+ /* For FRAME_TYPE_INTERP_EXIT */
+ gpointer interp_exit_data;
+
+ /* For FRAME_TYPE_INTERP */
+ gpointer interp_frame;
+
/* The next fields are only useful for the jit */
gpointer lmf;
guint32 unwind_info_len;