X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Fmini-exceptions.c;h=c0fc622058afb5c708057c3e5787ebf0d4544e91;hb=b43d924d7569d58f8d97f6ba1f4ce22ad48a492b;hp=e1a83ec56f82120f35fe479aa00d9b97c5b377a2;hpb=a532e023384b9135483f8518d7e6f8c094777106;p=mono.git diff --git a/mono/mini/mini-exceptions.c b/mono/mini/mini-exceptions.c index e1a83ec56f8..c0fc622058a 100644 --- a/mono/mini/mini-exceptions.c +++ b/mono/mini/mini-exceptions.c @@ -3,8 +3,10 @@ * * Authors: * Dietmar Maurer (dietmar@ximian.com) + * Mono Team (mono-list@lists.ximian.com) * - * (C) 2001 Ximian, Inc. + * Copyright 2001-2003 Ximian, Inc. + * Copyright 2003-2008 Ximian, Inc. */ #include @@ -16,11 +18,22 @@ #include #endif -#ifndef PLATFORM_WIN32 +#ifdef HAVE_SYS_TYPES_H #include +#endif + +#ifdef HAVE_SYS_WAIT_H +#include +#endif + +#ifdef HAVE_UNISTD_H #include #endif +#ifdef HAVE_SYS_SYSCALL_H +#include +#endif + #include #include #include @@ -33,27 +46,219 @@ #include #include "mini.h" +#include "debug-mini.h" #include "trace.h" -#ifdef HAVE_UNISTD_H -#include -#endif +#include "debugger-agent.h" #ifndef MONO_ARCH_CONTEXT_DEF #define MONO_ARCH_CONTEXT_DEF #endif +static gpointer restore_context_func, call_filter_func; +static gpointer throw_exception_func, rethrow_exception_func; +static gpointer throw_exception_by_name_func, throw_corlib_exception_func; + +static gpointer try_more_restore_tramp = NULL; +static gpointer restore_stack_protection_tramp = NULL; + +static void try_more_restore (void); +static void restore_stack_protection (void); + void mono_exceptions_init (void) { -#ifndef CUSTOM_EXCEPTION_HANDLING - mono_arch_get_restore_context (); - mono_arch_get_call_filter (); - mono_arch_get_throw_exception (); - mono_arch_get_rethrow_exception (); +#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES + guint32 code_size; + MonoJumpInfo *ji; + + if (mono_aot_only) { + restore_context_func = mono_aot_get_named_code ("restore_context"); + call_filter_func = mono_aot_get_named_code ("call_filter"); + throw_exception_func = mono_aot_get_named_code ("throw_exception"); + rethrow_exception_func = mono_aot_get_named_code ("rethrow_exception"); + } else { + restore_context_func = mono_arch_get_restore_context_full (&code_size, &ji, FALSE); + call_filter_func = mono_arch_get_call_filter_full (&code_size, &ji, FALSE); + throw_exception_func = mono_arch_get_throw_exception_full (&code_size, &ji, FALSE); + rethrow_exception_func = mono_arch_get_rethrow_exception_full (&code_size, &ji, FALSE); + } +#else + restore_context_func = mono_arch_get_restore_context (); + call_filter_func = mono_arch_get_call_filter (); + throw_exception_func = mono_arch_get_throw_exception (); + rethrow_exception_func = mono_arch_get_rethrow_exception (); +#endif +#ifdef MONO_ARCH_HAVE_RESTORE_STACK_SUPPORT + try_more_restore_tramp = mono_create_specific_trampoline (try_more_restore, MONO_TRAMPOLINE_RESTORE_STACK_PROT, mono_domain_get (), NULL); + restore_stack_protection_tramp = mono_create_specific_trampoline (restore_stack_protection, MONO_TRAMPOLINE_RESTORE_STACK_PROT, mono_domain_get (), NULL); +#endif + +#ifdef MONO_ARCH_HAVE_EXCEPTIONS_INIT + mono_arch_exceptions_init (); #endif } -#ifndef mono_find_jit_info +gpointer +mono_get_throw_exception (void) +{ + g_assert (throw_exception_func); + return throw_exception_func; +} + +gpointer +mono_get_rethrow_exception (void) +{ + g_assert (rethrow_exception_func); + return rethrow_exception_func; +} + +gpointer +mono_get_call_filter (void) +{ + g_assert (call_filter_func); + return call_filter_func; +} + +gpointer +mono_get_restore_context (void) +{ + g_assert (restore_context_func); + return restore_context_func; +} + +gpointer +mono_get_throw_exception_by_name (void) +{ +#ifdef MONO_ARCH_HAVE_THROW_EXCEPTION_BY_NAME + + gpointer code = NULL; +#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES + guint32 code_size; + MonoJumpInfo *ji; +#endif + + /* This depends on corlib classes so cannot be inited in mono_exceptions_init () */ + if (throw_exception_by_name_func) + return throw_exception_by_name_func; + +#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES + if (mono_aot_only) + code = mono_aot_get_named_code ("throw_exception_by_name"); + else + code = mono_arch_get_throw_exception_by_name_full (&code_size, &ji, FALSE); +#else + code = mono_arch_get_throw_exception_by_name (); +#endif + + mono_memory_barrier (); + + throw_exception_by_name_func = code; + +#else + + throw_exception_by_name_func = NULL; + + g_assert_not_reached (); +#endif + + return throw_exception_by_name_func; +} + +gpointer +mono_get_throw_corlib_exception (void) +{ + gpointer code = NULL; +#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES + guint32 code_size; + MonoJumpInfo *ji; +#endif + + /* This depends on corlib classes so cannot be inited in mono_exceptions_init () */ + if (throw_corlib_exception_func) + return throw_corlib_exception_func; + +#if MONO_ARCH_HAVE_THROW_CORLIB_EXCEPTION +#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES + if (mono_aot_only) + code = mono_aot_get_named_code ("throw_corlib_exception"); + else + code = mono_arch_get_throw_corlib_exception_full (&code_size, &ji, FALSE); +#else + code = mono_arch_get_throw_corlib_exception (); +#endif +#else + g_assert_not_reached (); +#endif + + mono_memory_barrier (); + + throw_corlib_exception_func = code; + + return throw_corlib_exception_func; +} + +#ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT + +/* + * find_jit_info_no_ext: + * + * If the target has the find_jit_info_ext version of this function, define the old + * version here which translates between the old and new APIs. + */ +static MonoJitInfo * +find_jit_info_no_ext (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, + MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed) +{ + StackFrameInfo frame; + MonoJitInfo *ji; + gboolean err; + gpointer ip = MONO_CONTEXT_GET_IP (ctx); + + /* Avoid costly table lookup during stack overflow */ + if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size))) + ji = prev_ji; + else + ji = mini_jit_info_table_find (domain, ip, NULL); + + if (managed) + *managed = FALSE; + + err = mono_arch_find_jit_info_ext (domain, jit_tls, ji, ctx, new_ctx, lmf, &frame); + if (!err) + return (gpointer)-1; + + /* Convert between the new and the old APIs */ + switch (frame.type) { + case FRAME_TYPE_MANAGED: + if (managed) + *managed = TRUE; + return ji; + case FRAME_TYPE_MANAGED_TO_NATIVE: + if (frame.ji) + return frame.ji; + else { + memset (res, 0, sizeof (MonoJitInfo)); + res->method = frame.method; + return res; + } + case FRAME_TYPE_DEBUGGER_INVOKE: { + MonoContext tmp_ctx; + + /* + * The normal exception handling code can't handle this frame, so just + * skip it. + */ + ji = find_jit_info_no_ext (domain, jit_tls, res, NULL, new_ctx, &tmp_ctx, lmf, managed); + memcpy (new_ctx, &tmp_ctx, sizeof (MonoContext)); + return ji; + } + default: + g_assert_not_reached (); + return NULL; + } +} + +#endif /* mono_find_jit_info: * @@ -64,8 +269,8 @@ mono_exceptions_init (void) * the @lmf if necessary. @native_offset return the IP offset from the * start of the function or -1 if that info is not available. */ -static MonoJitInfo * -mono_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, +MonoJitInfo * +mono_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset, gboolean *managed) { @@ -82,12 +287,16 @@ mono_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *re if (managed) *managed = FALSE; - ji = mono_arch_find_jit_info (domain, jit_tls, res, prev_ji, ctx, new_ctx, NULL, lmf, NULL, &managed2); +#ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT + ji = find_jit_info_no_ext (domain, jit_tls, res, prev_ji, ctx, new_ctx, lmf, &managed2); +#else + ji = mono_arch_find_jit_info (domain, jit_tls, res, prev_ji, ctx, new_ctx, lmf, &managed2); +#endif if (ji == (gpointer)-1) return ji; - if (managed2 || ji->method->wrapper_type) { + if (managed2 || (ji && ji->method->wrapper_type)) { const char *real_ip, *start; gint32 offset; @@ -123,7 +332,166 @@ mono_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *re return ji; } -#endif /* mono_find_jit_info */ +#ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT + +/* + * mono_find_jit_info_ext: + * + * A version of mono_find_jit_info which returns all data in the StackFrameInfo + * structure. + */ +gboolean +mono_find_jit_info_ext (MonoDomain *domain, MonoJitTlsData *jit_tls, + MonoJitInfo *prev_ji, MonoContext *ctx, + MonoContext *new_ctx, char **trace, MonoLMF **lmf, + StackFrameInfo *frame) +{ + gboolean err; + gpointer ip = MONO_CONTEXT_GET_IP (ctx); + MonoJitInfo *ji; + MonoDomain *target_domain; + + if (trace) + *trace = NULL; + + /* Avoid costly table lookup during stack overflow */ + if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size))) + ji = prev_ji; + else + ji = mini_jit_info_table_find (domain, ip, &target_domain); + + if (!target_domain) + target_domain = domain; + + err = mono_arch_find_jit_info_ext (target_domain, jit_tls, ji, ctx, new_ctx, lmf, frame); + if (!err) + return FALSE; + + frame->native_offset = -1; + frame->domain = target_domain; + + ji = frame->ji; + + if (ji && (frame->managed || ji->method->wrapper_type)) { + const char *real_ip, *start; + + start = (const char *)ji->code_start; + if (!frame->managed) + /* ctx->ip points into native code */ + real_ip = (const char*)MONO_CONTEXT_GET_IP (new_ctx); + else + real_ip = (const char*)ip; + + if ((real_ip >= start) && (real_ip <= start + ji->code_size)) + frame->native_offset = real_ip - start; + else + frame->native_offset = -1; + + if (trace) + *trace = mono_debug_print_stack_frame (ji->method, frame->native_offset, domain); + } else { + if (trace && frame->method) { + char *fname = mono_method_full_name (frame->method, TRUE); + *trace = g_strdup_printf ("in (unmanaged) %s", fname); + g_free (fname); + } + } + + return TRUE; +} + +#endif /* MONO_ARCH_HAVE_FIND_JIT_INFO_EXT */ + +static gpointer +get_generic_info_from_stack_frame (MonoJitInfo *ji, MonoContext *ctx) +{ + MonoGenericJitInfo *gi; + gpointer info; + + if (!ji->has_generic_jit_info) + return NULL; + gi = mono_jit_info_get_generic_jit_info (ji); + if (!gi->has_this) + return NULL; + + if (gi->this_in_reg) + info = mono_arch_context_get_int_reg (ctx, gi->this_reg); + else + info = *(gpointer*)(gpointer)((char*)mono_arch_context_get_int_reg (ctx, gi->this_reg) + + gi->this_offset); + if (mono_method_get_context (ji->method)->method_inst) { + return info; + } else if ((ji->method->flags & METHOD_ATTRIBUTE_STATIC) || ji->method->klass->valuetype) { + return info; + } else { + /* Avoid returning a managed object */ + MonoObject *this_obj = info; + + return this_obj->vtable->klass; + } +} + +static MonoGenericContext +get_generic_context_from_stack_frame (MonoJitInfo *ji, gpointer generic_info) +{ + MonoGenericContext context = { NULL, NULL }; + MonoClass *class, *method_container_class; + + g_assert (generic_info); + + g_assert (ji->method->is_inflated); + if (mono_method_get_context (ji->method)->method_inst) { + MonoMethodRuntimeGenericContext *mrgctx = generic_info; + + class = mrgctx->class_vtable->klass; + context.method_inst = mrgctx->method_inst; + g_assert (context.method_inst); + } else if ((ji->method->flags & METHOD_ATTRIBUTE_STATIC) || ji->method->klass->valuetype) { + MonoVTable *vtable = generic_info; + + class = vtable->klass; + } else { + class = generic_info; + } + + g_assert (!ji->method->klass->generic_container); + if (ji->method->klass->generic_class) + method_container_class = ji->method->klass->generic_class->container_class; + else + method_container_class = ji->method->klass; + + /* class might refer to a subclass of ji->method's class */ + while (class->generic_class && class->generic_class->container_class != method_container_class) { + class = class->parent; + g_assert (class); + } + + if (class->generic_class || class->generic_container) + context.class_inst = mini_class_get_context (class)->class_inst; + + if (class->generic_class) + g_assert (mono_class_has_parent_and_ignore_generics (class->generic_class->container_class, method_container_class)); + else + g_assert (mono_class_has_parent_and_ignore_generics (class, method_container_class)); + + return context; +} + +static MonoMethod* +get_method_from_stack_frame (MonoJitInfo *ji, gpointer generic_info) +{ + MonoGenericContext context; + MonoMethod *method; + + if (!ji->has_generic_jit_info || !mono_jit_info_get_generic_jit_info (ji)->has_this) + return ji->method; + context = get_generic_context_from_stack_frame (ji, generic_info); + + method = mono_method_get_declaring_generic_method (ji->method); + method = mono_class_inflate_generic_method (method, &context); + + return method; +} MonoString * ves_icall_System_Exception_get_trace (MonoException *ex) @@ -138,11 +506,12 @@ ves_icall_System_Exception_get_trace (MonoException *ex) /* Exception is not thrown yet */ return NULL; - len = mono_array_length (ta); + len = mono_array_length (ta) >> 1; trace_str = g_string_new (""); for (i = 0; i < len; i++) { MonoJitInfo *ji; - gpointer ip = mono_array_get (ta, gpointer, i); + gpointer ip = mono_array_get (ta, gpointer, i * 2 + 0); + gpointer generic_info = mono_array_get (ta, gpointer, i * 2 + 1); ji = mono_jit_info_table_find (domain, ip); if (ji == NULL) { @@ -151,10 +520,11 @@ ves_icall_System_Exception_get_trace (MonoException *ex) } else { gchar *location; gint32 address; + MonoMethod *method = get_method_from_stack_frame (ji, generic_info); address = (char *)ip - (char *)ji->code_start; location = mono_debug_print_stack_frame ( - ji->method, address, ex->object.vtable->domain); + method, address, ex->object.vtable->domain); g_string_append_printf (trace_str, "%s\n", location); g_free (location); @@ -180,15 +550,17 @@ ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info /* Exception is not thrown yet */ return mono_array_new (domain, mono_defaults.stack_frame_class, 0); } - - len = mono_array_length (ta); + + len = mono_array_length (ta) >> 1; res = mono_array_new (domain, mono_defaults.stack_frame_class, len > skip ? len - skip : 0); for (i = skip; i < len; i++) { MonoJitInfo *ji; MonoStackFrame *sf = (MonoStackFrame *)mono_object_new (domain, mono_defaults.stack_frame_class); - gpointer ip = mono_array_get (ta, gpointer, i); + gpointer ip = mono_array_get (ta, gpointer, i * 2 + 0); + gpointer generic_info = mono_array_get (ta, gpointer, i * 2 + 1); + MonoMethod *method; ji = mono_jit_info_table_find (domain, ip); if (ji == NULL) { @@ -199,16 +571,17 @@ ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info g_assert (ji != NULL); + method = get_method_from_stack_frame (ji, generic_info); if (ji->method->wrapper_type) { char *s; sf->method = NULL; - s = mono_method_full_name (ji->method, TRUE); + s = mono_method_full_name (method, TRUE); MONO_OBJECT_SETREF (sf, internal_method_name, mono_string_new (domain, s)); g_free (s); } else - MONO_OBJECT_SETREF (sf, method, mono_method_get_object (domain, ji->method, NULL)); + MONO_OBJECT_SETREF (sf, method, mono_method_get_object (domain, method, NULL)); sf->native_offset = (char *)ip - (char *)ji->code_start; /* @@ -223,7 +596,7 @@ ves_icall_get_trace (MonoException *exc, gint32 skip, MonoBoolean need_file_info sf->il_offset = 0; if (need_file_info) { - if (location) { + if (location && location->source_file) { MONO_OBJECT_SETREF (sf, filename, mono_string_new (domain, location->source_file)); sf->line = location->row; sf->column = location->column; @@ -282,8 +655,6 @@ mono_walk_stack (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoContext *start } } -#ifndef CUSTOM_STACK_WALK - void mono_jit_walk_stack_from_ctx (MonoStackWalk func, MonoContext *start_ctx, gboolean do_il_offset, gpointer user_data) { @@ -338,6 +709,73 @@ mono_jit_walk_stack (MonoStackWalk func, gboolean do_il_offset, gpointer user_da mono_jit_walk_stack_from_ctx (func, NULL, do_il_offset, user_data); } +void +mono_jit_walk_stack_from_ctx_in_thread (MonoJitStackWalk func, MonoDomain *domain, MonoContext *start_ctx, gboolean do_il_offset, MonoInternalThread *thread, MonoLMF *lmf, gpointer user_data) +{ + MonoJitTlsData *jit_tls = thread->jit_data; + gint il_offset; + MonoContext ctx, new_ctx; + StackFrameInfo frame; +#ifndef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT + gint native_offset; + gboolean managed; + MonoJitInfo *ji, rji; +#else + gboolean res; +#endif + + MONO_ARCH_CONTEXT_DEF + + mono_arch_flush_register_windows (); + + if (start_ctx) { + memcpy (&ctx, start_ctx, sizeof (MonoContext)); + } else { +#ifdef MONO_INIT_CONTEXT_FROM_CURRENT + MONO_INIT_CONTEXT_FROM_CURRENT (&ctx); +#else + MONO_INIT_CONTEXT_FROM_FUNC (&ctx, mono_jit_walk_stack_from_ctx); +#endif + g_assert (thread == mono_thread_internal_current ()); + } + + while (MONO_CONTEXT_GET_SP (&ctx) < jit_tls->end_of_stack) { + frame.lmf = lmf; +#ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT + res = mono_find_jit_info_ext (domain, jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, &frame); + if (!res) + return; +#else + ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed); + g_assert (ji); + frame.type = FRAME_TYPE_MANAGED; + frame.ji = ji; + frame.managed = managed; + frame.native_offset = native_offset; + + if (ji == (gpointer)-1) + return; +#endif + + if (do_il_offset && frame.ji) { + MonoDebugSourceLocation *source; + + source = mono_debug_lookup_source_location (frame.ji->method, frame.native_offset, domain); + il_offset = source ? source->il_offset : -1; + mono_debug_free_source_location (source); + } else + il_offset = -1; + + frame.il_offset = il_offset; + + if (func (&frame, &ctx, user_data)) + return; + + ctx = new_ctx; + } +} + + MonoBoolean ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, MonoReflectionMethod **method, @@ -348,9 +786,9 @@ ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); MonoLMF *lmf = mono_get_lmf (); MonoJitInfo *ji, rji; - MonoContext ctx, new_ctx; + MonoContext ctx, new_ctx, ji_ctx; MonoDebugSourceLocation *location; - MonoMethod *last_method = NULL; + MonoMethod *last_method = NULL, *actual_method; MONO_ARCH_CONTEXT_DEF; @@ -363,10 +801,16 @@ ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, #endif do { - ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, native_offset, NULL); - + ji_ctx = ctx; + ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, (int*) native_offset, NULL); ctx = new_ctx; - + + if (ji && ji != (gpointer)-1 && + MONO_CONTEXT_GET_IP (&ctx) >= ji->code_start && + (guint8*)MONO_CONTEXT_GET_IP (&ctx) < (guint8*)ji->code_start + ji->code_size) { + ji_ctx = ctx; + } + if (!ji || ji == (gpointer)-1 || MONO_CONTEXT_GET_SP (&ctx) >= jit_tls->end_of_stack) return FALSE; @@ -394,7 +838,9 @@ ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, } while (skip >= 0); - *method = mono_method_get_object (domain, ji->method, NULL); + actual_method = get_method_from_stack_frame (ji, get_generic_info_from_stack_frame (ji, &ji_ctx)); + + mono_gc_wbarrier_generic_store (method, (MonoObject*) mono_method_get_object (domain, actual_method, NULL)); location = mono_debug_lookup_source_location (ji->method, *native_offset, domain); if (location) @@ -404,7 +850,7 @@ ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, if (need_file_info) { if (location) { - *file = mono_string_new (domain, location->source_file); + mono_gc_wbarrier_generic_store (file, (MonoObject*) mono_string_new (domain, location->source_file)); *line = location->row; *column = location->column; } else { @@ -418,8 +864,6 @@ ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, return TRUE; } -#endif /* CUSTOM_STACK_WALK */ - typedef struct { guint32 skips; MonoSecurityFrame *frame; @@ -594,48 +1038,79 @@ ves_icall_System_Security_SecurityFrame_GetSecurityStack (gint32 skip) return ss.stack; } -#ifndef CUSTOM_EXCEPTION_HANDLING - static MonoClass* get_exception_catch_class (MonoJitExceptionInfo *ei, MonoJitInfo *ji, MonoContext *ctx) { MonoClass *catch_class = ei->data.catch_class; + MonoType *inflated_type; + MonoGenericContext context; - if (ji->has_generic_jit_info) { - MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (ji); - gpointer info; - MonoClass *class; - MonoType *inflated_type; + if (!catch_class) + return NULL; - if (gi->this_in_reg) - info = mono_arch_context_get_int_reg (ctx, gi->this_reg); - else - info = *(gpointer*)((char*)mono_arch_context_get_int_reg (ctx, gi->this_reg) + - gi->this_offset); + if (!ji->has_generic_jit_info || !mono_jit_info_get_generic_jit_info (ji)->has_this) + return catch_class; + context = get_generic_context_from_stack_frame (ji, get_generic_info_from_stack_frame (ji, ctx)); - if (ji->method->flags & METHOD_ATTRIBUTE_STATIC) { - MonoRuntimeGenericContext *rgctx = info; + /* FIXME: we shouldn't inflate but instead put the + type in the rgctx and fetch it from there. It + might be a good idea to do this lazily, i.e. only + when the exception is actually thrown, so as not to + waste space for exception clauses which might never + be encountered. */ + inflated_type = mono_class_inflate_generic_type (&catch_class->byval_arg, &context); + catch_class = mono_class_from_mono_type (inflated_type); + mono_metadata_free_type (inflated_type); - class = rgctx->vtable->klass; - } else { - MonoObject *this = info; + return catch_class; +} - class = this->vtable->klass; +/* + * mini_jit_info_table_find: + * + * Same as mono_jit_info_table_find, but search all the domains of the current thread + * if ADDR is not found in DOMAIN. The domain where the method was found is stored into + * OUT_DOMAIN if it is not NULL. + */ +MonoJitInfo* +mini_jit_info_table_find (MonoDomain *domain, char *addr, MonoDomain **out_domain) +{ + MonoJitInfo *ji; + MonoInternalThread *t = mono_thread_internal_current (); + GSList *l; + + if (out_domain) + *out_domain = NULL; + + ji = mono_jit_info_table_find (domain, addr); + if (ji) { + if (out_domain) + *out_domain = domain; + return ji; + } + + /* maybe it is shared code, so we also search in the root domain */ + if (domain != mono_get_root_domain ()) { + ji = mono_jit_info_table_find (mono_get_root_domain (), addr); + if (ji) { + if (out_domain) + *out_domain = mono_get_root_domain (); + return ji; } + } - /* FIXME: we shouldn't inflate but instead put the - type in the rgctx and fetch it from there. It - might be a good idea to do this lazily, i.e. only - when the exception is actually thrown, so as not to - waste space for exception clauses which might never - be encountered. */ - inflated_type = mono_class_inflate_generic_type (&catch_class->byval_arg, - mini_class_get_context (class)); - catch_class = mono_class_from_mono_type (inflated_type); - g_free (inflated_type); + for (l = t->appdomain_refs; l; l = l->next) { + if (l->data != domain) { + ji = mono_jit_info_table_find ((MonoDomain*)l->data, addr); + if (ji) { + if (out_domain) + *out_domain = (MonoDomain*)l->data; + return ji; + } + } } - return catch_class; + return NULL; } /** @@ -645,9 +1120,10 @@ get_exception_catch_class (MonoJitExceptionInfo *ei, MonoJitInfo *ji, MonoContex * @test_only: only test if the exception is caught, but dont call handlers * @out_filter_idx: out parameter. if test_only is true, set to the index of * the first filter clause which caught the exception. + * @resume: whenever to resume unwinding based on the state in MonoJitTlsData. */ static gboolean -mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer original_ip, gboolean test_only, gint32 *out_filter_idx) +mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer original_ip, gboolean test_only, gboolean resume, gint32 *out_filter_idx, MonoJitInfo **out_ji) { MonoDomain *domain = mono_domain_get (); MonoJitInfo *ji, rji; @@ -671,11 +1147,17 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina obj = (MonoObject *)ex; } + if (!test_only) + mono_debugger_agent_handle_exception (obj, ctx); + /* * Allocate a new exception object instead of the preconstructed ones. */ if (obj == domain->stack_overflow_ex) { - obj = mono_get_exception_stack_overflow (); + /* + * It is not a good idea to try and put even more pressure on the little stack available. + * obj = mono_get_exception_stack_overflow (); + */ stack_overflow = TRUE; } else if (obj == domain->null_reference_ex) { @@ -689,7 +1171,7 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina mono_ex = NULL; } - if (mono_ex && jit_tls->class_cast_to && !strcmp (mono_ex->object.vtable->klass->name, "InvalidCastException")) { + if (mono_ex && jit_tls->class_cast_from && !strcmp (mono_ex->object.vtable->klass->name, "InvalidCastException")) { 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); @@ -700,10 +1182,10 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina } if (!call_filter) - call_filter = mono_arch_get_call_filter (); + call_filter = mono_get_call_filter (); if (!restore_context) - restore_context = mono_arch_get_restore_context (); + restore_context = mono_get_restore_context (); g_assert (jit_tls->end_of_stack); g_assert (jit_tls->abort_func); @@ -711,32 +1193,22 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina if (!test_only) { MonoContext ctx_cp = *ctx; if (mono_trace_is_enabled ()) - g_print ("EXCEPTION handling: %s\n", mono_object_class (obj)->name); + g_print ("[%p:] EXCEPTION handling: %s\n", (void*)GetCurrentThreadId (), mono_object_class (obj)->name); mono_profiler_exception_thrown (obj); - if (!mono_handle_exception_internal (&ctx_cp, obj, original_ip, TRUE, &first_filter_idx)) { + if (!mono_handle_exception_internal (&ctx_cp, obj, original_ip, TRUE, FALSE, &first_filter_idx, out_ji)) { if (mono_break_on_exc) G_BREAKPOINT (); // FIXME: This runs managed code so it might cause another stack overflow when // we are handling a stack overflow + mono_debugger_agent_handle_unhandled_exception (obj, ctx); mono_unhandled_exception (obj); - - if (mono_debugger_unhandled_exception (original_ip, MONO_CONTEXT_GET_SP (ctx), obj)) { - /* - * If this returns true, then we're running inside the - * Mono Debugger and the debugger wants us to restore the - * context and continue (normally, the debugger inserts - * a breakpoint on the `original_ip', so it regains control - * immediately after restoring the context). - */ - MONO_CONTEXT_SET_IP (ctx, original_ip); - restore_context (ctx); - g_assert_not_reached (); - } } } if (out_filter_idx) *out_filter_idx = -1; + if (out_ji) + *out_ji = NULL; filter_idx = 0; initial_ctx = *ctx; memset (&rji, 0, sizeof (rji)); @@ -773,6 +1245,8 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina */ if (!initial_trace_ips && (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)); } } @@ -799,7 +1273,12 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina MonoJitExceptionInfo *ei = &ji->clauses [i]; gboolean filtered = FALSE; -#ifdef __s390__ +#if defined(__s390__) + /* + * This is required in cases where a try block starts immediately after + * a call which causes an exception. Testcase: tests/exception8.cs. + * FIXME: Clean this up. + */ if (ei->try_start < MONO_CONTEXT_GET_IP (ctx) && #else if (ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && @@ -809,16 +1288,27 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina MonoClass *catch_class = get_exception_catch_class (ei, ji, ctx); if ((ei->flags == MONO_EXCEPTION_CLAUSE_NONE) || (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)) { - /* store the exception object in bp + ei->exvar_offset */ - *((gpointer *)(gpointer)((char *)MONO_CONTEXT_GET_BP (ctx) + ei->exvar_offset)) = obj; + if (ji->from_llvm) { +#ifdef MONO_CONTEXT_SET_LLVM_EXC_REG + MONO_CONTEXT_SET_LLVM_EXC_REG (ctx, obj); +#else + g_assert_not_reached (); +#endif + } else { + /* store the exception object in bp + ei->exvar_offset */ + *((gpointer *)(gpointer)((char *)MONO_CONTEXT_GET_BP (ctx) + ei->exvar_offset)) = obj; + } } if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) { - // mono_debugger_handle_exception (ei->data.filter, MONO_CONTEXT_GET_SP (ctx), obj); if (test_only) { + mono_perfcounters->exceptions_filters++; + mono_debugger_call_exception_handler (ei->data.filter, MONO_CONTEXT_GET_SP (ctx), obj); filtered = call_filter (ctx, ei->data.filter); if (filtered && out_filter_idx) *out_filter_idx = filter_idx; + if (out_ji) + *out_ji = ji; } else { /* @@ -847,9 +1337,12 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina if (mono_trace_is_enabled () && mono_trace_eval (ji->method)) g_print ("EXCEPTION: catch found at clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE)); mono_profiler_exception_clause_handler (ji->method, ei->flags, i); - mono_debugger_handle_exception (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj); + mono_debugger_call_exception_handler (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj); MONO_CONTEXT_SET_IP (ctx, ei->handler_start); *(mono_get_lmf_addr ()) = lmf; + mono_perfcounters->exceptions_depth += frame_count; + if (obj == domain->stack_overflow_ex) + jit_tls->handling_stack_ovf = FALSE; return 0; } @@ -859,7 +1352,7 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina if (mono_trace_is_enabled () && mono_trace_eval (ji->method)) g_print ("EXCEPTION: fault clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE)); mono_profiler_exception_clause_handler (ji->method, ei->flags, i); - mono_debugger_handle_exception (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj); + mono_debugger_call_exception_handler (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj); call_filter (ctx, ei->handler_start); } if (!test_only && ei->try_start <= MONO_CONTEXT_GET_IP (ctx) && @@ -868,8 +1361,26 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina if (mono_trace_is_enabled () && mono_trace_eval (ji->method)) g_print ("EXCEPTION: finally clause %d of %s\n", i, mono_method_full_name (ji->method, TRUE)); mono_profiler_exception_clause_handler (ji->method, ei->flags, i); - mono_debugger_handle_exception (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj); - call_filter (ctx, ei->handler_start); + mono_debugger_call_exception_handler (ei->handler_start, MONO_CONTEXT_GET_SP (ctx), obj); + mono_perfcounters->exceptions_finallys++; + *(mono_get_lmf_addr ()) = lmf; + if (ji->from_llvm) { + /* + * LLVM compiled finally handlers follow the design + * of the c++ ehabi, i.e. they call a resume function + * at the end instead of returning to the caller. + * So save the exception handling state, + * mono_resume_unwind () will call us again to continue + * the unwinding. + */ + MONO_CONTEXT_SET_IP (ctx, ei->handler_start); + *(mono_get_lmf_addr ()) = lmf; + jit_tls->ex_ctx = new_ctx; + jit_tls->ex_obj = obj; + return 0; + } else { + call_filter (ctx, ei->handler_start); + } } } @@ -905,6 +1416,72 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina g_assert_not_reached (); } +/* + * mono_debugger_handle_exception: + * + * Notify the debugger about exceptions. Returns TRUE if the debugger wants us to stop + * at the exception and FALSE to resume with the normal exception handling. + * + * The arch code is responsible to setup @ctx in a way that MONO_CONTEXT_GET_IP () and + * MONO_CONTEXT_GET_SP () point to the throw instruction; ie. before executing the + * `callq throw' instruction. + */ +gboolean +mono_debugger_handle_exception (MonoContext *ctx, MonoObject *obj) +{ + MonoDebuggerExceptionAction action; + + if (!mono_debug_using_mono_debugger ()) + return FALSE; + + if (!obj) { + MonoException *ex = mono_get_exception_null_reference (); + MONO_OBJECT_SETREF (ex, message, mono_string_new (mono_domain_get (), "Object reference not set to an instance of an object")); + obj = (MonoObject *)ex; + } + + action = _mono_debugger_throw_exception (MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), obj); + + if (action == MONO_DEBUGGER_EXCEPTION_ACTION_STOP) { + /* + * The debugger wants us to stop on the `throw' instruction. + * By the time we get here, it already inserted a breakpoint there. + */ + return TRUE; + } else if (action == MONO_DEBUGGER_EXCEPTION_ACTION_STOP_UNHANDLED) { + MonoContext ctx_cp = *ctx; + MonoJitInfo *ji = NULL; + gboolean ret; + + /* + * The debugger wants us to stop only if this exception is user-unhandled. + */ + + ret = mono_handle_exception_internal (&ctx_cp, obj, MONO_CONTEXT_GET_IP (ctx), TRUE, FALSE, NULL, &ji); + if (ret && (ji != NULL) && (ji->method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE)) { + /* + * The exception is handled in a runtime-invoke wrapper, that means that it's unhandled + * inside the method being invoked, so we handle it like a user-unhandled exception. + */ + ret = FALSE; + } + + if (!ret) { + /* + * The exception is user-unhandled - tell the debugger to stop. + */ + return _mono_debugger_unhandled_exception (MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), obj); + } + + /* + * The exception is catched somewhere - resume with the normal exception handling and don't + * stop in the debugger. + */ + } + + return FALSE; +} + /** * mono_debugger_run_finally: * @start_ctx: saved processor state @@ -934,7 +1511,7 @@ mono_debugger_run_finally (MonoContext *start_ctx) return; if (!call_filter) - call_filter = mono_arch_get_call_filter (); + call_filter = mono_get_call_filter (); for (i = 0; i < ji->num_clauses; i++) { MonoJitExceptionInfo *ei = &ji->clauses [i]; @@ -956,10 +1533,11 @@ mono_debugger_run_finally (MonoContext *start_ctx) gboolean mono_handle_exception (MonoContext *ctx, gpointer obj, gpointer original_ip, gboolean test_only) { - return mono_handle_exception_internal (ctx, obj, original_ip, test_only, NULL); -} + if (!test_only) + mono_perfcounters->exceptions_thrown++; -#endif /* CUSTOM_EXCEPTION_HANDLING */ + return mono_handle_exception_internal (ctx, obj, original_ip, test_only, FALSE, NULL, NULL); +} #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK @@ -967,6 +1545,8 @@ mono_handle_exception (MonoContext *ctx, gpointer obj, gpointer original_ip, gbo #error "Can't use sigaltstack without sigaction" #endif +#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1)) + void mono_setup_altstack (MonoJitTlsData *tls) { @@ -986,7 +1566,7 @@ mono_setup_altstack (MonoJitTlsData *tls) /*g_print ("thread %p, stack_base: %p, stack_size: %d\n", (gpointer)pthread_self (), staddr, stsize);*/ tls->stack_ovf_guard_base = staddr + mono_pagesize (); - tls->stack_ovf_guard_size = mono_pagesize () * 8; + tls->stack_ovf_guard_size = ALIGN_TO (8 * 4096, mono_pagesize ()); if (mono_mprotect (tls->stack_ovf_guard_base, tls->stack_ovf_guard_size, MONO_MMAP_NONE)) { /* mprotect can fail for the main thread stack */ @@ -1003,7 +1583,7 @@ mono_setup_altstack (MonoJitTlsData *tls) tls->stack_size = stsize + mono_pagesize (); /* Setup an alternate signal stack */ - tls->signal_stack = mono_valloc (0, MONO_ARCH_SIGNAL_STACK_SIZE, MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC|MONO_MMAP_PRIVATE|MONO_MMAP_ANON); + tls->signal_stack = mono_valloc (0, MONO_ARCH_SIGNAL_STACK_SIZE, MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_PRIVATE|MONO_MMAP_ANON); tls->signal_stack_size = MONO_ARCH_SIGNAL_STACK_SIZE; g_assert (tls->signal_stack); @@ -1044,6 +1624,107 @@ mono_free_altstack (MonoJitTlsData *tls) #endif /* MONO_ARCH_SIGSEGV_ON_ALTSTACK */ +static gboolean +try_restore_stack_protection (MonoJitTlsData *jit_tls, int extra_bytes) +{ + gint32 unprotect_size = jit_tls->stack_ovf_guard_size; + /* we need to leave some room for throwing the exception */ + while (unprotect_size >= 0 && (char*)jit_tls->stack_ovf_guard_base + unprotect_size > ((char*)&unprotect_size - extra_bytes)) + unprotect_size -= mono_pagesize (); + /* at this point we could try and build a new domain->stack_overflow_ex, but only if there + * is sufficient stack + */ + //fprintf (stderr, "restoring stack protection: %p-%p (%d)\n", jit_tls->stack_ovf_guard_base, (char*)jit_tls->stack_ovf_guard_base + unprotect_size, unprotect_size); + if (unprotect_size) + mono_mprotect (jit_tls->stack_ovf_guard_base, unprotect_size, MONO_MMAP_NONE); + return unprotect_size == jit_tls->stack_ovf_guard_size; +} + +static void +try_more_restore (void) +{ + MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); + if (try_restore_stack_protection (jit_tls, 500)) + jit_tls->restore_stack_prot = NULL; +} + +static void +restore_stack_protection (void) +{ + MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); + 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 + * code. + */ + if (try_restore_stack_protection (jit_tls, 4096)) + jit_tls->restore_stack_prot = NULL; + else + jit_tls->restore_stack_prot = try_more_restore_tramp; + /* here we also throw a stack overflow exception */ + ex->trace_ips = NULL; + ex->stack_trace = NULL; + mono_raise_exception (ex); +} + +gpointer +mono_altstack_restore_prot (mgreg_t *regs, guint8 *code, gpointer *tramp_data, guint8* tramp) +{ + void (*func)(void) = (gpointer)tramp_data; + func (); + return NULL; +} + +gboolean +mono_handle_soft_stack_ovf (MonoJitTlsData *jit_tls, MonoJitInfo *ji, void *ctx, guint8* fault_addr) +{ + /* we got a stack overflow in the soft-guard pages + * There are two cases: + * 1) managed code caused the overflow: we unprotect the soft-guard page + * and let the arch-specific code trigger the exception handling mechanism + * in the thread stack. The soft-guard pages will be protected again as the stack is unwound. + * 2) unmanaged code caused the overflow: we unprotect the soft-guard page + * and hope we can continue with those enabled, at least until the hard-guard page + * is hit. The alternative to continuing here is to just print a message and abort. + * We may add in the future the code to protect the pages again in the codepath + * when we return from unmanaged to managed code. + */ + if (jit_tls->stack_ovf_guard_size && fault_addr >= (guint8*)jit_tls->stack_ovf_guard_base && + fault_addr < (guint8*)jit_tls->stack_ovf_guard_base + jit_tls->stack_ovf_guard_size) { + /* we unprotect the minimum amount we can */ + guint32 guard_size; + gboolean handled = FALSE; + + guard_size = jit_tls->stack_ovf_guard_size - (mono_pagesize () * SIZEOF_VOID_P / 4); + while (guard_size && fault_addr < (guint8*)jit_tls->stack_ovf_guard_base + guard_size) { + guard_size -= mono_pagesize (); + } + guard_size = jit_tls->stack_ovf_guard_size - guard_size; + /*fprintf (stderr, "unprotecting: %d\n", guard_size);*/ + mono_mprotect ((char*)jit_tls->stack_ovf_guard_base + jit_tls->stack_ovf_guard_size - guard_size, guard_size, MONO_MMAP_READ|MONO_MMAP_WRITE); +#ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK + if (ji) { + mono_arch_handle_altstack_exception (ctx, fault_addr, TRUE); + handled = TRUE; + } +#endif + if (!handled) { + /* We print a message: after this even managed stack overflows + * may crash the runtime + */ + fprintf (stderr, "Stack overflow in unmanaged: IP: %p, fault addr: %p\n", mono_arch_ip_from_context (ctx), fault_addr); + if (!jit_tls->handling_stack_ovf) { + jit_tls->restore_stack_prot = restore_stack_protection_tramp; + jit_tls->handling_stack_ovf = 1; + } else { + /*fprintf (stderr, "Already handling stack overflow\n");*/ + } + } + return TRUE; + } + return FALSE; +} + static gboolean print_stack_frame (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed, gpointer data) { @@ -1059,7 +1740,7 @@ print_stack_frame (MonoMethod *method, gint32 native_offset, gint32 il_offset, g return FALSE; } -static gboolean +static G_GNUC_UNUSED gboolean print_stack_frame_to_string (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed, gpointer data) { @@ -1086,29 +1767,37 @@ static gboolean handling_sigsegv = FALSE; void mono_handle_native_sigsegv (int signal, void *ctx) { -#ifndef PLATFORM_WIN32 +#ifdef MONO_ARCH_USE_SIGACTION struct sigaction sa; #endif + MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); + if (handling_sigsegv) return; + if (mini_get_debug_options ()->suspend_on_sigsegv) { + fprintf (stderr, "Received SIGSEGV, suspending..."); + while (1) + ; + } + /* To prevent infinite loops when the stack walk causes a crash */ handling_sigsegv = TRUE; - fprintf (stderr, "Stacktrace:\n\n"); + /* !jit_tls means the thread was not registered with the runtime */ + if (jit_tls && mono_thread_internal_current ()) { + fprintf (stderr, "Stacktrace:\n\n"); - mono_jit_walk_stack (print_stack_frame, TRUE, stderr); + mono_jit_walk_stack (print_stack_frame, TRUE, stderr); - fflush (stderr); + fflush (stderr); + } #ifdef HAVE_BACKTRACE_SYMBOLS { void *array [256]; char **names; - char cmd [1024]; int i, size; - gchar *out, *err; - gint exit_status; const char *signal_str = (signal == SIGSEGV) ? "SIGSEGV" : "SIGABRT"; fprintf (stderr, "\nNative stacktrace:\n\n"); @@ -1124,15 +1813,51 @@ mono_handle_native_sigsegv (int signal, void *ctx) /* Try to get more meaningful information using gdb */ -#ifndef PLATFORM_WIN32 - sprintf (cmd, "gdb --ex 'attach %ld' --ex 'info threads' --ex 'thread apply all bt' --batch", (long)getpid ()); - { - int res = g_spawn_command_line_sync (cmd, &out, &err, &exit_status, NULL); +#if !defined(HOST_WIN32) && defined(HAVE_SYS_SYSCALL_H) && defined(SYS_fork) + if (!mini_get_debug_options ()->no_gdb_backtrace && !mono_debug_using_mono_debugger ()) { + /* From g_spawn_command_line_sync () in eglib */ + int res; + int stdout_pipe [2] = { -1, -1 }; + pid_t pid; + int status; + char buffer [1024]; + + res = pipe (stdout_pipe); + g_assert (res != -1); + + //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 (pid == 0) { + close (stdout_pipe [0]); + dup2 (stdout_pipe [1], STDOUT_FILENO); + + for (i = getdtablesize () - 1; i >= 3; i--) + close (i); - if (res) { - fprintf (stderr, "\nDebug info from gdb:\n\n"); - fprintf (stderr, "%s\n", out); + if (!mono_gdb_render_native_backtraces ()) + close (STDOUT_FILENO); + + exit (1); } + + close (stdout_pipe [1]); + + fprintf (stderr, "\nDebug info from gdb:\n\n"); + + while (1) { + int nread = read (stdout_pipe [0], buffer, 1024); + + if (nread <= 0) + break; + write (STDERR_FILENO, buffer, nread); + } + + waitpid (pid, &status, WNOHANG); } #endif /* @@ -1152,7 +1877,7 @@ mono_handle_native_sigsegv (int signal, void *ctx) } #endif -#ifndef PLATFORM_WIN32 +#ifdef MONO_ARCH_USE_SIGACTION /* Remove our SIGABRT handler */ sa.sa_handler = SIG_DFL; @@ -1166,20 +1891,15 @@ mono_handle_native_sigsegv (int signal, void *ctx) abort (); } -/* - * mono_print_thread_dump: - * - * Print information about the current thread to stdout. - */ -void -mono_print_thread_dump (void *sigctx) +static void +mono_print_thread_dump_internal (void *sigctx, MonoContext *start_ctx) { - MonoThread *thread = mono_thread_current (); + MonoInternalThread *thread = mono_thread_internal_current (); #if defined(__i386__) || defined(__x86_64__) MonoContext ctx; #endif GString* text = g_string_new (0); - char *name; + char *name, *wapi_desc; GError *error = NULL; if (thread->name) { @@ -1188,21 +1908,72 @@ mono_print_thread_dump (void *sigctx) g_string_append_printf (text, "\n\"%s\"", name); g_free (name); } + else if (thread->threadpool_thread) + g_string_append (text, "\n\"\""); else - g_string_append (text, "\n\"\""); + g_string_append (text, "\n\"\""); - g_string_append_printf (text, " tid=0x%p this=0x%p:\n", (gpointer)(gsize)thread->tid, thread); +#ifndef HOST_WIN32 + wapi_desc = wapi_current_thread_desc (); + g_string_append_printf (text, " tid=0x%p this=0x%p %s\n", (gpointer)(gsize)thread->tid, thread, wapi_desc); + free (wapi_desc); +#endif - /* FIXME: */ -#if defined(__i386__) || defined(__x86_64__) - mono_arch_sigctx_to_monoctx (sigctx, &ctx); +#ifdef MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX + if (start_ctx) { + memcpy (&ctx, start_ctx, sizeof (MonoContext)); + } else if (!sigctx) + MONO_INIT_CONTEXT_FROM_FUNC (&ctx, mono_print_thread_dump); + else + mono_arch_sigctx_to_monoctx (sigctx, &ctx); mono_jit_walk_stack_from_ctx (print_stack_frame_to_string, &ctx, TRUE, text); #else printf ("\t\n"); #endif - fprintf (stdout, text->str); + fprintf (stdout, "%s", text->str); g_string_free (text, TRUE); fflush (stdout); } + +/* + * mono_print_thread_dump: + * + * Print information about the current thread to stdout. + * SIGCTX can be NULL, allowing this to be called from gdb. + */ +void +mono_print_thread_dump (void *sigctx) +{ + mono_print_thread_dump_internal (sigctx, NULL); +} + +void +mono_print_thread_dump_from_ctx (MonoContext *ctx) +{ + mono_print_thread_dump_internal (NULL, ctx); +} + +/* + * mono_resume_unwind: + * + * This is called by code at the end of LLVM compiled finally clauses to continue + * unwinding. + */ +void +mono_resume_unwind (void) +{ + MonoJitTlsData *jit_tls = TlsGetValue (mono_jit_tls_id); + static void (*restore_context) (MonoContext *); + MonoContext ctx; + + memcpy (&ctx, &jit_tls->ex_ctx, sizeof (MonoContext)); + + mono_handle_exception_internal (&ctx, jit_tls->ex_obj, NULL, FALSE, TRUE, NULL, NULL); + + if (!restore_context) + restore_context = mono_get_restore_context (); + + restore_context (&ctx); +}