xpcom/base/nsObjCExceptions.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 // Undo the damage that exception_defines.h does.
michael@0 7 #undef try
michael@0 8 #undef catch
michael@0 9
michael@0 10 #ifndef nsObjCExceptions_h_
michael@0 11 #define nsObjCExceptions_h_
michael@0 12
michael@0 13 #import <Foundation/Foundation.h>
michael@0 14
michael@0 15 #ifdef DEBUG
michael@0 16 #import <ExceptionHandling/NSExceptionHandler.h>
michael@0 17 #endif
michael@0 18
michael@0 19 #if defined(MOZ_CRASHREPORTER) && defined(__cplusplus)
michael@0 20 #include "nsICrashReporter.h"
michael@0 21 #include "nsCOMPtr.h"
michael@0 22 #include "nsServiceManagerUtils.h"
michael@0 23 #endif
michael@0 24
michael@0 25 #include <unistd.h>
michael@0 26 #include <signal.h>
michael@0 27 #include "nsError.h"
michael@0 28
michael@0 29 // Undo the damage that exception_defines.h does.
michael@0 30 #undef try
michael@0 31 #undef catch
michael@0 32
michael@0 33 /* NOTE: Macros that claim to abort no longer abort, see bug 486574.
michael@0 34 * If you actually want to log and abort, call "nsObjCExceptionLogAbort"
michael@0 35 * from an exception handler. At some point we will fix this by replacing
michael@0 36 * all macros in the tree with appropriately-named macros.
michael@0 37 */
michael@0 38
michael@0 39 // See Mozilla bug 163260.
michael@0 40 // This file can only be included in an Objective-C context.
michael@0 41
michael@0 42 __attribute__((unused))
michael@0 43 static void nsObjCExceptionLog(NSException* aException)
michael@0 44 {
michael@0 45 NSLog(@"Mozilla has caught an Obj-C exception [%@: %@]",
michael@0 46 [aException name], [aException reason]);
michael@0 47
michael@0 48 #if defined(MOZ_CRASHREPORTER) && defined(__cplusplus)
michael@0 49 // Attach exception info to the crash report.
michael@0 50 nsCOMPtr<nsICrashReporter> crashReporter =
michael@0 51 do_GetService("@mozilla.org/toolkit/crash-reporter;1");
michael@0 52 if (crashReporter)
michael@0 53 crashReporter->AppendObjCExceptionInfoToAppNotes(static_cast<void*>(aException));
michael@0 54 #endif
michael@0 55
michael@0 56 #ifdef DEBUG
michael@0 57 @try {
michael@0 58 // Try to get stack information out of the exception. 10.5 returns the stack
michael@0 59 // info with the callStackReturnAddresses selector.
michael@0 60 NSArray *stackTrace = nil;
michael@0 61 if ([aException respondsToSelector:@selector(callStackReturnAddresses)]) {
michael@0 62 NSArray* addresses = (NSArray*)
michael@0 63 [aException performSelector:@selector(callStackReturnAddresses)];
michael@0 64 if ([addresses count])
michael@0 65 stackTrace = addresses;
michael@0 66 }
michael@0 67
michael@0 68 // 10.4 doesn't respond to callStackReturnAddresses so we'll try to pull the
michael@0 69 // stack info out of the userInfo. It might not be there, sadly :(
michael@0 70 if (!stackTrace)
michael@0 71 stackTrace = [[aException userInfo] objectForKey:NSStackTraceKey];
michael@0 72
michael@0 73 if (stackTrace) {
michael@0 74 // The command line should look like this:
michael@0 75 // /usr/bin/atos -p <pid> -printHeader <stack frame addresses>
michael@0 76 NSMutableArray *args =
michael@0 77 [NSMutableArray arrayWithCapacity:[stackTrace count] + 3];
michael@0 78
michael@0 79 [args addObject:@"-p"];
michael@0 80 int pid = [[NSProcessInfo processInfo] processIdentifier];
michael@0 81 [args addObject:[NSString stringWithFormat:@"%d", pid]];
michael@0 82
michael@0 83 [args addObject:@"-printHeader"];
michael@0 84
michael@0 85 unsigned int stackCount = [stackTrace count];
michael@0 86 unsigned int stackIndex = 0;
michael@0 87 for (; stackIndex < stackCount; stackIndex++) {
michael@0 88 unsigned long address =
michael@0 89 [[stackTrace objectAtIndex:stackIndex] unsignedLongValue];
michael@0 90 [args addObject:[NSString stringWithFormat:@"0x%lx", address]];
michael@0 91 }
michael@0 92
michael@0 93 NSPipe *outPipe = [NSPipe pipe];
michael@0 94
michael@0 95 NSTask *task = [[NSTask alloc] init];
michael@0 96 [task setLaunchPath:@"/usr/bin/atos"];
michael@0 97 [task setArguments:args];
michael@0 98 [task setStandardOutput:outPipe];
michael@0 99 [task setStandardError:outPipe];
michael@0 100
michael@0 101 NSLog(@"Generating stack trace for Obj-C exception...");
michael@0 102
michael@0 103 // This will throw an exception if the atos tool cannot be found, and in
michael@0 104 // that case we'll just hit our @catch block below.
michael@0 105 [task launch];
michael@0 106
michael@0 107 [task waitUntilExit];
michael@0 108 [task release];
michael@0 109
michael@0 110 NSData *outData =
michael@0 111 [[outPipe fileHandleForReading] readDataToEndOfFile];
michael@0 112 NSString *outString =
michael@0 113 [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding];
michael@0 114
michael@0 115 NSLog(@"Stack trace:\n%@", outString);
michael@0 116
michael@0 117 [outString release];
michael@0 118 }
michael@0 119 else {
michael@0 120 NSLog(@"<No stack information available for Obj-C exception>");
michael@0 121 }
michael@0 122 }
michael@0 123 @catch (NSException *exn) {
michael@0 124 NSLog(@"Failed to generate stack trace for Obj-C exception [%@: %@]",
michael@0 125 [exn name], [exn reason]);
michael@0 126 }
michael@0 127 #endif
michael@0 128 }
michael@0 129
michael@0 130 __attribute__((unused))
michael@0 131 static void nsObjCExceptionAbort()
michael@0 132 {
michael@0 133 // We need to raise a mach-o signal here, the Mozilla crash reporter on
michael@0 134 // Mac OS X does not respond to POSIX signals. Raising mach-o signals directly
michael@0 135 // is tricky so we do it by just derefing a null pointer.
michael@0 136 int* foo = nullptr;
michael@0 137 *foo = 1;
michael@0 138 }
michael@0 139
michael@0 140 __attribute__((unused))
michael@0 141 static void nsObjCExceptionLogAbort(NSException *e)
michael@0 142 {
michael@0 143 nsObjCExceptionLog(e);
michael@0 144 nsObjCExceptionAbort();
michael@0 145 }
michael@0 146
michael@0 147 #define NS_OBJC_TRY(_e, _fail) \
michael@0 148 @try { _e; } \
michael@0 149 @catch(NSException *_exn) { \
michael@0 150 nsObjCExceptionLog(_exn); \
michael@0 151 _fail; \
michael@0 152 }
michael@0 153
michael@0 154 #define NS_OBJC_TRY_EXPR(_e, _fail) \
michael@0 155 ({ \
michael@0 156 typeof(_e) _tmp; \
michael@0 157 @try { _tmp = (_e); } \
michael@0 158 @catch(NSException *_exn) { \
michael@0 159 nsObjCExceptionLog(_exn); \
michael@0 160 _fail; \
michael@0 161 } \
michael@0 162 _tmp; \
michael@0 163 })
michael@0 164
michael@0 165 #define NS_OBJC_TRY_EXPR_NULL(_e) \
michael@0 166 NS_OBJC_TRY_EXPR(_e, 0)
michael@0 167
michael@0 168 #define NS_OBJC_TRY_IGNORE(_e) \
michael@0 169 NS_OBJC_TRY(_e, )
michael@0 170
michael@0 171 // To reduce code size the abort versions do not reuse above macros. This allows
michael@0 172 // catch blocks to only contain one call.
michael@0 173
michael@0 174 #define NS_OBJC_TRY_ABORT(_e) \
michael@0 175 @try { _e; } \
michael@0 176 @catch(NSException *_exn) { \
michael@0 177 nsObjCExceptionLog(_exn); \
michael@0 178 }
michael@0 179
michael@0 180 #define NS_OBJC_TRY_EXPR_ABORT(_e) \
michael@0 181 ({ \
michael@0 182 typeof(_e) _tmp; \
michael@0 183 @try { _tmp = (_e); } \
michael@0 184 @catch(NSException *_exn) { \
michael@0 185 nsObjCExceptionLog(_exn); \
michael@0 186 } \
michael@0 187 _tmp; \
michael@0 188 })
michael@0 189
michael@0 190 // For wrapping blocks of Obj-C calls. Does not actually terminate.
michael@0 191 #define NS_OBJC_BEGIN_TRY_ABORT_BLOCK @try {
michael@0 192 #define NS_OBJC_END_TRY_ABORT_BLOCK } @catch(NSException *_exn) { \
michael@0 193 nsObjCExceptionLog(_exn); \
michael@0 194 }
michael@0 195
michael@0 196 // Same as above ABORT_BLOCK but returns a value after the try/catch block to
michael@0 197 // suppress compiler warnings. This allows us to avoid having to refactor code
michael@0 198 // to get scoping right when wrapping an entire method.
michael@0 199
michael@0 200 #define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL @try {
michael@0 201 #define NS_OBJC_END_TRY_ABORT_BLOCK_NIL } @catch(NSException *_exn) { \
michael@0 202 nsObjCExceptionLog(_exn); \
michael@0 203 } \
michael@0 204 return nil;
michael@0 205
michael@0 206 #define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL @try {
michael@0 207 #define NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL } @catch(NSException *_exn) { \
michael@0 208 nsObjCExceptionLog(_exn); \
michael@0 209 } \
michael@0 210 return nullptr;
michael@0 211
michael@0 212 #define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT @try {
michael@0 213 #define NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT } @catch(NSException *_exn) { \
michael@0 214 nsObjCExceptionLog(_exn); \
michael@0 215 } \
michael@0 216 return NS_ERROR_FAILURE;
michael@0 217
michael@0 218 #define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN @try {
michael@0 219 #define NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(_rv) } @catch(NSException *_exn) { \
michael@0 220 nsObjCExceptionLog(_exn);\
michael@0 221 } \
michael@0 222 return _rv;
michael@0 223
michael@0 224 #define NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK @try {
michael@0 225 #define NS_OBJC_END_TRY_LOGONLY_BLOCK } @catch(NSException *_exn) { \
michael@0 226 nsObjCExceptionLog(_exn); \
michael@0 227 }
michael@0 228
michael@0 229 #define NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK_RETURN @try {
michael@0 230 #define NS_OBJC_END_TRY_LOGONLY_BLOCK_RETURN(_rv) } @catch(NSException *_exn) { \
michael@0 231 nsObjCExceptionLog(_exn); \
michael@0 232 } \
michael@0 233 return _rv;
michael@0 234
michael@0 235 #endif // nsObjCExceptions_h_

mercurial