Upstream version 5.34.92.0
[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     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     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 }
262
263 //=============================================================================
264 #pragma mark -
265
266 //=============================================================================
267 bool Breakpad::Initialize(NSDictionary *parameters) {
268   // Initialize
269   current_breakpad_ = this;
270   config_params_ = NULL;
271   handler_ = NULL;
272
273   // Gather any user specified parameters
274   if (!ExtractParameters(parameters)) {
275     return false;
276   }
277
278   // Check for debugger
279   if (IsDebuggerActive()) {
280     return true;
281   }
282
283   // Create the handler (allocating it in our special protected pool)
284   handler_ =
285       new (gBreakpadAllocator->Allocate(
286           sizeof(google_breakpad::ExceptionHandler)))
287           google_breakpad::ExceptionHandler(
288               config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY),
289               0, &HandleMinidumpCallback, this, true, 0);
290   NSSetUncaughtExceptionHandler(&Breakpad::UncaughtExceptionHandler);
291   return true;
292 }
293
294 //=============================================================================
295 Breakpad::~Breakpad() {
296   NSSetUncaughtExceptionHandler(NULL);
297   current_breakpad_ = NULL;
298   // Note that we don't use operator delete() on these pointers,
299   // since they were allocated by ProtectedMemoryAllocator objects.
300   //
301   if (config_params_) {
302     config_params_->~SimpleStringDictionary();
303   }
304
305   if (handler_)
306     handler_->~ExceptionHandler();
307 }
308
309 //=============================================================================
310 bool Breakpad::ExtractParameters(NSDictionary *parameters) {
311   NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE];
312   NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
313   NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
314   NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
315   NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
316   NSString *vendor =
317       [parameters objectForKey:@BREAKPAD_VENDOR];
318   // We check both parameters and the environment variable here.
319   char *envVarDumpSubdirectory = getenv(BREAKPAD_DUMP_DIRECTORY);
320   NSString *dumpSubdirectory = envVarDumpSubdirectory ?
321       [NSString stringWithUTF8String:envVarDumpSubdirectory] :
322           [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
323
324   NSDictionary *serverParameters =
325       [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
326
327   if (!product)
328     product = [parameters objectForKey:@"CFBundleName"];
329
330   if (!display) {
331     display = [parameters objectForKey:@"CFBundleDisplayName"];
332     if (!display) {
333       display = product;
334     }
335   }
336
337   if (!version)
338     version = [parameters objectForKey:@"CFBundleVersion"];
339
340   if (!vendor) {
341     vendor = @"Vendor not specified";
342   }
343
344   if (!dumpSubdirectory) {
345     NSString *cachePath =
346         [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
347                                              NSUserDomainMask,
348                                              YES)
349             objectAtIndex:0];
350     dumpSubdirectory =
351         [cachePath stringByAppendingPathComponent:@kDefaultLibrarySubdirectory];
352
353     EnsureDirectoryPathExists(dumpSubdirectory);
354   }
355
356   // The product, version, and URL are required values.
357   if (![product length]) {
358     return false;
359   }
360
361   if (![version length]) {
362     return false;
363   }
364
365   if (![urlStr length]) {
366     return false;
367   }
368
369   config_params_ =
370       new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
371         SimpleStringDictionary();
372
373   SimpleStringDictionary &dictionary = *config_params_;
374
375   dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE,     [serverType UTF8String]);
376   dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
377   dictionary.SetKeyValue(BREAKPAD_PRODUCT,         [product UTF8String]);
378   dictionary.SetKeyValue(BREAKPAD_VERSION,         [version UTF8String]);
379   dictionary.SetKeyValue(BREAKPAD_URL,             [urlStr UTF8String]);
380   dictionary.SetKeyValue(BREAKPAD_VENDOR,          [vendor UTF8String]);
381   dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
382                          [dumpSubdirectory UTF8String]);
383
384   struct timeval tv;
385   gettimeofday(&tv, NULL);
386   char timeStartedString[32];
387   sprintf(timeStartedString, "%zd", tv.tv_sec);
388   dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, timeStartedString);
389
390   if (serverParameters) {
391     // For each key-value pair, call BreakpadAddUploadParameter()
392     NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
393     NSString *aParameter;
394     while ((aParameter = [keyEnumerator nextObject])) {
395       BreakpadAddUploadParameter(this, aParameter,
396                                  [serverParameters objectForKey:aParameter]);
397     }
398   }
399   return true;
400 }
401
402 //=============================================================================
403 void Breakpad::SetKeyValue(NSString *key, NSString *value) {
404   // We allow nil values. This is the same as removing the keyvalue.
405   if (!config_params_ || !key)
406     return;
407
408   config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
409 }
410
411 //=============================================================================
412 NSString *Breakpad::KeyValue(NSString *key) {
413   if (!config_params_ || !key)
414     return nil;
415
416   const char *value = config_params_->GetValueForKey([key UTF8String]);
417   return value ? [NSString stringWithUTF8String:value] : nil;
418 }
419
420 //=============================================================================
421 void Breakpad::RemoveKeyValue(NSString *key) {
422   if (!config_params_ || !key) return;
423
424   config_params_->RemoveKey([key UTF8String]);
425 }
426
427 //=============================================================================
428 NSArray *Breakpad::CrashReportsToUpload() {
429   NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
430   if (!directory)
431     return nil;
432   NSArray *dirContents = [[NSFileManager defaultManager]
433       contentsOfDirectoryAtPath:directory error:nil];
434   NSArray *configs = [dirContents filteredArrayUsingPredicate:[NSPredicate
435       predicateWithFormat:@"self BEGINSWITH 'Config-'"]];
436   return configs;
437 }
438
439 //=============================================================================
440 NSString *Breakpad::NextCrashReportToUpload() {
441   NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
442   if (!directory)
443     return nil;
444   NSString *config = [CrashReportsToUpload() lastObject];
445   if (!config)
446     return nil;
447   return [NSString stringWithFormat:@"%@/%@", directory, config];
448 }
449
450 //=============================================================================
451 void Breakpad::UploadNextReport(NSDictionary *server_parameters) {
452   NSString *configFile = NextCrashReportToUpload();
453   if (configFile) {
454     Uploader *uploader = [[[Uploader alloc]
455         initWithConfigFile:[configFile UTF8String]] autorelease];
456     if (uploader) {
457       for (NSString *key in server_parameters) {
458         [uploader addServerParameter:[server_parameters objectForKey:key]
459                               forKey:key];
460       }
461       [uploader report];
462     }
463   }
464 }
465
466 //=============================================================================
467 void Breakpad::UploadData(NSData *data, NSString *name,
468                           NSDictionary *server_parameters) {
469   NSMutableDictionary *config = [NSMutableDictionary dictionary];
470
471   SimpleStringDictionary::Iterator it(*config_params_);
472   while (const SimpleStringDictionary::Entry *next = it.Next()) {
473     [config setValue:[NSString stringWithUTF8String:next->value]
474               forKey:[NSString stringWithUTF8String:next->key]];
475   }
476
477   Uploader *uploader =
478       [[[Uploader alloc] initWithConfig:config] autorelease];
479   for (NSString *key in server_parameters) {
480     [uploader addServerParameter:[server_parameters objectForKey:key]
481                           forKey:key];
482   }
483   [uploader uploadData:data name:name];
484 }
485
486 //=============================================================================
487 NSDictionary *Breakpad::GenerateReport(NSDictionary *server_parameters) {
488   NSString *dumpDirAsNSString = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
489   if (!dumpDirAsNSString)
490     return nil;
491   const char *dumpDir = [dumpDirAsNSString UTF8String];
492
493   google_breakpad::MinidumpGenerator generator(mach_task_self(),
494                                                MACH_PORT_NULL);
495   std::string dumpId;
496   std::string dumpFilename = generator.UniqueNameInDirectory(dumpDir, &dumpId);
497   bool success = generator.Write(dumpFilename.c_str());
498   if (!success)
499     return nil;
500
501   SimpleStringDictionary params = *config_params_;
502   for (NSString *key in server_parameters) {
503     params.SetKeyValue([key UTF8String],
504                        [[server_parameters objectForKey:key] UTF8String]);
505   }
506   ConfigFile config_file;
507   config_file.WriteFile(dumpDir, &params, dumpDir, dumpId.c_str());
508
509   // Handle results.
510   NSMutableDictionary *result = [NSMutableDictionary dictionary];
511   NSString *dumpFullPath = [NSString stringWithUTF8String:dumpFilename.c_str()];
512   [result setValue:dumpFullPath
513             forKey:@BREAKPAD_OUTPUT_DUMP_FILE];
514   [result setValue:[NSString stringWithUTF8String:config_file.GetFilePath()]
515             forKey:@BREAKPAD_OUTPUT_CONFIG_FILE];
516   return result;
517 }
518
519 //=============================================================================
520 bool Breakpad::HandleMinidump(const char *dump_dir,
521                               const char *minidump_id) {
522   config_file_.WriteFile(dump_dir,
523                          config_params_,
524                          dump_dir,
525                          minidump_id);
526
527   // Return true here to indicate that we've processed things as much as we
528   // want.
529   return true;
530 }
531
532 //=============================================================================
533 void Breakpad::HandleUncaughtException(NSException *exception) {
534   // Generate the minidump.
535   google_breakpad::IosExceptionMinidumpGenerator generator(exception);
536   const char *minidump_path =
537       config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
538   std::string minidump_id;
539   std::string minidump_filename = generator.UniqueNameInDirectory(minidump_path,
540                                                                   &minidump_id);
541   generator.Write(minidump_filename.c_str());
542
543   // Copy the config params and our custom parameter. This is necessary for 2
544   // reasons:
545   // 1- config_params_ is protected.
546   // 2- If the application crash while trying to handle this exception, a usual
547   //    report will be generated. This report must not contain these special
548   //    keys.
549   SimpleStringDictionary params = *config_params_;
550   params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "type", "exception");
551   params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionName",
552                      [[exception name] UTF8String]);
553   params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionReason",
554                      [[exception reason] UTF8String]);
555
556   // And finally write the config file.
557   ConfigFile config_file;
558   config_file.WriteFile(minidump_path,
559                         &params,
560                         minidump_path,
561                         minidump_id.c_str());
562 }
563
564 //=============================================================================
565
566 #pragma mark -
567 #pragma mark Public API
568
569 //=============================================================================
570 BreakpadRef BreakpadCreate(NSDictionary *parameters) {
571   try {
572     // This is confusing.  Our two main allocators for breakpad memory are:
573     //    - gKeyValueAllocator for the key/value memory
574     //    - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other
575     //      breakpad allocations which are accessed at exception handling time.
576     //
577     // But in order to avoid these two allocators themselves from being smashed,
578     // we'll protect them as well by allocating them with gMasterAllocator.
579     //
580     // gMasterAllocator itself will NOT be protected, but this doesn't matter,
581     // since once it does its allocations and locks the memory, smashes to
582     // itself don't affect anything we care about.
583     gMasterAllocator =
584         new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
585
586     gKeyValueAllocator =
587         new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
588             ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
589
590     // Create a mutex for use in accessing the SimpleStringDictionary
591     int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
592     if (mutexResult == 0) {
593
594       // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
595       // Let's round up to the nearest page size.
596       //
597       int breakpad_pool_size = 4096;
598
599       /*
600        sizeof(Breakpad)
601        + sizeof(google_breakpad::ExceptionHandler)
602        + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
603        */
604
605       gBreakpadAllocator =
606           new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
607               ProtectedMemoryAllocator(breakpad_pool_size);
608
609       // Stack-based autorelease pool for Breakpad::Create() obj-c code.
610       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
611       Breakpad *breakpad = Breakpad::Create(parameters);
612
613       if (breakpad) {
614         // Make read-only to protect against memory smashers
615         gMasterAllocator->Protect();
616         gKeyValueAllocator->Protect();
617         gBreakpadAllocator->Protect();
618         // Can uncomment this line to figure out how much space was actually
619         // allocated using this allocator
620         //     printf("gBreakpadAllocator allocated size = %d\n",
621         //         gBreakpadAllocator->GetAllocatedSize() );
622         [pool release];
623         return (BreakpadRef)breakpad;
624       }
625
626       [pool release];
627     }
628   } catch(...) {    // don't let exceptions leave this C API
629     fprintf(stderr, "BreakpadCreate() : error\n");
630   }
631
632   if (gKeyValueAllocator) {
633     gKeyValueAllocator->~ProtectedMemoryAllocator();
634     gKeyValueAllocator = NULL;
635   }
636
637   if (gBreakpadAllocator) {
638     gBreakpadAllocator->~ProtectedMemoryAllocator();
639     gBreakpadAllocator = NULL;
640   }
641
642   delete gMasterAllocator;
643   gMasterAllocator = NULL;
644
645   return NULL;
646 }
647
648 //=============================================================================
649 void BreakpadRelease(BreakpadRef ref) {
650   try {
651     Breakpad *breakpad = (Breakpad *)ref;
652
653     if (gMasterAllocator) {
654       gMasterAllocator->Unprotect();
655       gKeyValueAllocator->Unprotect();
656       gBreakpadAllocator->Unprotect();
657
658       breakpad->~Breakpad();
659
660       // Unfortunately, it's not possible to deallocate this stuff
661       // because the exception handling thread is still finishing up
662       // asynchronously at this point...  OK, it could be done with
663       // locks, etc.  But since BreakpadRelease() should usually only
664       // be called right before the process exits, it's not worth
665       // deallocating this stuff.
666 #if 0
667       gKeyValueAllocator->~ProtectedMemoryAllocator();
668       gBreakpadAllocator->~ProtectedMemoryAllocator();
669       delete gMasterAllocator;
670
671       gMasterAllocator = NULL;
672       gKeyValueAllocator = NULL;
673       gBreakpadAllocator = NULL;
674 #endif
675
676       pthread_mutex_destroy(&gDictionaryMutex);
677     }
678   } catch(...) {    // don't let exceptions leave this C API
679     fprintf(stderr, "BreakpadRelease() : error\n");
680   }
681 }
682
683 //=============================================================================
684 void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
685   try {
686     // Not called at exception time
687     Breakpad *breakpad = (Breakpad *)ref;
688
689     if (breakpad && key && gKeyValueAllocator) {
690       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
691
692       breakpad->SetKeyValue(key, value);
693     }
694   } catch(...) {    // don't let exceptions leave this C API
695     fprintf(stderr, "BreakpadSetKeyValue() : error\n");
696   }
697 }
698
699 void BreakpadAddUploadParameter(BreakpadRef ref,
700                                 NSString *key,
701                                 NSString *value) {
702   // The only difference, internally, between an upload parameter and
703   // a key value one that is set with BreakpadSetKeyValue is that we
704   // prepend the keyname with a special prefix.  This informs the
705   // crash sender that the parameter should be sent along with the
706   // POST of the crash dump upload.
707   try {
708     Breakpad *breakpad = (Breakpad *)ref;
709
710     if (breakpad && key && gKeyValueAllocator) {
711       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
712
713       NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
714                                 stringByAppendingString:key];
715       breakpad->SetKeyValue(prefixedKey, value);
716     }
717   } catch(...) {    // don't let exceptions leave this C API
718     fprintf(stderr, "BreakpadSetKeyValue() : error\n");
719   }
720 }
721
722 void BreakpadRemoveUploadParameter(BreakpadRef ref,
723                                    NSString *key) {
724   try {
725     // Not called at exception time
726     Breakpad *breakpad = (Breakpad *)ref;
727
728     if (breakpad && key && gKeyValueAllocator) {
729       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
730
731       NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
732                                         @BREAKPAD_SERVER_PARAMETER_PREFIX, key];
733       breakpad->RemoveKeyValue(prefixedKey);
734     }
735   } catch(...) {    // don't let exceptions leave this C API
736     fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
737   }
738 }
739 //=============================================================================
740 NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
741   NSString *value = nil;
742
743   try {
744     // Not called at exception time
745     Breakpad *breakpad = (Breakpad *)ref;
746
747     if (!breakpad || !key || !gKeyValueAllocator)
748       return nil;
749
750     ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
751
752     value = breakpad->KeyValue(key);
753   } catch(...) {    // don't let exceptions leave this C API
754     fprintf(stderr, "BreakpadKeyValue() : error\n");
755   }
756
757   return value;
758 }
759
760 //=============================================================================
761 void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
762   try {
763     // Not called at exception time
764     Breakpad *breakpad = (Breakpad *)ref;
765
766     if (breakpad && key && gKeyValueAllocator) {
767       ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
768
769       breakpad->RemoveKeyValue(key);
770     }
771   } catch(...) {    // don't let exceptions leave this C API
772     fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
773   }
774 }
775
776 //=============================================================================
777 int BreakpadGetCrashReportCount(BreakpadRef ref) {
778   try {
779     // Not called at exception time
780     Breakpad *breakpad = (Breakpad *)ref;
781
782     if (breakpad) {
783        return [breakpad->CrashReportsToUpload() count];
784     }
785   } catch(...) {    // don't let exceptions leave this C API
786     fprintf(stderr, "BreakpadGetCrashReportCount() : error\n");
787   }
788   return false;
789 }
790
791 //=============================================================================
792 void BreakpadUploadNextReport(BreakpadRef ref,
793                               NSDictionary *server_parameters) {
794   try {
795     // Not called at exception time
796     Breakpad *breakpad = (Breakpad *)ref;
797
798     if (breakpad) {
799        breakpad->UploadNextReport(server_parameters);
800     }
801   } catch(...) {    // don't let exceptions leave this C API
802     fprintf(stderr, "BreakpadUploadNextReport() : error\n");
803   }
804 }
805
806 //=============================================================================
807 void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name,
808                         NSDictionary *server_parameters) {
809   try {
810     // Not called at exception time
811     Breakpad *breakpad = (Breakpad *)ref;
812
813     if (breakpad) {
814       breakpad->UploadData(data, name, server_parameters);
815     }
816   } catch(...) {    // don't let exceptions leave this C API
817     fprintf(stderr, "BreakpadUploadData() : error\n");
818   }
819 }
820
821 //=============================================================================
822 NSDictionary *BreakpadGenerateReport(BreakpadRef ref,
823                                      NSDictionary *server_parameters) {
824   try {
825     // Not called at exception time
826     Breakpad *breakpad = (Breakpad *)ref;
827
828     if (breakpad) {
829       return breakpad->GenerateReport(server_parameters);
830     } else {
831       return nil;
832     }
833   } catch(...) {    // don't let exceptions leave this C API
834     fprintf(stderr, "BreakpadGenerateReport() : error\n");
835     return nil;
836   }
837 }