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