[netcore] Reorder test targets
[mono-project.git] / mono / utils / mono-merp.c
blobf85bae8dfa797e2e8c1735ed83fafc2e077216b1
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 static struct utsname name;
49 static const char *version_string;
51 if (!version_string) {
52 memset (&name, 0, sizeof (name)); // WSL does not always nul terminate.
54 if (uname (&name) >= 0)
55 version_string = name.release;
57 if (!version_string)
58 version_string = "";
60 return version_string;
61 #endif
62 return "";
65 // To get the path of the running process
66 #include <libproc.h>
68 typedef enum {
69 MerpArchInvalid = 0,
71 MerpArchx86_64 = 1,
72 MerpArchx86 = 2,
73 MerpArchPPC = 3,
74 MerpArchPPC64 = 4
75 } MerpArch;
77 typedef enum
79 MERP_EXC_NONE = 0,
81 MERP_EXC_FORCE_QUIT = 1,
82 MERP_EXC_SIGSEGV = 2,
83 MERP_EXC_SIGABRT = 3,
84 MERP_EXC_SIGSYS = 4,
85 MERP_EXC_SIGILL = 5,
86 MERP_EXC_SIGBUS = 6,
87 MERP_EXC_SIGFPE = 7 ,
88 MERP_EXC_SIGTRAP = 8,
89 MERP_EXC_SIGKILL = 9,
90 MERP_EXC_HANG = 10
91 } MERPExcType;
93 typedef struct {
94 const char *merpFilePath;
95 const char *crashLogPath;
96 const char *werXmlPath;
98 const char *bundleIDArg; // App Bundle ID (required for bucketization)
99 const char *versionArg; // App Version (required for bucketization)
101 MerpArch archArg; // Arch, MacOS only, bails out if not found also required for bucketization. (required)
102 MERPExcType exceptionArg; // Exception type (refer to merpcommon.h and mach/exception_types.h for more info (optional)
104 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)
105 const char *servicePathArg; // The path to the executable, used to relaunch the crashed app.
107 const char *moduleName;
108 const char *moduleVersion;
109 size_t moduleOffset;
111 const char *osVersion;
112 int uiLidArg; // MONO_LOCALE_INVARIANT 0x007F
114 char systemModel [100];
115 const char *systemManufacturer;
117 const char *eventType;
119 MonoStackHash hashes;
120 GSList *annotations;
121 } MERPStruct;
123 typedef struct {
124 gboolean enable_merp;
126 const char *appBundleID;
127 const char *appPath;
128 const char *appSignature;
129 const char *appVersion;
130 const char *merpGUIPath;
131 const char *eventType;
132 const char *merpFilePath;
133 const char *crashLogPath;
134 const char *werXmlPath;
135 const char *moduleVersion;
137 gboolean log;
138 GSList *annotations;
139 } MerpOptions;
141 static MerpOptions config;
143 typedef struct {
144 char *key;
145 char *value;
146 } MonoMerpAnnotationEntry;
148 static const char *
149 get_merp_bitness (MerpArch arch)
151 switch (arch) {
152 case MerpArchx86_64:
153 return "x64";
154 case MerpArchx86:
155 return "x32";
156 default:
157 g_assert_not_reached ();
161 static MerpArch
162 get_merp_arch (void)
164 #ifdef TARGET_X86
165 return MerpArchx86;
166 #elif defined(TARGET_AMD64)
167 return MerpArchx86_64;
168 #elif defined(TARGET_POWERPC)
169 return MerpArchPPC;
170 #elif defined(TARGET_POWERPC64)
171 return MerpArchPPC64;
172 #else
173 g_assert_not_reached ();
174 #endif
177 static const char *
178 get_merp_exctype (MERPExcType exc)
180 switch (exc) {
181 case MERP_EXC_FORCE_QUIT:
182 return "0x10000000";
183 case MERP_EXC_SIGSEGV:
184 return "0x20000000";
185 case MERP_EXC_SIGABRT:
186 return "0x30000000";
187 case MERP_EXC_SIGSYS:
188 return "0x40000000";
189 case MERP_EXC_SIGILL:
190 return "0x50000000";
191 case MERP_EXC_SIGBUS:
192 return "0x60000000";
193 case MERP_EXC_SIGFPE:
194 return "0x70000000";
195 case MERP_EXC_SIGTRAP:
196 return "0x03000000";
197 case MERP_EXC_SIGKILL:
198 return "0x04000000";
199 case MERP_EXC_HANG:
200 return "0x02000000";
201 case MERP_EXC_NONE:
202 // Exception type documented as optional, not optional
203 g_assert_not_reached ();
204 default:
205 g_assert_not_reached ();
209 static MERPExcType
210 parse_exception_type (const char *signal)
212 if (!strcmp (signal, "SIGSEGV"))
213 return MERP_EXC_SIGSEGV;
215 if (!strcmp (signal, "SIGFPE"))
216 return MERP_EXC_SIGFPE;
218 if (!strcmp (signal, "SIGILL"))
219 return MERP_EXC_SIGILL;
221 if (!strcmp (signal, "SIGABRT"))
222 return MERP_EXC_SIGABRT;
224 // Force quit == hang?
225 // We need a default for this
226 if (!strcmp (signal, "SIGTERM"))
227 return MERP_EXC_HANG;
229 // FIXME: There are no other such signal
230 // strings passed to mono_handle_native_crash at the
231 // time of writing this
232 g_error ("Merp doesn't know how to handle %s\n", signal);
235 static int merp_file_permissions = S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH;
237 static gboolean
238 mono_merp_write_params (MERPStruct *merp)
240 int handle = g_open (merp->merpFilePath, O_TRUNC | O_WRONLY | O_CREAT, merp_file_permissions);
241 g_assertf (handle != -1, "Could not open MERP file at %s", merp->merpFilePath);
243 g_async_safe_fprintf(handle, "ApplicationBundleId: %s\n", merp->bundleIDArg);
244 g_async_safe_fprintf(handle, "ApplicationVersion: %s\n", merp->versionArg);
245 g_async_safe_fprintf(handle, "ApplicationBitness: %s\n", get_merp_bitness (merp->archArg));
247 g_async_safe_fprintf(handle, "ApplicationName: %s\n", merp->serviceNameArg);
248 g_async_safe_fprintf(handle, "ApplicationPath: %s\n", merp->servicePathArg);
249 g_async_safe_fprintf(handle, "BlameModuleName: %s\n", merp->moduleName);
250 g_async_safe_fprintf(handle, "BlameModuleVersion: %s\n", merp->moduleVersion);
251 g_async_safe_fprintf(handle, "BlameModuleOffset: 0x%llx\n", (unsigned long long)merp->moduleOffset);
252 g_async_safe_fprintf(handle, "ExceptionType: %s\n", get_merp_exctype (merp->exceptionArg));
253 g_async_safe_fprintf(handle, "StackChecksum: 0x%llx\n", merp->hashes.offset_free_hash);
254 g_async_safe_fprintf(handle, "StackHash: 0x%llx\n", merp->hashes.offset_rich_hash);
256 // Provided by icall
257 g_async_safe_fprintf(handle, "OSVersion: %s\n", merp->osVersion);
258 g_async_safe_fprintf(handle, "LanguageID: 0x%x\n", merp->uiLidArg);
259 g_async_safe_fprintf(handle, "SystemManufacturer: %s\n", merp->systemManufacturer);
260 g_async_safe_fprintf(handle, "SystemModel: %s\n", merp->systemModel);
261 g_async_safe_fprintf(handle, "EventType: %s\n", merp->eventType);
263 close (handle);
264 return TRUE;
267 static gboolean
268 mono_merp_send (MERPStruct *merp)
270 gboolean invoke_success = FALSE;
272 #if defined(HAVE_EXECV) && defined(HAVE_FORK)
273 pid_t pid = (pid_t) fork ();
275 // Only one we define on OSX
276 if (pid == 0) {
277 const char *open_path = "/usr/bin/open";
278 const char *argvOpen[] = {open_path, "-a", config.merpGUIPath, NULL};
279 execv (open_path, (char**)argvOpen);
280 exit (-1);
281 } else {
282 int status;
283 waitpid (pid, &status, 0);
284 gboolean exit_success = FALSE;
285 int exit_status = FALSE;
287 while (TRUE) {
288 if (waitpid(pid, &status, WUNTRACED | WCONTINUED) == -1)
289 break;
291 if (WIFEXITED(status)) {
292 exit_status = WEXITSTATUS(status);
293 exit_success = TRUE;
294 invoke_success = exit_status == TRUE;
295 break;
296 } else if (WIFSIGNALED(status)) {
297 break;
302 // // Create process to launch merp gui application
303 #endif
305 return invoke_success;
308 static void
309 get_apple_model (char *buffer, size_t max_length)
311 size_t sz = 0;
313 // Get the number of bytes to copy
314 sysctlbyname("hw.model", NULL, &sz, NULL, 0);
316 if (sz > max_length) {
317 buffer[0] = '\0';
318 return;
321 sysctlbyname("hw.model", buffer, &sz, NULL, 0);
324 static void
325 mono_init_merp (const intptr_t crashed_pid, const char *signal, MonoStackHash *hashes, MERPStruct *merp)
327 mono_memory_barrier ();
328 g_assert (mono_merp_enabled ());
330 merp->merpFilePath = config.merpFilePath;
331 merp->crashLogPath = config.crashLogPath;
332 merp->werXmlPath = config.werXmlPath;
334 // If these aren't set, icall wasn't made
335 // don't do merp? / don't set the variable to use merp;
336 g_assert (config.appBundleID);
337 g_assert (config.appVersion);
338 merp->bundleIDArg = config.appSignature;
339 merp->versionArg = config.appVersion;
341 merp->archArg = get_merp_arch ();
342 merp->exceptionArg = parse_exception_type (signal);
344 merp->serviceNameArg = config.appBundleID;
345 merp->servicePathArg = config.appPath;
347 merp->moduleName = "Mono Exception";
348 merp->moduleVersion = config.moduleVersion;
350 merp->moduleOffset = 0;
352 merp->uiLidArg = MONO_LOCALE_INVARIANT;
354 merp->osVersion = os_version_string ();
356 // FIXME: THis is apple-only for now
357 merp->systemManufacturer = "apple";
358 get_apple_model ((char *) merp->systemModel, sizeof (merp->systemModel));
360 merp->eventType = config.eventType;
362 merp->hashes = *hashes;
364 merp->annotations = config.annotations;
367 static gboolean
368 mono_merp_write_fingerprint_payload (const char *non_param_data, const MERPStruct *merp)
370 int handle = g_open (merp->crashLogPath, O_TRUNC | O_WRONLY | O_CREAT, merp_file_permissions);
371 g_assertf (handle != -1, "Could not open crash log file at %s", merp->crashLogPath);
373 g_async_safe_fprintf(handle, "{\n");
374 g_async_safe_fprintf(handle, "\t\"payload\" : \n");
375 g_write (handle, non_param_data, (guint32)strlen (non_param_data)); \
376 g_async_safe_fprintf(handle, ",\n");
378 g_async_safe_fprintf(handle, "\t\"parameters\" : \n{\n");
379 g_async_safe_fprintf(handle, "\t\t\"ApplicationBundleId\" : \"%s\",\n", merp->bundleIDArg);
380 g_async_safe_fprintf(handle, "\t\t\"ApplicationVersion\" : \"%s\",\n", merp->versionArg);
381 g_async_safe_fprintf(handle, "\t\t\"ApplicationBitness\" : \"%s\",\n", get_merp_bitness (merp->archArg));
382 g_async_safe_fprintf(handle, "\t\t\"ApplicationName\" : \"%s\",\n", merp->serviceNameArg);
383 g_async_safe_fprintf(handle, "\t\t\"BlameModuleName\" : \"%s\",\n", merp->moduleName);
384 g_async_safe_fprintf(handle, "\t\t\"BlameModuleVersion\" : \"%s\",\n", merp->moduleVersion);
385 g_async_safe_fprintf(handle, "\t\t\"BlameModuleOffset\" : \"0x%lx\",\n", merp->moduleOffset);
386 g_async_safe_fprintf(handle, "\t\t\"ExceptionType\" : \"%s\",\n", get_merp_exctype (merp->exceptionArg));
387 g_async_safe_fprintf(handle, "\t\t\"StackChecksum\" : \"0x%llx\",\n", merp->hashes.offset_free_hash);
388 g_async_safe_fprintf(handle, "\t\t\"StackHash\" : \"0x%llx\",\n", merp->hashes.offset_rich_hash);
389 g_async_safe_fprintf(handle, "\t\t\"Extra\" : \n\t\t{\n");
391 for (GSList *cursor = merp->annotations; cursor; cursor = cursor->next) {
392 MonoMerpAnnotationEntry *iter = (MonoMerpAnnotationEntry *) cursor->data;
393 g_async_safe_fprintf(handle, "\t\t\t\"%s\" : \"%s\"\n", iter->key, iter->value);
396 g_async_safe_fprintf(handle, "\t\t},\n");
398 // Provided by icall
399 g_async_safe_fprintf(handle, "\t\t\"OSVersion\" : \"%s\",\n", merp->osVersion);
400 g_async_safe_fprintf(handle, "\t\t\"LanguageID\" : \"0x%x\",\n", merp->uiLidArg);
401 g_async_safe_fprintf(handle, "\t\t\"SystemManufacturer\" : \"%s\",\n", merp->systemManufacturer);
402 g_async_safe_fprintf(handle, "\t\t\"SystemModel\" : \"%s\",\n", merp->systemModel);
403 g_async_safe_fprintf(handle, "\t\t\"EventType\" : \"%s\"\n", merp->eventType);
405 // End of parameters
406 g_async_safe_fprintf(handle, "\t}\n");
407 g_async_safe_fprintf(handle, "}\n");
409 // End of object
410 close (handle);
412 return TRUE;
415 static gboolean
416 mono_write_wer_template (MERPStruct *merp)
418 // Note about missing ProcessInformation block: we have no PID that makes sense
419 // and when mono is embedded and used to run functions without an entry point,
420 // there is no image that would make any semantic sense to send either.
421 // It's a nuanced problem, each way we can run mono would need a separate fix.
423 int handle = g_open (merp->werXmlPath, O_WRONLY | O_CREAT | O_TRUNC, merp_file_permissions);
424 g_assertf (handle != -1, "Could not open WER XML file at %s", merp->werXmlPath);
426 // Provided by icall
427 g_async_safe_fprintf(handle, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
428 g_async_safe_fprintf(handle, "<WERReportMetadata>\n");
429 g_async_safe_fprintf(handle, "<ProblemSignatures>\n");
430 g_async_safe_fprintf(handle, "<EventType>%s</EventType>\n", merp->eventType);
432 int i=0;
433 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->bundleIDArg, i);
434 i++;
435 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->versionArg, i);
436 i++;
437 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, get_merp_bitness (merp->archArg), i);
438 i++;
439 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->serviceNameArg, i);
440 i++;
441 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->moduleName, i);
442 i++;
443 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->moduleVersion, i);
444 i++;
445 g_async_safe_fprintf(handle, "<Parameter%d>0x%zx</Parameter%d>\n", i, merp->moduleOffset, i);
446 i++;
447 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, get_merp_exctype (merp->exceptionArg), i);
448 i++;
449 g_async_safe_fprintf(handle, "<Parameter%d>0x%llx</Parameter%d>\n", i, merp->hashes.offset_free_hash, i);
450 i++;
451 g_async_safe_fprintf(handle, "<Parameter%d>0x%llx</Parameter%d>\n", i, merp->hashes.offset_rich_hash, i);
452 i++;
453 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->osVersion, i);
454 i++;
455 g_async_safe_fprintf(handle, "<Parameter%d>0x%x</Parameter%d>\n", i, merp->uiLidArg, i);
456 i++;
457 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->systemManufacturer, i);
458 i++;
459 g_async_safe_fprintf(handle, "<Parameter%d>%s</Parameter%d>\n", i, merp->systemModel, i);
460 i++;
462 g_async_safe_fprintf(handle, "</ProblemSignatures>\n");
463 g_async_safe_fprintf(handle, "</WERReportMetadata>\n");
465 close (handle);
467 return TRUE;
470 // Returns success
471 gboolean
472 mono_merp_invoke (const intptr_t crashed_pid, const char *signal, const char *non_param_data, MonoStackHash *hashes)
474 MonoStateMem mem;
475 int merp_tmp_file_tag = 2;
476 gboolean alloc_success = mono_state_alloc_mem (&mem, merp_tmp_file_tag, sizeof (MERPStruct));
477 if (!alloc_success)
478 return FALSE;
480 MERPStruct *merp = (MERPStruct *) mem.mem;
481 memset (merp, 0, sizeof (*merp));
483 mono_summarize_timeline_phase_log (MonoSummaryMerpWriter);
485 mono_init_merp (crashed_pid, signal, hashes, merp);
487 if (!mono_merp_write_params (merp))
488 return FALSE;
490 if (!mono_merp_write_fingerprint_payload (non_param_data, merp))
491 return FALSE;
493 if (!mono_write_wer_template (merp))
494 return FALSE;
496 // Start program
497 mono_summarize_timeline_phase_log (MonoSummaryMerpInvoke);
498 gboolean success = mono_merp_send (merp);
500 if (success)
501 mono_summarize_timeline_phase_log (MonoSummaryCleanup);
503 mono_state_free_mem (&mem);
505 return success;
508 void
509 mono_merp_add_annotation (const char *key, const char *value)
511 MonoMerpAnnotationEntry *entry = g_new0 (MonoMerpAnnotationEntry, 1);
512 entry->key = g_strdup (key);
513 entry->value = g_strdup (value);
514 config.annotations = g_slist_prepend (config.annotations, entry);
517 void
518 mono_merp_disable (void)
520 mono_memory_barrier ();
522 if (!config.enable_merp)
523 return;
525 g_free ((char*)config.appBundleID); // cast away const
526 g_free ((char*)config.appSignature);
527 g_free ((char*)config.appVersion);
528 g_free ((char*)config.merpGUIPath);
529 g_free ((char*)config.eventType);
530 g_free ((char*)config.appPath);
531 g_free ((char*)config.moduleVersion);
532 g_slist_free (config.annotations);
533 memset (&config, 0, sizeof (config));
535 mono_memory_barrier ();
538 void
539 mono_merp_enable (const char *appBundleID, const char *appSignature, const char *appVersion, const char *merpGUIPath, const char *eventType, const char *appPath, const char *configDir)
541 mono_memory_barrier ();
543 g_assert (!config.enable_merp);
545 char *prefix = NULL;
547 if (!configDir) {
548 const char *home = g_get_home_dir ();
549 prefix = g_strdup_printf ("%s/Library/Group Containers/UBF8T346G9.ms/", home);
550 } else {
551 prefix = g_strdup (configDir);
553 config.merpFilePath = g_strdup_printf ("%s%s", prefix, "MERP.uploadparams.txt");
554 config.crashLogPath = g_strdup_printf ("%s%s", prefix, "lastcrashlog.txt");
555 config.werXmlPath = g_strdup_printf ("%s%s", prefix, "CustomLogsMetadata.xml");
556 g_free (prefix);
558 config.moduleVersion = mono_get_runtime_callbacks ()->get_runtime_build_info ();
560 config.appBundleID = g_strdup (appBundleID);
561 config.appSignature = g_strdup (appSignature);
562 config.appVersion = g_strdup (appVersion);
563 config.merpGUIPath = g_strdup (merpGUIPath);
564 config.eventType = g_strdup (eventType);
565 config.appPath = g_strdup (appPath);
567 config.log = g_getenv ("MONO_MERP_VERBOSE") != NULL;
569 config.enable_merp = TRUE;
571 mono_memory_barrier ();
574 gboolean
575 mono_merp_enabled (void)
577 return config.enable_merp;
580 #endif // TARGET_OSX