[Coop] Convert System.CLRConfig to coop. (#8606)
[mono-project.git] / mono / utils / mono-merp.c
blob7750997d788d6d04dd3c239941ecf64f2c4cae6a
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 typedef enum {
29 MerpArchInvalid = 0,
31 MerpArchx86_64 = 1,
32 MerpArchx86 = 2,
33 MerpArchPPC = 3,
34 MerpArchPPC64 = 4
35 } MerpArch;
37 typedef enum
39 MERP_EXC_NONE = 0,
41 MERP_EXC_FORCE_QUIT = 1,
42 MERP_EXC_SIGSEGV = 2,
43 MERP_EXC_SIGABRT = 3,
44 MERP_EXC_SIGSYS = 4,
45 MERP_EXC_SIGILL = 5,
46 MERP_EXC_SIGBUS = 6,
47 MERP_EXC_SIGFPE = 7 ,
48 MERP_EXC_SIGTRAP = 8,
49 MERP_EXC_SIGKILL = 9,
50 MERP_EXC_HANG = 10
51 } MERPExcType;
53 typedef struct {
54 pid_t pidArg; // Process ID of crashed app (required for crash log generation)
55 MerpArch archArg; // Arch, MacOS only, bails out if not found also required for bucketization. (required)
56 uintptr_t capabilitiesArg; // App capabilities (optional) i.e. recover files, etc.
57 uintptr_t threadArg; // Stack Pointer of crashing thread. (required for crash log generation) Used for identifying crashing thread on crawl back when generating crashreport.txt
58 uintptr_t timeArg; // Total runtime (optional)
60 MERPExcType exceptionArg; // Exception type (refer to merpcommon.h and mach/exception_types.h for more info (optional)
62 const char *bundleIDArg; // App Bundle ID (required for bucketization)
63 const char *signatureArg; // App Bundle Signature (required)
65 const char *versionArg; // App Version (required for bucketization)
66 const char *devRegionArg; // App region (optional)
67 const char *uiLidArg; // Application LCID aka Language ID (optional for bucketization)
69 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)
71 const char *appLoggerName; // App loger name (optional)
72 const char *appLogSessionUUID; // App Session ID (optional but very useful)
73 gboolean isOfficeApplication; // Is office application (1 = True 0 = false). Needs to be 1 to continue. (Required by design)
74 gboolean isObjCException; // Is objectiveC Exception. (optional for crash log generation)
75 size_t memVirt; // Virtual memory (pagination) at the time of crash (optional)
76 size_t memRes; // Physical memory allocated at the time of the crash (optional)
77 const char *treSessionID; // Rules engine session ID (optional)
78 const char *exceptionCodeArg; // Exception code (optional)
79 const char *exceptionAddressArg; // Exception address (optional)
80 gboolean isAppRegisteredWithMAU; // Was this app installed with Microsoft Autoupdate (1 = True 0 = false)
81 } MERPStruct;
83 typedef struct {
84 gboolean enable_merp;
86 const char *appBundleID;
87 const char *appSignature;
88 const char *appVersion;
89 const char *merpGUIPath;
90 gboolean log;
91 } MerpOptions;
93 static MerpOptions config;
95 static void
96 append_merp_arch (GString *output, MerpArch arch)
98 switch (arch) {
99 case MerpArchx86_64:
100 g_string_append_printf (output, "01000007\n");
101 break;
102 case MerpArchx86:
103 g_string_append_printf (output, "00000007\n");
104 break;
105 case MerpArchPPC:
106 g_string_append_printf (output, "00000012\n");
107 break;
108 case MerpArchPPC64:
109 g_string_append_printf (output, "01000012\n");
110 break;
111 default:
112 g_assert_not_reached ();
116 static MerpArch
117 get_merp_arch (void)
119 #ifdef TARGET_X86
120 return MerpArchx86;
121 #elif defined(TARGET_AMD64)
122 return MerpArchx86_64;
123 #elif defined(TARGET_POWERPC)
124 return MerpArchPPC;
125 #elif defined(TARGET_POWERPC64)
126 return MerpArchPPC64;
127 #else
128 g_assert_not_reached ();
129 #endif
132 static void
133 append_merp_exctype (GString *output, MERPExcType exc)
135 switch (exc) {
136 case MERP_EXC_FORCE_QUIT:
137 g_string_append_printf (output, "10000000\n");
138 break;
139 case MERP_EXC_SIGSEGV:
140 g_string_append_printf (output, "20000000\n");
141 break;
142 case MERP_EXC_SIGABRT:
143 g_string_append_printf (output, "30000000\n");
144 break;
145 case MERP_EXC_SIGSYS:
146 g_string_append_printf (output, "40000000\n");
147 break;
148 case MERP_EXC_SIGILL:
149 g_string_append_printf (output, "50000000\n");
150 break;
151 case MERP_EXC_SIGBUS:
152 g_string_append_printf (output, "60000000\n");
153 break;
154 case MERP_EXC_SIGFPE:
155 g_string_append_printf (output, "70000000\n");
156 break;
157 case MERP_EXC_SIGTRAP:
158 g_string_append_printf (output, "80000000\n");
159 break;
160 case MERP_EXC_SIGKILL:
161 g_string_append_printf (output, "90000000\n");
162 break;
163 case MERP_EXC_HANG:
164 g_string_append_printf (output, "02000000\n");
165 break;
166 case MERP_EXC_NONE:
167 // Exception type is optional
168 g_string_append_printf (output, "\n");
169 break;
170 default:
171 g_assert_not_reached ();
175 static MERPExcType
176 parse_exception_type (const char *signal)
178 if (!strcmp (signal, "SIGSEGV"))
179 return MERP_EXC_SIGSEGV;
181 if (!strcmp (signal, "SIGFPE"))
182 return MERP_EXC_SIGFPE;
184 if (!strcmp (signal, "SIGILL"))
185 return MERP_EXC_SIGILL;
187 if (!strcmp (signal, "SIGABRT"))
188 return MERP_EXC_SIGABRT;
190 // FIXME: There are no other such signal
191 // strings passed to mono_handle_native_crash at the
192 // time of writing this
193 g_error ("Merp doesn't know how to handle %s\n", signal);
196 static void
197 print_string_or_blank (GString *output, const char *maybeString)
199 if (maybeString)
200 g_string_append_printf (output, "%s", maybeString);
201 g_string_append_printf (output, "\n");
204 static void
205 print_pointer_param (GString *output, intptr_t ptr)
207 // The format is arch-specific
208 #if defined(TARGET_AMD64)
209 g_string_append_printf (output, "%016llx\n", ptr);
210 #elif defined(TARGET_X86)
211 g_string_append_printf (output, "%08x\n", ptr);
212 #endif
215 static void
216 print_memory_fraction (GString *output, size_t mem_bytes)
218 if (!mem_bytes) {
219 g_string_append_printf (output, "\n");
220 return;
223 mem_bytes >>= 10; // convert from bytes to kb
224 float frac = (float) mem_bytes / 1024.0; // kb to mb
226 g_string_append_printf (output, "%f\n", frac);
229 static void
230 mono_encode_merp (GString *output, MERPStruct *merp)
232 // Format of integers seems to be 8 digits with padding
233 g_assert (merp->pidArg);
234 g_string_append_printf (output, "%.8x\n", merp->pidArg);
236 g_assert (merp->archArg);
237 append_merp_arch (output, merp->archArg);
239 g_string_append_printf (output, "%.8x\n", merp->capabilitiesArg);
240 print_pointer_param (output, merp->threadArg);
241 print_pointer_param (output, merp->timeArg);
243 append_merp_exctype (output, merp->exceptionArg);
245 g_assert (merp->bundleIDArg);
246 g_assert (merp->signatureArg);
247 g_assert (merp->versionArg);
248 g_string_append_printf (output, "%s\n%s\n%s\n", merp->bundleIDArg, merp->signatureArg, merp->versionArg);
250 print_string_or_blank (output, merp->devRegionArg);
251 print_string_or_blank (output, merp->uiLidArg);
253 g_assert (merp->serviceNameArg);
254 g_string_append_printf (output, "%s\n", merp->serviceNameArg);
256 print_string_or_blank (output, merp->appLoggerName);
257 print_string_or_blank (output, merp->appLogSessionUUID);
259 g_string_append_printf (output, "%d\n", merp->isOfficeApplication ? 1 : 0);
261 print_string_or_blank (output, merp->isObjCException ? "1" : NULL);
263 print_memory_fraction (output, merp->memRes);
264 print_memory_fraction (output, merp->memVirt);
266 print_string_or_blank (output, merp->treSessionID);
267 print_string_or_blank (output, merp->exceptionCodeArg);
268 print_string_or_blank (output, merp->exceptionAddressArg);
269 print_string_or_blank (output, merp->isAppRegisteredWithMAU ? "1" : "0");
272 // Darwin-only for now
273 static void
274 mono_arch_memory_info (size_t *resOut, size_t *vmOut)
276 struct task_basic_info t_info;
277 memset (&t_info, 0, sizeof (t_info));
278 mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
279 task_name_t task = mach_task_self ();
281 task_info(task, TASK_BASIC_INFO, (task_info_t) &t_info, &t_info_count);
283 *resOut = (size_t) t_info.resident_size;
284 *vmOut = (size_t) t_info.virtual_size;
287 static void
288 write_file (GString *str, const char *fileName)
290 FILE *outfile = fopen (fileName, "w");
291 if (!outfile)
292 g_error ("Could not create file %s\n", fileName);
293 fwrite (str->str, sizeof (gchar), str->len, outfile);
294 fclose (outfile);
298 * This struct is the wire protocol between MERP
299 * and mono
302 typedef struct {
303 mach_msg_header_t head;
305 /* start of the merp-specific data */
306 mach_msg_body_t msgh_body;
307 mach_msg_port_descriptor_t task;
308 /* end of the merp-specific data */
310 } MerpRequest;
312 static void
313 send_mach_message (mach_port_t *mach_port)
315 task_name_t task = mach_task_self ();
317 // Setup request
318 MerpRequest req;
319 memset (&req, 0, sizeof (req));
320 req.head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, 0);
321 req.task.name = task;
322 req.task.disposition = 19;
323 req.task.type = MACH_MSG_PORT_DESCRIPTOR;
325 /* msgh_size passed as argument */
326 req.head.msgh_remote_port = *mach_port;
327 req.head.msgh_local_port = MACH_PORT_NULL;
328 req.head.msgh_id = 400;
330 req.msgh_body.msgh_descriptor_count = 1;
332 // Send port to merp GUI
333 if (config.log)
334 fprintf (stderr, "Sending message to MERP\n");
335 mach_msg_return_t res = mach_msg (&req.head, MACH_SEND_MSG|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(MerpRequest), 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
336 g_assert (res == KERN_SUCCESS);
337 if (config.log)
338 fprintf (stderr, "Successfully sent message to MERP\n");
341 static void
342 connect_to_merp (const char *serviceName, mach_port_t *merp_port)
344 // // Create process to launch merp gui application
345 const char *argvOpen[] = {"/usr/bin/open", "-a", config.merpGUIPath, NULL};
346 int status = posix_spawn(NULL, "/usr/bin/open", NULL, NULL, (char *const*)(argvOpen), NULL);
348 // // FIXME error handling
349 g_assert (status == 0);
351 // Register our service name with this task
352 // BOOTSTRAP_UNKNOWN_SERVICE is returned while the service doesn't exist.
353 // We rely on MERP to make the service with serviceName for us
354 kern_return_t kernErr = bootstrap_look_up(bootstrap_port, serviceName, merp_port);
355 while (TRUE) {
356 for (int i = 0; BOOTSTRAP_UNKNOWN_SERVICE == kernErr && i < 5000; i++)
357 kernErr = bootstrap_look_up(bootstrap_port, serviceName, merp_port);
359 if (kernErr != BOOTSTRAP_UNKNOWN_SERVICE)
360 break;
362 if (config.log)
363 fprintf (stderr, "Merp: Service not registered with name %s, resetting counter after 10s sleep\n", serviceName);
364 sleep(10);
367 /*// FIXME error handling*/
368 g_assert (KERN_SUCCESS == kernErr);
371 static void
372 mono_merp_send (GString *str, const char *serviceName)
374 // Write struct to magic file location
375 // This registers our mach service so we can connect
376 // to the merp process
377 const char *home = g_get_home_dir ();
378 char *merpParamPath = g_strdup_printf ("%s/Library/Group Containers/UBF8T346G9.ms/MERP.Params.txt", home);
379 write_file (str, merpParamPath);
380 g_free (merpParamPath);
382 mach_port_t merpPort = MACH_PORT_NULL;
384 // Start merpGui application
385 // Assign to merpPort the port that merpGui opens
386 // Connecting to the service with name serviceName
387 // that merpGUI starts
388 connect_to_merp (serviceName, &merpPort);
390 // Send this mach task to MERP over the port
391 send_mach_message (&merpPort);
393 // After we resume from the suspend, we are done
394 // We're spinning down the process shortly.
395 // This thread exits, and the crashing thread's
396 // waitpid on this thread ends.
397 return;
400 static void
401 mono_init_merp (const char *serviceName, const char *signal, pid_t crashed_pid, intptr_t thread_pointer, MERPStruct *merp)
403 g_assert (mono_merp_enabled ());
405 merp->pidArg = crashed_pid;
406 merp->archArg = get_merp_arch ();
407 merp->capabilitiesArg = 0x0;
409 merp->threadArg = thread_pointer;
411 // FIXME: time the runtime?
412 merp->timeArg = 0x0;
414 merp->exceptionArg = parse_exception_type (signal);
416 // If these aren't set, icall wasn't made
417 // don't do merp? / don't set the variable to use merp;
418 merp->bundleIDArg = config.appBundleID;
419 merp->signatureArg = config.appSignature;
420 merp->versionArg = config.appVersion;
422 // FIXME: Do we want these?
423 merp->devRegionArg = NULL;
424 merp->uiLidArg = NULL;
426 merp->serviceNameArg = serviceName;
428 // FIXME: Do we want these?
429 merp->appLoggerName = NULL;
430 merp->appLogSessionUUID = NULL;
432 // Should be set for our usage as per document
433 merp->isOfficeApplication = TRUE;
434 merp->isObjCException = FALSE;
436 mono_arch_memory_info (&merp->memRes, &merp->memVirt);
438 // No sessions right now
439 merp->treSessionID = NULL;
440 merp->exceptionCodeArg = NULL;
441 merp->exceptionAddressArg = NULL;
443 // Not certain? Maybe expose to config options
444 merp->isAppRegisteredWithMAU = TRUE;
447 void
448 mono_merp_invoke (pid_t crashed_pid, intptr_t thread_pointer, const char *signal)
450 // This unique service name is used to communicate with merp over mach service ports
451 char *serviceName = g_strdup_printf ("com.mono.merp.%.8x", crashed_pid);
453 MERPStruct merp;
454 memset (&merp, 0, sizeof (merp));
455 mono_init_merp (serviceName, signal, crashed_pid, thread_pointer, &merp);
457 GString *output = g_string_new ("");
458 mono_encode_merp (output, &merp);
460 if (config.log)
461 fprintf (stderr, "Results: \n(%s)\n", output->str);
463 // We send the merp over the port
464 mono_merp_send (output, serviceName);
466 g_string_free (output, TRUE);
467 g_free (serviceName);
470 void
471 mono_merp_disable (void)
473 if (!config.enable_merp)
474 return;
476 g_free ((char*)config.appBundleID); // cast away const
477 g_free ((char*)config.appSignature);
478 g_free ((char*)config.appVersion);
479 g_free ((char*)config.merpGUIPath);
480 memset (&config, 0, sizeof (config));
483 void
484 mono_merp_enable (const char *appBundleID, const char *appSignature, const char *appVersion, const char *merpGUIPath)
486 g_assert (!config.enable_merp);
488 config.appBundleID = g_strdup (appBundleID);
489 config.appSignature = g_strdup (appSignature);
490 config.appVersion = g_strdup (appVersion);
491 config.merpGUIPath = g_strdup (merpGUIPath);
493 config.log = g_getenv ("MONO_MERP_VERBOSE") != NULL;
495 config.enable_merp = TRUE;
498 gboolean
499 mono_merp_enabled (void)
501 return config.enable_merp;
504 #endif // TARGET_OSX