aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/ios/Breakpad.mm56
-rw-r--r--src/client/ios/Breakpad.xcodeproj/project.pbxproj19
-rw-r--r--src/client/ios/handler/ios_exception_minidump_generator.h64
-rw-r--r--src/client/ios/handler/ios_exception_minidump_generator.mm164
-rw-r--r--src/client/mac/handler/minidump_generator.h16
5 files changed, 312 insertions, 7 deletions
diff --git a/src/client/ios/Breakpad.mm b/src/client/ios/Breakpad.mm
index 8e29bd53..efcc35b5 100644
--- a/src/client/ios/Breakpad.mm
+++ b/src/client/ios/Breakpad.mm
@@ -45,6 +45,7 @@
#import "client/mac/handler/exception_handler.h"
#import "client/mac/handler/minidump_generator.h"
#import "client/ios/Breakpad.h"
+#import "client/ios/handler/ios_exception_minidump_generator.h"
#import "client/mac/handler/protected_memory_allocator.h"
#import <sys/stat.h>
@@ -174,6 +175,12 @@ class Breakpad {
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)
@@ -181,8 +188,14 @@ class Breakpad {
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
@@ -241,11 +254,20 @@ bool Breakpad::HandleMinidumpCallback(const char *dump_dir,
}
//=============================================================================
+void Breakpad::UncaughtExceptionHandler(NSException *exception) {
+ NSSetUncaughtExceptionHandler(NULL);
+ if (current_breakpad_) {
+ current_breakpad_->HandleUncaughtException(exception);
+ }
+}
+
+//=============================================================================
#pragma mark -
//=============================================================================
bool Breakpad::Initialize(NSDictionary *parameters) {
// Initialize
+ current_breakpad_ = this;
config_params_ = NULL;
handler_ = NULL;
@@ -267,11 +289,14 @@ bool Breakpad::Initialize(NSDictionary *parameters) {
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.
//
@@ -499,6 +524,37 @@ bool Breakpad::HandleMinidump(const char *dump_dir,
}
//=============================================================================
+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 -
diff --git a/src/client/ios/Breakpad.xcodeproj/project.pbxproj b/src/client/ios/Breakpad.xcodeproj/project.pbxproj
index c91de231..5baadf42 100644
--- a/src/client/ios/Breakpad.xcodeproj/project.pbxproj
+++ b/src/client/ios/Breakpad.xcodeproj/project.pbxproj
@@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
+ 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 */; };
@@ -55,6 +57,8 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
+ 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>"; };
@@ -151,6 +155,15 @@
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 = (
@@ -185,6 +198,7 @@
16C7C969147D4A4200776EAD /* ios */ = {
isa = PBXGroup;
children = (
+ 16BFA66A14E195E9009704F8 /* handler */,
16C7C96A147D4A4200776EAD /* Breakpad.h */,
16C7C96B147D4A4200776EAD /* Breakpad.mm */,
);
@@ -312,6 +326,7 @@
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 */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -397,6 +412,7 @@
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 */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -415,7 +431,6 @@
"\"$(SRCROOT)/../mac/build/Debug\"",
);
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -479,7 +494,6 @@
../../common/mac,
);
OTHER_LDFLAGS = "-ObjC";
- PREBINDING = NO;
SDKROOT = iphoneos;
};
name = Debug;
@@ -497,7 +511,6 @@
../../common/mac,
);
OTHER_LDFLAGS = "-ObjC";
- PREBINDING = NO;
SDKROOT = iphoneos;
};
name = Release;
diff --git a/src/client/ios/handler/ios_exception_minidump_generator.h b/src/client/ios/handler/ios_exception_minidump_generator.h
new file mode 100644
index 00000000..59f24819
--- /dev/null
+++ b/src/client/ios/handler/ios_exception_minidump_generator.h
@@ -0,0 +1,64 @@
+// 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.
+ uint32_t GetPCFromException();
+
+ // Write a virtual thread context for the crashing site.
+ bool WriteCrashingContext(MDLocationDescriptor *register_location);
+
+ NSArray *return_addresses_;
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_
diff --git a/src/client/ios/handler/ios_exception_minidump_generator.mm b/src/client/ios/handler/ios_exception_minidump_generator.mm
new file mode 100644
index 00000000..6b8b1064
--- /dev/null
+++ b/src/client/ios/handler/ios_exception_minidump_generator.mm
@@ -0,0 +1,164 @@
+// 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 "processor/scoped_ptr.h"
+
+namespace {
+
+const uint32_t kExpectedFinalFp = 4;
+const uint32_t kExpectedFinalSp = 0;
+const int kExceptionType = EXC_SOFTWARE;
+const int kExceptionCode = 0xDEADBEEF;
+
+// Append the given 4 bytes value to the sp position of the stack represented
+// by memory.
+void AppendToMemory(uint8_t *memory, uint32_t sp, uint32_t data) {
+ assert(sizeof(data) == 4);
+ memcpy(memory + sp, &data, sizeof(data));
+}
+
+} // 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
+ 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[7] = kExpectedFinalFp; // FP
+ context_ptr->iregs[13] = kExpectedFinalSp; // SP
+ uint32_t pc = GetPCFromException();
+ context_ptr->iregs[14] = pc; // LR
+ context_ptr->iregs[15] = pc; // PC
+ return true;
+#else
+ assert(false);
+ return false;
+#endif
+}
+
+uint32_t IosExceptionMinidumpGenerator::GetPCFromException() {
+ return [[return_addresses_ objectAtIndex:0] unsignedIntegerValue];
+}
+
+bool IosExceptionMinidumpGenerator::WriteExceptionStream(
+ MDRawDirectory *exception_stream) {
+#ifdef HAS_ARM_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) {
+#ifdef HAS_ARM_SUPPORT
+ if (pthread_mach_thread_np(pthread_self()) != thread_id)
+ return MinidumpGenerator::WriteThreadStream(thread_id, thread);
+
+ size_t frame_count = [return_addresses_ count];
+ UntypedMDRVA memory(&writer_);
+ size_t size = 8 * (frame_count - 1) + 4;
+ if (!memory.Allocate(size))
+ return false;
+ scoped_array<uint8_t> stack_memory(new uint8_t[size]);
+ uint32_t sp = size - 4;
+ uint32_t fp = 0;
+ uint32_t lr = [[return_addresses_ lastObject] unsignedIntegerValue];
+ for (int current_frame = frame_count - 2;
+ current_frame >= 0;
+ --current_frame) {
+ AppendToMemory(stack_memory.get(), sp, fp);
+ sp -= 4;
+ fp = sp;
+ AppendToMemory(stack_memory.get(), sp, lr);
+ sp -= 4;
+ lr = [[return_addresses_ objectAtIndex:current_frame] unsignedIntegerValue];
+ }
+ if (!memory.Copy(stack_memory.get(), size))
+ return false;
+ assert(sp == kExpectedFinalSp);
+ assert(fp == kExpectedFinalFp);
+ assert(lr == GetPCFromException());
+ 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
diff --git a/src/client/mac/handler/minidump_generator.h b/src/client/mac/handler/minidump_generator.h
index f5a4d44a..80bb116c 100644
--- a/src/client/mac/handler/minidump_generator.h
+++ b/src/client/mac/handler/minidump_generator.h
@@ -82,7 +82,7 @@ class MinidumpGenerator {
MinidumpGenerator();
MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread);
- ~MinidumpGenerator();
+ virtual ~MinidumpGenerator();
// Return <dir>/<unique_name>.dmp
// Sets |unique_name| (if requested) to the unique name for the minidump
@@ -106,13 +106,19 @@ class MinidumpGenerator {
// the MinidumpGenerator class.
static void GatherSystemInformation();
+ protected:
+ // Overridable Stream writers
+ virtual bool WriteExceptionStream(MDRawDirectory *exception_stream);
+
+ // Overridable Helper
+ virtual bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);
+
private:
- typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *);
+ typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *);
// Stream writers
bool WriteThreadListStream(MDRawDirectory *thread_list_stream);
bool WriteMemoryListStream(MDRawDirectory *memory_list_stream);
- bool WriteExceptionStream(MDRawDirectory *exception_stream);
bool WriteSystemInfoStream(MDRawDirectory *system_info_stream);
bool WriteModuleListStream(MDRawDirectory *module_list_stream);
bool WriteMiscInfoStream(MDRawDirectory *misc_info_stream);
@@ -128,7 +134,6 @@ class MinidumpGenerator {
MDMemoryDescriptor *stack_location);
bool WriteContext(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
- bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);
bool WriteCVRecord(MDRawModule *module, int cpu_type,
const char *module_path, bool in_memory);
bool WriteModuleStream(unsigned int index, MDRawModule *module);
@@ -172,9 +177,11 @@ class MinidumpGenerator {
explicit MinidumpGenerator(const MinidumpGenerator &);
void operator=(const MinidumpGenerator &);
+ protected:
// Use this writer to put the data to disk
MinidumpFileWriter writer_;
+ private:
// Exception information
int exception_type_;
int exception_code_;
@@ -199,6 +206,7 @@ class MinidumpGenerator {
// directly from the system, even while handling an exception.
mutable PageAllocator allocator_;
+ protected:
// Blocks of memory written to the dump. These are all currently
// written while writing the thread list stream, but saved here
// so a memory list stream can be written afterwards.