Bug 1724368 - Add support for catching and processing EXC_RESOURCE exceptions on macOS r=KrisWright
authorGabriele Svelto <gsvelto@mozilla.com>
Fri, 20 Aug 2021 08:06:00 +0000
changeset 589409 b3cfc657b5fbf5197746bc954f3a24197ac41a50
parent 589408 e9f5adca20301f24f28c9db7826f40a912b08e76
child 589410 614187392c034d2385386d2c1e2532b5c77433b5
push id38722
push usermlaza@mozilla.com
push dateFri, 20 Aug 2021 21:33:13 +0000
treeherdermozilla-central@fe930f350465 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersKrisWright
bugs1724368
milestone93.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1724368 - Add support for catching and processing EXC_RESOURCE exceptions on macOS r=KrisWright This introduces a few changes to the crash reporting machinery: * The macOS exception handler now registers itself for catching EXC_RESOURCE exceptions, those are thrown when the process exceeds a pre-set resource limit (memory, CPU usage, I/O, etc...) * The minidump writer has been updated to correctly store the subcode from the EXC_RESOURCE exceptions, this involves widening to 64-bits the code and subcode passed to the writer. The upper 32 bits of the code are now set in the minidump's exception_flags field (vs the lower 32 bits for all other exceptions). Additionally the exception type, code and subcode are now stored in the exception_information array like Crashpad does. This preserves the entirety of the data that came with the exception. * The stackwalker has been modified to print out these type of exceptions as well as the resource type and flavor. Differential Revision: https://phabricator.services.mozilla.com/D122229
toolkit/crashreporter/breakpad-client/mac/handler/exception_handler.cc
toolkit/crashreporter/breakpad-client/mac/handler/exception_handler.h
toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.cc
toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.h
toolkit/crashreporter/breakpad-patches/24-macos-exc-resource.patch
toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_mac.h
toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc
--- a/toolkit/crashreporter/breakpad-client/mac/handler/exception_handler.cc
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/exception_handler.cc
@@ -120,17 +120,18 @@ struct ExceptionReplyMessage {
   NDR_record_t       ndr;
   kern_return_t      return_code;
 };
 #pragma pack(pop)
 
 // Only catch these three exceptions.  The other ones are nebulously defined
 // and may result in treating a non-fatal exception as fatal.
 exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
-EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
+EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT |
+EXC_MASK_RESOURCE;
 
 kern_return_t ForwardException(mach_port_t task,
                                mach_port_t failed_thread,
                                exception_type_t exception,
                                mach_exception_data_t code,
                                mach_msg_type_number_t code_count);
 
 // The contents of mach_exc_server() and mach_exception_raise() are derived
@@ -421,17 +422,17 @@ static void GetPHCAddrInfo(int64_t excep
     char* addr = reinterpret_cast<char*>(exception_subcode);
     ReplaceMalloc::IsPHCAllocation(addr, addr_info);
   }
 }
 #endif
 
 bool ExceptionHandler::WriteMinidumpWithException(
     int exception_type,
-    int exception_code,
+    int64_t exception_code,
     int64_t exception_subcode,
     breakpad_ucontext_t* task_context,
     mach_port_t thread_name,
     mach_port_t task_name,
     bool exit_after_write,
     bool report_current_thread) {
   bool result = false;
 
@@ -609,17 +610,17 @@ void* ExceptionHandler::WaitForMessage(v
 
 #if USE_PROTECTED_ALLOCATIONS
         if (gBreakpadAllocator)
           gBreakpadAllocator->Unprotect();
 #endif
 
         mach_port_t thread = MACH_PORT_NULL;
         int exception_type = 0;
-        int exception_code = 0;
+        int64_t exception_code = 0;
         if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) {
           thread = receive.thread.name;
           exception_type = EXC_BREAKPOINT;
 #if defined(__i386__) || defined(__x86_64__)
           exception_code = EXC_I386_BPT;
 #elif defined(__ppc__) || defined(__ppc64__)
           exception_code = EXC_PPC_BREAKPOINT;
 #elif defined(__arm__) || defined(__aarch64__)
@@ -654,18 +655,26 @@ void* ExceptionHandler::WaitForMessage(v
           self->SuspendThreads();
 
 #if USE_PROTECTED_ALLOCATIONS
           if (gBreakpadAllocator)
             gBreakpadAllocator->Unprotect();
 #endif
 
           mach_exception_data_type_t subcode = 0;
-          if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1)
-            subcode = receive.code[1];
+          if (receive.code_count > 1) {
+            switch (receive.exception) {
+              case EXC_BAD_ACCESS:
+              case EXC_RESOURCE:
+                subcode = receive.code[1];
+                break;
+              default:
+                subcode = 0;
+            }
+          }
 
           // Generate the minidump with the exception data.
           self->WriteMinidumpWithException(receive.exception, receive.code[0],
                                            subcode, NULL, receive.thread.name,
                                            mach_task_self(),  true, false);
 
 #if USE_PROTECTED_ALLOCATIONS
           // This may have become protected again within
@@ -716,18 +725,18 @@ void ExceptionHandler::SignalHandler(int
 #if USE_PROTECTED_ALLOCATIONS
   if (gBreakpadAllocator)
     gBreakpadAllocator->Protect();
 #endif
 }
 
 // static
 bool ExceptionHandler::WriteForwardedExceptionMinidump(int exception_type,
-						       int exception_code,
-						       int exception_subcode,
+						       int64_t exception_code,
+						       int64_t exception_subcode,
 						       mach_port_t thread,
 						       mach_port_t task)
 {
   if (!gProtectedData.handler) {
     return false;
   }
   return gProtectedData.handler->WriteMinidumpWithException(exception_type, exception_code,
 							    exception_subcode, NULL, thread, task,
--- a/toolkit/crashreporter/breakpad-client/mac/handler/exception_handler.h
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/exception_handler.h
@@ -160,18 +160,18 @@ class ExceptionHandler {
   static bool WriteMinidumpForChild(mach_port_t child,
 				    mach_port_t child_blamed_thread,
 				    const std::string &dump_path,
 				    MinidumpCallback callback,
 				    void *callback_context);
 
   // Write a minidump for an exception that was received by another handler.
   static bool WriteForwardedExceptionMinidump(int exception_type,
-					      int exception_code,
-					      int exception_subcode,
+					      int64_t exception_code,
+					      int64_t exception_subcode,
 					      mach_port_t thread,
 					      mach_port_t task);
 
   // Returns whether out-of-process dump generation is used or not.
   bool IsOutOfProcess() const {
 #if TARGET_OS_IPHONE
     return false;
 #else
@@ -197,17 +197,17 @@ class ExceptionHandler {
   // Send a mach message to the exception handler.  Return true on
   // success, false otherwise.
   bool SendMessageToHandlerThread(HandlerThreadMessage message_id);
 
   // All minidump writing goes through this one routine.
   // |task_context| can be NULL. If not, it will be used to retrieve the
   // context of the current thread, instead of using |thread_get_state|.
   bool WriteMinidumpWithException(int exception_type,
-                                  int exception_code,
+                                  int64_t exception_code,
                                   int64_t exception_subcode,
                                   breakpad_ucontext_t *task_context,
                                   mach_port_t thread_name,
                                   mach_port_t task_name,
                                   bool exit_after_write,
                                   bool report_current_thread);
 
   // When installed, this static function will be call from a newly created
--- a/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.cc
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.cc
@@ -1145,33 +1145,52 @@ MinidumpGenerator::WriteExceptionStream(
   exception_stream->stream_type = MD_EXCEPTION_STREAM;
   exception_stream->location = exception.location();
   MDRawExceptionStream *exception_ptr = exception.get();
   exception_ptr->thread_id = exception_thread_;
 
   // This naming is confusing, but it is the proper translation from
   // mach naming to minidump naming.
   exception_ptr->exception_record.exception_code = exception_type_;
-  exception_ptr->exception_record.exception_flags = exception_code_;
+
+  uint32_t exception_flags = 0;
+  if (exception_type_ == EXC_RESOURCE) {
+    // For EXC_RESOURCE crashes Crashpad records the uppermost 32 bits of
+    // the exception code in the exception flags, let's do the same here.
+    uint64_t unsigned_exception_code = exception_code_;
+    exception_flags = unsigned_exception_code >> 32;
+  } else {
+    exception_flags = exception_code_;
+  }
+
+  exception_ptr->exception_record.exception_flags = exception_flags;
 
   breakpad_thread_state_data_t state;
   mach_msg_type_number_t state_count
       = static_cast<mach_msg_type_number_t>(sizeof(state));
 
   if (!GetThreadState(exception_thread_, state, &state_count))
     return false;
 
   if (!WriteContext(state, &exception_ptr->thread_context))
     return false;
 
   if (exception_type_ == EXC_BAD_ACCESS)
     exception_ptr->exception_record.exception_address = exception_subcode_;
   else
     exception_ptr->exception_record.exception_address = CurrentPCForStack(state);
 
+  // Crashpad stores the exception type and the optional exception codes in
+  // the exception information field, so we do the same here.
+  exception_ptr->exception_record.number_parameters =
+    (exception_subcode_ != 0) ? 3 : 2;
+  exception_ptr->exception_record.exception_information[0] = exception_type_;
+  exception_ptr->exception_record.exception_information[1] = exception_code_;
+  exception_ptr->exception_record.exception_information[2] = exception_subcode_;
+
   return true;
 }
 
 bool MinidumpGenerator::WriteSystemInfoStream(
     MDRawDirectory *system_info_stream) {
   TypedMDRVA<MDRawSystemInfo> info(&writer_);
 
   if (!info.Allocate())
--- a/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.h
+++ b/toolkit/crashreporter/breakpad-client/mac/handler/minidump_generator.h
@@ -109,17 +109,17 @@ class MinidumpGenerator {
   static string UniqueNameInDirectory(const string &dir, string *unique_name);
 
   // Write out the minidump into |path|
   // All of the components of |path| must exist and be writable
   // Return true if successful, false otherwise
   bool Write(const char *path);
 
   // Specify some exception information, if applicable
-  void SetExceptionInformation(int type, int code, int64_t subcode,
+  void SetExceptionInformation(int type, int64_t code, int64_t subcode,
                                mach_port_t thread_name) {
     exception_type_ = type;
     exception_code_ = code;
     exception_subcode_ = subcode;
     exception_thread_ = thread_name;
   }
 
   // Specify the task context. If |task_context| is not NULL, it will be used
@@ -222,17 +222,17 @@ class MinidumpGenerator {
 
  protected:
   // Use this writer to put the data to disk
   MinidumpFileWriter writer_;
 
  private:
   // Exception information
   int exception_type_;
-  int exception_code_;
+  int64_t exception_code_;
   int64_t exception_subcode_;
   mach_port_t exception_thread_;
   mach_port_t crashing_task_;
   mach_port_t handler_thread_;
 
   // CPU type of the task being dumped.
   cpu_type_t cpu_type_;
 
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/breakpad-patches/24-macos-exc-resource.patch
@@ -0,0 +1,155 @@
+diff --git a/src/google_breakpad/common/minidump_exception_mac.h b/src/google_breakpad/common/minidump_exception_mac.h
+--- a/src/google_breakpad/common/minidump_exception_mac.h
++++ b/src/google_breakpad/common/minidump_exception_mac.h
+@@ -62,16 +62,18 @@ typedef enum {
+   MD_EXCEPTION_MAC_BREAKPOINT      = 6,  /* code is CPU-specific */
+       /* EXC_BREAKPOINT */
+   MD_EXCEPTION_MAC_SYSCALL         = 7,
+       /* EXC_SYSCALL */
+   MD_EXCEPTION_MAC_MACH_SYSCALL    = 8,
+       /* EXC_MACH_SYSCALL */
+   MD_EXCEPTION_MAC_RPC_ALERT       = 9,
+       /* EXC_RPC_ALERT */
++  MD_EXCEPTION_MAC_RESOURCE        = 11,
++      /* EXC_RESOURCE */
+   MD_EXCEPTION_MAC_SIMULATED       = 0x43507378
+       /* Fake exception code used by Crashpad's SimulateCrash ('CPsx'). */
+ } MDExceptionMac;
+ 
+ /* For (MDException).exception_flags.  Breakpad minidump extension for Mac OS X
+  * support.  Based on Darwin/Mac OS X' mach/ppc/exception.h and
+  * mach/i386/exception.h.  This is what Mac OS X calls a "code". */
+ typedef enum {
+@@ -201,9 +203,42 @@ typedef enum {
+   /* EXC_I386_PGFLT     = 14: should not occur in user space */
+   /* EXC_I386_EXTERRFLT = 16: mapped to EXC_ARITHMETIC/EXC_I386_EXTERR */
+   MD_EXCEPTION_CODE_MAC_X86_ALIGNMENT_FAULT            = 17
+       /* EXC_ALIGNFLT (for vector operations) */
+   /* EXC_I386_ENOEXTFLT = 32: should be handled by the kernel */
+   /* EXC_I386_ENDPERR   = 33: should not occur */
+ } MDExceptionCodeMac;
+ 
++// The following definitions were taken from  Darwin/XNU kernel sources.
++// See https://github.com/apple/darwin-xnu/blob/main/osfmk/kern/exc_resource.h
++
++typedef enum MDMacExcResourceType {
++  MD_MAC_EXC_RESOURCE_TYPE_CPU     = 1,
++  MD_MAC_EXC_RESOURCE_TYPE_WAKEUPS = 2,
++  MD_MAC_EXC_RESOURCE_TYPE_MEMORY  = 3,
++  MD_MAC_EXC_RESOURCE_TYPE_IO      = 4,
++  MD_MAC_EXC_RESOURCE_TYPE_THREADS = 5
++} MDMacExcResourceType;
++
++typedef enum MDMacExcResourceFlavorCpu {
++  MD_MAC_EXC_RESOURCE_FLAVOR_CPU_MONITOR       = 1,
++  MD_MAC_EXC_RESOURCE_FLAVOR_CPU_MONITOR_FATAL = 2
++} MDMacExcResourceFlavorCpu;
++
++typedef enum MDMacExcResourceFlavorWakeup {
++  MD_MAC_EXC_RESOURCE_FLAVOR_WAKEUPS_MONITOR = 1,
++} MDMacExcResourceFlavorWakeup;
++
++typedef enum MDMacExcResourceFlavorMemory {
++  MD_MAC_EXC_RESOURCE_FLAVOR_HIGH_WATERMARK = 1,
++} MDMacExcResourceFlavorMemory;
++
++typedef enum MDMacExcResourceIOFlavor {
++  MD_MAC_EXC_RESOURCE_FLAVOR_IO_PHYSICAL_WRITES = 1,
++  MD_MAC_EXC_RESOURCE_FLAVOR_IO_LOGICAL_WRITES = 2,
++} MDMacExcResourceIOFlavor;
++
++typedef enum MDMacExcResourceThreadsFlavor {
++  MD_MAC_EXC_RESOURCE_FLAVOR_THREADS_HIGH_WATERMARK = 1,
++} MDMacExcResourceThreadsFlavor;
++
+ #endif  /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_MAC_OSX_H__ */
+diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc
+--- a/src/processor/minidump_processor.cc
++++ b/src/processor/minidump_processor.cc
+@@ -1143,16 +1143,86 @@ string MinidumpProcessor::GetCrashReason
+         case MD_EXCEPTION_MAC_MACH_SYSCALL:
+           reason = "EXC_MACH_SYSCALL / ";
+           reason.append(flags_string);
+           break;
+         case MD_EXCEPTION_MAC_RPC_ALERT:
+           reason = "EXC_RPC_ALERT / ";
+           reason.append(flags_string);
+           break;
++        case MD_EXCEPTION_MAC_RESOURCE:
++          reason = "EXC_RESOURCE / ";
++          {
++            uint32_t type = (exception_flags >> 29) & 0x7ULL;
++            uint32_t flavor = (exception_flags >> 26) & 0x7ULL;
++            char flavor_string[4] = {};
++            switch (type) {
++              case MD_MAC_EXC_RESOURCE_TYPE_CPU:
++                reason.append("RESOURCE_TYPE_CPU / ");
++                switch (flavor) {
++                  case MD_MAC_EXC_RESOURCE_FLAVOR_CPU_MONITOR:
++                    reason.append("FLAVOR_CPU_MONITOR");
++                    break;
++                  case MD_MAC_EXC_RESOURCE_FLAVOR_CPU_MONITOR_FATAL:
++                    reason.append("FLAVOR_CPU_MONITOR_FATAL");
++                    break;
++                  default:
++                    snprintf(flavor_string, sizeof(flavor_string), "%#3x", flavor);
++                    reason.append(flavor_string);
++                    break;
++                }
++                break;
++              case MD_MAC_EXC_RESOURCE_TYPE_WAKEUPS:
++                reason.append("RESOURCE_TYPE_WAKEUPS / ");
++                if (flavor == MD_MAC_EXC_RESOURCE_FLAVOR_WAKEUPS_MONITOR) {
++                  reason.append("FLAVOR_WAKEUPS_MONITOR");
++                } else {
++                  snprintf(flavor_string, sizeof(flavor_string), "%#3x", flavor);
++                  reason.append(flavor_string);
++                }
++                break;
++              case MD_MAC_EXC_RESOURCE_TYPE_MEMORY:
++                reason.append("RESOURCE_TYPE_MEMORY / ");
++                if (flavor == MD_MAC_EXC_RESOURCE_FLAVOR_HIGH_WATERMARK) {
++                  reason.append("FLAVOR_HIGH_WATERMARK");
++                } else {
++                  snprintf(flavor_string, sizeof(flavor_string), "%#3x", flavor);
++                  reason.append(flavor_string);
++                }
++                break;
++              case MD_MAC_EXC_RESOURCE_TYPE_IO:
++                reason.append("EXC_RESOURCE_TYPE_IO / ");
++                switch (flavor) {
++                  case MD_MAC_EXC_RESOURCE_FLAVOR_IO_PHYSICAL_WRITES:
++                    reason.append("FLAVOR_IO_PHYSICAL_WRITES");
++                    break;
++                  case MD_MAC_EXC_RESOURCE_FLAVOR_IO_LOGICAL_WRITES:
++                    reason.append("FLAVOR_IO_LOGICAL_WRITES");
++                    break;
++                  default:
++                    snprintf(flavor_string, sizeof(flavor_string), "%#3x", flavor);
++                    reason.append(flavor_string);
++                    break;
++                }
++                break;
++              case MD_MAC_EXC_RESOURCE_TYPE_THREADS:
++                reason.append("EXC_RESOURCE_TYPE_THREADS / ");
++                if (flavor == MD_MAC_EXC_RESOURCE_FLAVOR_THREADS_HIGH_WATERMARK) {
++                  reason.append("FLAVOR_THREADS_HIGH_WATERMARK");
++                } else {
++                  snprintf(flavor_string, sizeof(flavor_string), "%#3x", flavor);
++                  reason.append(flavor_string);
++                }
++                break;
++              default:
++                reason.append(flags_string);
++                break;
++            }
++          }
++          break;
+         case MD_EXCEPTION_MAC_SIMULATED:
+           reason = "Simulated Exception";
+           break;
+       }
+       break;
+     }
+ 
+     case MD_OS_WIN32_NT:
--- a/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_mac.h
+++ b/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_mac.h
@@ -62,16 +62,18 @@ typedef enum {
   MD_EXCEPTION_MAC_BREAKPOINT      = 6,  /* code is CPU-specific */
       /* EXC_BREAKPOINT */
   MD_EXCEPTION_MAC_SYSCALL         = 7,
       /* EXC_SYSCALL */
   MD_EXCEPTION_MAC_MACH_SYSCALL    = 8,
       /* EXC_MACH_SYSCALL */
   MD_EXCEPTION_MAC_RPC_ALERT       = 9,
       /* EXC_RPC_ALERT */
+  MD_EXCEPTION_MAC_RESOURCE        = 11,
+      /* EXC_RESOURCE */
   MD_EXCEPTION_MAC_SIMULATED       = 0x43507378
       /* Fake exception code used by Crashpad's SimulateCrash ('CPsx'). */
 } MDExceptionMac;
 
 /* For (MDException).exception_flags.  Breakpad minidump extension for Mac OS X
  * support.  Based on Darwin/Mac OS X' mach/ppc/exception.h and
  * mach/i386/exception.h.  This is what Mac OS X calls a "code". */
 typedef enum {
@@ -201,9 +203,42 @@ typedef enum {
   /* EXC_I386_PGFLT     = 14: should not occur in user space */
   /* EXC_I386_EXTERRFLT = 16: mapped to EXC_ARITHMETIC/EXC_I386_EXTERR */
   MD_EXCEPTION_CODE_MAC_X86_ALIGNMENT_FAULT            = 17
       /* EXC_ALIGNFLT (for vector operations) */
   /* EXC_I386_ENOEXTFLT = 32: should be handled by the kernel */
   /* EXC_I386_ENDPERR   = 33: should not occur */
 } MDExceptionCodeMac;
 
+// The following definitions were taken from  Darwin/XNU kernel sources.
+// See https://github.com/apple/darwin-xnu/blob/main/osfmk/kern/exc_resource.h
+
+typedef enum MDMacExcResourceType {
+  MD_MAC_EXC_RESOURCE_TYPE_CPU     = 1,
+  MD_MAC_EXC_RESOURCE_TYPE_WAKEUPS = 2,
+  MD_MAC_EXC_RESOURCE_TYPE_MEMORY  = 3,
+  MD_MAC_EXC_RESOURCE_TYPE_IO      = 4,
+  MD_MAC_EXC_RESOURCE_TYPE_THREADS = 5
+} MDMacExcResourceType;
+
+typedef enum MDMacExcResourceFlavorCpu {
+  MD_MAC_EXC_RESOURCE_FLAVOR_CPU_MONITOR       = 1,
+  MD_MAC_EXC_RESOURCE_FLAVOR_CPU_MONITOR_FATAL = 2
+} MDMacExcResourceFlavorCpu;
+
+typedef enum MDMacExcResourceFlavorWakeup {
+  MD_MAC_EXC_RESOURCE_FLAVOR_WAKEUPS_MONITOR = 1,
+} MDMacExcResourceFlavorWakeup;
+
+typedef enum MDMacExcResourceFlavorMemory {
+  MD_MAC_EXC_RESOURCE_FLAVOR_HIGH_WATERMARK = 1,
+} MDMacExcResourceFlavorMemory;
+
+typedef enum MDMacExcResourceIOFlavor {
+  MD_MAC_EXC_RESOURCE_FLAVOR_IO_PHYSICAL_WRITES = 1,
+  MD_MAC_EXC_RESOURCE_FLAVOR_IO_LOGICAL_WRITES = 2,
+} MDMacExcResourceIOFlavor;
+
+typedef enum MDMacExcResourceThreadsFlavor {
+  MD_MAC_EXC_RESOURCE_FLAVOR_THREADS_HIGH_WATERMARK = 1,
+} MDMacExcResourceThreadsFlavor;
+
 #endif  /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_MAC_OSX_H__ */
--- a/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc
+++ b/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc
@@ -1143,16 +1143,86 @@ string MinidumpProcessor::GetCrashReason
         case MD_EXCEPTION_MAC_MACH_SYSCALL:
           reason = "EXC_MACH_SYSCALL / ";
           reason.append(flags_string);
           break;
         case MD_EXCEPTION_MAC_RPC_ALERT:
           reason = "EXC_RPC_ALERT / ";
           reason.append(flags_string);
           break;
+        case MD_EXCEPTION_MAC_RESOURCE:
+          reason = "EXC_RESOURCE / ";
+          {
+            uint32_t type = (exception_flags >> 29) & 0x7ULL;
+            uint32_t flavor = (exception_flags >> 26) & 0x7ULL;
+            char flavor_string[4] = {};
+            switch (type) {
+              case MD_MAC_EXC_RESOURCE_TYPE_CPU:
+                reason.append("RESOURCE_TYPE_CPU / ");
+                switch (flavor) {
+                  case MD_MAC_EXC_RESOURCE_FLAVOR_CPU_MONITOR:
+                    reason.append("FLAVOR_CPU_MONITOR");
+                    break;
+                  case MD_MAC_EXC_RESOURCE_FLAVOR_CPU_MONITOR_FATAL:
+                    reason.append("FLAVOR_CPU_MONITOR_FATAL");
+                    break;
+                  default:
+                    snprintf(flavor_string, sizeof(flavor_string), "%#3x", flavor);
+                    reason.append(flavor_string);
+                    break;
+                }
+                break;
+              case MD_MAC_EXC_RESOURCE_TYPE_WAKEUPS:
+                reason.append("RESOURCE_TYPE_WAKEUPS / ");
+                if (flavor == MD_MAC_EXC_RESOURCE_FLAVOR_WAKEUPS_MONITOR) {
+                  reason.append("FLAVOR_WAKEUPS_MONITOR");
+                } else {
+                  snprintf(flavor_string, sizeof(flavor_string), "%#3x", flavor);
+                  reason.append(flavor_string);
+                }
+                break;
+              case MD_MAC_EXC_RESOURCE_TYPE_MEMORY:
+                reason.append("RESOURCE_TYPE_MEMORY / ");
+                if (flavor == MD_MAC_EXC_RESOURCE_FLAVOR_HIGH_WATERMARK) {
+                  reason.append("FLAVOR_HIGH_WATERMARK");
+                } else {
+                  snprintf(flavor_string, sizeof(flavor_string), "%#3x", flavor);
+                  reason.append(flavor_string);
+                }
+                break;
+              case MD_MAC_EXC_RESOURCE_TYPE_IO:
+                reason.append("EXC_RESOURCE_TYPE_IO / ");
+                switch (flavor) {
+                  case MD_MAC_EXC_RESOURCE_FLAVOR_IO_PHYSICAL_WRITES:
+                    reason.append("FLAVOR_IO_PHYSICAL_WRITES");
+                    break;
+                  case MD_MAC_EXC_RESOURCE_FLAVOR_IO_LOGICAL_WRITES:
+                    reason.append("FLAVOR_IO_LOGICAL_WRITES");
+                    break;
+                  default:
+                    snprintf(flavor_string, sizeof(flavor_string), "%#3x", flavor);
+                    reason.append(flavor_string);
+                    break;
+                }
+                break;
+              case MD_MAC_EXC_RESOURCE_TYPE_THREADS:
+                reason.append("EXC_RESOURCE_TYPE_THREADS / ");
+                if (flavor == MD_MAC_EXC_RESOURCE_FLAVOR_THREADS_HIGH_WATERMARK) {
+                  reason.append("FLAVOR_THREADS_HIGH_WATERMARK");
+                } else {
+                  snprintf(flavor_string, sizeof(flavor_string), "%#3x", flavor);
+                  reason.append(flavor_string);
+                }
+                break;
+              default:
+                reason.append(flags_string);
+                break;
+            }
+          }
+          break;
         case MD_EXCEPTION_MAC_SIMULATED:
           reason = "Simulated Exception";
           break;
       }
       break;
     }
 
     case MD_OS_WIN32_NT: