-/*
- * 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/mono-endian.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"
#define MONO_ARCH_CONTEXT_DEF
#endif
+#ifndef MONO_ARCH_STACK_GROWS_UP
+#define MONO_ARCH_STACK_GROWS_UP 0
+#endif
+
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_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
+first_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer addr)
+{
+ gpointer **data = (gpointer **)addr;
+
+ if (!frame->managed)
+ return FALSE;
+
+ if (!ctx) {
+ // FIXME: Happens with llvm_only
+ *data = NULL;
+ return TRUE;
+ }
+
+ *data = MONO_CONTEXT_GET_SP (ctx);
+ g_assert (*data);
+ return TRUE;
+}
+
+static gpointer
+mono_thread_get_managed_sp (void)
+{
+ gpointer addr = NULL;
+ mono_walk_stack (first_managed, MONO_UNWIND_SIGNAL_SAFE, &addr);
+ return addr;
+}
+
+static inline int
+mini_abort_threshold_offset (gpointer threshold, gpointer sp)
+{
+ intptr_t stack_threshold = (intptr_t) threshold;
+ intptr_t stack_pointer = (intptr_t) sp;
+
+ const int direction = MONO_ARCH_STACK_GROWS_UP ? -1 : 1;
+ intptr_t magnitude = stack_pointer - stack_threshold;
+
+ return direction * magnitude;
+}
+
+static inline void
+mini_clear_abort_threshold (void)
+{
+ MonoJitTlsData *jit_tls = mono_get_jit_tls ();
+ jit_tls->abort_exc_stack_threshold = NULL;
+}
+
+static inline void
+mini_set_abort_threshold (MonoContext *ctx)
+{
+ gpointer sp = MONO_CONTEXT_GET_SP (ctx);
+ MonoJitTlsData *jit_tls = mono_get_jit_tls ();
+ // Only move it up, to avoid thrown/caught
+ // exceptions lower in the stack from triggering
+ // a rethrow
+ gboolean above_threshold = mini_abort_threshold_offset (jit_tls->abort_exc_stack_threshold, sp) >= 0;
+ if (!jit_tls->abort_exc_stack_threshold || above_threshold) {
+ jit_tls->abort_exc_stack_threshold = sp;
+ }
+}
+
+// Note: In the case that the frame is above where the thread abort
+// was set we bump the threshold so that functions called from the new,
+// higher threshold don't trigger the thread abort exception
+static inline gboolean
+mini_above_abort_threshold (void)
+{
+ gpointer sp = mono_thread_get_managed_sp ();
+ MonoJitTlsData *jit_tls = (MonoJitTlsData*) mono_tls_get_jit_tls ();
+
+ if (!sp)
+ return TRUE;
+
+ gboolean above_threshold = mini_abort_threshold_offset (jit_tls->abort_exc_stack_threshold, sp) >= 0;
+
+ if (above_threshold)
+ jit_tls->abort_exc_stack_threshold = sp;
+
+ return above_threshold;
+}
+
+static int
+mono_get_seq_point_for_native_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset)
+{
+ SeqPoint sp;
+ if (mono_find_prev_seq_point_for_native_offset (domain, method, native_offset, NULL, &sp))
+ return sp.il_offset;
+ return -1;
+}
+
void
mono_exceptions_init (void)
{
#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)
cbs.mono_exception_walk_trace = mono_exception_walk_trace;
cbs.mono_install_handler_block_guard = mono_install_handler_block_guard;
cbs.mono_current_thread_has_handle_block_guard = mono_current_thread_has_handle_block_guard;
+ cbs.mono_clear_abort_threshold = mini_clear_abort_threshold;
+ cbs.mono_above_abort_threshold = mini_above_abort_threshold;
mono_install_eh_callbacks (&cbs);
+ mono_install_get_seq_point (mono_get_seq_point_for_native_offset);
}
gpointer
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.
*/
klass = vtable->klass;
}
- //g_assert (!method->klass->generic_container);
- if (method->klass->generic_class)
- method_container_class = method->klass->generic_class->container_class;
+ //g_assert (!mono_class_is_gtd (method->klass));
+ if (mono_class_is_ginst (method->klass))
+ method_container_class = mono_class_get_generic_class (method->klass)->container_class;
else
method_container_class = method->klass;
/* class might refer to a subclass of method's class */
- while (!(klass == method->klass || (klass->generic_class && klass->generic_class->container_class == method_container_class))) {
+ while (!(klass == method->klass || (mono_class_is_ginst (klass) && mono_class_get_generic_class (klass)->container_class == method_container_class))) {
klass = klass->parent;
g_assert (klass);
}
- if (klass->generic_class || klass->generic_container)
+ if (mono_class_is_ginst (klass) || mono_class_is_gtd (klass))
context.class_inst = mini_class_get_context (klass)->class_inst;
- if (klass->generic_class)
- g_assert (mono_class_has_parent_and_ignore_generics (klass->generic_class->container_class, method_container_class));
+ if (mono_class_is_ginst (klass))
+ g_assert (mono_class_has_parent_and_ignore_generics (mono_class_get_generic_class (klass)->container_class, method_container_class));
else
g_assert (mono_class_has_parent_and_ignore_generics (klass, method_container_class));
/**
* 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
mono_runtime_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data)
{
if (!start_ctx) {
- MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
+ MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
if (jit_tls && jit_tls->orig_ex_ctx_set)
start_ctx = &jit_tls->orig_ex_ctx;
}
}
/**
* 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.
*/
{
MonoError error;
MonoDomain *domain = mono_domain_get ();
- MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
+ MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
MonoLMF *lmf = mono_get_lmf ();
MonoJitInfo *ji = NULL;
MonoContext ctx, new_ctx;
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
}
/* Class lazy loading functions */
-static GENERATE_GET_CLASS_WITH_CACHE (runtime_compat_attr, System.Runtime.CompilerServices, RuntimeCompatibilityAttribute)
+static GENERATE_GET_CLASS_WITH_CACHE (runtime_compat_attr, "System.Runtime.CompilerServices", "RuntimeCompatibilityAttribute")
/*
* wrap_non_exception_throws:
return val;
}
-#ifndef MONO_ARCH_STACK_GROWS_UP
-#define DOES_STACK_GROWS_UP 1
-#else
-#define DOES_STACK_GROWS_UP 0
-#endif
-
#define MAX_UNMANAGED_BACKTRACE 128
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;
}
static void
-setup_stack_trace (MonoException *mono_ex, GSList *dynamic_methods, MonoArray *initial_trace_ips, GList **trace_ips)
+setup_stack_trace (MonoException *mono_ex, GSList *dynamic_methods, GList **trace_ips)
{
- if (mono_ex && !initial_trace_ips) {
+ if (mono_ex) {
*trace_ips = g_list_reverse (*trace_ips);
MonoError error;
MonoArray *ips_arr = mono_glist_to_array (*trace_ips, mono_defaults.int_class, &error);
MonoDomain *domain = mono_domain_get ();
MonoJitInfo *ji = NULL;
static int (*call_filter) (MonoContext *, gpointer) = NULL;
- MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
+ MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
MonoLMF *lmf = mono_get_lmf ();
- MonoArray *initial_trace_ips = NULL;
GList *trace_ips = NULL;
GSList *dynamic_methods = NULL;
MonoException *mono_ex;
gint32 filter_idx;
int i;
MonoObject *ex_obj;
+ Unwinder unwinder;
+ gboolean in_interp;
g_assert (ctx != NULL);
stack_overflow = TRUE;
mono_ex = (MonoException*)obj;
- initial_trace_ips = mono_ex->trace_ips;
+ MonoArray *initial_trace_ips = mono_ex->trace_ips;
+ if (initial_trace_ips) {
+ int len = mono_array_length (initial_trace_ips) >> 1;
+
+ 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);
+ }
+ }
- if (mono_object_isinst_checked (obj, mono_defaults.exception_class, &error)) {
- mono_ex = (MonoException*)obj;
- initial_trace_ips = mono_ex->trace_ips;
- } else {
+ if (!mono_object_isinst_checked (obj, mono_defaults.exception_class, &error)) {
mono_error_assert_ok (&error);
mono_ex = 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, initial_trace_ips, &trace_ips);
+ 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 (method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE && mono_ex) {
- /*
- * Avoid overwriting the stack trace if the exception is
- * rethrown. Also avoid giant stack traces during a stack
- * overflow.
- */
- if (!initial_trace_ips && (frame_count < 1000)) {
+ // avoid giant stack traces during a stack overflow
+ if (frame_count < 1000) {
trace_ips = g_list_prepend (trace_ips, MONO_CONTEXT_GET_IP (ctx));
- trace_ips = g_list_prepend (trace_ips,
- get_generic_info_from_stack_frame (ji, ctx));
+ trace_ips = g_list_prepend (trace_ips, get_generic_info_from_stack_frame (ji, ctx));
}
}
dynamic_methods = g_slist_prepend (dynamic_methods, method);
if (stack_overflow) {
- if (DOES_STACK_GROWS_UP)
- free_stack = (guint8*)(MONO_CONTEXT_GET_SP (ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx));
- else
+ if (MONO_ARCH_STACK_GROWS_UP)
free_stack = (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (ctx));
+ else
+ free_stack = (guint8*)(MONO_CONTEXT_GET_SP (ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx));
} else {
free_stack = 0xffffff;
}
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, initial_trace_ips, &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, initial_trace_ips, &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);
MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
return TRUE;
}
}
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, initial_trace_ips, &trace_ips);
+ setup_stack_trace (mono_ex, dynamic_methods, &trace_ips);
g_slist_free (dynamic_methods);
if (out_ji)
*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)
MonoDomain *domain = mono_domain_get ();
MonoJitInfo *ji, *prev_ji;
static int (*call_filter) (MonoContext *, gpointer) = NULL;
- MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
+ MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
MonoLMF *lmf = mono_get_lmf ();
MonoException *mono_ex;
gboolean stack_overflow = FALSE;
int i;
MonoObject *ex_obj;
MonoObject *non_exception = NULL;
+ Unwinder unwinder;
+ gboolean in_interp;
g_assert (ctx != NULL);
if (!obj) {
// FIXME: This runs managed code so it might cause another stack overflow when
// we are handling a stack overflow
+ mini_set_abort_threshold (ctx);
mono_unhandled_exception (obj);
} else {
gboolean unhandled = FALSE;
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 ++;
//printf ("M: %s %d.\n", mono_method_full_name (method, TRUE), frame_count);
if (stack_overflow) {
- if (DOES_STACK_GROWS_UP)
- free_stack = (guint8*)(MONO_CONTEXT_GET_SP (ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx));
- else
+ if (MONO_ARCH_STACK_GROWS_UP)
free_stack = (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (ctx));
+ else
+ free_stack = (guint8*)(MONO_CONTEXT_GET_SP (ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx));
} else {
free_stack = 0xffffff;
}
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) {
/*
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*/
}
}
jit_tls->orig_ex_ctx_set = TRUE;
mono_profiler_exception_clause_handler (method, ei->flags, i);
jit_tls->orig_ex_ctx_set = FALSE;
- MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
+ mini_set_abort_threshold (ctx);
+
+ 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;
jit_tls->orig_ex_ctx_set = TRUE;
mono_profiler_exception_clause_handler (method, ei->flags, i);
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) {
jit_tls->resume_state.lmf = lmf;
jit_tls->resume_state.first_filter_idx = first_filter_idx;
jit_tls->resume_state.filter_idx = filter_idx;
+ mini_set_abort_threshold (ctx);
MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
return 0;
} else {
- call_filter (ctx, ei->handler_start);
+ mini_set_abort_threshold (ctx);
+ if (in_interp)
+ mono_interp_run_finally (&frame, i, ei->handler_start);
+ else
+ call_filter (ctx, ei->handler_start);
}
}
}
/**
* 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
{
static int (*call_filter) (MonoContext *, gpointer) = NULL;
MonoDomain *domain = mono_domain_get ();
- MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
+ MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
MonoLMF *lmf = mono_get_lmf ();
MonoContext ctx, new_ctx;
MonoJitInfo *ji, rji;
/**
* mono_handle_exception:
- * @ctx: saved processor state
- * @obj: the exception object
+ * \param ctx saved processor state
+ * \param obj the exception object
*/
gboolean
mono_handle_exception (MonoContext *ctx, MonoObject *obj)
static G_GNUC_UNUSED void
try_more_restore (void)
{
- MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
+ MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
if (try_restore_stack_protection (jit_tls, 500))
jit_tls->restore_stack_prot = NULL;
}
static G_GNUC_UNUSED void
restore_stack_protection (void)
{
- MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
+ MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
MonoException *ex = mono_domain_get ()->stack_overflow_ex;
/* if we can't restore the stack protection, keep a callback installed so
* we'll try to restore as much stack as we can at each return from unmanaged
#ifndef MONO_CROSS_COMPILE
+static void print_process_map (void)
+{
+#ifdef __linux__
+ FILE *fp = fopen ("/proc/self/maps", "r");
+ char line [256];
+
+ if (fp == NULL) {
+ mono_runtime_printf_err ("no /proc/self/maps, not on linux?\n");
+ return;
+ }
+
+ mono_runtime_printf_err ("/proc/self/maps:");
+
+ while (fgets (line, sizeof (line), fp)) {
+ // strip newline
+ size_t len = strlen (line) - 1;
+ if (len >= 0 && line [len] == '\n')
+ line [len] = '\0';
+
+ mono_runtime_printf_err ("%s", line);
+ }
+
+ fclose (fp);
+#else
+ /* do nothing */
+#endif
+}
+
static gboolean handling_sigsegv = FALSE;
/*
- * mono_handle_native_sigsegv:
+ * mono_handle_native_crash:
*
- * Handle a SIGSEGV received while in native code by printing diagnostic
- * information and aborting.
+ * Handle a native crash (e.g. SIGSEGV) while in native code by
+ * printing diagnostic information and aborting.
*/
void
-mono_handle_native_sigsegv (int signal, void *ctx, MONO_SIG_HANDLER_INFO_TYPE *info)
+mono_handle_native_crash (const char *signal, void *ctx, MONO_SIG_HANDLER_INFO_TYPE *info)
{
#ifdef MONO_ARCH_USE_SIGACTION
struct sigaction sa;
#endif
- MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
- const char *signal_str = (signal == SIGSEGV) ? "SIGSEGV" : "SIGABRT";
+ MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
- if (handling_sigsegv)
+ gboolean is_sigsegv = !strcmp ("SIGSEGV", signal);
+
+ if (handling_sigsegv && is_sigsegv)
return;
- if (mini_get_debug_options ()->suspend_on_sigsegv) {
- mono_runtime_printf_err ("Received SIGSEGV, suspending...");
+ if (mini_get_debug_options ()->suspend_on_native_crash) {
+ mono_runtime_printf_err ("Received %s, suspending...", signal);
#ifdef HOST_WIN32
while (1)
;
}
/* To prevent infinite loops when the stack walk causes a crash */
- handling_sigsegv = TRUE;
+ if (is_sigsegv)
+ handling_sigsegv = TRUE;
/* !jit_tls means the thread was not registered with the runtime */
if (jit_tls && mono_thread_internal_current ()) {
mono_walk_stack (print_stack_frame_to_stderr, MONO_UNWIND_LOOKUP_IL_OFFSET, NULL);
}
+ print_process_map ();
+
#ifdef HAVE_BACKTRACE_SYMBOLS
{
void *array [256];
int status;
pid_t crashed_pid = getpid ();
- //pid = fork ();
/*
* glibc fork acquires some locks, so if the crash happened inside malloc/free,
* it will deadlock. Call the syscall directly instead.
*/
- pid = mono_runtime_syscall_fork ();
+#if defined(PLATFORM_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)
+ pid = (pid_t) syscall (SYS_fork);
+#elif defined(PLATFORM_MACOSX) && HAVE_FORK
+ pid = (pid_t) fork ();
+#else
+ g_assert_not_reached ();
+#endif
+
#if defined (HAVE_PRCTL) && defined(PR_SET_PTRACER)
if (pid > 0) {
// Allow gdb to attach to the process even if ptrace_scope sysctl variable is set to
#endif
}
#else
- mono_exception_native_unwind (ctx, info);
+#ifdef PLATFORM_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
+ * set this on start-up as DUMPABLE has security implications. */
+ prctl (PR_SET_DUMPABLE, 1);
+
+ mono_runtime_printf_err ("\nNo native Android stacktrace (see debuggerd output).\n");
+#endif
#endif
/*
"a fatal error in the mono runtime or one of the native libraries \n"
"used by your application.\n"
"=================================================================\n",
- signal_str);
-
+ signal);
#ifdef MONO_ARCH_USE_SIGACTION
-
- /* Remove our SIGABRT handler */
sa.sa_handler = SIG_DFL;
sigemptyset (&sa.sa_mask);
sa.sa_flags = 0;
+ /* Remove our SIGABRT handler */
g_assert (sigaction (SIGABRT, &sa, NULL) != -1);
+ /* On some systems we get a SIGILL when calling abort (), because it might
+ * fail to raise SIGABRT */
+ g_assert (sigaction (SIGILL, &sa, NULL) != -1);
#endif
if (!mono_do_crash_chaining) {
#else
void
-mono_handle_native_sigsegv (int signal, void *ctx, MONO_SIG_HANDLER_INFO_TYPE *info)
+mono_handle_native_crash (const char *signal, void *ctx, MONO_SIG_HANDLER_INFO_TYPE *info)
{
g_assert_not_reached ();
}
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_REQ_GC_UNSAFE_MODE;
- MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
+ MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
MonoContext new_ctx;
MONO_CONTEXT_SET_IP (ctx, MONO_CONTEXT_GET_IP (&jit_tls->resume_state.ctx));
static gboolean
mono_current_thread_has_handle_block_guard (void)
{
- MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
+ MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
return jit_tls && jit_tls->handler_block_return_address != NULL;
}
MonoJitTlsData *jit_tls = NULL;
if (mini_get_debug_options ()->better_cast_details) {
- jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
+ jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
jit_tls->class_cast_from = from;
jit_tls->class_cast_to = to;
}
MonoThreadInfo *thread = mono_thread_info_current_unchecked ();
if (!thread) {
ctx->valid = FALSE;
- G_BREAKPOINT ();
return FALSE;
}
mono_setup_async_callback (MonoContext *ctx, void (*async_cb)(void *fun), gpointer user_data)
{
#ifdef MONO_ARCH_HAVE_SETUP_ASYNC_CALLBACK
- MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
+ MonoJitTlsData *jit_tls = (MonoJitTlsData *)mono_tls_get_jit_tls ();
jit_tls->ex_ctx = *ctx;
mono_arch_setup_async_callback (ctx, async_cb, user_data);
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);
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