3 * Support for interop with the Microsoft Error Reporting tool
6 * Alexander Kyte (alkyte@microsoft.com)
8 * (C) 2018 Microsoft, Inc.
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>
39 #include <mono/utils/json.h>
42 os_version_string (void)
44 #ifdef HAVE_SYS_UTSNAME_H
47 memset (&name
, 0, sizeof (name
)); // WSL does not always nul terminate.
49 if (uname (&name
) >= 0)
50 return g_strdup_printf ("%s", name
.release
);
55 // To get the path of the running process
71 MERP_EXC_FORCE_QUIT
= 1,
84 const char *bundleIDArg
; // App Bundle ID (required for bucketization)
85 const char *versionArg
; // App Version (required for bucketization)
87 MerpArch archArg
; // Arch, MacOS only, bails out if not found also required for bucketization. (required)
88 MERPExcType exceptionArg
; // Exception type (refer to merpcommon.h and mach/exception_types.h for more info (optional)
90 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)
92 const char *moduleName
;
93 const char *moduleVersion
;
96 const char *osVersion
;
97 int uiLidArg
; // Application LCID
99 const char systemModel
[100];
100 const char *systemManufacturer
;
102 const char *eventType
;
104 MonoStackHash hashes
;
108 gboolean enable_merp
;
110 const char *appBundleID
;
111 const char *appSignature
;
112 const char *appVersion
;
113 const char *merpGUIPath
;
117 static MerpOptions config
;
120 get_merp_bitness (MerpArch arch
)
128 g_assert_not_reached ();
137 #elif defined(TARGET_AMD64)
138 return MerpArchx86_64
;
139 #elif defined(TARGET_POWERPC)
141 #elif defined(TARGET_POWERPC64)
142 return MerpArchPPC64
;
144 g_assert_not_reached ();
149 get_merp_exctype (MERPExcType exc
)
152 case MERP_EXC_FORCE_QUIT
:
154 case MERP_EXC_SIGSEGV
:
156 case MERP_EXC_SIGABRT
:
158 case MERP_EXC_SIGSYS
:
160 case MERP_EXC_SIGILL
:
162 case MERP_EXC_SIGBUS
:
164 case MERP_EXC_SIGFPE
:
166 case MERP_EXC_SIGTRAP
:
168 case MERP_EXC_SIGKILL
:
173 // Exception type is optional
176 g_assert_not_reached ();
181 parse_exception_type (const char *signal
)
183 if (!strcmp (signal
, "SIGSEGV"))
184 return MERP_EXC_SIGSEGV
;
186 if (!strcmp (signal
, "SIGFPE"))
187 return MERP_EXC_SIGFPE
;
189 if (!strcmp (signal
, "SIGILL"))
190 return MERP_EXC_SIGILL
;
192 if (!strcmp (signal
, "SIGABRT"))
193 return MERP_EXC_SIGABRT
;
195 // FIXME: There are no other such signal
196 // strings passed to mono_handle_native_crash at the
197 // time of writing this
198 g_error ("Merp doesn't know how to handle %s\n", signal
);
202 mono_encode_merp_params (MERPStruct
*merp
)
204 GString
*output
= g_string_new ("");
207 g_string_append_printf (output
, "ApplicationBundleId: %s\n", merp
->bundleIDArg
);
208 g_string_append_printf (output
, "ApplicationVersion: %s\n", merp
->versionArg
);
210 g_string_append_printf (output
, "ApplicationBitness: %s\n", get_merp_bitness (merp
->archArg
));
213 g_string_append_printf (output
, "ApplicationName: %s\n", merp
->serviceNameArg
);
216 g_string_append_printf (output
, "BlameModuleName: %s\n", merp
->moduleName
);
217 g_string_append_printf (output
, "BlameModuleVersion: %s\n", merp
->moduleVersion
);
218 g_string_append_printf (output
, "BlameModuleOffset: 0x%x\n", merp
->moduleOffset
);
220 g_string_append_printf (output
, "ExceptionType: %s\n", get_merp_exctype (merp
->exceptionArg
));
222 g_string_append_printf (output
, "StackChecksum: 0x%x\n", merp
->hashes
.offset_free_hash
);
223 g_string_append_printf (output
, "StackHash: 0x%x\n", merp
->hashes
.offset_rich_hash
);
226 g_string_append_printf (output
, "OSVersion: %s\n", merp
->osVersion
);
227 g_string_append_printf (output
, "LanguageID: 0x%x\n", merp
->uiLidArg
);
228 g_string_append_printf (output
, "SystemManufacturer: %s\n", merp
->systemManufacturer
);
229 g_string_append_printf (output
, "SystemModel: %s\n", merp
->systemModel
);
230 g_string_append_printf (output
, "EventType: %s\n", merp
->eventType
);
232 return g_string_free (output
, FALSE
);
236 write_file (const char *payload
, const char *fileName
)
238 FILE *outfile
= fopen (fileName
, "w");
240 g_error ("Could not create file %s\n", fileName
);
241 fprintf (outfile
, "%s\n", payload
);
246 connect_to_merp (const char *serviceName
, mach_port_t
*merp_port
)
248 // // Create process to launch merp gui application
249 const char *argvOpen
[] = {"/usr/bin/open", "-a", config
.merpGUIPath
, NULL
};
250 int status
= posix_spawn(NULL
, "/usr/bin/open", NULL
, NULL
, (char *const*)(argvOpen
), NULL
);
252 // // FIXME error handling
253 g_assert (status
== 0);
258 mono_merp_send (const char *merpFile
, const char *crashLog
, const char *werXml
)
260 // Write struct to magic file location
261 // This registers our mach service so we can connect
262 // to the merp process
263 const char *home
= g_get_home_dir ();
264 char *merpParamPath
= g_strdup_printf ("%s/Library/Group Containers/UBF8T346G9.ms/MERP.uploadparams.txt", home
);
265 write_file (merpFile
, merpParamPath
);
266 g_free (merpParamPath
);
268 char *crashLogPath
= g_strdup_printf ("%s/Library/Group Containers/UBF8T346G9.ms/lastcrashlog.txt", home
);
269 write_file (crashLog
, crashLogPath
);
270 g_free (crashLogPath
);
272 char *werXmlPath
= g_strdup_printf ("%s/Library/Group Containers/UBF8T346G9.ms/CustomLogsMetadata.xml", home
);
273 write_file (werXml
, werXmlPath
);
277 if (merpFile
!= NULL
)
278 fprintf (stderr
, "Crashing MERP File:\n####\n%s\n####\n", merpFile
);
279 if (crashLog
!= NULL
)
280 fprintf (stderr
, "Crashing Dump File:\n####\n%s\n####\n", crashLog
);
282 fprintf (stderr
, "Crashing XML WER File:\n####\n%s\n####\n", werXmlPath
);
285 // // Create process to launch merp gui application
286 const char *argvOpen
[] = {"/usr/bin/open", "-a", config
.merpGUIPath
, NULL
};
287 int status
= posix_spawn(NULL
, "/usr/bin/open", NULL
, NULL
, (char *const*)(argvOpen
), NULL
);
289 // // FIXME error handling
291 g_error ("Could not start merp\n");
297 get_apple_model (char *buffer
, size_t max_length
)
301 // Get the number of bytes to copy
302 sysctlbyname("hw.model", NULL
, &sz
, NULL
, 0);
304 if (sz
> max_length
) {
309 sysctlbyname("hw.model", buffer
, &sz
, NULL
, 0);
314 mono_init_merp (const intptr_t crashed_pid
, const char *signal
, MonoStackHash
*hashes
, MERPStruct
*merp
, const char *version
)
316 g_assert (mono_merp_enabled ());
318 // If these aren't set, icall wasn't made
319 // don't do merp? / don't set the variable to use merp;
320 g_assert (config
.appBundleID
);
321 g_assert (config
.appVersion
);
322 merp
->bundleIDArg
= config
.appSignature
;
323 merp
->versionArg
= config
.appVersion
;
325 merp
->archArg
= get_merp_arch ();
326 merp
->exceptionArg
= parse_exception_type (signal
);
328 merp
->serviceNameArg
= config
.appBundleID
;
330 merp
->moduleName
= "Mono Exception";
331 merp
->moduleVersion
= version
;
333 merp
->moduleOffset
= 0;
336 merp
->uiLidArg
= ves_icall_System_Threading_Thread_current_lcid (error
);
337 mono_error_assert_ok (error
);
339 merp
->osVersion
= os_version_string ();
341 // FIXME: THis is apple-only for now
342 merp
->systemManufacturer
= "apple";
343 get_apple_model ((char *) merp
->systemModel
, sizeof (merp
->systemModel
));
345 merp
->eventType
= "MonoAppCrash";
347 merp
->hashes
= *hashes
;
351 mono_merp_fingerprint_payload (const char *non_param_data
, const MERPStruct
*merp
)
354 mono_json_writer_init (&writer
);
356 mono_json_writer_object_begin(&writer
);
358 mono_json_writer_indent (&writer
);
359 mono_json_writer_object_key(&writer
, "payload");
360 mono_json_writer_printf (&writer
, "%s,\n", non_param_data
);
362 mono_json_writer_indent (&writer
);
363 mono_json_writer_object_key(&writer
, "parameters");
364 mono_json_writer_object_begin(&writer
);
366 mono_json_writer_indent (&writer
);
367 mono_json_writer_object_key(&writer
, "ApplicationBundleId:");
368 mono_json_writer_printf (&writer
, "\"%s\",\n", merp
->bundleIDArg
);
370 mono_json_writer_indent (&writer
);
371 mono_json_writer_object_key(&writer
, "ApplicationVersion:");
372 mono_json_writer_printf (&writer
, "\"%s\",\n", merp
->versionArg
);
374 mono_json_writer_indent (&writer
);
375 mono_json_writer_object_key(&writer
, "ApplicationBitness:");
376 mono_json_writer_printf (&writer
, "\"%s\",\n", get_merp_bitness (merp
->archArg
));
378 mono_json_writer_indent (&writer
);
379 mono_json_writer_object_key(&writer
, "ApplicationName:");
380 mono_json_writer_printf (&writer
, "\"%s\",\n", merp
->serviceNameArg
);
382 mono_json_writer_indent (&writer
);
383 mono_json_writer_object_key(&writer
, "BlameModuleName:");
384 mono_json_writer_printf (&writer
, "\"%s\",\n", merp
->moduleName
);
386 mono_json_writer_indent (&writer
);
387 mono_json_writer_object_key(&writer
, "BlameModuleVersion:");
388 mono_json_writer_printf (&writer
, "\"%s\",\n", merp
->moduleVersion
);
390 mono_json_writer_indent (&writer
);
391 mono_json_writer_object_key(&writer
, "BlameModuleOffset:");
392 mono_json_writer_printf (&writer
, "\"0x%x\",\n", merp
->moduleOffset
);
394 mono_json_writer_indent (&writer
);
395 mono_json_writer_object_key(&writer
, "ExceptionType:");
396 mono_json_writer_printf (&writer
, "\"%s\",\n", get_merp_exctype (merp
->exceptionArg
));
398 mono_json_writer_indent (&writer
);
399 mono_json_writer_object_key(&writer
, "StackChecksum:");
400 mono_json_writer_printf (&writer
, "\"0x%x\",\n", merp
->hashes
.offset_free_hash
);
402 mono_json_writer_indent (&writer
);
403 mono_json_writer_object_key(&writer
, "StackHash:");
404 mono_json_writer_printf (&writer
, "\"0x%x\",\n", merp
->hashes
.offset_rich_hash
);
407 mono_json_writer_indent (&writer
);
408 mono_json_writer_object_key(&writer
, "OSVersion:");
409 mono_json_writer_printf (&writer
, "\"%s\",\n", merp
->osVersion
);
411 mono_json_writer_indent (&writer
);
412 mono_json_writer_object_key(&writer
, "LanguageID:");
413 mono_json_writer_printf (&writer
, "\"0x%x\",\n", merp
->uiLidArg
);
415 mono_json_writer_indent (&writer
);
416 mono_json_writer_object_key(&writer
, "SystemManufacturer:");
417 mono_json_writer_printf (&writer
, "\"%s\",\n", merp
->systemManufacturer
);
419 mono_json_writer_indent (&writer
);
420 mono_json_writer_object_key(&writer
, "SystemModel:");
421 mono_json_writer_printf (&writer
, "\"%s\"\n", merp
->systemModel
);
423 mono_json_writer_indent (&writer
);
424 mono_json_writer_object_key(&writer
, "EventType:");
425 mono_json_writer_printf (&writer
, "\"%s\"\n", merp
->eventType
);
428 mono_json_writer_indent (&writer
);
429 mono_json_writer_object_end (&writer
);
430 mono_json_writer_printf (&writer
, "\n");
433 mono_json_writer_indent_pop (&writer
);
434 mono_json_writer_indent (&writer
);
435 mono_json_writer_object_end (&writer
);
437 gchar
*output
= g_strdup (writer
.text
->str
);
438 mono_json_writer_destroy (&writer
);
444 mono_wer_template (MERPStruct
*merp
)
446 // Note about missing ProcessInformation block: we have no PID that makes sense
447 // and when mono is embedded and used to run functions without an entry point,
448 // there is no image that would make any semantic sense to send either.
449 // It's a nuanced problem, each way we can run mono would need a separate fix.
451 GString
*output
= g_string_new ("");
453 g_string_append_printf (output
, "<?xml version=\"1.0\" encoding=\"UTF-16\"?>\n");
454 g_string_append_printf (output
, "<WERReportMetadata>\n");
455 g_string_append_printf (output
, "<ProblemSignatures>\n");
456 g_string_append_printf (output
, "<EventType>%s</EventType>\n", merp
->eventType
);
460 g_string_append_printf (output
, "<Parameter%d>%s</Parameter0>\n", i
, merp
->bundleIDArg
, i
);
463 g_string_append_printf (output
, "<Parameter%d>%s</Parameter%d>\n", i
, merp
->versionArg
, i
);
466 g_string_append_printf (output
, "<Parameter%d>%s</Parameter%d>\n", i
, get_merp_bitness (merp
->archArg
), i
);
469 g_string_append_printf (output
, "<Parameter%d>%s</Parameter%d>\n", i
, merp
->serviceNameArg
, i
);
472 g_string_append_printf (output
, "<Parameter%d>%s</Parameter%d>\n", i
, merp
->moduleName
, i
);
475 g_string_append_printf (output
, "<Parameter%d>%s</Parameter%d>\n", i
, merp
->moduleVersion
, i
);
478 g_string_append_printf (output
, "<Parameter%d>0x%x</Parameter%d>\n", i
, merp
->moduleOffset
, i
);
481 g_string_append_printf (output
, "<Parameter%d>%s</Parameter%d>\n", i
, get_merp_exctype (merp
->exceptionArg
), i
);
484 g_string_append_printf (output
, "<Parameter%d>0x%x</Parameter%d>\n", i
, merp
->hashes
.offset_free_hash
, i
);
487 g_string_append_printf (output
, "<Parameter%d>0x%x</Parameter%d>\n", i
, merp
->hashes
.offset_rich_hash
, i
);
490 g_string_append_printf (output
, "<Parameter%d>%s</Parameter%d>\n", i
, merp
->osVersion
, i
);
493 g_string_append_printf (output
, "<Parameter%d>0x%x</Parameter%d>\n", i
, merp
->uiLidArg
, i
);
496 g_string_append_printf (output
, "<Parameter%d>%s</Parameter%d>\n", i
, merp
->systemManufacturer
, i
);
499 g_string_append_printf (output
, "<Parameter%d>%s</Parameter%d>\n", i
, merp
->systemModel
, i
);
502 g_string_append_printf (output
, "</ProblemSignatures>\n");
503 g_string_append_printf (output
, "</WERReportMetadata>\n");
505 return g_string_free (output
, FALSE
);
509 mono_merp_invoke (const intptr_t crashed_pid
, const char *signal
, const char *non_param_data
, MonoStackHash
*hashes
, char *version
)
512 memset (&merp
, 0, sizeof (merp
));
513 mono_init_merp (crashed_pid
, signal
, hashes
, &merp
, version
);
515 gchar
*merpCfg
= mono_encode_merp_params (&merp
);
516 gchar
*fullData
= mono_merp_fingerprint_payload (non_param_data
, &merp
);
517 gchar
*werXmlCfg
= mono_wer_template (&merp
);
519 // Write out to disk, start program
520 mono_merp_send (merpCfg
, fullData
, werXmlCfg
);
528 mono_merp_disable (void)
530 if (!config
.enable_merp
)
533 g_free ((char*)config
.appBundleID
); // cast away const
534 g_free ((char*)config
.appSignature
);
535 g_free ((char*)config
.appVersion
);
536 g_free ((char*)config
.merpGUIPath
);
537 memset (&config
, 0, sizeof (config
));
541 mono_merp_enable (const char *appBundleID
, const char *appSignature
, const char *appVersion
, const char *merpGUIPath
)
543 g_assert (!config
.enable_merp
);
545 config
.appBundleID
= g_strdup (appBundleID
);
546 config
.appSignature
= g_strdup (appSignature
);
547 config
.appVersion
= g_strdup (appVersion
);
548 config
.merpGUIPath
= g_strdup (merpGUIPath
);
550 config
.log
= g_getenv ("MONO_MERP_VERBOSE") != NULL
;
552 config
.enable_merp
= TRUE
;
556 mono_merp_enabled (void)
558 return config
.enable_merp
;