From 1b7cbbb2c089de150e9427a0d216eddf5ab3bfcd Mon Sep 17 00:00:00 2001 From: Alexander Kyte Date: Mon, 30 Apr 2018 01:16:57 -0400 Subject: [PATCH] [runtime] Write unmanaged exception dumper --- mono/metadata/object-internals.h | 1 + mono/metadata/threads-types.h | 39 ++++ mono/metadata/threads.c | 130 ++++++++++- mono/mini/mini-exceptions.c | 120 +++++++++- mono/mini/mini-posix.c | 14 +- mono/utils/Makefile.am | 2 + mono/utils/mono-state.c | 476 +++++++++++++++++++++++++++++++++++++++ mono/utils/mono-state.h | 33 +++ 8 files changed, 808 insertions(+), 7 deletions(-) create mode 100644 mono/utils/mono-state.c create mode 100644 mono/utils/mono-state.h diff --git a/mono/metadata/object-internals.h b/mono/metadata/object-internals.h index 986c501cbc2..61da5ac2f36 100644 --- a/mono/metadata/object-internals.h +++ b/mono/metadata/object-internals.h @@ -649,6 +649,7 @@ typedef struct { gboolean (*mono_above_abort_threshold) (void); void (*mono_clear_abort_threshold) (void); void (*mono_reraise_exception) (MonoException *ex); + void (*mono_summarize_stack) (MonoDomain *domain, MonoThreadSummary *out, MonoContext *crash_ctx); } MonoRuntimeExceptionHandlingCallbacks; MONO_COLD void mono_set_pending_exception (MonoException *exc); diff --git a/mono/metadata/threads-types.h b/mono/metadata/threads-types.h index e0be3df352f..1bc54808254 100644 --- a/mono/metadata/threads-types.h +++ b/mono/metadata/threads-types.h @@ -338,4 +338,43 @@ mono_threads_exit_gc_safe_region_unbalanced_internal (gpointer cookie, MonoStack void mono_set_thread_dump_dir(gchar* dir); +#ifdef TARGET_OSX +#define MONO_MAX_SUMMARY_NAME_LEN 140 +#define MONO_MAX_SUMMARY_THREADS 32 +#define MONO_MAX_SUMMARY_FRAMES 40 + +typedef struct { + gboolean is_managed; + char str_descr [MONO_MAX_SUMMARY_NAME_LEN]; + struct { + int token; + int il_offset; + int native_offset; + } managed_data; + struct { + intptr_t ip; + gboolean is_trampoline; + gboolean has_name; + } unmanaged_data; +} MonoFrameSummary; + +typedef struct { + gboolean is_managed; + + const char *name; + intptr_t managed_thread_ptr; + intptr_t info_addr; + intptr_t native_thread_id; + + int num_managed_frames; + MonoFrameSummary managed_frames [MONO_MAX_SUMMARY_FRAMES]; + + int num_unmanaged_frames; + MonoFrameSummary unmanaged_frames [MONO_MAX_SUMMARY_FRAMES]; +} MonoThreadSummary; + +gboolean +mono_threads_summarize (MonoContext *ctx, gchar **out); +#endif + #endif /* _MONO_METADATA_THREADS_TYPES_H_ */ diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 970723f9e17..336e2d22f34 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -55,6 +55,7 @@ #include #include #include +#include #ifdef HAVE_SIGNAL_H #include @@ -5884,4 +5885,131 @@ mono_thread_internal_is_current (MonoInternalThread *internal) void mono_set_thread_dump_dir (gchar* dir) { thread_dump_dir = dir; -} \ No newline at end of file +} + +#ifdef TARGET_OSX + +static size_t num_threads_summarized = 0; + +// mono_thread_internal_current doesn't always work in signal +// handler contexts. This does. +static MonoInternalThread * +find_missing_thread (MonoNativeThreadId id) +{ + MonoInternalThread *thread_array [128]; + int nthreads = collect_threads (thread_array, 128); + + for (int i=0; i < nthreads; i++) { + MonoNativeThreadId tid = thread_get_tid (thread_array [i]); + if (tid == id) + return thread_array [i]; + } + return NULL; +} + +static gboolean +mono_threads_summarize_one (MonoThreadSummary *out, MonoContext *ctx) +{ + MonoDomain *domain; + + MonoNativeThreadId current = mono_native_thread_id_get(); + MonoInternalThread *thread = find_missing_thread (current); + + // Not one of ours + if (!thread) + return FALSE; + + memset (out, 0, sizeof (MonoThreadSummary)); + domain = thread->obj.vtable->domain; + out->native_thread_id = (intptr_t) thread_get_tid (thread); + out->managed_thread_ptr = (intptr_t) get_current_thread_ptr_for_domain (domain, thread); + out->info_addr = (intptr_t) thread->thread_info; + if (thread->name) { + char *name = g_utf16_to_utf8 (thread->name, thread->name_len, NULL, NULL, NULL); + out->name = name; + } + mono_get_eh_callbacks ()->mono_summarize_stack (domain, out, ctx); + + // FIXME: handle failure gracefully + // Enable when doing unmanaged + /*g_assert (out->num_frames > 0);*/ + /*if (out->num_frames == 0)*/ + /*return FALSE;*/ + + return TRUE; +} + +static gint32 summary_started; + +gboolean +mono_threads_summarize (MonoContext *ctx, gchar **out) +{ + gboolean already_started = ves_icall_System_Threading_Interlocked_CompareExchange_Int(&summary_started, 0x1, 0x0) != 0x0; + if (!already_started) { + // Setup state + mono_summarize_native_state_begin (); + + MonoNativeThreadId current = mono_native_thread_id_get(); + + if (!current) + g_error ("Can't get native thread ID"); + + // FIXME: The sgen thread never shows up here + MonoInternalThread *thread_array [128]; + int nthreads = collect_threads (thread_array, 128); + + sigset_t sigset, old_sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGTERM); + + + for (int i=0; i < nthreads; i++) { + MonoNativeThreadId tid = thread_get_tid (thread_array [i]); + if (current == tid) + continue; + + // Request every other thread dumps themselves before us + MonoThreadInfo *info = (MonoThreadInfo*) thread_array [i]->thread_info; + + mono_memory_barrier (); + size_t old_num_summarized = num_threads_summarized; + + sigprocmask (SIG_UNBLOCK, &sigset, &old_sigset); + mono_threads_pthread_kill (info, SIGTERM); + + while (old_num_summarized == num_threads_summarized) { + sleep (1); + mono_memory_barrier (); + /*mono_threads_pthread_kill (info, SIGTERM);*/ + + // Pause this handler so other handlers can run + /*int signum;*/ + fprintf (stderr, "Waiting to collect stacktrace\n"); + /*sigsuspend (&sigset);*/ + /*mono_threads_pthread_kill (info, SIGTERM);*/ + /*sigprocmask (SIG_UNBLOCK, &old_sigset, NULL);*/ + /*g_assert (success == 0);*/ + } + } + } + + // Dump ourselves + MonoThreadSummary this_thread; + if (mono_threads_summarize_one (&this_thread, ctx)) + mono_summarize_native_state_add_thread (&this_thread, ctx); + + mono_memory_barrier (); + num_threads_summarized++; + mono_memory_barrier (); + + if (!already_started) { + *out = mono_summarize_native_state_end (); + return TRUE; + } + + while (1) + sleep (10); +} +#endif + + diff --git a/mono/mini/mini-exceptions.c b/mono/mini/mini-exceptions.c index d36f16fe1b1..29a726794ce 100644 --- a/mono/mini/mini-exceptions.c +++ b/mono/mini/mini-exceptions.c @@ -69,6 +69,7 @@ #include #include #include +#include #include "mini.h" #include "trace.h" @@ -92,6 +93,10 @@ #define MONO_ARCH_CONTEXT_DEF #endif +#ifdef TARGET_OSX +#include +#endif + /* * Raw frame information is stored in MonoException.trace_ips as an IntPtr[]. * This structure represents one entry. @@ -121,6 +126,8 @@ static gboolean mono_current_thread_has_handle_block_guard (void); static gboolean mono_install_handler_block_guard (MonoThreadUnwindState *ctx); static void mono_uninstall_current_handler_block_guard (void); +static void mono_summarize_stack (MonoDomain *domain, MonoThreadSummary *out, MonoContext *crash_ctx); + static gboolean first_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer addr) { @@ -225,6 +232,8 @@ mono_exceptions_init (void) cbs.mono_walk_stack_with_ctx = mono_runtime_walk_stack_with_ctx; cbs.mono_walk_stack_with_state = mono_walk_stack_with_state; + cbs.mono_summarize_stack = mono_summarize_stack; + if (mono_llvm_only) { cbs.mono_raise_exception = mono_llvm_raise_exception; cbs.mono_reraise_exception = mono_llvm_reraise_exception; @@ -1240,7 +1249,7 @@ mono_get_portable_ip (intptr_t in_ip, intptr_t *out_ip, char *out_name) // We're only able to get reliable info about pointers in this assembly Dl_info info; int success = dladdr ((void*) mono_get_portable_ip, &info); - intptr_t this_module = (gpointer) info.dli_fbase; + intptr_t this_module = (intptr_t) info.dli_fbase; g_assert (success); success = dladdr ((void*)in_ip, &info); @@ -1262,14 +1271,117 @@ mono_get_portable_ip (intptr_t in_ip, intptr_t *out_ip, char *out_name) } #ifndef MONO_PRIVATE_CRASHES - if (info.dli_saddr && out_name) { - strncpy (out_name, info.dli_sname, MONO_MAX_SUMMARY_NAME_LEN); - } + if (info.dli_saddr && out_name) + copy_summary_string_safe (out_name, info.dli_sname); #endif return TRUE; } #endif +typedef struct { + MonoFrameSummary *frames; + int num_frames; + int max_frames; +} MonoSummarizeUserData; + +static void +copy_summary_string_safe (char *in, char *out) +{ + for (int i=0; i < MONO_MAX_SUMMARY_NAME_LEN; i++) { + in [i] = out [i]; + if (out [i] == '\0') + return; + } + + // Overflowed + in [MONO_MAX_SUMMARY_NAME_LEN] = '\0'; + return; +} + +static gboolean +summarize_frame (StackFrameInfo *frame, MonoContext *ctx, gpointer data) +{ + MonoMethod *method = NULL; + MonoSummarizeUserData *ud = (MonoSummarizeUserData *) data; + g_assert (ud->num_frames + 1 < ud->max_frames); + MonoFrameSummary *dest = &ud->frames [ud->num_frames]; + ud->num_frames++; + + mono_get_portable_ip ((intptr_t) MONO_CONTEXT_GET_IP (ctx), &dest->unmanaged_data.ip, NULL); + dest->unmanaged_data.is_trampoline = frame->ji && frame->ji->is_trampoline; + + if (frame->ji && frame->type != FRAME_TYPE_TRAMPOLINE) + method = jinfo_get_method (frame->ji); + + dest->is_managed = (method != NULL); + if (method && method->wrapper_type != MONO_WRAPPER_NONE) { + dest->is_managed = FALSE; + dest->unmanaged_data.has_name = TRUE; + copy_summary_string_safe (dest->str_descr, mono_method_full_name (method, TRUE)); + } + + MonoDebugSourceLocation *location = NULL; + + if (dest->is_managed) { + dest->managed_data.native_offset = frame->native_offset; + copy_summary_string_safe (dest->str_descr, mono_class_get_image(method->klass)->assembly_name); + dest->managed_data.token = method->token; + location = mono_debug_lookup_source_location (method, frame->native_offset, mono_domain_get ()); + } else { + dest->managed_data.token = -1; + } + + if (location) { + dest->managed_data.il_offset = location->il_offset; + + mono_debug_free_source_location (location); + } + + return FALSE; +} + +static void +mono_summarize_stack (MonoDomain *domain, MonoThreadSummary *out, MonoContext *crash_ctx) +{ + intptr_t frame_ips [MONO_MAX_SUMMARY_FRAMES]; + + MonoSummarizeUserData data; + data.max_frames = MONO_MAX_SUMMARY_FRAMES; + data.num_frames = 0; + data.frames = out->managed_frames; + + // FIXME: collect stack pointer for both and sort frames by SP + // so people can see relative ordering of both managed and unmanaged frames. + + // + // Summarize managed stack + // + mono_walk_stack_with_ctx (summarize_frame, crash_ctx, MONO_UNWIND_LOOKUP_IL_OFFSET, &data); + out->num_managed_frames = data.num_frames; + + + // + // Summarize unmanaged stack + // + +#ifdef HAVE_BACKTRACE_SYMBOLS + out->num_unmanaged_frames = backtrace ((void **)frame_ips, MONO_MAX_SUMMARY_FRAMES); + + for (int i =0; i < out->num_unmanaged_frames; ++i) { + intptr_t ip = frame_ips [i]; + + int success = mono_get_portable_ip (ip, &out->unmanaged_frames [i].unmanaged_data.ip, (char *) &out->unmanaged_frames [i].str_descr); + if (!success) + continue; + + if (out->unmanaged_frames [i].str_descr [0] != '\0') + out->unmanaged_frames [i].unmanaged_data.has_name = TRUE; + } +#endif + + return; +} + MonoBoolean ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, MonoReflectionMethod **method, diff --git a/mono/mini/mini-posix.c b/mono/mini/mini-posix.c index 33b1edb9a41..693cae121c4 100644 --- a/mono/mini/mini-posix.c +++ b/mono/mini/mini-posix.c @@ -215,8 +215,18 @@ MONO_SIG_HANDLER_FUNC (static, sigterm_signal_handler) MONO_SIG_HANDLER_INFO_TYPE *info = MONO_SIG_HANDLER_GET_INFO (); MONO_SIG_HANDLER_GET_CONTEXT; - if (mono_merp_enabled ()) - mono_handle_native_crash ("SIGTERM", ctx, info); + // Note: this function only returns for a single thread + // When it's invoked on other threads once the dump begins, + // those threads perform their dumps and then sleep until we + // die. The dump ends with the exit(1) below + MonoContext mctx; + gchar *output = NULL; + mono_sigctx_to_monoctx (ctx, &mctx); + if (!mono_threads_summarize (&mctx, &output)) + g_assert_not_reached (); + + // Only the dumping-supervisor thread exits mono_thread_summarize + fprintf (stderr, "Unhandled exception dump: \n######\n%s\n######\n", output); mono_chain_signal (MONO_SIG_HANDLER_PARAMS); exit (1); diff --git a/mono/utils/Makefile.am b/mono/utils/Makefile.am index ca7c3ca5f05..1bece61040c 100644 --- a/mono/utils/Makefile.am +++ b/mono/utils/Makefile.am @@ -52,6 +52,8 @@ monoutils_sources = \ mono-log-darwin.c \ mono-merp.c \ mono-merp.h \ + mono-state.h \ + mono-state.c \ mono-internal-hash.c \ mono-internal-hash.h \ mono-io-portability.c \ diff --git a/mono/utils/mono-state.c b/mono/utils/mono-state.c new file mode 100644 index 00000000000..dfa2e089bf3 --- /dev/null +++ b/mono/utils/mono-state.c @@ -0,0 +1,476 @@ +/** + * \file + * Support for verbose unmanaged crash dumps + * + * Author: + * Alexander Kyte (alkyte@microsoft.com) + * + * (C) 2018 Microsoft, Inc. + * + */ +#include +#include +#include +#include +#include + +extern GCStats mono_gc_stats; + +// For AOT mode +#include + +#ifdef TARGET_OSX +#include +#include +#endif + +#define MONO_MAX_SUMMARY_LEN 900 +static JsonWriter writer; +static GString static_gstr; +static char output_dump_str [MONO_MAX_SUMMARY_LEN]; + +static void mono_json_writer_init_static (void) { + static_gstr.len = 0; + static_gstr.allocated_len = MONO_MAX_SUMMARY_LEN; + static_gstr.str = output_dump_str; + memset (output_dump_str, 0, sizeof (output_dump_str)); + + writer.indent = 0; + writer.text = &static_gstr; +} + +static void +mono_native_state_add_ctx (JsonWriter *writer, MonoContext *ctx) +{ + // Context + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "ctx"); + mono_json_writer_object_begin(writer); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "IP"); + mono_json_writer_printf (writer, "\"%p\",\n", (gpointer) MONO_CONTEXT_GET_IP (ctx)); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "SP"); + mono_json_writer_printf (writer, "\"%p\",\n", (gpointer) MONO_CONTEXT_GET_SP (ctx)); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "BP"); + mono_json_writer_printf (writer, "\"%p\"\n", (gpointer) MONO_CONTEXT_GET_BP (ctx)); + + mono_json_writer_indent_pop (writer); + mono_json_writer_indent (writer); + mono_json_writer_object_end (writer); + mono_json_writer_printf (writer, ",\n"); +} + +static void +mono_native_state_add_frame (JsonWriter *writer, MonoFrameSummary *frame) +{ + mono_json_writer_indent (writer); + mono_json_writer_object_begin(writer); + + if (frame->is_managed) { + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "is_managed"); + mono_json_writer_printf (writer, "\"%s\",\n", frame->is_managed ? "true" : "false"); + } + + if (frame->unmanaged_data.is_trampoline) { + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "is_trampoline"); + mono_json_writer_printf (writer, "\"true\","); + } + + if (frame->is_managed) { + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "assembly"); + mono_json_writer_printf (writer, "\"%s\",\n", frame->str_descr); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "token"); + mono_json_writer_printf (writer, "\"0x%05x\",\n", frame->managed_data.token); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "native_offset"); + mono_json_writer_printf (writer, "\"0x%x\",\n", frame->managed_data.native_offset); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "il_offset"); + mono_json_writer_printf (writer, "\"0x%05x\"\n", frame->managed_data.il_offset); + + } else { + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "native_address"); + if (frame->unmanaged_data.ip) + mono_json_writer_printf (writer, "\"%p\"", (void *) frame->unmanaged_data.ip); + else + mono_json_writer_printf (writer, "\"outside mono-sgen\""); + + if (frame->unmanaged_data.has_name) { + mono_json_writer_printf (writer, ",\n"); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "unmanaged_name"); + mono_json_writer_printf (writer, "\"%s\"\n", frame->str_descr); + } else { + mono_json_writer_printf (writer, "\n"); + } + } + + mono_json_writer_indent_pop (writer); + mono_json_writer_indent (writer); + mono_json_writer_object_end (writer); +} + +static void +mono_native_state_add_frames (JsonWriter *writer, int num_frames, MonoFrameSummary *frames, const char *label) +{ + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, label); + + mono_json_writer_array_begin (writer); + + mono_native_state_add_frame (writer, &frames [0]); + for (int i = 1; i < num_frames; ++i) { + mono_json_writer_printf (writer, ",\n"); + mono_native_state_add_frame (writer, &frames [i]); + } + mono_json_writer_printf (writer, "\n"); + + mono_json_writer_indent_pop (writer); + mono_json_writer_indent (writer); + mono_json_writer_array_end (writer); +} + + +static void +mono_native_state_add_thread (JsonWriter *writer, MonoThreadSummary *thread, MonoContext *ctx) +{ + static gboolean not_first_thread; + + if (not_first_thread) { + mono_json_writer_printf (writer, ",\n"); + } else { + not_first_thread = TRUE; + } + + mono_json_writer_indent (writer); + mono_json_writer_object_begin(writer); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "is_managed"); + mono_json_writer_printf (writer, "%s,\n", thread->is_managed ? "true" : "false"); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "managed_thread_ptr"); + mono_json_writer_printf (writer, "\"0x%x\",\n", (gpointer) thread->managed_thread_ptr); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "thread_info_addr"); + mono_json_writer_printf (writer, "\"0x%x\",\n", (gpointer) thread->info_addr); + + if (thread->name) { + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "thread_name"); + mono_json_writer_printf (writer, "\"%s\",\n", thread->name); + } + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "native_thread_id"); + mono_json_writer_printf (writer, "\"0x%x\",\n", (gpointer) thread->native_thread_id); + + mono_native_state_add_ctx (writer, ctx); + + if (thread->num_managed_frames > 0) { + mono_native_state_add_frames (writer, thread->num_managed_frames, thread->managed_frames, "managed_frames"); + } + if (thread->num_unmanaged_frames > 0) { + if (thread->num_managed_frames > 0) + mono_json_writer_printf (writer, ",\n"); + mono_native_state_add_frames (writer, thread->num_unmanaged_frames, thread->unmanaged_frames, "unmanaged_frames"); + } + mono_json_writer_printf (writer, "\n"); + + mono_json_writer_indent (writer); + mono_json_writer_object_end (writer); +} + +static void +mono_native_state_add_ee_info (JsonWriter *writer) +{ + // FIXME: setup callbacks to enable + /*const char *aot_mode;*/ + /*MonoAotMode mono_aot_mode = mono_jit_get_aot_mode ();*/ + /*switch (mono_aot_mode) {*/ + /*case MONO_AOT_MODE_NONE:*/ + /*aot_mode = "none";*/ + /*break;*/ + /*case MONO_AOT_MODE_NORMAL:*/ + /*aot_mode = "normal";*/ + /*break;*/ + /*case MONO_AOT_MODE_HYBRID:*/ + /*aot_mode = "hybrid";*/ + /*break;*/ + /*case MONO_AOT_MODE_FULL:*/ + /*aot_mode = "full";*/ + /*break;*/ + /*case MONO_AOT_MODE_LLVMONLY:*/ + /*aot_mode = "llvmonly";*/ + /*break;*/ + /*case MONO_AOT_MODE_INTERP:*/ + /*aot_mode = "interp";*/ + /*break;*/ + /*case MONO_AOT_MODE_INTERP_LLVMONLY:*/ + /*aot_mode = "interp_llvmonly";*/ + /*break;*/ + /*default:*/ + /*aot_mode = "error";*/ + /*}*/ + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "execution_context"); + mono_json_writer_object_begin(writer); + + /*mono_json_writer_indent (writer);*/ + /*mono_json_writer_object_key(writer, "aot_mode");*/ + /*mono_json_writer_printf (writer, "\"%s\",\n", aot_mode);*/ + + /*mono_json_writer_indent (writer);*/ + /*mono_json_writer_object_key(writer, "mono_use_llvm");*/ + /*mono_json_writer_printf (writer, "\"%s\",\n", mono_use_llvm ? "true" : "false");*/ + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "coop-enabled"); + mono_json_writer_printf (writer, "\"%s\"\n", mono_threads_is_cooperative_suspension_enabled () ? "true" : "false"); + + mono_json_writer_indent_pop (writer); + mono_json_writer_indent (writer); + mono_json_writer_object_end (writer); + mono_json_writer_printf (writer, ",\n"); +} + +// Taken from driver.c +#if defined(MONO_ARCH_ARCHITECTURE) +/* Redefine MONO_ARCHITECTURE to include more information */ +#undef MONO_ARCHITECTURE +#define MONO_ARCHITECTURE MONO_ARCH_ARCHITECTURE +#endif + +static void +mono_native_state_add_version (JsonWriter *writer) +{ + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "configuration"); + mono_json_writer_object_begin(writer); + + char *build = mono_get_runtime_callbacks ()->get_runtime_build_info (); + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "version"); + mono_json_writer_printf (writer, "\"%s\",\n", build); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "tlc"); +#ifdef HAVE_KW_THREAD + mono_json_writer_printf (writer, "\"__thread\",\n"); +#else + mono_json_writer_printf (writer, "\"normal\",\n"); +#endif /* HAVE_KW_THREAD */ + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "sigsgev"); +#ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK + mono_json_writer_printf (writer, "\"altstack\",\n"); +#else + mono_json_writer_printf (writer, "\"normal\",\n"); +#endif + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "notifications"); +#ifdef HAVE_EPOLL + mono_json_writer_printf (writer, "\"epoll\",\n"); +#elif defined(HAVE_KQUEUE) + mono_json_writer_printf (writer, "\"kqueue\",\n"); +#else + mono_json_writer_printf (writer, "\"thread+polling\",\n"); +#endif + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "architecture"); + mono_json_writer_printf (writer, "\"%s\",\n", MONO_ARCHITECTURE); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "disabled_features"); + mono_json_writer_printf (writer, "\"%s\",\n", DISABLED_FEATURES); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "smallconfig"); +#ifdef MONO_SMALL_CONFIG + mono_json_writer_printf (writer, "\"enabled\",\n"); +#else + mono_json_writer_printf (writer, "\"disabled\",\n"); +#endif + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "bigarrays"); +#ifdef MONO_BIG_ARRAYS + mono_json_writer_printf (writer, "\"enabled\",\n"); +#else + mono_json_writer_printf (writer, "\"disabled\",\n"); +#endif + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "softdebug"); +#if !defined(DISABLE_SDB) + mono_json_writer_printf (writer, "\"enabled\",\n"); +#else + mono_json_writer_printf (writer, "\"disabled\",\n"); +#endif + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "interpreter"); +#ifndef DISABLE_INTERPRETER + mono_json_writer_printf (writer, "\"enabled\",\n"); +#else + mono_json_writer_printf (writer, "\"disabled\",\n"); +#endif + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "llvm_support"); +#ifdef MONO_ARCH_LLVM_SUPPORTED +#ifdef ENABLE_LLVM + mono_json_writer_printf (writer, "\"%s\"\n", LLVM_VERSION); +#else + mono_json_writer_printf (writer, "\"disabled\"\n"); +#endif +#endif + + mono_json_writer_indent_pop (writer); + mono_json_writer_indent (writer); + mono_json_writer_object_end (writer); + mono_json_writer_printf (writer, ",\n"); +} + +static void +mono_native_state_add_memory (JsonWriter *writer) +{ + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "memory"); + mono_json_writer_object_begin(writer); + +#ifdef TARGET_OSX + struct task_basic_info t_info; + memset (&t_info, 0, sizeof (t_info)); + mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; + task_name_t task = mach_task_self (); + task_info(task, TASK_BASIC_INFO, (task_info_t) &t_info, &t_info_count); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "Resident Size"); + mono_json_writer_printf (writer, "\"%lu\",\n", t_info.resident_size); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "Virtual Size"); + mono_json_writer_printf (writer, "\"%lu\",\n", t_info.virtual_size); +#endif + + GCStats stats; + memcpy (&stats, &mono_gc_stats, sizeof (GCStats)); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "minor_gc_time"); + mono_json_writer_printf (writer, "\"%lu\",\n", stats.minor_gc_time); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "major_gc_time"); + mono_json_writer_printf (writer, "\"%lu\",\n", stats.major_gc_time); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "minor_gc_count"); + mono_json_writer_printf (writer, "\"%lu\",\n", stats.minor_gc_count); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "major_gc_count"); + mono_json_writer_printf (writer, "\"%lu\",\n", stats.major_gc_count); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "major_gc_time_concurrent"); + mono_json_writer_printf (writer, "\"%lu\"\n", stats.major_gc_time_concurrent); + + mono_json_writer_indent_pop (writer); + mono_json_writer_indent (writer); + mono_json_writer_object_end (writer); + mono_json_writer_printf (writer, ",\n"); +} + +static void +mono_native_state_add_prologue (JsonWriter *writer) +{ + mono_json_writer_init (writer); + mono_json_writer_object_begin(writer); + + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "protocol_version"); + mono_json_writer_printf (writer, "\"%s\",\n", MONO_NATIVE_STATE_PROTOCOL_VERSION); + + mono_native_state_add_version (writer); + +#ifndef MONO_PRIVATE_CRASHES + mono_native_state_add_ee_info (writer); +#endif + + mono_native_state_add_memory (writer); + + const char *assertion_msg = g_get_assertion_message (); + if (assertion_msg != NULL) { + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "assertion_message"); + + char *pos; + if ((pos = strchr (assertion_msg, '\n')) != NULL) + *pos = '\0'; + + mono_json_writer_printf (writer, "\"%s\",\n", assertion_msg); + } + + // Start threads array + mono_json_writer_indent (writer); + mono_json_writer_object_key(writer, "threads"); + mono_json_writer_array_begin (writer); +} + +static void +mono_native_state_add_epilogue (JsonWriter *writer) +{ + mono_json_writer_indent_pop (writer); + mono_json_writer_printf (writer, "\n"); + mono_json_writer_indent (writer); + mono_json_writer_array_end (writer); + + mono_json_writer_indent_pop (writer); + mono_json_writer_indent (writer); + mono_json_writer_object_end (writer); + mono_json_writer_printf (writer, "\n"); +} + +void +mono_summarize_native_state_begin (void) +{ + mono_json_writer_init_static (); + mono_native_state_add_prologue (&writer); +} + +char * +mono_summarize_native_state_end (void) +{ + mono_native_state_add_epilogue (&writer); + return writer.text->str; +} + +void +mono_summarize_native_state_add_thread (MonoThreadSummary *thread, MonoContext *ctx) +{ + mono_native_state_add_thread (&writer, thread, ctx); +} + diff --git a/mono/utils/mono-state.h b/mono/utils/mono-state.h new file mode 100644 index 00000000000..e973fc57354 --- /dev/null +++ b/mono/utils/mono-state.h @@ -0,0 +1,33 @@ +/** + * \file + * Support for cooperative creation of unmanaged state dumps + * + * Author: + * Alexander Kyte (alkyte@microsoft.com) + * + * (C) 2018 Microsoft, Inc. + * + */ +#ifndef __MONO_UTILS_NATIVE_STATE__ +#define __MONO_UTILS_NATIVE_STATE__ + +#include +#include +#include + +#define MONO_NATIVE_STATE_PROTOCOL_VERSION "0.0.1" + +MONO_BEGIN_DECLS + +void +mono_summarize_native_state_begin (void); + +char * +mono_summarize_native_state_end (void); + +void +mono_summarize_native_state_add_thread (MonoThreadSummary *thread, MonoContext *ctx); + +MONO_END_DECLS + +#endif // MONO_UTILS_NATIVE_STATE -- 2.11.4.GIT