toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.mm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.mm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,840 @@
     1.4 +// Copyright (c) 2011, Google Inc.
     1.5 +// All rights reserved.
     1.6 +//
     1.7 +// Redistribution and use in source and binary forms, with or without
     1.8 +// modification, are permitted provided that the following conditions are
     1.9 +// met:
    1.10 +//
    1.11 +//     * Redistributions of source code must retain the above copyright
    1.12 +// notice, this list of conditions and the following disclaimer.
    1.13 +//     * Redistributions in binary form must reproduce the above
    1.14 +// copyright notice, this list of conditions and the following disclaimer
    1.15 +// in the documentation and/or other materials provided with the
    1.16 +// distribution.
    1.17 +//     * Neither the name of Google Inc. nor the names of its
    1.18 +// contributors may be used to endorse or promote products derived from
    1.19 +// this software without specific prior written permission.
    1.20 +//
    1.21 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    1.22 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    1.23 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.24 +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    1.25 +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.26 +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    1.27 +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.28 +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    1.29 +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    1.30 +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    1.31 +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.32 +
    1.33 +#define VERBOSE 0
    1.34 +
    1.35 +#if VERBOSE
    1.36 +  static bool gDebugLog = true;
    1.37 +#else
    1.38 +  static bool gDebugLog = false;
    1.39 +#endif
    1.40 +
    1.41 +#define DEBUGLOG if (gDebugLog) fprintf
    1.42 +#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
    1.43 +
    1.44 +#import "client/ios/Breakpad.h"
    1.45 +
    1.46 +#import <Foundation/Foundation.h>
    1.47 +#include <pthread.h>
    1.48 +#include <sys/stat.h>
    1.49 +#include <sys/sysctl.h>
    1.50 +
    1.51 +#import "client/mac/crash_generation/ConfigFile.h"
    1.52 +#import "client/mac/handler/exception_handler.h"
    1.53 +#import "client/mac/handler/minidump_generator.h"
    1.54 +#import "client/mac/sender/uploader.h"
    1.55 +#import "common/mac/SimpleStringDictionary.h"
    1.56 +#import "client/ios/handler/ios_exception_minidump_generator.h"
    1.57 +#import "client/mac/handler/protected_memory_allocator.h"
    1.58 +
    1.59 +#ifndef __EXCEPTIONS
    1.60 +// This file uses C++ try/catch (but shouldn't). Duplicate the macros from
    1.61 +// <c++/4.2.1/exception_defines.h> allowing this file to work properly with
    1.62 +// exceptions disabled even when other C++ libraries are used. #undef the try
    1.63 +// and catch macros first in case libstdc++ is in use and has already provided
    1.64 +// its own definitions.
    1.65 +#undef try
    1.66 +#define try       if (true)
    1.67 +#undef catch
    1.68 +#define catch(X)  if (false)
    1.69 +#endif  // __EXCEPTIONS
    1.70 +
    1.71 +using google_breakpad::ConfigFile;
    1.72 +using google_breakpad::EnsureDirectoryPathExists;
    1.73 +using google_breakpad::KeyValueEntry;
    1.74 +using google_breakpad::SimpleStringDictionary;
    1.75 +using google_breakpad::SimpleStringDictionaryIterator;
    1.76 +
    1.77 +//=============================================================================
    1.78 +// We want any memory allocations which are used by breakpad during the
    1.79 +// exception handling process (after a crash has happened) to be read-only
    1.80 +// to prevent them from being smashed before a crash occurs.  Unfortunately
    1.81 +// we cannot protect against smashes to our exception handling thread's
    1.82 +// stack.
    1.83 +//
    1.84 +// NOTE: Any memory allocations which are not used during the exception
    1.85 +// handling process may be allocated in the normal ways.
    1.86 +//
    1.87 +// The ProtectedMemoryAllocator class provides an Allocate() method which
    1.88 +// we'll using in conjunction with placement operator new() to control
    1.89 +// allocation of C++ objects.  Note that we don't use operator delete()
    1.90 +// but instead call the objects destructor directly:  object->~ClassName();
    1.91 +//
    1.92 +ProtectedMemoryAllocator *gMasterAllocator = NULL;
    1.93 +ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
    1.94 +ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
    1.95 +
    1.96 +// Mutex for thread-safe access to the key/value dictionary used by breakpad.
    1.97 +// It's a global instead of an instance variable of Breakpad
    1.98 +// since it can't live in a protected memory area.
    1.99 +pthread_mutex_t gDictionaryMutex;
   1.100 +
   1.101 +//=============================================================================
   1.102 +// Stack-based object for thread-safe access to a memory-protected region.
   1.103 +// It's assumed that normally the memory block (allocated by the allocator)
   1.104 +// is protected (read-only).  Creating a stack-based instance of
   1.105 +// ProtectedMemoryLocker will unprotect this block after taking the lock.
   1.106 +// Its destructor will first re-protect the memory then release the lock.
   1.107 +class ProtectedMemoryLocker {
   1.108 +public:
   1.109 +  // allocator may be NULL, in which case no Protect() or Unprotect() calls
   1.110 +  // will be made, but a lock will still be taken
   1.111 +  ProtectedMemoryLocker(pthread_mutex_t *mutex,
   1.112 +                        ProtectedMemoryAllocator *allocator)
   1.113 +  : mutex_(mutex), allocator_(allocator) {
   1.114 +    // Lock the mutex
   1.115 +    assert(pthread_mutex_lock(mutex_) == 0);
   1.116 +
   1.117 +    // Unprotect the memory
   1.118 +    if (allocator_ ) {
   1.119 +      allocator_->Unprotect();
   1.120 +    }
   1.121 +  }
   1.122 +
   1.123 +  ~ProtectedMemoryLocker() {
   1.124 +    // First protect the memory
   1.125 +    if (allocator_) {
   1.126 +      allocator_->Protect();
   1.127 +    }
   1.128 +
   1.129 +    // Then unlock the mutex
   1.130 +    assert(pthread_mutex_unlock(mutex_) == 0);
   1.131 +  };
   1.132 +
   1.133 +private:
   1.134 +  //  Keep anybody from ever creating one of these things not on the stack.
   1.135 +  ProtectedMemoryLocker() { }
   1.136 +  ProtectedMemoryLocker(const ProtectedMemoryLocker&);
   1.137 +  ProtectedMemoryLocker & operator=(ProtectedMemoryLocker&);
   1.138 +
   1.139 +  pthread_mutex_t           *mutex_;
   1.140 +  ProtectedMemoryAllocator  *allocator_;
   1.141 +};
   1.142 +
   1.143 +//=============================================================================
   1.144 +class Breakpad {
   1.145 + public:
   1.146 +  // factory method
   1.147 +  static Breakpad *Create(NSDictionary *parameters) {
   1.148 +    // Allocate from our special allocation pool
   1.149 +    Breakpad *breakpad =
   1.150 +      new (gBreakpadAllocator->Allocate(sizeof(Breakpad)))
   1.151 +        Breakpad();
   1.152 +
   1.153 +    if (!breakpad)
   1.154 +      return NULL;
   1.155 +
   1.156 +    if (!breakpad->Initialize(parameters)) {
   1.157 +      // Don't use operator delete() here since we allocated from special pool
   1.158 +      breakpad->~Breakpad();
   1.159 +      return NULL;
   1.160 +    }
   1.161 +
   1.162 +    return breakpad;
   1.163 +  }
   1.164 +
   1.165 +  ~Breakpad();
   1.166 +
   1.167 +  void SetKeyValue(NSString *key, NSString *value);
   1.168 +  NSString *KeyValue(NSString *key);
   1.169 +  void RemoveKeyValue(NSString *key);
   1.170 +  NSString *NextCrashReportToUpload();
   1.171 +  void UploadNextReport();
   1.172 +  void UploadData(NSData *data, NSString *name,
   1.173 +                  NSDictionary *server_parameters);
   1.174 +  NSDictionary *GenerateReport(NSDictionary *server_parameters);
   1.175 +
   1.176 + private:
   1.177 +  Breakpad()
   1.178 +    : handler_(NULL),
   1.179 +      config_params_(NULL) {}
   1.180 +
   1.181 +  bool Initialize(NSDictionary *parameters);
   1.182 +
   1.183 +  bool ExtractParameters(NSDictionary *parameters);
   1.184 +
   1.185 +  // Dispatches to HandleMinidump()
   1.186 +  static bool HandleMinidumpCallback(const char *dump_dir,
   1.187 +                                     const char *minidump_id,
   1.188 +                                     void *context, bool succeeded);
   1.189 +
   1.190 +  bool HandleMinidump(const char *dump_dir,
   1.191 +                      const char *minidump_id);
   1.192 +
   1.193 +  // NSException handler
   1.194 +  static void UncaughtExceptionHandler(NSException *exception);
   1.195 +
   1.196 +  // Handle an uncaught NSException.
   1.197 +  void HandleUncaughtException(NSException *exception);
   1.198 +
   1.199 +  // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's
   1.200 +  // MachineExceptions.h, we have to explicitly name the handler.
   1.201 +  google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG)
   1.202 +
   1.203 +  SimpleStringDictionary  *config_params_; // Create parameters (STRONG)
   1.204 +
   1.205 +  ConfigFile config_file_;
   1.206 +
   1.207 +  // A static reference to the current Breakpad instance. Used for handling
   1.208 +  // NSException.
   1.209 +  static Breakpad *current_breakpad_;
   1.210 +};
   1.211 +
   1.212 +Breakpad *Breakpad::current_breakpad_ = NULL;
   1.213 +
   1.214 +#pragma mark -
   1.215 +#pragma mark Helper functions
   1.216 +
   1.217 +//=============================================================================
   1.218 +// Helper functions
   1.219 +
   1.220 +//=============================================================================
   1.221 +static BOOL IsDebuggerActive() {
   1.222 +  BOOL result = NO;
   1.223 +  NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
   1.224 +
   1.225 +  // We check both defaults and the environment variable here
   1.226 +
   1.227 +  BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER];
   1.228 +
   1.229 +  if (!ignoreDebugger) {
   1.230 +    char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER);
   1.231 +    ignoreDebugger =
   1.232 +        (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0;
   1.233 +  }
   1.234 +
   1.235 +  if (!ignoreDebugger) {
   1.236 +    pid_t pid = getpid();
   1.237 +    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
   1.238 +    int mibSize = sizeof(mib) / sizeof(int);
   1.239 +    size_t actualSize;
   1.240 +
   1.241 +    if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) {
   1.242 +      struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize);
   1.243 +
   1.244 +      if (info) {
   1.245 +        // This comes from looking at the Darwin xnu Kernel
   1.246 +        if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0)
   1.247 +          result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO;
   1.248 +
   1.249 +        free(info);
   1.250 +      }
   1.251 +    }
   1.252 +  }
   1.253 +
   1.254 +  return result;
   1.255 +}
   1.256 +
   1.257 +//=============================================================================
   1.258 +bool Breakpad::HandleMinidumpCallback(const char *dump_dir,
   1.259 +                                      const char *minidump_id,
   1.260 +                                      void *context, bool succeeded) {
   1.261 +  Breakpad *breakpad = (Breakpad *)context;
   1.262 +
   1.263 +  // If our context is damaged or something, just return false to indicate that
   1.264 +  // the handler should continue without us.
   1.265 +  if (!breakpad || !succeeded)
   1.266 +    return false;
   1.267 +
   1.268 +  return breakpad->HandleMinidump(dump_dir, minidump_id);
   1.269 +}
   1.270 +
   1.271 +//=============================================================================
   1.272 +void Breakpad::UncaughtExceptionHandler(NSException *exception) {
   1.273 +  NSSetUncaughtExceptionHandler(NULL);
   1.274 +  if (current_breakpad_) {
   1.275 +    current_breakpad_->HandleUncaughtException(exception);
   1.276 +  }
   1.277 +}
   1.278 +
   1.279 +//=============================================================================
   1.280 +#pragma mark -
   1.281 +
   1.282 +//=============================================================================
   1.283 +bool Breakpad::Initialize(NSDictionary *parameters) {
   1.284 +  // Initialize
   1.285 +  current_breakpad_ = this;
   1.286 +  config_params_ = NULL;
   1.287 +  handler_ = NULL;
   1.288 +
   1.289 +  // Gather any user specified parameters
   1.290 +  if (!ExtractParameters(parameters)) {
   1.291 +    return false;
   1.292 +  }
   1.293 +
   1.294 +  // Check for debugger
   1.295 +  if (IsDebuggerActive()) {
   1.296 +    DEBUGLOG(stderr, "Debugger is active:  Not installing handler\n");
   1.297 +    return true;
   1.298 +  }
   1.299 +
   1.300 +  // Create the handler (allocating it in our special protected pool)
   1.301 +  handler_ =
   1.302 +      new (gBreakpadAllocator->Allocate(
   1.303 +          sizeof(google_breakpad::ExceptionHandler)))
   1.304 +          google_breakpad::ExceptionHandler(
   1.305 +              config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY),
   1.306 +              0, &HandleMinidumpCallback, this, true, 0);
   1.307 +  NSSetUncaughtExceptionHandler(&Breakpad::UncaughtExceptionHandler);
   1.308 +  return true;
   1.309 +}
   1.310 +
   1.311 +//=============================================================================
   1.312 +Breakpad::~Breakpad() {
   1.313 +  NSSetUncaughtExceptionHandler(NULL);
   1.314 +  current_breakpad_ = NULL;
   1.315 +  // Note that we don't use operator delete() on these pointers,
   1.316 +  // since they were allocated by ProtectedMemoryAllocator objects.
   1.317 +  //
   1.318 +  if (config_params_) {
   1.319 +    config_params_->~SimpleStringDictionary();
   1.320 +  }
   1.321 +
   1.322 +  if (handler_)
   1.323 +    handler_->~ExceptionHandler();
   1.324 +}
   1.325 +
   1.326 +//=============================================================================
   1.327 +bool Breakpad::ExtractParameters(NSDictionary *parameters) {
   1.328 +  NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE];
   1.329 +  NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
   1.330 +  NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
   1.331 +  NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
   1.332 +  NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
   1.333 +  NSString *vendor =
   1.334 +      [parameters objectForKey:@BREAKPAD_VENDOR];
   1.335 +  NSString *dumpSubdirectory =
   1.336 +      [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
   1.337 +
   1.338 +  NSDictionary *serverParameters =
   1.339 +      [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
   1.340 +
   1.341 +  if (!product)
   1.342 +    product = [parameters objectForKey:@"CFBundleName"];
   1.343 +
   1.344 +  if (!display) {
   1.345 +    display = [parameters objectForKey:@"CFBundleDisplayName"];
   1.346 +    if (!display) {
   1.347 +      display = product;
   1.348 +    }
   1.349 +  }
   1.350 +
   1.351 +  if (!version)
   1.352 +    version = [parameters objectForKey:@"CFBundleVersion"];
   1.353 +
   1.354 +  if (!vendor) {
   1.355 +    vendor = @"Vendor not specified";
   1.356 +  }
   1.357 +
   1.358 +  if (!dumpSubdirectory) {
   1.359 +    NSString *cachePath =
   1.360 +        [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
   1.361 +                                             NSUserDomainMask,
   1.362 +                                             YES)
   1.363 +            objectAtIndex:0];
   1.364 +    dumpSubdirectory =
   1.365 +        [cachePath stringByAppendingPathComponent:@kDefaultLibrarySubdirectory];
   1.366 +
   1.367 +    EnsureDirectoryPathExists(dumpSubdirectory);
   1.368 +  }
   1.369 +
   1.370 +  // The product, version, and URL are required values.
   1.371 +  if (![product length]) {
   1.372 +    DEBUGLOG(stderr, "Missing required product key.\n");
   1.373 +    return false;
   1.374 +  }
   1.375 +
   1.376 +  if (![version length]) {
   1.377 +    DEBUGLOG(stderr, "Missing required version key.\n");
   1.378 +    return false;
   1.379 +  }
   1.380 +
   1.381 +  if (![urlStr length]) {
   1.382 +    DEBUGLOG(stderr, "Missing required URL key.\n");
   1.383 +    return false;
   1.384 +  }
   1.385 +
   1.386 +  config_params_ =
   1.387 +      new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
   1.388 +        SimpleStringDictionary();
   1.389 +
   1.390 +  SimpleStringDictionary &dictionary = *config_params_;
   1.391 +
   1.392 +  dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE,     [serverType UTF8String]);
   1.393 +  dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
   1.394 +  dictionary.SetKeyValue(BREAKPAD_PRODUCT,         [product UTF8String]);
   1.395 +  dictionary.SetKeyValue(BREAKPAD_VERSION,         [version UTF8String]);
   1.396 +  dictionary.SetKeyValue(BREAKPAD_URL,             [urlStr UTF8String]);
   1.397 +  dictionary.SetKeyValue(BREAKPAD_VENDOR,          [vendor UTF8String]);
   1.398 +  dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
   1.399 +                         [dumpSubdirectory UTF8String]);
   1.400 +
   1.401 +  struct timeval tv;
   1.402 +  gettimeofday(&tv, NULL);
   1.403 +  char timeStartedString[32];
   1.404 +  sprintf(timeStartedString, "%zd", tv.tv_sec);
   1.405 +  dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, timeStartedString);
   1.406 +
   1.407 +  if (serverParameters) {
   1.408 +    // For each key-value pair, call BreakpadAddUploadParameter()
   1.409 +    NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
   1.410 +    NSString *aParameter;
   1.411 +    while ((aParameter = [keyEnumerator nextObject])) {
   1.412 +      BreakpadAddUploadParameter(this, aParameter,
   1.413 +				 [serverParameters objectForKey:aParameter]);
   1.414 +    }
   1.415 +  }
   1.416 +  return true;
   1.417 +}
   1.418 +
   1.419 +//=============================================================================
   1.420 +void Breakpad::SetKeyValue(NSString *key, NSString *value) {
   1.421 +  // We allow nil values. This is the same as removing the keyvalue.
   1.422 +  if (!config_params_ || !key)
   1.423 +    return;
   1.424 +
   1.425 +  config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
   1.426 +}
   1.427 +
   1.428 +//=============================================================================
   1.429 +NSString *Breakpad::KeyValue(NSString *key) {
   1.430 +  if (!config_params_ || !key)
   1.431 +    return nil;
   1.432 +
   1.433 +  const char *value = config_params_->GetValueForKey([key UTF8String]);
   1.434 +  return value ? [NSString stringWithUTF8String:value] : nil;
   1.435 +}
   1.436 +
   1.437 +//=============================================================================
   1.438 +void Breakpad::RemoveKeyValue(NSString *key) {
   1.439 +  if (!config_params_ || !key) return;
   1.440 +
   1.441 +  config_params_->RemoveKey([key UTF8String]);
   1.442 +}
   1.443 +
   1.444 +//=============================================================================
   1.445 +NSString *Breakpad::NextCrashReportToUpload() {
   1.446 +  NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
   1.447 +  if (!directory)
   1.448 +    return nil;
   1.449 +  NSArray *dirContents = [[NSFileManager defaultManager]
   1.450 +      contentsOfDirectoryAtPath:directory error:nil];
   1.451 +  NSArray *configs = [dirContents filteredArrayUsingPredicate:[NSPredicate
   1.452 +      predicateWithFormat:@"self BEGINSWITH 'Config-'"]];
   1.453 +  NSString *config = [configs lastObject];
   1.454 +  if (!config)
   1.455 +    return nil;
   1.456 +  return [NSString stringWithFormat:@"%@/%@", directory, config];
   1.457 +}
   1.458 +
   1.459 +//=============================================================================
   1.460 +void Breakpad::UploadNextReport() {
   1.461 +  NSString *configFile = NextCrashReportToUpload();
   1.462 +  if (configFile) {
   1.463 +    Uploader *uploader = [[[Uploader alloc]
   1.464 +        initWithConfigFile:[configFile UTF8String]] autorelease];
   1.465 +    if (uploader)
   1.466 +      [uploader report];
   1.467 +  }
   1.468 +}
   1.469 +
   1.470 +//=============================================================================
   1.471 +void Breakpad::UploadData(NSData *data, NSString *name,
   1.472 +                          NSDictionary *server_parameters) {
   1.473 +  NSMutableDictionary *config = [NSMutableDictionary dictionary];
   1.474 +
   1.475 +  SimpleStringDictionaryIterator it(*config_params_);
   1.476 +  while (const KeyValueEntry *next = it.Next()) {
   1.477 +    [config setValue:[NSString stringWithUTF8String:next->GetValue()]
   1.478 +              forKey:[NSString stringWithUTF8String:next->GetKey()]];
   1.479 +  }
   1.480 +
   1.481 +  Uploader *uploader =
   1.482 +      [[[Uploader alloc] initWithConfig:config] autorelease];
   1.483 +  for (NSString *key in server_parameters) {
   1.484 +    [uploader addServerParameter:[server_parameters objectForKey:key]
   1.485 +                          forKey:key];
   1.486 +  }
   1.487 +  [uploader uploadData:data name:name];
   1.488 +}
   1.489 +
   1.490 +//=============================================================================
   1.491 +NSDictionary *Breakpad::GenerateReport(NSDictionary *server_parameters) {
   1.492 +  NSString *dumpDirAsNSString = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
   1.493 +  if (!dumpDirAsNSString)
   1.494 +    return nil;
   1.495 +  const char *dumpDir = [dumpDirAsNSString UTF8String];
   1.496 +
   1.497 +  google_breakpad::MinidumpGenerator generator(mach_task_self(),
   1.498 +                                               MACH_PORT_NULL);
   1.499 +  std::string dumpId;
   1.500 +  std::string dumpFilename = generator.UniqueNameInDirectory(dumpDir, &dumpId);
   1.501 +  bool success = generator.Write(dumpFilename.c_str());
   1.502 +  if (!success)
   1.503 +    return nil;
   1.504 +
   1.505 +  SimpleStringDictionary params = *config_params_;
   1.506 +  for (NSString *key in server_parameters) {
   1.507 +    params.SetKeyValue([key UTF8String],
   1.508 +                       [[server_parameters objectForKey:key] UTF8String]);
   1.509 +  }
   1.510 +  ConfigFile config_file;
   1.511 +  config_file.WriteFile(dumpDir, &params, dumpDir, dumpId.c_str());
   1.512 +
   1.513 +  // Handle results.
   1.514 +  NSMutableDictionary *result = [NSMutableDictionary dictionary];
   1.515 +  NSString *dumpFullPath = [dumpDirAsNSString stringByAppendingPathComponent:
   1.516 +      [NSString stringWithUTF8String:dumpFilename.c_str()]];
   1.517 +  [result setValue:dumpFullPath
   1.518 +            forKey:@BREAKPAD_OUTPUT_DUMP_FILE];
   1.519 +  [result setValue:[NSString stringWithUTF8String:config_file.GetFilePath()]
   1.520 +            forKey:@BREAKPAD_OUTPUT_CONFIG_FILE];
   1.521 +  return result;
   1.522 +}
   1.523 +
   1.524 +//=============================================================================
   1.525 +bool Breakpad::HandleMinidump(const char *dump_dir,
   1.526 +                              const char *minidump_id) {
   1.527 +  DEBUGLOG(stderr, "Breakpad: a minidump has been created.\n");
   1.528 +
   1.529 +  config_file_.WriteFile(dump_dir,
   1.530 +                         config_params_,
   1.531 +                         dump_dir,
   1.532 +                         minidump_id);
   1.533 +
   1.534 +  // Return true here to indicate that we've processed things as much as we
   1.535 +  // want.
   1.536 +  return true;
   1.537 +}
   1.538 +
   1.539 +//=============================================================================
   1.540 +void Breakpad::HandleUncaughtException(NSException *exception) {
   1.541 +  // Generate the minidump.
   1.542 +  google_breakpad::IosExceptionMinidumpGenerator generator(exception);
   1.543 +  const char *minidump_path =
   1.544 +      config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
   1.545 +  std::string minidump_id;
   1.546 +  std::string minidump_filename = generator.UniqueNameInDirectory(minidump_path,
   1.547 +                                                                  &minidump_id);
   1.548 +  generator.Write(minidump_filename.c_str());
   1.549 +
   1.550 +  // Copy the config params and our custom parameter. This is necessary for 2
   1.551 +  // reasons:
   1.552 +  // 1- config_params_ is protected.
   1.553 +  // 2- If the application crash while trying to handle this exception, a usual
   1.554 +  //    report will be generated. This report must not contain these special
   1.555 +  //    keys.
   1.556 +  SimpleStringDictionary params = *config_params_;
   1.557 +  params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "type", "exception");
   1.558 +  params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionName",
   1.559 +                     [[exception name] UTF8String]);
   1.560 +  params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionReason",
   1.561 +                     [[exception reason] UTF8String]);
   1.562 +
   1.563 +  // And finally write the config file.
   1.564 +  ConfigFile config_file;
   1.565 +  config_file.WriteFile(minidump_path,
   1.566 +                        &params,
   1.567 +                        minidump_path,
   1.568 +                        minidump_id.c_str());
   1.569 +}
   1.570 +
   1.571 +//=============================================================================
   1.572 +
   1.573 +#pragma mark -
   1.574 +#pragma mark Public API
   1.575 +
   1.576 +//=============================================================================
   1.577 +BreakpadRef BreakpadCreate(NSDictionary *parameters) {
   1.578 +  try {
   1.579 +    // This is confusing.  Our two main allocators for breakpad memory are:
   1.580 +    //    - gKeyValueAllocator for the key/value memory
   1.581 +    //    - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other
   1.582 +    //      breakpad allocations which are accessed at exception handling time.
   1.583 +    //
   1.584 +    // But in order to avoid these two allocators themselves from being smashed,
   1.585 +    // we'll protect them as well by allocating them with gMasterAllocator.
   1.586 +    //
   1.587 +    // gMasterAllocator itself will NOT be protected, but this doesn't matter,
   1.588 +    // since once it does its allocations and locks the memory, smashes to
   1.589 +    // itself don't affect anything we care about.
   1.590 +    gMasterAllocator =
   1.591 +        new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
   1.592 +
   1.593 +    gKeyValueAllocator =
   1.594 +        new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
   1.595 +            ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
   1.596 +
   1.597 +    // Create a mutex for use in accessing the SimpleStringDictionary
   1.598 +    int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
   1.599 +    if (mutexResult == 0) {
   1.600 +
   1.601 +      // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
   1.602 +      // Let's round up to the nearest page size.
   1.603 +      //
   1.604 +      int breakpad_pool_size = 4096;
   1.605 +
   1.606 +      /*
   1.607 +       sizeof(Breakpad)
   1.608 +       + sizeof(google_breakpad::ExceptionHandler)
   1.609 +       + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
   1.610 +       */
   1.611 +
   1.612 +      gBreakpadAllocator =
   1.613 +          new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
   1.614 +              ProtectedMemoryAllocator(breakpad_pool_size);
   1.615 +
   1.616 +      // Stack-based autorelease pool for Breakpad::Create() obj-c code.
   1.617 +      NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   1.618 +      Breakpad *breakpad = Breakpad::Create(parameters);
   1.619 +
   1.620 +      if (breakpad) {
   1.621 +        // Make read-only to protect against memory smashers
   1.622 +        gMasterAllocator->Protect();
   1.623 +        gKeyValueAllocator->Protect();
   1.624 +        gBreakpadAllocator->Protect();
   1.625 +        // Can uncomment this line to figure out how much space was actually
   1.626 +        // allocated using this allocator
   1.627 +        //     printf("gBreakpadAllocator allocated size = %d\n",
   1.628 +        //         gBreakpadAllocator->GetAllocatedSize() );
   1.629 +        [pool release];
   1.630 +        return (BreakpadRef)breakpad;
   1.631 +      }
   1.632 +
   1.633 +      [pool release];
   1.634 +    }
   1.635 +  } catch(...) {    // don't let exceptions leave this C API
   1.636 +    fprintf(stderr, "BreakpadCreate() : error\n");
   1.637 +  }
   1.638 +
   1.639 +  if (gKeyValueAllocator) {
   1.640 +    gKeyValueAllocator->~ProtectedMemoryAllocator();
   1.641 +    gKeyValueAllocator = NULL;
   1.642 +  }
   1.643 +
   1.644 +  if (gBreakpadAllocator) {
   1.645 +    gBreakpadAllocator->~ProtectedMemoryAllocator();
   1.646 +    gBreakpadAllocator = NULL;
   1.647 +  }
   1.648 +
   1.649 +  delete gMasterAllocator;
   1.650 +  gMasterAllocator = NULL;
   1.651 +
   1.652 +  return NULL;
   1.653 +}
   1.654 +
   1.655 +//=============================================================================
   1.656 +void BreakpadRelease(BreakpadRef ref) {
   1.657 +  try {
   1.658 +    Breakpad *breakpad = (Breakpad *)ref;
   1.659 +
   1.660 +    if (gMasterAllocator) {
   1.661 +      gMasterAllocator->Unprotect();
   1.662 +      gKeyValueAllocator->Unprotect();
   1.663 +      gBreakpadAllocator->Unprotect();
   1.664 +
   1.665 +      breakpad->~Breakpad();
   1.666 +
   1.667 +      // Unfortunately, it's not possible to deallocate this stuff
   1.668 +      // because the exception handling thread is still finishing up
   1.669 +      // asynchronously at this point...  OK, it could be done with
   1.670 +      // locks, etc.  But since BreakpadRelease() should usually only
   1.671 +      // be called right before the process exits, it's not worth
   1.672 +      // deallocating this stuff.
   1.673 +#if 0
   1.674 +      gKeyValueAllocator->~ProtectedMemoryAllocator();
   1.675 +      gBreakpadAllocator->~ProtectedMemoryAllocator();
   1.676 +      delete gMasterAllocator;
   1.677 +
   1.678 +      gMasterAllocator = NULL;
   1.679 +      gKeyValueAllocator = NULL;
   1.680 +      gBreakpadAllocator = NULL;
   1.681 +#endif
   1.682 +
   1.683 +      pthread_mutex_destroy(&gDictionaryMutex);
   1.684 +    }
   1.685 +  } catch(...) {    // don't let exceptions leave this C API
   1.686 +    fprintf(stderr, "BreakpadRelease() : error\n");
   1.687 +  }
   1.688 +}
   1.689 +
   1.690 +//=============================================================================
   1.691 +void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
   1.692 +  try {
   1.693 +    // Not called at exception time
   1.694 +    Breakpad *breakpad = (Breakpad *)ref;
   1.695 +
   1.696 +    if (breakpad && key && gKeyValueAllocator) {
   1.697 +      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
   1.698 +
   1.699 +      breakpad->SetKeyValue(key, value);
   1.700 +    }
   1.701 +  } catch(...) {    // don't let exceptions leave this C API
   1.702 +    fprintf(stderr, "BreakpadSetKeyValue() : error\n");
   1.703 +  }
   1.704 +}
   1.705 +
   1.706 +void BreakpadAddUploadParameter(BreakpadRef ref,
   1.707 +                                NSString *key,
   1.708 +                                NSString *value) {
   1.709 +  // The only difference, internally, between an upload parameter and
   1.710 +  // a key value one that is set with BreakpadSetKeyValue is that we
   1.711 +  // prepend the keyname with a special prefix.  This informs the
   1.712 +  // crash sender that the parameter should be sent along with the
   1.713 +  // POST of the crash dump upload.
   1.714 +  try {
   1.715 +    Breakpad *breakpad = (Breakpad *)ref;
   1.716 +
   1.717 +    if (breakpad && key && gKeyValueAllocator) {
   1.718 +      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
   1.719 +
   1.720 +      NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
   1.721 +				stringByAppendingString:key];
   1.722 +      breakpad->SetKeyValue(prefixedKey, value);
   1.723 +    }
   1.724 +  } catch(...) {    // don't let exceptions leave this C API
   1.725 +    fprintf(stderr, "BreakpadSetKeyValue() : error\n");
   1.726 +  }
   1.727 +}
   1.728 +
   1.729 +void BreakpadRemoveUploadParameter(BreakpadRef ref,
   1.730 +                                   NSString *key) {
   1.731 +  try {
   1.732 +    // Not called at exception time
   1.733 +    Breakpad *breakpad = (Breakpad *)ref;
   1.734 +
   1.735 +    if (breakpad && key && gKeyValueAllocator) {
   1.736 +      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
   1.737 +
   1.738 +      NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
   1.739 +                                        @BREAKPAD_SERVER_PARAMETER_PREFIX, key];
   1.740 +      breakpad->RemoveKeyValue(prefixedKey);
   1.741 +    }
   1.742 +  } catch(...) {    // don't let exceptions leave this C API
   1.743 +    fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
   1.744 +  }
   1.745 +}
   1.746 +//=============================================================================
   1.747 +NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
   1.748 +  NSString *value = nil;
   1.749 +
   1.750 +  try {
   1.751 +    // Not called at exception time
   1.752 +    Breakpad *breakpad = (Breakpad *)ref;
   1.753 +
   1.754 +    if (!breakpad || !key || !gKeyValueAllocator)
   1.755 +      return nil;
   1.756 +
   1.757 +    ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
   1.758 +
   1.759 +    value = breakpad->KeyValue(key);
   1.760 +  } catch(...) {    // don't let exceptions leave this C API
   1.761 +    fprintf(stderr, "BreakpadKeyValue() : error\n");
   1.762 +  }
   1.763 +
   1.764 +  return value;
   1.765 +}
   1.766 +
   1.767 +//=============================================================================
   1.768 +void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
   1.769 +  try {
   1.770 +    // Not called at exception time
   1.771 +    Breakpad *breakpad = (Breakpad *)ref;
   1.772 +
   1.773 +    if (breakpad && key && gKeyValueAllocator) {
   1.774 +      ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
   1.775 +
   1.776 +      breakpad->RemoveKeyValue(key);
   1.777 +    }
   1.778 +  } catch(...) {    // don't let exceptions leave this C API
   1.779 +    fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
   1.780 +  }
   1.781 +}
   1.782 +
   1.783 +//=============================================================================
   1.784 +bool BreakpadHasCrashReportToUpload(BreakpadRef ref) {
   1.785 +  try {
   1.786 +    // Not called at exception time
   1.787 +    Breakpad *breakpad = (Breakpad *)ref;
   1.788 +
   1.789 +    if (breakpad) {
   1.790 +       return breakpad->NextCrashReportToUpload() != 0;
   1.791 +    }
   1.792 +  } catch(...) {    // don't let exceptions leave this C API
   1.793 +    fprintf(stderr, "BreakpadHasCrashReportToUpload() : error\n");
   1.794 +  }
   1.795 +  return false;
   1.796 +}
   1.797 +
   1.798 +//=============================================================================
   1.799 +void BreakpadUploadNextReport(BreakpadRef ref) {
   1.800 +  try {
   1.801 +    // Not called at exception time
   1.802 +    Breakpad *breakpad = (Breakpad *)ref;
   1.803 +
   1.804 +    if (breakpad) {
   1.805 +       breakpad->UploadNextReport();
   1.806 +    }
   1.807 +  } catch(...) {    // don't let exceptions leave this C API
   1.808 +    fprintf(stderr, "BreakpadUploadNextReport() : error\n");
   1.809 +  }
   1.810 +}
   1.811 +
   1.812 +//=============================================================================
   1.813 +void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name,
   1.814 +                        NSDictionary *server_parameters) {
   1.815 +  try {
   1.816 +    // Not called at exception time
   1.817 +    Breakpad *breakpad = (Breakpad *)ref;
   1.818 +
   1.819 +    if (breakpad) {
   1.820 +      breakpad->UploadData(data, name, server_parameters);
   1.821 +    }
   1.822 +  } catch(...) {    // don't let exceptions leave this C API
   1.823 +    fprintf(stderr, "BreakpadUploadData() : error\n");
   1.824 +  }
   1.825 +}
   1.826 +
   1.827 +//=============================================================================
   1.828 +NSDictionary *BreakpadGenerateReport(BreakpadRef ref,
   1.829 +                                     NSDictionary *server_parameters) {
   1.830 +  try {
   1.831 +    // Not called at exception time
   1.832 +    Breakpad *breakpad = (Breakpad *)ref;
   1.833 +
   1.834 +    if (breakpad) {
   1.835 +      return breakpad->GenerateReport(server_parameters);
   1.836 +    } else {
   1.837 +      return nil;
   1.838 +    }
   1.839 +  } catch(...) {    // don't let exceptions leave this C API
   1.840 +    fprintf(stderr, "BreakpadGenerateReport() : error\n");
   1.841 +    return nil;
   1.842 +  }
   1.843 +}

mercurial