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