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