[2019-12] [merp] Capture Environment.FailFast message in crash report (#18921)
[mono-project.git] / mono / utils / mono-state.c
blob1ed7e5c6be512c1b57b0ff7b8e3033001a2e27ae
1 /**
2 * \file
3 * Support for verbose unmanaged crash dumps
5 * Author:
6 * Alexander Kyte (alkyte@microsoft.com)
8 * (C) 2018 Microsoft, Inc.
11 #include <config.h>
12 #include <glib.h>
13 #include <mono/utils/mono-state.h>
14 #include <mono/utils/atomic.h>
16 #ifndef DISABLE_CRASH_REPORTING
18 #include <mono/utils/mono-threads-coop.h>
19 #include <mono/metadata/object-internals.h>
20 #include <mono/metadata/mono-config-dirs.h>
22 #include <sys/param.h>
23 #include <fcntl.h>
24 #ifdef HAVE_SYS_STAT_H
25 #include <sys/stat.h>
26 #endif
27 #include <utils/mono-threads-debug.h>
29 extern GCStats mono_gc_stats;
31 // For AOT mode
32 #include <mono/mini/mini-runtime.h>
33 #include <mono/utils/mono-threads-debug.h>
34 #include <mono/utils/mono-merp.h>
36 #ifdef TARGET_OSX
37 #include <mach/mach.h>
38 #include <mach/task_info.h>
39 #endif
41 #ifdef HAVE_SYS_SYSCTL_H
42 #include <sys/sysctl.h>
43 #endif
45 #ifdef HAVE_SYS_MMAN_H
46 #include <sys/mman.h>
47 #endif
49 #ifdef TARGET_OSX
50 // OSX 10.9 does not have MAP_ANONYMOUS
51 #if !defined(MAP_ANONYMOUS)
52 #define NO_MAP_ANONYMOUS
53 #if defined(MAP_ANON)
54 #define MAP_ANONYMOUS MAP_ANON
55 #else
56 #define MAP_ANONYMOUS 0
57 #endif
58 #endif
59 #endif
61 #ifdef HAVE_EXECINFO_H
62 #include <execinfo.h>
63 #endif
65 #if defined(ENABLE_CHECKED_BUILD_CRASH_REPORTING) && defined (ENABLE_OVERRIDABLE_ALLOCATORS)
66 // Fixme: put behind preprocessor symbol?
67 static void
68 assert_not_reached_mem (const char *msg)
70 g_async_safe_printf ("%s\n", msg);
72 #if 0
73 pid_t crashed_pid = getpid ();
74 // Break here
75 g_async_safe_printf ("Attach to PID %d. Supervisor thread will signal us shortly.\n", crashed_pid);
76 while (TRUE) {
77 // Sleep for 1 second.
78 g_usleep (1000 * 1000);
80 #endif
82 g_error (msg);
85 static void
86 assert_not_reached_fn_ptr_free (gpointer ptr)
88 // Wrap the macro to provide as a function pointer
89 assert_not_reached_mem ("Attempted to call free during merp dump");
92 static gpointer
93 assert_not_reached_fn_ptr_malloc (gsize size)
95 // Wrap the macro to provide as a function pointer
96 assert_not_reached_mem ("Attempted to call malloc during merp dump");
97 return NULL;
100 static gpointer
101 assert_not_reached_fn_ptr_realloc (gpointer obj, gsize size)
103 // Wrap the macro to provide as a function pointer
104 assert_not_reached_mem ("Attempted to call realloc during merp dump");
105 return NULL;
108 static gpointer
109 assert_not_reached_fn_ptr_calloc (gsize n, gsize x)
111 // Wrap the macro to provide as a function pointer
112 assert_not_reached_mem ("Attempted to call calloc during merp dump");
113 return NULL;
115 #endif /* defined(ENABLE_CHECKED_BUILD_CRASH_REPORTING) && defined (ENABLE_OVERRIDABLE_ALLOCATORS) */
117 void
118 mono_summarize_toggle_assertions (gboolean enable)
120 #if defined(ENABLE_CHECKED_BUILD_CRASH_REPORTING) && defined (ENABLE_OVERRIDABLE_ALLOCATORS)
121 static GMemVTable g_mem_vtable_backup;
122 static gboolean saved;
124 if (enable) {
125 g_mem_get_vtable (&g_mem_vtable_backup);
126 saved = TRUE;
128 GMemVTable g_mem_vtable_assert = { assert_not_reached_fn_ptr_malloc, assert_not_reached_fn_ptr_realloc, assert_not_reached_fn_ptr_free, assert_not_reached_fn_ptr_calloc };
129 g_mem_set_vtable (&g_mem_vtable_assert);
130 } else if (saved) {
131 g_mem_set_vtable (&g_mem_vtable_backup);
132 saved = FALSE;
135 mono_memory_barrier ();
136 #endif
139 typedef struct {
140 const char *directory;
141 MonoSummaryStage level;
142 } MonoSummaryTimeline;
144 static const char *configured_timeline_dir;
145 static MonoSummaryTimeline log;
147 static void
148 file_for_summary_stage (const char *directory, MonoSummaryStage stage, gchar *buff, size_t sizeof_buff)
150 g_snprintf (buff, sizeof_buff, "%s%scrash_stage_%d", directory, G_DIR_SEPARATOR_S, stage);
153 static void
154 create_stage_mark_file (void)
156 char out_file [200];
157 file_for_summary_stage (log.directory, log.level, out_file, sizeof(out_file));
158 int handle = g_open (out_file, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
159 close(handle);
162 gboolean
163 mono_summarize_set_timeline_dir (const char *directory)
165 if (directory) {
166 configured_timeline_dir = strdup (directory);
167 return g_ensure_directory_exists (directory);
168 } else {
169 configured_timeline_dir = NULL;
170 return TRUE;
174 void
175 mono_summarize_timeline_start (void)
177 memset (&log, 0, sizeof (log));
179 if (!configured_timeline_dir)
180 return;
182 log.directory = configured_timeline_dir;
183 mono_summarize_timeline_phase_log (MonoSummarySetup);
186 void
187 mono_summarize_double_fault_log (void)
189 mono_summarize_timeline_phase_log (MonoSummaryDoubleFault);
192 void
193 mono_summarize_timeline_phase_log (MonoSummaryStage next)
195 if (!log.directory)
196 return;
198 MonoSummaryStage out_level;
199 switch (log.level) {
200 case MonoSummaryNone:
201 out_level = MonoSummarySetup;
202 break;
203 case MonoSummarySetup:
204 out_level = MonoSummarySuspendHandshake;
205 break;
206 case MonoSummarySuspendHandshake:
207 out_level = MonoSummaryUnmanagedStacks;
208 break;
209 case MonoSummaryUnmanagedStacks:
210 out_level = MonoSummaryManagedStacks;
211 break;
212 case MonoSummaryManagedStacks:
213 out_level = MonoSummaryStateWriter;
214 break;
215 case MonoSummaryStateWriter:
216 out_level = MonoSummaryStateWriterDone;
217 break;
218 case MonoSummaryStateWriterDone:
219 #ifdef TARGET_OSX
220 if (mono_merp_enabled ()) {
221 out_level = MonoSummaryMerpWriter;
222 } else
223 #endif
225 out_level = MonoSummaryCleanup;
227 break;
228 case MonoSummaryMerpWriter:
229 out_level = MonoSummaryMerpInvoke;
230 break;
231 case MonoSummaryMerpInvoke:
232 out_level = MonoSummaryCleanup;
233 break;
234 case MonoSummaryCleanup:
235 out_level = MonoSummaryDone;
236 break;
238 case MonoSummaryDone:
239 g_async_safe_printf ("Trying to log crash reporter timeline, already at done %d\n", log.level);
240 return;
241 default:
242 g_async_safe_printf ("Trying to log crash reporter timeline, illegal state %d\n", log.level);
243 return;
246 g_assertf(out_level == next || next == MonoSummaryDoubleFault, "Log Error: Log transition to %d, actual expected next step is %d\n", next, out_level);
248 log.level = out_level;
249 create_stage_mark_file ();
250 // To check, comment out normally
251 // DO NOT MERGE UNCOMMENTED
252 // As this does a lot of FILE io
254 // g_assert (out_level == mono_summarize_timeline_read_level (log.directory, FALSE));
256 if (out_level == MonoSummaryDone)
257 memset (&log, 0, sizeof (log));
259 return;
262 static void
263 mem_file_name (long tag, char *name, size_t limit)
265 name [0] = '\0';
266 pid_t pid = getpid ();
267 g_snprintf (name, limit, "mono_crash.mem.%d.%lx.blob", pid, tag);
270 gboolean
271 mono_state_alloc_mem (MonoStateMem *mem, long tag, size_t size)
273 char name [100];
274 mem_file_name (tag, name, sizeof (name));
276 memset (mem, 0, sizeof (*mem));
277 mem->tag = tag;
278 mem->size = size;
279 mem->handle = 0;
281 if (!g_hasenv ("MONO_CRASH_NOFILE"))
282 mem->handle = g_open (name, O_RDWR | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
284 if (mem->handle < 1) {
285 mem->mem = (gpointer *) mmap (0, mem->size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
286 } else {
287 lseek (mem->handle, mem->size, SEEK_SET);
288 g_write (mem->handle, "", 1);
290 mem->mem = (gpointer *) mmap (0, mem->size, PROT_READ | PROT_WRITE, MAP_SHARED, mem->handle, 0);
292 if (mem->mem == GINT_TO_POINTER (-1))
293 return FALSE;
295 return TRUE;
298 void
299 mono_state_free_mem (MonoStateMem *mem)
301 if (!mem->mem)
302 return;
304 msync(mem->mem, mem->size, MS_SYNC);
305 munmap (mem->mem, mem->size);
307 // Note: We aren't calling msync on this file.
308 // There is no guarantee that we're going to persist
309 // changes to it at all, in the case that we fail before
310 // removing it. Don't try to debug where in the crash we were
311 // by the file contents.
312 if (mem->handle)
313 close (mem->handle);
314 else
315 g_async_safe_printf ("NULL handle mono-state mem on freeing\n");
317 char name [100];
318 mem_file_name (mem->tag, name, sizeof (name));
319 unlink (name);
322 static gboolean
323 timeline_has_level (const char *directory, char *log_file, size_t log_file_size, gboolean clear, MonoSummaryStage stage)
325 memset (log_file, 0, log_file_size);
326 file_for_summary_stage (directory, stage, log_file, log_file_size);
327 gboolean exists = g_file_test (log_file, G_FILE_TEST_EXISTS);
328 if (clear && exists)
329 remove (log_file);
331 return exists;
334 MonoSummaryStage
335 mono_summarize_timeline_read_level (const char *directory, gboolean clear)
337 char out_file [200];
339 if (!directory)
340 directory = log.directory;
342 if (!directory)
343 return MonoSummaryNone;
345 // Make sure that clear gets to erase all of these files if they exist
346 gboolean has_level_done = timeline_has_level (directory, out_file, sizeof(out_file), clear, MonoSummaryDone);
347 gboolean has_level_cleanup = timeline_has_level (directory, out_file, sizeof(out_file), clear, MonoSummaryCleanup);
348 gboolean has_level_merp_invoke = timeline_has_level (directory, out_file, sizeof(out_file), clear, MonoSummaryMerpInvoke);
349 gboolean has_level_merp_writer = timeline_has_level (directory, out_file, sizeof(out_file), clear, MonoSummaryMerpWriter);
350 gboolean has_level_state_writer = timeline_has_level (directory, out_file, sizeof(out_file), clear, MonoSummaryStateWriter);
351 gboolean has_level_state_writer_done = timeline_has_level (directory, out_file, sizeof(out_file), clear, MonoSummaryStateWriterDone);
352 gboolean has_level_managed_stacks = timeline_has_level (directory, out_file, sizeof(out_file), clear, MonoSummaryManagedStacks);
353 gboolean has_level_unmanaged_stacks = timeline_has_level (directory, out_file, sizeof(out_file), clear, MonoSummaryUnmanagedStacks);
354 gboolean has_level_suspend_handshake = timeline_has_level (directory, out_file, sizeof(out_file), clear, MonoSummarySuspendHandshake);
355 gboolean has_level_setup = timeline_has_level (directory, out_file, sizeof(out_file), clear, MonoSummarySetup);
357 if (has_level_done)
358 return MonoSummaryDone;
359 else if (has_level_cleanup)
360 return MonoSummaryCleanup;
361 else if (has_level_merp_invoke)
362 return MonoSummaryMerpInvoke;
363 else if (has_level_merp_writer)
364 return MonoSummaryMerpWriter;
365 else if (has_level_state_writer_done)
366 return MonoSummaryStateWriterDone;
367 else if (has_level_state_writer)
368 return MonoSummaryStateWriter;
369 else if (has_level_managed_stacks)
370 return MonoSummaryManagedStacks;
371 else if (has_level_unmanaged_stacks)
372 return MonoSummaryUnmanagedStacks;
373 else if (has_level_suspend_handshake)
374 return MonoSummarySuspendHandshake;
375 else if (has_level_setup)
376 return MonoSummarySetup;
377 else
378 return MonoSummaryNone;
381 static void
382 assert_has_space (MonoStateWriter *writer)
384 // Each individual key/value append should be roughly less than this many characters
385 const int margin = 35;
387 // Not using static, exit
388 if (writer->allocated_len == 0)
389 return;
391 g_assertf (writer->allocated_len - writer->len >= margin, "Ran out of memory to create crash dump json blob. Current state:\n%s\n", writer->output_str);
394 static void
395 mono_state_writer_printf (MonoStateWriter *writer, const gchar *format, ...)
397 g_assert (writer->len == strlen(writer->output_str));
399 va_list args;
400 va_start (args, format);
401 int written = vsnprintf (&writer->output_str [writer->len], writer->allocated_len - writer->len, format, args);
402 va_end (args);
404 if (written > 0) writer->len += written;
405 g_assert (writer->len == strlen (writer->output_str));
408 static void
409 mono_state_writer_indent (MonoStateWriter *writer)
411 for (int i = 0; i < writer->indent; ++i)
412 mono_state_writer_printf(writer, " ");
415 static void
416 mono_state_writer_object_key (MonoStateWriter *writer, const char *key)
418 mono_state_writer_indent (writer);
419 mono_state_writer_printf(writer, "\"%s\" : ", key);
422 static void
423 mono_native_state_add_ctx (MonoStateWriter *writer, MonoContext *ctx)
425 // Context
426 mono_state_writer_indent (writer);
427 mono_state_writer_object_key (writer, "ctx");
428 mono_state_writer_printf(writer, "{\n");
429 writer->indent++;
431 assert_has_space (writer);
432 mono_state_writer_indent (writer);
433 mono_state_writer_object_key (writer, "IP");
434 mono_state_writer_printf(writer, "\"%p\",\n", (gpointer) MONO_CONTEXT_GET_IP (ctx));
436 assert_has_space (writer);
437 mono_state_writer_indent (writer);
438 mono_state_writer_object_key (writer, "SP");
439 mono_state_writer_printf(writer, "\"%p\",\n", (gpointer) MONO_CONTEXT_GET_SP (ctx));
441 assert_has_space (writer);
442 mono_state_writer_indent (writer);
443 mono_state_writer_object_key (writer, "BP");
444 mono_state_writer_printf(writer, "\"%p\"\n", (gpointer) MONO_CONTEXT_GET_BP (ctx));
446 writer->indent--;
447 mono_state_writer_indent (writer);
448 mono_state_writer_printf(writer, "}");
451 static void
452 mono_native_state_add_frame (MonoStateWriter *writer, MonoFrameSummary *frame)
454 mono_state_writer_indent (writer);
455 mono_state_writer_printf(writer, "{\n");
456 writer->indent++;
458 assert_has_space (writer);
459 mono_state_writer_indent (writer);
460 mono_state_writer_object_key (writer, "is_managed");
461 mono_state_writer_printf(writer, "\"%s\",", frame->is_managed ? "true" : "false");
463 if (frame->unmanaged_data.is_trampoline) {
464 mono_state_writer_printf(writer, "\n");
465 assert_has_space (writer);
466 mono_state_writer_indent (writer);
467 mono_state_writer_object_key (writer, "is_trampoline");
468 mono_state_writer_printf(writer, "\"true\",");
471 if (frame->is_managed) {
472 mono_state_writer_printf(writer, "\n");
473 assert_has_space (writer);
474 mono_state_writer_indent (writer);
475 mono_state_writer_object_key (writer, "guid");
476 mono_state_writer_printf(writer, "\"%s\",\n", frame->managed_data.guid);
478 assert_has_space (writer);
479 mono_state_writer_indent (writer);
480 mono_state_writer_object_key (writer, "token");
481 mono_state_writer_printf(writer, "\"0x%05x\",\n", frame->managed_data.token);
483 assert_has_space (writer);
484 mono_state_writer_indent (writer);
485 mono_state_writer_object_key (writer, "native_offset");
486 mono_state_writer_printf(writer, "\"0x%x\",\n", frame->managed_data.native_offset);
488 #ifndef MONO_PRIVATE_CRASHES
489 if (frame->managed_data.name != NULL) {
490 assert_has_space (writer);
491 mono_state_writer_indent (writer);
492 mono_state_writer_object_key (writer, "method_name");
493 mono_state_writer_printf(writer, "\"%s\",\n", frame->managed_data.name);
495 #endif
497 assert_has_space (writer);
498 mono_state_writer_indent (writer);
499 mono_state_writer_object_key (writer, "filename");
500 mono_state_writer_printf(writer, "\"%s\",\n", frame->managed_data.filename);
502 assert_has_space (writer);
503 mono_state_writer_indent (writer);
504 mono_state_writer_object_key (writer, "sizeofimage");
505 mono_state_writer_printf(writer, "\"0x%x\",\n", frame->managed_data.image_size);
507 assert_has_space (writer);
508 mono_state_writer_indent (writer);
509 mono_state_writer_object_key (writer, "timestamp");
510 mono_state_writer_printf(writer, "\"0x%x\",\n", frame->managed_data.time_date_stamp);
512 assert_has_space (writer);
513 mono_state_writer_indent (writer);
514 mono_state_writer_object_key (writer, "il_offset");
515 mono_state_writer_printf(writer, "\"0x%05x\"\n", frame->managed_data.il_offset);
517 } else {
518 mono_state_writer_printf(writer, "\n");
519 assert_has_space (writer);
520 mono_state_writer_indent (writer);
521 mono_state_writer_object_key (writer, "native_address");
522 if (frame->unmanaged_data.ip) {
523 mono_state_writer_printf(writer, "\"0x%" PRIx64 "\"", (guint64) frame->unmanaged_data.ip);
524 } else
525 mono_state_writer_printf(writer, "\"unregistered\"");
527 if (frame->unmanaged_data.ip) {
528 mono_state_writer_printf(writer, ",\n");
530 assert_has_space (writer);
531 mono_state_writer_indent (writer);
532 mono_state_writer_object_key (writer, "native_offset");
533 mono_state_writer_printf(writer, "\"0x%05x\"", frame->unmanaged_data.offset);
536 if (frame->unmanaged_data.module [0] != '\0') {
537 mono_state_writer_printf(writer, ",\n");
539 assert_has_space (writer);
540 mono_state_writer_indent (writer);
541 mono_state_writer_object_key (writer, "native_module");
542 mono_state_writer_printf(writer, "\"%s\"", frame->unmanaged_data.module);
545 if (frame->unmanaged_data.has_name) {
546 mono_state_writer_printf(writer, ",\n");
548 assert_has_space (writer);
549 mono_state_writer_indent (writer);
550 mono_state_writer_object_key (writer, "unmanaged_name");
551 mono_state_writer_printf(writer, "\"%s\"\n", frame->str_descr);
552 } else {
553 mono_state_writer_printf(writer, "\n");
557 mono_state_writer_indent (writer);
558 writer->indent--;
559 mono_state_writer_printf(writer, "}\n");
562 static void
563 mono_native_state_add_frames (MonoStateWriter *writer, int num_frames, MonoFrameSummary *frames, const char *label)
565 mono_state_writer_indent (writer);
566 mono_state_writer_object_key (writer, label);
568 mono_state_writer_printf(writer, "[\n");
570 for (int i = 0; i < num_frames; ++i) {
571 if (i > 0)
572 mono_state_writer_printf(writer, ",\n");
573 mono_native_state_add_frame (writer, &frames [i]);
575 mono_state_writer_printf(writer, "\n");
577 mono_state_writer_indent (writer);
578 writer->indent--;
579 mono_state_writer_printf(writer, "]");
582 static void
583 mono_native_state_add_managed_exc (MonoStateWriter *writer, MonoExcSummary *exc)
585 mono_state_writer_indent (writer);
586 mono_state_writer_printf(writer, "{\n");
587 writer->indent++;
589 assert_has_space (writer);
590 mono_state_writer_indent (writer);
591 mono_state_writer_object_key (writer, "type");
592 mono_state_writer_printf(writer, "\"%s.%s\",\n", m_class_get_name_space (exc->managed_exc_type), m_class_get_name (exc->managed_exc_type));
594 mono_native_state_add_frames (writer, exc->num_managed_frames, exc->managed_frames, "managed_frames");
596 mono_state_writer_indent (writer);
597 writer->indent--;
598 mono_state_writer_printf(writer, "}\n");
601 static void
602 mono_native_state_add_managed_excs (MonoStateWriter *writer, int num_excs, MonoExcSummary *excs)
604 mono_state_writer_indent (writer);
605 mono_state_writer_object_key (writer, "exceptions");
607 mono_state_writer_printf(writer, "[\n");
609 for (int i = 0; i < num_excs; ++i) {
610 if (i > 0)
611 mono_state_writer_printf(writer, ",\n");
612 mono_native_state_add_managed_exc (writer, &excs [i]);
615 mono_state_writer_indent (writer);
616 writer->indent--;
617 mono_state_writer_printf(writer, "]");
621 void
622 mono_native_state_add_thread (MonoStateWriter *writer, MonoThreadSummary *thread, MonoContext *ctx, gboolean first_thread, gboolean crashing_thread)
624 assert_has_space (writer);
626 if (!first_thread) {
627 mono_state_writer_printf(writer, ",\n");
630 mono_state_writer_indent (writer);
631 mono_state_writer_printf(writer, "{\n");
632 writer->indent++;
634 assert_has_space (writer);
635 mono_state_writer_indent (writer);
636 mono_state_writer_object_key (writer, "is_managed");
637 mono_state_writer_printf(writer, "%s,\n", thread->is_managed ? "true" : "false");
639 assert_has_space (writer);
640 mono_state_writer_indent (writer);
641 mono_state_writer_object_key (writer, "offset_free_hash");
642 mono_state_writer_printf(writer, "\"0x%" PRIx64 "\",\n", thread->hashes.offset_free_hash);
644 assert_has_space (writer);
645 mono_state_writer_indent (writer);
646 mono_state_writer_object_key (writer, "offset_rich_hash");
647 mono_state_writer_printf(writer, "\"0x%" PRIx64 "\",\n", thread->hashes.offset_rich_hash);
649 assert_has_space (writer);
650 mono_state_writer_indent (writer);
651 mono_state_writer_object_key (writer, "crashed");
652 mono_state_writer_printf(writer, "%s,\n", crashing_thread ? "true" : "false");
654 assert_has_space (writer);
655 mono_state_writer_indent (writer);
656 mono_state_writer_object_key (writer, "native_thread_id");
657 mono_state_writer_printf(writer, "\"0x%" PRIx64 "\",\n", (guint64) thread->native_thread_id);
659 assert_has_space (writer);
660 mono_state_writer_indent (writer);
661 mono_state_writer_object_key (writer, "thread_info_addr");
662 mono_state_writer_printf(writer, "\"0x%" PRIx64 "\"", (guint64) thread->info_addr);
664 if (thread->error_msg != NULL) {
665 mono_state_writer_printf(writer, ",\n");
666 assert_has_space (writer);
667 mono_state_writer_indent (writer);
668 mono_state_writer_object_key (writer, "dumping_error");
669 mono_state_writer_printf(writer, "\"%s\"", thread->error_msg);
672 if (thread->name [0] != '\0') {
673 mono_state_writer_printf(writer, ",\n");
674 assert_has_space (writer);
675 mono_state_writer_indent (writer);
676 mono_state_writer_object_key (writer, "thread_name");
677 mono_state_writer_printf(writer, "\"%s\"", thread->name);
680 if (ctx) {
681 mono_state_writer_printf(writer, ",\n");
682 mono_native_state_add_ctx (writer, ctx);
685 if (thread->num_exceptions > 0) {
686 mono_state_writer_printf(writer, ",\n");
687 mono_native_state_add_managed_excs (writer, thread->num_exceptions, thread->exceptions);
690 if (thread->num_managed_frames > 0) {
691 mono_state_writer_printf(writer, ",\n");
692 mono_native_state_add_frames (writer, thread->num_managed_frames, thread->managed_frames, "managed_frames");
695 if (thread->num_unmanaged_frames > 0) {
696 mono_state_writer_printf(writer, ",\n");
697 mono_native_state_add_frames (writer, thread->num_unmanaged_frames, thread->unmanaged_frames, "unmanaged_frames");
700 mono_state_writer_printf(writer, "\n");
702 mono_state_writer_indent (writer);
703 mono_state_writer_printf(writer, "}");
706 static void
707 mono_native_state_add_ee_info (MonoStateWriter *writer)
709 #ifndef MONO_PRIVATE_CRASHES
710 // FIXME: setup callbacks to enable
711 /*const char *aot_mode;*/
712 /*MonoAotMode mono_aot_mode = mono_jit_get_aot_mode ();*/
713 /*switch (mono_aot_mode) {*/
714 /*case MONO_AOT_MODE_NONE:*/
715 /*aot_mode = "none";*/
716 /*break;*/
717 /*case MONO_AOT_MODE_NORMAL:*/
718 /*aot_mode = "normal";*/
719 /*break;*/
720 /*case MONO_AOT_MODE_HYBRID:*/
721 /*aot_mode = "hybrid";*/
722 /*break;*/
723 /*case MONO_AOT_MODE_FULL:*/
724 /*aot_mode = "full";*/
725 /*break;*/
726 /*case MONO_AOT_MODE_LLVMONLY:*/
727 /*aot_mode = "llvmonly";*/
728 /*break;*/
729 /*case MONO_AOT_MODE_INTERP:*/
730 /*aot_mode = "interp";*/
731 /*break;*/
732 /*case MONO_AOT_MODE_INTERP_LLVMONLY:*/
733 /*aot_mode = "interp_llvmonly";*/
734 /*break;*/
735 /*default:*/
736 /*aot_mode = "error";*/
737 /*}*/
739 assert_has_space (writer);
740 mono_state_writer_indent (writer);
741 mono_state_writer_object_key (writer, "execution_context");
742 mono_state_writer_printf(writer, "{\n");
743 writer->indent++;
745 /*mono_state_writer_indent (writer);*/
746 /*mono_state_writer_object_key (writer, "aot_mode");*/
747 /*mono_state_writer_printf(writer, "\"%s\",\n", aot_mode);*/
749 /*mono_state_writer_indent (writer);*/
750 /*mono_state_writer_object_key (writer, "mono_use_llvm");*/
751 /*mono_state_writer_printf(writer, "\"%s\",\n", mono_use_llvm ? "true" : "false");*/
753 assert_has_space (writer);
754 mono_state_writer_indent (writer);
755 mono_state_writer_object_key (writer, "coop-enabled");
756 mono_state_writer_printf(writer, "\"%s\"\n", mono_threads_is_cooperative_suspension_enabled () ? "true" : "false");
758 writer->indent--;
759 mono_state_writer_indent (writer);
760 mono_state_writer_printf(writer, "},\n");
761 #endif
764 // Taken from driver.c
765 #if defined(MONO_ARCH_ARCHITECTURE)
766 /* Redefine MONO_ARCHITECTURE to include more information */
767 #undef MONO_ARCHITECTURE
768 #define MONO_ARCHITECTURE MONO_ARCH_ARCHITECTURE
769 #endif
771 static void
772 mono_native_state_add_version (MonoStateWriter *writer)
774 assert_has_space (writer);
775 mono_state_writer_indent (writer);
776 mono_state_writer_object_key (writer, "configuration");
777 mono_state_writer_printf(writer, "{\n");
778 writer->indent++;
780 assert_has_space (writer);
781 mono_state_writer_indent (writer);
782 mono_state_writer_object_key (writer, "version");
783 mono_state_writer_printf(writer, "\"(%s) (%s)\",\n", VERSION, mono_get_runtime_callbacks ()->get_runtime_build_version ());
785 assert_has_space (writer);
786 mono_state_writer_indent (writer);
787 mono_state_writer_object_key (writer, "tlc");
788 #ifdef MONO_KEYWORD_THREAD
789 mono_state_writer_printf(writer, "\"__thread\",\n");
790 #else
791 mono_state_writer_printf(writer, "\"normal\",\n");
792 #endif /* MONO_KEYWORD_THREAD */
794 assert_has_space (writer);
795 mono_state_writer_indent (writer);
796 mono_state_writer_object_key (writer, "sigsgev");
797 #ifdef MONO_ARCH_SIGSEGV_ON_ALTSTACK
798 mono_state_writer_printf(writer, "\"altstack\",\n");
799 #else
800 mono_state_writer_printf(writer, "\"normal\",\n");
801 #endif
803 assert_has_space (writer);
804 mono_state_writer_indent (writer);
805 mono_state_writer_object_key (writer, "notifications");
806 #ifdef HAVE_EPOLL
807 mono_state_writer_printf(writer, "\"epoll\",\n");
808 #elif defined(HAVE_KQUEUE)
809 mono_state_writer_printf(writer, "\"kqueue\",\n");
810 #else
811 mono_state_writer_printf(writer, "\"thread+polling\",\n");
812 #endif
814 assert_has_space (writer);
815 mono_state_writer_indent (writer);
816 mono_state_writer_object_key (writer, "architecture");
817 mono_state_writer_printf(writer, "\"%s\",\n", MONO_ARCHITECTURE);
819 assert_has_space (writer);
820 mono_state_writer_indent (writer);
821 mono_state_writer_object_key (writer, "disabled_features");
822 mono_state_writer_printf(writer, "\"%s\",\n", DISABLED_FEATURES);
824 assert_has_space (writer);
825 mono_state_writer_indent (writer);
826 mono_state_writer_object_key (writer, "smallconfig");
827 #ifdef MONO_SMALL_CONFIG
828 mono_state_writer_printf(writer, "\"enabled\",\n");
829 #else
830 mono_state_writer_printf(writer, "\"disabled\",\n");
831 #endif
833 assert_has_space (writer);
834 mono_state_writer_indent (writer);
835 mono_state_writer_object_key (writer, "bigarrays");
836 #ifdef MONO_BIG_ARRAYS
837 mono_state_writer_printf(writer, "\"enabled\",\n");
838 #else
839 mono_state_writer_printf(writer, "\"disabled\",\n");
840 #endif
842 assert_has_space (writer);
843 mono_state_writer_indent (writer);
844 mono_state_writer_object_key (writer, "softdebug");
845 #if !defined(DISABLE_SDB)
846 mono_state_writer_printf(writer, "\"enabled\",\n");
847 #else
848 mono_state_writer_printf(writer, "\"disabled\",\n");
849 #endif
851 assert_has_space (writer);
852 mono_state_writer_indent (writer);
853 mono_state_writer_object_key (writer, "interpreter");
854 #ifndef DISABLE_INTERPRETER
855 mono_state_writer_printf(writer, "\"enabled\",\n");
856 #else
857 mono_state_writer_printf(writer, "\"disabled\",\n");
858 #endif
860 assert_has_space (writer);
861 mono_state_writer_indent (writer);
862 mono_state_writer_object_key (writer, "llvm_support");
863 #ifdef MONO_ARCH_LLVM_SUPPORTED
864 #ifdef ENABLE_LLVM
865 mono_state_writer_printf(writer, "\"%d\",\n", LLVM_API_VERSION);
866 #else
867 mono_state_writer_printf(writer, "\"disabled\",\n");
868 #endif
869 #endif
871 const char *susp_policy = mono_threads_suspend_policy_name (mono_threads_suspend_policy ());
872 assert_has_space (writer);
873 mono_state_writer_indent (writer);
874 mono_state_writer_object_key (writer, "suspend");
875 mono_state_writer_printf(writer, "\"%s\"\n", susp_policy);
877 assert_has_space (writer);
878 mono_state_writer_indent (writer);
879 mono_state_writer_printf(writer, "},\n");
880 writer->indent--;
883 static void
884 mono_native_state_add_memory (MonoStateWriter *writer)
886 assert_has_space (writer);
887 mono_state_writer_indent (writer);
888 mono_state_writer_object_key (writer, "memory");
889 mono_state_writer_printf(writer, "{\n");
890 writer->indent++;
892 #ifdef TARGET_OSX
893 struct task_basic_info t_info;
894 memset (&t_info, 0, sizeof (t_info));
895 mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
896 task_name_t task = mach_task_self ();
897 task_info(task, TASK_BASIC_INFO, (task_info_t) &t_info, &t_info_count);
899 assert_has_space (writer);
900 mono_state_writer_indent (writer);
901 mono_state_writer_object_key (writer, "Resident Size");
902 mono_state_writer_printf(writer, "\"%lu\",\n", t_info.resident_size);
904 assert_has_space (writer);
905 mono_state_writer_indent (writer);
906 mono_state_writer_object_key (writer, "Virtual Size");
907 mono_state_writer_printf(writer, "\"%lu\",\n", t_info.virtual_size);
908 #endif
910 GCStats stats;
911 memcpy (&stats, &mono_gc_stats, sizeof (GCStats));
913 assert_has_space (writer);
914 mono_state_writer_indent (writer);
915 mono_state_writer_object_key (writer, "minor_gc_time");
916 mono_state_writer_printf(writer, "\"%lld\",\n", stats.minor_gc_time);
918 assert_has_space (writer);
919 mono_state_writer_indent (writer);
920 mono_state_writer_object_key (writer, "major_gc_time");
921 mono_state_writer_printf(writer, "\"%lld\",\n", stats.major_gc_time);
923 assert_has_space (writer);
924 mono_state_writer_indent (writer);
925 mono_state_writer_object_key (writer, "minor_gc_count");
926 mono_state_writer_printf(writer, "\"%d\",\n", stats.minor_gc_count);
928 assert_has_space (writer);
929 mono_state_writer_indent (writer);
930 mono_state_writer_object_key (writer, "major_gc_count");
931 mono_state_writer_printf(writer, "\"%d\",\n", stats.major_gc_count);
933 assert_has_space (writer);
934 mono_state_writer_indent (writer);
935 mono_state_writer_object_key (writer, "major_gc_time_concurrent");
936 mono_state_writer_printf(writer, "\"%lld\"\n", stats.major_gc_time_concurrent);
938 writer->indent--;
939 mono_state_writer_indent (writer);
940 mono_state_writer_printf(writer, "},\n");
943 #define MONO_CRASH_REPORTING_MAPPING_LINE_LIMIT 30
945 #if !MONO_PRIVATE_CRASHES
947 static void
948 mono_native_state_add_process_map (MonoStateWriter *writer)
950 #if defined(__linux__) && !defined(HOST_ANDROID)
951 int handle = g_open ("/proc/self/maps", O_RDONLY, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
952 if (handle == -1) {
953 g_async_safe_printf ("Couldn't find /proc/self/maps on Linux system. Continuing.");
954 return;
957 assert_has_space (writer);
958 mono_state_writer_indent (writer);
959 mono_state_writer_object_key (writer, "process_map");
960 mono_state_writer_printf(writer, "[\n");
962 int mapping = 0;
963 while (mapping < MONO_CRASH_REPORTING_MAPPING_LINE_LIMIT) {
964 if (mapping > 0)
965 mono_state_writer_printf (writer, "\",\n");
967 mono_state_writer_printf (writer, "\t\"");
969 while (TRUE) {
970 char line [10];
971 gboolean newline = FALSE;
972 int charsCopied = g_async_safe_fgets (line, sizeof (line), handle, &newline);
974 if (charsCopied == 0)
975 break;
977 for (int i=0; i < charsCopied; i++)
978 g_assert (isprint (line [i]));
980 g_assert (line [charsCopied] == '\0');
982 mono_state_writer_printf (writer, "%s", line);
984 if (newline)
985 break;
988 mapping++;
991 if (mapping > 0)
992 mono_state_writer_printf (writer, "\"");
994 mono_state_writer_indent (writer);
995 writer->indent--;
996 mono_state_writer_printf(writer, "],\n");
998 close (handle);
999 #endif
1002 #endif
1004 static void
1005 mono_native_state_add_logged_message (MonoStateWriter *writer, const char *object_key, const char *msg)
1007 if (msg != NULL) {
1008 assert_has_space (writer);
1009 mono_state_writer_indent (writer);
1010 mono_state_writer_object_key (writer, object_key);
1012 size_t length;
1013 const char *pos;
1014 if ((pos = strchr (msg, '\n')) != NULL)
1015 length = (size_t)(pos - msg);
1016 else
1017 length = strlen (msg);
1018 length = MIN (length, INT_MAX);
1020 mono_state_writer_printf(writer, "\"%.*s\",\n", (int)length, msg);
1024 static void
1025 mono_native_state_add_prologue (MonoStateWriter *writer)
1027 mono_state_writer_printf(writer, "{\n");
1028 writer->indent++;
1030 assert_has_space (writer);
1031 mono_state_writer_indent (writer);
1032 mono_state_writer_object_key (writer, "protocol_version");
1033 mono_state_writer_printf(writer, "\"%s\",\n", MONO_NATIVE_STATE_PROTOCOL_VERSION);
1035 mono_native_state_add_version (writer);
1037 mono_native_state_add_ee_info (writer);
1039 mono_native_state_add_memory (writer);
1041 const char *assertion_msg = g_get_assertion_message ();
1042 mono_native_state_add_logged_message (writer, "assertion_message", assertion_msg);
1044 const char *failfast_msg = mono_crash_get_failfast_msg ();
1045 mono_native_state_add_logged_message (writer, "failfast_message", failfast_msg);
1048 #ifndef MONO_PRIVATE_CRASHES
1049 mono_native_state_add_process_map (writer);
1050 #endif
1052 // Start threads array
1053 assert_has_space (writer);
1054 mono_state_writer_indent (writer);
1055 mono_state_writer_object_key (writer, "threads");
1056 mono_state_writer_printf(writer, "[\n");
1059 static void
1060 mono_native_state_add_epilogue (MonoStateWriter *writer)
1062 mono_state_writer_printf(writer, "\n");
1063 mono_state_writer_indent (writer);
1064 mono_state_writer_printf(writer, "]\n");
1065 writer->indent--;
1067 writer->indent--;
1068 mono_state_writer_indent (writer);
1069 mono_state_writer_printf(writer, "}");
1072 void
1073 mono_native_state_init (MonoStateWriter *writer)
1075 mono_native_state_add_prologue (writer);
1078 char *
1079 mono_native_state_emit (MonoStateWriter *writer)
1081 mono_native_state_add_epilogue (writer);
1082 return writer->output_str;
1085 char *
1086 mono_native_state_free (MonoStateWriter *writer, gboolean free_data)
1088 mono_native_state_add_epilogue (writer);
1089 char *output = NULL;
1091 // Make this interface work like the g_string free does
1092 if (!free_data)
1093 output = g_strdup (writer->output_str);
1095 return output;
1098 void
1099 mono_state_writer_init (MonoStateWriter *writer, gchar *output_str, int len)
1101 memset(writer, 0, sizeof(*writer));
1102 memset(output_str, 0, len * sizeof(gchar));
1104 writer->output_str = output_str;
1105 writer->allocated_len = len;
1106 writer->len = 0;
1107 writer->indent = 0;
1110 void
1111 mono_summarize_native_state_begin (MonoStateWriter *writer, gchar *mem, int size)
1113 g_assert (mem);
1114 mono_state_writer_init (writer, mem, size);
1115 mono_native_state_init (writer);
1118 char *
1119 mono_summarize_native_state_end (MonoStateWriter *writer)
1121 return mono_native_state_emit (writer);
1124 void
1125 mono_summarize_native_state_add_thread (MonoStateWriter *writer, MonoThreadSummary *thread, MonoContext *ctx, gboolean crashing_thread)
1127 static gboolean not_first_thread = FALSE;
1128 mono_native_state_add_thread (writer, thread, ctx, !not_first_thread, crashing_thread);
1129 not_first_thread = TRUE;
1132 void
1133 mono_crash_dump (const char *jsonFile, MonoStackHash *hashes)
1135 if (g_hasenv ("MONO_CRASH_NOFILE"))
1136 return;
1138 size_t size = strlen (jsonFile);
1140 gboolean success = FALSE;
1142 // Save up to 100 dump files for a given stacktrace hash
1143 for (int increment = 0; increment < 100; increment++) {
1144 char name [100];
1145 name [0] = '\0';
1146 g_snprintf (name, sizeof (name), "mono_crash.%" PRIx64 ".%d.json", hashes->offset_free_hash, increment);
1148 int handle = g_open (name, O_WRONLY | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
1149 if (handle != -1) {
1150 g_write (handle, jsonFile, (guint32) size);
1151 success = TRUE;
1154 /*cleanup*/
1155 if (handle)
1156 close (handle);
1158 if (success)
1159 return;
1162 g_assertf (!success, "Couldn't create any of (many) attempted crash files\n");
1163 return;
1166 #endif // DISABLE_CRASH_REPORTING
1168 static volatile int32_t dump_status;
1170 gboolean
1171 mono_dump_start (void)
1173 return (mono_atomic_xchg_i32(&dump_status, 1) == 0); // return true if we started the dump
1176 gboolean
1177 mono_dump_complete (void)
1179 return (mono_atomic_xchg_i32(&dump_status, 0) == 1); // return true if we completed the dump
1182 static char *saved_failfast_msg;
1185 * mono_crash_save_failfast_msg:
1186 * \param msg the message to save. Takes ownership, caller shouldn't free
1188 * \returns the previous message - caller is responsible for freeing.
1190 char*
1191 mono_crash_save_failfast_msg (char *msg)
1193 return (char*) mono_atomic_xchg_ptr ((gpointer*)&saved_failfast_msg, (void*)msg);
1196 const char*
1197 mono_crash_get_failfast_msg (void)
1199 return saved_failfast_msg;