summaryrefslogtreecommitdiffstats
path: root/xpcom/base/nsObjCExceptions.h
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/base/nsObjCExceptions.h')
-rw-r--r--xpcom/base/nsObjCExceptions.h225
1 files changed, 225 insertions, 0 deletions
diff --git a/xpcom/base/nsObjCExceptions.h b/xpcom/base/nsObjCExceptions.h
new file mode 100644
index 0000000000..89fc11f615
--- /dev/null
+++ b/xpcom/base/nsObjCExceptions.h
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsObjCExceptions_h_
+#define nsObjCExceptions_h_
+
+// Undo the damage that exception_defines.h does.
+#undef try
+#undef catch
+
+#import <Foundation/Foundation.h>
+
+#ifdef DEBUG
+# import <ExceptionHandling/NSExceptionHandler.h>
+#endif
+
+#if defined(__cplusplus)
+# include "nsICrashReporter.h"
+# include "nsCOMPtr.h"
+# include "nsServiceManagerUtils.h"
+#endif
+
+#include <unistd.h>
+#include <signal.h>
+#include "nsError.h"
+
+// Undo the damage that exception_defines.h does.
+#undef try
+#undef catch
+
+/* NOTE: Macros that claim to abort no longer abort, see bug 486574.
+ * If you actually want to log and abort, call "nsObjCExceptionLogAbort"
+ * from an exception handler. At some point we will fix this by replacing
+ * all macros in the tree with appropriately-named macros.
+ */
+
+// See Mozilla bug 163260.
+// This file can only be included in an Objective-C context.
+
+__attribute__((unused)) static void nsObjCExceptionLog(NSException* aException) {
+ NSLog(@"Mozilla has caught an Obj-C exception [%@: %@]", [aException name], [aException reason]);
+
+#if 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)];
+ if ([addresses count]) {
+ stackTrace = addresses;
+ }
+ }
+
+ // 10.4 doesn't respond to callStackReturnAddresses so we'll try to pull the
+ // stack info out of the userInfo. It might not be there, sadly :(
+ if (!stackTrace) {
+ stackTrace = [[aException userInfo] objectForKey:NSStackTraceKey];
+ }
+
+ if (stackTrace) {
+ // The command line should look like this:
+ // /usr/bin/atos -p <pid> -printHeader <stack frame addresses>
+ NSMutableArray* args = [NSMutableArray arrayWithCapacity:[stackTrace count] + 3];
+
+ [args addObject:@"-p"];
+ int pid = [[NSProcessInfo processInfo] processIdentifier];
+ [args addObject:[NSString stringWithFormat:@"%d", pid]];
+
+ [args addObject:@"-printHeader"];
+
+ unsigned int stackCount = [stackTrace count];
+ unsigned int stackIndex = 0;
+ for (; stackIndex < stackCount; stackIndex++) {
+ unsigned long address = [[stackTrace objectAtIndex:stackIndex] unsignedLongValue];
+ [args addObject:[NSString stringWithFormat:@"0x%lx", address]];
+ }
+
+ NSPipe* outPipe = [NSPipe pipe];
+
+ NSTask* task = [[NSTask alloc] init];
+ [task setLaunchPath:@"/usr/bin/atos"];
+ [task setArguments:args];
+ [task setStandardOutput:outPipe];
+ [task setStandardError:outPipe];
+
+ NSLog(@"Generating stack trace for Obj-C exception...");
+
+ // This will throw an exception if the atos tool cannot be found, and in
+ // that case we'll just hit our @catch block below.
+ [task launch];
+
+ [task waitUntilExit];
+ [task release];
+
+ NSData* outData = [[outPipe fileHandleForReading] readDataToEndOfFile];
+ NSString* outString = [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding];
+
+ NSLog(@"Stack trace:\n%@", outString);
+
+ [outString release];
+ } else {
+ NSLog(@"<No stack information available for Obj-C exception>");
+ }
+ } @catch (NSException* exn) {
+ NSLog(@"Failed to generate stack trace for Obj-C exception [%@: %@]", [exn name], [exn reason]);
+ }
+#endif
+}
+
+__attribute__((unused)) static void nsObjCExceptionAbort() {
+ // We need to raise a mach-o signal here, the Mozilla crash reporter on
+ // Mac OS X does not respond to POSIX signals. Raising mach-o signals directly
+ // is tricky so we do it by just derefing a null pointer.
+ int* foo = nullptr;
+ *foo = 1;
+}
+
+__attribute__((unused)) static void nsObjCExceptionLogAbort(NSException* aException) {
+ nsObjCExceptionLog(aException);
+ nsObjCExceptionAbort();
+}
+
+#define NS_OBJC_TRY(_e, _fail) \
+ @try { \
+ _e; \
+ } @catch (NSException * _exn) { \
+ nsObjCExceptionLog(_exn); \
+ _fail; \
+ }
+
+#define NS_OBJC_TRY_EXPR(_e, _fail) \
+ ({ \
+ typeof(_e) _tmp; \
+ @try { \
+ _tmp = (_e); \
+ } @catch (NSException * _exn) { \
+ nsObjCExceptionLog(_exn); \
+ _fail; \
+ } \
+ _tmp; \
+ })
+
+#define NS_OBJC_TRY_EXPR_NULL(_e) NS_OBJC_TRY_EXPR(_e, 0)
+
+#define NS_OBJC_TRY_IGNORE(_e) NS_OBJC_TRY(_e, )
+
+// To reduce code size the abort versions do not reuse above macros. This allows
+// catch blocks to only contain one call.
+
+#define NS_OBJC_TRY_ABORT(_e) \
+ @try { \
+ _e; \
+ } @catch (NSException * _exn) { \
+ nsObjCExceptionLog(_exn); \
+ }
+
+#define NS_OBJC_TRY_EXPR_ABORT(_e) \
+ ({ \
+ typeof(_e) _tmp; \
+ @try { \
+ _tmp = (_e); \
+ } @catch (NSException * _exn) { \
+ nsObjCExceptionLog(_exn); \
+ } \
+ _tmp; \
+ })
+
+// For wrapping blocks of Obj-C calls. Does not actually terminate.
+#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK @try {
+#define NS_OBJC_END_TRY_ABORT_BLOCK \
+ } \
+ @catch (NSException * _exn) { \
+ nsObjCExceptionLog(_exn); \
+ }
+
+// Same as above ABORT_BLOCK but returns a value after the try/catch block to
+// suppress compiler warnings. This allows us to avoid having to refactor code
+// to get scoping right when wrapping an entire method.
+
+#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL @try {
+#define NS_OBJC_END_TRY_ABORT_BLOCK_NIL \
+ } \
+ @catch (NSException * _exn) { \
+ nsObjCExceptionLog(_exn); \
+ } \
+ return nil;
+
+#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL @try {
+#define NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL \
+ } \
+ @catch (NSException * _exn) { \
+ nsObjCExceptionLog(_exn); \
+ } \
+ return nullptr;
+
+#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT @try {
+#define NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT \
+ } \
+ @catch (NSException * _exn) { \
+ nsObjCExceptionLog(_exn); \
+ } \
+ return NS_ERROR_FAILURE;
+
+#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN @try {
+#define NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(_rv) \
+ } \
+ @catch (NSException * _exn) { \
+ nsObjCExceptionLog(_exn); \
+ } \
+ return _rv;
+
+#endif // nsObjCExceptions_h_