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

View File

@@ -126,17 +126,22 @@ static bool CompareFile(const char *path) {
0x00000007, 0x06000007, 0x00000008, 0x07000008, 0x00000009, 0x08000009,
0x0000000a, 0x0900000a, 0x0000000b, 0x00000000
#else
0x0000beef, 0x0000001e, 0x00000018, 0x00000020, 0x00000038, 0x00000000,
0x00000018, 0x00690046, 0x00730072, 0x00200074, 0x00740053, 0x00690072,
0x0067006e, 0x00000000, 0x0000001a, 0x00650053, 0x006f0063, 0x0064006e,
0x00530020, 0x00720074, 0x006e0069, 0x00000067, 0x0001da00, 0x00000002,
0x0002da01, 0x00000003, 0x0003da02, 0x00000004, 0x0004da03, 0x00000005,
0x0005da04, 0x00000006, 0x0006da05, 0x00000007, 0x0007da06, 0x00000008,
0x0008da07, 0x00000009, 0x0009da08, 0x0000000a, 0x000ada09, 0x0000000b,
0x0000000a, 0x00018700, 0x00000002, 0x00028701, 0x00000003, 0x00038702,
0x00000004, 0x00048703, 0x00000005, 0x00058704, 0x00000006, 0x00068705,
0x00000007, 0x00078706, 0x00000008, 0x00088707, 0x00000009, 0x00098708,
0x0000000a, 0x000a8709, 0x0000000b, 0x00000000,
0x0000beef, 0x0000001e, 0x00000018, 0x00000020,
0x00000038, 0x00000000, 0x00000018, 0x00690046,
0x00730072, 0x00200074, 0x00740053, 0x00690072,
0x0067006e, 0x00000000, 0x0000001a, 0x00650053,
0x006f0063, 0x0064006e, 0x00530020, 0x00720074,
0x006e0069, 0x00000067, 0x00011e00, 0x00000002,
0x00021e01, 0x00000003, 0x00031e02, 0x00000004,
0x00041e03, 0x00000005, 0x00051e04, 0x00000006,
0x00061e05, 0x00000007, 0x00071e06, 0x00000008,
0x00081e07, 0x00000009, 0x00091e08, 0x0000000a,
0x000a1e09, 0x0000000b, 0x0000000a, 0x00011c00,
0x00000002, 0x00021c01, 0x00000003, 0x00031c02,
0x00000004, 0x00041c03, 0x00000005, 0x00051c04,
0x00000006, 0x00061c05, 0x00000007, 0x00071c06,
0x00000008, 0x00081c07, 0x00000009, 0x00091c08,
0x0000000a, 0x000a1c09, 0x0000000b, 0x00000000,
#endif
};
unsigned int expected_byte_count = sizeof(expected);
@@ -156,7 +161,6 @@ static bool CompareFile(const char *path) {
printf("%d\n",b1 - (char*)buffer);
ASSERT_EQ(memcmp(buffer, expected, expected_byte_count), 0);
return true;
}