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