xref: /aosp_15_r20/external/google-breakpad/src/client/ios/Breakpad.mm (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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
67using google_breakpad::ConfigFile;
68using google_breakpad::EnsureDirectoryPathExists;
69using 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//
86ProtectedMemoryAllocator* gMasterAllocator = NULL;
87ProtectedMemoryAllocator* gKeyValueAllocator = NULL;
88ProtectedMemoryAllocator* 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.
93pthread_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.
101class 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//=============================================================================
134class 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
212Breakpad* Breakpad::current_breakpad_ = NULL;
213
214#pragma mark -
215#pragma mark Helper functions
216
217//=============================================================================
218// Helper functions
219
220//=============================================================================
221static 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//=============================================================================
258bool 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//=============================================================================
272void 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//=============================================================================
284bool 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//=============================================================================
312Breakpad::~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//=============================================================================
327bool 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//=============================================================================
420void 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//=============================================================================
429NSString* 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//=============================================================================
438void Breakpad::RemoveKeyValue(NSString* key) {
439  if (!config_params_ || !key) return;
440
441  config_params_->RemoveKey([key UTF8String]);
442}
443
444//=============================================================================
445NSArray* 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//=============================================================================
457NSString* 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//=============================================================================
468NSDictionary* Breakpad::NextCrashReportConfiguration() {
469  NSDictionary* configuration = [Uploader readConfigurationDataFromFile:NextCrashReportToUpload()];
470  return FixedUpCrashReportConfiguration(configuration);
471}
472
473//=============================================================================
474NSDictionary* 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//=============================================================================
484NSDate* 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//=============================================================================
508void 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//=============================================================================
517void 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//=============================================================================
540void Breakpad::UploadNextReport(NSDictionary* server_parameters) {
541  NSDictionary* configuration = NextCrashReportConfiguration();
542  if (configuration) {
543    return UploadReportWithConfiguration(configuration, server_parameters,
544                                         nullptr);
545  }
546}
547
548//=============================================================================
549void 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//=============================================================================
569NSDictionary* 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//=============================================================================
602bool 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//=============================================================================
615void 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//=============================================================================
652BreakpadRef 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//=============================================================================
731void 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//=============================================================================
766void 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
781void 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
804void 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//=============================================================================
822NSString* 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//=============================================================================
843void 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//=============================================================================
859int 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//=============================================================================
874void BreakpadUploadNextReport(BreakpadRef ref) {
875  BreakpadUploadNextReportWithParameters(ref, nil, nullptr);
876}
877
878//=============================================================================
879NSDictionary* 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//=============================================================================
891NSDate* 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//=============================================================================
904void 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//=============================================================================
922void 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
940void 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//=============================================================================
956void 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//=============================================================================
971NSDictionary* 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}
987