1 // Copyright (c) 2011, Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
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
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.
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.
30 #define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
32 #import "client/ios/Breakpad.h"
35 #import <Foundation/Foundation.h>
38 #include <sys/sysctl.h>
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"
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.
57 #define catch(X) if (false)
58 #endif // __EXCEPTIONS
60 using google_breakpad::ConfigFile;
61 using google_breakpad::EnsureDirectoryPathExists;
62 using google_breakpad::SimpleStringDictionary;
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
71 // NOTE: Any memory allocations which are not used during the exception
72 // handling process may be allocated in the normal ways.
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();
79 ProtectedMemoryAllocator *gMasterAllocator = NULL;
80 ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
81 ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
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;
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 {
96 ProtectedMemoryLocker(pthread_mutex_t *mutex,
97 ProtectedMemoryAllocator *allocator)
99 allocator_(allocator) {
101 int rv = pthread_mutex_lock(mutex_);
104 // Unprotect the memory
105 allocator_->Unprotect();
108 ~ProtectedMemoryLocker() {
109 // First protect the memory
110 allocator_->Protect();
112 // Then unlock the mutex
113 int rv = pthread_mutex_unlock(mutex_);
118 ProtectedMemoryLocker();
119 ProtectedMemoryLocker(const ProtectedMemoryLocker&);
120 ProtectedMemoryLocker& operator=(const ProtectedMemoryLocker&);
122 pthread_mutex_t *mutex_;
123 ProtectedMemoryAllocator *allocator_;
126 //=============================================================================
130 static Breakpad *Create(NSDictionary *parameters) {
131 // Allocate from our special allocation pool
133 new (gBreakpadAllocator->Allocate(sizeof(Breakpad)))
139 if (!breakpad->Initialize(parameters)) {
140 // Don't use operator delete() here since we allocated from special pool
141 breakpad->~Breakpad();
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);
163 config_params_(NULL) {}
165 bool Initialize(NSDictionary *parameters);
167 bool ExtractParameters(NSDictionary *parameters);
169 // Dispatches to HandleMinidump()
170 static bool HandleMinidumpCallback(const char *dump_dir,
171 const char *minidump_id,
172 void *context, bool succeeded);
174 bool HandleMinidump(const char *dump_dir,
175 const char *minidump_id);
177 // NSException handler
178 static void UncaughtExceptionHandler(NSException *exception);
180 // Handle an uncaught NSException.
181 void HandleUncaughtException(NSException *exception);
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)
187 SimpleStringDictionary *config_params_; // Create parameters (STRONG)
189 ConfigFile config_file_;
191 // A static reference to the current Breakpad instance. Used for handling
193 static Breakpad *current_breakpad_;
196 Breakpad *Breakpad::current_breakpad_ = NULL;
199 #pragma mark Helper functions
201 //=============================================================================
204 //=============================================================================
205 static BOOL IsDebuggerActive() {
207 NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
209 // We check both defaults and the environment variable here
211 BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER];
213 if (!ignoreDebugger) {
214 char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER);
216 (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0;
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);
225 if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) {
226 struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize);
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;
241 //=============================================================================
242 bool Breakpad::HandleMinidumpCallback(const char *dump_dir,
243 const char *minidump_id,
244 void *context, bool succeeded) {
245 Breakpad *breakpad = (Breakpad *)context;
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)
252 return breakpad->HandleMinidump(dump_dir, minidump_id);
255 //=============================================================================
256 void Breakpad::UncaughtExceptionHandler(NSException *exception) {
257 NSSetUncaughtExceptionHandler(NULL);
258 if (current_breakpad_) {
259 current_breakpad_->HandleUncaughtException(exception);
263 //=============================================================================
266 //=============================================================================
267 bool Breakpad::Initialize(NSDictionary *parameters) {
269 current_breakpad_ = this;
270 config_params_ = NULL;
273 // Gather any user specified parameters
274 if (!ExtractParameters(parameters)) {
278 // Check for debugger
279 if (IsDebuggerActive()) {
283 // Create the handler (allocating it in our special protected pool)
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);
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.
301 if (config_params_) {
302 config_params_->~SimpleStringDictionary();
306 handler_->~ExceptionHandler();
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];
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];
324 NSDictionary *serverParameters =
325 [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
328 product = [parameters objectForKey:@"CFBundleName"];
331 display = [parameters objectForKey:@"CFBundleDisplayName"];
338 version = [parameters objectForKey:@"CFBundleVersion"];
341 vendor = @"Vendor not specified";
344 if (!dumpSubdirectory) {
345 NSString *cachePath =
346 [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
351 [cachePath stringByAppendingPathComponent:@kDefaultLibrarySubdirectory];
353 EnsureDirectoryPathExists(dumpSubdirectory);
356 // The product, version, and URL are required values.
357 if (![product length]) {
361 if (![version length]) {
365 if (![urlStr length]) {
370 new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
371 SimpleStringDictionary();
373 SimpleStringDictionary &dictionary = *config_params_;
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]);
385 gettimeofday(&tv, NULL);
386 char timeStartedString[32];
387 sprintf(timeStartedString, "%zd", tv.tv_sec);
388 dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, timeStartedString);
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]);
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)
408 config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
411 //=============================================================================
412 NSString *Breakpad::KeyValue(NSString *key) {
413 if (!config_params_ || !key)
416 const char *value = config_params_->GetValueForKey([key UTF8String]);
417 return value ? [NSString stringWithUTF8String:value] : nil;
420 //=============================================================================
421 void Breakpad::RemoveKeyValue(NSString *key) {
422 if (!config_params_ || !key) return;
424 config_params_->RemoveKey([key UTF8String]);
427 //=============================================================================
428 NSArray *Breakpad::CrashReportsToUpload() {
429 NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
432 NSArray *dirContents = [[NSFileManager defaultManager]
433 contentsOfDirectoryAtPath:directory error:nil];
434 NSArray *configs = [dirContents filteredArrayUsingPredicate:[NSPredicate
435 predicateWithFormat:@"self BEGINSWITH 'Config-'"]];
439 //=============================================================================
440 NSString *Breakpad::NextCrashReportToUpload() {
441 NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
444 NSString *config = [CrashReportsToUpload() lastObject];
447 return [NSString stringWithFormat:@"%@/%@", directory, config];
450 //=============================================================================
451 void Breakpad::UploadNextReport(NSDictionary *server_parameters) {
452 NSString *configFile = NextCrashReportToUpload();
454 Uploader *uploader = [[[Uploader alloc]
455 initWithConfigFile:[configFile UTF8String]] autorelease];
457 for (NSString *key in server_parameters) {
458 [uploader addServerParameter:[server_parameters objectForKey:key]
466 //=============================================================================
467 void Breakpad::UploadData(NSData *data, NSString *name,
468 NSDictionary *server_parameters) {
469 NSMutableDictionary *config = [NSMutableDictionary dictionary];
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]];
478 [[[Uploader alloc] initWithConfig:config] autorelease];
479 for (NSString *key in server_parameters) {
480 [uploader addServerParameter:[server_parameters objectForKey:key]
483 [uploader uploadData:data name:name];
486 //=============================================================================
487 NSDictionary *Breakpad::GenerateReport(NSDictionary *server_parameters) {
488 NSString *dumpDirAsNSString = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
489 if (!dumpDirAsNSString)
491 const char *dumpDir = [dumpDirAsNSString UTF8String];
493 google_breakpad::MinidumpGenerator generator(mach_task_self(),
496 std::string dumpFilename = generator.UniqueNameInDirectory(dumpDir, &dumpId);
497 bool success = generator.Write(dumpFilename.c_str());
501 SimpleStringDictionary params = *config_params_;
502 for (NSString *key in server_parameters) {
503 params.SetKeyValue([key UTF8String],
504 [[server_parameters objectForKey:key] UTF8String]);
506 ConfigFile config_file;
507 config_file.WriteFile(dumpDir, ¶ms, dumpDir, dumpId.c_str());
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];
519 //=============================================================================
520 bool Breakpad::HandleMinidump(const char *dump_dir,
521 const char *minidump_id) {
522 config_file_.WriteFile(dump_dir,
527 // Return true here to indicate that we've processed things as much as we
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,
541 generator.Write(minidump_filename.c_str());
543 // Copy the config params and our custom parameter. This is necessary for 2
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
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]);
556 // And finally write the config file.
557 ConfigFile config_file;
558 config_file.WriteFile(minidump_path,
561 minidump_id.c_str());
564 //=============================================================================
567 #pragma mark Public API
569 //=============================================================================
570 BreakpadRef BreakpadCreate(NSDictionary *parameters) {
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.
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.
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.
584 new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
587 new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
588 ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
590 // Create a mutex for use in accessing the SimpleStringDictionary
591 int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
592 if (mutexResult == 0) {
594 // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
595 // Let's round up to the nearest page size.
597 int breakpad_pool_size = 4096;
601 + sizeof(google_breakpad::ExceptionHandler)
602 + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
606 new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
607 ProtectedMemoryAllocator(breakpad_pool_size);
609 // Stack-based autorelease pool for Breakpad::Create() obj-c code.
610 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
611 Breakpad *breakpad = Breakpad::Create(parameters);
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() );
623 return (BreakpadRef)breakpad;
628 } catch(...) { // don't let exceptions leave this C API
629 fprintf(stderr, "BreakpadCreate() : error\n");
632 if (gKeyValueAllocator) {
633 gKeyValueAllocator->~ProtectedMemoryAllocator();
634 gKeyValueAllocator = NULL;
637 if (gBreakpadAllocator) {
638 gBreakpadAllocator->~ProtectedMemoryAllocator();
639 gBreakpadAllocator = NULL;
642 delete gMasterAllocator;
643 gMasterAllocator = NULL;
648 //=============================================================================
649 void BreakpadRelease(BreakpadRef ref) {
651 Breakpad *breakpad = (Breakpad *)ref;
653 if (gMasterAllocator) {
654 gMasterAllocator->Unprotect();
655 gKeyValueAllocator->Unprotect();
656 gBreakpadAllocator->Unprotect();
658 breakpad->~Breakpad();
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.
667 gKeyValueAllocator->~ProtectedMemoryAllocator();
668 gBreakpadAllocator->~ProtectedMemoryAllocator();
669 delete gMasterAllocator;
671 gMasterAllocator = NULL;
672 gKeyValueAllocator = NULL;
673 gBreakpadAllocator = NULL;
676 pthread_mutex_destroy(&gDictionaryMutex);
678 } catch(...) { // don't let exceptions leave this C API
679 fprintf(stderr, "BreakpadRelease() : error\n");
683 //=============================================================================
684 void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
686 // Not called at exception time
687 Breakpad *breakpad = (Breakpad *)ref;
689 if (breakpad && key && gKeyValueAllocator) {
690 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
692 breakpad->SetKeyValue(key, value);
694 } catch(...) { // don't let exceptions leave this C API
695 fprintf(stderr, "BreakpadSetKeyValue() : error\n");
699 void BreakpadAddUploadParameter(BreakpadRef ref,
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.
708 Breakpad *breakpad = (Breakpad *)ref;
710 if (breakpad && key && gKeyValueAllocator) {
711 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
713 NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
714 stringByAppendingString:key];
715 breakpad->SetKeyValue(prefixedKey, value);
717 } catch(...) { // don't let exceptions leave this C API
718 fprintf(stderr, "BreakpadSetKeyValue() : error\n");
722 void BreakpadRemoveUploadParameter(BreakpadRef ref,
725 // Not called at exception time
726 Breakpad *breakpad = (Breakpad *)ref;
728 if (breakpad && key && gKeyValueAllocator) {
729 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
731 NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
732 @BREAKPAD_SERVER_PARAMETER_PREFIX, key];
733 breakpad->RemoveKeyValue(prefixedKey);
735 } catch(...) { // don't let exceptions leave this C API
736 fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
739 //=============================================================================
740 NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
741 NSString *value = nil;
744 // Not called at exception time
745 Breakpad *breakpad = (Breakpad *)ref;
747 if (!breakpad || !key || !gKeyValueAllocator)
750 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
752 value = breakpad->KeyValue(key);
753 } catch(...) { // don't let exceptions leave this C API
754 fprintf(stderr, "BreakpadKeyValue() : error\n");
760 //=============================================================================
761 void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
763 // Not called at exception time
764 Breakpad *breakpad = (Breakpad *)ref;
766 if (breakpad && key && gKeyValueAllocator) {
767 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
769 breakpad->RemoveKeyValue(key);
771 } catch(...) { // don't let exceptions leave this C API
772 fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
776 //=============================================================================
777 int BreakpadGetCrashReportCount(BreakpadRef ref) {
779 // Not called at exception time
780 Breakpad *breakpad = (Breakpad *)ref;
783 return [breakpad->CrashReportsToUpload() count];
785 } catch(...) { // don't let exceptions leave this C API
786 fprintf(stderr, "BreakpadGetCrashReportCount() : error\n");
791 //=============================================================================
792 void BreakpadUploadNextReport(BreakpadRef ref,
793 NSDictionary *server_parameters) {
795 // Not called at exception time
796 Breakpad *breakpad = (Breakpad *)ref;
799 breakpad->UploadNextReport(server_parameters);
801 } catch(...) { // don't let exceptions leave this C API
802 fprintf(stderr, "BreakpadUploadNextReport() : error\n");
806 //=============================================================================
807 void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name,
808 NSDictionary *server_parameters) {
810 // Not called at exception time
811 Breakpad *breakpad = (Breakpad *)ref;
814 breakpad->UploadData(data, name, server_parameters);
816 } catch(...) { // don't let exceptions leave this C API
817 fprintf(stderr, "BreakpadUploadData() : error\n");
821 //=============================================================================
822 NSDictionary *BreakpadGenerateReport(BreakpadRef ref,
823 NSDictionary *server_parameters) {
825 // Not called at exception time
826 Breakpad *breakpad = (Breakpad *)ref;
829 return breakpad->GenerateReport(server_parameters);
833 } catch(...) { // don't let exceptions leave this C API
834 fprintf(stderr, "BreakpadGenerateReport() : error\n");