-/*
- * mini-exceptions.c: generic exception support
+/**
+ * \file
+ * generic exception support
*
* Authors:
* Dietmar Maurer (dietmar@ximian.com)
#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/profiler-private.h>
#include <mono/metadata/mono-endian.h>
#include <mono/metadata/environment.h>
#include <mono/metadata/mono-mlist.h>
#include "seq-points.h"
#include "llvm-runtime.h"
#include "mini-llvm.h"
+#include "interp/interp.h"
#ifdef ENABLE_LLVM
#include "mini-llvm-cpp.h"
#endif
+#ifdef TARGET_ARM
+#include "mini-arm.h"
+#endif
+
#ifndef MONO_ARCH_CONTEXT_DEF
#define MONO_ARCH_CONTEXT_DEF
#endif
#define MONO_ARCH_STACK_GROWS_UP 0
#endif
+/*
+ * Raw frame information is stored in MonoException.trace_ips as an IntPtr[].
+ * This structure represents one entry.
+ * This should consists of pointers only.
+ */
+typedef struct
+{
+ gpointer ip;
+ gpointer generic_info;
+} ExceptionTraceIp;
+
+/* Number of words in trace_ips belonging to one entry */
+#define TRACE_IP_ENTRY_SIZE (sizeof (ExceptionTraceIp) / sizeof (gpointer))
+
static gpointer restore_context_func, call_filter_func;
static gpointer throw_exception_func, rethrow_exception_func;
static gpointer throw_corlib_exception_func;
static void mono_raise_exception_with_ctx (MonoException *exc, MonoContext *ctx);
static void mono_runtime_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data);
static gboolean mono_current_thread_has_handle_block_guard (void);
+static gboolean mono_install_handler_block_guard (MonoThreadUnwindState *ctx);
static gboolean
first_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer addr)
#ifdef MONO_ARCH_HAVE_EXCEPTIONS_INIT
mono_arch_exceptions_init ();
#endif
- cbs.mono_walk_stack_with_ctx = mono_runtime_walk_stack_with_ctx;
+#ifdef ENABLE_INTERPRETER
+ if (mono_use_interpreter)
+ cbs.mono_walk_stack_with_ctx = interp_walk_stack_with_ctx;
+ else
+#endif
+ cbs.mono_walk_stack_with_ctx = mono_runtime_walk_stack_with_ctx;
+
cbs.mono_walk_stack_with_state = mono_walk_stack_with_state;
- if (mono_llvm_only)
+ if (mono_llvm_only) {
cbs.mono_raise_exception = mono_llvm_raise_exception;
- else
+ cbs.mono_reraise_exception = mono_llvm_reraise_exception;
+ } else {
cbs.mono_raise_exception = (void (*)(MonoException *))mono_get_throw_exception ();
+ cbs.mono_reraise_exception = (void (*)(MonoException *))mono_get_rethrow_exception ();
+ }
cbs.mono_raise_exception_with_ctx = mono_raise_exception_with_ctx;
cbs.mono_exception_walk_trace = mono_exception_walk_trace;
cbs.mono_install_handler_block_guard = mono_install_handler_block_guard;
for (i = 0; i < table->num_holes; ++i) {
MonoTryBlockHoleJitInfo *hole = &table->holes [i];
- if (hole->clause == clause && hole->offset <= offset && hole->offset + hole->length > offset)
+ if (ji->clauses [hole->clause].try_offset == ji->clauses [clause].try_offset && hole->offset <= offset && hole->offset + hole->length > offset)
return FALSE;
}
return TRUE;
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));
+}
+
+#if defined(__GNUC__) && defined(TARGET_ARM64)
+/* gcc 4.9.2 seems to miscompile this on arm64 */
+static __attribute__((optimize("O0"))) gboolean
+#else
+static gboolean
+#endif
+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.
*/
/**
* mono_exception_walk_native_trace:
- * @ex: The exception object whose frames should be walked
- * @func: callback to call for each stack frame
- * @user_data: data passed to the callback
- *
+ * \param ex The exception object whose frames should be walked
+ * \param func callback to call for each stack frame
+ * \param user_data data passed to the callback
* This function walks the stacktrace of an exception. For
* each frame the callback function is called with the relevant info.
* The walk ends when no more stack frames are found or when the callback
if (ta == NULL)
return FALSE;
- len = mono_array_length (ta) >> 1;
+ len = mono_array_length (ta) / TRACE_IP_ENTRY_SIZE;
for (i = 0; i < len; i++) {
- gpointer ip = mono_array_get (ta, gpointer, i * 2 + 0);
- gpointer generic_info = mono_array_get (ta, gpointer, i * 2 + 1);
+ ExceptionTraceIp trace_ip;
+
+ memcpy (&trace_ip, mono_array_addr_fast (ta, ExceptionTraceIp, i), sizeof (ExceptionTraceIp));
+ gpointer ip = trace_ip.ip;
+ gpointer generic_info = trace_ip.generic_info;
MonoJitInfo *ji = mono_jit_info_table_find (domain, (char *)ip);
if (ji == NULL) {
return res;
}
- len = mono_array_length (ta) >> 1;
+ len = mono_array_length (ta) / TRACE_IP_ENTRY_SIZE;
res = mono_array_new_checked (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0, &error);
if (mono_error_set_pending_exception (&error))
mono_error_set_pending_exception (&error);
return NULL;
}
- gpointer ip = mono_array_get (ta, gpointer, i * 2 + 0);
- gpointer generic_info = mono_array_get (ta, gpointer, i * 2 + 1);
+ ExceptionTraceIp trace_ip;
+ memcpy (&trace_ip, mono_array_addr_fast (ta, ExceptionTraceIp, i), sizeof (ExceptionTraceIp));
+ gpointer ip = trace_ip.ip;
+ gpointer generic_info = trace_ip.generic_info;
MonoMethod *method;
ji = mono_jit_info_table_find (domain, (char *)ip);
sf->method = NULL;
s = mono_method_get_name_full (method, TRUE, FALSE, MONO_TYPE_NAME_FORMAT_REFLECTION);
- MONO_OBJECT_SETREF (sf, internal_method_name, mono_string_new (domain, s));
+ MonoString *name = mono_string_new_checked (domain, s, &error);
g_free (s);
+ if (!is_ok (&error)) {
+ mono_error_set_pending_exception (&error);
+ return NULL;
+ }
+ MONO_OBJECT_SETREF (sf, internal_method_name, name);
}
else {
MonoReflectionMethod *rm = mono_method_get_object_checked (domain, method, NULL, &error);
if (need_file_info) {
if (location && location->source_file) {
- MONO_OBJECT_SETREF (sf, filename, mono_string_new (domain, location->source_file));
+ MonoString *filename = mono_string_new_checked (domain, location->source_file, &error);
+ if (!is_ok (&error)) {
+ mono_error_set_pending_exception (&error);
+ return NULL;
+ }
+ MONO_OBJECT_SETREF (sf, filename, filename);
sf->line = location->row;
sf->column = location->column;
} else {
}
/**
* mono_walk_stack_with_ctx:
- *
- * Unwind the current thread starting at @start_ctx.
- *
- * If @start_ctx is null, we capture the current context.
+ * Unwind the current thread starting at \p start_ctx.
+ * If \p start_ctx is null, we capture the current context.
*/
void
mono_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data)
/**
* mono_walk_stack_with_state:
- *
- * Unwind a thread described by @state.
+ * Unwind a thread described by \p state.
*
* State must be valid (state->valid == TRUE).
*
* If you are using this function to unwind another thread, make sure it is suspended.
*
- * If @state is null, we capture the current context.
+ * If \p state is null, we capture the current context.
*/
void
mono_walk_stack_with_state (MonoJitStackWalk func, MonoThreadUnwindState *state, MonoUnwindOptions unwind_options, void *user_data)
/**
* mono_walk_stack_full:
- * @func: callback to call for each stack frame
- * @domain: starting appdomain, can be NULL to use the current domain
- * @unwind_options: what extra information the unwinder should gather
- * @start_ctx: starting state of the stack walk, can be NULL.
- * @thread: the thread whose stack to walk, can be NULL to use the current thread
- * @lmf: the LMF of @thread, can be NULL to use the LMF of the current thread
- * @user_data: data passed to the callback
- *
+ * \param func callback to call for each stack frame
+ * \param domain starting appdomain, can be NULL to use the current domain
+ * \param unwind_options what extra information the unwinder should gather
+ * \param start_ctx starting state of the stack walk, can be NULL.
+ * \param thread the thread whose stack to walk, can be NULL to use the current thread
+ * \param lmf the LMF of \p thread, can be NULL to use the LMF of the current thread
+ * \param user_data data passed to the callback
* This function walks the stack of a thread, starting from the state
- * represented by start_ctx. For each frame the callback
+ * represented by \p start_ctx. For each frame the callback
* function is called with the relevant info. The walk ends when no more
* managed stack frames are found or when the callback returns a TRUE value.
*/
mgreg_t *new_reg_locations [MONO_MAX_IREGS];
gboolean get_reg_locations = unwind_options & MONO_UNWIND_REG_LOCATIONS;
gboolean async = mono_thread_info_is_async_context ();
+ Unwinder unwinder;
if (mono_llvm_only) {
GSList *l, *ips;
memcpy (&ctx, start_ctx, sizeof (MonoContext));
memset (reg_locations, 0, sizeof (reg_locations));
+ unwinder_init (&unwinder);
+
while (MONO_CONTEXT_GET_SP (&ctx) < jit_tls->end_of_stack) {
frame.lmf = lmf;
- res = mono_find_jit_info_ext (domain, jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, get_reg_locations ? new_reg_locations : NULL, &frame);
+ res = unwinder_unwind_frame (&unwinder, domain, jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, get_reg_locations ? new_reg_locations : NULL, &frame);
if (!res)
return;
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
if (need_file_info) {
if (location) {
- mono_gc_wbarrier_generic_store (file, (MonoObject*) mono_string_new (domain, location->source_file));
+ MonoString *filename = mono_string_new_checked (domain, location->source_file, &error);
+ if (!is_ok (&error)) {
+ mono_error_set_pending_exception (&error);
+ return FALSE;
+ }
+ mono_gc_wbarrier_generic_store (file, (MonoObject*)filename);
*line = location->row;
*column = location->column;
} else {
static MonoArray*
build_native_trace (MonoError *error)
{
- mono_error_init (error);
+ error_init (error);
/* This puppy only makes sense on mobile, IOW, ARM. */
#if defined (HAVE_BACKTRACE_SYMBOLS) && defined (TARGET_ARM)
MonoArray *res;
gint32 filter_idx;
int i;
MonoObject *ex_obj;
+ Unwinder unwinder;
+ gboolean in_interp;
g_assert (ctx != NULL);
mono_ex = (MonoException*)obj;
MonoArray *initial_trace_ips = mono_ex->trace_ips;
if (initial_trace_ips) {
- int len = mono_array_length (initial_trace_ips) >> 1;
+ int len = mono_array_length (initial_trace_ips) / TRACE_IP_ENTRY_SIZE;
for (i = 0; i < (len - 1); i++) {
- gpointer ip = mono_array_get (initial_trace_ips, gpointer, i * 2 + 0);
- gpointer generic_info = mono_array_get (initial_trace_ips, gpointer, i * 2 + 1);
- trace_ips = g_list_prepend (trace_ips, ip);
- trace_ips = g_list_prepend (trace_ips, generic_info);
+ for (int j = 0; j < TRACE_IP_ENTRY_SIZE; ++j) {
+ gpointer p = mono_array_get (initial_trace_ips, gpointer, (i * TRACE_IP_ENTRY_SIZE) + j);
+ trace_ips = g_list_prepend (trace_ips, p);
+ }
}
}
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 = (guint8*)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);
ex_obj = obj;
if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) {
- gboolean is_user_frame = method->wrapper_type == MONO_WRAPPER_NONE || method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD;
#ifndef DISABLE_PERFCOUNTERS
mono_perfcounters->exceptions_filters++;
#endif
- /*
- Here's the thing, if this is a filter clause done by a wrapper like runtime invoke, we don't want to
- trim the stackframe since if it returns FALSE we lose information.
-
- FIXME Not 100% sure if it's a good idea even with user clauses.
- */
- if (is_user_frame)
- setup_stack_trace (mono_ex, dynamic_methods, &trace_ips);
#ifndef MONO_CROSS_COMPILE
#ifdef MONO_CONTEXT_SET_LLVM_EXC_REG
filter_idx ++;
if (filtered) {
- if (!is_user_frame)
- setup_stack_trace (mono_ex, dynamic_methods, &trace_ips);
+ setup_stack_trace (mono_ex, dynamic_methods, &trace_ips);
g_slist_free (dynamic_methods);
/* mono_debugger_agent_handle_exception () needs this */
mini_set_abort_threshold (ctx);
}
MonoError isinst_error;
- mono_error_init (&isinst_error);
+ error_init (&isinst_error);
if (ei->flags == MONO_EXCEPTION_CLAUSE_NONE && mono_object_isinst_checked (ex_obj, catch_class, &error)) {
setup_stack_trace (mono_ex, dynamic_methods, &trace_ips);
g_slist_free (dynamic_methods);
*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);
/**
* mono_handle_exception_internal:
- * @ctx: saved processor state
- * @obj: the exception object
- * @resume: whenever to resume unwinding based on the state in MonoJitTlsData.
+ * \param ctx saved processor state
+ * \param obj the exception object
+ * \param resume whenever to resume unwinding based on the state in \c MonoJitTlsData.
*/
static gboolean
mono_handle_exception_internal (MonoContext *ctx, MonoObject *obj, gboolean resume, MonoJitInfo **out_ji)
int i;
MonoObject *ex_obj;
MonoObject *non_exception = NULL;
+ Unwinder unwinder;
+ gboolean in_interp;
g_assert (ctx != NULL);
if (!obj) {
MonoException *ex = mono_get_exception_null_reference ();
- MONO_OBJECT_SETREF (ex, message, mono_string_new (domain, "Object reference not set to an instance of an object"));
+ MonoString *msg = mono_string_new_checked (domain, "Object reference not set to an instance of an object", &error);
+ mono_error_assert_ok (&error);
+ MONO_OBJECT_SETREF (ex, message, msg);
obj = (MonoObject *)ex;
}
char *from_name = mono_type_get_full_name (jit_tls->class_cast_from);
char *to_name = mono_type_get_full_name (jit_tls->class_cast_to);
char *msg = g_strdup_printf ("Unable to cast object of type '%s' to type '%s'.", from_name, to_name);
- mono_ex->message = mono_string_new (domain, msg);
+ mono_ex->message = mono_string_new_checked (domain, msg, &error);
g_free (from_name);
g_free (to_name);
+ if (!is_ok (&error)) {
+ mono_runtime_printf_err ("Error creating class cast exception message '%s'\n", msg);
+ mono_error_assert_ok (&error);
+ }
g_free (msg);
}
if (!strcmp (mono_ex->object.vtable->klass->name, "ArrayTypeMismatchException")) {
char *from_name = mono_type_get_full_name (jit_tls->class_cast_from);
char *to_name = mono_type_get_full_name (jit_tls->class_cast_to);
char *msg = g_strdup_printf ("Source array of type '%s' cannot be cast to destination array type '%s'.", from_name, to_name);
- mono_ex->message = mono_string_new (domain, msg);
+ mono_ex->message = mono_string_new_checked (domain, msg, &error);
g_free (from_name);
g_free (to_name);
+ if (!is_ok (&error)) {
+ mono_runtime_printf_err ("Error creating array type mismatch exception message '%s'\n", msg);
+ mono_error_assert_ok (&error);
+ }
g_free (msg);
}
}
mono_print_thread_dump_from_ctx (ctx);
}
jit_tls->orig_ex_ctx_set = TRUE;
- mono_profiler_exception_thrown (obj);
+ MONO_PROFILER_RAISE (exception_throw, (obj));
jit_tls->orig_ex_ctx_set = FALSE;
res = mono_handle_exception_internal_first_pass (&ctx_cp, obj, &first_filter_idx, &ji, &prev_ji, non_exception);
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;
lmf = jit_tls->resume_state.lmf;
first_filter_idx = jit_tls->resume_state.first_filter_idx;
filter_idx = jit_tls->resume_state.filter_idx;
+ in_interp = FALSE;
} 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 = (guint8*)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);
filter_idx ++;
}
- mono_error_init (&error);
+ error_init (&error);
if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE &&
mono_object_isinst_checked (ex_obj, catch_class, &error)) || filtered) {
/*
* that was called by the EH machinery. It won't have a guard trampoline installed, so we must
* check for this situation here and resume interruption if we are below the guarded block.
*/
- if (G_UNLIKELY (jit_tls->handler_block_return_address)) {
+ if (G_UNLIKELY (jit_tls->handler_block)) {
gboolean is_outside = FALSE;
gpointer prot_bp = MONO_CONTEXT_GET_BP (&jit_tls->handler_block_context);
gpointer catch_bp = MONO_CONTEXT_GET_BP (ctx);
//FIXME make this stack direction aware
+
if (catch_bp > prot_bp) {
is_outside = TRUE;
} else if (catch_bp == prot_bp) {
}
}
if (is_outside) {
- jit_tls->handler_block_return_address = NULL;
jit_tls->handler_block = NULL;
- mono_thread_resume_interruption (); /*We ignore the exception here, it will be raised later*/
+ mono_thread_resume_interruption (TRUE); /*We ignore the exception here, it will be raised later*/
}
}
if (mono_trace_is_enabled () && mono_trace_eval (method))
g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (method, TRUE));
jit_tls->orig_ex_ctx_set = TRUE;
- mono_profiler_exception_clause_handler (method, ei->flags, i);
+ MONO_PROFILER_RAISE (exception_clause, (method, i, ei->flags, ex_obj));
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 (jit_tls, mono_ex, frame.interp_frame, ei->handler_start);
+ /* Undo the IP adjustment done by mono_arch_unwind_frame () */
+#if defined(TARGET_AMD64)
+ ctx->gregs [AMD64_RIP] ++;
+#elif defined(TARGET_ARM)
+ ctx->pc ++;
+ if (mono_arm_thumb_supported ())
+ ctx->pc |= 1;
+#elif defined(TARGET_ARM64)
+ ctx->pc ++;
+#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;
if (mono_trace_is_enabled () && mono_trace_eval (method))
g_print ("EXCEPTION: fault clause %d of %s\n", i, mono_method_full_name (method, TRUE));
jit_tls->orig_ex_ctx_set = TRUE;
- mono_profiler_exception_clause_handler (method, ei->flags, i);
+ MONO_PROFILER_RAISE (exception_clause, (method, i, ei->flags, ex_obj));
jit_tls->orig_ex_ctx_set = FALSE;
- mini_set_abort_threshold (ctx);
- call_filter (ctx, ei->handler_start);
}
if (ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
if (mono_trace_is_enabled () && mono_trace_eval (method))
g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (method, TRUE));
jit_tls->orig_ex_ctx_set = TRUE;
- mono_profiler_exception_clause_handler (method, ei->flags, i);
+ MONO_PROFILER_RAISE (exception_clause, (method, i, ei->flags, ex_obj));
jit_tls->orig_ex_ctx_set = FALSE;
#ifndef DISABLE_PERFCOUNTERS
mono_perfcounters->exceptions_finallys++;
#endif
+ }
+ if (ei->flags == MONO_EXCEPTION_CLAUSE_FAULT || ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
mono_set_lmf (lmf);
if (ji->from_llvm) {
/*
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);
}
}
}
}
- jit_tls->orig_ex_ctx_set = TRUE;
- mono_profiler_exception_method_leave (method);
- jit_tls->orig_ex_ctx_set = FALSE;
+ if (MONO_PROFILER_ENABLED (method_exception_leave) &&
+ mono_profiler_get_call_instrumentation_flags (method) & MONO_PROFILER_CALL_INSTRUMENTATION_EXCEPTION_LEAVE) {
+ jit_tls->orig_ex_ctx_set = TRUE;
+ MONO_PROFILER_RAISE (method_exception_leave, (method, ex_obj));
+ jit_tls->orig_ex_ctx_set = FALSE;
+ }
*ctx = new_ctx;
}
/**
* mono_debugger_run_finally:
- * @start_ctx: saved processor state
- *
- * This method is called by the Mono Debugger to call all `finally' clauses of the
- * current stack frame. It's used when the user issues a `return' command to make
+ * \param start_ctx saved processor state
+ * This method is called by the Mono Debugger to call all \c finally clauses of the
+ * current stack frame. It's used when the user issues a \c return command to make
* the current stack frame return. After returning from this method, the debugger
* unwinds the stack one frame and gives control back to the user.
- *
* NOTE: This method is only used when running inside the Mono Debugger.
*/
void
/**
* mono_handle_exception:
- * @ctx: saved processor state
- * @obj: the exception object
+ * \param ctx saved processor state
+ * \param obj the exception object
+ *
+ * Handle the exception OBJ starting from the state CTX. Modify CTX to point to the handler clause if the exception is caught, and
+ * return TRUE.
*/
gboolean
mono_handle_exception (MonoContext *ctx, MonoObject *obj)
#endif
}
-static gboolean handling_sigsegv = FALSE;
+static gboolean handle_crash_loop = FALSE;
/*
* mono_handle_native_crash:
#endif
MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
- gboolean is_sigsegv = !strcmp ("SIGSEGV", signal);
-
- if (handling_sigsegv && is_sigsegv)
+ if (handle_crash_loop)
return;
- if (mini_get_debug_options ()->suspend_on_sigsegv && is_sigsegv) {
+ if (mini_get_debug_options ()->suspend_on_native_crash) {
mono_runtime_printf_err ("Received %s, suspending...", signal);
#ifdef HOST_WIN32
while (1)
#endif
}
- /* To prevent infinite loops when the stack walk causes a crash */
- if (is_sigsegv)
- handling_sigsegv = TRUE;
+ /* prevent infinite loops in crash handling */
+ handle_crash_loop = TRUE;
/* !jit_tls means the thread was not registered with the runtime */
if (jit_tls && mono_thread_internal_current ()) {
* glibc fork acquires some locks, so if the crash happened inside malloc/free,
* it will deadlock. Call the syscall directly instead.
*/
-#if defined(PLATFORM_ANDROID)
+#if defined(HOST_ANDROID)
/* SYS_fork is defined to be __NR_fork which is not defined in some ndk versions */
g_assert_not_reached ();
-#elif !defined(PLATFORM_MACOSX) && defined(SYS_fork)
+#elif !defined(HOST_DARWIN) && defined(SYS_fork)
pid = (pid_t) syscall (SYS_fork);
-#elif defined(PLATFORM_MACOSX) && HAVE_FORK
+#elif defined(HOST_DARWIN) && HAVE_FORK
pid = (pid_t) fork ();
#else
g_assert_not_reached ();
#endif
}
#else
-#ifdef PLATFORM_ANDROID
+#ifdef HOST_ANDROID
/* set DUMPABLE for this process so debuggerd can attach with ptrace(2), see:
* https://android.googlesource.com/platform/bionic/+/151da681000c07da3c24cd30a3279b1ca017f452/linker/debugger.cpp#206
* this has changed on later versions of Android. Also, we don't want to
if (!mono_do_crash_chaining) {
/*Android abort is a fluke, it doesn't abort, it triggers another segv. */
-#if defined (PLATFORM_ANDROID)
+#if defined (HOST_ANDROID)
exit (-1);
#else
abort ();
mono_runtime_printf ("%s", text->str);
-#if PLATFORM_WIN32 && TARGET_WIN32 && _DEBUG
+#if HOST_WIN32 && TARGET_WIN32 && _DEBUG
OutputDebugStringA(text->str);
#endif
mono_runtime_stdout_fflush ();
}
-/*
+/**
* mono_print_thread_dump:
*
- * Print information about the current thread to stdout.
- * SIGCTX can be NULL, allowing this to be called from gdb.
+ * Print information about the current thread to stdout.
+ * \p sigctx can be NULL, allowing this to be called from gdb.
*/
void
mono_print_thread_dump (void *sigctx)
mono_restore_context (&new_ctx);
}
-#ifdef MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD
-
typedef struct {
MonoJitInfo *ji;
MonoContext ctx;
continue;
/*If ip points to the first instruction it means the handler block didn't start
so we can leave its execution to the EH machinery*/
- if (ei->handler_start < ip && ip < ei->data.handler_end) {
+ if (ei->handler_start <= ip && ip < ei->data.handler_end) {
pdata->ji = ji;
pdata->ei = ei;
pdata->ctx = *ctx;
}
-static gpointer
+static void
install_handler_block_guard (MonoJitInfo *ji, MonoContext *ctx)
{
int i;
MonoJitExceptionInfo *clause = NULL;
gpointer ip;
+ guint8 *bp;
ip = MONO_CONTEXT_GET_IP (ctx);
clause = &ji->clauses [i];
if (clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY)
continue;
- if (clause->handler_start < ip && clause->data.handler_end > ip)
+ if (clause->handler_start <= ip && clause->data.handler_end > ip)
break;
}
/*no matching finally */
if (i == ji->num_clauses)
- return NULL;
-
- /*If we stopped on the instruction right before the try, we haven't actually started executing it*/
- if (ip == clause->handler_start)
- return NULL;
+ return;
- return mono_arch_install_handler_block_guard (ji, clause, ctx, mono_create_handler_block_trampoline ());
+ /*Load the spvar*/
+ bp = (guint8*)MONO_CONTEXT_GET_BP (ctx);
+ *(bp + clause->exvar_offset) = 1;
}
/*
* Finds the bottom handler block running and install a block guard if needed.
*/
-gboolean
+static gboolean
mono_install_handler_block_guard (MonoThreadUnwindState *ctx)
{
FindHandlerBlockData data = { 0 };
MonoJitTlsData *jit_tls = (MonoJitTlsData *)ctx->unwind_data [MONO_UNWIND_DATA_JIT_TLS];
- gpointer resume_ip;
-
-#ifndef MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD_AOT
- if (mono_aot_only)
- return FALSE;
-#endif
/* Guard against a null MonoJitTlsData. This can happens if the thread receives the
* interrupt signal before the JIT has time to initialize its TLS data for the given thread.
*/
- if (!jit_tls || jit_tls->handler_block_return_address)
+ if (!jit_tls || jit_tls->handler_block)
return FALSE;
/* Do an async safe stack walk */
memcpy (&jit_tls->handler_block_context, &data.ctx, sizeof (MonoContext));
- resume_ip = install_handler_block_guard (data.ji, &data.ctx);
- if (resume_ip == NULL)
- return FALSE;
+ install_handler_block_guard (data.ji, &data.ctx);
- jit_tls->handler_block_return_address = resume_ip;
jit_tls->handler_block = data.ei;
return TRUE;
mono_current_thread_has_handle_block_guard (void)
{
MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
- return jit_tls && jit_tls->handler_block_return_address != NULL;
+ return jit_tls && jit_tls->handler_block != NULL;
}
-#else
-gboolean
-mono_install_handler_block_guard (MonoThreadUnwindState *ctx)
-{
- return FALSE;
-}
-
-static gboolean
-mono_current_thread_has_handle_block_guard (void)
-{
- return FALSE;
-}
-
-#endif
-
void
mono_set_cast_details (MonoClass *from, MonoClass *to)
{
guint8*
mono_jinfo_get_unwind_info (MonoJitInfo *ji, guint32 *unwind_info_len)
{
- if (ji->from_aot)
+ if (ji->has_unwind_info) {
+ /* The address/length in the MonoJitInfo structure itself */
+ MonoUnwindJitInfo *info = mono_jit_info_get_unwind_info (ji);
+ *unwind_info_len = info->unw_info_len;
+ return info->unw_info;
+ } else if (ji->from_aot)
return mono_aot_get_unwind_info (ji, unwind_info_len);
else
return mono_get_cached_unwind_info (ji->unwind_info, unwind_info_len);
mono_llvm_throw_exception ((MonoObject*)e);
}
+void
+mono_llvm_reraise_exception (MonoException *e)
+{
+ mono_llvm_rethrow_exception ((MonoObject*)e);
+}
+
void
mono_llvm_throw_corlib_exception (guint32 ex_token_index)
{
size_t upper = mono_array_length (mono_ex->trace_ips);
- for (int i = 0; i < upper; i+= 2) {
+ for (int i = 0; i < upper; i += TRACE_IP_ENTRY_SIZE) {
gpointer curr_ip = mono_array_get (mono_ex->trace_ips, gpointer, i);
- gpointer curr_info = mono_array_get (mono_ex->trace_ips, gpointer, i + 1);
- trace_ips = g_list_append (trace_ips, curr_ip);
- trace_ips = g_list_append (trace_ips, curr_info);
-
+ for (int j = 0; j < TRACE_IP_ENTRY_SIZE; ++j) {
+ gpointer p = mono_array_get (mono_ex->trace_ips, gpointer, i + j);
+ trace_ips = g_list_append (trace_ips, p);
+ }
if (ip == curr_ip)
break;
}