[interp] Remove unreachable code (#12411)
[mono-project.git] / mono / utils / mono-merp.c
blobef53dbb995db454268d20f9c9806e30349e093c5
1 /**
2 * \file
3 * Support for interop with the Microsoft Error Reporting tool
5 * Author:
6 * Alexander Kyte (alkyte@microsoft.com)
8 * (C) 2018 Microsoft, Inc.
12 #include <config.h>
13 #include <glib.h>
15 #if defined(TARGET_OSX) && !defined(DISABLE_CRASH_REPORTING)
16 #include "mono-merp.h"
18 #include <unistd.h>
19 #include <spawn.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>
29 #include <mini/jit.h>
31 #if defined(HAVE_SYS_UTSNAME_H)
32 #include <sys/utsname.h>
33 #endif
35 // To get the apple machine model
36 #include <sys/param.h>
37 #include <sys/sysctl.h>
38 #include <fcntl.h>
40 #include <mono/utils/json.h>
41 #include <mono/utils/mono-state.h>
42 #include <utils/mono-threads-debug.h>
44 static const char *
45 os_version_string (void)
47 #ifdef HAVE_SYS_UTSNAME_H
48 struct utsname name;
50 memset (&name, 0, sizeof (name)); // WSL does not always nul terminate.
52 if (uname (&name) >= 0)
53 return g_strdup_printf ("%s", name.release);
54 #endif
55 return "";
58 // To get the path of the running process
59 #include <libproc.h>
61 typedef enum {
62 MerpArchInvalid = 0,
64 MerpArchx86_64 = 1,
65 MerpArchx86 = 2,
66 MerpArchPPC = 3,
67 MerpArchPPC64 = 4
68 } MerpArch;
70 typedef enum
72 MERP_EXC_NONE = 0,
74 MERP_EXC_FORCE_QUIT = 1,
75 MERP_EXC_SIGSEGV = 2,
76 MERP_EXC_SIGABRT = 3,
77 MERP_EXC_SIGSYS = 4,
78 MERP_EXC_SIGILL = 5,
79 MERP_EXC_SIGBUS = 6,
80 MERP_EXC_SIGFPE = 7 ,
81 MERP_EXC_SIGTRAP = 8,
82 MERP_EXC_SIGKILL = 9,
83 MERP_EXC_HANG = 10
84 } MERPExcType;
86 typedef struct {
87 const char *merpFilePath;
88 const char *crashLogPath;
89 const char *werXmlPath;
91 const char *bundleIDArg; // App Bundle ID (required for bucketization)
92 const char *versionArg; // App Version (required for bucketization)
94 MerpArch archArg; // Arch, MacOS only, bails out if not found also required for bucketization. (required)
95 MERPExcType exceptionArg; // Exception type (refer to merpcommon.h and mach/exception_types.h for more info (optional)
97 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)
98 const char *servicePathArg; // The path to the executable, used to relaunch the crashed app.
100 const char *moduleName;
101 const char *moduleVersion;
102 size_t moduleOffset;
104 const char *osVersion;
105 int uiLidArg; // MONO_LOCALE_INVARIANT 0x007F
107 char systemModel [100];
108 const char *systemManufacturer;
110 const char *eventType;
112 MonoStackHash hashes;
113 GSList *annotations;
114 } MERPStruct;
116 typedef struct {
117 gboolean enable_merp;
119 const char *appBundleID;
120 const char *appPath;
121 const char *appSignature;
122 const char *appVersion;
123 const char *merpGUIPath;
124 const char *eventType;
125 const char *merpFilePath;
126 const char *crashLogPath;
127 const char *werXmlPath;
128 const char *moduleVersion;
130 gboolean log;
131 GSList *annotations;
132 } MerpOptions;
134 static MerpOptions config;
136 typedef struct {
137 char *key;
138 char *value;
139 } MonoMerpAnnotationEntry;
141 static const char *
142 get_merp_bitness (MerpArch arch)
144 switch (arch) {
145 case MerpArchx86_64:
146 return "x64";
147 case MerpArchx86:
148 return "x32";
149 default:
150 g_assert_not_reached ();
154 static MerpArch
155 get_merp_arch (void)
157 #ifdef TARGET_X86
158 return MerpArchx86;
159 #elif defined(TARGET_AMD64)
160 return MerpArchx86_64;
161 #elif defined(TARGET_POWERPC)
162 return MerpArchPPC;
163 #elif defined(TARGET_POWERPC64)
164 return MerpArchPPC64;
165 #else
166 g_assert_not_reached ();
167 #endif
170 static const char *
171 get_merp_exctype (MERPExcType exc)
173 switch (exc) {
174 case MERP_EXC_FORCE_QUIT:
175 return "0x10000000";
176 case MERP_EXC_SIGSEGV:
177 return "0x20000000";
178 case MERP_EXC_SIGABRT:
179 return "0x30000000";
180 case MERP_EXC_SIGSYS:
181 return "0x40000000";
182 case MERP_EXC_SIGILL:
183 return "0x50000000";
184 case MERP_EXC_SIGBUS:
185 return "0x60000000";
186 case MERP_EXC_SIGFPE:
187 return "0x70000000";
188 case MERP_EXC_SIGTRAP:
189 return "0x03000000";
190 case MERP_EXC_SIGKILL:
191 return "0x04000000";
192 case MERP_EXC_HANG:
193 return "0x02000000";
194 case MERP_EXC_NONE:
195 // Exception type documented as optional, not optional
196 g_assert_not_reached ();
197 default:
198 g_assert_not_reached ();
202 static MERPExcType
203 parse_exception_type (const char *signal)
205 if (!strcmp (signal, "SIGSEGV"))
206 return MERP_EXC_SIGSEGV;
208 if (!strcmp (signal, "SIGFPE"))
209 return MERP_EXC_SIGFPE;
211 if (!strcmp (signal, "SIGILL"))
212 return MERP_EXC_SIGILL;
214 if (!strcmp (signal, "SIGABRT"))
215 return MERP_EXC_SIGABRT;
217 // Force quit == hang?
218 // We need a default for this
219 if (!strcmp (signal, "SIGTERM"))
220 return MERP_EXC_HANG;
222 // FIXME: There are no other such signal
223 // strings passed to mono_handle_native_crash at the
224 // time of writing this
225 g_error ("Merp doesn't know how to handle %s\n", signal);
228 static int merp_file_permissions = S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH;
230 static gboolean
231 mono_merp_write_params (MERPStruct *merp)
233 int handle = g_open (merp->merpFilePath, O_TRUNC | O_WRONLY | O_CREAT, merp_file_permissions);
234 g_assertf (handle != -1, "Could not open MERP file at %s", merp->merpFilePath);
236 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "ApplicationBundleId: %s\n", merp->bundleIDArg);
237 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "ApplicationVersion: %s\n", merp->versionArg);
238 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "ApplicationBitness: %s\n", get_merp_bitness (merp->archArg));
240 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "ApplicationName: %s\n", merp->serviceNameArg);
241 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "ApplicationPath: %s\n", merp->servicePathArg);
242 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "BlameModuleName: %s\n", merp->moduleName);
243 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "BlameModuleVersion: %s\n", merp->moduleVersion);
244 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "BlameModuleOffset: 0x%llx\n", (unsigned long long)merp->moduleOffset);
245 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "ExceptionType: %s\n", get_merp_exctype (merp->exceptionArg));
246 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "StackChecksum: 0x%llx\n", merp->hashes.offset_free_hash);
247 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "StackHash: 0x%llx\n", merp->hashes.offset_rich_hash);
249 // Provided by icall
250 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "OSVersion: %s\n", merp->osVersion);
251 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "LanguageID: 0x%x\n", merp->uiLidArg);
252 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "SystemManufacturer: %s\n", merp->systemManufacturer);
253 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "SystemModel: %s\n", merp->systemModel);
254 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "EventType: %s\n", merp->eventType);
256 close (handle);
257 return TRUE;
260 static gboolean
261 mono_merp_send (MERPStruct *merp)
263 gboolean invoke_success = FALSE;
265 #if defined(HAVE_EXECV) && defined(HAVE_FORK)
266 pid_t pid = (pid_t) fork ();
268 // Only one we define on OSX
269 if (pid == 0) {
270 const char *open_path = "/usr/bin/open";
271 const char *argvOpen[] = {open_path, "-a", config.merpGUIPath, NULL};
272 execv (open_path, (char**)argvOpen);
273 exit (-1);
274 } else {
275 int status;
276 waitpid (pid, &status, 0);
277 gboolean exit_success = FALSE;
278 int exit_status = FALSE;
280 while (TRUE) {
281 if (waitpid(pid, &status, WUNTRACED | WCONTINUED) == -1)
282 break;
284 if (WIFEXITED(status)) {
285 exit_status = WEXITSTATUS(status);
286 exit_success = TRUE;
287 invoke_success = exit_status == TRUE;
288 break;
289 } else if (WIFSIGNALED(status)) {
290 break;
295 // // Create process to launch merp gui application
296 #endif
298 return invoke_success;
301 static void
302 get_apple_model (char *buffer, size_t max_length)
304 size_t sz = 0;
306 // Get the number of bytes to copy
307 sysctlbyname("hw.model", NULL, &sz, NULL, 0);
309 if (sz > max_length) {
310 buffer[0] = '\0';
311 return;
314 sysctlbyname("hw.model", buffer, &sz, NULL, 0);
317 static void
318 mono_init_merp (const intptr_t crashed_pid, const char *signal, MonoStackHash *hashes, MERPStruct *merp)
320 g_assert (mono_merp_enabled ());
322 merp->merpFilePath = config.merpFilePath;
323 merp->crashLogPath = config.crashLogPath;
324 merp->werXmlPath = config.werXmlPath;
326 // If these aren't set, icall wasn't made
327 // don't do merp? / don't set the variable to use merp;
328 g_assert (config.appBundleID);
329 g_assert (config.appVersion);
330 merp->bundleIDArg = config.appSignature;
331 merp->versionArg = config.appVersion;
333 merp->archArg = get_merp_arch ();
334 merp->exceptionArg = parse_exception_type (signal);
336 merp->serviceNameArg = config.appBundleID;
337 merp->servicePathArg = config.appPath;
339 merp->moduleName = "Mono Exception";
340 merp->moduleVersion = config.moduleVersion;
342 merp->moduleOffset = 0;
344 merp->uiLidArg = MONO_LOCALE_INVARIANT;
346 merp->osVersion = os_version_string ();
348 // FIXME: THis is apple-only for now
349 merp->systemManufacturer = "apple";
350 get_apple_model ((char *) merp->systemModel, sizeof (merp->systemModel));
352 merp->eventType = config.eventType;
354 merp->hashes = *hashes;
356 merp->annotations = config.annotations;
359 static gboolean
360 mono_merp_write_fingerprint_payload (const char *non_param_data, const MERPStruct *merp)
362 int handle = g_open (merp->crashLogPath, O_TRUNC | O_WRONLY | O_CREAT, merp_file_permissions);
363 g_assertf (handle != -1, "Could not open crash log file at %s", merp->crashLogPath);
365 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "{\n");
366 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\"payload\" : \n");
367 g_write (handle, non_param_data, (guint32)strlen (non_param_data)); \
368 MOSTLY_ASYNC_SAFE_FPRINTF(handle, ",\n");
370 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\"parameters\" : \n{\n");
371 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"ApplicationBundleId\" : \"%s\",\n", merp->bundleIDArg);
372 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"ApplicationVersion\" : \"%s\",\n", merp->versionArg);
373 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"ApplicationBitness\" : \"%s\",\n", get_merp_bitness (merp->archArg));
374 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"ApplicationName\" : \"%s\",\n", merp->serviceNameArg);
375 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"BlameModuleName\" : \"%s\",\n", merp->moduleName);
376 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"BlameModuleVersion\" : \"%s\",\n", merp->moduleVersion);
377 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"BlameModuleOffset\" : \"0x%lx\",\n", merp->moduleOffset);
378 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"ExceptionType\" : \"%s\",\n", get_merp_exctype (merp->exceptionArg));
379 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"StackChecksum\" : \"0x%llx\",\n", merp->hashes.offset_free_hash);
380 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"StackHash\" : \"0x%llx\",\n", merp->hashes.offset_rich_hash);
381 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"Extra\" : \n\t\t{\n");
383 for (GSList *cursor = merp->annotations; cursor; cursor = cursor->next) {
384 MonoMerpAnnotationEntry *iter = (MonoMerpAnnotationEntry *) cursor->data;
385 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\t\"%s\" : \"%s\"\n", iter->key, iter->value);
388 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t},\n");
390 // Provided by icall
391 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"OSVersion\" : \"%s\",\n", merp->osVersion);
392 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"LanguageID\" : \"0x%x\",\n", merp->uiLidArg);
393 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"SystemManufacturer\" : \"%s\",\n", merp->systemManufacturer);
394 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"SystemModel\" : \"%s\",\n", merp->systemModel);
395 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t\t\"EventType\" : \"%s\"\n", merp->eventType);
397 // End of parameters
398 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "\t}\n");
399 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "}\n");
401 // End of object
402 close (handle);
404 return TRUE;
407 static gboolean
408 mono_write_wer_template (MERPStruct *merp)
410 // Note about missing ProcessInformation block: we have no PID that makes sense
411 // and when mono is embedded and used to run functions without an entry point,
412 // there is no image that would make any semantic sense to send either.
413 // It's a nuanced problem, each way we can run mono would need a separate fix.
415 int handle = g_open (merp->werXmlPath, O_WRONLY | O_CREAT | O_TRUNC, merp_file_permissions);
416 g_assertf (handle != -1, "Could not open WER XML file at %s", merp->werXmlPath);
418 // Provided by icall
419 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
420 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<WERReportMetadata>\n");
421 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<ProblemSignatures>\n");
422 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<EventType>%s</EventType>\n", merp->eventType);
424 int i=0;
425 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->bundleIDArg, i);
426 i++;
427 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->versionArg, i);
428 i++;
429 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<Parameter%d>%s</Parameter%d>\n", i, get_merp_bitness (merp->archArg), i);
430 i++;
431 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->serviceNameArg, i);
432 i++;
433 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->moduleName, i);
434 i++;
435 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->moduleVersion, i);
436 i++;
437 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<Parameter%d>0x%zx</Parameter%d>\n", i, merp->moduleOffset, i);
438 i++;
439 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<Parameter%d>%s</Parameter%d>\n", i, get_merp_exctype (merp->exceptionArg), i);
440 i++;
441 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<Parameter%d>0x%llx</Parameter%d>\n", i, merp->hashes.offset_free_hash, i);
442 i++;
443 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<Parameter%d>0x%llx</Parameter%d>\n", i, merp->hashes.offset_rich_hash, i);
444 i++;
445 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->osVersion, i);
446 i++;
447 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<Parameter%d>0x%x</Parameter%d>\n", i, merp->uiLidArg, i);
448 i++;
449 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->systemManufacturer, i);
450 i++;
451 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->systemModel, i);
452 i++;
454 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "</ProblemSignatures>\n");
455 MOSTLY_ASYNC_SAFE_FPRINTF(handle, "</WERReportMetadata>\n");
457 close (handle);
459 return TRUE;
462 // Returns success
463 gboolean
464 mono_merp_invoke (const intptr_t crashed_pid, const char *signal, const char *non_param_data, MonoStackHash *hashes)
466 MERPStruct merp;
467 memset (&merp, 0, sizeof (merp));
469 mono_summarize_timeline_phase_log (MonoSummaryMerpWriter);
471 mono_init_merp (crashed_pid, signal, hashes, &merp);
472 if (!mono_merp_write_params (&merp))
473 return FALSE;
475 if (!mono_merp_write_fingerprint_payload (non_param_data, &merp))
476 return FALSE;
478 if (!mono_write_wer_template (&merp))
479 return FALSE;
481 // Start program
482 mono_summarize_timeline_phase_log (MonoSummaryMerpInvoke);
483 gboolean success = mono_merp_send (&merp);
485 if (success)
486 mono_summarize_timeline_phase_log (MonoSummaryCleanup);
488 return success;
491 void
492 mono_merp_add_annotation (const char *key, const char *value)
494 MonoMerpAnnotationEntry *entry = g_new0 (MonoMerpAnnotationEntry, 1);
495 entry->key = g_strdup (key);
496 entry->value = g_strdup (value);
497 config.annotations = g_slist_prepend (config.annotations, entry);
500 void
501 mono_merp_disable (void)
503 if (!config.enable_merp)
504 return;
506 g_free ((char*)config.appBundleID); // cast away const
507 g_free ((char*)config.appSignature);
508 g_free ((char*)config.appVersion);
509 g_free ((char*)config.merpGUIPath);
510 g_free ((char*)config.eventType);
511 g_free ((char*)config.appPath);
512 g_free ((char*)config.moduleVersion);
513 g_slist_free (config.annotations);
514 memset (&config, 0, sizeof (config));
517 void
518 mono_merp_enable (const char *appBundleID, const char *appSignature, const char *appVersion, const char *merpGUIPath, const char *eventType, const char *appPath, const char *configDir)
520 g_assert (!config.enable_merp);
522 char *prefix = NULL;
524 if (!configDir) {
525 const char *home = g_get_home_dir ();
526 prefix = g_strdup_printf ("%s/Library/Group Containers/UBF8T346G9.ms/", home);
527 } else {
528 prefix = g_strdup (configDir);
530 config.merpFilePath = g_strdup_printf ("%s%s", prefix, "MERP.uploadparams.txt");
531 config.crashLogPath = g_strdup_printf ("%s%s", prefix, "lastcrashlog.txt");
532 config.werXmlPath = g_strdup_printf ("%s%s", prefix, "CustomLogsMetadata.xml");
533 g_free (prefix);
535 config.moduleVersion = mono_get_runtime_callbacks ()->get_runtime_build_info ();
537 config.appBundleID = g_strdup (appBundleID);
538 config.appSignature = g_strdup (appSignature);
539 config.appVersion = g_strdup (appVersion);
540 config.merpGUIPath = g_strdup (merpGUIPath);
541 config.eventType = g_strdup (eventType);
542 config.appPath = g_strdup (appPath);
544 config.log = g_getenv ("MONO_MERP_VERBOSE") != NULL;
546 config.enable_merp = TRUE;
549 gboolean
550 mono_merp_enabled (void)
552 return config.enable_merp;
555 #endif // TARGET_OSX