[build] Skips RemoteExecuted bases tests on monodroid
[mono-project.git] / mono / utils / mono-merp.c
blob73031ab86ea146694c67e10340b90dfb2b94d5a8
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 #ifdef TARGET_OSX
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>
39 #include <mono/utils/json.h>
41 static const char *
42 os_version_string (void)
44 #ifdef HAVE_SYS_UTSNAME_H
45 struct utsname name;
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);
51 #endif
52 return "";
55 // To get the path of the running process
56 #include <libproc.h>
58 typedef enum {
59 MerpArchInvalid = 0,
61 MerpArchx86_64 = 1,
62 MerpArchx86 = 2,
63 MerpArchPPC = 3,
64 MerpArchPPC64 = 4
65 } MerpArch;
67 typedef enum
69 MERP_EXC_NONE = 0,
71 MERP_EXC_FORCE_QUIT = 1,
72 MERP_EXC_SIGSEGV = 2,
73 MERP_EXC_SIGABRT = 3,
74 MERP_EXC_SIGSYS = 4,
75 MERP_EXC_SIGILL = 5,
76 MERP_EXC_SIGBUS = 6,
77 MERP_EXC_SIGFPE = 7 ,
78 MERP_EXC_SIGTRAP = 8,
79 MERP_EXC_SIGKILL = 9,
80 MERP_EXC_HANG = 10
81 } MERPExcType;
83 typedef struct {
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;
94 size_t moduleOffset;
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;
105 } MERPStruct;
107 typedef struct {
108 gboolean enable_merp;
110 const char *appBundleID;
111 const char *appSignature;
112 const char *appVersion;
113 const char *merpGUIPath;
114 gboolean log;
115 } MerpOptions;
117 static MerpOptions config;
119 static const char *
120 get_merp_bitness (MerpArch arch)
122 switch (arch) {
123 case MerpArchx86_64:
124 return "x64";
125 case MerpArchx86:
126 return "x32";
127 default:
128 g_assert_not_reached ();
132 static MerpArch
133 get_merp_arch (void)
135 #ifdef TARGET_X86
136 return MerpArchx86;
137 #elif defined(TARGET_AMD64)
138 return MerpArchx86_64;
139 #elif defined(TARGET_POWERPC)
140 return MerpArchPPC;
141 #elif defined(TARGET_POWERPC64)
142 return MerpArchPPC64;
143 #else
144 g_assert_not_reached ();
145 #endif
148 static const char *
149 get_merp_exctype (MERPExcType exc)
151 switch (exc) {
152 case MERP_EXC_FORCE_QUIT:
153 return "0x10000000";
154 case MERP_EXC_SIGSEGV:
155 return "0x20000000";
156 case MERP_EXC_SIGABRT:
157 return "0x30000000";
158 case MERP_EXC_SIGSYS:
159 return "0x40000000";
160 case MERP_EXC_SIGILL:
161 return "0x50000000";
162 case MERP_EXC_SIGBUS:
163 return "0x60000000";
164 case MERP_EXC_SIGFPE:
165 return "0x70000000";
166 case MERP_EXC_SIGTRAP:
167 return "0x03000000";
168 case MERP_EXC_SIGKILL:
169 return "0x04000000";
170 case MERP_EXC_HANG:
171 return "0x02000000";
172 case MERP_EXC_NONE:
173 // Exception type is optional
174 return "";
175 default:
176 g_assert_not_reached ();
180 static MERPExcType
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);
201 static gchar *
202 mono_encode_merp_params (MERPStruct *merp)
204 GString *output = g_string_new ("");
206 // Provided by icall
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));
212 // Provided by icall
213 g_string_append_printf (output, "ApplicationName: %s\n", merp->serviceNameArg);
215 // Provided by icall
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);
225 // Provided by icall
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);
235 static void
236 write_file (const char *payload, const char *fileName)
238 FILE *outfile = fopen (fileName, "w");
239 if (!outfile)
240 g_error ("Could not create file %s\n", fileName);
241 fprintf (outfile, "%s\n", payload);
242 fclose (outfile);
245 static void
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);
257 static void
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);
274 g_free (werXmlPath);
276 if (config.log) {
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);
281 if (werXml != NULL)
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
290 if (status == 0)
291 g_error ("Could not start merp\n");
293 return;
296 static void
297 get_apple_model (char *buffer, size_t max_length)
299 size_t sz = 0;
301 // Get the number of bytes to copy
302 sysctlbyname("hw.model", NULL, &sz, NULL, 0);
304 if (sz > max_length) {
305 buffer[0] = '\0';
306 return;
309 sysctlbyname("hw.model", buffer, &sz, NULL, 0);
313 static void
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;
335 ERROR_DECL (error);
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;
350 static gchar *
351 mono_merp_fingerprint_payload (const char *non_param_data, const MERPStruct *merp)
353 JsonWriter writer;
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);
406 // Provided by icall
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);
427 // End of payload
428 mono_json_writer_indent (&writer);
429 mono_json_writer_object_end (&writer);
430 mono_json_writer_printf (&writer, "\n");
432 // End of object
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);
440 return output;
443 static gchar *
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);
458 int i=0;
460 g_string_append_printf (output, "<Parameter%d>%s</Parameter0>\n", i, merp->bundleIDArg, i);
461 i++;
463 g_string_append_printf (output, "<Parameter%d>%s</Parameter%d>\n", i, merp->versionArg, i);
464 i++;
466 g_string_append_printf (output, "<Parameter%d>%s</Parameter%d>\n", i, get_merp_bitness (merp->archArg), i);
467 i++;
469 g_string_append_printf (output, "<Parameter%d>%s</Parameter%d>\n", i, merp->serviceNameArg, i);
470 i++;
472 g_string_append_printf (output, "<Parameter%d>%s</Parameter%d>\n", i, merp->moduleName, i);
473 i++;
475 g_string_append_printf (output, "<Parameter%d>%s</Parameter%d>\n", i, merp->moduleVersion, i);
476 i++;
478 g_string_append_printf (output, "<Parameter%d>0x%x</Parameter%d>\n", i, merp->moduleOffset, i);
479 i++;
481 g_string_append_printf (output, "<Parameter%d>%s</Parameter%d>\n", i, get_merp_exctype (merp->exceptionArg), i);
482 i++;
484 g_string_append_printf (output, "<Parameter%d>0x%x</Parameter%d>\n", i, merp->hashes.offset_free_hash, i);
485 i++;
487 g_string_append_printf (output, "<Parameter%d>0x%x</Parameter%d>\n", i, merp->hashes.offset_rich_hash, i);
488 i++;
490 g_string_append_printf (output, "<Parameter%d>%s</Parameter%d>\n", i, merp->osVersion, i);
491 i++;
493 g_string_append_printf (output, "<Parameter%d>0x%x</Parameter%d>\n", i, merp->uiLidArg, i);
494 i++;
496 g_string_append_printf (output, "<Parameter%d>%s</Parameter%d>\n", i, merp->systemManufacturer, i);
497 i++;
499 g_string_append_printf (output, "<Parameter%d>%s</Parameter%d>\n", i, merp->systemModel, i);
500 i++;
502 g_string_append_printf (output, "</ProblemSignatures>\n");
503 g_string_append_printf (output, "</WERReportMetadata>\n");
505 return g_string_free (output, FALSE);
508 void
509 mono_merp_invoke (const intptr_t crashed_pid, const char *signal, const char *non_param_data, MonoStackHash *hashes, char *version)
511 MERPStruct merp;
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);
522 g_free (fullData);
523 g_free (merpCfg);
524 g_free (werXmlCfg);
527 void
528 mono_merp_disable (void)
530 if (!config.enable_merp)
531 return;
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));
540 void
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;
555 gboolean
556 mono_merp_enabled (void)
558 return config.enable_merp;
561 #endif // TARGET_OSX