xpcom/base/nsObjCExceptions.h

changeset 0
6474c204b198
     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_

mercurial