Include obj-c exception info in crash reports. b=451709 r=ted r=bent sr=roc
authorJosh Aas <joshmoz@gmail.com>
Thu, 28 Aug 2008 00:41:38 -0400
changeset 18492 2e06eade69ced0a79961eb77e735991d842c2758
parent 18491 d353f90de69a80f09ef8cbd9462058810450396c
child 18493 cdec704dbbd1e43315cb8da24f00f2d2985951d1
push idunknown
push userunknown
push dateunknown
reviewersted, bent, roc
bugs451709
milestone1.9.1a2pre
Include obj-c exception info in crash reports. b=451709 r=ted r=bent sr=roc
toolkit/crashreporter/mac_utils.h
toolkit/crashreporter/mac_utils.mm
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/crashreporter/nsExceptionHandler.h
toolkit/xre/MacLaunchHelper.h
toolkit/xre/MacLaunchHelper.m
toolkit/xre/MacLaunchHelper.mm
toolkit/xre/Makefile.in
toolkit/xre/nsAppRunner.cpp
xpcom/base/nsObjCExceptions.h
xpcom/system/nsICrashReporter.idl
--- a/toolkit/crashreporter/mac_utils.h
+++ b/toolkit/crashreporter/mac_utils.h
@@ -33,15 +33,20 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef toolkit_breakpad_mac_utils_h__
 #define toolkit_breakpad_mac_utils_h__
 
+#include "nsStringGlue.h"
+
 /*
  * Look up a setting in our user defaults indicating
  * that the user wants to see the OS crash reporting dialog.
  */
 bool PassToOSCrashReporter();
 
+// Given an Objective-C NSException object, put exception info into a string.
+void GetObjCExceptionInfo(void* inException, nsACString& outString);
+
 #endif /* toolkit_breakpad_mac_utils_h__ */
--- a/toolkit/crashreporter/mac_utils.mm
+++ b/toolkit/crashreporter/mac_utils.mm
@@ -15,16 +15,17 @@
  * The Original Code is Mozilla Breakpad integration
  *
  * The Initial Developer of the Original Code is
  * Ted Mielczarek <ted.mielczarek@gmail.com>
  * Portions created by the Initial Developer are Copyright (C) 2007
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
+ *  Josh Aas <josh@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -34,22 +35,55 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <Foundation/Foundation.h>
 
 #include "mac_utils.h"
 #include "nsObjCExceptions.h"
+#include "nsXPCOM.h"
 
 bool PassToOSCrashReporter()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
   NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
   BOOL osCrashReporter = [userDefaults boolForKey:@"OSCrashReporter"];
   [pool release];
 
   return osCrashReporter == YES;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
 }
+
+void GetObjCExceptionInfo(void* inException, nsACString& outString)
+{
+  NSException* e = (NSException*)inException;
+
+  NSString* name = [e name];
+  NSString* reason = [e reason];
+  unsigned int nameLength = [name length];
+  unsigned int reasonLength = [reason length];
+
+  unichar* nameBuffer = (unichar*)NS_Alloc(sizeof(unichar) * (nameLength + 1));
+  if (!nameBuffer)
+    return;
+  unichar* reasonBuffer = (unichar*)NS_Alloc(sizeof(unichar) * (reasonLength + 1));
+  if (!reasonBuffer) {
+    NS_Free(nameBuffer);
+    return;
+  }
+
+  [name getCharacters:nameBuffer];
+  [reason getCharacters:reasonBuffer];
+  nameBuffer[nameLength] = '\0';
+  reasonBuffer[reasonLength] = '\0';
+
+  outString.AssignLiteral("\nObj-C Exception data:\n");
+  AppendUTF16toUTF8(nameBuffer, outString);
+  outString.AppendLiteral(": ");
+  AppendUTF16toUTF8(reasonBuffer, outString);
+
+  NS_Free(nameBuffer);
+  NS_Free(reasonBuffer);
+}
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -15,16 +15,17 @@
  * The Original Code is Mozilla Breakpad integration
  *
  * The Initial Developer of the Original Code is
  * Ted Mielczarek <ted.mielczarek@gmail.com>
  * Portions created by the Initial Developer are Copyright (C) 2006
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
+ *  Josh Aas <josh@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -880,9 +881,19 @@ nsresult WriteMinidumpForException(EXCEP
 {
   if (!gExceptionHandler)
     return NS_ERROR_NOT_INITIALIZED;
 
   return gExceptionHandler->WriteMinidumpForException(aExceptionInfo) ? NS_OK : NS_ERROR_FAILURE;
 }
 #endif
 
+#ifdef XP_MACOSX
+nsresult AppendObjCExceptionInfoToAppNotes(void *inException)
+{
+  nsCAutoString excString;
+  GetObjCExceptionInfo(inException, excString);
+  AppendAppNotesToCrashReport(excString);
+  return NS_OK;
+}
+#endif
+
 } // namespace CrashReporter
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -57,11 +57,14 @@ nsresult UnsetExceptionHandler();
 nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data);
 nsresult AppendAppNotesToCrashReport(const nsACString& data);
 nsresult SetRestartArgs(int argc, char** argv);
 nsresult SetupExtraData(nsILocalFile* aAppDataDirectory,
                         const nsACString& aBuildID);
 #ifdef XP_WIN32
   nsresult WriteMinidumpForException(EXCEPTION_POINTERS* aExceptionInfo);
 #endif
+#ifdef XP_MACOSX
+  nsresult AppendObjCExceptionInfoToAppNotes(void *inException);
+#endif
 }
 
 #endif /* nsExceptionHandler_h__ */
--- a/toolkit/xre/MacLaunchHelper.h
+++ b/toolkit/xre/MacLaunchHelper.h
@@ -34,17 +34,13 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __MacLaunchHelper_h__
 #define __MacLaunchHelper_h__
 
-#ifdef __cplusplus
 extern "C" {
-#endif
   void LaunchChildMac(int aArgc, char** aArgv);
-#ifdef __cplusplus
 }
-#endif
 
 #endif
deleted file mode 100644
--- a/toolkit/xre/MacLaunchHelper.m
+++ /dev/null
@@ -1,96 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Communicator client code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Ben Goodger <ben@mozilla.org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "MacLaunchHelper.h"
-
-#include "nsObjCExceptions.h"
-
-#include <Cocoa/Cocoa.h>
-
-#ifdef __ppc__
-#include <sys/types.h>
-#include <sys/sysctl.h>
-#include <mach/machine.h>
-#endif /* __ppc__ */
-
-void LaunchChildMac(int aArgc, char** aArgv)
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
-
-  int i;
-  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
-  NSTask* child = [[[NSTask alloc] init] autorelease];
-  NSMutableArray* args = [[[NSMutableArray alloc] init] autorelease];
-
-#ifdef __ppc__
-  // It's possible that the app is a universal binary running under Rosetta
-  // translation because the user forced it to.  Relaunching via NSTask would
-  // launch the app natively, which the user apparently doesn't want.
-  // In that case, try to preserve translation.
-
-  // If the sysctl doesn't exist, it's because Rosetta doesn't exist,
-  // so don't try to force translation.  In case of other errors, just assume
-  // that the app is native.
-
-  int isNative = 0;
-  size_t sz = sizeof(isNative);
-
-  if (sysctlbyname("sysctl.proc_native", &isNative, &sz, NULL, 0) == 0 &&
-      !isNative) {
-    // Running translated on ppc.
-
-    cpu_type_t preferredCPU = CPU_TYPE_POWERPC;
-    sysctlbyname("sysctl.proc_exec_affinity", NULL, NULL,
-                 &preferredCPU, sizeof(preferredCPU));
-
-    // Nothing can be done to handle failure, relaunch anyway.
-  }
-#endif /* __ppc__ */
-
-  for (i = 1; i < aArgc; ++i) 
-    [args addObject: [NSString stringWithCString: aArgv[i]]];
-  
-  [child setCurrentDirectoryPath:[[[NSBundle mainBundle] executablePath] stringByDeletingLastPathComponent]];
-  [child setLaunchPath:[[NSBundle mainBundle] executablePath]];
-  [child setArguments:args];
-  [child launch];
-  [pool release];
-
-  NS_OBJC_END_TRY_ABORT_BLOCK;
-}
-
new file mode 100644
--- /dev/null
+++ b/toolkit/xre/MacLaunchHelper.mm
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Goodger <ben@mozilla.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "MacLaunchHelper.h"
+
+#include "nsObjCExceptions.h"
+
+#include <Cocoa/Cocoa.h>
+
+#ifdef __ppc__
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <mach/machine.h>
+#endif /* __ppc__ */
+
+void LaunchChildMac(int aArgc, char** aArgv)
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  int i;
+  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+  NSTask* child = [[[NSTask alloc] init] autorelease];
+  NSMutableArray* args = [[[NSMutableArray alloc] init] autorelease];
+
+#ifdef __ppc__
+  // It's possible that the app is a universal binary running under Rosetta
+  // translation because the user forced it to.  Relaunching via NSTask would
+  // launch the app natively, which the user apparently doesn't want.
+  // In that case, try to preserve translation.
+
+  // If the sysctl doesn't exist, it's because Rosetta doesn't exist,
+  // so don't try to force translation.  In case of other errors, just assume
+  // that the app is native.
+
+  int isNative = 0;
+  size_t sz = sizeof(isNative);
+
+  if (sysctlbyname("sysctl.proc_native", &isNative, &sz, NULL, 0) == 0 &&
+      !isNative) {
+    // Running translated on ppc.
+
+    cpu_type_t preferredCPU = CPU_TYPE_POWERPC;
+    sysctlbyname("sysctl.proc_exec_affinity", NULL, NULL,
+                 &preferredCPU, sizeof(preferredCPU));
+
+    // Nothing can be done to handle failure, relaunch anyway.
+  }
+#endif /* __ppc__ */
+
+  for (i = 1; i < aArgc; ++i) 
+    [args addObject: [NSString stringWithCString: aArgv[i]]];
+  
+  [child setCurrentDirectoryPath:[[[NSBundle mainBundle] executablePath] stringByDeletingLastPathComponent]];
+  [child setLaunchPath:[[NSBundle mainBundle] executablePath]];
+  [child setArguments:args];
+  [child launch];
+  [pool release];
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
--- a/toolkit/xre/Makefile.in
+++ b/toolkit/xre/Makefile.in
@@ -147,18 +147,18 @@ CPPSRCS += nsNativeAppSupportUnix.cpp
 else
 CPPSRCS += nsNativeAppSupportDefault.cpp
 endif
 endif
 endif
 endif
 endif
 
-ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
-CMSRCS = MacLaunchHelper.m
+ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
+CMMSRCS += MacLaunchHelper.mm
 CPPSRCS += nsCommandLineServiceMac.cpp
 LOCAL_INCLUDES += -I$(topsrcdir)/xpfe/bootstrap/appleevents
 OS_CXXFLAGS += -fexceptions
 SHARED_LIBRARY_LIBS += $(DEPTH)/xpfe/bootstrap/appleevents/$(LIB_PREFIX)appleevents_s.$(LIB_SUFFIX)
 endif
 ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 CMMSRCS += MacApplicationDelegate.mm
 endif
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -855,16 +855,26 @@ nsXULAppInfo::WriteMinidumpForException(
 #ifdef XP_WIN32
   return CrashReporter::WriteMinidumpForException(static_cast<EXCEPTION_POINTERS*>(aExceptionInfo));
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif
 }
 #endif
 
+NS_IMETHODIMP
+nsXULAppInfo::AppendObjCExceptionInfoToAppNotes(void* aException)
+{
+#ifdef XP_MACOSX
+  return CrashReporter::AppendObjCExceptionInfoToAppNotes(aException);
+#else
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
 static const nsXULAppInfo kAppInfo;
 static NS_METHOD AppInfoConstructor(nsISupports* aOuter,
                                     REFNSIID aIID, void **aResult)
 {
   NS_ENSURE_NO_AGGREGATION(aOuter);
 
   return const_cast<nsXULAppInfo*>(&kAppInfo)->
     QueryInterface(aIID, aResult);
--- a/xpcom/base/nsObjCExceptions.h
+++ b/xpcom/base/nsObjCExceptions.h
@@ -41,28 +41,42 @@
 #define nsObjCExceptions_h_
 
 #import <Foundation/Foundation.h>
 
 #ifdef DEBUG
 #import <ExceptionHandling/NSExceptionHandler.h>
 #endif
 
+#if defined(MOZ_CRASHREPORTER) && defined(__cplusplus)
+#include "nsICrashReporter.h"
+#include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h"
+#endif
+
 #include <unistd.h>
 #include <signal.h>
 #include "nsError.h"
 
 // See Mozilla bug 163260.
 // This file can only be included in an Objective-C context.
 
 static void nsObjCExceptionLog(NSException* aException)
 {
   NSLog(@"Mozilla has caught an Obj-C exception [%@: %@]",
         [aException name], [aException reason]);
 
+#if defined(MOZ_CRASHREPORTER) && defined(__cplusplus)
+  // Attach exception info to the crash report.
+  nsCOMPtr<nsICrashReporter> crashReporter =
+    do_GetService("@mozilla.org/toolkit/crash-reporter;1");
+  if (crashReporter)
+    crashReporter->AppendObjCExceptionInfoToAppNotes(static_cast<void*>(aException));
+#endif
+
 #ifdef DEBUG
   @try {
     // Try to get stack information out of the exception. 10.5 returns the stack
     // info with the callStackReturnAddresses selector.
     NSArray *stackTrace = nil;
     if ([aException respondsToSelector:@selector(callStackReturnAddresses)]) {
       NSArray* addresses = (NSArray*)
         [aException performSelector:@selector(callStackReturnAddresses)];
--- a/xpcom/system/nsICrashReporter.idl
+++ b/xpcom/system/nsICrashReporter.idl
@@ -38,17 +38,17 @@
 #include "nsISupports.idl"
 
 /**
  * Provides access to crash reporting functionality.
  * @status UNSTABLE - This interface is not frozen and will probably change in
  *                    future releases.
  */
 
-[scriptable, uuid(189c9392-157c-445f-84db-900eb46d4839)]
+[scriptable, uuid(D9A0F5B2-A7DF-4AEB-9775-21B9E01B4C59)]
 interface nsICrashReporter : nsISupports
 {
   /**
    * Add some extra data to be submitted with a crash report.
    * @param key
    *        Name of the data to be added.
    * @param data
    *        Data to be added.
@@ -74,10 +74,16 @@ interface nsICrashReporter : nsISupports
   void appendAppNotesToCrashReport(in ACString data);
   
   /**
     * Write a minidump immediately, with the user-supplied exception
     * information. This is implemented on Windows only, because
     * SEH (structured exception handling) exists on Windows only.
     * @param aExceptionInfo  EXCEPTION_INFO* provided by Window's SEH
     */
-  [noscript] void WriteMinidumpForException(in voidPtr aExceptionInfo);
+  [noscript] void writeMinidumpForException(in voidPtr aExceptionInfo);
+  
+  /**
+    * Append note containing an Obj-C exception's info.
+    * @param aException  NSException object to append note for
+    */
+  [noscript] void appendObjCExceptionInfoToAppNotes(in voidPtr aException);
 };