1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/base/nsObjCExceptions.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,235 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// Undo the damage that exception_defines.h does. 1.10 +#undef try 1.11 +#undef catch 1.12 + 1.13 +#ifndef nsObjCExceptions_h_ 1.14 +#define nsObjCExceptions_h_ 1.15 + 1.16 +#import <Foundation/Foundation.h> 1.17 + 1.18 +#ifdef DEBUG 1.19 +#import <ExceptionHandling/NSExceptionHandler.h> 1.20 +#endif 1.21 + 1.22 +#if defined(MOZ_CRASHREPORTER) && defined(__cplusplus) 1.23 +#include "nsICrashReporter.h" 1.24 +#include "nsCOMPtr.h" 1.25 +#include "nsServiceManagerUtils.h" 1.26 +#endif 1.27 + 1.28 +#include <unistd.h> 1.29 +#include <signal.h> 1.30 +#include "nsError.h" 1.31 + 1.32 +// Undo the damage that exception_defines.h does. 1.33 +#undef try 1.34 +#undef catch 1.35 + 1.36 +/* NOTE: Macros that claim to abort no longer abort, see bug 486574. 1.37 + * If you actually want to log and abort, call "nsObjCExceptionLogAbort" 1.38 + * from an exception handler. At some point we will fix this by replacing 1.39 + * all macros in the tree with appropriately-named macros. 1.40 + */ 1.41 + 1.42 +// See Mozilla bug 163260. 1.43 +// This file can only be included in an Objective-C context. 1.44 + 1.45 +__attribute__((unused)) 1.46 +static void nsObjCExceptionLog(NSException* aException) 1.47 +{ 1.48 + NSLog(@"Mozilla has caught an Obj-C exception [%@: %@]", 1.49 + [aException name], [aException reason]); 1.50 + 1.51 +#if defined(MOZ_CRASHREPORTER) && defined(__cplusplus) 1.52 + // Attach exception info to the crash report. 1.53 + nsCOMPtr<nsICrashReporter> crashReporter = 1.54 + do_GetService("@mozilla.org/toolkit/crash-reporter;1"); 1.55 + if (crashReporter) 1.56 + crashReporter->AppendObjCExceptionInfoToAppNotes(static_cast<void*>(aException)); 1.57 +#endif 1.58 + 1.59 +#ifdef DEBUG 1.60 + @try { 1.61 + // Try to get stack information out of the exception. 10.5 returns the stack 1.62 + // info with the callStackReturnAddresses selector. 1.63 + NSArray *stackTrace = nil; 1.64 + if ([aException respondsToSelector:@selector(callStackReturnAddresses)]) { 1.65 + NSArray* addresses = (NSArray*) 1.66 + [aException performSelector:@selector(callStackReturnAddresses)]; 1.67 + if ([addresses count]) 1.68 + stackTrace = addresses; 1.69 + } 1.70 + 1.71 + // 10.4 doesn't respond to callStackReturnAddresses so we'll try to pull the 1.72 + // stack info out of the userInfo. It might not be there, sadly :( 1.73 + if (!stackTrace) 1.74 + stackTrace = [[aException userInfo] objectForKey:NSStackTraceKey]; 1.75 + 1.76 + if (stackTrace) { 1.77 + // The command line should look like this: 1.78 + // /usr/bin/atos -p <pid> -printHeader <stack frame addresses> 1.79 + NSMutableArray *args = 1.80 + [NSMutableArray arrayWithCapacity:[stackTrace count] + 3]; 1.81 + 1.82 + [args addObject:@"-p"]; 1.83 + int pid = [[NSProcessInfo processInfo] processIdentifier]; 1.84 + [args addObject:[NSString stringWithFormat:@"%d", pid]]; 1.85 + 1.86 + [args addObject:@"-printHeader"]; 1.87 + 1.88 + unsigned int stackCount = [stackTrace count]; 1.89 + unsigned int stackIndex = 0; 1.90 + for (; stackIndex < stackCount; stackIndex++) { 1.91 + unsigned long address = 1.92 + [[stackTrace objectAtIndex:stackIndex] unsignedLongValue]; 1.93 + [args addObject:[NSString stringWithFormat:@"0x%lx", address]]; 1.94 + } 1.95 + 1.96 + NSPipe *outPipe = [NSPipe pipe]; 1.97 + 1.98 + NSTask *task = [[NSTask alloc] init]; 1.99 + [task setLaunchPath:@"/usr/bin/atos"]; 1.100 + [task setArguments:args]; 1.101 + [task setStandardOutput:outPipe]; 1.102 + [task setStandardError:outPipe]; 1.103 + 1.104 + NSLog(@"Generating stack trace for Obj-C exception..."); 1.105 + 1.106 + // This will throw an exception if the atos tool cannot be found, and in 1.107 + // that case we'll just hit our @catch block below. 1.108 + [task launch]; 1.109 + 1.110 + [task waitUntilExit]; 1.111 + [task release]; 1.112 + 1.113 + NSData *outData = 1.114 + [[outPipe fileHandleForReading] readDataToEndOfFile]; 1.115 + NSString *outString = 1.116 + [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding]; 1.117 + 1.118 + NSLog(@"Stack trace:\n%@", outString); 1.119 + 1.120 + [outString release]; 1.121 + } 1.122 + else { 1.123 + NSLog(@"<No stack information available for Obj-C exception>"); 1.124 + } 1.125 + } 1.126 + @catch (NSException *exn) { 1.127 + NSLog(@"Failed to generate stack trace for Obj-C exception [%@: %@]", 1.128 + [exn name], [exn reason]); 1.129 + } 1.130 +#endif 1.131 +} 1.132 + 1.133 +__attribute__((unused)) 1.134 +static void nsObjCExceptionAbort() 1.135 +{ 1.136 + // We need to raise a mach-o signal here, the Mozilla crash reporter on 1.137 + // Mac OS X does not respond to POSIX signals. Raising mach-o signals directly 1.138 + // is tricky so we do it by just derefing a null pointer. 1.139 + int* foo = nullptr; 1.140 + *foo = 1; 1.141 +} 1.142 + 1.143 +__attribute__((unused)) 1.144 +static void nsObjCExceptionLogAbort(NSException *e) 1.145 +{ 1.146 + nsObjCExceptionLog(e); 1.147 + nsObjCExceptionAbort(); 1.148 +} 1.149 + 1.150 +#define NS_OBJC_TRY(_e, _fail) \ 1.151 +@try { _e; } \ 1.152 +@catch(NSException *_exn) { \ 1.153 + nsObjCExceptionLog(_exn); \ 1.154 + _fail; \ 1.155 +} 1.156 + 1.157 +#define NS_OBJC_TRY_EXPR(_e, _fail) \ 1.158 +({ \ 1.159 + typeof(_e) _tmp; \ 1.160 + @try { _tmp = (_e); } \ 1.161 + @catch(NSException *_exn) { \ 1.162 + nsObjCExceptionLog(_exn); \ 1.163 + _fail; \ 1.164 + } \ 1.165 + _tmp; \ 1.166 +}) 1.167 + 1.168 +#define NS_OBJC_TRY_EXPR_NULL(_e) \ 1.169 +NS_OBJC_TRY_EXPR(_e, 0) 1.170 + 1.171 +#define NS_OBJC_TRY_IGNORE(_e) \ 1.172 +NS_OBJC_TRY(_e, ) 1.173 + 1.174 +// To reduce code size the abort versions do not reuse above macros. This allows 1.175 +// catch blocks to only contain one call. 1.176 + 1.177 +#define NS_OBJC_TRY_ABORT(_e) \ 1.178 +@try { _e; } \ 1.179 +@catch(NSException *_exn) { \ 1.180 + nsObjCExceptionLog(_exn); \ 1.181 +} 1.182 + 1.183 +#define NS_OBJC_TRY_EXPR_ABORT(_e) \ 1.184 +({ \ 1.185 + typeof(_e) _tmp; \ 1.186 + @try { _tmp = (_e); } \ 1.187 + @catch(NSException *_exn) { \ 1.188 + nsObjCExceptionLog(_exn); \ 1.189 + } \ 1.190 + _tmp; \ 1.191 +}) 1.192 + 1.193 +// For wrapping blocks of Obj-C calls. Does not actually terminate. 1.194 +#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK @try { 1.195 +#define NS_OBJC_END_TRY_ABORT_BLOCK } @catch(NSException *_exn) { \ 1.196 + nsObjCExceptionLog(_exn); \ 1.197 + } 1.198 + 1.199 +// Same as above ABORT_BLOCK but returns a value after the try/catch block to 1.200 +// suppress compiler warnings. This allows us to avoid having to refactor code 1.201 +// to get scoping right when wrapping an entire method. 1.202 + 1.203 +#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL @try { 1.204 +#define NS_OBJC_END_TRY_ABORT_BLOCK_NIL } @catch(NSException *_exn) { \ 1.205 + nsObjCExceptionLog(_exn); \ 1.206 + } \ 1.207 + return nil; 1.208 + 1.209 +#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL @try { 1.210 +#define NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL } @catch(NSException *_exn) { \ 1.211 + nsObjCExceptionLog(_exn); \ 1.212 + } \ 1.213 + return nullptr; 1.214 + 1.215 +#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT @try { 1.216 +#define NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT } @catch(NSException *_exn) { \ 1.217 + nsObjCExceptionLog(_exn); \ 1.218 + } \ 1.219 + return NS_ERROR_FAILURE; 1.220 + 1.221 +#define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN @try { 1.222 +#define NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(_rv) } @catch(NSException *_exn) { \ 1.223 + nsObjCExceptionLog(_exn);\ 1.224 + } \ 1.225 + return _rv; 1.226 + 1.227 +#define NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK @try { 1.228 +#define NS_OBJC_END_TRY_LOGONLY_BLOCK } @catch(NSException *_exn) { \ 1.229 + nsObjCExceptionLog(_exn); \ 1.230 + } 1.231 + 1.232 +#define NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK_RETURN @try { 1.233 +#define NS_OBJC_END_TRY_LOGONLY_BLOCK_RETURN(_rv) } @catch(NSException *_exn) { \ 1.234 + nsObjCExceptionLog(_exn); \ 1.235 + } \ 1.236 + return _rv; 1.237 + 1.238 +#endif // nsObjCExceptions_h_