3 * Support for interop with the Microsoft Error Reporting tool
6 * Alexander Kyte (alkyte@microsoft.com)
8 * (C) 2018 Microsoft, Inc.
15 #if defined(TARGET_OSX) && !defined(DISABLE_CRASH_REPORTING)
16 #include "mono-merp.h"
21 // OSX OS stuff now, for merpGUI interop
22 #include <mach/mach.h>
23 #include <mach/task_info.h>
24 #include <mach/mach_types.h>
25 #include <mach/mach_traps.h>
26 #include <servers/bootstrap.h>
28 #include <metadata/locales.h>
31 #if defined(HAVE_SYS_UTSNAME_H)
32 #include <sys/utsname.h>
35 // To get the apple machine model
36 #include <sys/param.h>
37 #include <sys/sysctl.h>
40 #include <mono/utils/json.h>
41 #include <mono/utils/mono-state.h>
42 #include <utils/mono-threads-debug.h>
45 kernel_version_string (void)
47 #ifdef HAVE_SYS_UTSNAME_H
48 static struct utsname name
;
49 static const char *version_string
;
51 if (!version_string
) {
52 // WSL does not always nul terminate. WSL was fixed February 2018.
53 // Restore memset if variable made non-static.
54 //memset (&name, 0, sizeof (name));
56 if (uname (&name
) >= 0)
57 version_string
= name
.release
;
62 return version_string
;
68 starts_with (const char *pre
, size_t pre_sz
, const char *str
)
70 return strncmp (pre
, str
, pre_sz
) == 0;
74 macos_version_string (void)
76 /* Can't allocate in here, could be called from a signal handler in a
79 static const char *version_string
;
81 static const size_t buf_size
= sizeof (buf
);
84 return version_string
;
86 /* macOS 10.13.6 or later */
87 if (!version_string
) {
89 if (sysctlbyname ("kern.osproductversion", NULL
, &size
, NULL
, 0) < 0 || size
>= buf_size
) {
90 /* if we couldn't get the size or if it needs more space that we have in buf, leave it empty */
92 return version_string
;
95 if (sysctlbyname ("kern.osproductversion", (char*)buf
, &size
, NULL
, 0) >= 0)
96 version_string
= &buf
[0];
98 /* macOS 10.13.5 or older */
99 if (!version_string
) {
100 const char *kv_string
= kernel_version_string ();
101 if (starts_with (G_STRING_CONSTANT_AND_LENGTH ("17"), kv_string
))
102 version_string
= "10.13"; // High Sierra
103 else if (starts_with (G_STRING_CONSTANT_AND_LENGTH ("16"), kv_string
))
104 version_string
= "10.12"; // Sierra
105 else if (starts_with (G_STRING_CONSTANT_AND_LENGTH ("15"), kv_string
))
106 version_string
= "10.11"; // El Capitan
107 else if (starts_with (G_STRING_CONSTANT_AND_LENGTH ("14"), kv_string
))
108 version_string
= "10.10"; // Yosemite
109 else if (starts_with (G_STRING_CONSTANT_AND_LENGTH ("13"), kv_string
))
110 version_string
= "10.9"; // Mavericks
111 else if (starts_with (G_STRING_CONSTANT_AND_LENGTH ("12"), kv_string
))
112 version_string
= "10.8"; // Mountain Lion
113 else if (starts_with (G_STRING_CONSTANT_AND_LENGTH ("11"), kv_string
))
114 version_string
= "10.7"; // Lion
119 return version_string
;
122 // To get the path of the running process
138 MERP_EXC_FORCE_QUIT
= 1,
139 MERP_EXC_SIGSEGV
= 2,
140 MERP_EXC_SIGABRT
= 3,
144 MERP_EXC_SIGFPE
= 7 ,
145 MERP_EXC_SIGTRAP
= 8,
146 MERP_EXC_SIGKILL
= 9,
148 MERP_EXC_MANAGED_EXCEPTION
= 11
152 const char *merpFilePath
;
153 const char *crashLogPath
;
154 const char *werXmlPath
;
156 const char *bundleIDArg
; // App Bundle ID (required for bucketization)
157 const char *versionArg
; // App Version (required for bucketization)
159 MerpArch archArg
; // Arch, MacOS only, bails out if not found also required for bucketization. (required)
160 MERPExcType exceptionArg
; // Exception type (refer to merpcommon.h and mach/exception_types.h for more info (optional)
162 const char *serviceNameArg
; // This is the Bootstrap service name that MERP GUI will create to receive mach_task_self on a port created. Bails out if MERP GUI fails to receive mach_task_self from the crashed app. (Required for crash log generation)
163 const char *servicePathArg
; // The path to the executable, used to relaunch the crashed app.
165 const char *moduleName
;
166 const char *moduleVersion
;
169 const char *osVersion
;
170 int uiLidArg
; // MONO_LOCALE_INVARIANT 0x007F
172 char systemModel
[100];
173 const char *systemManufacturer
;
175 const char *eventType
; /* Must be MONO_MERP_EVENT_TYPE_STR */
177 MonoStackHash hashes
;
181 /* The event type determines the format of the fields that are reported. It
182 * must be MonoAppCrash for the rest of our report to make sense.
184 #define MONO_MERP_EVENT_TYPE_STR "MonoAppCrash"
188 gboolean enable_merp
;
190 const char *appBundleID
;
192 const char *appSignature
;
193 const char *appVersion
;
194 const char *merpGUIPath
;
195 const char *eventType
;
196 const char *merpFilePath
;
197 const char *crashLogPath
;
198 const char *werXmlPath
;
199 const char *moduleVersion
;
205 static MerpOptions config
;
210 } MonoMerpAnnotationEntry
;
213 get_merp_bitness (MerpArch arch
)
221 g_assert_not_reached ();
230 #elif defined(TARGET_AMD64)
231 return MerpArchx86_64
;
232 #elif defined(TARGET_POWERPC)
234 #elif defined(TARGET_POWERPC64)
235 return MerpArchPPC64
;
237 g_assert_not_reached ();
242 get_merp_exctype (MERPExcType exc
)
245 case MERP_EXC_FORCE_QUIT
:
247 case MERP_EXC_SIGSEGV
:
249 case MERP_EXC_SIGABRT
:
251 case MERP_EXC_SIGSYS
:
253 case MERP_EXC_SIGILL
:
255 case MERP_EXC_SIGBUS
:
257 case MERP_EXC_SIGFPE
:
259 case MERP_EXC_SIGTRAP
:
261 case MERP_EXC_SIGKILL
:
265 case MERP_EXC_MANAGED_EXCEPTION
:
268 // Exception type documented as optional, not optional
269 g_assert_not_reached ();
271 g_assert_not_reached ();
276 parse_exception_type (const char *signal
)
278 if (!strcmp (signal
, "SIGSEGV"))
279 return MERP_EXC_SIGSEGV
;
281 if (!strcmp (signal
, "SIGFPE"))
282 return MERP_EXC_SIGFPE
;
284 if (!strcmp (signal
, "SIGILL"))
285 return MERP_EXC_SIGILL
;
287 if (!strcmp (signal
, "SIGABRT"))
288 return MERP_EXC_SIGABRT
;
290 // Force quit == hang?
291 // We need a default for this
292 if (!strcmp (signal
, "SIGTERM"))
293 return MERP_EXC_HANG
;
295 if (!strcmp (signal
, "MANAGED_EXCEPTION"))
296 return MERP_EXC_MANAGED_EXCEPTION
;
298 // FIXME: There are no other such signal
299 // strings passed to mono_handle_native_crash at the
300 // time of writing this
301 g_error ("Merp doesn't know how to handle %s\n", signal
);
304 static int merp_file_permissions
= S_IWUSR
| S_IRUSR
| S_IRGRP
| S_IROTH
;
307 mono_merp_write_params (MERPStruct
*merp
)
309 int handle
= g_open (merp
->merpFilePath
, O_TRUNC
| O_WRONLY
| O_CREAT
, merp_file_permissions
);
310 g_assertf (handle
!= -1, "Could not open MERP file at %s", merp
->merpFilePath
);
312 g_async_safe_fprintf(handle
, "ApplicationBundleId: %s\n", merp
->bundleIDArg
);
313 g_async_safe_fprintf(handle
, "ApplicationVersion: %s\n", merp
->versionArg
);
314 g_async_safe_fprintf(handle
, "ApplicationBitness: %s\n", get_merp_bitness (merp
->archArg
));
316 g_async_safe_fprintf(handle
, "ApplicationName: %s\n", merp
->serviceNameArg
);
317 g_async_safe_fprintf(handle
, "ApplicationPath: %s\n", merp
->servicePathArg
);
318 g_async_safe_fprintf(handle
, "BlameModuleName: %s\n", merp
->moduleName
);
319 g_async_safe_fprintf(handle
, "BlameModuleVersion: %s\n", merp
->moduleVersion
);
320 g_async_safe_fprintf(handle
, "BlameModuleOffset: 0x%llx\n", (unsigned long long)merp
->moduleOffset
);
321 g_async_safe_fprintf(handle
, "ExceptionType: %s\n", get_merp_exctype (merp
->exceptionArg
));
322 g_async_safe_fprintf(handle
, "StackChecksum: 0x%llx\n", merp
->hashes
.offset_free_hash
);
323 g_async_safe_fprintf(handle
, "StackHash: 0x%llx\n", merp
->hashes
.offset_rich_hash
);
326 g_async_safe_fprintf(handle
, "OSVersion: %s\n", merp
->osVersion
);
327 g_async_safe_fprintf(handle
, "LanguageID: 0x%x\n", merp
->uiLidArg
);
328 g_async_safe_fprintf(handle
, "SystemManufacturer: %s\n", merp
->systemManufacturer
);
329 g_async_safe_fprintf(handle
, "SystemModel: %s\n", merp
->systemModel
);
330 g_async_safe_fprintf(handle
, "EventType: %s\n", merp
->eventType
);
337 mono_merp_send (MERPStruct
*merp
)
339 gboolean invoke_success
= FALSE
;
341 #if defined(HAVE_EXECV) && defined(HAVE_FORK)
342 pid_t pid
= (pid_t
) fork ();
344 // Only one we define on OSX
346 const char *open_path
= "/usr/bin/open";
347 const char *argvOpen
[] = {open_path
, "-a", config
.merpGUIPath
, NULL
};
348 execv (open_path
, (char**)argvOpen
);
352 int exit_status
= FALSE
;
355 if (waitpid(pid
, &status
, WUNTRACED
| WCONTINUED
) == -1)
358 if (WIFEXITED(status
)) {
359 exit_status
= WEXITSTATUS(status
);
360 invoke_success
= (exit_status
== 0);
362 } else if (WIFSIGNALED(status
)) {
368 // // Create process to launch merp gui application
371 return invoke_success
;
375 get_apple_model (char *buffer
, size_t max_length
)
379 // Get the number of bytes to copy
380 sysctlbyname("hw.model", NULL
, &sz
, NULL
, 0);
382 if (sz
> max_length
) {
387 sysctlbyname("hw.model", buffer
, &sz
, NULL
, 0);
391 mono_init_merp (const intptr_t crashed_pid
, const char *signal
, MonoStackHash
*hashes
, MERPStruct
*merp
)
393 mono_memory_barrier ();
394 g_assert (mono_merp_enabled ());
396 merp
->merpFilePath
= config
.merpFilePath
;
397 merp
->crashLogPath
= config
.crashLogPath
;
398 merp
->werXmlPath
= config
.werXmlPath
;
400 // If these aren't set, icall wasn't made
401 // don't do merp? / don't set the variable to use merp;
402 g_assert (config
.appBundleID
);
403 g_assert (config
.appVersion
);
404 merp
->bundleIDArg
= config
.appSignature
;
405 merp
->versionArg
= config
.appVersion
;
407 merp
->archArg
= get_merp_arch ();
408 merp
->exceptionArg
= parse_exception_type (signal
);
410 merp
->serviceNameArg
= config
.appBundleID
;
411 merp
->servicePathArg
= config
.appPath
;
413 merp
->moduleName
= "Mono Exception";
414 merp
->moduleVersion
= config
.moduleVersion
;
416 merp
->moduleOffset
= 0;
418 merp
->uiLidArg
= MONO_LOCALE_INVARIANT
;
419 #if defined (TARGET_OSX)
420 merp
->osVersion
= macos_version_string ();
422 merp
->osVersion
= kernel_version_string ();
425 // FIXME: THis is apple-only for now
426 merp
->systemManufacturer
= "apple";
427 get_apple_model ((char *) merp
->systemModel
, sizeof (merp
->systemModel
));
429 merp
->eventType
= MONO_MERP_EVENT_TYPE_STR
;
431 merp
->hashes
= *hashes
;
433 merp
->annotations
= config
.annotations
;
437 mono_merp_write_fingerprint_payload (const char *non_param_data
, const MERPStruct
*merp
)
439 int handle
= g_open (merp
->crashLogPath
, O_TRUNC
| O_WRONLY
| O_CREAT
, merp_file_permissions
);
440 g_assertf (handle
!= -1, "Could not open crash log file at %s", merp
->crashLogPath
);
442 g_async_safe_fprintf(handle
, "{\n");
443 g_async_safe_fprintf(handle
, "\t\"payload\" : \n");
444 g_write (handle
, non_param_data
, (guint32
)strlen (non_param_data
));
445 g_async_safe_fprintf(handle
, ",\n");
447 g_async_safe_fprintf(handle
, "\t\"parameters\" : \n{\n");
448 g_async_safe_fprintf(handle
, "\t\t\"ApplicationBundleId\" : \"%s\",\n", merp
->bundleIDArg
);
449 g_async_safe_fprintf(handle
, "\t\t\"ApplicationVersion\" : \"%s\",\n", merp
->versionArg
);
450 g_async_safe_fprintf(handle
, "\t\t\"ApplicationBitness\" : \"%s\",\n", get_merp_bitness (merp
->archArg
));
451 g_async_safe_fprintf(handle
, "\t\t\"ApplicationName\" : \"%s\",\n", merp
->serviceNameArg
);
452 g_async_safe_fprintf(handle
, "\t\t\"BlameModuleName\" : \"%s\",\n", merp
->moduleName
);
453 g_async_safe_fprintf(handle
, "\t\t\"BlameModuleVersion\" : \"%s\",\n", merp
->moduleVersion
);
454 g_async_safe_fprintf(handle
, "\t\t\"BlameModuleOffset\" : \"0x%lx\",\n", merp
->moduleOffset
);
455 g_async_safe_fprintf(handle
, "\t\t\"ExceptionType\" : \"%s\",\n", get_merp_exctype (merp
->exceptionArg
));
456 g_async_safe_fprintf(handle
, "\t\t\"StackChecksum\" : \"0x%llx\",\n", merp
->hashes
.offset_free_hash
);
457 g_async_safe_fprintf(handle
, "\t\t\"StackHash\" : \"0x%llx\",\n", merp
->hashes
.offset_rich_hash
);
458 g_async_safe_fprintf(handle
, "\t\t\"Extra\" : \n\t\t{\n");
460 for (GSList
*cursor
= merp
->annotations
; cursor
; cursor
= cursor
->next
) {
461 MonoMerpAnnotationEntry
*iter
= (MonoMerpAnnotationEntry
*) cursor
->data
;
462 g_async_safe_fprintf(handle
, "\t\t\t\"%s\" : \"%s\"\n", iter
->key
, iter
->value
);
465 g_async_safe_fprintf(handle
, "\t\t},\n");
468 g_async_safe_fprintf(handle
, "\t\t\"OSVersion\" : \"%s\",\n", merp
->osVersion
);
469 g_async_safe_fprintf(handle
, "\t\t\"LanguageID\" : \"0x%x\",\n", merp
->uiLidArg
);
470 g_async_safe_fprintf(handle
, "\t\t\"SystemManufacturer\" : \"%s\",\n", merp
->systemManufacturer
);
471 g_async_safe_fprintf(handle
, "\t\t\"SystemModel\" : \"%s\",\n", merp
->systemModel
);
472 g_async_safe_fprintf(handle
, "\t\t\"EventType\" : \"%s\"\n", merp
->eventType
);
475 g_async_safe_fprintf(handle
, "\t}\n");
476 g_async_safe_fprintf(handle
, "}\n");
485 mono_write_wer_template (MERPStruct
*merp
)
487 // Note about missing ProcessInformation block: we have no PID that makes sense
488 // and when mono is embedded and used to run functions without an entry point,
489 // there is no image that would make any semantic sense to send either.
490 // It's a nuanced problem, each way we can run mono would need a separate fix.
492 int handle
= g_open (merp
->werXmlPath
, O_WRONLY
| O_CREAT
| O_TRUNC
, merp_file_permissions
);
493 g_assertf (handle
!= -1, "Could not open WER XML file at %s", merp
->werXmlPath
);
496 g_async_safe_fprintf(handle
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
497 g_async_safe_fprintf(handle
, "<WERReportMetadata>\n");
498 g_async_safe_fprintf(handle
, "<ProblemSignatures>\n");
499 g_async_safe_fprintf(handle
, "<EventType>%s</EventType>\n", merp
->eventType
);
502 g_async_safe_fprintf(handle
, "<Parameter%d>%s</Parameter%d>\n", i
, merp
->bundleIDArg
, i
);
504 g_async_safe_fprintf(handle
, "<Parameter%d>%s</Parameter%d>\n", i
, merp
->versionArg
, i
);
506 g_async_safe_fprintf(handle
, "<Parameter%d>%s</Parameter%d>\n", i
, get_merp_bitness (merp
->archArg
), i
);
508 g_async_safe_fprintf(handle
, "<Parameter%d>%s</Parameter%d>\n", i
, merp
->serviceNameArg
, i
);
510 g_async_safe_fprintf(handle
, "<Parameter%d>%s</Parameter%d>\n", i
, merp
->moduleName
, i
);
512 g_async_safe_fprintf(handle
, "<Parameter%d>%s</Parameter%d>\n", i
, merp
->moduleVersion
, i
);
514 g_async_safe_fprintf(handle
, "<Parameter%d>0x%zx</Parameter%d>\n", i
, merp
->moduleOffset
, i
);
516 g_async_safe_fprintf(handle
, "<Parameter%d>%s</Parameter%d>\n", i
, get_merp_exctype (merp
->exceptionArg
), i
);
518 g_async_safe_fprintf(handle
, "<Parameter%d>0x%llx</Parameter%d>\n", i
, merp
->hashes
.offset_free_hash
, i
);
520 g_async_safe_fprintf(handle
, "<Parameter%d>0x%llx</Parameter%d>\n", i
, merp
->hashes
.offset_rich_hash
, i
);
522 g_async_safe_fprintf(handle
, "<Parameter%d>%s</Parameter%d>\n", i
, merp
->osVersion
, i
);
524 g_async_safe_fprintf(handle
, "<Parameter%d>0x%x</Parameter%d>\n", i
, merp
->uiLidArg
, i
);
526 g_async_safe_fprintf(handle
, "<Parameter%d>%s</Parameter%d>\n", i
, merp
->systemManufacturer
, i
);
528 g_async_safe_fprintf(handle
, "<Parameter%d>%s</Parameter%d>\n", i
, merp
->systemModel
, i
);
531 g_async_safe_fprintf(handle
, "</ProblemSignatures>\n");
532 g_async_safe_fprintf(handle
, "</WERReportMetadata>\n");
541 mono_merp_invoke (const intptr_t crashed_pid
, const char *signal
, const char *non_param_data
, MonoStackHash
*hashes
)
544 int merp_tmp_file_tag
= 2;
545 gboolean alloc_success
= mono_state_alloc_mem (&mem
, merp_tmp_file_tag
, sizeof (MERPStruct
));
549 MERPStruct
*merp
= (MERPStruct
*) mem
.mem
;
550 memset (merp
, 0, sizeof (*merp
));
552 mono_summarize_timeline_phase_log (MonoSummaryMerpWriter
);
554 mono_init_merp (crashed_pid
, signal
, hashes
, merp
);
556 if (!mono_merp_write_params (merp
))
559 if (!mono_merp_write_fingerprint_payload (non_param_data
, merp
))
562 if (!mono_write_wer_template (merp
))
566 mono_summarize_timeline_phase_log (MonoSummaryMerpInvoke
);
567 gboolean success
= mono_merp_send (merp
);
570 mono_summarize_timeline_phase_log (MonoSummaryCleanup
);
572 mono_state_free_mem (&mem
);
578 mono_merp_add_annotation (const char *key
, const char *value
)
580 MonoMerpAnnotationEntry
*entry
= g_new0 (MonoMerpAnnotationEntry
, 1);
581 entry
->key
= g_strdup (key
);
582 entry
->value
= g_strdup (value
);
583 config
.annotations
= g_slist_prepend (config
.annotations
, entry
);
587 mono_merp_disable (void)
589 mono_memory_barrier ();
591 if (!config
.enable_merp
)
594 g_free ((char*)config
.appBundleID
); // cast away const
595 g_free ((char*)config
.appSignature
);
596 g_free ((char*)config
.appVersion
);
597 g_free ((char*)config
.merpGUIPath
);
598 g_free ((char*)config
.appPath
);
599 g_free ((char*)config
.moduleVersion
);
600 g_slist_free (config
.annotations
);
601 memset (&config
, 0, sizeof (config
));
603 mono_memory_barrier ();
607 mono_merp_enable (const char *appBundleID
, const char *appSignature
, const char *appVersion
, const char *merpGUIPath
, const char *appPath
, const char *configDir
)
609 mono_memory_barrier ();
611 g_assert (!config
.enable_merp
);
616 const char *home
= g_get_home_dir ();
617 prefix
= g_strdup_printf ("%s/Library/Group Containers/UBF8T346G9.ms/", home
);
619 prefix
= g_strdup (configDir
);
621 config
.merpFilePath
= g_strdup_printf ("%s%s", prefix
, "MERP.uploadparams.txt");
622 config
.crashLogPath
= g_strdup_printf ("%s%s", prefix
, "lastcrashlog.txt");
623 config
.werXmlPath
= g_strdup_printf ("%s%s", prefix
, "CustomLogsMetadata.xml");
626 config
.moduleVersion
= mono_get_runtime_callbacks ()->get_runtime_build_info ();
628 config
.appBundleID
= g_strdup (appBundleID
);
629 config
.appSignature
= g_strdup (appSignature
);
630 config
.appVersion
= g_strdup (appVersion
);
631 config
.merpGUIPath
= g_strdup (merpGUIPath
);
632 config
.appPath
= g_strdup (appPath
);
634 config
.log
= g_getenv ("MONO_MERP_VERBOSE") != NULL
;
636 config
.enable_merp
= TRUE
;
638 mono_memory_barrier ();
642 mono_merp_enabled (void)
644 return config
.enable_merp
;
649 #include <mono/utils/mono-compiler.h>
651 MONO_EMPTY_SOURCE_FILE (mono_merp
);