Home | History | Annotate | Download | only in ios
      1 // Copyright (c) 2011, Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 #define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
     31 
     32 #import "client/ios/Breakpad.h"
     33 
     34 #include <assert.h>
     35 #import <Foundation/Foundation.h>
     36 #include <pthread.h>
     37 #include <sys/stat.h>
     38 #include <sys/sysctl.h>
     39 
     40 #import "client/ios/handler/ios_exception_minidump_generator.h"
     41 #import "client/mac/crash_generation/ConfigFile.h"
     42 #import "client/mac/handler/exception_handler.h"
     43 #import "client/mac/handler/minidump_generator.h"
     44 #import "client/mac/sender/uploader.h"
     45 #import "client/mac/handler/protected_memory_allocator.h"
     46 #import "common/simple_string_dictionary.h"
     47 
     48 #if !defined(__EXCEPTIONS) || (__clang__ && !__has_feature(cxx_exceptions))
     49 // This file uses C++ try/catch (but shouldn't). Duplicate the macros from
     50 // <c++/4.2.1/exception_defines.h> allowing this file to work properly with
     51 // exceptions disabled even when other C++ libraries are used. #undef the try
     52 // and catch macros first in case libstdc++ is in use and has already provided
     53 // its own definitions.
     54 #undef try
     55 #define try       if (true)
     56 #undef catch
     57 #define catch(X)  if (false)
     58 #endif  // __EXCEPTIONS
     59 
     60 using google_breakpad::ConfigFile;
     61 using google_breakpad::EnsureDirectoryPathExists;
     62 using google_breakpad::SimpleStringDictionary;
     63 
     64 //=============================================================================
     65 // We want any memory allocations which are used by breakpad during the
     66 // exception handling process (after a crash has happened) to be read-only
     67 // to prevent them from being smashed before a crash occurs.  Unfortunately
     68 // we cannot protect against smashes to our exception handling thread's
     69 // stack.
     70 //
     71 // NOTE: Any memory allocations which are not used during the exception
     72 // handling process may be allocated in the normal ways.
     73 //
     74 // The ProtectedMemoryAllocator class provides an Allocate() method which
     75 // we'll using in conjunction with placement operator new() to control
     76 // allocation of C++ objects.  Note that we don't use operator delete()
     77 // but instead call the objects destructor directly:  object->~ClassName();
     78 //
     79 ProtectedMemoryAllocator *gMasterAllocator = NULL;
     80 ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
     81 ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
     82 
     83 // Mutex for thread-safe access to the key/value dictionary used by breakpad.
     84 // It's a global instead of an instance variable of Breakpad
     85 // since it can't live in a protected memory area.
     86 pthread_mutex_t gDictionaryMutex;
     87 
     88 //=============================================================================
     89 // Stack-based object for thread-safe access to a memory-protected region.
     90 // It's assumed that normally the memory block (allocated by the allocator)
     91 // is protected (read-only).  Creating a stack-based instance of
     92 // ProtectedMemoryLocker will unprotect this block after taking the lock.
     93 // Its destructor will first re-protect the memory then release the lock.
     94 class ProtectedMemoryLocker {
     95  public:
     96   ProtectedMemoryLocker(pthread_mutex_t *mutex,
     97                         ProtectedMemoryAllocator *allocator)
     98       : mutex_(mutex),
     99         allocator_(allocator) {
    100     // Lock the mutex
    101     __attribute__((unused)) int rv = pthread_mutex_lock(mutex_);
    102     assert(rv == 0);
    103 
    104     // Unprotect the memory
    105     allocator_->Unprotect();
    106   }
    107 
    108   ~ProtectedMemoryLocker() {
    109     // First protect the memory
    110     allocator_->Protect();
    111 
    112     // Then unlock the mutex
    113     __attribute__((unused)) int rv = pthread_mutex_unlock(mutex_);
    114     assert(rv == 0);
    115   };
    116 
    117  private:
    118   ProtectedMemoryLocker();
    119   ProtectedMemoryLocker(const ProtectedMemoryLocker&);
    120   ProtectedMemoryLocker& operator=(const ProtectedMemoryLocker&);
    121 
    122   pthread_mutex_t           *mutex_;
    123   ProtectedMemoryAllocator  *allocator_;
    124 };
    125 
    126 //=============================================================================
    127 class Breakpad {
    128  public:
    129   // factory method
    130   static Breakpad *Create(NSDictionary *parameters) {
    131     // Allocate from our special allocation pool
    132     Breakpad *breakpad =
    133       new (gBreakpadAllocator->Allocate(sizeof(Breakpad)))
    134         Breakpad();
    135 
    136     if (!breakpad)
    137       return NULL;
    138 
    139     if (!breakpad->Initialize(parameters)) {
    140       // Don't use operator delete() here since we allocated from special pool
    141       breakpad->~Breakpad();
    142       return NULL;
    143     }
    144 
    145     return breakpad;
    146   }
    147 
    148   ~Breakpad();
    149 
    150   void SetKeyValue(NSString *key, NSString *value);
    151   NSString *KeyValue(NSString *key);
    152   void RemoveKeyValue(NSString *key);
    153   NSArray *CrashReportsToUpload();
    154   NSString *NextCrashReportToUpload();
    155   NSDictionary *NextCrashReportConfiguration();
    156   void UploadNextReport(NSDictionary *server_parameters);
    157   void UploadReportWithConfiguration(NSDictionary *configuration,
    158                                      NSDictionary *server_parameters);
    159   void UploadData(NSData *data, NSString *name,
    160                   NSDictionary *server_parameters);
    161   void HandleNetworkResponse(NSDictionary *configuration,
    162                              NSData *data,
    163                              NSError *error);
    164   NSDictionary *GenerateReport(NSDictionary *server_parameters);
    165 
    166  private:
    167   Breakpad()
    168     : handler_(NULL),
    169       config_params_(NULL) {}
    170 
    171   bool Initialize(NSDictionary *parameters);
    172 
    173   bool ExtractParameters(NSDictionary *parameters);
    174 
    175   // Dispatches to HandleMinidump()
    176   static bool HandleMinidumpCallback(const char *dump_dir,
    177                                      const char *minidump_id,
    178                                      void *context, bool succeeded);
    179 
    180   bool HandleMinidump(const char *dump_dir,
    181                       const char *minidump_id);
    182 
    183   // NSException handler
    184   static void UncaughtExceptionHandler(NSException *exception);
    185 
    186   // Handle an uncaught NSException.
    187   void HandleUncaughtException(NSException *exception);
    188 
    189   // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's
    190   // MachineExceptions.h, we have to explicitly name the handler.
    191   google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG)
    192 
    193   SimpleStringDictionary  *config_params_; // Create parameters (STRONG)
    194 
    195   ConfigFile config_file_;
    196 
    197   // A static reference to the current Breakpad instance. Used for handling
    198   // NSException.
    199   static Breakpad *current_breakpad_;
    200 };
    201 
    202 Breakpad *Breakpad::current_breakpad_ = NULL;
    203 
    204 #pragma mark -
    205 #pragma mark Helper functions
    206 
    207 //=============================================================================
    208 // Helper functions
    209 
    210 //=============================================================================
    211 static BOOL IsDebuggerActive() {
    212   BOOL result = NO;
    213   NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
    214 
    215   // We check both defaults and the environment variable here
    216 
    217   BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER];
    218 
    219   if (!ignoreDebugger) {
    220     char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER);
    221     ignoreDebugger =
    222         (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0;
    223   }
    224 
    225   if (!ignoreDebugger) {
    226     pid_t pid = getpid();
    227     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
    228     int mibSize = sizeof(mib) / sizeof(int);
    229     size_t actualSize;
    230 
    231     if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) {
    232       struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize);
    233 
    234       if (info) {
    235         // This comes from looking at the Darwin xnu Kernel
    236         if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0)
    237           result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO;
    238 
    239         free(info);
    240       }
    241     }
    242   }
    243 
    244   return result;
    245 }
    246 
    247 //=============================================================================
    248 bool Breakpad::HandleMinidumpCallback(const char *dump_dir,
    249                                       const char *minidump_id,
    250                                       void *context, bool succeeded) {
    251   Breakpad *breakpad = (Breakpad *)context;
    252 
    253   // If our context is damaged or something, just return false to indicate that
    254   // the handler should continue without us.
    255   if (!breakpad || !succeeded)
    256     return false;
    257 
    258   return breakpad->HandleMinidump(dump_dir, minidump_id);
    259 }
    260 
    261 //=============================================================================
    262 void Breakpad::UncaughtExceptionHandler(NSException *exception) {
    263   NSSetUncaughtExceptionHandler(NULL);
    264   if (current_breakpad_) {
    265     current_breakpad_->HandleUncaughtException(exception);
    266   }
    267   BreakpadRelease(current_breakpad_);
    268 }
    269 
    270 //=============================================================================
    271 #pragma mark -
    272 
    273 //=============================================================================
    274 bool Breakpad::Initialize(NSDictionary *parameters) {
    275   // Initialize
    276   current_breakpad_ = this;
    277   config_params_ = NULL;
    278   handler_ = NULL;
    279 
    280   // Gather any user specified parameters
    281   if (!ExtractParameters(parameters)) {
    282     return false;
    283   }
    284 
    285   // Check for debugger
    286   if (IsDebuggerActive()) {
    287     return true;
    288   }
    289 
    290   // Create the handler (allocating it in our special protected pool)
    291   handler_ =
    292       new (gBreakpadAllocator->Allocate(
    293           sizeof(google_breakpad::ExceptionHandler)))
    294           google_breakpad::ExceptionHandler(
    295               config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY),
    296               0, &HandleMinidumpCallback, this, true, 0);
    297   NSSetUncaughtExceptionHandler(&Breakpad::UncaughtExceptionHandler);
    298   return true;
    299 }
    300 
    301 //=============================================================================
    302 Breakpad::~Breakpad() {
    303   NSSetUncaughtExceptionHandler(NULL);
    304   current_breakpad_ = NULL;
    305   // Note that we don't use operator delete() on these pointers,
    306   // since they were allocated by ProtectedMemoryAllocator objects.
    307   //
    308   if (config_params_) {
    309     config_params_->~SimpleStringDictionary();
    310   }
    311 
    312   if (handler_)
    313     handler_->~ExceptionHandler();
    314 }
    315 
    316 //=============================================================================
    317 bool Breakpad::ExtractParameters(NSDictionary *parameters) {
    318   NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE];
    319   NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
    320   NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
    321   NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
    322   NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
    323   NSString *vendor =
    324       [parameters objectForKey:@BREAKPAD_VENDOR];
    325   // We check both parameters and the environment variable here.
    326   char *envVarDumpSubdirectory = getenv(BREAKPAD_DUMP_DIRECTORY);
    327   NSString *dumpSubdirectory = envVarDumpSubdirectory ?
    328       [NSString stringWithUTF8String:envVarDumpSubdirectory] :
    329           [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
    330 
    331   NSDictionary *serverParameters =
    332       [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
    333 
    334   if (!product)
    335     product = [parameters objectForKey:@"CFBundleName"];
    336 
    337   if (!display) {
    338     display = [parameters objectForKey:@"CFBundleDisplayName"];
    339     if (!display) {
    340       display = product;
    341     }
    342   }
    343 
    344   if (!version)
    345     version = [parameters objectForKey:@"CFBundleVersion"];
    346 
    347   if (!vendor) {
    348     vendor = @"Vendor not specified";
    349   }
    350 
    351   if (!dumpSubdirectory) {
    352     NSString *cachePath =
    353         [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
    354                                              NSUserDomainMask,
    355                                              YES)
    356             objectAtIndex:0];
    357     dumpSubdirectory =
    358         [cachePath stringByAppendingPathComponent:@kDefaultLibrarySubdirectory];
    359 
    360     EnsureDirectoryPathExists(dumpSubdirectory);
    361   }
    362 
    363   // The product, version, and URL are required values.
    364   if (![product length]) {
    365     return false;
    366   }
    367 
    368   if (![version length]) {
    369     return false;
    370   }
    371 
    372   if (![urlStr length]) {
    373     return false;
    374   }
    375 
    376   config_params_ =
    377       new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
    378         SimpleStringDictionary();
    379 
    380   SimpleStringDictionary &dictionary = *config_params_;
    381 
    382   dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE,     [serverType UTF8String]);
    383   dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
    384   dictionary.SetKeyValue(BREAKPAD_PRODUCT,         [product UTF8String]);
    385   dictionary.SetKeyValue(BREAKPAD_VERSION,         [version UTF8String]);
    386   dictionary.SetKeyValue(BREAKPAD_URL,             [urlStr UTF8String]);
    387   dictionary.SetKeyValue(BREAKPAD_VENDOR,          [vendor UTF8String]);
    388   dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
    389                          [dumpSubdirectory UTF8String]);
    390 
    391   struct timeval tv;
    392   gettimeofday(&tv, NULL);
    393   char timeStartedString[32];
    394   sprintf(timeStartedString, "%zd", tv.tv_sec);
    395   dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, timeStartedString);
    396 
    397   if (serverParameters) {
    398     // For each key-value pair, call BreakpadAddUploadParameter()
    399     NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
    400     NSString *aParameter;
    401     while ((aParameter = [keyEnumerator nextObject])) {
    402       BreakpadAddUploadParameter(this, aParameter,
    403 				 [serverParameters objectForKey:aParameter]);
    404     }
    405   }
    406   return true;
    407 }
    408 
    409 //=============================================================================
    410 void Breakpad::SetKeyValue(NSString *key, NSString *value) {
    411   // We allow nil values. This is the same as removing the keyvalue.
    412   if (!config_params_ || !key)
    413     return;
    414 
    415   config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
    416 }
    417 
    418 //=============================================================================
    419 NSString *Breakpad::KeyValue(NSString *key) {
    420   if (!config_params_ || !key)
    421     return nil;
    422 
    423   const char *value = config_params_->GetValueForKey([key UTF8String]);
    424   return value ? [NSString stringWithUTF8String:value] : nil;
    425 }
    426 
    427 //=============================================================================
    428 void Breakpad::RemoveKeyValue(NSString *key) {
    429   if (!config_params_ || !key) return;
    430 
    431   config_params_->RemoveKey([key UTF8String]);
    432 }
    433 
    434 //=============================================================================
    435 NSArray *Breakpad::CrashReportsToUpload() {
    436   NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
    437   if (!directory)
    438     return nil;
    439   NSArray *dirContents = [[NSFileManager defaultManager]
    440       contentsOfDirectoryAtPath:directory error:nil];
    441   NSArray *configs = [dirContents filteredArrayUsingPredicate:[NSPredicate
    442       predicateWithFormat:@"self BEGINSWITH 'Config-'"]];
    443   return configs;
    444 }
    445 
    446 //=============================================================================
    447 NSString *Breakpad::NextCrashReportToUpload() {
    448   NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
    449   if (!directory)
    450     return nil;
    451   NSString *config = [CrashReportsToUpload() lastObject];
    452   if (!config)
    453     return nil;
    454   return [NSString stringWithFormat:@"%@/%@", directory, config];
    455 }
    456 
    457 //=============================================================================
    458 NSDictionary *Breakpad::NextCrashReportConfiguration() {
    459   return [Uploader readConfigurationDataFromFile:NextCrashReportToUpload()];
    460 }
    461 
    462 //=============================================================================
    463 void Breakpad::HandleNetworkResponse(NSDictionary *configuration,
    464                                      NSData *data,
    465                                      NSError *error) {
    466   Uploader *uploader = [[[Uploader alloc]
    467       initWithConfig:configuration] autorelease];
    468   [uploader handleNetworkResponse:data withError:error];
    469 }
    470 
    471 //=============================================================================
    472 void Breakpad::UploadReportWithConfiguration(NSDictionary *configuration,
    473                                              NSDictionary *server_parameters) {
    474   Uploader *uploader = [[[Uploader alloc]
    475       initWithConfig:configuration] autorelease];
    476   if (!uploader)
    477     return;
    478   for (NSString *key in server_parameters) {
    479     [uploader addServerParameter:[server_parameters objectForKey:key]
    480                           forKey:key];
    481   }
    482   [uploader report];
    483 }
    484 
    485 //=============================================================================
    486 void Breakpad::UploadNextReport(NSDictionary *server_parameters) {
    487   NSDictionary *configuration = NextCrashReportConfiguration();
    488   if (configuration) {
    489     return UploadReportWithConfiguration(configuration, server_parameters);
    490   }
    491 }
    492 
    493 //=============================================================================
    494 void Breakpad::UploadData(NSData *data, NSString *name,
    495                           NSDictionary *server_parameters) {
    496   NSMutableDictionary *config = [NSMutableDictionary dictionary];
    497 
    498   SimpleStringDictionary::Iterator it(*config_params_);
    499   while (const SimpleStringDictionary::Entry *next = it.Next()) {
    500     [config setValue:[NSString stringWithUTF8String:next->value]
    501               forKey:[NSString stringWithUTF8String:next->key]];
    502   }
    503 
    504   Uploader *uploader =
    505       [[[Uploader alloc] initWithConfig:config] autorelease];
    506   for (NSString *key in server_parameters) {
    507     [uploader addServerParameter:[server_parameters objectForKey:key]
    508                           forKey:key];
    509   }
    510   [uploader uploadData:data name:name];
    511 }
    512 
    513 //=============================================================================
    514 NSDictionary *Breakpad::GenerateReport(NSDictionary *server_parameters) {
    515   NSString *dumpDirAsNSString = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
    516   if (!dumpDirAsNSString)
    517     return nil;
    518   const char *dumpDir = [dumpDirAsNSString UTF8String];
    519 
    520   google_breakpad::MinidumpGenerator generator(mach_task_self(),
    521                                                MACH_PORT_NULL);
    522   std::string dumpId;
    523   std::string dumpFilename = generator.UniqueNameInDirectory(dumpDir, &dumpId);
    524   bool success = generator.Write(dumpFilename.c_str());
    525   if (!success)
    526     return nil;
    527 
    528   SimpleStringDictionary params = *config_params_;
    529   for (NSString *key in server_parameters) {
    530     params.SetKeyValue([key UTF8String],
    531                        [[server_parameters objectForKey:key] UTF8String]);
    532   }
    533   ConfigFile config_file;
    534   config_file.WriteFile(dumpDir, &params, dumpDir, dumpId.c_str());
    535 
    536   // Handle results.
    537   NSMutableDictionary *result = [NSMutableDictionary dictionary];
    538   NSString *dumpFullPath = [NSString stringWithUTF8String:dumpFilename.c_str()];
    539   [result setValue:dumpFullPath
    540             forKey:@BREAKPAD_OUTPUT_DUMP_FILE];
    541   [result setValue:[NSString stringWithUTF8String:config_file.GetFilePath()]
    542             forKey:@BREAKPAD_OUTPUT_CONFIG_FILE];
    543   return result;
    544 }
    545 
    546 //=============================================================================
    547 bool Breakpad::HandleMinidump(const char *dump_dir,
    548                               const char *minidump_id) {
    549   config_file_.WriteFile(dump_dir,
    550                          config_params_,
    551                          dump_dir,
    552                          minidump_id);
    553 
    554   // Return true here to indicate that we've processed things as much as we
    555   // want.
    556   return true;
    557 }
    558 
    559 //=============================================================================
    560 void Breakpad::HandleUncaughtException(NSException *exception) {
    561   // Generate the minidump.
    562   google_breakpad::IosExceptionMinidumpGenerator generator(exception);
    563   const char *minidump_path =
    564       config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
    565   std::string minidump_id;
    566   std::string minidump_filename = generator.UniqueNameInDirectory(minidump_path,
    567                                                                   &minidump_id);
    568   generator.Write(minidump_filename.c_str());
    569 
    570   // Copy the config params and our custom parameter. This is necessary for 2
    571   // reasons:
    572   // 1- config_params_ is protected.
    573   // 2- If the application crash while trying to handle this exception, a usual
    574   //    report will be generated. This report must not contain these special
    575   //    keys.
    576   SimpleStringDictionary params = *config_params_;
    577   params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "type", "exception");
    578   params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionName",
    579                      [[exception name] UTF8String]);
    580   params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionReason",
    581                      [[exception reason] UTF8String]);
    582 
    583   // And finally write the config file.
    584   ConfigFile config_file;
    585   config_file.WriteFile(minidump_path,
    586                         &params,
    587                         minidump_path,
    588                         minidump_id.c_str());
    589 }
    590 
    591 //=============================================================================
    592 
    593 #pragma mark -
    594 #pragma mark Public API
    595 
    596 //=============================================================================
    597 BreakpadRef BreakpadCreate(NSDictionary *parameters) {
    598   try {
    599     // This is confusing.  Our two main allocators for breakpad memory are:
    600     //    - gKeyValueAllocator for the key/value memory
    601     //    - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other
    602     //      breakpad allocations which are accessed at exception handling time.
    603     //
    604     // But in order to avoid these two allocators themselves from being smashed,
    605     // we'll protect them as well by allocating them with gMasterAllocator.
    606     //
    607     // gMasterAllocator itself will NOT be protected, but this doesn't matter,
    608     // since once it does its allocations and locks the memory, smashes to
    609     // itself don't affect anything we care about.
    610     gMasterAllocator =
    611         new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
    612 
    613     gKeyValueAllocator =
    614         new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
    615             ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
    616 
    617     // Create a mutex for use in accessing the SimpleStringDictionary
    618     int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
    619     if (mutexResult == 0) {
    620 
    621       // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
    622       // Let's round up to the nearest page size.
    623       //
    624       int breakpad_pool_size = 4096;
    625 
    626       /*
    627        sizeof(Breakpad)
    628        + sizeof(google_breakpad::ExceptionHandler)
    629        + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
    630        */
    631 
    632       gBreakpadAllocator =
    633           new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
    634               ProtectedMemoryAllocator(breakpad_pool_size);
    635 
    636       // Stack-based autorelease pool for Breakpad::Create() obj-c code.
    637       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    638       Breakpad *breakpad = Breakpad::Create(parameters);
    639 
    640       if (breakpad) {
    641         // Make read-only to protect against memory smashers
    642         gMasterAllocator->Protect();
    643         gKeyValueAllocator->Protect();
    644         gBreakpadAllocator->Protect();
    645         // Can uncomment this line to figure out how much space was actually
    646         // allocated using this allocator
    647         //     printf("gBreakpadAllocator allocated size = %d\n",
    648         //         gBreakpadAllocator->GetAllocatedSize() );
    649         [pool release];
    650         return (BreakpadRef)breakpad;
    651       }
    652 
    653       [pool release];
    654     }
    655   } catch(...) {    // don't let exceptions leave this C API
    656     fprintf(stderr, "BreakpadCreate() : error\n");
    657   }
    658 
    659   if (gKeyValueAllocator) {
    660     gKeyValueAllocator->~ProtectedMemoryAllocator();
    661     gKeyValueAllocator = NULL;
    662   }
    663 
    664   if (gBreakpadAllocator) {
    665     gBreakpadAllocator->~ProtectedMemoryAllocator();
    666     gBreakpadAllocator = NULL;
    667   }
    668 
    669   delete gMasterAllocator;
    670   gMasterAllocator = NULL;
    671 
    672   return NULL;
    673 }
    674 
    675 //=============================================================================
    676 void BreakpadRelease(BreakpadRef ref) {
    677   try {
    678     Breakpad *breakpad = (Breakpad *)ref;
    679 
    680     if (gMasterAllocator) {
    681       gMasterAllocator->Unprotect();
    682       gKeyValueAllocator->Unprotect();
    683       gBreakpadAllocator->Unprotect();
    684 
    685       breakpad->~Breakpad();
    686 
    687       // Unfortunately, it's not possible to deallocate this stuff
    688       // because the exception handling thread is still finishing up
    689       // asynchronously at this point...  OK, it could be done with
    690       // locks, etc.  But since BreakpadRelease() should usually only
    691       // be called right before the process exits, it's not worth
    692       // deallocating this stuff.
    693 #if 0
    694       gKeyValueAllocator->~ProtectedMemoryAllocator();
    695       gBreakpadAllocator->~ProtectedMemoryAllocator();
    696       delete gMasterAllocator;
    697 
    698       gMasterAllocator = NULL;
    699       gKeyValueAllocator = NULL;
    700       gBreakpadAllocator = NULL;
    701 #endif
    702 
    703       pthread_mutex_destroy(&gDictionaryMutex);
    704     }
    705   } catch(...) {    // don't let exceptions leave this C API
    706     fprintf(stderr, "BreakpadRelease() : error\n");
    707   }
    708 }
    709 
    710 //=============================================================================
    711 void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
    712   try {
    713     // Not called at exception time
    714     Breakpad *breakpad = (Breakpad *)ref;
    715 
    716     if (breakpad && key && gKeyValueAllocator) {
    717       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
    718 
    719       breakpad->SetKeyValue(key, value);
    720     }
    721   } catch(...) {    // don't let exceptions leave this C API
    722     fprintf(stderr, "BreakpadSetKeyValue() : error\n");
    723   }
    724 }
    725 
    726 void BreakpadAddUploadParameter(BreakpadRef ref,
    727                                 NSString *key,
    728                                 NSString *value) {
    729   // The only difference, internally, between an upload parameter and
    730   // a key value one that is set with BreakpadSetKeyValue is that we
    731   // prepend the keyname with a special prefix.  This informs the
    732   // crash sender that the parameter should be sent along with the
    733   // POST of the crash dump upload.
    734   try {
    735     Breakpad *breakpad = (Breakpad *)ref;
    736 
    737     if (breakpad && key && gKeyValueAllocator) {
    738       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
    739 
    740       NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
    741 				stringByAppendingString:key];
    742       breakpad->SetKeyValue(prefixedKey, value);
    743     }
    744   } catch(...) {    // don't let exceptions leave this C API
    745     fprintf(stderr, "BreakpadSetKeyValue() : error\n");
    746   }
    747 }
    748 
    749 void BreakpadRemoveUploadParameter(BreakpadRef ref,
    750                                    NSString *key) {
    751   try {
    752     // Not called at exception time
    753     Breakpad *breakpad = (Breakpad *)ref;
    754 
    755     if (breakpad && key && gKeyValueAllocator) {
    756       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
    757 
    758       NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
    759                                         @BREAKPAD_SERVER_PARAMETER_PREFIX, key];
    760       breakpad->RemoveKeyValue(prefixedKey);
    761     }
    762   } catch(...) {    // don't let exceptions leave this C API
    763     fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
    764   }
    765 }
    766 //=============================================================================
    767 NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
    768   NSString *value = nil;
    769 
    770   try {
    771     // Not called at exception time
    772     Breakpad *breakpad = (Breakpad *)ref;
    773 
    774     if (!breakpad || !key || !gKeyValueAllocator)
    775       return nil;
    776 
    777     ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
    778 
    779     value = breakpad->KeyValue(key);
    780   } catch(...) {    // don't let exceptions leave this C API
    781     fprintf(stderr, "BreakpadKeyValue() : error\n");
    782   }
    783 
    784   return value;
    785 }
    786 
    787 //=============================================================================
    788 void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
    789   try {
    790     // Not called at exception time
    791     Breakpad *breakpad = (Breakpad *)ref;
    792 
    793     if (breakpad && key && gKeyValueAllocator) {
    794       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
    795 
    796       breakpad->RemoveKeyValue(key);
    797     }
    798   } catch(...) {    // don't let exceptions leave this C API
    799     fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
    800   }
    801 }
    802 
    803 //=============================================================================
    804 int BreakpadGetCrashReportCount(BreakpadRef ref) {
    805   try {
    806     // Not called at exception time
    807     Breakpad *breakpad = (Breakpad *)ref;
    808 
    809     if (breakpad) {
    810        return static_cast<int>([breakpad->CrashReportsToUpload() count]);
    811     }
    812   } catch(...) {    // don't let exceptions leave this C API
    813     fprintf(stderr, "BreakpadGetCrashReportCount() : error\n");
    814   }
    815   return false;
    816 }
    817 
    818 //=============================================================================
    819 void BreakpadUploadNextReport(BreakpadRef ref) {
    820   BreakpadUploadNextReportWithParameters(ref, nil);
    821 }
    822 
    823 //=============================================================================
    824 NSDictionary *BreakpadGetNextReportConfiguration(BreakpadRef ref) {
    825   try {
    826     Breakpad *breakpad = (Breakpad *)ref;
    827     if (breakpad)
    828       return breakpad->NextCrashReportConfiguration();
    829   } catch(...) {    // don't let exceptions leave this C API
    830     fprintf(stderr, "BreakpadGetNextReportConfiguration() : error\n");
    831   }
    832   return nil;
    833 }
    834 
    835 //=============================================================================
    836 void BreakpadUploadReportWithParametersAndConfiguration(
    837     BreakpadRef ref,
    838     NSDictionary *server_parameters,
    839     NSDictionary *configuration) {
    840   try {
    841     Breakpad *breakpad = (Breakpad *)ref;
    842     if (!breakpad || !configuration)
    843       return;
    844     breakpad->UploadReportWithConfiguration(configuration, server_parameters);
    845   } catch(...) {    // don't let exceptions leave this C API
    846     fprintf(stderr,
    847         "BreakpadUploadReportWithParametersAndConfiguration() : error\n");
    848   }
    849 
    850 }
    851 
    852 //=============================================================================
    853 void BreakpadUploadNextReportWithParameters(BreakpadRef ref,
    854                                             NSDictionary *server_parameters) {
    855   try {
    856     Breakpad *breakpad = (Breakpad *)ref;
    857     if (!breakpad)
    858       return;
    859     NSDictionary *configuration = breakpad->NextCrashReportConfiguration();
    860     if (!configuration)
    861       return;
    862     return BreakpadUploadReportWithParametersAndConfiguration(ref,
    863                                                               server_parameters,
    864                                                               configuration);
    865   } catch(...) {    // don't let exceptions leave this C API
    866     fprintf(stderr, "BreakpadUploadNextReportWithParameters() : error\n");
    867   }
    868 }
    869 
    870 void BreakpadHandleNetworkResponse(BreakpadRef ref,
    871                                    NSDictionary *configuration,
    872                                    NSData *data,
    873                                    NSError *error) {
    874   try {
    875     // Not called at exception time
    876     Breakpad *breakpad = (Breakpad *)ref;
    877     if (breakpad && configuration)
    878       breakpad->HandleNetworkResponse(configuration,data, error);
    879 
    880   } catch(...) {    // don't let exceptions leave this C API
    881     fprintf(stderr, "BreakpadHandleNetworkResponse() : error\n");
    882   }
    883 }
    884 
    885 //=============================================================================
    886 void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name,
    887                         NSDictionary *server_parameters) {
    888   try {
    889     // Not called at exception time
    890     Breakpad *breakpad = (Breakpad *)ref;
    891 
    892     if (breakpad) {
    893       breakpad->UploadData(data, name, server_parameters);
    894     }
    895   } catch(...) {    // don't let exceptions leave this C API
    896     fprintf(stderr, "BreakpadUploadData() : error\n");
    897   }
    898 }
    899 
    900 //=============================================================================
    901 NSDictionary *BreakpadGenerateReport(BreakpadRef ref,
    902                                      NSDictionary *server_parameters) {
    903   try {
    904     // Not called at exception time
    905     Breakpad *breakpad = (Breakpad *)ref;
    906 
    907     if (breakpad) {
    908       return breakpad->GenerateReport(server_parameters);
    909     } else {
    910       return nil;
    911     }
    912   } catch(...) {    // don't let exceptions leave this C API
    913     fprintf(stderr, "BreakpadGenerateReport() : error\n");
    914     return nil;
    915   }
    916 }
    917