Open sourcing the Breakpad framework from Google.

A=many, many people
R=nealsid, jeremy moskovich(from Chromium project)



git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@322 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
nealsid
2009-04-01 03:18:49 +00:00
parent 0abe34ce5d
commit 3ebdb1bd7a
49 changed files with 12695 additions and 22 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,181 @@
// Copyright (c) 2006, 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. Prompt the user (using CFUserNotification)
// 3. Invoke a command line reporting tool to send the minidump to a
// server
//
// By specifying parameters to the BreakpadCreate function, you can
// modify the default behavior to suit your needs and wants and
// desires.
typedef void *BreakpadRef;
#ifdef __cplusplus
extern "C" {
#endif
#include <CoreFoundation/CoreFoundation.h>
#include <Foundation/Foundation.h>
// Keys for configuration file
#define kReporterMinidumpDirectoryKey "MinidumpDir"
#define kReporterMinidumpIDKey "MinidumpID"
// Specify some special keys to be used in the configuration file that is
// generated by Breakpad and consumed by the crash_sender.
#define BREAKPAD_PRODUCT_DISPLAY "BreakpadProductDisplay"
#define BREAKPAD_PRODUCT "BreakpadProduct"
#define BREAKPAD_VENDOR "BreakpadVendor"
#define BREAKPAD_VERSION "BreakpadVersion"
#define BREAKPAD_URL "BreakpadURL"
#define BREAKPAD_REPORT_INTERVAL "BreakpadReportInterval"
#define BREAKPAD_SKIP_CONFIRM "BreakpadSkipConfirm"
#define BREAKPAD_SEND_AND_EXIT "BreakpadSendAndExit"
#define BREAKPAD_INSPECTOR_LOCATION "BreakpadInspectorLocation"
#define BREAKPAD_REPORTER_EXE_LOCATION \
"BreakpadReporterExeLocation"
#define BREAKPAD_LOGFILES "BreakpadLogFiles"
#define BREAKPAD_LOGFILE_UPLOAD_SIZE "BreakpadLogFileTailSize"
#define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile"
#define BREAKPAD_EMAIL "BreakpadEmail"
#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments"
#define BREAKPAD_COMMENTS "BreakpadComments"
// Optional user-defined function to dec 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);
// 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
// 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 to
// BREAKPAD_PRODUCT if not specified.
// BREAKPAD_VERSION Product version (e.g., 1.2.3), used
// as metadata for crash report
// BREAKPAD_VENDOR Vendor named, used in UI (e.g. the Xxxx
// foo bar company product widget has crashed)
// BREAKPAD_URL URL destination for reporting
// BREAKPAD_REPORT_INTERVAL # of seconds between sending
// reports. If an additional report is
// generated within this time, it will
// be ignored. Default: 3600sec.
// Specify 0 to send all reports.
// BREAKPAD_SKIP_CONFIRM If true, the reporter will send the report
// without any user intervention.
// BREAKPAD_SEND_AND_EXIT If true, the handler will exit after sending.
// This will prevent any other handler (e.g.,
// CrashReporter) from getting the crash.
// BREAKPAD_INSPECTOR_LOCATION The full path to the Inspector executable.
// BREAKPAD_REPORTER_EXE_LOCATION The full path to the Reporter/sender
// executable.
// BREAKPAD_LOGFILES Indicates an array of log file paths that
// should be uploaded at crash time
// BREAKPAD_REQUEST_COMMENTS If true, the message dialog will have a
// text box for the user to enter comments as
// well as a name and email address.
// BREAKPAD_COMMENTS The text the user provided as comments.
//
// The BREAKPAD_PRODUCT and BREAKPAD_VERSION 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. 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 BREAKPAD_SKIP_CONFIRM and BREAKPAD_SEND_AND_EXIT default
// values are NO and YES. However, they can be controlled by setting their
// values in a user or global plist.
//
// It's easiest to use Breakpad via the Framework, but if you're compiling the
// code in directly, BREAKPAD_INSPECTOR_LOCATION and
// BREAKPAD_REPORTER_EXE_LOCATION allow you to specify custom paths
// to the helper executables.
//
// 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);
// Clients may set an optional callback which gets called when a crash occurs.
// The callback function should return |true| if we should handle the crash,
// generate a crash report, etc. or |false| if we should ignore it and forward
// the crash (normally to CrashReporter)
void BreakpadSetFilterCallback(BreakpadRef ref,
BreakpadFilterCallback callback);
// User defined key and value string storage
// All set keys will be uploaded with the minidump if a crash occurs
// Keys and Values are limited to 255 bytes (256 - 1 for terminator).
// NB this is BYTES not GLYPHS.
// 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 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.
void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value);
NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key);
void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key);
// Add a log file for Breakpad to read and send upon crash dump
void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname);
// Generate a minidump and send
void BreakpadGenerateAndSendReport(BreakpadRef ref);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,915 @@
// Copyright (c) 2006, 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 VERBOSE 0
#if VERBOSE
static bool gDebugLog = true;
#else
static bool gDebugLog = false;
#endif
#define DEBUGLOG if (gDebugLog) fprintf
#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
#import "client/mac/Framework/Breakpad.h"
#import "client/mac/crash_generation/Inspector.h"
#import "client/mac/Framework/OnDemandServer.h"
#import "client/mac/handler/protected_memory_allocator.h"
#import "common/mac/MachIPC.h"
#import "common/mac/SimpleStringDictionary.h"
#import <sys/stat.h>
#import <sys/sysctl.h>
#import <Foundation/Foundation.h>
#import "exception_handler.h"
#import "string_utilities.h"
using google_breakpad::KeyValueEntry;
using google_breakpad::SimpleStringDictionary;
using google_breakpad::SimpleStringDictionaryIterator;
//=============================================================================
// 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:
// allocator may be NULL, in which case no Protect() or Unprotect() calls
// will be made, but a lock will still be taken
ProtectedMemoryLocker(pthread_mutex_t *mutex,
ProtectedMemoryAllocator *allocator)
: mutex_(mutex), allocator_(allocator) {
// Lock the mutex
assert(pthread_mutex_lock(mutex_) == 0);
// Unprotect the memory
if (allocator_ ) {
allocator_->Unprotect();
}
}
~ProtectedMemoryLocker() {
// First protect the memory
if (allocator_) {
allocator_->Protect();
}
// Then unlock the mutex
assert(pthread_mutex_unlock(mutex_) == 0);
};
private:
// Keep anybody from ever creating one of these things not on the stack.
ProtectedMemoryLocker() { }
ProtectedMemoryLocker(const ProtectedMemoryLocker&);
ProtectedMemoryLocker & operator=(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);
void GenerateAndSendReport();
void SetFilterCallback(BreakpadFilterCallback callback) {
filter_callback_ = callback;
}
private:
Breakpad()
: handler_(NULL),
config_params_(NULL),
send_and_exit_(true),
filter_callback_(NULL) {
inspector_path_[0] = 0;
}
bool Initialize(NSDictionary *parameters);
bool ExtractParameters(NSDictionary *parameters);
// Dispatches to HandleException()
static bool ExceptionHandlerDirectCallback(void *context,
int exception_type,
int exception_code,
mach_port_t crashing_thread);
bool HandleException(int exception_type,
int exception_code,
mach_port_t crashing_thread);
// 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)
char inspector_path_[PATH_MAX]; // Path to inspector tool
SimpleStringDictionary *config_params_; // Create parameters (STRONG)
OnDemandServer inspector_;
bool send_and_exit_; // Exit after sending, if true
BreakpadFilterCallback filter_callback_;
unsigned int logFileCounter;
};
#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::ExceptionHandlerDirectCallback(void *context,
int exception_type,
int exception_code,
mach_port_t crashing_thread) {
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)
return false;
return breakpad->HandleException( exception_type,
exception_code,
crashing_thread);
}
//=============================================================================
#pragma mark -
#include <mach-o/dyld.h>
//=============================================================================
// Returns the pathname to the Resources directory for this version of
// Breakpad which we are now running.
//
// Don't make the function static, since _dyld_lookup_and_bind_fully needs a
// simple non-static C name
//
extern "C" {
NSString * GetResourcePath();
NSString * GetResourcePath() {
NSString *resourcePath = nil;
// If there are multiple breakpads installed then calling bundleWithIdentifier
// will not work properly, so only use that as a backup plan.
// We want to find the bundle containing the code where this function lives
// and work from there
//
// Get the pathname to the code which contains this function
void *address = nil;
NSModule module = nil;
_dyld_lookup_and_bind_fully("_GetResourcePath",
&address,
&module);
if (module && address) {
const char* moduleName = NSNameOfModule(module);
if (moduleName) {
// The "Resources" directory should be in the same directory as the
// executable code, since that's how the Breakpad framework is built.
resourcePath = [NSString stringWithUTF8String:moduleName];
resourcePath = [resourcePath stringByDeletingLastPathComponent];
resourcePath = [resourcePath stringByAppendingPathComponent:@"Resources/"];
} else {
DEBUGLOG(stderr, "Missing moduleName\n");
}
} else {
DEBUGLOG(stderr, "Could not find GetResourcePath\n");
// fallback plan
NSBundle *bundle =
[NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"];
resourcePath = [bundle resourcePath];
}
return resourcePath;
}
} // extern "C"
//=============================================================================
bool Breakpad::Initialize(NSDictionary *parameters) {
// Initialize
config_params_ = NULL;
handler_ = NULL;
// Check for debugger
if (IsDebuggerActive()) {
DEBUGLOG(stderr, "Debugger is active: Not installing handler\n");
return true;
}
// Gather any user specified parameters
if (!ExtractParameters(parameters)) {
return false;
}
// Get path to Inspector executable.
NSString *inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION);
// Standardize path (resolve symlinkes, etc.) and escape spaces
inspectorPathString = [inspectorPathString stringByStandardizingPath];
inspectorPathString = [[inspectorPathString componentsSeparatedByString:@" "]
componentsJoinedByString:@"\\ "];
// Create an on-demand server object representing the Inspector.
// In case of a crash, we simply need to call the LaunchOnDemand()
// method on it, then send a mach message to its service port.
// It will then launch and perform a process inspection of our crashed state.
// See the HandleException() method for the details.
#define RECEIVE_PORT_NAME "com.Breakpad.Inspector"
name_t portName;
snprintf(portName, sizeof(name_t), "%s%d", RECEIVE_PORT_NAME, getpid());
// Save the location of the Inspector
strlcpy(inspector_path_, [inspectorPathString fileSystemRepresentation],
sizeof(inspector_path_));
// Append a single command-line argument to the Inspector path
// representing the bootstrap name of the launch-on-demand receive port.
// When the Inspector is launched, it can use this to lookup the port
// by calling bootstrap_check_in().
strlcat(inspector_path_, " ", sizeof(inspector_path_));
strlcat(inspector_path_, portName, sizeof(inspector_path_));
kern_return_t kr = inspector_.Initialize(inspector_path_,
portName,
true); // shutdown on exit
if (kr != KERN_SUCCESS) {
return false;
}
// Create the handler (allocating it in our special protected pool)
handler_ =
new (gBreakpadAllocator->Allocate(sizeof(google_breakpad::ExceptionHandler)))
google_breakpad::ExceptionHandler(
Breakpad::ExceptionHandlerDirectCallback, this, true);
return true;
}
//=============================================================================
Breakpad::~Breakpad() {
// 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) {
NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
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 *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL];
NSString *inspectorPathString =
[parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION];
NSString *reporterPathString =
[parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION];
NSString *skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM];
NSString *sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT];
NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES];
NSString *logFileTailSize = [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE];
NSString *reportEmail = [parameters objectForKey:@BREAKPAD_EMAIL];
NSString *requestUserText =
[parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS];
NSString *vendor =
[parameters objectForKey:@BREAKPAD_VENDOR];
// If these two are not already set(skipConfirm and sendAndExit can
// come from user defaults and take priority)
if (!skipConfirm) {
skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM];
}
if (!sendAndExit) {
sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT];
}
if (!product)
product = [parameters objectForKey:@"CFBundleName"];
if (!display)
display = product;
if (!version)
version = [parameters objectForKey:@"CFBundleVersion"];
if (!interval)
interval = @"3600";
if (!logFileTailSize)
logFileTailSize = @"200000";
if (!vendor) {
vendor = @"Vendor not specified";
}
// Normalize the values
if (skipConfirm) {
skipConfirm = [skipConfirm uppercaseString];
if ([skipConfirm isEqualToString:@"YES"] ||
[skipConfirm isEqualToString:@"TRUE"] ||
[skipConfirm isEqualToString:@"1"])
skipConfirm = @"YES";
else
skipConfirm = @"NO";
} else {
skipConfirm = @"NO";
}
send_and_exit_ = true;
if (sendAndExit) {
sendAndExit = [sendAndExit uppercaseString];
if ([sendAndExit isEqualToString:@"NO"] ||
[sendAndExit isEqualToString:@"FALSE"] ||
[sendAndExit isEqualToString:@"0"])
send_and_exit_ = false;
}
if (requestUserText) {
requestUserText = [requestUserText uppercaseString];
if ([requestUserText isEqualToString:@"YES"] ||
[requestUserText isEqualToString:@"TRUE"] ||
[requestUserText isEqualToString:@"1"])
requestUserText = @"YES";
else
requestUserText = @"NO";
} else {
requestUserText = @"NO";
}
// Find the helper applications if not specified in user config.
NSString *resourcePath = nil;
if (!inspectorPathString || !reporterPathString) {
resourcePath = GetResourcePath();
if (!resourcePath) {
DEBUGLOG(stderr, "Could not get resource path\n");
return false;
}
}
// Find Inspector.
if (!inspectorPathString) {
inspectorPathString =
[resourcePath stringByAppendingPathComponent:@"Inspector"];
}
// Verify that there is an Inspector tool
if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) {
DEBUGLOG(stderr, "Cannot find Inspector tool\n");
return false;
}
// Find Reporter.
if (!reporterPathString) {
reporterPathString =
[resourcePath stringByAppendingPathComponent:@"crash_report_sender.app"];
reporterPathString = [[NSBundle bundleWithPath:reporterPathString] executablePath];
}
// Verify that there is a Reporter application
if (![[NSFileManager defaultManager]
fileExistsAtPath:reporterPathString]) {
DEBUGLOG(stderr, "Cannot find Reporter tool\n");
return false;
}
// The product and version are required values
if (![product length] || ![version length]) {
DEBUGLOG(stderr, "Missing required product and/or version keys\n");
return false;
}
config_params_ =
new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
SimpleStringDictionary();
SimpleStringDictionary &dictionary = *config_params_;
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_REPORT_INTERVAL, [interval UTF8String]);
dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM, [skipConfirm UTF8String]);
dictionary.SetKeyValue(BREAKPAD_INSPECTOR_LOCATION,
[inspectorPathString fileSystemRepresentation]);
dictionary.SetKeyValue(BREAKPAD_REPORTER_EXE_LOCATION,
[reporterPathString fileSystemRepresentation]);
dictionary.SetKeyValue(BREAKPAD_LOGFILE_UPLOAD_SIZE,
[logFileTailSize UTF8String]);
dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS,
[requestUserText UTF8String]);
dictionary.SetKeyValue(BREAKPAD_VENDOR,
[vendor UTF8String]);
if (logFilePaths) {
char logFileKey[255];
for(unsigned int i = 0; i < [logFilePaths count]; i++) {
sprintf(logFileKey,"%s%d", BREAKPAD_LOGFILE_KEY_PREFIX, i);
dictionary.SetKeyValue(logFileKey, [[logFilePaths objectAtIndex:i] fileSystemRepresentation]);
}
}
if (reportEmail) {
dictionary.SetKeyValue(BREAKPAD_EMAIL,
[reportEmail UTF8String]);
}
#if 0 // for testing
BreakpadSetKeyValue(this, @"UserKey1", @"User Value 1");
BreakpadSetKeyValue(this, @"UserKey2", @"User Value 2");
BreakpadSetKeyValue(this, @"UserKey3", @"User Value 3");
BreakpadSetKeyValue(this, @"UserKey4", @"User Value 4");
#endif
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]);
}
//=============================================================================
void Breakpad::GenerateAndSendReport() {
HandleException(0, 0, 0);
}
//=============================================================================
bool Breakpad::HandleException(int exception_type,
int exception_code,
mach_port_t crashing_thread) {
DEBUGLOG(stderr, "Breakpad: an exception occurred\n");
if (filter_callback_) {
bool should_handle = filter_callback_(exception_type,
exception_code,
crashing_thread);
if (!should_handle) return false;
}
// We need to reset the memory protections to be read/write,
// since LaunchOnDemand() requires changing state.
gBreakpadAllocator->Unprotect();
// Configure the server to launch when we message the service port.
// The reason we do this here, rather than at startup, is that we
// can leak a bootstrap service entry if this method is called and
// there never ends up being a crash.
inspector_.LaunchOnDemand();
gBreakpadAllocator->Protect();
// The Inspector should send a message to this port to verify it
// received our information and has finished the inspection.
ReceivePort acknowledge_port;
// Send initial information to the Inspector.
MachSendMessage message(kMsgType_InspectorInitialInfo);
message.AddDescriptor(mach_task_self()); // our task
message.AddDescriptor(crashing_thread); // crashing thread
message.AddDescriptor(mach_thread_self()); // exception-handling thread
message.AddDescriptor(acknowledge_port.GetPort());// message receive port
InspectorInfo info;
info.exception_type = exception_type;
info.exception_code = exception_code;
info.parameter_count = config_params_->GetCount();
message.SetData(&info, sizeof(info));
MachPortSender sender(inspector_.GetServicePort());
kern_return_t result = sender.SendMessage(message, 2000);
if (result == KERN_SUCCESS) {
// Now, send a series of key-value pairs to the Inspector.
const KeyValueEntry *entry = NULL;
SimpleStringDictionaryIterator iter(*config_params_);
while ( (entry = iter.Next()) ) {
KeyValueMessageData keyvalue_data(*entry);
MachSendMessage keyvalue_message(kMsgType_InspectorKeyValuePair);
keyvalue_message.SetData(&keyvalue_data, sizeof(keyvalue_data));
result = sender.SendMessage(keyvalue_message, 2000);
if (result != KERN_SUCCESS) {
break;
}
}
if (result == KERN_SUCCESS) {
// Wait for acknowledgement that the inspection has finished.
MachReceiveMessage acknowledge_messsage;
result = acknowledge_port.WaitForMessage(&acknowledge_messsage, 2000);
}
}
#if VERBOSE
PRINT_MACH_RESULT(result, "Breakpad: SendMessage ");
printf("Breakpad: Inspector service port = %#x\n",
inspector_.GetServicePort());
#endif
// If we don't want any forwarding, return true here to indicate that we've
// processed things as much as we want.
if (send_and_exit_)
return true;
return false;
}
//=============================================================================
//=============================================================================
#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) {
throw mutexResult; // caught down below
}
// 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();
} else {
[pool release];
#ifdef __EXCEPTIONS
throw(-1);
#else
return NULL;
#endif
}
// 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;
} catch(...) { // don't let exception leave this C API
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 exception 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 exception leave this C API
fprintf(stderr, "BreakpadSetKeyValue() : 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 exception 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 exception leave this C API
fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
}
}
//=============================================================================
void BreakpadGenerateAndSendReport(BreakpadRef ref) {
try {
Breakpad *breakpad = (Breakpad *)ref;
if (breakpad && gKeyValueAllocator) {
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
gBreakpadAllocator->Unprotect();
breakpad->GenerateAndSendReport();
gBreakpadAllocator->Protect();
}
} catch(...) { // don't let exception leave this C API
fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n");
}
}
//=============================================================================
void BreakpadSetFilterCallback(BreakpadRef ref,
BreakpadFilterCallback callback) {
try {
Breakpad *breakpad = (Breakpad *)ref;
if (breakpad && gBreakpadAllocator) {
// share the dictionary mutex here (we really don't need a mutex)
ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator);
breakpad->SetFilterCallback(callback);
}
} catch(...) { // don't let exception leave this C API
fprintf(stderr, "BreakpadSetFilterCallback() : error\n");
}
}
//============================================================================
void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname) {
int logFileCounter = 0;
NSString *logFileKey = [NSString stringWithFormat:@"%@%d",
@BREAKPAD_LOGFILE_KEY_PREFIX,
logFileCounter];
NSString *existingLogFilename = nil;
existingLogFilename = BreakpadKeyValue(ref, logFileKey);
// Find the first log file key that we can use by testing for existence
while (existingLogFilename) {
if ([existingLogFilename isEqualToString:logPathname]) {
return;
}
logFileCounter++;
logFileKey = [NSString stringWithFormat:@"%@%d",
@BREAKPAD_LOGFILE_KEY_PREFIX,
logFileCounter];
existingLogFilename = BreakpadKeyValue(ref, logFileKey);
}
BreakpadSetKeyValue(ref, logFileKey, logPathname);
}

View File

@@ -0,0 +1,8 @@
//
// Prefix header for all source files of the 'Breakpad' target in the
// 'Breakpad' project.
//
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#endif

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.yourcompany.${PRODUCT_NAME:identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@@ -0,0 +1,146 @@
// Copyright (c) 2007, 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 <iostream>
#import <mach/mach.h>
#import <servers/bootstrap.h>
#import <stdio.h>
#import <stdlib.h>
#import <sys/stat.h>
#import <unistd.h>
//==============================================================================
// class OnDemandServer :
// A basic on-demand server launcher supporting a single named service port
//
// Example Usage :
//
// kern_return_t result;
// OnDemandServer *server = OnDemandServer::Create("/tmp/myserver",
// "com.MyCompany.MyServiceName",
// true,
// &result);
//
// if (server) {
// server->LaunchOnDemand();
// mach_port_t service_port = GetServicePort();
//
// // Send a mach message to service_port and "myserver" will be launched
// }
//
//
// ---- Now in the server code ----
//
// // "myserver" should get the service port and read the message which
// // launched it:
// mach_port_t service_rcv_port_;
// kern_return_t kr = bootstrap_check_in(bootstrap_port,
// "com.MyCompany.MyServiceName",
// &service_rcv_port_);
// // mach_msg() read service_rcv_port_ ....
//
// ....
//
// // Later "myserver" may want to unregister the service if it doesn't
// // want its bootstrap service to stick around after it exits.
//
// // DO NOT use mach_port_deallocate() here -- it will fail and the
// // following bootstrap_register() will also fail leaving our service
// // name hanging around forever (until reboot)
// kern_return_t kr = mach_port_destroy(mach_task_self(), service_rcv_port_);
//
// kr = bootstrap_register(bootstrap_port,
// "com.MyCompany.MyServiceName",
// MACH_PORT_NULL);
class OnDemandServer {
public:
// must call Initialize() to be useful
OnDemandServer()
: server_port_(MACH_PORT_NULL),
service_port_(MACH_PORT_NULL),
unregister_on_cleanup_(true) {
}
// Creates the bootstrap server and service
kern_return_t Initialize(const char *server_command,
const char *service_name,
bool unregister_on_cleanup);
// Returns an OnDemandServer object if successful, or NULL if there's
// an error. The error result will be returned in out_result.
//
// server_command : the full path name including optional command-line
// arguments to the executable representing the server
//
// service_name : represents service name
// something like "com.company.ServiceName"
//
// unregister_on_cleanup : if true, unregisters the service name
// when the OnDemandServer is deleted -- unregistering will
// ONLY be possible if LaunchOnDemand() has NOT been called.
// If false, then the service will continue to be registered
// even after the current process quits.
//
// out_result : if non-NULL, returns the result
// this value will be KERN_SUCCESS if Create() returns non-NULL
//
static OnDemandServer *Create(const char *server_command,
const char *service_name,
bool unregister_on_cleanup,
kern_return_t *out_result);
// Cleans up and if LaunchOnDemand() has not yet been called then
// the bootstrap service will be unregistered.
~OnDemandServer();
// This must be called if we intend to commit to launching the server
// by sending a mach message to our service port. Do not call it otherwise
// or it will be difficult (impossible?) to unregister the service name.
void LaunchOnDemand();
// This is the port we need to send a mach message to after calling
// LaunchOnDemand(). Sending a message causing an immediate launch
// of the server
mach_port_t GetServicePort() { return service_port_; };
private:
// Disallow copy constructor
OnDemandServer(const OnDemandServer&);
// Cleans up and if LaunchOnDemand() has not yet been called then
// the bootstrap service will be unregistered.
void Unregister();
name_t service_name_;
mach_port_t server_port_;
mach_port_t service_port_;
bool unregister_on_cleanup_;
};

View File

@@ -0,0 +1,145 @@
// Copyright (c) 2007, 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 "OnDemandServer.h"
#if DEBUG
#define PRINT_MACH_RESULT(result_, message_) \
printf(message_"%s (%d)\n", mach_error_string(result_), result_ );
#else
#define PRINT_MACH_RESULT(result_, message_)
#endif
//==============================================================================
OnDemandServer *OnDemandServer::Create(const char *server_command,
const char *service_name,
bool unregister_on_cleanup,
kern_return_t *out_result) {
OnDemandServer *server = new OnDemandServer();
if (!server) return NULL;
kern_return_t result = server->Initialize(server_command,
service_name,
unregister_on_cleanup);
if (out_result) {
*out_result = result;
}
if (result == KERN_SUCCESS) {
return server;
}
delete server;
return NULL;
};
//==============================================================================
kern_return_t OnDemandServer::Initialize(const char *server_command,
const char *service_name,
bool unregister_on_cleanup) {
unregister_on_cleanup_ = unregister_on_cleanup;
kern_return_t kr =
bootstrap_create_server(bootstrap_port,
const_cast<char*>(server_command),
geteuid(), // server uid
true,
&server_port_);
if (kr != KERN_SUCCESS) {
PRINT_MACH_RESULT(kr, "bootstrap_create_server() : ");
return kr;
}
strlcpy(service_name_, service_name, sizeof(service_name_));
// Create a service called service_name, and return send rights to
// that port in service_port_.
kr = bootstrap_create_service(server_port_,
const_cast<char*>(service_name),
&service_port_);
if (kr != KERN_SUCCESS) {
//PRINT_MACH_RESULT(kr, "bootstrap_create_service() : ");
// perhaps the service has already been created - try to look it up
kr = bootstrap_look_up(bootstrap_port, (char*)service_name, &service_port_);
if (kr != KERN_SUCCESS) {
PRINT_MACH_RESULT(kr, "bootstrap_look_up() : ");
Unregister(); // clean up server port
return kr;
}
}
return KERN_SUCCESS;
}
//==============================================================================
OnDemandServer::~OnDemandServer() {
if (unregister_on_cleanup_) {
Unregister();
}
}
//==============================================================================
void OnDemandServer::LaunchOnDemand() {
// We need to do this, since the launched server is another process
// and holding on to this port delays launching until the current process
// exits!
mach_port_deallocate(mach_task_self(), server_port_);
server_port_ = NULL;
// Now, the service is still registered and all we need to do is send
// a mach message to the service port in order to launch the server.
}
//==============================================================================
void OnDemandServer::Unregister() {
if (service_port_ != MACH_PORT_NULL) {
mach_port_deallocate(mach_task_self(), service_port_);
service_port_ = MACH_PORT_NULL;
}
if (server_port_ != MACH_PORT_NULL) {
// unregister the service
kern_return_t kr = bootstrap_register(server_port_,
service_name_,
MACH_PORT_NULL);
if (kr != KERN_SUCCESS) {
PRINT_MACH_RESULT(kr, "Breakpad UNREGISTER : bootstrap_register() : ");
}
mach_port_deallocate(mach_task_self(), server_port_);
server_port_ = MACH_PORT_NULL;
}
}

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.yourcompany.${PRODUCT_NAME:identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
</dict>
</plist>

View File

@@ -0,0 +1,200 @@
// Copyright (c) 2007, 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.
//
// Interface file between the Breakpad.framework and
// the Inspector process.
#import "common/mac/SimpleStringDictionary.h"
#import <Foundation/Foundation.h>
#import "client/mac/handler/minidump_generator.h"
#define VERBOSE 0
extern bool gDebugLog;
#define DEBUGLOG if (gDebugLog) fprintf
// Types of mach messsages (message IDs)
enum {
kMsgType_InspectorInitialInfo = 0, // data is InspectorInfo
kMsgType_InspectorKeyValuePair = 1, // data is KeyValueMessageData
kMsgType_InspectorAcknowledgement = 2 // no data sent
};
// Initial information sent from the crashed process by
// Breakpad.framework to the Inspector process
// The mach message with this struct as data will also include
// several descriptors for sending mach port rights to the crashed
// task, etc.
struct InspectorInfo {
int exception_type;
int exception_code;
unsigned int parameter_count; // key-value pairs
};
// Key/value message data to be sent to the Inspector
struct KeyValueMessageData {
public:
KeyValueMessageData() {}
KeyValueMessageData(const google_breakpad::KeyValueEntry &inEntry) {
strlcpy(key, inEntry.GetKey(), sizeof(key) );
strlcpy(value, inEntry.GetValue(), sizeof(value) );
}
char key[google_breakpad::KeyValueEntry::MAX_STRING_STORAGE_SIZE];
char value[google_breakpad::KeyValueEntry::MAX_STRING_STORAGE_SIZE];
};
using std::string;
using google_breakpad::MinidumpGenerator;
namespace google_breakpad {
static BOOL EnsureDirectoryPathExists(NSString *dirPath);
//=============================================================================
class ConfigFile {
public:
ConfigFile() {
config_file_ = -1;
config_file_path_[0] = 0;
has_created_file_ = false;
};
~ConfigFile() {
};
void WriteFile(const SimpleStringDictionary *configurationParameters,
const char *dump_dir,
const char *minidump_id);
const char *GetFilePath() { return config_file_path_; }
void Unlink() {
if (config_file_ != -1)
unlink(config_file_path_);
config_file_ = -1;
}
private:
BOOL WriteData(const void *data, size_t length);
BOOL AppendConfigData(const char *key,
const void *data,
size_t length);
BOOL AppendConfigString(const char *key,
const char *value);
int config_file_; // descriptor for config file
char config_file_path_[PATH_MAX]; // Path to configuration file
bool has_created_file_;
};
//=============================================================================
class MinidumpLocation {
public:
MinidumpLocation() {
NSString *minidumpDirBase = NSHomeDirectory();
NSString *minidumpDir;
// Put root processes at root
if (geteuid() == 0)
minidumpDirBase = @"/";
minidumpDir =
[minidumpDirBase stringByAppendingPathComponent:@"Library/Logs/Google"];
// Ensure that the path exists. Fallback to /tmp if unable to locate path.
if (!EnsureDirectoryPathExists(minidumpDir)) {
DEBUGLOG(stderr, "Unable to create: %s\n", [minidumpDir UTF8String]);
minidumpDir = @"/tmp";
}
strlcpy(minidump_dir_path_, [minidumpDir fileSystemRepresentation],
sizeof(minidump_dir_path_));
// now generate a unique ID
string dump_path(minidump_dir_path_);
string next_minidump_id;
string next_minidump_path_ =
(MinidumpGenerator::UniqueNameInDirectory(dump_path, &next_minidump_id));
strlcpy(minidump_id_, next_minidump_id.c_str(), sizeof(minidump_id_));
};
const char *GetPath() { return minidump_dir_path_; }
const char *GetID() { return minidump_id_; }
private:
char minidump_dir_path_[PATH_MAX]; // Path to minidump directory
char minidump_id_[128];
};
//=============================================================================
class Inspector {
public:
Inspector() {};
// given a bootstrap service name, receives mach messages
// from a crashed process, then inspects it, creates a minidump file
// and asks the user if he wants to upload it to a server.
void Inspect(const char *receive_port_name);
private:
kern_return_t ServiceCheckIn(const char *receive_port_name);
kern_return_t ServiceCheckOut(const char *receive_port_name);
kern_return_t ReadMessages();
bool InspectTask();
kern_return_t SendAcknowledgement();
void LaunchReporter(const char *inConfigFilePath);
mach_port_t service_rcv_port_;
int exception_type_;
int exception_code_;
mach_port_t remote_task_;
mach_port_t crashing_thread_;
mach_port_t handler_thread_;
mach_port_t ack_port_;
SimpleStringDictionary config_params_;
ConfigFile config_file_;
};
} // namespace google_breakpad

View File

@@ -0,0 +1,431 @@
// Copyright (c) 2007, 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.
//
// Utility that can inspect another process and write a crash dump
#import <cstdio>
#import <iostream>
#import <stdio.h>
#import <string.h>
#import <string>
#import "client/mac/crash_generation/Inspector.h"
#import "client/mac/Framework/Breakpad.h"
#import "client/mac/handler/minidump_generator.h"
#import "common/mac/SimpleStringDictionary.h"
#import "common/mac/MachIPC.h"
#import <Foundation/Foundation.h>
#if VERBOSE
bool gDebugLog = true;
#else
bool gDebugLog = false;
#endif
namespace google_breakpad {
//=============================================================================
static BOOL EnsureDirectoryPathExists(NSString *dirPath) {
NSFileManager *mgr = [NSFileManager defaultManager];
// If we got a relative path, prepend the current directory
if (![dirPath isAbsolutePath])
dirPath = [[mgr currentDirectoryPath] stringByAppendingPathComponent:dirPath];
NSString *path = dirPath;
// Ensure that no file exists within the path which would block creation
while (1) {
BOOL isDir;
if ([mgr fileExistsAtPath:path isDirectory:&isDir]) {
if (isDir)
break;
return NO;
}
path = [path stringByDeletingLastPathComponent];
}
// Path now contains the first valid directory (or is empty)
if (![path length])
return NO;
NSString *common =
[dirPath commonPrefixWithString:path options:NSLiteralSearch];
// If everything is good
if ([common isEqualToString:dirPath])
return YES;
// Break up the difference into components
NSString *diff = [dirPath substringFromIndex:[common length] + 1];
NSArray *components = [diff pathComponents];
unsigned count = [components count];
// Rebuild the path one component at a time
NSDictionary *attrs =
[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750]
forKey:NSFilePosixPermissions];
path = common;
for (unsigned i = 0; i < count; ++i) {
path = [path stringByAppendingPathComponent:[components objectAtIndex:i]];
if (![mgr createDirectoryAtPath:path attributes:attrs])
return NO;
}
return YES;
}
//=============================================================================
BOOL ConfigFile::WriteData(const void *data, size_t length) {
size_t result = write(config_file_, data, length);
return result == length;
}
//=============================================================================
BOOL ConfigFile::AppendConfigData(const char *key,
const void *data, size_t length) {
assert(config_file_ != -1);
if (!key) {
DEBUGLOG(stderr, "Breakpad: Missing Key\n");
return NO;
}
if (!data) {
DEBUGLOG(stderr, "Breakpad: Missing data for key: %s\n", key ? key :
"<Unknown Key>");
return NO;
}
// Write the key, \n, length of data (ascii integer), \n, data
char buffer[16];
char nl = '\n';
BOOL result = WriteData(key, strlen(key));
snprintf(buffer, sizeof(buffer) - 1, "\n%lu\n", length);
result &= WriteData(buffer, strlen(buffer));
result &= WriteData(data, length);
result &= WriteData(&nl, 1);
return result;
}
//=============================================================================
BOOL ConfigFile::AppendConfigString(const char *key,
const char *value) {
return AppendConfigData(key, value, strlen(value));
}
//=============================================================================
void ConfigFile::WriteFile(const SimpleStringDictionary *configurationParameters,
const char *dump_dir,
const char *minidump_id) {
assert(config_file_ == -1);
// Open and write out configuration file preamble
strlcpy(config_file_path_, "/tmp/Config-XXXXXX",
sizeof(config_file_path_));
config_file_ = mkstemp(config_file_path_);
if (config_file_ == -1)
return;
has_created_file_ = true;
// Add the minidump dir
AppendConfigString(kReporterMinidumpDirectoryKey, dump_dir);
AppendConfigString(kReporterMinidumpIDKey, minidump_id);
// Write out the configuration parameters
BOOL result = YES;
const SimpleStringDictionary &dictionary = *configurationParameters;
const KeyValueEntry *entry = NULL;
SimpleStringDictionaryIterator iter(dictionary);
while ((entry = iter.Next())) {
result = AppendConfigString(entry->GetKey(), entry->GetValue());
if (!result)
break;
}
close(config_file_);
config_file_ = -1;
}
//=============================================================================
void Inspector::Inspect(const char *receive_port_name) {
kern_return_t result = ServiceCheckIn(receive_port_name);
if (result == KERN_SUCCESS) {
result = ReadMessages();
if (result == KERN_SUCCESS) {
// Inspect the task and write a minidump file.
InspectTask();
// Send acknowledgement to the crashed process that the inspection
// has finished. It will then be able to cleanly exit.
if (SendAcknowledgement() == KERN_SUCCESS) {
// Ask the user if he wants to upload the crash report to a server,
// and do so if he agrees.
LaunchReporter(config_file_.GetFilePath());
}
// Now that we're done reading messages, cleanup the service, but only
// if there was an actual exception
// Otherwise, it means the dump was generated on demand and the process
// lives on, and we might be needed again in the future.
if (exception_code_) {
ServiceCheckOut(receive_port_name);
}
} else {
PRINT_MACH_RESULT(result, "Inspector: WaitForMessage()");
}
}
}
//=============================================================================
kern_return_t Inspector::ServiceCheckIn(const char *receive_port_name) {
// We need to get the mach port representing this service, so we can
// get information from the crashed process.
kern_return_t kr = bootstrap_check_in(bootstrap_port,
(char*)receive_port_name,
&service_rcv_port_);
if (kr != KERN_SUCCESS) {
#if VERBOSE
PRINT_MACH_RESULT(kr, "Inspector: bootstrap_check_in()");
#endif
}
return kr;
}
//=============================================================================
kern_return_t Inspector::ServiceCheckOut(const char *receive_port_name) {
// We're done receiving mach messages from the crashed process,
// so clean up a bit.
kern_return_t kr;
// DO NOT use mach_port_deallocate() here -- it will fail and the
// following bootstrap_register() will also fail leaving our service
// name hanging around forever (until reboot)
kr = mach_port_destroy(mach_task_self(), service_rcv_port_);
if (kr != KERN_SUCCESS) {
PRINT_MACH_RESULT(kr,
"Inspector: UNREGISTERING: service_rcv_port mach_port_deallocate()");
return kr;
}
// Unregister the service associated with the receive port.
kr = bootstrap_register(bootstrap_port,
(char*)receive_port_name,
MACH_PORT_NULL);
if (kr != KERN_SUCCESS) {
PRINT_MACH_RESULT(kr, "Inspector: UNREGISTERING: bootstrap_register()");
}
return kr;
}
//=============================================================================
kern_return_t Inspector::ReadMessages() {
// Wait for an initial message from the crashed process containing basic
// information about the crash.
ReceivePort receive_port(service_rcv_port_);
MachReceiveMessage message;
kern_return_t result = receive_port.WaitForMessage(&message, 1000);
if (result == KERN_SUCCESS) {
InspectorInfo &info = (InspectorInfo &)*message.GetData();
exception_type_ = info.exception_type;
exception_code_ = info.exception_code;
#if VERBOSE
printf("message ID = %d\n", message.GetMessageID());
#endif
remote_task_ = message.GetTranslatedPort(0);
crashing_thread_ = message.GetTranslatedPort(1);
handler_thread_ = message.GetTranslatedPort(2);
ack_port_ = message.GetTranslatedPort(3);
#if VERBOSE
printf("exception_type = %d\n", exception_type_);
printf("exception_code = %d\n", exception_code_);
printf("remote_task = %d\n", remote_task_);
printf("crashing_thread = %d\n", crashing_thread_);
printf("handler_thread = %d\n", handler_thread_);
printf("ack_port_ = %d\n", ack_port_);
printf("parameter count = %d\n", info.parameter_count);
#endif
// The initial message contains the number of key value pairs that
// we are expected to read.
// Read each key/value pair, one mach message per key/value pair.
for (unsigned int i = 0; i < info.parameter_count; ++i) {
MachReceiveMessage message;
result = receive_port.WaitForMessage(&message, 1000);
if(result == KERN_SUCCESS) {
KeyValueMessageData &key_value_data =
(KeyValueMessageData&)*message.GetData();
config_params_.SetKeyValue(key_value_data.key, key_value_data.value);
} else {
PRINT_MACH_RESULT(result, "Inspector: key/value message");
break;
}
}
}
return result;
}
//=============================================================================
bool Inspector::InspectTask() {
// keep the task quiet while we're looking at it
task_suspend(remote_task_);
MinidumpLocation minidumpLocation;
config_file_.WriteFile( &config_params_,
minidumpLocation.GetPath(),
minidumpLocation.GetID());
MinidumpGenerator generator(remote_task_, handler_thread_);
if (exception_type_ && exception_code_) {
generator.SetExceptionInformation(exception_type_,
exception_code_,
crashing_thread_);
}
NSString *minidumpPath = [NSString stringWithFormat:@"%s/%s.dmp",
minidumpLocation.GetPath(), minidumpLocation.GetID()];
bool result = generator.Write([minidumpPath fileSystemRepresentation]);
DEBUGLOG(stderr, "Inspector: finished writing minidump file: %s\n",
[minidumpPath fileSystemRepresentation]);
// let the task continue
task_resume(remote_task_);
return result;
}
//=============================================================================
// The crashed task needs to be told that the inspection has finished.
// It will wait on a mach port (with timeout) until we send acknowledgement.
kern_return_t Inspector::SendAcknowledgement() {
if (ack_port_ != MACH_PORT_DEAD) {
MachPortSender sender(ack_port_);
MachSendMessage ack_message(kMsgType_InspectorAcknowledgement);
DEBUGLOG(stderr, "Inspector: trying to send acknowledgement to port %d\n",
ack_port_);
kern_return_t result = sender.SendMessage(ack_message, 2000);
#if VERBOSE
PRINT_MACH_RESULT(result, "Inspector: sent acknowledgement");
#endif
return result;
}
DEBUGLOG(stderr, "Inspector: port translation failure!\n");
return KERN_INVALID_NAME;
}
//=============================================================================
void Inspector::LaunchReporter(const char *inConfigFilePath) {
// Extract the path to the reporter executable.
const char *reporterExecutablePath =
config_params_.GetValueForKey(BREAKPAD_REPORTER_EXE_LOCATION);
DEBUGLOG(stderr, "reporter path = %s\n", reporterExecutablePath);
// Setup and launch the crash dump sender.
const char *argv[3];
argv[0] = reporterExecutablePath;
argv[1] = inConfigFilePath;
argv[2] = NULL;
// Launch the reporter
pid_t pid = fork();
// If we're in the child, load in our new executable and run.
// The parent will not wait for the child to complete.
if (pid == 0) {
execv(argv[0], (char * const *)argv);
config_file_.Unlink(); // launch failed - get rid of config file
DEBUGLOG(stderr, "Inspector: unable to launch reporter app\n");
_exit(1);
}
// Wait until the Reporter child process exits.
//
// We'll use a timeout of one minute.
int timeoutCount = 60; // 60 seconds
while (timeoutCount-- > 0) {
int status;
pid_t result = waitpid(pid, &status, WNOHANG);
if (result == 0) {
// The child has not yet finished.
sleep(1);
} else if (result == -1) {
DEBUGLOG(stderr, "Inspector: waitpid error (%d) waiting for reporter app\n",
errno);
break;
} else {
// child has finished
break;
}
}
}
} // namespace google_breakpad

View File

@@ -0,0 +1,65 @@
// Copyright (c) 2007, 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.
//
// Main driver for Inspector
#import "client/mac/crash_generation/Inspector.h"
#import <Cocoa/Cocoa.h>
namespace google_breakpad {
//=============================================================================
extern "C" {
int main(int argc, char *const argv[]) {
#if DEBUG
// Since we're launched on-demand, this is necessary to see debugging
// output in the console window.
freopen("/dev/console", "w", stdout);
freopen("/dev/console", "w", stderr);
#endif
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (argc != 2) {
exit(0);
}
// Our first command-line argument contains the name of the service
// that we're providing.
google_breakpad::Inspector inspector;
inspector.Inspect(argv[1]);
[pool release];
return 0;
}
} // extern "C"
} // namespace google_breakpad

Binary file not shown.

View File

@@ -490,7 +490,6 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
if (self->use_minidump_write_mutex_)
pthread_mutex_unlock(&self->minidump_write_mutex_);
} else {
// When forking a child process with the exception handler installed,
// if the child crashes, it will send the exception back to the parent
// process. The check for task == self_task() ensures that only

View File

@@ -55,6 +55,7 @@ static void *SleepyFunction(void *) {
while (1) {
sleep(10000);
}
return NULL;
}
static void Crasher() {
@@ -77,15 +78,14 @@ bool MDCallback(const char *dump_dir, const char *file_name,
fprintf(stdout, "Minidump: %s\n", path.c_str());
// Indicate that we've handled the callback
return true;
exit(0);
}
int main(int argc, char * const argv[]) {
char buffer[PATH_MAX];
struct passwd *user = getpwuid(getuid());
// Home dir
snprintf(buffer, sizeof(buffer), "/Users/%s/Desktop/", user->pw_name);
snprintf(buffer, sizeof(buffer), "/tmp/");
string path(buffer);
ExceptionHandler eh(path, NULL, MDCallback, NULL, true);
@@ -97,8 +97,8 @@ int main(int argc, char * const argv[]) {
perror("pthread_create");
}
// Dump a test
eh.WriteMinidump();
// // Dump a test
// eh.WriteMinidump();
// Test the handler
SoonToCrash();

View File

@@ -45,14 +45,13 @@ static bool doneWritingReport = false;
static void *Reporter(void *) {
char buffer[PATH_MAX];
MinidumpGenerator md;
struct passwd *user = getpwuid(getuid());
// Write it to the desktop
snprintf(buffer,
sizeof(buffer),
"/Users/%s/Desktop/test.dmp",
user->pw_name);
"/tmp/test.dmp");
fprintf(stdout, "Writing %s\n", buffer);
unlink(buffer);
md.Write(buffer);

View File

@@ -0,0 +1,834 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.02">
<data>
<int key="IBDocument.SystemTarget">1050</int>
<string key="IBDocument.SystemVersion">9F33</string>
<string key="IBDocument.InterfaceBuilderVersion">670</string>
<string key="IBDocument.AppKitVersion">949.34</string>
<string key="IBDocument.HIToolboxVersion">352.00</string>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
<integer value="2"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
</object>
<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSCustomObject" id="1001">
<string key="NSClassName">Reporter</string>
</object>
<object class="NSCustomObject" id="1003">
<string key="NSClassName">FirstResponder</string>
</object>
<object class="NSCustomObject" id="1004">
<string key="NSClassName">NSApplication</string>
</object>
<object class="NSWindowTemplate" id="1005">
<int key="NSWindowStyleMask">1</int>
<int key="NSWindowBacking">2</int>
<string key="NSWindowRect">{{196, 157}, {484, 353}}</string>
<int key="NSWTFlags">536871936</int>
<string key="NSWindowTitle"/>
<string key="NSWindowClass">NSWindow</string>
<nil key="NSViewClass"/>
<string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string>
<object class="NSView" key="NSWindowView" id="1006">
<reference key="NSNextResponder"/>
<int key="NSvFlags">264</int>
<object class="NSMutableArray" key="NSSubviews">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSTextField" id="52942477">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">266</int>
<string key="NSFrame">{{89, 279}, {378, 54}}</string>
<reference key="NSSuperview" ref="1006"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="237530958">
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">272629760</int>
<string type="base64-UTF8" key="NSContents">RE8gTk9UIExPQ0FMSVpFLiBUaGUgPFJlYWxseSBMb25nIENvbXBhbnkgTmFtZT4gcHJvZ3JhbSA8UmVh
bGx5IExvbmcgQXBwIE5hbWUgSGVyZT4gaGFzIHVuZXhwZWN0ZWRseSBxdWl0LiA</string>
<object class="NSFont" key="NSSupport">
<string key="NSName">LucidaGrande-Bold</string>
<double key="NSSize">1.400000e+01</double>
<int key="NSfFlags">16</int>
</object>
<reference key="NSControlView" ref="52942477"/>
<object class="NSColor" key="NSBackgroundColor" id="738791573">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">controlColor</string>
<object class="NSColor" key="NSColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MC42NjY2NjY2OQA</bytes>
</object>
</object>
<object class="NSColor" key="NSTextColor" id="909242260">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">controlTextColor</string>
<object class="NSColor" key="NSColor" id="488201583">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MAA</bytes>
</object>
</object>
</object>
</object>
<object class="NSImageView" id="131954859">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">268</int>
<object class="NSMutableSet" key="NSDragTypes">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSMutableArray" key="set.sortedObjects">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>Apple PDF pasteboard type</string>
<string>Apple PICT pasteboard type</string>
<string>Apple PNG pasteboard type</string>
<string>NSFilenamesPboardType</string>
<string>NeXT Encapsulated PostScript v1.2 pasteboard type</string>
<string>NeXT TIFF v4.0 pasteboard type</string>
</object>
</object>
<string key="NSFrame">{{20, 269}, {64, 64}}</string>
<reference key="NSSuperview" ref="1006"/>
<bool key="NSEnabled">YES</bool>
<object class="NSImageCell" key="NSCell" id="863447683">
<int key="NSCellFlags">130560</int>
<int key="NSCellFlags2">33554432</int>
<object class="NSCustomResource" key="NSContents">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">NSApplicationIcon</string>
</object>
<int key="NSAlign">0</int>
<int key="NSScale">0</int>
<int key="NSStyle">0</int>
<bool key="NSAnimates">NO</bool>
</object>
<bool key="NSEditable">YES</bool>
</object>
<object class="NSTextField" id="547141541">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">266</int>
<string key="NSFrame">{{17, 191}, {450, 70}}</string>
<reference key="NSSuperview" ref="1006"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="857260085">
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">272760832</int>
<string type="base64-UTF8" key="NSContents">RE8gTk9UIExPQ0FMSVpFLiBUaGUgc3lzdGVtIGFuZCBvdGhlciBhcHBsaWNhdGlvbnMgaGF2ZSBub3Qg
YmVlbiBhZmZlY3RlZC4gQSByZXBvcnQgaGFzIGJlZW4gY3JlYXRlZCB0aGF0IHlvdSBjYW4gc2VuZCB0
byA8UmVhbGx5IExvbmcgQ29tcGFueSBOYW1lPiB0byBoZWxwIGlkZW50aWZ5IHRoZSBwcm9ibGVtLgoK
UGxlYXNlIGhlbHAgdXMgZml4IHRoZSBwcm9ibGVtIGJ5IGRlc2NyaWJpbmcgd2hhdCBoYXBwZW5lZCBi
ZWZvcmUgdGhlIGNyYXNoLg</string>
<object class="NSFont" key="NSSupport" id="26">
<string key="NSName">LucidaGrande</string>
<double key="NSSize">1.100000e+01</double>
<int key="NSfFlags">3100</int>
</object>
<reference key="NSControlView" ref="547141541"/>
<reference key="NSBackgroundColor" ref="738791573"/>
<reference key="NSTextColor" ref="909242260"/>
</object>
</object>
<object class="NSTextField" id="637803025">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">290</int>
<string key="NSFrame">{{17, 85}, {450, 28}}</string>
<reference key="NSSuperview" ref="1006"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="862965674">
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">272760832</int>
<string key="NSContents">DO NOT LOCALIZE. Providing your email address is optional and will allow us contact you in case we need more details.</string>
<reference key="NSSupport" ref="26"/>
<reference key="NSControlView" ref="637803025"/>
<reference key="NSBackgroundColor" ref="738791573"/>
<reference key="NSTextColor" ref="909242260"/>
</object>
</object>
<object class="NSButton" id="506323760">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">289</int>
<string key="NSFrame">{{353, 12}, {117, 32}}</string>
<reference key="NSSuperview" ref="1006"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="555878343">
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">134217728</int>
<string key="NSContents">Send Report</string>
<object class="NSFont" key="NSSupport" id="890637240">
<string key="NSName">LucidaGrande</string>
<double key="NSSize">1.300000e+01</double>
<int key="NSfFlags">1044</int>
</object>
<reference key="NSControlView" ref="506323760"/>
<int key="NSButtonFlags">-2038284033</int>
<int key="NSButtonFlags2">129</int>
<reference key="NSAlternateImage" ref="890637240"/>
<string key="NSAlternateContents"/>
<string type="base64-UTF8" key="NSKeyEquivalent">DQ</string>
<int key="NSPeriodicDelay">200</int>
<int key="NSPeriodicInterval">25</int>
</object>
</object>
<object class="NSButton" id="1066255572">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">289</int>
<string key="NSFrame">{{243, 12}, {110, 32}}</string>
<reference key="NSSuperview" ref="1006"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="910201563">
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">134217728</int>
<string key="NSContents">Cancel</string>
<reference key="NSSupport" ref="890637240"/>
<reference key="NSControlView" ref="1066255572"/>
<int key="NSButtonFlags">-2038284033</int>
<int key="NSButtonFlags2">129</int>
<reference key="NSAlternateImage" ref="890637240"/>
<string key="NSAlternateContents"/>
<string type="base64-UTF8" key="NSKeyEquivalent">Gw</string>
<int key="NSPeriodicDelay">200</int>
<int key="NSPeriodicInterval">25</int>
</object>
</object>
<object class="NSTextField" id="834332784">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">290</int>
<string key="NSFrame">{{59, 58}, {195, 19}}</string>
<reference key="NSSuperview" ref="1006"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="454024475">
<int key="NSCellFlags">-1804468671</int>
<int key="NSCellFlags2">272761856</int>
<string key="NSContents">preston.jackson@gmail.com</string>
<reference key="NSSupport" ref="26"/>
<string key="NSPlaceholderString">optional</string>
<reference key="NSControlView" ref="834332784"/>
<bool key="NSDrawsBackground">YES</bool>
<object class="NSColor" key="NSBackgroundColor" id="181834562">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">textBackgroundColor</string>
<object class="NSColor" key="NSColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
</object>
<object class="NSColor" key="NSTextColor" id="332510420">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">textColor</string>
<reference key="NSColor" ref="488201583"/>
</object>
</object>
</object>
<object class="NSTextField" id="1001751188">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{17, 60}, {37, 14}}</string>
<reference key="NSSuperview" ref="1006"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="220926093">
<int key="NSCellFlags">68288064</int>
<int key="NSCellFlags2">71435264</int>
<string key="NSContents">Email:</string>
<reference key="NSSupport" ref="26"/>
<reference key="NSControlView" ref="1001751188"/>
<reference key="NSBackgroundColor" ref="738791573"/>
<reference key="NSTextColor" ref="909242260"/>
</object>
</object>
<object class="NSButton" id="645396128">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">289</int>
<string key="NSFrame">{{352, 59}, {16, 17}}</string>
<reference key="NSSuperview" ref="1006"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="776409024">
<int key="NSCellFlags">-2080244224</int>
<int key="NSCellFlags2">262144</int>
<string key="NSContents">Privacy Policy</string>
<object class="NSFont" key="NSSupport">
<string key="NSName">LucidaGrande</string>
<double key="NSSize">9.000000e+00</double>
<int key="NSfFlags">3614</int>
</object>
<reference key="NSControlView" ref="645396128"/>
<int key="NSButtonFlags">-2040250113</int>
<int key="NSButtonFlags2">36</int>
<object class="NSCustomResource" key="NSNormalImage">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">NSFollowLinkFreestandingTemplate</string>
</object>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
<int key="NSPeriodicDelay">400</int>
<int key="NSPeriodicInterval">75</int>
</object>
</object>
<object class="NSTextField" id="271427294">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">289</int>
<string key="NSFrame">{{259, 60}, {89, 14}}</string>
<reference key="NSSuperview" ref="1006"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="718717750">
<int key="NSCellFlags">68288064</int>
<int key="NSCellFlags2">71435264</int>
<string key="NSContents">Privacy Policy</string>
<reference key="NSSupport" ref="26"/>
<reference key="NSControlView" ref="271427294"/>
<reference key="NSBackgroundColor" ref="738791573"/>
<reference key="NSTextColor" ref="909242260"/>
</object>
</object>
<object class="NSTextField" id="652207274">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">274</int>
<string key="NSFrame">{{20, 124}, {444, 61}}</string>
<reference key="NSSuperview" ref="1006"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="748395329">
<int key="NSCellFlags">341966337</int>
<int key="NSCellFlags2">272760832</int>
<string key="NSContents">DO NOT LOCALIZE: Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 </string>
<reference key="NSSupport" ref="26"/>
<reference key="NSControlView" ref="652207274"/>
<bool key="NSDrawsBackground">YES</bool>
<reference key="NSBackgroundColor" ref="181834562"/>
<reference key="NSTextColor" ref="332510420"/>
</object>
</object>
</object>
<string key="NSFrameSize">{484, 353}</string>
<reference key="NSSuperview"/>
</object>
<string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string>
<string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string>
</object>
</object>
<object class="IBObjectContainer" key="IBDocument.Objects">
<object class="NSMutableArray" key="connectionRecords">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">alertWindow</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="1005"/>
</object>
<int key="connectionID">42</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">sendReport:</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="506323760"/>
</object>
<int key="connectionID">45</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">cancel:</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="1066255572"/>
</object>
<int key="connectionID">46</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">showPrivacyPolicy:</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="645396128"/>
</object>
<int key="connectionID">53</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: headerMessage</string>
<reference key="source" ref="52942477"/>
<reference key="destination" ref="1001"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="52942477"/>
<reference key="NSDestination" ref="1001"/>
<string key="NSLabel">value: headerMessage</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">headerMessage</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">85</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: reportMessage</string>
<reference key="source" ref="547141541"/>
<reference key="destination" ref="1001"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="547141541"/>
<reference key="NSDestination" ref="1001"/>
<string key="NSLabel">value: reportMessage</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">reportMessage</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">86</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: emailMessage</string>
<reference key="source" ref="637803025"/>
<reference key="destination" ref="1001"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="637803025"/>
<reference key="NSDestination" ref="1001"/>
<string key="NSLabel">value: emailMessage</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">emailMessage</string>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">87</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: emailValue</string>
<reference key="source" ref="834332784"/>
<reference key="destination" ref="1001"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="834332784"/>
<reference key="NSDestination" ref="1001"/>
<string key="NSLabel">value: emailValue</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">emailValue</string>
<object class="NSDictionary" key="NSOptions">
<string key="NS.key.0">NSNullPlaceholder</string>
<string key="NS.object.0">optional</string>
</object>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">90</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">initialFirstResponder</string>
<reference key="source" ref="1005"/>
<reference key="destination" ref="506323760"/>
</object>
<int key="connectionID">91</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: commentsValue</string>
<reference key="source" ref="652207274"/>
<reference key="destination" ref="1001"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="652207274"/>
<reference key="NSDestination" ref="1001"/>
<string key="NSLabel">value: commentsValue</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">commentsValue</string>
<object class="NSDictionary" key="NSOptions">
<string key="NS.key.0">NSNullPlaceholder</string>
<string key="NS.object.0">optional comments</string>
</object>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">124</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">nextKeyView</string>
<reference key="source" ref="834332784"/>
<reference key="destination" ref="506323760"/>
</object>
<int key="connectionID">125</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">nextKeyView</string>
<reference key="source" ref="652207274"/>
<reference key="destination" ref="834332784"/>
</object>
<int key="connectionID">126</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">nextKeyView</string>
<reference key="source" ref="506323760"/>
<reference key="destination" ref="652207274"/>
</object>
<int key="connectionID">127</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="652207274"/>
<reference key="destination" ref="1001"/>
</object>
<int key="connectionID">128</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBObjectRecord">
<int key="objectID">0</int>
<object class="NSArray" key="object" id="1002">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="1001"/>
<reference key="parent" ref="1002"/>
<string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="1003"/>
<reference key="parent" ref="1002"/>
<string key="objectName">First Responder</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-3</int>
<reference key="object" ref="1004"/>
<reference key="parent" ref="1002"/>
<string key="objectName">Application</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">1</int>
<reference key="object" ref="1005"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="1006"/>
</object>
<reference key="parent" ref="1002"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">2</int>
<reference key="object" ref="1006"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="131954859"/>
<reference ref="52942477"/>
<reference ref="652207274"/>
<reference ref="637803025"/>
<reference ref="1001751188"/>
<reference ref="834332784"/>
<reference ref="271427294"/>
<reference ref="645396128"/>
<reference ref="506323760"/>
<reference ref="1066255572"/>
<reference ref="547141541"/>
</object>
<reference key="parent" ref="1005"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">3</int>
<reference key="object" ref="52942477"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="237530958"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">4</int>
<reference key="object" ref="237530958"/>
<reference key="parent" ref="52942477"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">6</int>
<reference key="object" ref="131954859"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="863447683"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">7</int>
<reference key="object" ref="863447683"/>
<reference key="parent" ref="131954859"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">8</int>
<reference key="object" ref="547141541"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="857260085"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">9</int>
<reference key="object" ref="857260085"/>
<reference key="parent" ref="547141541"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">12</int>
<reference key="object" ref="506323760"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="555878343"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">13</int>
<reference key="object" ref="555878343"/>
<reference key="parent" ref="506323760"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">14</int>
<reference key="object" ref="1066255572"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="910201563"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">15</int>
<reference key="object" ref="910201563"/>
<reference key="parent" ref="1066255572"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">18</int>
<reference key="object" ref="834332784"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="454024475"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">19</int>
<reference key="object" ref="454024475"/>
<reference key="parent" ref="834332784"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">20</int>
<reference key="object" ref="1001751188"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="220926093"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">21</int>
<reference key="object" ref="220926093"/>
<reference key="parent" ref="1001751188"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">48</int>
<reference key="object" ref="645396128"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="776409024"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">49</int>
<reference key="object" ref="776409024"/>
<reference key="parent" ref="645396128"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">58</int>
<reference key="object" ref="637803025"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="862965674"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">59</int>
<reference key="object" ref="862965674"/>
<reference key="parent" ref="637803025"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">66</int>
<reference key="object" ref="271427294"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="718717750"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">67</int>
<reference key="object" ref="718717750"/>
<reference key="parent" ref="271427294"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">116</int>
<reference key="object" ref="652207274"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="748395329"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">117</int>
<reference key="object" ref="748395329"/>
<reference key="parent" ref="652207274"/>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSMutableArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>-1.IBPluginDependency</string>
<string>-2.IBPluginDependency</string>
<string>-3.IBPluginDependency</string>
<string>1.IBEditorWindowLastContentRect</string>
<string>1.IBPluginDependency</string>
<string>1.IBViewEditorWindowController.showingBoundsRectangles</string>
<string>1.IBViewEditorWindowController.showingLayoutRectangles</string>
<string>1.IBWindowTemplateEditedContentRect</string>
<string>1.NSWindowTemplate.visibleAtLaunch</string>
<string>1.WindowOrigin</string>
<string>1.editorWindowContentRectSynchronizationRect</string>
<string>116.IBPluginDependency</string>
<string>117.IBPluginDependency</string>
<string>12.IBPluginDependency</string>
<string>13.IBPluginDependency</string>
<string>14.IBPluginDependency</string>
<string>15.IBPluginDependency</string>
<string>18.IBPluginDependency</string>
<string>19.IBPluginDependency</string>
<string>2.IBPluginDependency</string>
<string>2.IBUserGuides</string>
<string>20.IBPluginDependency</string>
<string>21.IBPluginDependency</string>
<string>3.IBPluginDependency</string>
<string>4.IBPluginDependency</string>
<string>48.IBPluginDependency</string>
<string>49.IBPluginDependency</string>
<string>58.IBPluginDependency</string>
<string>59.IBPluginDependency</string>
<string>6.IBPluginDependency</string>
<string>66.IBPluginDependency</string>
<string>67.IBPluginDependency</string>
<string>7.IBPluginDependency</string>
<string>8.IBPluginDependency</string>
<string>9.IBPluginDependency</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>{{641, 409}, {484, 353}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<integer value="0" id="6"/>
<reference ref="6"/>
<string>{{641, 409}, {484, 353}}</string>
<reference ref="6"/>
<string>{196, 240}</string>
<string>{{357, 418}, {480, 270}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<object class="NSMutableArray">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBUserGuide">
<reference key="view" ref="1006"/>
<float key="location">1.120000e+02</float>
<int key="affinity">1</int>
</object>
</object>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
</object>
</object>
<object class="NSMutableDictionary" key="unlocalizedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<nil key="activeLocalization"/>
<object class="NSMutableDictionary" key="localizations">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<nil key="sourceID"/>
<int key="maxID">128</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">Reporter</string>
<string key="superclassName">NSObject</string>
<object class="NSMutableDictionary" key="actions">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSMutableArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>cancel:</string>
<string>sendReport:</string>
<string>showPrivacyPolicy:</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>id</string>
<string>id</string>
<string>id</string>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">alertWindow</string>
<string key="NS.object.0">NSWindow</string>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">sender/crash_report_sender.h</string>
</object>
</object>
</object>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.LastKnownRelativeProjectPath">../../Breakpad.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
</data>
</archive>

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.Breakpad.${PRODUCT_NAME:identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

View File

@@ -0,0 +1,89 @@
// Copyright (c) 2006, 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.
//
// This component uses the HTTPMultipartUpload of the breakpad project to send
// the minidump and associated data to the crash reporting servers.
// It will perform throttling based on the parameters passed to it and will
// prompt the user to send the minidump.
#include <Foundation/Foundation.h>
#include "client/mac/Framework/Breakpad.h"
#define kClientIdPreferenceKey @"clientid"
@interface Reporter : NSObject {
@public
IBOutlet NSWindow *alertWindow; // The alert window
// Values bound in the XIB
NSString *headerMessage_; // Message notifying of the crash
NSString *reportMessage_; // Message explaining the crash report
NSString *commentsValue_; // Comments from the user
NSString *emailMessage_; // Message requesting user email
NSString *emailValue_; // Email from the user
@private
int configFile_; // File descriptor for config file
NSMutableDictionary *parameters_; // Key value pairs of data (STRONG)
NSData *minidumpContents_; // The data in the minidump (STRONG)
NSData *logFileData_; // An NSdata for the tar, bz2'd log file
}
// Stops the modal panel with an NSAlertDefaultReturn value. This is the action
// invoked by the "Send Report" button.
- (IBAction)sendReport:(id)sender;
// Stops the modal panel with an NSAlertAlternateReturn value. This is the
// action invoked by the "Cancel" button.
- (IBAction)cancel:(id)sender;
// Opens the Google Privacy Policy in the default web browser.
- (IBAction)showPrivacyPolicy:(id)sender;
// Delegate methods for the NSTextField for comments. We want to capture the
// Return key and use it to send the message when no text has been entered.
// Otherwise, we want Return to add a carriage return to the comments field.
- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView
doCommandBySelector:(SEL)commandSelector;
// Accessors to make bindings work
- (NSString *)headerMessage;
- (void)setHeaderMessage:(NSString *)value;
- (NSString *)reportMessage;
- (void)setReportMessage:(NSString *)value;
- (NSString *)commentsValue;
- (void)setCommentsValue:(NSString *)value;
- (NSString *)emailMessage;
- (void)setEmailMessage:(NSString *)value;
- (NSString *)emailValue;
- (void)setEmailValue:(NSString *)value;
@end

View File

@@ -0,0 +1,762 @@
// Copyright (c) 2006, 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 <pwd.h>
#import <sys/stat.h>
#import <unistd.h>
#import <Cocoa/Cocoa.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import "common/mac/HTTPMultipartUpload.h"
#import "crash_report_sender.h"
#import "common/mac/GTMLogger.h"
#define kLastSubmission @"LastSubmission"
const int kMinidumpFileLengthLimit = 800000;
#define kApplePrefsSyncExcludeAllKey @"com.apple.PreferenceSync.ExcludeAllSyncKeys"
@interface Reporter(PrivateMethods)
+ (uid_t)consoleUID;
- (id)initWithConfigurationFD:(int)fd;
- (NSString *)readString;
- (NSData *)readData:(ssize_t)length;
- (BOOL)readConfigurationData;
- (BOOL)readMinidumpData;
- (BOOL)readLogFileData;
- (BOOL)askUserPermissionToSend:(BOOL)shouldSubmitReport;
- (BOOL)shouldSubmitReport;
// Run an alert window with the given timeout. Returns NSAlertButtonDefault if
// the timeout is exceeded. A timeout of 0 queues the message immediately in the
// modal run loop.
- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout;
- (NSString*)clientID;
@end
@implementation Reporter
//=============================================================================
+ (uid_t)consoleUID {
SCDynamicStoreRef store =
SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("Reporter"), NULL, NULL);
uid_t uid = -2; // Default to "nobody"
if (store) {
CFStringRef user = SCDynamicStoreCopyConsoleUser(store, &uid, NULL);
if (user)
CFRelease(user);
else
uid = -2;
CFRelease(store);
}
return uid;
}
//=============================================================================
- (id)initWithConfigurationFD:(int)fd {
if ((self = [super init])) {
configFile_ = fd;
}
// Because the reporter is embedded in the framework (and many copies
// of the framework may exist) its not completely certain that the OS
// will obey the com.apple.PreferenceSync.ExcludeAllSyncKeys in our
// Info.plist. To make sure, also set the key directly if needed.
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
if (![ud boolForKey:kApplePrefsSyncExcludeAllKey]) {
[ud setBool:YES forKey:kApplePrefsSyncExcludeAllKey];
}
return self;
}
//=============================================================================
- (NSString *)readString {
NSMutableString *str = [NSMutableString stringWithCapacity:32];
char ch[2] = { 0 };
while (read(configFile_, &ch[0], 1) == 1) {
if (ch[0] == '\n') {
// Break if this is the first newline after reading some other string
// data.
if ([str length])
break;
} else {
[str appendString:[NSString stringWithUTF8String:ch]];
}
}
return str;
}
//=============================================================================
- (NSData *)readData:(ssize_t)length {
NSMutableData *data = [NSMutableData dataWithLength:length];
char *bytes = (char *)[data bytes];
if (read(configFile_, bytes, length) != length)
return nil;
return data;
}
//=============================================================================
- (BOOL)readConfigurationData {
parameters_ = [[NSMutableDictionary alloc] init];
while (1) {
NSString *key = [self readString];
if (![key length])
break;
// Read the data. Try to convert to a UTF-8 string, or just save
// the data
NSString *lenStr = [self readString];
ssize_t len = [lenStr intValue];
NSData *data = [self readData:len];
id value = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
[parameters_ setObject:value ? value : data forKey:key];
[value release];
}
// generate a unique client ID based on this host's MAC address
// then add a key/value pair for it
NSString *clientID = [self clientID];
[parameters_ setObject:clientID forKey:@"guid"];
close(configFile_);
configFile_ = -1;
return YES;
}
// Per user per machine
- (NSString *)clientID {
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSString *crashClientID = [ud stringForKey:kClientIdPreferenceKey];
if (crashClientID) {
return crashClientID;
}
// Otherwise, if we have no client id, generate one!
srandom([[NSDate date] timeIntervalSince1970]);
long clientId1 = random();
long clientId2 = random();
long clientId3 = random();
crashClientID = [NSString stringWithFormat:@"%x%x%x",
clientId1, clientId2, clientId3];
[ud setObject:crashClientID forKey:kClientIdPreferenceKey];
[ud synchronize];
return crashClientID;
}
//=============================================================================
- (BOOL)readLogFileData {
unsigned int logFileCounter = 0;
NSString *logPath;
int logFileTailSize = [[parameters_ objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE]
intValue];
NSMutableArray *logFilenames; // An array of NSString, one per log file
logFilenames = [[NSMutableArray alloc] init];
char tmpDirTemplate[80] = "/tmp/CrashUpload-XXXXX";
char *tmpDir = mkdtemp(tmpDirTemplate);
// Construct key names for the keys we expect to contain log file paths
for(logFileCounter = 0;; logFileCounter++) {
NSString *logFileKey = [NSString stringWithFormat:@"%@%d",
@BREAKPAD_LOGFILE_KEY_PREFIX,
logFileCounter];
logPath = [parameters_ objectForKey:logFileKey];
// They should all be consecutive, so if we don't find one, assume
// we're done
if (!logPath) {
break;
}
NSData *entireLogFile = [[NSData alloc] initWithContentsOfFile:logPath];
if (entireLogFile == nil) {
continue;
}
NSRange fileRange;
// Truncate the log file, only if necessary
if ([entireLogFile length] <= logFileTailSize) {
fileRange = NSMakeRange(0, [entireLogFile length]);
} else {
fileRange = NSMakeRange([entireLogFile length] - logFileTailSize,
logFileTailSize);
}
char tmpFilenameTemplate[100];
// Generate a template based on the log filename
sprintf(tmpFilenameTemplate,"%s/%s-XXXX", tmpDir,
[[logPath lastPathComponent] fileSystemRepresentation]);
char *tmpFile = mktemp(tmpFilenameTemplate);
NSData *logSubdata = [entireLogFile subdataWithRange:fileRange];
NSString *tmpFileString = [NSString stringWithUTF8String:tmpFile];
[logSubdata writeToFile:tmpFileString atomically:NO];
[logFilenames addObject:[tmpFileString lastPathComponent]];
[entireLogFile release];
}
if ([logFilenames count] == 0) {
[logFilenames release];
logFileData_ = nil;
return NO;
}
// now, bzip all files into one
NSTask *tarTask = [[NSTask alloc] init];
[tarTask setCurrentDirectoryPath:[NSString stringWithUTF8String:tmpDir]];
[tarTask setLaunchPath:@"/usr/bin/tar"];
NSMutableArray *bzipArgs = [NSMutableArray arrayWithObjects:@"-cjvf",
@"log.tar.bz2",nil];
[bzipArgs addObjectsFromArray:logFilenames];
[logFilenames release];
[tarTask setArguments:bzipArgs];
[tarTask launch];
[tarTask waitUntilExit];
[tarTask release];
NSString *logTarFile = [NSString stringWithFormat:@"%s/log.tar.bz2",tmpDir];
logFileData_ = [[NSData alloc] initWithContentsOfFile:logTarFile];
if (logFileData_ == nil) {
GTMLoggerDebug(@"Cannot find temp tar log file: %@", logTarFile);
return NO;
}
return YES;
}
//=============================================================================
- (BOOL)readMinidumpData {
NSString *minidumpDir = [parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey];
if (![minidumpID length])
return NO;
NSString *path = [minidumpDir stringByAppendingPathComponent:minidumpID];
path = [path stringByAppendingPathExtension:@"dmp"];
// check the size of the minidump and limit it to a reasonable size
// before attempting to load into memory and upload
const char *fileName = [path fileSystemRepresentation];
struct stat fileStatus;
BOOL success = YES;
if (!stat(fileName, &fileStatus)) {
if (fileStatus.st_size > kMinidumpFileLengthLimit) {
fprintf(stderr, "Breakpad Reporter: minidump file too large " \
"to upload : %d\n", (int)fileStatus.st_size);
success = NO;
}
} else {
fprintf(stderr, "Breakpad Reporter: unable to determine minidump " \
"file length\n");
success = NO;
}
if (success) {
minidumpContents_ = [[NSData alloc] initWithContentsOfFile:path];
success = ([minidumpContents_ length] ? YES : NO);
}
if (!success) {
// something wrong with the minidump file -- delete it
unlink(fileName);
}
return success;
}
//=============================================================================
- (BOOL)askUserPermissionToSend:(BOOL)shouldSubmitReport {
// Send without confirmation
if ([[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM] isEqualToString:@"YES"]) {
GTMLoggerDebug(@"Skipping confirmation and sending report");
return YES;
}
// Determine if we should create a text box for user feedback
BOOL shouldRequestComments =
[[parameters_ objectForKey:@BREAKPAD_REQUEST_COMMENTS]
isEqual:@"YES"];
NSString *display = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
if (![display length])
display = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
NSString *vendor = [parameters_ objectForKey:@BREAKPAD_VENDOR];
if (![vendor length])
vendor = @"Vendor not specified";
NSBundle *bundle = [NSBundle mainBundle];
[self setHeaderMessage:[NSString stringWithFormat:
NSLocalizedStringFromTableInBundle(@"headerFmt", nil,
bundle,
@""), vendor, display]];
NSString *defaultButtonTitle = nil;
NSString *otherButtonTitle = nil;
NSTimeInterval timeout = 60.0; // timeout value for the user notification
// Get the localized alert strings
// If we're going to submit a report, prompt the user if this is okay.
// Otherwise, just let them know that the app crashed.
if (shouldSubmitReport) {
NSString *msgFormat = NSLocalizedStringFromTableInBundle(@"msg",
nil,
bundle, @"");
[self setReportMessage:[NSString stringWithFormat:msgFormat, vendor]];
defaultButtonTitle = NSLocalizedStringFromTableInBundle(@"sendReportButton",
nil, bundle, @"");
otherButtonTitle = NSLocalizedStringFromTableInBundle(@"cancelButton", nil,
bundle, @"");
// Nominally use the report interval
timeout = [[parameters_ objectForKey:@BREAKPAD_REPORT_INTERVAL]
floatValue];
} else {
[self setReportMessage:NSLocalizedStringFromTableInBundle(@"noSendMsg", nil,
bundle, @"")];
defaultButtonTitle = NSLocalizedStringFromTableInBundle(@"noSendButton",
nil, bundle, @"");
timeout = 60.0;
}
// show the notification for at least one minute
if (timeout < 60.0) {
timeout = 60.0;
}
// Initialize Cocoa, needed to display the alert
NSApplicationLoad();
int buttonPressed = NSAlertAlternateReturn;
if (shouldRequestComments) {
BOOL didLoadNib = [NSBundle loadNibNamed:@"Breakpad" owner:self];
if (didLoadNib) {
// Append the request for comments to the |reportMessage| string.
NSString *commentsMessage =
NSLocalizedStringFromTableInBundle(@"commentsMsg", nil, bundle, @"");
[self setReportMessage:[NSString stringWithFormat:@"%@\n\n%@",
[self reportMessage],
commentsMessage]];
// Add the request for email address.
[self setEmailMessage:
NSLocalizedStringFromTableInBundle(@"emailMsg", nil, bundle, @"")];
// Run the alert
buttonPressed = [self runModalWindow:alertWindow withTimeout:timeout];
// Extract info from the user into the parameters_ dictionary
if ([self commentsValue]) {
[parameters_ setObject:[self commentsValue]
forKey:@BREAKPAD_COMMENTS];
}
if ([self emailValue]) {
[parameters_ setObject:[self emailValue]
forKey:@BREAKPAD_EMAIL];
}
}
} else {
// Create an alert panel to tell the user something happened
NSPanel* alert = NSGetAlertPanel([self headerMessage],
[self reportMessage],
defaultButtonTitle,
otherButtonTitle, nil);
// Pop the alert with an automatic timeout, and wait for the response
buttonPressed = [self runModalWindow:alert withTimeout:timeout];
// Release the panel memory
NSReleaseAlertPanel(alert);
}
return buttonPressed == NSAlertDefaultReturn;
}
- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout {
// Queue a |stopModal| message to be performed in |timeout| seconds.
[NSApp performSelector:@selector(stopModal)
withObject:nil
afterDelay:timeout];
// Run the window modally and wait for either a |stopModal| message or a
// button click.
[NSApp activateIgnoringOtherApps:YES];
int returnMethod = [NSApp runModalForWindow:window];
// Cancel the pending |stopModal| message.
if (returnMethod != NSRunStoppedResponse) {
[NSObject cancelPreviousPerformRequestsWithTarget:NSApp
selector:@selector(stopModal)
object:nil];
}
return returnMethod;
}
- (IBAction)sendReport:(id)sender {
[alertWindow orderOut:self];
// Use NSAlertDefaultReturn so that the return value of |runModalWithWindow|
// matches the AppKit function NSRunAlertPanel()
[NSApp stopModalWithCode:NSAlertDefaultReturn];
}
// UI Button Actions
//=============================================================================
- (IBAction)cancel:(id)sender {
[alertWindow orderOut:self];
// Use NSAlertDefaultReturn so that the return value of |runModalWithWindow|
// matches the AppKit function NSRunAlertPanel()
[NSApp stopModalWithCode:NSAlertAlternateReturn];
}
- (IBAction)showPrivacyPolicy:(id)sender {
// Get the localized privacy policy URL and open it in the default browser.
NSURL* privacyPolicyURL =
[NSURL URLWithString:NSLocalizedStringFromTableInBundle(
@"privacyPolicyURL", nil, [NSBundle mainBundle], @"")];
[[NSWorkspace sharedWorkspace] openURL:privacyPolicyURL];
}
// Text Field Delegate Methods
//=============================================================================
- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView
doCommandBySelector:(SEL)commandSelector {
BOOL result = NO;
// If the user has entered text, don't end editing on "return"
if (commandSelector == @selector(insertNewline:)
&& [[textView string] length] > 0) {
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
return result;
}
// Accessors
//=============================================================================
- (NSString *)headerMessage {
return [[headerMessage_ retain] autorelease];
}
- (void)setHeaderMessage:(NSString *)value {
if (headerMessage_ != value) {
[headerMessage_ autorelease];
headerMessage_ = [value copy];
}
}
- (NSString *)reportMessage {
return [[reportMessage_ retain] autorelease];
}
- (void)setReportMessage:(NSString *)value {
if (reportMessage_ != value) {
[reportMessage_ autorelease];
reportMessage_ = [value copy];
}
}
- (NSString *)commentsValue {
return [[commentsValue_ retain] autorelease];
}
- (void)setCommentsValue:(NSString *)value {
if (commentsValue_ != value) {
[commentsValue_ autorelease];
commentsValue_ = [value copy];
}
}
- (NSString *)emailMessage {
return [[emailMessage_ retain] autorelease];
}
- (void)setEmailMessage:(NSString *)value {
if (emailMessage_ != value) {
[emailMessage_ autorelease];
emailMessage_ = [value copy];
}
}
- (NSString *)emailValue {
return [[emailValue_ retain] autorelease];
}
- (void)setEmailValue:(NSString *)value {
if (emailValue_ != value) {
[emailValue_ autorelease];
emailValue_ = [value copy];
}
}
//=============================================================================
- (BOOL)shouldSubmitReport {
float interval = [[parameters_ objectForKey:@BREAKPAD_REPORT_INTERVAL]
floatValue];
NSString *program = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *programDict =
[NSMutableDictionary dictionaryWithDictionary:[ud dictionaryForKey:program]];
NSNumber *lastTimeNum = [programDict objectForKey:kLastSubmission];
NSTimeInterval lastTime = lastTimeNum ? [lastTimeNum floatValue] : 0;
NSTimeInterval now = CFAbsoluteTimeGetCurrent();
NSTimeInterval spanSeconds = (now - lastTime);
[programDict setObject:[NSNumber numberWithFloat:now] forKey:kLastSubmission];
[ud setObject:programDict forKey:program];
[ud synchronize];
// If we've specified an interval and we're within that time, don't ask the
// user if we should report
GTMLoggerDebug(@"Reporter Interval: %f", interval);
if (interval > spanSeconds) {
GTMLoggerDebug(@"Within throttling interval, not sending report");
return NO;
}
return YES;
}
//=============================================================================
- (void)report {
NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]];
HTTPMultipartUpload *upload = [[HTTPMultipartUpload alloc] initWithURL:url];
NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary];
// Set the known parameters. This should be kept up to date with the
// parameters defined in the Breakpad.h list of parameters. The intent
// is so that if there's a parameter that's not in this list, we consider
// it to be a "user-defined" parameter and we'll upload it to the server.
NSSet *knownParameters =
[NSSet setWithObjects:@kReporterMinidumpDirectoryKey,
@kReporterMinidumpIDKey, @BREAKPAD_PRODUCT_DISPLAY,
@BREAKPAD_PRODUCT, @BREAKPAD_VERSION, @BREAKPAD_URL,
@BREAKPAD_REPORT_INTERVAL, @BREAKPAD_SKIP_CONFIRM,
@BREAKPAD_SEND_AND_EXIT, @BREAKPAD_REPORTER_EXE_LOCATION,
@BREAKPAD_INSPECTOR_LOCATION, @BREAKPAD_LOGFILES,
@BREAKPAD_LOGFILE_UPLOAD_SIZE, @BREAKPAD_EMAIL,
@BREAKPAD_REQUEST_COMMENTS, @BREAKPAD_COMMENTS,
@BREAKPAD_VENDOR, nil];
// Add parameters
[uploadParameters setObject:[parameters_ objectForKey:@BREAKPAD_PRODUCT]
forKey:@"prod"];
[uploadParameters setObject:[parameters_ objectForKey:@BREAKPAD_VERSION]
forKey:@"ver"];
if ([parameters_ objectForKey:@BREAKPAD_EMAIL]) {
[uploadParameters setObject:[parameters_ objectForKey:@BREAKPAD_EMAIL] forKey:@"email"];
}
NSString* comments = [parameters_ objectForKey:@BREAKPAD_COMMENTS];
if (comments != nil) {
[uploadParameters setObject:comments forKey:@"comments"];
}
// Add any user parameters
NSArray *parameterKeys = [parameters_ allKeys];
int keyCount = [parameterKeys count];
for (int i = 0; i < keyCount; ++i) {
NSString *key = [parameterKeys objectAtIndex:i];
if (![knownParameters containsObject:key] &&
![key hasPrefix:@BREAKPAD_LOGFILE_KEY_PREFIX])
[uploadParameters setObject:[parameters_ objectForKey:key] forKey:key];
}
[upload setParameters:uploadParameters];
// Add minidump file
if (minidumpContents_) {
[upload addFileContents:minidumpContents_ name:@"upload_file_minidump"];
// Send it
NSError *error = nil;
NSData *data = [upload send:&error];
NSString *result = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
const char *reportID = "ERR";
if (error)
fprintf(stderr, "Breakpad Reporter: Send Error: %s\n",
[[error description] UTF8String]);
else
reportID = [result UTF8String];
// rename the minidump file according to the id returned from the server
NSString *minidumpDir = [parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey];
NSString *srcString = [NSString stringWithFormat:@"%@/%@.dmp",
minidumpDir, minidumpID];
NSString *destString = [NSString stringWithFormat:@"%@/%s.dmp",
minidumpDir, reportID];
const char *src = [srcString fileSystemRepresentation];
const char *dest = [destString fileSystemRepresentation];
if (rename(src, dest) == 0) {
fprintf(stderr, "Breakpad Reporter: Renamed %s to %s after successful " \
"upload\n",src, dest);
}
else {
// can't rename - don't worry - it's not important for users
fprintf(stderr, "Breakpad Reporter: successful upload report ID = %s\n",
reportID );
}
[result release];
}
if (logFileData_) {
HTTPMultipartUpload *logUpload = [[HTTPMultipartUpload alloc] initWithURL:url];
[uploadParameters setObject:@"log" forKey:@"type"];
[logUpload setParameters:uploadParameters];
[logUpload addFileContents:logFileData_ name:@"log"];
NSError *error = nil;
NSData *data = [logUpload send:&error];
NSString *result = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
[result release];
[logUpload release];
}
[upload release];
}
//=============================================================================
- (void)dealloc {
[parameters_ release];
[minidumpContents_ release];
[logFileData_ release];
[super dealloc];
}
@end
//=============================================================================
int main(int argc, const char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// The expectation is that there will be one argument which is the path
// to the configuration file
if (argc != 2) {
exit(1);
}
// Open the file before (potentially) switching to console user
int configFile = open(argv[1], O_RDONLY, 0600);
// we want to avoid a build-up of old config files even if they
// have been incorrectly written by the framework
unlink(argv[1]);
if (configFile == -1) {
exit(1);
}
Reporter *reporter = [[Reporter alloc] initWithConfigurationFD:configFile];
// Gather the configuration data
if (![reporter readConfigurationData]) {
exit(1);
}
// Read the minidump into memory before we (potentially) switch from the
// root user
[reporter readMinidumpData];
[reporter readLogFileData];
// only submit a report if we have not recently crashed in the past
BOOL shouldSubmitReport = [reporter shouldSubmitReport];
BOOL okayToSend = NO;
// ask user if we should send
if (shouldSubmitReport) {
okayToSend = [reporter askUserPermissionToSend:shouldSubmitReport];
}
// If we're running as root, switch over to nobody
if (getuid() == 0 || geteuid() == 0) {
struct passwd *pw = getpwnam("nobody");
// If we can't get a non-root uid, don't send the report
if (!pw)
exit(0);
if (setgid(pw->pw_gid) == -1)
exit(0);
if (setuid(pw->pw_uid) == -1)
exit(0);
}
if (okayToSend && shouldSubmitReport) {
[reporter report];
}
// Cleanup
[reporter release];
[pool release];
return 0;
}

View File

@@ -0,0 +1,65 @@
// Copyright (c) 2006, 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 <Cocoa/Cocoa.h>
#import <Breakpad/Breakpad.h>
enum BreakpadForkBehavior {
DONOTHING = 0,
UNINSTALL,
RESETEXCEPTIONPORT
};
enum BreakpadForkTestCrashPoint {
DURINGLAUNCH = 5,
AFTERLAUNCH = 6,
BETWEENFORKEXEC = 7
};
@interface Controller : NSObject {
IBOutlet NSWindow *window_;
IBOutlet NSWindow *forkTestOptions_;
BreakpadRef breakpad_;
enum BreakpadForkBehavior bpForkOption;
BOOL useVFork;
enum BreakpadForkTestCrashPoint progCrashPoint;
}
- (IBAction)crash:(id)sender;
- (IBAction)forkTestOptions:(id)sender;
- (IBAction)forkTestGo:(id)sender;
- (IBAction)showForkTestWindow:(id) sender;
- (void)generateReportWithoutCrash:(id)sender;
- (void)awakeFromNib;
@end

View File

@@ -0,0 +1,257 @@
// Copyright (c) 2006, 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 <Breakpad/Breakpad.h>
#import "Controller.h"
#import "TestClass.h"
#include <unistd.h>
#include <mach/mach.h>
@implementation Controller
- (void)causeCrash {
float *aPtr = nil;
NSLog(@"Crash!");
NSLog(@"Bad programmer: %f", *aPtr);
}
- (void)generateReportWithoutCrash:(id)sender {
BreakpadGenerateAndSendReport(breakpad_);
}
- (IBAction)showForkTestWindow:(id) sender {
[forkTestOptions_ setIsVisible:YES];
}
- (IBAction)forkTestOptions:(id)sender {
int tag = [[sender selectedCell] tag];
NSLog(@"sender tag: %d", tag);
if (tag <= 2) {
bpForkOption = tag;
}
if (tag == 3) {
useVFork = NO;
}
if (tag == 4) {
useVFork = YES;
}
if (tag >= 5 && tag <= 7) {
progCrashPoint = tag;
}
}
- (IBAction)forkTestGo:(id)sender {
NSString *resourcePath = [[NSBundle bundleForClass:
[self class]] resourcePath];
NSString *execProgname;
if (progCrashPoint == DURINGLAUNCH) {
execProgname = [resourcePath stringByAppendingString:@"/crashduringload"];
} else if (progCrashPoint == AFTERLAUNCH) {
execProgname = [resourcePath stringByAppendingString:@"/crashInMain"];
}
const char *progName = NULL;
if (progCrashPoint != BETWEENFORKEXEC) {
progName = [execProgname UTF8String];
}
int pid;
if (bpForkOption == UNINSTALL) {
BreakpadRelease(breakpad_);
}
if (useVFork) {
pid = vfork();
} else {
pid = fork();
}
if (pid == 0) {
sleep(3);
NSLog(@"Child continuing");
FILE *fd = fopen("/tmp/childlog.txt","wt");
kern_return_t kr;
if (bpForkOption == RESETEXCEPTIONPORT) {
kr = task_set_exception_ports(mach_task_self(),
EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION |
EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT,
MACH_PORT_NULL,
EXCEPTION_DEFAULT,
THREAD_STATE_NONE);
fprintf(fd,"task_set_exception_ports returned %d\n", kr);
}
if (progCrashPoint == BETWEENFORKEXEC) {
fprintf(fd,"crashing post-fork\n");
int *a = NULL;
printf("%d\n",*a++);
}
fprintf(fd,"about to call exec with %s\n", progName);
fclose(fd);
int i = execl(progName, progName, NULL);
fprintf(fd, "exec returned! %d\n", i);
fclose(fd);
}
}
- (IBAction)crash:(id)sender {
int tag = [sender tag];
if (tag == 1) {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:@selector(causeCrash) withObject:nil afterDelay:10];
[sender setState:NSOnState];
return;
}
if (tag == 2 && breakpad_) {
BreakpadRelease(breakpad_);
breakpad_ = NULL;
return;
}
[self causeCrash];
}
- (void)anotherThread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
TestClass *tc = [[TestClass alloc] init];
[tc wait];
[pool release];
}
- (void)awakeFromNib {
NSBundle *bundle = [NSBundle mainBundle];
NSDictionary *info = [bundle infoDictionary];
breakpad_ = BreakpadCreate(info);
// Do some unit tests with keys
// first a series of bogus values
BreakpadSetKeyValue(breakpad_, nil, @"bad2");
BreakpadSetKeyValue(nil, @"bad3", @"bad3");
// Now some good ones
BreakpadSetKeyValue(breakpad_,@"key1", @"value1");
BreakpadSetKeyValue(breakpad_,@"key2", @"value2");
BreakpadSetKeyValue(breakpad_,@"key3", @"value3");
// Look for a bogus one that we didn't try to set
NSString *test = BreakpadKeyValue(breakpad_, @"bad4");
if (test) {
NSLog(@"Bad BreakpadKeyValue (bad4)");
}
// Look for a bogus one we did try to set
test = BreakpadKeyValue(breakpad_, @"bad1");
if (test) {
NSLog(@"Bad BreakpadKeyValue (bad1)");
}
// Test some bad args for BreakpadKeyValue
test = BreakpadKeyValue(nil, @"bad5");
if (test) {
NSLog(@"Bad BreakpadKeyValue (bad5)");
}
test = BreakpadKeyValue(breakpad_, nil);
if (test) {
NSLog(@"Bad BreakpadKeyValue (nil)");
}
// Find some we did set
test = BreakpadKeyValue(breakpad_, @"key1");
if (![test isEqualToString:@"value1"]) {
NSLog(@"Can't find BreakpadKeyValue (key1)");
}
test = BreakpadKeyValue(breakpad_, @"key2");
if (![test isEqualToString:@"value2"]) {
NSLog(@"Can't find BreakpadKeyValue (key2)");
}
test = BreakpadKeyValue(breakpad_, @"key3");
if (![test isEqualToString:@"value3"]) {
NSLog(@"Can't find BreakpadKeyValue (key3)");
}
// Bad args for BreakpadRemoveKeyValue
BreakpadRemoveKeyValue(nil, @"bad6");
BreakpadRemoveKeyValue(breakpad_, nil);
// Remove one that is valid
BreakpadRemoveKeyValue(breakpad_, @"key3");
// Try and find it
test = BreakpadKeyValue(breakpad_, @"key3");
if (test) {
NSLog(@"Shouldn't find BreakpadKeyValue (key3)");
}
// Try and remove it again
BreakpadRemoveKeyValue(breakpad_, @"key3");
// Try removal by setting to nil
BreakpadSetKeyValue(breakpad_,@"key2", nil);
// Try and find it
test = BreakpadKeyValue(breakpad_, @"key2");
if (test) {
NSLog(@"Shouldn't find BreakpadKeyValue (key2)");
}
[NSThread detachNewThreadSelector:@selector(anotherThread)
toTarget:self withObject:nil];
NSUserDefaults *args = [NSUserDefaults standardUserDefaults];
// If the user specified autocrash on the command line, toggle
// Breakpad to not confirm and crash immediately. This is for
// automated testing.
if ([args boolForKey:@"autocrash"]) {
BreakpadSetKeyValue(breakpad_,
@BREAKPAD_SKIP_CONFIRM,
@"YES");
[self causeCrash];
}
progCrashPoint = DURINGLAUNCH;
[window_ center];
[window_ makeKeyAndOrderFront:self];
}
@end

Binary file not shown.

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBClasses</key>
<array>
<dict>
<key>ACTIONS</key>
<dict>
<key>crash</key>
<string>id</string>
<key>forkTestGo</key>
<string>id</string>
<key>forkTestOptions</key>
<string>id</string>
<key>generateReportWithoutCrash</key>
<string>id</string>
<key>showForkTestWindow</key>
<string>id</string>
</dict>
<key>CLASS</key>
<string>Controller</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>OUTLETS</key>
<dict>
<key>forkTestOptions_</key>
<string>NSWindow</string>
<key>window_</key>
<string>NSWindow</string>
</dict>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
<dict>
<key>CLASS</key>
<string>FirstResponder</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
</array>
<key>IBVersion</key>
<string>1</string>
</dict>
</plist>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBFramework Version</key>
<string>670</string>
<key>IBLastKnownRelativeProjectPath</key>
<string>../GoogleBreakpadTest.xcodeproj</string>
<key>IBOldestOS</key>
<integer>5</integer>
<key>IBOpenObjects</key>
<array>
<integer>221</integer>
<integer>29</integer>
<integer>2</integer>
</array>
<key>IBSystem Version</key>
<string>9F33</string>
<key>targetFramework</key>
<string>IBCocoaFramework</string>
</dict>
</plist>

Binary file not shown.

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>bomb</string>
<key>CFBundleIdentifier</key>
<string>com.Google.BreakpadTest</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>BreakpadProductDisplay</key>
<string>Google Breakpad Tester</string>
<key>BreakpadProduct</key>
<string>Breakpad_Tester</string>
<key>BreakpadVersion</key>
<string>1.2.3.4</string>
<key>BreakpadReportInterval</key>
<string>10</string>
<key>BreakpadSkipConfirm</key>
<string>NO</string>
<key>BreakpadSendAndExit</key>
<string>YES</string>
<key>BreakpadRequestComments</key>
<string>YES</string>
<key>BreakpadVendor</key>
<string>Foo Bar Corp, Incorporated, LTD, LLC</string>
<key>LSUIElement</key>
<string>1</string>
</dict>
</plist>

View File

@@ -0,0 +1,37 @@
// Copyright (c) 2006, 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 <Cocoa/Cocoa.h>
@interface TestClass : NSObject {
}
- (void)wait;
@end

View File

@@ -0,0 +1,95 @@
// Copyright (c) 2006, 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 <unistd.h>
#import "TestClass.h"
struct AStruct {
int x;
float y;
double z;
};
class InternalTestClass {
public:
InternalTestClass(int a) : a_(a) {}
~InternalTestClass() {}
void snooze(float a);
void snooze(int a);
int snooze(int a, float b);
protected:
int a_;
AStruct s_;
static void InternalFunction(AStruct &s);
static float kStaticFloatValue;
};
void InternalTestClass::snooze(float a) {
InternalFunction(s_);
sleep(a_ * a);
}
void InternalTestClass::snooze(int a) {
InternalFunction(s_);
sleep(a_ * a);
}
int InternalTestClass::snooze(int a, float b) {
InternalFunction(s_);
sleep(a_ * a * b);
return 33;
}
void InternalTestClass::InternalFunction(AStruct &s) {
s.x = InternalTestClass::kStaticFloatValue;
}
float InternalTestClass::kStaticFloatValue = 42;
static float PlainOldFunction() {
return 3.14145;
}
@implementation TestClass
- (void)wait {
InternalTestClass t(10);
float z = PlainOldFunction();
while (1) {
t.snooze(z);
}
}
@end

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,34 @@
// Copyright (c) 2006, 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 <Cocoa/Cocoa.h>
int main(int argc, char *argv[]) {
return NSApplicationMain(argc, (const char **) argv);
}

View File

@@ -0,0 +1,40 @@
// Copyright (c) 2008, 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 <GTMSenTestCase.h>
#import "SimpleStringDictionary.h"
@interface SimpleStringDictionaryTest : GTMTestCase {
}
- (void)testKeyValueEntry;
- (void)testSimpleStringDictionary;
- (void)testSimpleStringDictionaryIterator;
@end

View File

@@ -0,0 +1,243 @@
// Copyright (c) 2008, 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 "SimpleStringDictionaryTest.h"
#import "SimpleStringDictionary.h"
using google_breakpad::KeyValueEntry;
using google_breakpad::SimpleStringDictionary;
using google_breakpad::SimpleStringDictionaryIterator;
@implementation SimpleStringDictionaryTest
//==============================================================================
- (void)testKeyValueEntry {
KeyValueEntry entry;
// Verify that initial state is correct
STAssertFalse(entry.IsActive(), @"Initial key value entry is active!");
STAssertEquals(strlen(entry.GetKey()), (size_t)0, @"Empty key value did not "
@"have length 0");
STAssertEquals(strlen(entry.GetValue()), (size_t)0, @"Empty key value did not "
@"have length 0");
// Try setting a key/value and then verify
entry.SetKeyValue("key1", "value1");
STAssertEqualCStrings(entry.GetKey(), "key1", @"key was not equal to key1");
STAssertEqualCStrings(entry.GetValue(), "value1", @"value was not equal");
// Try setting a new value
entry.SetValue("value3");
// Make sure the new value took
STAssertEqualCStrings(entry.GetValue(), "value3", @"value was not equal");
// Make sure the key didn't change
STAssertEqualCStrings(entry.GetKey(), "key1", @"key changed after setting "
@"value!");
// Try setting a new key/value and then verify
entry.SetKeyValue("key2", "value2");
STAssertEqualCStrings(entry.GetKey(), "key2", @"New key was not equal to "
@"key2");
STAssertEqualCStrings(entry.GetValue(), "value2", @"New value was not equal "
@"to value2");
// Clear the entry and verify the key and value are empty strings
entry.Clear();
STAssertFalse(entry.IsActive(), @"Key value clear did not clear object");
STAssertEquals(strlen(entry.GetKey()), (size_t)0, @"Length of cleared key "
@"was not 0");
STAssertEquals(strlen(entry.GetValue()), (size_t)0, @"Length of cleared "
@"value was not 0!");
}
- (void)testEmptyKeyValueCombos {
KeyValueEntry entry;
entry.SetKeyValue(NULL, NULL);
STAssertEqualCStrings(entry.GetKey(), "", @"Setting NULL key did not return "
@"empty key!");
STAssertEqualCStrings(entry.GetValue(), "", @"Setting NULL value did not "
@"set empty string value!");
}
//==============================================================================
- (void)testSimpleStringDictionary {
// Make a new dictionary
SimpleStringDictionary *dict = new SimpleStringDictionary();
STAssertTrue(dict != NULL, nil);
// try passing in NULL for key
//dict->SetKeyValue(NULL, "bad"); // causes assert() to fire
// Set three distinct values on three keys
dict->SetKeyValue("key1", "value1");
dict->SetKeyValue("key2", "value2");
dict->SetKeyValue("key3", "value3");
STAssertTrue(!strcmp(dict->GetValueForKey("key1"), "value1"), nil);
STAssertTrue(!strcmp(dict->GetValueForKey("key2"), "value2"), nil);
STAssertTrue(!strcmp(dict->GetValueForKey("key3"), "value3"), nil);
STAssertEquals(dict->GetCount(), 3, @"GetCount did not return 3");
// try an unknown key
STAssertTrue(dict->GetValueForKey("key4") == NULL, nil);
// try a NULL key
//STAssertTrue(dict->GetValueForKey(NULL) == NULL, nil); // asserts
// Remove a key
dict->RemoveKey("key3");
// Now make sure it's not there anymore
STAssertTrue(dict->GetValueForKey("key3") == NULL, nil);
// Remove a NULL key
//dict->RemoveKey(NULL); // will cause assert() to fire
// Remove by setting value to NULL
dict->SetKeyValue("key2", NULL);
// Now make sure it's not there anymore
STAssertTrue(dict->GetValueForKey("key2") == NULL, nil);
}
//==============================================================================
// The idea behind this test is to add a bunch of values to the dictionary,
// remove some in the middle, then add a few more in. We then create a
// SimpleStringDictionaryIterator and iterate through the dictionary, taking
// note of the key/value pairs we see. We then verify that it iterates
// through exactly the number of key/value pairs we expect, and that they
// match one-for-one with what we would expect. In all cases we're setting
// key value pairs of the form:
//
// key<n>/value<n> (like key0/value0, key17,value17, etc.)
//
- (void)testSimpleStringDictionaryIterator {
SimpleStringDictionary *dict = new SimpleStringDictionary();
STAssertTrue(dict != NULL, nil);
char key[KeyValueEntry::MAX_STRING_STORAGE_SIZE];
char value[KeyValueEntry::MAX_STRING_STORAGE_SIZE];
const int kDictionaryCapacity = SimpleStringDictionary::MAX_NUM_ENTRIES;
const int kPartitionIndex = kDictionaryCapacity - 5;
// We assume at least this size in the tests below
STAssertTrue(kDictionaryCapacity >= 64, nil);
// We'll keep track of the number of key/value pairs we think should
// be in the dictionary
int expectedDictionarySize = 0;
// Set a bunch of key/value pairs like key0/value0, key1/value1, ...
for (int i = 0; i < kPartitionIndex; ++i) {
sprintf(key, "key%d", i);
sprintf(value, "value%d", i);
dict->SetKeyValue(key, value);
}
expectedDictionarySize = kPartitionIndex;
// set a couple of the keys twice (with the same value) - should be nop
dict->SetKeyValue("key2", "value2");
dict->SetKeyValue("key4", "value4");
dict->SetKeyValue("key15", "value15");
// Remove some random elements in the middle
dict->RemoveKey("key7");
dict->RemoveKey("key18");
dict->RemoveKey("key23");
dict->RemoveKey("key31");
expectedDictionarySize -= 4; // we just removed four key/value pairs
// Set some more key/value pairs like key59/value59, key60/value60, ...
for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) {
sprintf(key, "key%d", i);
sprintf(value, "value%d", i);
dict->SetKeyValue(key, value);
}
expectedDictionarySize += kDictionaryCapacity - kPartitionIndex;
// Now create an iterator on the dictionary
SimpleStringDictionaryIterator iter(*dict);
// We then verify that it iterates through exactly the number of
// key/value pairs we expect, and that they match one-for-one with what we
// would expect. The ordering of the iteration does not matter...
// used to keep track of number of occurrences found for key/value pairs
int count[kDictionaryCapacity];
memset(count, 0, sizeof(count));
int totalCount = 0;
const KeyValueEntry *entry;
while ((entry = iter.Next())) {
totalCount++;
// Extract keyNumber from a string of the form key<keyNumber>
int keyNumber;
sscanf(entry->GetKey(), "key%d", &keyNumber);
// Extract valueNumber from a string of the form value<valueNumber>
int valueNumber;
sscanf(entry->GetValue(), "value%d", &valueNumber);
// The value number should equal the key number since that's how we set them
STAssertTrue(keyNumber == valueNumber, nil);
// Key and value numbers should be in proper range:
// 0 <= keyNumber < kDictionaryCapacity
bool isKeyInGoodRange =
(keyNumber >= 0 && keyNumber < kDictionaryCapacity);
bool isValueInGoodRange =
(valueNumber >= 0 && valueNumber < kDictionaryCapacity);
STAssertTrue(isKeyInGoodRange, nil);
STAssertTrue(isValueInGoodRange, nil);
if (isKeyInGoodRange && isValueInGoodRange) {
++count[keyNumber];
}
}
// Make sure each of the key/value pairs showed up exactly one time, except
// for the ones which we removed.
for (int i = 0; i < kDictionaryCapacity; ++i) {
// Skip over key7, key18, key23, and key31, since we removed them
if (!(i == 7 || i == 18 || i == 23 || i == 31)) {
STAssertTrue(count[i] == 1, nil);
}
}
// Make sure the number of iterations matches the expected dictionary size.
STAssertTrue(totalCount == expectedDictionarySize, nil);
}
@end