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

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

mercurial