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 __attribute__((unused)) 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 __attribute__((unused)) 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);
261 BreakpadRelease(current_breakpad_);
264 //=============================================================================
267 //=============================================================================
268 bool Breakpad::Initialize(NSDictionary *parameters) {
270 current_breakpad_ = this;
271 config_params_ = NULL;
274 // Gather any user specified parameters
275 if (!ExtractParameters(parameters)) {
279 // Check for debugger
280 if (IsDebuggerActive()) {
284 // Create the handler (allocating it in our special protected pool)
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);
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.
302 if (config_params_) {
303 config_params_->~SimpleStringDictionary();
307 handler_->~ExceptionHandler();
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];
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];
325 NSDictionary *serverParameters =
326 [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
329 product = [parameters objectForKey:@"CFBundleName"];
332 display = [parameters objectForKey:@"CFBundleDisplayName"];
339 version = [parameters objectForKey:@"CFBundleVersion"];
342 vendor = @"Vendor not specified";
345 if (!dumpSubdirectory) {
346 NSString *cachePath =
347 [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
352 [cachePath stringByAppendingPathComponent:@kDefaultLibrarySubdirectory];
354 EnsureDirectoryPathExists(dumpSubdirectory);
357 // The product, version, and URL are required values.
358 if (![product length]) {
362 if (![version length]) {
366 if (![urlStr length]) {
371 new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
372 SimpleStringDictionary();
374 SimpleStringDictionary &dictionary = *config_params_;
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]);
386 gettimeofday(&tv, NULL);
387 char timeStartedString[32];
388 sprintf(timeStartedString, "%zd", tv.tv_sec);
389 dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, timeStartedString);
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]);
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)
409 config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
412 //=============================================================================
413 NSString *Breakpad::KeyValue(NSString *key) {
414 if (!config_params_ || !key)
417 const char *value = config_params_->GetValueForKey([key UTF8String]);
418 return value ? [NSString stringWithUTF8String:value] : nil;
421 //=============================================================================
422 void Breakpad::RemoveKeyValue(NSString *key) {
423 if (!config_params_ || !key) return;
425 config_params_->RemoveKey([key UTF8String]);
428 //=============================================================================
429 NSArray *Breakpad::CrashReportsToUpload() {
430 NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
433 NSArray *dirContents = [[NSFileManager defaultManager]
434 contentsOfDirectoryAtPath:directory error:nil];
435 NSArray *configs = [dirContents filteredArrayUsingPredicate:[NSPredicate
436 predicateWithFormat:@"self BEGINSWITH 'Config-'"]];
440 //=============================================================================
441 NSString *Breakpad::NextCrashReportToUpload() {
442 NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
445 NSString *config = [CrashReportsToUpload() lastObject];
448 return [NSString stringWithFormat:@"%@/%@", directory, config];
451 //=============================================================================
452 void Breakpad::UploadNextReport(NSDictionary *server_parameters) {
453 NSString *configFile = NextCrashReportToUpload();
455 Uploader *uploader = [[[Uploader alloc]
456 initWithConfigFile:[configFile UTF8String]] autorelease];
458 for (NSString *key in server_parameters) {
459 [uploader addServerParameter:[server_parameters objectForKey:key]
467 //=============================================================================
468 void Breakpad::UploadData(NSData *data, NSString *name,
469 NSDictionary *server_parameters) {
470 NSMutableDictionary *config = [NSMutableDictionary dictionary];
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]];
479 [[[Uploader alloc] initWithConfig:config] autorelease];
480 for (NSString *key in server_parameters) {
481 [uploader addServerParameter:[server_parameters objectForKey:key]
484 [uploader uploadData:data name:name];
487 //=============================================================================
488 NSDictionary *Breakpad::GenerateReport(NSDictionary *server_parameters) {
489 NSString *dumpDirAsNSString = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
490 if (!dumpDirAsNSString)
492 const char *dumpDir = [dumpDirAsNSString UTF8String];
494 google_breakpad::MinidumpGenerator generator(mach_task_self(),
497 std::string dumpFilename = generator.UniqueNameInDirectory(dumpDir, &dumpId);
498 bool success = generator.Write(dumpFilename.c_str());
502 SimpleStringDictionary params = *config_params_;
503 for (NSString *key in server_parameters) {
504 params.SetKeyValue([key UTF8String],
505 [[server_parameters objectForKey:key] UTF8String]);
507 ConfigFile config_file;
508 config_file.WriteFile(dumpDir, ¶ms, dumpDir, dumpId.c_str());
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];
520 //=============================================================================
521 bool Breakpad::HandleMinidump(const char *dump_dir,
522 const char *minidump_id) {
523 config_file_.WriteFile(dump_dir,
528 // Return true here to indicate that we've processed things as much as we
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,
542 generator.Write(minidump_filename.c_str());
544 // Copy the config params and our custom parameter. This is necessary for 2
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
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]);
557 // And finally write the config file.
558 ConfigFile config_file;
559 config_file.WriteFile(minidump_path,
562 minidump_id.c_str());
565 //=============================================================================
568 #pragma mark Public API
570 //=============================================================================
571 BreakpadRef BreakpadCreate(NSDictionary *parameters) {
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.
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.
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.
585 new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
588 new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
589 ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
591 // Create a mutex for use in accessing the SimpleStringDictionary
592 int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
593 if (mutexResult == 0) {
595 // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
596 // Let's round up to the nearest page size.
598 int breakpad_pool_size = 4096;
602 + sizeof(google_breakpad::ExceptionHandler)
603 + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
607 new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
608 ProtectedMemoryAllocator(breakpad_pool_size);
610 // Stack-based autorelease pool for Breakpad::Create() obj-c code.
611 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
612 Breakpad *breakpad = Breakpad::Create(parameters);
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() );
624 return (BreakpadRef)breakpad;
629 } catch(...) { // don't let exceptions leave this C API
630 fprintf(stderr, "BreakpadCreate() : error\n");
633 if (gKeyValueAllocator) {
634 gKeyValueAllocator->~ProtectedMemoryAllocator();
635 gKeyValueAllocator = NULL;
638 if (gBreakpadAllocator) {
639 gBreakpadAllocator->~ProtectedMemoryAllocator();
640 gBreakpadAllocator = NULL;
643 delete gMasterAllocator;
644 gMasterAllocator = NULL;
649 //=============================================================================
650 void BreakpadRelease(BreakpadRef ref) {
652 Breakpad *breakpad = (Breakpad *)ref;
654 if (gMasterAllocator) {
655 gMasterAllocator->Unprotect();
656 gKeyValueAllocator->Unprotect();
657 gBreakpadAllocator->Unprotect();
659 breakpad->~Breakpad();
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.
668 gKeyValueAllocator->~ProtectedMemoryAllocator();
669 gBreakpadAllocator->~ProtectedMemoryAllocator();
670 delete gMasterAllocator;
672 gMasterAllocator = NULL;
673 gKeyValueAllocator = NULL;
674 gBreakpadAllocator = NULL;
677 pthread_mutex_destroy(&gDictionaryMutex);
679 } catch(...) { // don't let exceptions leave this C API
680 fprintf(stderr, "BreakpadRelease() : error\n");
684 //=============================================================================
685 void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
687 // Not called at exception time
688 Breakpad *breakpad = (Breakpad *)ref;
690 if (breakpad && key && gKeyValueAllocator) {
691 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
693 breakpad->SetKeyValue(key, value);
695 } catch(...) { // don't let exceptions leave this C API
696 fprintf(stderr, "BreakpadSetKeyValue() : error\n");
700 void BreakpadAddUploadParameter(BreakpadRef ref,
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.
709 Breakpad *breakpad = (Breakpad *)ref;
711 if (breakpad && key && gKeyValueAllocator) {
712 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
714 NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
715 stringByAppendingString:key];
716 breakpad->SetKeyValue(prefixedKey, value);
718 } catch(...) { // don't let exceptions leave this C API
719 fprintf(stderr, "BreakpadSetKeyValue() : error\n");
723 void BreakpadRemoveUploadParameter(BreakpadRef ref,
726 // Not called at exception time
727 Breakpad *breakpad = (Breakpad *)ref;
729 if (breakpad && key && gKeyValueAllocator) {
730 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
732 NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
733 @BREAKPAD_SERVER_PARAMETER_PREFIX, key];
734 breakpad->RemoveKeyValue(prefixedKey);
736 } catch(...) { // don't let exceptions leave this C API
737 fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
740 //=============================================================================
741 NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
742 NSString *value = nil;
745 // Not called at exception time
746 Breakpad *breakpad = (Breakpad *)ref;
748 if (!breakpad || !key || !gKeyValueAllocator)
751 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
753 value = breakpad->KeyValue(key);
754 } catch(...) { // don't let exceptions leave this C API
755 fprintf(stderr, "BreakpadKeyValue() : error\n");
761 //=============================================================================
762 void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
764 // Not called at exception time
765 Breakpad *breakpad = (Breakpad *)ref;
767 if (breakpad && key && gKeyValueAllocator) {
768 ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
770 breakpad->RemoveKeyValue(key);
772 } catch(...) { // don't let exceptions leave this C API
773 fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
777 //=============================================================================
778 int BreakpadGetCrashReportCount(BreakpadRef ref) {
780 // Not called at exception time
781 Breakpad *breakpad = (Breakpad *)ref;
784 return static_cast<int>([breakpad->CrashReportsToUpload() count]);
786 } catch(...) { // don't let exceptions leave this C API
787 fprintf(stderr, "BreakpadGetCrashReportCount() : error\n");
792 //=============================================================================
793 void BreakpadUploadNextReport(BreakpadRef ref) {
794 BreakpadUploadNextReportWithParameters(ref, nil);
797 //=============================================================================
798 void BreakpadUploadNextReportWithParameters(BreakpadRef ref,
799 NSDictionary *server_parameters) {
801 // Not called at exception time
802 Breakpad *breakpad = (Breakpad *)ref;
805 breakpad->UploadNextReport(server_parameters);
807 } catch(...) { // don't let exceptions leave this C API
808 fprintf(stderr, "BreakpadUploadNextReport() : error\n");
812 //=============================================================================
813 void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name,
814 NSDictionary *server_parameters) {
816 // Not called at exception time
817 Breakpad *breakpad = (Breakpad *)ref;
820 breakpad->UploadData(data, name, server_parameters);
822 } catch(...) { // don't let exceptions leave this C API
823 fprintf(stderr, "BreakpadUploadData() : error\n");
827 //=============================================================================
828 NSDictionary *BreakpadGenerateReport(BreakpadRef ref,
829 NSDictionary *server_parameters) {
831 // Not called at exception time
832 Breakpad *breakpad = (Breakpad *)ref;
835 return breakpad->GenerateReport(server_parameters);
839 } catch(...) { // don't let exceptions leave this C API
840 fprintf(stderr, "BreakpadGenerateReport() : error\n");