summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.mm
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.mm')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.mm916
1 files changed, 916 insertions, 0 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.mm b/toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.mm
new file mode 100644
index 000000000..ce635bd27
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.mm
@@ -0,0 +1,916 @@
+// Copyright (c) 2011, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
+
+#import "client/ios/Breakpad.h"
+
+#include <assert.h>
+#import <Foundation/Foundation.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#import "client/ios/handler/ios_exception_minidump_generator.h"
+#import "client/mac/crash_generation/ConfigFile.h"
+#import "client/mac/handler/exception_handler.h"
+#import "client/mac/handler/minidump_generator.h"
+#import "client/mac/sender/uploader.h"
+#import "client/mac/handler/protected_memory_allocator.h"
+#import "common/simple_string_dictionary.h"
+
+#if !defined(__EXCEPTIONS) || (__clang__ && !__has_feature(cxx_exceptions))
+// This file uses C++ try/catch (but shouldn't). Duplicate the macros from
+// <c++/4.2.1/exception_defines.h> allowing this file to work properly with
+// exceptions disabled even when other C++ libraries are used. #undef the try
+// and catch macros first in case libstdc++ is in use and has already provided
+// its own definitions.
+#undef try
+#define try if (true)
+#undef catch
+#define catch(X) if (false)
+#endif // __EXCEPTIONS
+
+using google_breakpad::ConfigFile;
+using google_breakpad::EnsureDirectoryPathExists;
+using google_breakpad::SimpleStringDictionary;
+
+//=============================================================================
+// We want any memory allocations which are used by breakpad during the
+// exception handling process (after a crash has happened) to be read-only
+// to prevent them from being smashed before a crash occurs. Unfortunately
+// we cannot protect against smashes to our exception handling thread's
+// stack.
+//
+// NOTE: Any memory allocations which are not used during the exception
+// handling process may be allocated in the normal ways.
+//
+// The ProtectedMemoryAllocator class provides an Allocate() method which
+// we'll using in conjunction with placement operator new() to control
+// allocation of C++ objects. Note that we don't use operator delete()
+// but instead call the objects destructor directly: object->~ClassName();
+//
+ProtectedMemoryAllocator *gMasterAllocator = NULL;
+ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
+ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
+
+// Mutex for thread-safe access to the key/value dictionary used by breakpad.
+// It's a global instead of an instance variable of Breakpad
+// since it can't live in a protected memory area.
+pthread_mutex_t gDictionaryMutex;
+
+//=============================================================================
+// Stack-based object for thread-safe access to a memory-protected region.
+// It's assumed that normally the memory block (allocated by the allocator)
+// is protected (read-only). Creating a stack-based instance of
+// ProtectedMemoryLocker will unprotect this block after taking the lock.
+// Its destructor will first re-protect the memory then release the lock.
+class ProtectedMemoryLocker {
+ public:
+ ProtectedMemoryLocker(pthread_mutex_t *mutex,
+ ProtectedMemoryAllocator *allocator)
+ : mutex_(mutex),
+ allocator_(allocator) {
+ // Lock the mutex
+ __attribute__((unused)) int rv = pthread_mutex_lock(mutex_);
+ assert(rv == 0);
+
+ // Unprotect the memory
+ allocator_->Unprotect();
+ }
+
+ ~ProtectedMemoryLocker() {
+ // First protect the memory
+ allocator_->Protect();
+
+ // Then unlock the mutex
+ __attribute__((unused)) int rv = pthread_mutex_unlock(mutex_);
+ assert(rv == 0);
+ };
+
+ private:
+ ProtectedMemoryLocker();
+ ProtectedMemoryLocker(const ProtectedMemoryLocker&);
+ ProtectedMemoryLocker& operator=(const ProtectedMemoryLocker&);
+
+ pthread_mutex_t *mutex_;
+ ProtectedMemoryAllocator *allocator_;
+};
+
+//=============================================================================
+class Breakpad {
+ public:
+ // factory method
+ static Breakpad *Create(NSDictionary *parameters) {
+ // Allocate from our special allocation pool
+ Breakpad *breakpad =
+ new (gBreakpadAllocator->Allocate(sizeof(Breakpad)))
+ Breakpad();
+
+ if (!breakpad)
+ return NULL;
+
+ if (!breakpad->Initialize(parameters)) {
+ // Don't use operator delete() here since we allocated from special pool
+ breakpad->~Breakpad();
+ return NULL;
+ }
+
+ return breakpad;
+ }
+
+ ~Breakpad();
+
+ void SetKeyValue(NSString *key, NSString *value);
+ NSString *KeyValue(NSString *key);
+ void RemoveKeyValue(NSString *key);
+ NSArray *CrashReportsToUpload();
+ NSString *NextCrashReportToUpload();
+ NSDictionary *NextCrashReportConfiguration();
+ void UploadNextReport(NSDictionary *server_parameters);
+ void UploadReportWithConfiguration(NSDictionary *configuration,
+ NSDictionary *server_parameters);
+ void UploadData(NSData *data, NSString *name,
+ NSDictionary *server_parameters);
+ void HandleNetworkResponse(NSDictionary *configuration,
+ NSData *data,
+ NSError *error);
+ NSDictionary *GenerateReport(NSDictionary *server_parameters);
+
+ private:
+ Breakpad()
+ : handler_(NULL),
+ config_params_(NULL) {}
+
+ bool Initialize(NSDictionary *parameters);
+
+ bool ExtractParameters(NSDictionary *parameters);
+
+ // Dispatches to HandleMinidump()
+ static bool HandleMinidumpCallback(const char *dump_dir,
+ const char *minidump_id,
+ void *context, bool succeeded);
+
+ bool HandleMinidump(const char *dump_dir,
+ const char *minidump_id);
+
+ // NSException handler
+ static void UncaughtExceptionHandler(NSException *exception);
+
+ // Handle an uncaught NSException.
+ void HandleUncaughtException(NSException *exception);
+
+ // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's
+ // MachineExceptions.h, we have to explicitly name the handler.
+ google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG)
+
+ SimpleStringDictionary *config_params_; // Create parameters (STRONG)
+
+ ConfigFile config_file_;
+
+ // A static reference to the current Breakpad instance. Used for handling
+ // NSException.
+ static Breakpad *current_breakpad_;
+};
+
+Breakpad *Breakpad::current_breakpad_ = NULL;
+
+#pragma mark -
+#pragma mark Helper functions
+
+//=============================================================================
+// Helper functions
+
+//=============================================================================
+static BOOL IsDebuggerActive() {
+ BOOL result = NO;
+ NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
+
+ // We check both defaults and the environment variable here
+
+ BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER];
+
+ if (!ignoreDebugger) {
+ char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER);
+ ignoreDebugger =
+ (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0;
+ }
+
+ if (!ignoreDebugger) {
+ pid_t pid = getpid();
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+ int mibSize = sizeof(mib) / sizeof(int);
+ size_t actualSize;
+
+ if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) {
+ struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize);
+
+ if (info) {
+ // This comes from looking at the Darwin xnu Kernel
+ if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0)
+ result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO;
+
+ free(info);
+ }
+ }
+ }
+
+ return result;
+}
+
+//=============================================================================
+bool Breakpad::HandleMinidumpCallback(const char *dump_dir,
+ const char *minidump_id,
+ void *context, bool succeeded) {
+ Breakpad *breakpad = (Breakpad *)context;
+
+ // If our context is damaged or something, just return false to indicate that
+ // the handler should continue without us.
+ if (!breakpad || !succeeded)
+ return false;
+
+ return breakpad->HandleMinidump(dump_dir, minidump_id);
+}
+
+//=============================================================================
+void Breakpad::UncaughtExceptionHandler(NSException *exception) {
+ NSSetUncaughtExceptionHandler(NULL);
+ if (current_breakpad_) {
+ current_breakpad_->HandleUncaughtException(exception);
+ BreakpadRelease(current_breakpad_);
+ }
+}
+
+//=============================================================================
+#pragma mark -
+
+//=============================================================================
+bool Breakpad::Initialize(NSDictionary *parameters) {
+ // Initialize
+ current_breakpad_ = this;
+ config_params_ = NULL;
+ handler_ = NULL;
+
+ // Gather any user specified parameters
+ if (!ExtractParameters(parameters)) {
+ return false;
+ }
+
+ // Check for debugger
+ if (IsDebuggerActive()) {
+ return true;
+ }
+
+ // Create the handler (allocating it in our special protected pool)
+ handler_ =
+ new (gBreakpadAllocator->Allocate(
+ sizeof(google_breakpad::ExceptionHandler)))
+ google_breakpad::ExceptionHandler(
+ config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY),
+ 0, &HandleMinidumpCallback, this, true, 0);
+ NSSetUncaughtExceptionHandler(&Breakpad::UncaughtExceptionHandler);
+ return true;
+}
+
+//=============================================================================
+Breakpad::~Breakpad() {
+ NSSetUncaughtExceptionHandler(NULL);
+ current_breakpad_ = NULL;
+ // Note that we don't use operator delete() on these pointers,
+ // since they were allocated by ProtectedMemoryAllocator objects.
+ //
+ if (config_params_) {
+ config_params_->~SimpleStringDictionary();
+ }
+
+ if (handler_)
+ handler_->~ExceptionHandler();
+}
+
+//=============================================================================
+bool Breakpad::ExtractParameters(NSDictionary *parameters) {
+ NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE];
+ NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
+ NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
+ NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
+ NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
+ NSString *vendor =
+ [parameters objectForKey:@BREAKPAD_VENDOR];
+ // We check both parameters and the environment variable here.
+ char *envVarDumpSubdirectory = getenv(BREAKPAD_DUMP_DIRECTORY);
+ NSString *dumpSubdirectory = envVarDumpSubdirectory ?
+ [NSString stringWithUTF8String:envVarDumpSubdirectory] :
+ [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
+
+ NSDictionary *serverParameters =
+ [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
+
+ if (!product)
+ product = [parameters objectForKey:@"CFBundleName"];
+
+ if (!display) {
+ display = [parameters objectForKey:@"CFBundleDisplayName"];
+ if (!display) {
+ display = product;
+ }
+ }
+
+ if (!version.length) // Default nil or empty string to CFBundleVersion
+ version = [parameters objectForKey:@"CFBundleVersion"];
+
+ if (!vendor) {
+ vendor = @"Vendor not specified";
+ }
+
+ if (!dumpSubdirectory) {
+ NSString *cachePath =
+ [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
+ NSUserDomainMask,
+ YES)
+ objectAtIndex:0];
+ dumpSubdirectory =
+ [cachePath stringByAppendingPathComponent:@kDefaultLibrarySubdirectory];
+
+ EnsureDirectoryPathExists(dumpSubdirectory);
+ }
+
+ // The product, version, and URL are required values.
+ if (![product length]) {
+ return false;
+ }
+
+ if (![version length]) {
+ return false;
+ }
+
+ if (![urlStr length]) {
+ return false;
+ }
+
+ config_params_ =
+ new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
+ SimpleStringDictionary();
+
+ SimpleStringDictionary &dictionary = *config_params_;
+
+ dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]);
+ dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
+ dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]);
+ dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]);
+ dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]);
+ dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]);
+ dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
+ [dumpSubdirectory UTF8String]);
+
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ char timeStartedString[32];
+ sprintf(timeStartedString, "%zd", tv.tv_sec);
+ dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, timeStartedString);
+
+ if (serverParameters) {
+ // For each key-value pair, call BreakpadAddUploadParameter()
+ NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
+ NSString *aParameter;
+ while ((aParameter = [keyEnumerator nextObject])) {
+ BreakpadAddUploadParameter(this, aParameter,
+ [serverParameters objectForKey:aParameter]);
+ }
+ }
+ return true;
+}
+
+//=============================================================================
+void Breakpad::SetKeyValue(NSString *key, NSString *value) {
+ // We allow nil values. This is the same as removing the keyvalue.
+ if (!config_params_ || !key)
+ return;
+
+ config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
+}
+
+//=============================================================================
+NSString *Breakpad::KeyValue(NSString *key) {
+ if (!config_params_ || !key)
+ return nil;
+
+ const char *value = config_params_->GetValueForKey([key UTF8String]);
+ return value ? [NSString stringWithUTF8String:value] : nil;
+}
+
+//=============================================================================
+void Breakpad::RemoveKeyValue(NSString *key) {
+ if (!config_params_ || !key) return;
+
+ config_params_->RemoveKey([key UTF8String]);
+}
+
+//=============================================================================
+NSArray *Breakpad::CrashReportsToUpload() {
+ NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
+ if (!directory)
+ return nil;
+ NSArray *dirContents = [[NSFileManager defaultManager]
+ contentsOfDirectoryAtPath:directory error:nil];
+ NSArray *configs = [dirContents filteredArrayUsingPredicate:[NSPredicate
+ predicateWithFormat:@"self BEGINSWITH 'Config-'"]];
+ return configs;
+}
+
+//=============================================================================
+NSString *Breakpad::NextCrashReportToUpload() {
+ NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
+ if (!directory)
+ return nil;
+ NSString *config = [CrashReportsToUpload() lastObject];
+ if (!config)
+ return nil;
+ return [NSString stringWithFormat:@"%@/%@", directory, config];
+}
+
+//=============================================================================
+NSDictionary *Breakpad::NextCrashReportConfiguration() {
+ return [Uploader readConfigurationDataFromFile:NextCrashReportToUpload()];
+}
+
+//=============================================================================
+void Breakpad::HandleNetworkResponse(NSDictionary *configuration,
+ NSData *data,
+ NSError *error) {
+ Uploader *uploader = [[[Uploader alloc]
+ initWithConfig:configuration] autorelease];
+ [uploader handleNetworkResponse:data withError:error];
+}
+
+//=============================================================================
+void Breakpad::UploadReportWithConfiguration(NSDictionary *configuration,
+ NSDictionary *server_parameters) {
+ Uploader *uploader = [[[Uploader alloc]
+ initWithConfig:configuration] autorelease];
+ if (!uploader)
+ return;
+ for (NSString *key in server_parameters) {
+ [uploader addServerParameter:[server_parameters objectForKey:key]
+ forKey:key];
+ }
+ [uploader report];
+}
+
+//=============================================================================
+void Breakpad::UploadNextReport(NSDictionary *server_parameters) {
+ NSDictionary *configuration = NextCrashReportConfiguration();
+ if (configuration) {
+ return UploadReportWithConfiguration(configuration, server_parameters);
+ }
+}
+
+//=============================================================================
+void Breakpad::UploadData(NSData *data, NSString *name,
+ NSDictionary *server_parameters) {
+ NSMutableDictionary *config = [NSMutableDictionary dictionary];
+
+ SimpleStringDictionary::Iterator it(*config_params_);
+ while (const SimpleStringDictionary::Entry *next = it.Next()) {
+ [config setValue:[NSString stringWithUTF8String:next->value]
+ forKey:[NSString stringWithUTF8String:next->key]];
+ }
+
+ Uploader *uploader =
+ [[[Uploader alloc] initWithConfig:config] autorelease];
+ for (NSString *key in server_parameters) {
+ [uploader addServerParameter:[server_parameters objectForKey:key]
+ forKey:key];
+ }
+ [uploader uploadData:data name:name];
+}
+
+//=============================================================================
+NSDictionary *Breakpad::GenerateReport(NSDictionary *server_parameters) {
+ NSString *dumpDirAsNSString = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
+ if (!dumpDirAsNSString)
+ return nil;
+ const char *dumpDir = [dumpDirAsNSString UTF8String];
+
+ google_breakpad::MinidumpGenerator generator(mach_task_self(),
+ MACH_PORT_NULL);
+ std::string dumpId;
+ std::string dumpFilename = generator.UniqueNameInDirectory(dumpDir, &dumpId);
+ bool success = generator.Write(dumpFilename.c_str());
+ if (!success)
+ return nil;
+
+ SimpleStringDictionary params = *config_params_;
+ for (NSString *key in server_parameters) {
+ params.SetKeyValue([key UTF8String],
+ [[server_parameters objectForKey:key] UTF8String]);
+ }
+ ConfigFile config_file;
+ config_file.WriteFile(dumpDir, &params, dumpDir, dumpId.c_str());
+
+ // Handle results.
+ NSMutableDictionary *result = [NSMutableDictionary dictionary];
+ NSString *dumpFullPath = [NSString stringWithUTF8String:dumpFilename.c_str()];
+ [result setValue:dumpFullPath
+ forKey:@BREAKPAD_OUTPUT_DUMP_FILE];
+ [result setValue:[NSString stringWithUTF8String:config_file.GetFilePath()]
+ forKey:@BREAKPAD_OUTPUT_CONFIG_FILE];
+ return result;
+}
+
+//=============================================================================
+bool Breakpad::HandleMinidump(const char *dump_dir,
+ const char *minidump_id) {
+ config_file_.WriteFile(dump_dir,
+ config_params_,
+ dump_dir,
+ minidump_id);
+
+ // Return true here to indicate that we've processed things as much as we
+ // want.
+ return true;
+}
+
+//=============================================================================
+void Breakpad::HandleUncaughtException(NSException *exception) {
+ // Generate the minidump.
+ google_breakpad::IosExceptionMinidumpGenerator generator(exception);
+ const char *minidump_path =
+ config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
+ std::string minidump_id;
+ std::string minidump_filename = generator.UniqueNameInDirectory(minidump_path,
+ &minidump_id);
+ generator.Write(minidump_filename.c_str());
+
+ // Copy the config params and our custom parameter. This is necessary for 2
+ // reasons:
+ // 1- config_params_ is protected.
+ // 2- If the application crash while trying to handle this exception, a usual
+ // report will be generated. This report must not contain these special
+ // keys.
+ SimpleStringDictionary params = *config_params_;
+ params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "type", "exception");
+ params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionName",
+ [[exception name] UTF8String]);
+ params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionReason",
+ [[exception reason] UTF8String]);
+
+ // And finally write the config file.
+ ConfigFile config_file;
+ config_file.WriteFile(minidump_path,
+ &params,
+ minidump_path,
+ minidump_id.c_str());
+}
+
+//=============================================================================
+
+#pragma mark -
+#pragma mark Public API
+
+//=============================================================================
+BreakpadRef BreakpadCreate(NSDictionary *parameters) {
+ try {
+ // This is confusing. Our two main allocators for breakpad memory are:
+ // - gKeyValueAllocator for the key/value memory
+ // - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other
+ // breakpad allocations which are accessed at exception handling time.
+ //
+ // But in order to avoid these two allocators themselves from being smashed,
+ // we'll protect them as well by allocating them with gMasterAllocator.
+ //
+ // gMasterAllocator itself will NOT be protected, but this doesn't matter,
+ // since once it does its allocations and locks the memory, smashes to
+ // itself don't affect anything we care about.
+ gMasterAllocator =
+ new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
+
+ gKeyValueAllocator =
+ new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
+ ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
+
+ // Create a mutex for use in accessing the SimpleStringDictionary
+ int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
+ if (mutexResult == 0) {
+
+ // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
+ // Let's round up to the nearest page size.
+ //
+ int breakpad_pool_size = 4096;
+
+ /*
+ sizeof(Breakpad)
+ + sizeof(google_breakpad::ExceptionHandler)
+ + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
+ */
+
+ gBreakpadAllocator =
+ new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
+ ProtectedMemoryAllocator(breakpad_pool_size);
+
+ // Stack-based autorelease pool for Breakpad::Create() obj-c code.
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ Breakpad *breakpad = Breakpad::Create(parameters);
+
+ if (breakpad) {
+ // Make read-only to protect against memory smashers
+ gMasterAllocator->Protect();
+ gKeyValueAllocator->Protect();
+ gBreakpadAllocator->Protect();
+ // Can uncomment this line to figure out how much space was actually
+ // allocated using this allocator
+ // printf("gBreakpadAllocator allocated size = %d\n",
+ // gBreakpadAllocator->GetAllocatedSize() );
+ [pool release];
+ return (BreakpadRef)breakpad;
+ }
+
+ [pool release];
+ }
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadCreate() : error\n");
+ }
+
+ if (gKeyValueAllocator) {
+ gKeyValueAllocator->~ProtectedMemoryAllocator();
+ gKeyValueAllocator = NULL;
+ }
+
+ if (gBreakpadAllocator) {
+ gBreakpadAllocator->~ProtectedMemoryAllocator();
+ gBreakpadAllocator = NULL;
+ }
+
+ delete gMasterAllocator;
+ gMasterAllocator = NULL;
+
+ return NULL;
+}
+
+//=============================================================================
+void BreakpadRelease(BreakpadRef ref) {
+ try {
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (gMasterAllocator) {
+ gMasterAllocator->Unprotect();
+ gKeyValueAllocator->Unprotect();
+ gBreakpadAllocator->Unprotect();
+
+ breakpad->~Breakpad();
+
+ // Unfortunately, it's not possible to deallocate this stuff
+ // because the exception handling thread is still finishing up
+ // asynchronously at this point... OK, it could be done with
+ // locks, etc. But since BreakpadRelease() should usually only
+ // be called right before the process exits, it's not worth
+ // deallocating this stuff.
+#if 0
+ gKeyValueAllocator->~ProtectedMemoryAllocator();
+ gBreakpadAllocator->~ProtectedMemoryAllocator();
+ delete gMasterAllocator;
+
+ gMasterAllocator = NULL;
+ gKeyValueAllocator = NULL;
+ gBreakpadAllocator = NULL;
+#endif
+
+ pthread_mutex_destroy(&gDictionaryMutex);
+ }
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadRelease() : error\n");
+ }
+}
+
+//=============================================================================
+void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
+ try {
+ // Not called at exception time
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (breakpad && key && gKeyValueAllocator) {
+ ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
+
+ breakpad->SetKeyValue(key, value);
+ }
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadSetKeyValue() : error\n");
+ }
+}
+
+void BreakpadAddUploadParameter(BreakpadRef ref,
+ NSString *key,
+ NSString *value) {
+ // The only difference, internally, between an upload parameter and
+ // a key value one that is set with BreakpadSetKeyValue is that we
+ // prepend the keyname with a special prefix. This informs the
+ // crash sender that the parameter should be sent along with the
+ // POST of the crash dump upload.
+ try {
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (breakpad && key && gKeyValueAllocator) {
+ ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
+
+ NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
+ stringByAppendingString:key];
+ breakpad->SetKeyValue(prefixedKey, value);
+ }
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadSetKeyValue() : error\n");
+ }
+}
+
+void BreakpadRemoveUploadParameter(BreakpadRef ref,
+ NSString *key) {
+ try {
+ // Not called at exception time
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (breakpad && key && gKeyValueAllocator) {
+ ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
+
+ NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
+ @BREAKPAD_SERVER_PARAMETER_PREFIX, key];
+ breakpad->RemoveKeyValue(prefixedKey);
+ }
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
+ }
+}
+//=============================================================================
+NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
+ NSString *value = nil;
+
+ try {
+ // Not called at exception time
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (!breakpad || !key || !gKeyValueAllocator)
+ return nil;
+
+ ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
+
+ value = breakpad->KeyValue(key);
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadKeyValue() : error\n");
+ }
+
+ return value;
+}
+
+//=============================================================================
+void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
+ try {
+ // Not called at exception time
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (breakpad && key && gKeyValueAllocator) {
+ ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
+
+ breakpad->RemoveKeyValue(key);
+ }
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
+ }
+}
+
+//=============================================================================
+int BreakpadGetCrashReportCount(BreakpadRef ref) {
+ try {
+ // Not called at exception time
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (breakpad) {
+ return static_cast<int>([breakpad->CrashReportsToUpload() count]);
+ }
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadGetCrashReportCount() : error\n");
+ }
+ return false;
+}
+
+//=============================================================================
+void BreakpadUploadNextReport(BreakpadRef ref) {
+ BreakpadUploadNextReportWithParameters(ref, nil);
+}
+
+//=============================================================================
+NSDictionary *BreakpadGetNextReportConfiguration(BreakpadRef ref) {
+ try {
+ Breakpad *breakpad = (Breakpad *)ref;
+ if (breakpad)
+ return breakpad->NextCrashReportConfiguration();
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadGetNextReportConfiguration() : error\n");
+ }
+ return nil;
+}
+
+//=============================================================================
+void BreakpadUploadReportWithParametersAndConfiguration(
+ BreakpadRef ref,
+ NSDictionary *server_parameters,
+ NSDictionary *configuration) {
+ try {
+ Breakpad *breakpad = (Breakpad *)ref;
+ if (!breakpad || !configuration)
+ return;
+ breakpad->UploadReportWithConfiguration(configuration, server_parameters);
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr,
+ "BreakpadUploadReportWithParametersAndConfiguration() : error\n");
+ }
+
+}
+
+//=============================================================================
+void BreakpadUploadNextReportWithParameters(BreakpadRef ref,
+ NSDictionary *server_parameters) {
+ try {
+ Breakpad *breakpad = (Breakpad *)ref;
+ if (!breakpad)
+ return;
+ NSDictionary *configuration = breakpad->NextCrashReportConfiguration();
+ if (!configuration)
+ return;
+ return BreakpadUploadReportWithParametersAndConfiguration(ref,
+ server_parameters,
+ configuration);
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadUploadNextReportWithParameters() : error\n");
+ }
+}
+
+void BreakpadHandleNetworkResponse(BreakpadRef ref,
+ NSDictionary *configuration,
+ NSData *data,
+ NSError *error) {
+ try {
+ // Not called at exception time
+ Breakpad *breakpad = (Breakpad *)ref;
+ if (breakpad && configuration)
+ breakpad->HandleNetworkResponse(configuration,data, error);
+
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadHandleNetworkResponse() : error\n");
+ }
+}
+
+//=============================================================================
+void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name,
+ NSDictionary *server_parameters) {
+ try {
+ // Not called at exception time
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (breakpad) {
+ breakpad->UploadData(data, name, server_parameters);
+ }
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadUploadData() : error\n");
+ }
+}
+
+//=============================================================================
+NSDictionary *BreakpadGenerateReport(BreakpadRef ref,
+ NSDictionary *server_parameters) {
+ try {
+ // Not called at exception time
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (breakpad) {
+ return breakpad->GenerateReport(server_parameters);
+ } else {
+ return nil;
+ }
+ } catch(...) { // don't let exceptions leave this C API
+ fprintf(stderr, "BreakpadGenerateReport() : error\n");
+ return nil;
+ }
+}