diff options
Diffstat (limited to 'xpcom/base/nsObjCExceptions.h')
-rw-r--r-- | xpcom/base/nsObjCExceptions.h | 225 |
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_ |