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>
41 MERP_EXC_FORCE_QUIT
= 1,
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)
86 const char *appBundleID
;
87 const char *appSignature
;
88 const char *appVersion
;
89 const char *merpGUIPath
;
93 static MerpOptions config
;
96 append_merp_arch (GString
*output
, MerpArch arch
)
100 g_string_append_printf (output
, "01000007\n");
103 g_string_append_printf (output
, "00000007\n");
106 g_string_append_printf (output
, "00000012\n");
109 g_string_append_printf (output
, "01000012\n");
112 g_assert_not_reached ();
121 #elif defined(TARGET_AMD64)
122 return MerpArchx86_64
;
123 #elif defined(TARGET_POWERPC)
125 #elif defined(TARGET_POWERPC64)
126 return MerpArchPPC64
;
128 g_assert_not_reached ();
133 append_merp_exctype (GString
*output
, MERPExcType exc
)
136 case MERP_EXC_FORCE_QUIT
:
137 g_string_append_printf (output
, "10000000\n");
139 case MERP_EXC_SIGSEGV
:
140 g_string_append_printf (output
, "20000000\n");
142 case MERP_EXC_SIGABRT
:
143 g_string_append_printf (output
, "30000000\n");
145 case MERP_EXC_SIGSYS
:
146 g_string_append_printf (output
, "40000000\n");
148 case MERP_EXC_SIGILL
:
149 g_string_append_printf (output
, "50000000\n");
151 case MERP_EXC_SIGBUS
:
152 g_string_append_printf (output
, "60000000\n");
154 case MERP_EXC_SIGFPE
:
155 g_string_append_printf (output
, "70000000\n");
157 case MERP_EXC_SIGTRAP
:
158 g_string_append_printf (output
, "80000000\n");
160 case MERP_EXC_SIGKILL
:
161 g_string_append_printf (output
, "90000000\n");
164 g_string_append_printf (output
, "02000000\n");
167 // Exception type is optional
168 g_string_append_printf (output
, "\n");
171 g_assert_not_reached ();
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
);
197 print_string_or_blank (GString
*output
, const char *maybeString
)
200 g_string_append_printf (output
, "%s", maybeString
);
201 g_string_append_printf (output
, "\n");
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
);
216 print_memory_fraction (GString
*output
, size_t mem_bytes
)
219 g_string_append_printf (output
, "\n");
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
);
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
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
;
288 write_file (GString
*str
, const char *fileName
)
290 FILE *outfile
= fopen (fileName
, "w");
292 g_error ("Could not create file %s\n", fileName
);
293 fwrite (str
->str
, sizeof (gchar
), str
->len
, outfile
);
298 * This struct is the wire protocol between MERP
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 */
313 send_mach_message (mach_port_t
*mach_port
)
315 task_name_t task
= mach_task_self ();
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
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
);
338 fprintf (stderr
, "Successfully sent message to MERP\n");
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
);
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
)
363 fprintf (stderr
, "Merp: Service not registered with name %s, resetting counter after 10s sleep\n", serviceName
);
367 /*// FIXME error handling*/
368 g_assert (KERN_SUCCESS
== kernErr
);
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.
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?
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
;
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
);
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
);
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
);
471 mono_merp_disable (void)
473 if (!config
.enable_merp
)
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
));
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
;
499 mono_merp_enabled (void)
501 return config
.enable_merp
;