summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/client/ios
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/client/ios')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.h246
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.mm916
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.xcodeproj/project.pbxproj578
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/ios/BreakpadController.h141
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/ios/BreakpadController.mm354
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad_Prefix.pch7
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/ios/handler/ios_exception_minidump_generator.h74
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/ios/handler/ios_exception_minidump_generator.mm210
8 files changed, 2526 insertions, 0 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.h b/toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.h
new file mode 100644
index 000000000..c099ad07c
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.h
@@ -0,0 +1,246 @@
+// 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.
+
+// Framework to provide a simple C API to crash reporting for
+// applications. By default, if any machine-level exception (e.g.,
+// EXC_BAD_ACCESS) occurs, it will be handled by the BreakpadRef
+// object as follows:
+//
+// 1. Create a minidump file (see Breakpad for details)
+// 2. Create a config file.
+//
+// These files can then be uploaded to a server.
+
+typedef void *BreakpadRef;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <Foundation/Foundation.h>
+
+#include <client/apple/Framework/BreakpadDefines.h>
+
+// The keys in the dictionary returned by |BreakpadGenerateReport|.
+#define BREAKPAD_OUTPUT_DUMP_FILE "BreakpadDumpFile"
+#define BREAKPAD_OUTPUT_CONFIG_FILE "BreakpadConfigFile"
+
+// Optional user-defined function to decide if we should handle this crash or
+// forward it along.
+// Return true if you want Breakpad to handle it.
+// Return false if you want Breakpad to skip it
+// The exception handler always returns false, as if SEND_AND_EXIT were false
+// (which means the next exception handler will take the exception)
+typedef bool (*BreakpadFilterCallback)(int exception_type,
+ int exception_code,
+ mach_port_t crashing_thread,
+ void *context);
+
+// Create a new BreakpadRef object and install it as an exception
+// handler. The |parameters| will typically be the contents of your
+// bundle's Info.plist.
+//
+// You can also specify these additional keys for customizable behavior:
+// Key: Value:
+// BREAKPAD_PRODUCT Product name (e.g., "MyAwesomeProduct")
+// This one is used as the key to identify
+// the product when uploading. Falls back to
+// CFBundleName if not specified.
+// REQUIRED
+//
+// BREAKPAD_PRODUCT_DISPLAY This is the display name, e.g. a pretty
+// name for the product when the crash_sender
+// pops up UI for the user. Falls back first to
+// CFBundleDisplayName and then to
+// BREAKPAD_PRODUCT if not specified.
+//
+// BREAKPAD_VERSION Product version (e.g., 1.2.3), used
+// as metadata for crash report. Falls back to
+// CFBundleVersion if not specified.
+// REQUIRED
+//
+// BREAKPAD_VENDOR Vendor name, used in UI (e.g. "A report has
+// been created that you can send to <vendor>")
+//
+// BREAKPAD_URL URL destination for reporting
+// REQUIRED
+//
+// BREAKPAD_DUMP_DIRECTORY The directory to store crash-dumps
+// in. By default, we use
+// ~/Library/Cache/Breakpad/<BREAKPAD_PRODUCT>
+// The path you specify here is tilde-expanded.
+//
+// BREAKPAD_SERVER_TYPE A parameter that tells Breakpad how to
+// rewrite the upload parameters for a specific
+// server type. The currently valid values are
+// 'socorro' or 'google'. If you want to add
+// other types, see the function in
+// crash_report_sender.m that maps parameters to
+// URL parameters. Defaults to 'google'.
+//
+// BREAKPAD_SERVER_PARAMETER_DICT A plist dictionary of static
+// parameters that are uploaded to the
+// server. The parameters are sent as
+// is to the crash server. Their
+// content isn't added to the minidump
+// but pass as URL parameters when
+// uploading theminidump to the crash
+// server.
+//=============================================================================
+// The BREAKPAD_PRODUCT, BREAKPAD_VERSION and BREAKPAD_URL are
+// required to have non-NULL values. By default, the BREAKPAD_PRODUCT
+// will be the CFBundleName and the BREAKPAD_VERSION will be the
+// CFBundleVersion when these keys are present in the bundle's
+// Info.plist, which is usually passed in to BreakpadCreate() as an
+// NSDictionary (you could also pass in another dictionary that had
+// the same keys configured). If the BREAKPAD_PRODUCT or
+// BREAKPAD_VERSION are ultimately undefined, BreakpadCreate() will
+// fail. You have been warned.
+//
+// If you are running in a debugger, Breakpad will not install, unless the
+// BREAKPAD_IGNORE_DEBUGGER envionment variable is set and/or non-zero.
+//
+//=============================================================================
+// The following are NOT user-supplied but are documented here for
+// completeness. They are calculated by Breakpad during initialization &
+// crash-dump generation, or entered in by the user.
+//
+// BREAKPAD_PROCESS_START_TIME The time, in seconds since the Epoch, the
+// process started
+//
+// BREAKPAD_PROCESS_CRASH_TIME The time, in seconds since the Epoch, the
+// process crashed.
+//
+// BREAKPAD_PROCESS_UP_TIME The total time in milliseconds the process
+// has been running. This parameter is not
+// set until the crash-dump-generation phase.
+//
+// BREAKPAD_SERVER_PARAMETER_PREFIX This prefix is used by Breakpad
+// internally, because Breakpad uses
+// the same dictionary internally to
+// track both its internal
+// configuration parameters and
+// parameters meant to be uploaded
+// to the server. This string is
+// used internally by Breakpad to
+// prefix user-supplied parameter
+// names so those can be sent to the
+// server without leaking Breakpad's
+// internal values.
+
+// Returns a new BreakpadRef object on success, NULL otherwise.
+BreakpadRef BreakpadCreate(NSDictionary *parameters);
+
+// Uninstall and release the data associated with |ref|.
+void BreakpadRelease(BreakpadRef ref);
+
+// User defined key and value string storage. Generally this is used
+// to configure Breakpad's internal operation, such as whether the
+// crash_sender should prompt the user, or the filesystem location for
+// the minidump file. See Breakpad.h for some parameters that can be
+// set. Anything longer than 255 bytes will be truncated. Note that
+// the string is converted to UTF8 before truncation, so any multibyte
+// character that straddles the 255(256 - 1 for terminator) byte limit
+// will be mangled.
+//
+// A maximum number of 64 key/value pairs are supported. An assert()
+// will fire if more than this number are set. Unfortunately, right
+// now, the same dictionary is used for both Breakpad's parameters AND
+// the Upload parameters.
+//
+// TODO (nealsid): Investigate how necessary this is if we don't
+// automatically upload parameters to the server anymore.
+// TODO (nealsid): separate server parameter dictionary from the
+// dictionary used to configure Breakpad, and document limits for each
+// independently.
+void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value);
+NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key);
+void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key);
+
+// You can use this method to specify parameters that will be uploaded
+// to the crash server. They will be automatically encoded as
+// necessary. Note that as mentioned above there are limits on both
+// the number of keys and their length.
+void BreakpadAddUploadParameter(BreakpadRef ref, NSString *key,
+ NSString *value);
+
+// This method will remove a previously-added parameter from the
+// upload parameter set.
+void BreakpadRemoveUploadParameter(BreakpadRef ref, NSString *key);
+
+// Method to handle uploading data to the server
+
+// Returns the number of crash reports waiting to send to the server.
+int BreakpadGetCrashReportCount(BreakpadRef ref);
+
+// Returns the next upload configuration. The report file is deleted.
+NSDictionary *BreakpadGetNextReportConfiguration(BreakpadRef ref);
+
+// Upload next report to the server.
+void BreakpadUploadNextReport(BreakpadRef ref);
+
+// Upload next report to the server.
+// |server_parameters| is additional server parameters to send.
+void BreakpadUploadNextReportWithParameters(BreakpadRef ref,
+ NSDictionary *server_parameters);
+
+// Upload a report to the server.
+// |server_parameters| is additional server parameters to send.
+// |configuration| is the configuration of the breakpad report to send.
+void BreakpadUploadReportWithParametersAndConfiguration(
+ BreakpadRef ref,
+ NSDictionary *server_parameters,
+ NSDictionary *configuration);
+
+// Handles the network response of a breakpad upload. This function is needed if
+// the actual upload is done by the Breakpad client.
+// |configuration| is the configuration of the upload. It must contain the same
+// fields as the configuration passed to
+// BreakpadUploadReportWithParametersAndConfiguration.
+// |data| and |error| contain the network response.
+void BreakpadHandleNetworkResponse(BreakpadRef ref,
+ NSDictionary *configuration,
+ NSData *data,
+ NSError *error);
+
+// Upload a file to the server. |data| is the content of the file to sent.
+// |server_parameters| is additional server parameters to send.
+void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name,
+ NSDictionary *server_parameters);
+
+// Generate a breakpad minidump and configuration file in the dump directory.
+// The report will be available for uploading. The paths of the created files
+// are returned in the dictionary. |server_parameters| is additional server
+// parameters to add in the config file.
+NSDictionary *BreakpadGenerateReport(BreakpadRef ref,
+ NSDictionary *server_parameters);
+
+#ifdef __cplusplus
+}
+#endif
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;
+ }
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.xcodeproj/project.pbxproj b/toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..e9fcae3f9
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad.xcodeproj/project.pbxproj
@@ -0,0 +1,578 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 14569321182CE29F0029C465 /* ucontext_compat.h in Headers */ = {isa = PBXBuildFile; fileRef = 14569320182CE29F0029C465 /* ucontext_compat.h */; };
+ 14569323182CE2C10029C465 /* mach_vm_compat.h in Headers */ = {isa = PBXBuildFile; fileRef = 14569322182CE2C10029C465 /* mach_vm_compat.h */; };
+ 16BFA67014E195E9009704F8 /* ios_exception_minidump_generator.h in Headers */ = {isa = PBXBuildFile; fileRef = 16BFA66E14E195E9009704F8 /* ios_exception_minidump_generator.h */; };
+ 16BFA67214E1965A009704F8 /* ios_exception_minidump_generator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16BFA67114E1965A009704F8 /* ios_exception_minidump_generator.mm */; };
+ 16C7CCCB147D4A4300776EAD /* BreakpadDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7C968147D4A4200776EAD /* BreakpadDefines.h */; };
+ 16C7CCCC147D4A4300776EAD /* Breakpad.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7C96A147D4A4200776EAD /* Breakpad.h */; };
+ 16C7CCCD147D4A4300776EAD /* Breakpad.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7C96B147D4A4200776EAD /* Breakpad.mm */; };
+ 16C7CDE8147D4A4300776EAD /* ConfigFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CB9E147D4A4300776EAD /* ConfigFile.h */; };
+ 16C7CDE9147D4A4300776EAD /* ConfigFile.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CB9F147D4A4300776EAD /* ConfigFile.mm */; };
+ 16C7CDF5147D4A4300776EAD /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBAD147D4A4300776EAD /* breakpad_nlist_64.cc */; };
+ 16C7CDF6147D4A4300776EAD /* breakpad_nlist_64.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBAE147D4A4300776EAD /* breakpad_nlist_64.h */; };
+ 16C7CDF7147D4A4300776EAD /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBAF147D4A4300776EAD /* dynamic_images.cc */; };
+ 16C7CDF8147D4A4300776EAD /* dynamic_images.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBB0147D4A4300776EAD /* dynamic_images.h */; };
+ 16C7CDF9147D4A4300776EAD /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBB1147D4A4300776EAD /* exception_handler.cc */; };
+ 16C7CDFA147D4A4300776EAD /* exception_handler.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBB2147D4A4300776EAD /* exception_handler.h */; };
+ 16C7CDFC147D4A4300776EAD /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBB4147D4A4300776EAD /* minidump_generator.cc */; };
+ 16C7CDFD147D4A4300776EAD /* minidump_generator.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBB5147D4A4300776EAD /* minidump_generator.h */; };
+ 16C7CDFE147D4A4300776EAD /* protected_memory_allocator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBBC147D4A4300776EAD /* protected_memory_allocator.cc */; };
+ 16C7CDFF147D4A4300776EAD /* protected_memory_allocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBBD147D4A4300776EAD /* protected_memory_allocator.h */; };
+ 16C7CE08147D4A4300776EAD /* uploader.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBEA147D4A4300776EAD /* uploader.h */; };
+ 16C7CE09147D4A4300776EAD /* uploader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBEB147D4A4300776EAD /* uploader.mm */; };
+ 16C7CE18147D4A4300776EAD /* minidump_file_writer-inl.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC04147D4A4300776EAD /* minidump_file_writer-inl.h */; };
+ 16C7CE19147D4A4300776EAD /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC05147D4A4300776EAD /* minidump_file_writer.cc */; };
+ 16C7CE1A147D4A4300776EAD /* minidump_file_writer.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC06147D4A4300776EAD /* minidump_file_writer.h */; };
+ 16C7CE40147D4A4300776EAD /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC4A147D4A4300776EAD /* convert_UTF.c */; };
+ 16C7CE41147D4A4300776EAD /* convert_UTF.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC4B147D4A4300776EAD /* convert_UTF.h */; };
+ 16C7CE78147D4A4300776EAD /* GTMLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC88147D4A4300776EAD /* GTMLogger.h */; };
+ 16C7CE79147D4A4300776EAD /* GTMLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC89147D4A4300776EAD /* GTMLogger.m */; };
+ 16C7CE7A147D4A4300776EAD /* HTTPMultipartUpload.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */; };
+ 16C7CE7B147D4A4300776EAD /* HTTPMultipartUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */; };
+ 16C7CE83147D4A4300776EAD /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC93147D4A4300776EAD /* file_id.cc */; };
+ 16C7CE84147D4A4300776EAD /* file_id.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC94147D4A4300776EAD /* file_id.h */; };
+ 16C7CE85147D4A4300776EAD /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC95147D4A4300776EAD /* macho_id.cc */; };
+ 16C7CE86147D4A4300776EAD /* macho_id.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC96147D4A4300776EAD /* macho_id.h */; };
+ 16C7CE8A147D4A4300776EAD /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC9A147D4A4300776EAD /* macho_utilities.cc */; };
+ 16C7CE8B147D4A4300776EAD /* macho_utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC9B147D4A4300776EAD /* macho_utilities.h */; };
+ 16C7CE8C147D4A4300776EAD /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC9C147D4A4300776EAD /* macho_walker.cc */; };
+ 16C7CE8D147D4A4300776EAD /* macho_walker.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC9D147D4A4300776EAD /* macho_walker.h */; };
+ 16C7CE8F147D4A4300776EAD /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC9F147D4A4300776EAD /* string_utilities.cc */; };
+ 16C7CE90147D4A4300776EAD /* string_utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CCA0147D4A4300776EAD /* string_utilities.h */; };
+ 16C7CE93147D4A4300776EAD /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CCA4147D4A4300776EAD /* md5.cc */; };
+ 16C7CE94147D4A4300776EAD /* md5.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CCA5147D4A4300776EAD /* md5.h */; };
+ 16C7CEA7147D4A4300776EAD /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CCB9147D4A4300776EAD /* string_conversion.cc */; };
+ 16C7CEA8147D4A4300776EAD /* string_conversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CCBA147D4A4300776EAD /* string_conversion.h */; };
+ 16C92FAD150DF8330053D7BA /* BreakpadController.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C92FAB150DF8330053D7BA /* BreakpadController.h */; };
+ 16C92FAE150DF8330053D7BA /* BreakpadController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C92FAC150DF8330053D7BA /* BreakpadController.mm */; };
+ 1EEEB60F1720821900F7E689 /* simple_string_dictionary.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1EEEB60C1720821900F7E689 /* simple_string_dictionary.cc */; };
+ 1EEEB6101720821900F7E689 /* simple_string_dictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EEEB60D1720821900F7E689 /* simple_string_dictionary.h */; };
+ AA747D9F0F9514B9006C5449 /* Breakpad_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */; };
+ AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 14569320182CE29F0029C465 /* ucontext_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ucontext_compat.h; sourceTree = "<group>"; };
+ 14569322182CE2C10029C465 /* mach_vm_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mach_vm_compat.h; sourceTree = "<group>"; };
+ 16BFA66E14E195E9009704F8 /* ios_exception_minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ios_exception_minidump_generator.h; sourceTree = "<group>"; };
+ 16BFA67114E1965A009704F8 /* ios_exception_minidump_generator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ios_exception_minidump_generator.mm; sourceTree = "<group>"; };
+ 16C7C968147D4A4200776EAD /* BreakpadDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BreakpadDefines.h; sourceTree = "<group>"; };
+ 16C7C96A147D4A4200776EAD /* Breakpad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Breakpad.h; sourceTree = "<group>"; };
+ 16C7C96B147D4A4200776EAD /* Breakpad.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Breakpad.mm; sourceTree = "<group>"; };
+ 16C7CB9E147D4A4300776EAD /* ConfigFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConfigFile.h; sourceTree = "<group>"; };
+ 16C7CB9F147D4A4300776EAD /* ConfigFile.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ConfigFile.mm; sourceTree = "<group>"; };
+ 16C7CBAD147D4A4300776EAD /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_64.cc; sourceTree = "<group>"; };
+ 16C7CBAE147D4A4300776EAD /* breakpad_nlist_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_64.h; sourceTree = "<group>"; };
+ 16C7CBAF147D4A4300776EAD /* dynamic_images.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dynamic_images.cc; sourceTree = "<group>"; };
+ 16C7CBB0147D4A4300776EAD /* dynamic_images.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dynamic_images.h; sourceTree = "<group>"; };
+ 16C7CBB1147D4A4300776EAD /* exception_handler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = exception_handler.cc; sourceTree = "<group>"; };
+ 16C7CBB2147D4A4300776EAD /* exception_handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = exception_handler.h; sourceTree = "<group>"; };
+ 16C7CBB4147D4A4300776EAD /* minidump_generator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_generator.cc; sourceTree = "<group>"; };
+ 16C7CBB5147D4A4300776EAD /* minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = minidump_generator.h; sourceTree = "<group>"; };
+ 16C7CBBC147D4A4300776EAD /* protected_memory_allocator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = protected_memory_allocator.cc; sourceTree = "<group>"; };
+ 16C7CBBD147D4A4300776EAD /* protected_memory_allocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = protected_memory_allocator.h; sourceTree = "<group>"; };
+ 16C7CBEA147D4A4300776EAD /* uploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uploader.h; sourceTree = "<group>"; };
+ 16C7CBEB147D4A4300776EAD /* uploader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = uploader.mm; sourceTree = "<group>"; };
+ 16C7CC04147D4A4300776EAD /* minidump_file_writer-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "minidump_file_writer-inl.h"; sourceTree = "<group>"; };
+ 16C7CC05147D4A4300776EAD /* minidump_file_writer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_file_writer.cc; sourceTree = "<group>"; };
+ 16C7CC06147D4A4300776EAD /* minidump_file_writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = minidump_file_writer.h; sourceTree = "<group>"; };
+ 16C7CC07147D4A4300776EAD /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_file_writer_unittest.cc; sourceTree = "<group>"; };
+ 16C7CC4A147D4A4300776EAD /* convert_UTF.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = convert_UTF.c; sourceTree = "<group>"; };
+ 16C7CC4B147D4A4300776EAD /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = convert_UTF.h; sourceTree = "<group>"; };
+ 16C7CC88147D4A4300776EAD /* GTMLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMLogger.h; sourceTree = "<group>"; };
+ 16C7CC89147D4A4300776EAD /* GTMLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMLogger.m; sourceTree = "<group>"; };
+ 16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPMultipartUpload.h; sourceTree = "<group>"; };
+ 16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTTPMultipartUpload.m; sourceTree = "<group>"; };
+ 16C7CC93147D4A4300776EAD /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_id.cc; sourceTree = "<group>"; };
+ 16C7CC94147D4A4300776EAD /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_id.h; sourceTree = "<group>"; };
+ 16C7CC95147D4A4300776EAD /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_id.cc; sourceTree = "<group>"; };
+ 16C7CC96147D4A4300776EAD /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_id.h; sourceTree = "<group>"; };
+ 16C7CC9A147D4A4300776EAD /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_utilities.cc; sourceTree = "<group>"; };
+ 16C7CC9B147D4A4300776EAD /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_utilities.h; sourceTree = "<group>"; };
+ 16C7CC9C147D4A4300776EAD /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_walker.cc; sourceTree = "<group>"; };
+ 16C7CC9D147D4A4300776EAD /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_walker.h; sourceTree = "<group>"; };
+ 16C7CC9F147D4A4300776EAD /* string_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_utilities.cc; sourceTree = "<group>"; };
+ 16C7CCA0147D4A4300776EAD /* string_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_utilities.h; sourceTree = "<group>"; };
+ 16C7CCA4147D4A4300776EAD /* md5.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = md5.cc; sourceTree = "<group>"; };
+ 16C7CCA5147D4A4300776EAD /* md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = md5.h; sourceTree = "<group>"; };
+ 16C7CCB9147D4A4300776EAD /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_conversion.cc; sourceTree = "<group>"; };
+ 16C7CCBA147D4A4300776EAD /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_conversion.h; sourceTree = "<group>"; };
+ 16C92FAB150DF8330053D7BA /* BreakpadController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BreakpadController.h; sourceTree = "<group>"; };
+ 16C92FAC150DF8330053D7BA /* BreakpadController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BreakpadController.mm; sourceTree = "<group>"; };
+ 1EEEB60C1720821900F7E689 /* simple_string_dictionary.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = simple_string_dictionary.cc; sourceTree = "<group>"; };
+ 1EEEB60D1720821900F7E689 /* simple_string_dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simple_string_dictionary.h; sourceTree = "<group>"; };
+ AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Breakpad_Prefix.pch; sourceTree = SOURCE_ROOT; };
+ AACBBE490F95108600F1A2B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ D2AAC07E0554694100DB518D /* libBreakpad.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBreakpad.a; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ D2AAC07C0554694100DB518D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 034768DFFF38A50411DB9C8B /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ D2AAC07E0554694100DB518D /* libBreakpad.a */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 0867D691FE84028FC02AAC07 /* Breakpad */ = {
+ isa = PBXGroup;
+ children = (
+ 08FB77AEFE84172EC02AAC07 /* Classes */,
+ 32C88DFF0371C24200C91783 /* Other Sources */,
+ 0867D69AFE84028FC02AAC07 /* Frameworks */,
+ 034768DFFF38A50411DB9C8B /* Products */,
+ );
+ name = Breakpad;
+ sourceTree = "<group>";
+ };
+ 0867D69AFE84028FC02AAC07 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ AACBBE490F95108600F1A2B1 /* Foundation.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 08FB77AEFE84172EC02AAC07 /* Classes */ = {
+ isa = PBXGroup;
+ children = (
+ 16C7C965147D4A4200776EAD /* client */,
+ 16C7CC47147D4A4300776EAD /* common */,
+ );
+ name = Classes;
+ sourceTree = "<group>";
+ };
+ 16BFA66A14E195E9009704F8 /* handler */ = {
+ isa = PBXGroup;
+ children = (
+ 16BFA67114E1965A009704F8 /* ios_exception_minidump_generator.mm */,
+ 16BFA66E14E195E9009704F8 /* ios_exception_minidump_generator.h */,
+ );
+ path = handler;
+ sourceTree = "<group>";
+ };
+ 16C7C965147D4A4200776EAD /* client */ = {
+ isa = PBXGroup;
+ children = (
+ 16C7C966147D4A4200776EAD /* apple */,
+ 16C7C969147D4A4200776EAD /* ios */,
+ 16C7C99E147D4A4200776EAD /* mac */,
+ 16C7CC04147D4A4300776EAD /* minidump_file_writer-inl.h */,
+ 16C7CC05147D4A4300776EAD /* minidump_file_writer.cc */,
+ 16C7CC06147D4A4300776EAD /* minidump_file_writer.h */,
+ 16C7CC07147D4A4300776EAD /* minidump_file_writer_unittest.cc */,
+ );
+ name = client;
+ path = ..;
+ sourceTree = SOURCE_ROOT;
+ };
+ 16C7C966147D4A4200776EAD /* apple */ = {
+ isa = PBXGroup;
+ children = (
+ 16C7C967147D4A4200776EAD /* Framework */,
+ );
+ path = apple;
+ sourceTree = "<group>";
+ };
+ 16C7C967147D4A4200776EAD /* Framework */ = {
+ isa = PBXGroup;
+ children = (
+ 16C7C968147D4A4200776EAD /* BreakpadDefines.h */,
+ );
+ path = Framework;
+ sourceTree = "<group>";
+ };
+ 16C7C969147D4A4200776EAD /* ios */ = {
+ isa = PBXGroup;
+ children = (
+ 16C92FAB150DF8330053D7BA /* BreakpadController.h */,
+ 16C92FAC150DF8330053D7BA /* BreakpadController.mm */,
+ 16BFA66A14E195E9009704F8 /* handler */,
+ 16C7C96A147D4A4200776EAD /* Breakpad.h */,
+ 16C7C96B147D4A4200776EAD /* Breakpad.mm */,
+ );
+ path = ios;
+ sourceTree = "<group>";
+ };
+ 16C7C99E147D4A4200776EAD /* mac */ = {
+ isa = PBXGroup;
+ children = (
+ 16C7CB9D147D4A4300776EAD /* crash_generation */,
+ 16C7CBAA147D4A4300776EAD /* handler */,
+ 16C7CBC8147D4A4300776EAD /* sender */,
+ );
+ path = mac;
+ sourceTree = "<group>";
+ };
+ 16C7CB9D147D4A4300776EAD /* crash_generation */ = {
+ isa = PBXGroup;
+ children = (
+ 16C7CB9E147D4A4300776EAD /* ConfigFile.h */,
+ 16C7CB9F147D4A4300776EAD /* ConfigFile.mm */,
+ );
+ path = crash_generation;
+ sourceTree = "<group>";
+ };
+ 16C7CBAA147D4A4300776EAD /* handler */ = {
+ isa = PBXGroup;
+ children = (
+ 16C7CBAD147D4A4300776EAD /* breakpad_nlist_64.cc */,
+ 16C7CBAE147D4A4300776EAD /* breakpad_nlist_64.h */,
+ 16C7CBAF147D4A4300776EAD /* dynamic_images.cc */,
+ 16C7CBB0147D4A4300776EAD /* dynamic_images.h */,
+ 16C7CBB1147D4A4300776EAD /* exception_handler.cc */,
+ 16C7CBB2147D4A4300776EAD /* exception_handler.h */,
+ 14569322182CE2C10029C465 /* mach_vm_compat.h */,
+ 16C7CBB4147D4A4300776EAD /* minidump_generator.cc */,
+ 16C7CBB5147D4A4300776EAD /* minidump_generator.h */,
+ 16C7CBBC147D4A4300776EAD /* protected_memory_allocator.cc */,
+ 16C7CBBD147D4A4300776EAD /* protected_memory_allocator.h */,
+ 14569320182CE29F0029C465 /* ucontext_compat.h */,
+ );
+ path = handler;
+ sourceTree = "<group>";
+ };
+ 16C7CBC8147D4A4300776EAD /* sender */ = {
+ isa = PBXGroup;
+ children = (
+ 16C7CBEA147D4A4300776EAD /* uploader.h */,
+ 16C7CBEB147D4A4300776EAD /* uploader.mm */,
+ );
+ path = sender;
+ sourceTree = "<group>";
+ };
+ 16C7CC47147D4A4300776EAD /* common */ = {
+ isa = PBXGroup;
+ children = (
+ 1EEEB60C1720821900F7E689 /* simple_string_dictionary.cc */,
+ 1EEEB60D1720821900F7E689 /* simple_string_dictionary.h */,
+ 16C7CC4A147D4A4300776EAD /* convert_UTF.c */,
+ 16C7CC4B147D4A4300776EAD /* convert_UTF.h */,
+ 16C7CC82147D4A4300776EAD /* mac */,
+ 16C7CCA4147D4A4300776EAD /* md5.cc */,
+ 16C7CCA5147D4A4300776EAD /* md5.h */,
+ 16C7CCB9147D4A4300776EAD /* string_conversion.cc */,
+ 16C7CCBA147D4A4300776EAD /* string_conversion.h */,
+ );
+ name = common;
+ path = ../../common;
+ sourceTree = SOURCE_ROOT;
+ };
+ 16C7CC82147D4A4300776EAD /* mac */ = {
+ isa = PBXGroup;
+ children = (
+ 16C7CC88147D4A4300776EAD /* GTMLogger.h */,
+ 16C7CC89147D4A4300776EAD /* GTMLogger.m */,
+ 16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */,
+ 16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */,
+ 16C7CC93147D4A4300776EAD /* file_id.cc */,
+ 16C7CC94147D4A4300776EAD /* file_id.h */,
+ 16C7CC95147D4A4300776EAD /* macho_id.cc */,
+ 16C7CC96147D4A4300776EAD /* macho_id.h */,
+ 16C7CC9A147D4A4300776EAD /* macho_utilities.cc */,
+ 16C7CC9B147D4A4300776EAD /* macho_utilities.h */,
+ 16C7CC9C147D4A4300776EAD /* macho_walker.cc */,
+ 16C7CC9D147D4A4300776EAD /* macho_walker.h */,
+ 16C7CC9F147D4A4300776EAD /* string_utilities.cc */,
+ 16C7CCA0147D4A4300776EAD /* string_utilities.h */,
+ );
+ path = mac;
+ sourceTree = "<group>";
+ };
+ 32C88DFF0371C24200C91783 /* Other Sources */ = {
+ isa = PBXGroup;
+ children = (
+ AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */,
+ );
+ name = "Other Sources";
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ D2AAC07A0554694100DB518D /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ AA747D9F0F9514B9006C5449 /* Breakpad_Prefix.pch in Headers */,
+ 16C7CCCB147D4A4300776EAD /* BreakpadDefines.h in Headers */,
+ 16C7CCCC147D4A4300776EAD /* Breakpad.h in Headers */,
+ 16C7CDE8147D4A4300776EAD /* ConfigFile.h in Headers */,
+ 14569321182CE29F0029C465 /* ucontext_compat.h in Headers */,
+ 16C7CDF6147D4A4300776EAD /* breakpad_nlist_64.h in Headers */,
+ 16C7CDF8147D4A4300776EAD /* dynamic_images.h in Headers */,
+ 16C7CDFA147D4A4300776EAD /* exception_handler.h in Headers */,
+ 16C7CDFD147D4A4300776EAD /* minidump_generator.h in Headers */,
+ 16C7CDFF147D4A4300776EAD /* protected_memory_allocator.h in Headers */,
+ 16C7CE08147D4A4300776EAD /* uploader.h in Headers */,
+ 16C7CE18147D4A4300776EAD /* minidump_file_writer-inl.h in Headers */,
+ 16C7CE1A147D4A4300776EAD /* minidump_file_writer.h in Headers */,
+ 16C7CE41147D4A4300776EAD /* convert_UTF.h in Headers */,
+ 16C7CE78147D4A4300776EAD /* GTMLogger.h in Headers */,
+ 16C7CE7A147D4A4300776EAD /* HTTPMultipartUpload.h in Headers */,
+ 16C7CE84147D4A4300776EAD /* file_id.h in Headers */,
+ 16C7CE86147D4A4300776EAD /* macho_id.h in Headers */,
+ 16C7CE8B147D4A4300776EAD /* macho_utilities.h in Headers */,
+ 16C7CE8D147D4A4300776EAD /* macho_walker.h in Headers */,
+ 16C7CE90147D4A4300776EAD /* string_utilities.h in Headers */,
+ 16C7CE94147D4A4300776EAD /* md5.h in Headers */,
+ 16C7CEA8147D4A4300776EAD /* string_conversion.h in Headers */,
+ 16BFA67014E195E9009704F8 /* ios_exception_minidump_generator.h in Headers */,
+ 16C92FAD150DF8330053D7BA /* BreakpadController.h in Headers */,
+ 1EEEB6101720821900F7E689 /* simple_string_dictionary.h in Headers */,
+ 14569323182CE2C10029C465 /* mach_vm_compat.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ D2AAC07D0554694100DB518D /* Breakpad */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */;
+ buildPhases = (
+ D2AAC07A0554694100DB518D /* Headers */,
+ D2AAC07B0554694100DB518D /* Sources */,
+ D2AAC07C0554694100DB518D /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Breakpad;
+ productName = Breakpad;
+ productReference = D2AAC07E0554694100DB518D /* libBreakpad.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 0867D690FE84028FC02AAC07 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0510;
+ };
+ buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "Breakpad" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 1;
+ knownRegions = (
+ English,
+ Japanese,
+ French,
+ German,
+ da,
+ de,
+ es,
+ fr,
+ it,
+ ja,
+ nl,
+ no,
+ sl,
+ sv,
+ tr,
+ );
+ mainGroup = 0867D691FE84028FC02AAC07 /* Breakpad */;
+ productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ D2AAC07D0554694100DB518D /* Breakpad */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+ D2AAC07B0554694100DB518D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 16C7CCCD147D4A4300776EAD /* Breakpad.mm in Sources */,
+ 16C7CDE9147D4A4300776EAD /* ConfigFile.mm in Sources */,
+ 16C7CDF5147D4A4300776EAD /* breakpad_nlist_64.cc in Sources */,
+ 16C7CDF7147D4A4300776EAD /* dynamic_images.cc in Sources */,
+ 16C7CDF9147D4A4300776EAD /* exception_handler.cc in Sources */,
+ 16C7CDFC147D4A4300776EAD /* minidump_generator.cc in Sources */,
+ 16C7CDFE147D4A4300776EAD /* protected_memory_allocator.cc in Sources */,
+ 16C7CE09147D4A4300776EAD /* uploader.mm in Sources */,
+ 16C7CE19147D4A4300776EAD /* minidump_file_writer.cc in Sources */,
+ 16C7CE40147D4A4300776EAD /* convert_UTF.c in Sources */,
+ 16C7CE79147D4A4300776EAD /* GTMLogger.m in Sources */,
+ 16C7CE7B147D4A4300776EAD /* HTTPMultipartUpload.m in Sources */,
+ 16C7CE83147D4A4300776EAD /* file_id.cc in Sources */,
+ 16C7CE85147D4A4300776EAD /* macho_id.cc in Sources */,
+ 16C7CE8A147D4A4300776EAD /* macho_utilities.cc in Sources */,
+ 16C7CE8C147D4A4300776EAD /* macho_walker.cc in Sources */,
+ 16C7CE8F147D4A4300776EAD /* string_utilities.cc in Sources */,
+ 16C7CE93147D4A4300776EAD /* md5.cc in Sources */,
+ 16C7CEA7147D4A4300776EAD /* string_conversion.cc in Sources */,
+ 16BFA67214E1965A009704F8 /* ios_exception_minidump_generator.mm in Sources */,
+ 16C92FAE150DF8330053D7BA /* BreakpadController.mm in Sources */,
+ 1EEEB60F1720821900F7E689 /* simple_string_dictionary.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 1DEB921F08733DC00010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ DSTROOT = /tmp/Breakpad.dst;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)/../mac/build/Debug\"",
+ );
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = Breakpad_Prefix.pch;
+ INSTALL_PATH = /usr/local/lib;
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/i386\"",
+ "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/x86_64\"",
+ "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/i386\"",
+ "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/x86_64\"",
+ "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/i386\"",
+ "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/x86_64\"",
+ "\"$(SRCROOT)/../mac/build/Debug\"",
+ "\"$(SRCROOT)/../mac/gcov\"",
+ );
+ PRODUCT_NAME = Breakpad;
+ };
+ name = Debug;
+ };
+ 1DEB922008733DC00010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ DSTROOT = /tmp/Breakpad.dst;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)/../mac/build/Debug\"",
+ );
+ GCC_MODEL_TUNING = G5;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = Breakpad_Prefix.pch;
+ INSTALL_PATH = /usr/local/lib;
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/i386\"",
+ "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/x86_64\"",
+ "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/i386\"",
+ "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/x86_64\"",
+ "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/i386\"",
+ "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/x86_64\"",
+ "\"$(SRCROOT)/../mac/build/Debug\"",
+ "\"$(SRCROOT)/../mac/gcov\"",
+ );
+ PRODUCT_NAME = Breakpad;
+ };
+ name = Release;
+ };
+ 1DEB922308733DC00010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_SHADOW = YES;
+ GCC_WARN_SIGN_COMPARE = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNKNOWN_PRAGMAS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_LABEL = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = (
+ ../../,
+ ../../client/apple/Framework,
+ ../../common/mac,
+ );
+ IPHONEOS_DEPLOYMENT_TARGET = 5.0;
+ ONLY_ACTIVE_ARCH = YES;
+ OTHER_LDFLAGS = "-ObjC";
+ SDKROOT = iphoneos;
+ WARNING_CFLAGS = "-Wundef";
+ };
+ name = Debug;
+ };
+ 1DEB922408733DC00010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_SHADOW = YES;
+ GCC_WARN_SIGN_COMPARE = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNKNOWN_PRAGMAS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_LABEL = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = (
+ ../../,
+ ../../client/apple/Framework,
+ ../../common/mac,
+ );
+ IPHONEOS_DEPLOYMENT_TARGET = 5.0;
+ OTHER_LDFLAGS = "-ObjC";
+ SDKROOT = iphoneos;
+ WARNING_CFLAGS = "-Wundef";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB921F08733DC00010E9CD /* Debug */,
+ 1DEB922008733DC00010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "Breakpad" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB922308733DC00010E9CD /* Debug */,
+ 1DEB922408733DC00010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 0867D690FE84028FC02AAC07 /* Project object */;
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/client/ios/BreakpadController.h b/toolkit/crashreporter/google-breakpad/src/client/ios/BreakpadController.h
new file mode 100644
index 000000000..13609cb8d
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/ios/BreakpadController.h
@@ -0,0 +1,141 @@
+// Copyright (c) 2012, 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.
+
+#ifndef CLIENT_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_
+#define CLIENT_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_
+
+#import <Foundation/Foundation.h>
+
+#import "client/ios/Breakpad.h"
+
+// This class is used to offer a higher level API around BreakpadRef. It
+// configures it, ensures thread-safety, and sends crash reports back to the
+// collecting server. By default, no crash reports are sent, the user must call
+// |setUploadingEnabled:YES| to start the uploading.
+@interface BreakpadController : NSObject {
+ @private
+ // The dispatch queue that will own the breakpad reference.
+ dispatch_queue_t queue_;
+
+ // Instance of Breakpad crash reporter. This is owned by the queue, but can
+ // be created on the main thread at startup.
+ BreakpadRef breakpadRef_;
+
+ // The dictionary that contains configuration for breakpad. Modifying it
+ // should only happen when the controller is not started. The initial value
+ // is the infoDictionary of the bundle of the application.
+ NSMutableDictionary* configuration_;
+
+ // Whether or not crash reports should be uploaded.
+ BOOL enableUploads_;
+
+ // Whether the controller has been started on the main thread. This is only
+ // used to assert the initialization order is correct.
+ BOOL started_;
+
+ // The interval to wait between two uploads. Value is 0 if no upload must be
+ // done.
+ int uploadIntervalInSeconds_;
+
+ // The dictionary that contains additional server parameters to send when
+ // uploading crash reports.
+ NSDictionary* uploadTimeParameters_;
+}
+
+// Singleton.
++ (BreakpadController*)sharedInstance;
+
+// Update the controller configuration. Merges its old configuration with the
+// new one. Merge is done by replacing the old values by the new values.
+- (void)updateConfiguration:(NSDictionary*)configuration;
+
+// Reset the controller configuration to its initial value, which is the
+// infoDictionary of the bundle of the application.
+- (void)resetConfiguration;
+
+// Configure the URL to upload the report to. This must be called at least once
+// if the URL is not in the bundle information.
+- (void)setUploadingURL:(NSString*)url;
+
+// Set the minimal interval between two uploads in seconds. This must be called
+// at least once if the interval is not in the bundle information. A value of 0
+// will prevent uploads.
+- (void)setUploadInterval:(int)intervalInSeconds;
+
+// Set additional server parameters to send when uploading crash reports.
+- (void)setParametersToAddAtUploadTime:(NSDictionary*)uploadTimeParameters;
+
+// Specify an upload parameter that will be added to the crash report when a
+// crash report is generated. See |BreakpadAddUploadParameter|.
+- (void)addUploadParameter:(NSString*)value forKey:(NSString*)key;
+
+// Remove a previously-added parameter from the upload parameter set. See
+// |BreakpadRemoveUploadParameter|.
+- (void)removeUploadParameterForKey:(NSString*)key;
+
+// Access the underlying BreakpadRef. This method is asynchronous, and will be
+// executed on the thread owning the BreakpadRef variable. Moreover, if the
+// controller is not started, the block will be called with a NULL parameter.
+- (void)withBreakpadRef:(void(^)(BreakpadRef))callback;
+
+// Starts the BreakpadController by registering crash handlers. If
+// |onCurrentThread| is YES, all setup is done on the current thread, otherwise
+// it is done on a private queue.
+- (void)start:(BOOL)onCurrentThread;
+
+// Unregisters the crash handlers.
+- (void)stop;
+
+// Enables or disables uploading of crash reports, but does not stop the
+// BreakpadController.
+- (void)setUploadingEnabled:(BOOL)enabled;
+
+// Check if there is currently a crash report to upload.
+- (void)hasReportToUpload:(void(^)(BOOL))callback;
+
+// Get the number of crash reports waiting to upload.
+- (void)getCrashReportCount:(void(^)(int))callback;
+
+// Get the next report to upload.
+// - If upload is disabled, callback will be called with (nil, -1).
+// - If a delay is to be waited before sending, callback will be called with
+// (nil, n), with n (> 0) being the number of seconds to wait.
+// - if no delay is needed, callback will be called with (0, configuration),
+// configuration being next report to upload, or nil if none is pending.
+- (void)getNextReportConfigurationOrSendDelay:
+ (void(^)(NSDictionary*, int))callback;
+
+// Sends synchronously the report specified by |configuration|. This method is
+// NOT thread safe and must be called from the breakpad thread.
+- (void)threadUnsafeSendReportWithConfiguration:(NSDictionary*)configuration
+ withBreakpadRef:(BreakpadRef)ref;
+
+@end
+
+#endif // CLIENT_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/client/ios/BreakpadController.mm b/toolkit/crashreporter/google-breakpad/src/client/ios/BreakpadController.mm
new file mode 100644
index 000000000..dd71cff68
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/ios/BreakpadController.mm
@@ -0,0 +1,354 @@
+// Copyright (c) 2012, 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.
+
+#import "BreakpadController.h"
+
+#import <UIKit/UIKit.h>
+#include <asl.h>
+#include <execinfo.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+
+#include <common/scoped_ptr.h>
+
+#pragma mark -
+#pragma mark Private Methods
+
+@interface BreakpadController ()
+
+// Init the singleton instance.
+- (id)initSingleton;
+
+// Load a crash report and send it to the server.
+- (void)sendStoredCrashReports;
+
+// Returns when a report can be sent. |-1| means never, |0| means that a report
+// can be sent immediately, a positive number is the number of seconds to wait
+// before being allowed to upload a report.
+- (int)sendDelay;
+
+// Notifies that a report will be sent, and update the last sending time
+// accordingly.
+- (void)reportWillBeSent;
+
+@end
+
+#pragma mark -
+#pragma mark Anonymous namespace
+
+namespace {
+
+// The name of the user defaults key for the last submission to the crash
+// server.
+NSString* const kLastSubmission = @"com.google.Breakpad.LastSubmission";
+
+// Returns a NSString describing the current platform.
+NSString* GetPlatform() {
+ // Name of the system call for getting the platform.
+ static const char kHwMachineSysctlName[] = "hw.machine";
+
+ NSString* result = nil;
+
+ size_t size = 0;
+ if (sysctlbyname(kHwMachineSysctlName, NULL, &size, NULL, 0) || size == 0)
+ return nil;
+ google_breakpad::scoped_array<char> machine(new char[size]);
+ if (sysctlbyname(kHwMachineSysctlName, machine.get(), &size, NULL, 0) == 0)
+ result = [NSString stringWithUTF8String:machine.get()];
+ return result;
+}
+
+} // namespace
+
+#pragma mark -
+#pragma mark BreakpadController Implementation
+
+@implementation BreakpadController
+
++ (BreakpadController*)sharedInstance {
+ @synchronized(self) {
+ static BreakpadController* sharedInstance_ =
+ [[BreakpadController alloc] initSingleton];
+ return sharedInstance_;
+ }
+}
+
+- (id)init {
+ return nil;
+}
+
+- (id)initSingleton {
+ self = [super init];
+ if (self) {
+ queue_ = dispatch_queue_create("com.google.BreakpadQueue", NULL);
+ enableUploads_ = NO;
+ started_ = NO;
+ [self resetConfiguration];
+ }
+ return self;
+}
+
+// Since this class is a singleton, this method is not expected to be called.
+- (void)dealloc {
+ assert(!breakpadRef_);
+ dispatch_release(queue_);
+ [configuration_ release];
+ [uploadTimeParameters_ release];
+ [super dealloc];
+}
+
+#pragma mark -
+
+- (void)start:(BOOL)onCurrentThread {
+ if (started_)
+ return;
+ started_ = YES;
+ void(^startBlock)() = ^{
+ assert(!breakpadRef_);
+ breakpadRef_ = BreakpadCreate(configuration_);
+ if (breakpadRef_) {
+ BreakpadAddUploadParameter(breakpadRef_, @"platform", GetPlatform());
+ }
+ };
+ if (onCurrentThread)
+ startBlock();
+ else
+ dispatch_async(queue_, startBlock);
+}
+
+- (void)stop {
+ if (!started_)
+ return;
+ started_ = NO;
+ dispatch_sync(queue_, ^{
+ if (breakpadRef_) {
+ BreakpadRelease(breakpadRef_);
+ breakpadRef_ = NULL;
+ }
+ });
+}
+
+// This method must be called from the breakpad queue.
+- (void)threadUnsafeSendReportWithConfiguration:(NSDictionary*)configuration
+ withBreakpadRef:(BreakpadRef)ref {
+ NSAssert(started_, @"The controller must be started before "
+ "threadUnsafeSendReportWithConfiguration is called");
+ if (breakpadRef_) {
+ BreakpadUploadReportWithParametersAndConfiguration(breakpadRef_,
+ uploadTimeParameters_,
+ configuration);
+ }
+}
+
+- (void)setUploadingEnabled:(BOOL)enabled {
+ NSAssert(started_,
+ @"The controller must be started before setUploadingEnabled is called");
+ dispatch_async(queue_, ^{
+ if (enabled == enableUploads_)
+ return;
+ if (enabled) {
+ // Set this before calling doSendStoredCrashReport, because that
+ // calls sendDelay, which in turn checks this flag.
+ enableUploads_ = YES;
+ [self sendStoredCrashReports];
+ } else {
+ enableUploads_ = NO;
+ [NSObject cancelPreviousPerformRequestsWithTarget:self
+ selector:@selector(sendStoredCrashReports)
+ object:nil];
+ }
+ });
+}
+
+- (void)updateConfiguration:(NSDictionary*)configuration {
+ NSAssert(!started_,
+ @"The controller must not be started when updateConfiguration is called");
+ [configuration_ addEntriesFromDictionary:configuration];
+ NSString* uploadInterval =
+ [configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL];
+ if (uploadInterval)
+ [self setUploadInterval:[uploadInterval intValue]];
+}
+
+- (void)resetConfiguration {
+ NSAssert(!started_,
+ @"The controller must not be started when resetConfiguration is called");
+ [configuration_ autorelease];
+ configuration_ = [[[NSBundle mainBundle] infoDictionary] mutableCopy];
+ NSString* uploadInterval =
+ [configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL];
+ [self setUploadInterval:[uploadInterval intValue]];
+ [self setParametersToAddAtUploadTime:nil];
+}
+
+- (void)setUploadingURL:(NSString*)url {
+ NSAssert(!started_,
+ @"The controller must not be started when setUploadingURL is called");
+ [configuration_ setValue:url forKey:@BREAKPAD_URL];
+}
+
+- (void)setUploadInterval:(int)intervalInSeconds {
+ NSAssert(!started_,
+ @"The controller must not be started when setUploadInterval is called");
+ [configuration_ removeObjectForKey:@BREAKPAD_REPORT_INTERVAL];
+ uploadIntervalInSeconds_ = intervalInSeconds;
+ if (uploadIntervalInSeconds_ < 0)
+ uploadIntervalInSeconds_ = 0;
+}
+
+- (void)setParametersToAddAtUploadTime:(NSDictionary*)uploadTimeParameters {
+ NSAssert(!started_, @"The controller must not be started when "
+ "setParametersToAddAtUploadTime is called");
+ [uploadTimeParameters_ autorelease];
+ uploadTimeParameters_ = [uploadTimeParameters copy];
+}
+
+- (void)addUploadParameter:(NSString*)value forKey:(NSString*)key {
+ NSAssert(started_,
+ @"The controller must be started before addUploadParameter is called");
+ dispatch_async(queue_, ^{
+ if (breakpadRef_)
+ BreakpadAddUploadParameter(breakpadRef_, key, value);
+ });
+}
+
+- (void)removeUploadParameterForKey:(NSString*)key {
+ NSAssert(started_, @"The controller must be started before "
+ "removeUploadParameterForKey is called");
+ dispatch_async(queue_, ^{
+ if (breakpadRef_)
+ BreakpadRemoveUploadParameter(breakpadRef_, key);
+ });
+}
+
+- (void)withBreakpadRef:(void(^)(BreakpadRef))callback {
+ NSAssert(started_,
+ @"The controller must be started before withBreakpadRef is called");
+ dispatch_async(queue_, ^{
+ callback(breakpadRef_);
+ });
+}
+
+- (void)hasReportToUpload:(void(^)(BOOL))callback {
+ NSAssert(started_, @"The controller must be started before "
+ "hasReportToUpload is called");
+ dispatch_async(queue_, ^{
+ callback(breakpadRef_ && (BreakpadGetCrashReportCount(breakpadRef_) > 0));
+ });
+}
+
+- (void)getCrashReportCount:(void(^)(int))callback {
+ NSAssert(started_, @"The controller must be started before "
+ "getCrashReportCount is called");
+ dispatch_async(queue_, ^{
+ callback(breakpadRef_ ? BreakpadGetCrashReportCount(breakpadRef_) : 0);
+ });
+}
+
+- (void)getNextReportConfigurationOrSendDelay:
+ (void(^)(NSDictionary*, int))callback {
+ NSAssert(started_, @"The controller must be started before "
+ "getNextReportConfigurationOrSendDelay is called");
+ dispatch_async(queue_, ^{
+ if (!breakpadRef_) {
+ callback(nil, -1);
+ return;
+ }
+ int delay = [self sendDelay];
+ if (delay != 0) {
+ callback(nil, delay);
+ return;
+ }
+ [self reportWillBeSent];
+ callback(BreakpadGetNextReportConfiguration(breakpadRef_), 0);
+ });
+}
+
+#pragma mark -
+
+- (int)sendDelay {
+ if (!breakpadRef_ || uploadIntervalInSeconds_ <= 0 || !enableUploads_)
+ return -1;
+
+ // To prevent overloading the crash server, crashes are not sent than one
+ // report every |uploadIntervalInSeconds_|. A value in the user defaults is
+ // used to keep the time of the last upload.
+ NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
+ NSNumber *lastTimeNum = [userDefaults objectForKey:kLastSubmission];
+ NSTimeInterval lastTime = lastTimeNum ? [lastTimeNum floatValue] : 0;
+ NSTimeInterval spanSeconds = CFAbsoluteTimeGetCurrent() - lastTime;
+
+ if (spanSeconds >= uploadIntervalInSeconds_)
+ return 0;
+ return uploadIntervalInSeconds_ - static_cast<int>(spanSeconds);
+}
+
+- (void)reportWillBeSent {
+ NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
+ [userDefaults setObject:[NSNumber numberWithDouble:CFAbsoluteTimeGetCurrent()]
+ forKey:kLastSubmission];
+ [userDefaults synchronize];
+}
+
+- (void)sendStoredCrashReports {
+ dispatch_async(queue_, ^{
+ if (BreakpadGetCrashReportCount(breakpadRef_) == 0)
+ return;
+
+ int timeToWait = [self sendDelay];
+
+ // Unable to ever send report.
+ if (timeToWait == -1)
+ return;
+
+ // A report can be sent now.
+ if (timeToWait == 0) {
+ [self reportWillBeSent];
+ BreakpadUploadNextReportWithParameters(breakpadRef_,
+ uploadTimeParameters_);
+
+ // If more reports must be sent, make sure this method is called again.
+ if (BreakpadGetCrashReportCount(breakpadRef_) > 0)
+ timeToWait = uploadIntervalInSeconds_;
+ }
+
+ // A report must be sent later.
+ if (timeToWait > 0) {
+ // performSelector: doesn't work on queue_
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self performSelector:@selector(sendStoredCrashReports)
+ withObject:nil
+ afterDelay:timeToWait];
+ });
+ }
+ });
+}
+
+@end
diff --git a/toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad_Prefix.pch b/toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad_Prefix.pch
new file mode 100644
index 000000000..bfb739423
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/ios/Breakpad_Prefix.pch
@@ -0,0 +1,7 @@
+//
+// Prefix header for all source files of the 'CocoaTouchStaticLibrary' target in the 'CocoaTouchStaticLibrary' project.
+//
+
+#ifdef __OBJC__
+ #import <Foundation/Foundation.h>
+#endif
diff --git a/toolkit/crashreporter/google-breakpad/src/client/ios/handler/ios_exception_minidump_generator.h b/toolkit/crashreporter/google-breakpad/src/client/ios/handler/ios_exception_minidump_generator.h
new file mode 100644
index 000000000..21133e632
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/ios/handler/ios_exception_minidump_generator.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2012, 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.
+
+// ios_exception_minidump_generator.h: Create a fake minidump from a
+// NSException.
+
+#ifndef CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_
+#define CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_
+
+#include <Foundation/Foundation.h>
+
+#include "client/mac/handler/minidump_generator.h"
+
+namespace google_breakpad {
+
+class IosExceptionMinidumpGenerator : public MinidumpGenerator {
+ public:
+ explicit IosExceptionMinidumpGenerator(NSException *exception);
+ virtual ~IosExceptionMinidumpGenerator();
+
+ protected:
+ virtual bool WriteExceptionStream(MDRawDirectory *exception_stream);
+ virtual bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);
+
+ private:
+
+ // Get the crashing program counter from the exception.
+ uintptr_t GetPCFromException();
+
+ // Get the crashing link register from the exception.
+ uintptr_t GetLRFromException();
+
+ // Write a virtual thread context for the crashing site.
+ bool WriteCrashingContext(MDLocationDescriptor *register_location);
+ // Per-CPU implementations of the above method.
+#ifdef HAS_ARM_SUPPORT
+ bool WriteCrashingContextARM(MDLocationDescriptor *register_location);
+#endif
+#ifdef HAS_ARM64_SUPPORT
+ bool WriteCrashingContextARM64(MDLocationDescriptor *register_location);
+#endif
+
+ NSArray *return_addresses_;
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/client/ios/handler/ios_exception_minidump_generator.mm b/toolkit/crashreporter/google-breakpad/src/client/ios/handler/ios_exception_minidump_generator.mm
new file mode 100644
index 000000000..82ea5bb5d
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/client/ios/handler/ios_exception_minidump_generator.mm
@@ -0,0 +1,210 @@
+// Copyright (c) 2012, 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.
+
+#include "client/ios/handler/ios_exception_minidump_generator.h"
+
+#include <pthread.h>
+
+#include "google_breakpad/common/minidump_cpu_arm.h"
+#include "google_breakpad/common/minidump_cpu_arm64.h"
+#include "google_breakpad/common/minidump_exception_mac.h"
+#include "client/minidump_file_writer-inl.h"
+#include "common/scoped_ptr.h"
+
+#if defined(HAS_ARM_SUPPORT) && defined(HAS_ARM64_SUPPORT)
+#error "This file should be compiled for only one architecture at a time"
+#endif
+
+namespace {
+
+const int kExceptionType = EXC_SOFTWARE;
+const int kExceptionCode = MD_EXCEPTION_CODE_MAC_NS_EXCEPTION;
+
+#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
+const uintptr_t kExpectedFinalFp = sizeof(uintptr_t);
+const uintptr_t kExpectedFinalSp = 0;
+
+// Append the given value to the sp position of the stack represented
+// by memory.
+void AppendToMemory(uint8_t *memory, uintptr_t sp, uintptr_t data) {
+ memcpy(memory + sp, &data, sizeof(data));
+}
+#endif
+
+} // namespace
+
+namespace google_breakpad {
+
+IosExceptionMinidumpGenerator::IosExceptionMinidumpGenerator(
+ NSException *exception)
+ : MinidumpGenerator(mach_task_self(), 0) {
+ return_addresses_ = [[exception callStackReturnAddresses] retain];
+ SetExceptionInformation(kExceptionType,
+ kExceptionCode,
+ 0,
+ pthread_mach_thread_np(pthread_self()));
+}
+
+IosExceptionMinidumpGenerator::~IosExceptionMinidumpGenerator() {
+ [return_addresses_ release];
+}
+
+bool IosExceptionMinidumpGenerator::WriteCrashingContext(
+ MDLocationDescriptor *register_location) {
+#ifdef HAS_ARM_SUPPORT
+ return WriteCrashingContextARM(register_location);
+#elif defined(HAS_ARM64_SUPPORT)
+ return WriteCrashingContextARM64(register_location);
+#else
+ assert(false);
+ return false;
+#endif
+}
+
+#ifdef HAS_ARM_SUPPORT
+bool IosExceptionMinidumpGenerator::WriteCrashingContextARM(
+ MDLocationDescriptor *register_location) {
+ TypedMDRVA<MDRawContextARM> context(&writer_);
+ if (!context.Allocate())
+ return false;
+ *register_location = context.location();
+ MDRawContextARM *context_ptr = context.get();
+ memset(context_ptr, 0, sizeof(MDRawContextARM));
+ context_ptr->context_flags = MD_CONTEXT_ARM_FULL;
+ context_ptr->iregs[MD_CONTEXT_ARM_REG_IOS_FP] = kExpectedFinalFp; // FP
+ context_ptr->iregs[MD_CONTEXT_ARM_REG_SP] = kExpectedFinalSp; // SP
+ context_ptr->iregs[MD_CONTEXT_ARM_REG_LR] = GetLRFromException(); // LR
+ context_ptr->iregs[MD_CONTEXT_ARM_REG_PC] = GetPCFromException(); // PC
+ return true;
+}
+#endif
+
+#ifdef HAS_ARM64_SUPPORT
+bool IosExceptionMinidumpGenerator::WriteCrashingContextARM64(
+ MDLocationDescriptor *register_location) {
+ TypedMDRVA<MDRawContextARM64> context(&writer_);
+ if (!context.Allocate())
+ return false;
+ *register_location = context.location();
+ MDRawContextARM64 *context_ptr = context.get();
+ memset(context_ptr, 0, sizeof(*context_ptr));
+ context_ptr->context_flags = MD_CONTEXT_ARM64_FULL;
+ context_ptr->iregs[MD_CONTEXT_ARM64_REG_FP] = kExpectedFinalFp; // FP
+ context_ptr->iregs[MD_CONTEXT_ARM64_REG_SP] = kExpectedFinalSp; // SP
+ context_ptr->iregs[MD_CONTEXT_ARM64_REG_LR] = GetLRFromException(); // LR
+ context_ptr->iregs[MD_CONTEXT_ARM64_REG_PC] = GetPCFromException(); // PC
+ return true;
+}
+#endif
+
+uintptr_t IosExceptionMinidumpGenerator::GetPCFromException() {
+ return [[return_addresses_ objectAtIndex:0] unsignedIntegerValue];
+}
+
+uintptr_t IosExceptionMinidumpGenerator::GetLRFromException() {
+ return [[return_addresses_ objectAtIndex:1] unsignedIntegerValue];
+}
+
+bool IosExceptionMinidumpGenerator::WriteExceptionStream(
+ MDRawDirectory *exception_stream) {
+#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
+ TypedMDRVA<MDRawExceptionStream> exception(&writer_);
+
+ if (!exception.Allocate())
+ return false;
+
+ exception_stream->stream_type = MD_EXCEPTION_STREAM;
+ exception_stream->location = exception.location();
+ MDRawExceptionStream *exception_ptr = exception.get();
+ exception_ptr->thread_id = pthread_mach_thread_np(pthread_self());
+
+ // This naming is confusing, but it is the proper translation from
+ // mach naming to minidump naming.
+ exception_ptr->exception_record.exception_code = kExceptionType;
+ exception_ptr->exception_record.exception_flags = kExceptionCode;
+
+ if (!WriteCrashingContext(&exception_ptr->thread_context))
+ return false;
+
+ exception_ptr->exception_record.exception_address = GetPCFromException();
+ return true;
+#else
+ return MinidumpGenerator::WriteExceptionStream(exception_stream);
+#endif
+}
+
+bool IosExceptionMinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
+ MDRawThread *thread) {
+#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT)
+ if (pthread_mach_thread_np(pthread_self()) != thread_id)
+ return MinidumpGenerator::WriteThreadStream(thread_id, thread);
+
+ size_t frame_count = [return_addresses_ count];
+ if (frame_count == 0)
+ return false;
+ UntypedMDRVA memory(&writer_);
+ size_t pointer_size = sizeof(uintptr_t);
+ size_t frame_record_size = 2 * pointer_size;
+ size_t stack_size = frame_record_size * (frame_count - 1) + pointer_size;
+ if (!memory.Allocate(stack_size))
+ return false;
+ scoped_array<uint8_t> stack_memory(new uint8_t[stack_size]);
+ uintptr_t sp = stack_size - pointer_size;
+ uintptr_t fp = 0;
+ uintptr_t lr = 0;
+ for (size_t current_frame = frame_count - 1;
+ current_frame > 0;
+ --current_frame) {
+ AppendToMemory(stack_memory.get(), sp, lr);
+ sp -= pointer_size;
+ AppendToMemory(stack_memory.get(), sp, fp);
+ fp = sp;
+ sp -= pointer_size;
+ lr = [[return_addresses_ objectAtIndex:current_frame] unsignedIntegerValue];
+ }
+ if (!memory.Copy(stack_memory.get(), stack_size))
+ return false;
+ assert(sp == kExpectedFinalSp);
+ assert(fp == kExpectedFinalFp);
+ assert(lr == GetLRFromException());
+ thread->stack.start_of_memory_range = sp;
+ thread->stack.memory = memory.location();
+ memory_blocks_.push_back(thread->stack);
+
+ if (!WriteCrashingContext(&thread->thread_context))
+ return false;
+
+ thread->thread_id = thread_id;
+ return true;
+#else
+ return MinidumpGenerator::WriteThreadStream(thread_id, thread);
+#endif
+}
+
+} // namespace google_breakpad