Add support for Google breakpad on mac. (Uploading crash reports doesn't work yet due to redirect).

This commit is contained in:
John Maguire 2011-03-25 11:38:03 +00:00
parent d50cfaed46
commit d86dce4b96
174 changed files with 56851 additions and 22 deletions

View File

@ -2,19 +2,35 @@ cmake_minimum_required(VERSION 2.6)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
set(SOURCES
client/linux/crash_generation/crash_generation_client.cc
client/linux/handler/exception_handler.cc
client/linux/minidump_writer/linux_dumper.cc
client/linux/minidump_writer/minidump_writer.cc
client/minidump_file_writer.cc
common/convert_UTF.c
common/md5.c
common/string_conversion.cc
common/linux/file_id.cc
common/linux/guid_creator.cc
)
if (LINUX)
set(SOURCES
client/linux/crash_generation/crash_generation_client.cc
client/linux/handler/exception_handler.cc
client/linux/minidump_writer/linux_dumper.cc
client/linux/minidump_writer/minidump_writer.cc
client/minidump_file_writer.cc
common/convert_UTF.c
common/md5.c
common/string_conversion.cc
common/linux/file_id.cc
common/linux/guid_creator.cc
)
ADD_LIBRARY(breakpad STATIC
${SOURCES}
)
ADD_LIBRARY(breakpad STATIC
${SOURCES}
)
endif (LINUX)
if (APPLE)
add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/tools/mac/dump_syms/build/Release/dump_syms
COMMAND xcodebuild -target dump_syms -configuration Release -sdk macosx10.5 ARCHS=i386
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tools/mac/dump_syms)
add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/client/mac/build/Release/Breakpad.Framework
COMMAND xcodebuild -target Breakpad -configuration Release -sdk macosx10.5 ARCHS=i386
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/client/mac)
add_custom_target(breakpad
DEPENDS tools/mac/dump_syms/build/Release/dump_syms
client/mac/build/Release/Breakpad.Framework)
endif (APPLE)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,316 @@
// 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"
// Filename for recording uploaded IDs
#define kReporterLogFilename "uploads.log"
// The default subdirectory of the Library to put crash dumps in
// The subdirectory is
// ~/Library/<kDefaultLibrarySubdirectory>/<GoogleBreakpadProduct>
#define kDefaultLibrarySubdirectory "Breakpad"
// 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 "BreakpadProduct"
#define BREAKPAD_PRODUCT_DISPLAY "BreakpadProductDisplay"
#define BREAKPAD_VERSION "BreakpadVersion"
#define BREAKPAD_VENDOR "BreakpadVendor"
#define BREAKPAD_URL "BreakpadURL"
#define BREAKPAD_REPORT_INTERVAL "BreakpadReportInterval"
#define BREAKPAD_SKIP_CONFIRM "BreakpadSkipConfirm"
#define BREAKPAD_CONFIRM_TIMEOUT "BreakpadConfirmTimeout"
#define BREAKPAD_SEND_AND_EXIT "BreakpadSendAndExit"
#define BREAKPAD_DUMP_DIRECTORY "BreakpadMinidumpLocation"
#define BREAKPAD_INSPECTOR_LOCATION "BreakpadInspectorLocation"
#define BREAKPAD_REPORTER_EXE_LOCATION \
"BreakpadReporterExeLocation"
#define BREAKPAD_LOGFILES "BreakpadLogFiles"
#define BREAKPAD_LOGFILE_UPLOAD_SIZE "BreakpadLogFileTailSize"
#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments"
#define BREAKPAD_COMMENTS "BreakpadComments"
#define BREAKPAD_REQUEST_EMAIL "BreakpadRequestEmail"
#define BREAKPAD_EMAIL "BreakpadEmail"
#define BREAKPAD_SERVER_TYPE "BreakpadServerType"
#define BREAKPAD_SERVER_PARAMETER_DICT "BreakpadServerParameters"
// The keys below are NOT user supplied, and are used internally.
#define BREAKPAD_PROCESS_START_TIME "BreakpadProcStartTime"
#define BREAKPAD_PROCESS_UP_TIME "BreakpadProcessUpTime"
#define BREAKPAD_PROCESS_CRASH_TIME "BreakpadProcessCrashTime"
#define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile"
#define BREAKPAD_SERVER_PARAMETER_PREFIX "BreakpadServerParameterPrefix_"
#define BREAKPAD_ON_DEMAND "BreakpadOnDemand"
// 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,
void *context);
// Create a new BreakpadRef object and install it as an exception
// handler. The |parameters| will typically be the contents of your
// bundle's Info.plist.
//
// You can also specify these additional keys for customizable behavior:
// Key: Value:
// BREAKPAD_PRODUCT Product name (e.g., "MyAwesomeProduct")
// This one is used as the key to identify
// the product when uploading. Falls back to
// CFBundleName if not specified.
// REQUIRED
//
// BREAKPAD_PRODUCT_DISPLAY This is the display name, e.g. a pretty
// name for the product when the crash_sender
// pops up UI for the user. Falls back first to
// CFBundleDisplayName and then to
// BREAKPAD_PRODUCT if not specified.
//
// BREAKPAD_VERSION Product version (e.g., 1.2.3), used
// as metadata for crash report. Falls back to
// CFBundleVersion if not specified.
// REQUIRED
//
// BREAKPAD_VENDOR Vendor name, used in UI (e.g. "A report has
// been created that you can send to <vendor>")
//
// BREAKPAD_URL URL destination for reporting
// REQUIRED
//
// BREAKPAD_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.
// Defaults to NO
//
// BREAKPAD_CONFIRM_TIMEOUT Number of seconds before the upload
// confirmation dialog will be automatically
// dismissed (cancelling the upload).
// Default: 300 seconds (min of 60).
// Specify 0 to prevent timeout.
//
// 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.
// Defaults TO YES
//
// BREAKPAD_DUMP_DIRECTORY The directory to store crash-dumps
// in. By default, we use
// ~/Library/Breakpad/<BREAKPAD_PRODUCT>
// The path you specify here is tilde-expanded.
//
// BREAKPAD_INSPECTOR_LOCATION The full path to the Inspector executable.
// Defaults to <Framework resources>/Inspector
//
// BREAKPAD_REPORTER_EXE_LOCATION The full path to the Reporter/sender
// executable.
// Default:
// <Framework Resources>/crash_report_sender.app
//
// 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.
// Default: NO
//
// BREAKPAD_REQUEST_EMAIL If true and BREAKPAD_REQUEST_COMMENTS is also
// true, the message dialog will have a text
// box for the user to enter their email address.
// Default: NO
//
// BREAKPAD_SERVER_TYPE A parameter that tells Breakpad how to
// rewrite the upload parameters for a specific
// server type. The currently valid values are
// 'socorro' or 'google'. If you want to add
// other types, see the function in
// crash_report_sender.m that maps parameters to
// URL parameters. Defaults to 'google'.
//
// BREAKPAD_SERVER_PARAMETER_DICT A plist dictionary of static
// parameters that are uploaded to the
// server. The parameters are sent as
// is to the crash server. Their
// content isn't added to the minidump
// but pass as URL parameters when
// uploading theminidump to the crash
// server.
//=============================================================================
// The BREAKPAD_PRODUCT, BREAKPAD_VERSION and BREAKPAD_URL are
// required to have non-NULL values. By default, the BREAKPAD_PRODUCT
// will be the CFBundleName and the BREAKPAD_VERSION will be the
// CFBundleVersion when these keys are present in the bundle's
// Info.plist, which is usually passed in to BreakpadCreate() as an
// NSDictionary (you could also pass in another dictionary that had
// the same keys configured). If the BREAKPAD_PRODUCT or
// BREAKPAD_VERSION are ultimately undefined, BreakpadCreate() will
// fail. You have been warned.
//
// If you are running in a debugger, Breakpad will not install, unless the
// BREAKPAD_IGNORE_DEBUGGER envionment variable is set and/or non-zero.
//
// The 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.
//
//=============================================================================
// The following are NOT user-supplied but are documented here for
// completeness. They are calculated by Breakpad during initialization &
// crash-dump generation, or entered in by the user.
//
// BREAKPAD_PROCESS_START_TIME The time the process started.
//
// BREAKPAD_PROCESS_CRASH_TIME The time the process crashed.
//
// BREAKPAD_PROCESS_UP_TIME The total time the process has been
// running. This parameter is not set
// until the crash-dump-generation phase.
//
// BREAKPAD_LOGFILE_KEY_PREFIX Used to find out which parameters in the
// parameter dictionary correspond to log
// file paths.
//
// BREAKPAD_SERVER_PARAMETER_PREFIX This prefix is used by Breakpad
// internally, because Breakpad uses
// the same dictionary internally to
// track both its internal
// configuration parameters and
// parameters meant to be uploaded
// to the server. This string is
// used internally by Breakpad to
// prefix user-supplied parameter
// names so those can be sent to the
// server without leaking Breakpad's
// internal values.
//
// BREAKPAD_ON_DEMAND Used internally to indicate to the
// Reporter that we're sending on-demand,
// not as result of a crash.
//
// BREAKPAD_COMMENTS The text the user provided as comments.
// Only used in crash_report_sender.
// 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).
// Context is a pointer to arbitrary data to make the callback with.
void BreakpadSetFilterCallback(BreakpadRef ref,
BreakpadFilterCallback callback,
void *context);
// User defined key and value string storage. Generally this is used
// to configure Breakpad's internal operation, such as whether the
// crash_sender should prompt the user, or the filesystem location for
// the minidump file. See Breakpad.h for some parameters that can be
// set. Anything longer than 255 bytes will be truncated. Note that
// the string is converted to UTF8 before truncation, so any multibyte
// character that straddles the 255(256 - 1 for terminator) byte limit
// will be mangled.
//
// A maximum number of 64 key/value pairs are supported. An assert()
// will fire if more than this number are set. Unfortunately, right
// now, the same dictionary is used for both Breakpad's parameters AND
// the Upload parameters.
//
// TODO (nealsid): Investigate how necessary this is if we don't
// automatically upload parameters to the server anymore.
// TODO (nealsid): separate server parameter dictionary from the
// dictionary used to configure Breakpad, and document limits for each
// independently.
void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value);
NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key);
void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key);
// You can use this method to specify parameters that will be uploaded
// to the crash server. They will be automatically encoded as
// necessary. Note that as mentioned above there are limits on both
// the number of keys and their length.
void BreakpadAddUploadParameter(BreakpadRef ref, NSString *key,
NSString *value);
// This method will remove a previously-added parameter from the
// upload parameter set.
void BreakpadRemoveUploadParameter(BreakpadRef ref, NSString *key);
// 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,996 @@
// 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 "common/mac/MachIPC.h"
#import "common/mac/SimpleStringDictionary.h"
#import "client/mac/crash_generation/Inspector.h"
#import "client/mac/handler/exception_handler.h"
#import "client/mac/Framework/Breakpad.h"
#import "client/mac/Framework/OnDemandServer.h"
#import "client/mac/handler/protected_memory_allocator.h"
#import <sys/stat.h>
#import <sys/sysctl.h>
#import <Foundation/Foundation.h>
using google_breakpad::KeyValueEntry;
using google_breakpad::MachPortSender;
using google_breakpad::MachReceiveMessage;
using google_breakpad::MachSendMessage;
using google_breakpad::ReceivePort;
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, void *context) {
filter_callback_ = callback;
filter_callback_context_ = context;
}
private:
Breakpad()
: handler_(NULL),
config_params_(NULL),
send_and_exit_(true),
filter_callback_(NULL),
filter_callback_context_(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,
int exception_subcode,
mach_port_t crashing_thread);
bool HandleException(int exception_type,
int exception_code,
int exception_subcode,
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_;
void *filter_callback_context_;
};
#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,
int exception_subcode,
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,
exception_subcode,
crashing_thread);
}
//=============================================================================
#pragma mark -
#include <dlfcn.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
Dl_info info;
if (dladdr((const void*)GetResourcePath, &info) != 0) {
NSFileManager *filemgr = [NSFileManager defaultManager];
NSString *filePath =
[filemgr stringWithFileSystemRepresentation:info.dli_fname
length:strlen(info.dli_fname)];
NSString *bundlePath = [filePath stringByDeletingLastPathComponent];
// The "Resources" directory should be in the same directory as the
// executable code, since that's how the Breakpad framework is built.
resourcePath = [bundlePath stringByAppendingPathComponent:@"Resources/"];
} 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 *skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM];
NSString *sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT];
NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE];
NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
NSString *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL];
NSString *inspectorPathString =
[parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION];
NSString *reporterPathString =
[parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION];
NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT];
NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES];
NSString *logFileTailSize =
[parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE];
NSString *requestUserText =
[parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS];
NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL];
NSString *vendor =
[parameters objectForKey:@BREAKPAD_VENDOR];
NSString *dumpSubdirectory =
[parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
NSDictionary *serverParameters =
[parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
// These may have been set above as user prefs, which take priority.
if (!skipConfirm) {
skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM];
}
if (!sendAndExit) {
sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT];
}
if (!product)
product = [parameters objectForKey:@"CFBundleName"];
if (!display) {
display = [parameters objectForKey:@"CFBundleDisplayName"];
if (!display) {
display = product;
}
}
if (!version)
version = [parameters objectForKey:@"CFBundleVersion"];
if (!interval)
interval = @"3600";
if (!timeout)
timeout = @"300";
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;
}
if (!dumpSubdirectory) {
dumpSubdirectory = @"";
}
// The product, version, and URL are required values.
if (![product length]) {
DEBUGLOG(stderr, "Missing required product key.\n");
return false;
}
if (![version length]) {
DEBUGLOG(stderr, "Missing required version key.\n");
return false;
}
if (![urlStr length]) {
DEBUGLOG(stderr, "Missing required URL key.\n");
return false;
}
config_params_ =
new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
SimpleStringDictionary();
SimpleStringDictionary &dictionary = *config_params_;
dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]);
dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]);
dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]);
dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]);
dictionary.SetKeyValue(BREAKPAD_REPORT_INTERVAL, [interval UTF8String]);
dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM, [skipConfirm UTF8String]);
dictionary.SetKeyValue(BREAKPAD_CONFIRM_TIMEOUT, [timeout 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_REQUEST_EMAIL, [requestEmail UTF8String]);
dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]);
dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
[dumpSubdirectory UTF8String]);
struct timeval tv;
gettimeofday(&tv, NULL);
char timeStartedString[32];
sprintf(timeStartedString, "%zd", tv.tv_sec);
dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME,
timeStartedString);
if (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 (serverParameters) {
// For each key-value pair, call BreakpadAddUploadParameter()
NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
NSString *aParameter;
while ((aParameter = [keyEnumerator nextObject])) {
BreakpadAddUploadParameter(this, aParameter,
[serverParameters objectForKey:aParameter]);
}
}
return true;
}
//=============================================================================
void Breakpad::SetKeyValue(NSString *key, NSString *value) {
// We allow nil values. This is the same as removing the keyvalue.
if (!config_params_ || !key)
return;
config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
}
//=============================================================================
NSString *Breakpad::KeyValue(NSString *key) {
if (!config_params_ || !key)
return nil;
const char *value = config_params_->GetValueForKey([key UTF8String]);
return value ? [NSString stringWithUTF8String:value] : nil;
}
//=============================================================================
void Breakpad::RemoveKeyValue(NSString *key) {
if (!config_params_ || !key) return;
config_params_->RemoveKey([key UTF8String]);
}
//=============================================================================
void Breakpad::GenerateAndSendReport() {
config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES");
HandleException(0, 0, 0, mach_thread_self());
config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO");
}
//=============================================================================
bool Breakpad::HandleException(int exception_type,
int exception_code,
int exception_subcode,
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,
filter_callback_context_);
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.exception_subcode = exception_subcode;
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, 5000);
}
}
#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) {
// With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
// Let's round up to the nearest page size.
//
int breakpad_pool_size = 4096;
/*
sizeof(Breakpad)
+ sizeof(google_breakpad::ExceptionHandler)
+ sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
*/
gBreakpadAllocator =
new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
ProtectedMemoryAllocator(breakpad_pool_size);
// Stack-based autorelease pool for Breakpad::Create() obj-c code.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Breakpad *breakpad = Breakpad::Create(parameters);
if (breakpad) {
// Make read-only to protect against memory smashers
gMasterAllocator->Protect();
gKeyValueAllocator->Protect();
gBreakpadAllocator->Protect();
// Can uncomment this line to figure out how much space was actually
// allocated using this allocator
// printf("gBreakpadAllocator allocated size = %d\n",
// gBreakpadAllocator->GetAllocatedSize() );
[pool release];
return (BreakpadRef)breakpad;
}
[pool release];
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadCreate() : error\n");
}
if (gKeyValueAllocator) {
gKeyValueAllocator->~ProtectedMemoryAllocator();
gKeyValueAllocator = NULL;
}
if (gBreakpadAllocator) {
gBreakpadAllocator->~ProtectedMemoryAllocator();
gBreakpadAllocator = NULL;
}
delete gMasterAllocator;
gMasterAllocator = NULL;
return NULL;
}
//=============================================================================
void BreakpadRelease(BreakpadRef ref) {
try {
Breakpad *breakpad = (Breakpad *)ref;
if (gMasterAllocator) {
gMasterAllocator->Unprotect();
gKeyValueAllocator->Unprotect();
gBreakpadAllocator->Unprotect();
breakpad->~Breakpad();
// Unfortunately, it's not possible to deallocate this stuff
// because the exception handling thread is still finishing up
// asynchronously at this point... OK, it could be done with
// locks, etc. But since BreakpadRelease() should usually only
// be called right before the process exits, it's not worth
// deallocating this stuff.
#if 0
gKeyValueAllocator->~ProtectedMemoryAllocator();
gBreakpadAllocator->~ProtectedMemoryAllocator();
delete gMasterAllocator;
gMasterAllocator = NULL;
gKeyValueAllocator = NULL;
gBreakpadAllocator = NULL;
#endif
pthread_mutex_destroy(&gDictionaryMutex);
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadRelease() : error\n");
}
}
//=============================================================================
void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
try {
// Not called at exception time
Breakpad *breakpad = (Breakpad *)ref;
if (breakpad && key && gKeyValueAllocator) {
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
breakpad->SetKeyValue(key, value);
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadSetKeyValue() : error\n");
}
}
void BreakpadAddUploadParameter(BreakpadRef ref,
NSString *key,
NSString *value) {
// The only difference, internally, between an upload parameter and
// a key value one that is set with BreakpadSetKeyValue is that we
// prepend the keyname with a special prefix. This informs the
// crash sender that the parameter should be sent along with the
// POST of the crash dump upload.
try {
Breakpad *breakpad = (Breakpad *)ref;
if (breakpad && key && gKeyValueAllocator) {
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
stringByAppendingString:key];
breakpad->SetKeyValue(prefixedKey, value);
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadSetKeyValue() : error\n");
}
}
void BreakpadRemoveUploadParameter(BreakpadRef ref,
NSString *key) {
try {
// Not called at exception time
Breakpad *breakpad = (Breakpad *)ref;
if (breakpad && key && gKeyValueAllocator) {
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
@BREAKPAD_SERVER_PARAMETER_PREFIX, key];
breakpad->RemoveKeyValue(prefixedKey);
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
}
}
//=============================================================================
NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
NSString *value = nil;
try {
// Not called at exception time
Breakpad *breakpad = (Breakpad *)ref;
if (!breakpad || !key || !gKeyValueAllocator)
return nil;
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
value = breakpad->KeyValue(key);
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadKeyValue() : error\n");
}
return value;
}
//=============================================================================
void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
try {
// Not called at exception time
Breakpad *breakpad = (Breakpad *)ref;
if (breakpad && key && gKeyValueAllocator) {
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
breakpad->RemoveKeyValue(key);
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
}
}
//=============================================================================
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 exceptions leave this C API
fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n");
}
}
//=============================================================================
void BreakpadSetFilterCallback(BreakpadRef ref,
BreakpadFilterCallback callback,
void *context) {
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, context);
}
} catch(...) { // don't let exceptions 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.googlecode.google-breakpad</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_ = MACH_PORT_DEAD;
// 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,193 @@
// 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;
int exception_subcode;
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(const NSString *minidumpDir) {
// Ensure that the path exists. Fallback to /tmp if unable to locate path.
assert(minidumpDir);
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);
void SetCrashTimeParameters();
mach_port_t service_rcv_port_;
int exception_type_;
int exception_code_;
int exception_subcode_;
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,555 @@
// 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
#include <cstdio>
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <sys/time.h>
#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 "GTMDefines.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];
NSUInteger count = [components count];
// Rebuild the path one component at a time
NSDictionary *attrs =
[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750]
forKey:NSFilePosixPermissions];
path = common;
for (NSUInteger 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) {
DEBUGLOG(stderr,
"mkstemp(config_file_path_) == -1 (%s)\n",
strerror(errno));
return;
}
else {
DEBUGLOG(stderr, "Writing config file to (%s)\n", config_file_path_);
}
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())) {
DEBUGLOG(stderr,
"config: (%s) -> (%s)\n",
entry->GetKey(),
entry->GetValue());
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.
bool wrote_minidump = InspectTask();
// Send acknowledgement to the crashed process that the inspection
// has finished. It will then be able to cleanly exit.
// The return value is ignored because failure isn't fatal. If the process
// didn't get the message there's nothing we can do, and we still want to
// send the report.
SendAcknowledgement();
if (wrote_minidump) {
// Ask the user if he wants to upload the crash report to a server,
// and do so if he agrees.
LaunchReporter(config_file_.GetFilePath());
} else {
fprintf(stderr, "Inspection of crashed process failed\n");
}
// 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;
exception_subcode_ = info.exception_subcode;
#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("exception_subcode = %d\n", exception_subcode_);
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
// In certain situations where multiple crash requests come
// through quickly, we can end up with the mach IPC messages not
// coming through correctly. Since we don't know what parameters
// we've missed, we can't do much besides abort the crash dump
// situation in this case.
unsigned int parameters_read = 0;
// 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 parameter_message;
result = receive_port.WaitForMessage(&parameter_message, 1000);
if(result == KERN_SUCCESS) {
KeyValueMessageData &key_value_data =
(KeyValueMessageData&)*parameter_message.GetData();
// If we get a blank key, make sure we don't increment the
// parameter count; in some cases (notably on-demand generation
// many times in a short period of time) caused the Mach IPC
// messages to not come through correctly.
if (strlen(key_value_data.key) == 0) {
continue;
}
parameters_read++;
config_params_.SetKeyValue(key_value_data.key, key_value_data.value);
} else {
PRINT_MACH_RESULT(result, "Inspector: key/value message");
break;
}
}
if (parameters_read != info.parameter_count) {
DEBUGLOG(stderr, "Only read %d parameters instead of %d, aborting crash "
"dump generation.", parameters_read, info.parameter_count);
return KERN_FAILURE;
}
}
return result;
}
//=============================================================================
// Sets keys in the parameters dictionary that are specific to process uptime.
// The two we set are process up time, and process crash time.
void Inspector::SetCrashTimeParameters() {
// Set process uptime parameter
struct timeval tv;
gettimeofday(&tv, NULL);
char processUptimeString[32], processCrashtimeString[32];
const char *processStartTimeString =
config_params_.GetValueForKey(BREAKPAD_PROCESS_START_TIME);
// Set up time if we've received the start time.
if (processStartTimeString) {
time_t processStartTime = strtol(processStartTimeString, NULL, 10);
time_t processUptime = tv.tv_sec - processStartTime;
sprintf(processUptimeString, "%zd", processUptime);
config_params_.SetKeyValue(BREAKPAD_PROCESS_UP_TIME, processUptimeString);
}
sprintf(processCrashtimeString, "%zd", tv.tv_sec);
config_params_.SetKeyValue(BREAKPAD_PROCESS_CRASH_TIME,
processCrashtimeString);
}
bool Inspector::InspectTask() {
// keep the task quiet while we're looking at it
task_suspend(remote_task_);
DEBUGLOG(stderr, "Suspended Remote task\n");
NSString *minidumpDir;
const char *minidumpDirectory =
config_params_.GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
SetCrashTimeParameters();
// If the client app has not specified a minidump directory,
// use a default of Library/<kDefaultLibrarySubdirectory>/<Product Name>
if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) {
NSArray *libraryDirectories =
NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask,
YES);
NSString *applicationSupportDirectory =
[libraryDirectories objectAtIndex:0];
NSString *library_subdirectory = [NSString
stringWithUTF8String:kDefaultLibrarySubdirectory];
NSString *breakpad_product = [NSString
stringWithUTF8String:config_params_.GetValueForKey(BREAKPAD_PRODUCT)];
NSArray *path_components = [NSArray
arrayWithObjects:applicationSupportDirectory,
library_subdirectory,
breakpad_product,
nil];
minidumpDir = [NSString pathWithComponents:path_components];
} else {
minidumpDir = [[NSString stringWithUTF8String:minidumpDirectory]
stringByExpandingTildeInPath];
}
DEBUGLOG(stderr,
"Writing minidump to directory (%s)\n",
[minidumpDir UTF8String]);
MinidumpLocation minidumpLocation(minidumpDir);
// Obscure bug alert:
// Don't use [NSString stringWithFormat] to build up the path here since it
// assumes system encoding and in RTL locales will prepend an LTR override
// character for paths beginning with '/' which fileSystemRepresentation does
// not remove. Filed as rdar://6889706 .
NSString *path_ns = [NSString
stringWithUTF8String:minidumpLocation.GetPath()];
NSString *pathid_ns = [NSString
stringWithUTF8String:minidumpLocation.GetID()];
NSString *minidumpPath = [path_ns stringByAppendingPathComponent:pathid_ns];
minidumpPath = [minidumpPath
stringByAppendingPathExtension:@"dmp"];
DEBUGLOG(stderr,
"minidump path (%s)\n",
[minidumpPath UTF8String]);
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_,
exception_subcode_,
crashing_thread_);
}
bool result = generator.Write([minidumpPath fileSystemRepresentation]);
if (result) {
DEBUGLOG(stderr, "Wrote minidump - OK\n");
} else {
DEBUGLOG(stderr, "Error writing minidump - errno=%s\n", strerror(errno));
}
// let the task continue
task_resume(remote_task_);
DEBUGLOG(stderr, "Resumed remote task\n");
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

View File

@ -0,0 +1,47 @@
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
#define CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
namespace google_breakpad {
class ClientInfo {
public:
explicit ClientInfo(pid_t pid) : pid_(pid) {}
pid_t pid() const { return pid_; }
private:
pid_t pid_;
};
} // namespace google_breakpad
#endif // CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_

View File

@ -0,0 +1,72 @@
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "client/mac/crash_generation/crash_generation_client.h"
#include "client/mac/crash_generation/crash_generation_server.h"
#include "common/mac/MachIPC.h"
namespace google_breakpad {
bool CrashGenerationClient::RequestDumpForException(
int exception_type,
int exception_code,
int exception_subcode,
mach_port_t crashing_thread) {
// The server will send a message to this port indicating that it
// has finished its work.
ReceivePort acknowledge_port;
MachSendMessage message(kDumpRequestMessage);
message.AddDescriptor(mach_task_self()); // this task
message.AddDescriptor(crashing_thread); // crashing thread
message.AddDescriptor(mach_thread_self()); // handler thread
message.AddDescriptor(acknowledge_port.GetPort()); // message receive port
ExceptionInfo info;
info.exception_type = exception_type;
info.exception_code = exception_code;
info.exception_subcode = exception_subcode;
message.SetData(&info, sizeof(info));
const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
kern_return_t result = sender_.SendMessage(message, kSendTimeoutMs);
if (result != KERN_SUCCESS)
return false;
// Give the server slightly longer to reply since it has to
// inspect this task and write the minidump.
const mach_msg_timeout_t kReceiveTimeoutMs = 5 * 1000;
MachReceiveMessage acknowledge_message;
result = acknowledge_port.WaitForMessage(&acknowledge_message,
kReceiveTimeoutMs);
return result == KERN_SUCCESS;
}
} // namespace google_breakpad

View File

@ -0,0 +1,65 @@
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
#include "common/mac/MachIPC.h"
namespace google_breakpad {
class CrashGenerationClient {
public:
explicit CrashGenerationClient(const char* mach_port_name)
: sender_(mach_port_name) {
}
// Request the crash server to generate a dump.
//
// Return true if the dump was successful; false otherwise.
bool RequestDumpForException(int exception_type,
int exception_code,
int exception_subcode,
mach_port_t crashing_thread);
bool RequestDump() {
return RequestDumpForException(0, 0, 0, MACH_PORT_NULL);
}
private:
MachPortSender sender_;
// Prevent copy construction and assignment.
CrashGenerationClient(const CrashGenerationClient&);
CrashGenerationClient& operator=(const CrashGenerationClient&);
};
} // namespace google_breakpad
#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_

View File

@ -0,0 +1,160 @@
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "client/mac/crash_generation/crash_generation_server.h"
#include "client/mac/crash_generation/client_info.h"
#include "client/mac/handler/minidump_generator.h"
#include "common/mac/scoped_task_suspend-inl.h"
namespace google_breakpad {
CrashGenerationServer::CrashGenerationServer(
const char *mach_port_name,
OnClientDumpRequestCallback dump_callback,
void *dump_context,
OnClientExitingCallback exit_callback,
void *exit_context,
bool generate_dumps,
const std::string &dump_path)
: dump_callback_(dump_callback),
dump_context_(dump_context),
exit_callback_(exit_callback),
exit_context_(exit_context),
generate_dumps_(generate_dumps),
dump_dir_(dump_path.empty() ? "/tmp" : dump_path),
started_(false),
receive_port_(mach_port_name),
mach_port_name_(mach_port_name) {
}
CrashGenerationServer::~CrashGenerationServer() {
if (started_)
Stop();
}
bool CrashGenerationServer::Start() {
int thread_create_result = pthread_create(&server_thread_, NULL,
&WaitForMessages, this);
started_ = thread_create_result == 0;
return started_;
}
bool CrashGenerationServer::Stop() {
if (!started_)
return false;
// Send a quit message to the background thread, and then join it.
MachPortSender sender(mach_port_name_.c_str());
MachSendMessage quit_message(kQuitMessage);
const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
kern_return_t result = sender.SendMessage(quit_message, kSendTimeoutMs);
if (result == KERN_SUCCESS) {
int thread_join_result = pthread_join(server_thread_, NULL);
started_ = thread_join_result != 0;
}
return !started_;
}
// static
void *CrashGenerationServer::WaitForMessages(void *server) {
CrashGenerationServer *self =
reinterpret_cast<CrashGenerationServer*>(server);
while (self->WaitForOneMessage()) {}
return NULL;
}
bool CrashGenerationServer::WaitForOneMessage() {
MachReceiveMessage message;
kern_return_t result = receive_port_.WaitForMessage(&message,
MACH_MSG_TIMEOUT_NONE);
if (result == KERN_SUCCESS) {
switch (message.GetMessageID()) {
case kDumpRequestMessage: {
ExceptionInfo &info = (ExceptionInfo &)*message.GetData();
mach_port_t remote_task = message.GetTranslatedPort(0);
mach_port_t crashing_thread = message.GetTranslatedPort(1);
mach_port_t handler_thread = message.GetTranslatedPort(2);
mach_port_t ack_port = message.GetTranslatedPort(3);
pid_t remote_pid = -1;
pid_for_task(remote_task, &remote_pid);
ClientInfo client(remote_pid);
bool result;
std::string dump_path;
if (generate_dumps_) {
ScopedTaskSuspend suspend(remote_task);
MinidumpGenerator generator(remote_task, handler_thread);
dump_path = generator.UniqueNameInDirectory(dump_dir_, NULL);
if (info.exception_type && info.exception_code) {
generator.SetExceptionInformation(info.exception_type,
info.exception_code,
info.exception_subcode,
crashing_thread);
}
result = generator.Write(dump_path.c_str());
} else {
result = true;
}
if (result && dump_callback_) {
dump_callback_(dump_context_, client, dump_path);
}
// TODO(ted): support a way for the client to send additional data,
// perhaps with a callback so users of the server can read the data
// themselves?
if (ack_port != MACH_PORT_DEAD && ack_port != MACH_PORT_NULL) {
MachPortSender sender(ack_port);
MachSendMessage ack_message(kAcknowledgementMessage);
const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
sender.SendMessage(ack_message, kSendTimeoutMs);
}
if (exit_callback_) {
exit_callback_(exit_context_, client);
}
break;
}
case kQuitMessage:
return false;
}
} else { // result != KERN_SUCCESS
return false;
}
return true;
}
} // namespace google_breakpad

View File

@ -0,0 +1,141 @@
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
#include <stdint.h>
#include <string>
#include "common/mac/MachIPC.h"
namespace google_breakpad {
class ClientInfo;
// Messages the server can read via its mach port
enum {
kDumpRequestMessage = 1,
kAcknowledgementMessage = 2,
kQuitMessage = 3
};
// Exception details sent by the client when requesting a dump.
struct ExceptionInfo {
int32_t exception_type;
int32_t exception_code;
int32_t exception_subcode;
};
class CrashGenerationServer {
public:
// WARNING: callbacks may be invoked on a different thread
// than that which creates the CrashGenerationServer. They must
// be thread safe.
typedef void (*OnClientDumpRequestCallback)(void *context,
const ClientInfo &client_info,
const std::string &file_path);
typedef void (*OnClientExitingCallback)(void *context,
const ClientInfo &client_info);
// Create an instance with the given parameters.
//
// mach_port_name: Named server port to listen on.
// dump_callback: Callback for a client crash dump request.
// dump_context: Context for client crash dump request callback.
// exit_callback: Callback for client process exit.
// exit_context: Context for client exit callback.
// generate_dumps: Whether to automatically generate dumps.
// Client code of this class might want to generate dumps explicitly
// in the crash dump request callback. In that case, false can be
// passed for this parameter.
// dump_path: Path for generating dumps; required only if true is
// passed for generateDumps parameter; NULL can be passed otherwise.
CrashGenerationServer(const char *mach_port_name,
OnClientDumpRequestCallback dump_callback,
void *dump_context,
OnClientExitingCallback exit_callback,
void *exit_context,
bool generate_dumps,
const std::string &dump_path);
~CrashGenerationServer();
// Perform initialization steps needed to start listening to clients.
//
// Return true if initialization is successful; false otherwise.
bool Start();
// Stop the server.
bool Stop();
private:
// Return a unique filename at which a minidump can be written.
bool MakeMinidumpFilename(std::string &outFilename);
// Loop reading client messages and responding to them until
// a quit message is received.
static void *WaitForMessages(void *server);
// Wait for a single client message and respond to it. Returns false
// if a quit message was received or if an error occurred.
bool WaitForOneMessage();
OnClientDumpRequestCallback dump_callback_;
void *dump_context_;
OnClientExitingCallback exit_callback_;
void *exit_context_;
bool generate_dumps_;
std::string dump_dir_;
bool started_;
// The mach port that receives requests to dump from child processes.
ReceivePort receive_port_;
// The name of the mach port. Stored so the Stop method can message
// the background thread to shut it down.
std::string mach_port_name_;
// The thread that waits on the receive port.
pthread_t server_thread_;
// Disable copy constructor and operator=.
CrashGenerationServer(const CrashGenerationServer&);
CrashGenerationServer& operator=(const CrashGenerationServer&);
};
} // namespace google_breakpad
#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,258 @@
#ifndef _exc_user_
#define _exc_user_
/* Module exc */
#include <string.h>
#include <mach/ndr.h>
#include <mach/boolean.h>
#include <mach/kern_return.h>
#include <mach/notify.h>
#include <mach/mach_types.h>
#include <mach/message.h>
#include <mach/mig_errors.h>
#include <mach/port.h>
#ifdef AUTOTEST
#ifndef FUNCTION_PTR_T
#define FUNCTION_PTR_T
typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t);
typedef struct {
char *name;
function_ptr_t function;
} function_table_entry;
typedef function_table_entry *function_table_t;
#endif /* FUNCTION_PTR_T */
#endif /* AUTOTEST */
#ifndef exc_MSG_COUNT
#define exc_MSG_COUNT 3
#endif /* exc_MSG_COUNT */
#include <mach/std_types.h>
#include <mach/mig.h>
#include <mach/mig.h>
#include <mach/mach_types.h>
#ifdef __BeforeMigUserHeader
__BeforeMigUserHeader
#endif /* __BeforeMigUserHeader */
#include <sys/cdefs.h>
__BEGIN_DECLS
/* Routine exception_raise */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
kern_return_t exception_raise
(
mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t codeCnt
);
/* Routine exception_raise_state */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
kern_return_t exception_raise_state
(
mach_port_t exception_port,
exception_type_t exception,
const exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
const thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
);
/* Routine exception_raise_state_identity */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
kern_return_t exception_raise_state_identity
(
mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
);
__END_DECLS
/********************** Caution **************************/
/* The following data types should be used to calculate */
/* maximum message sizes only. The actual message may be */
/* smaller, and the position of the arguments within the */
/* message layout may vary from what is presented here. */
/* For example, if any of the arguments are variable- */
/* sized, and less than the maximum is sent, the data */
/* will be packed tight in the actual message to reduce */
/* the presence of holes. */
/********************** Caution **************************/
/* typedefs for all requests */
#ifndef __Request__exc_subsystem__defined
#define __Request__exc_subsystem__defined
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
integer_t code[2];
} __Request__exception_raise_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
integer_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[144];
} __Request__exception_raise_state_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
integer_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[144];
} __Request__exception_raise_state_identity_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#endif /* !__Request__exc_subsystem__defined */
/* union of all requests */
#ifndef __RequestUnion__exc_subsystem__defined
#define __RequestUnion__exc_subsystem__defined
union __RequestUnion__exc_subsystem {
__Request__exception_raise_t Request_exception_raise;
__Request__exception_raise_state_t Request_exception_raise_state;
__Request__exception_raise_state_identity_t Request_exception_raise_state_identity;
};
#endif /* !__RequestUnion__exc_subsystem__defined */
/* typedefs for all replies */
#ifndef __Reply__exc_subsystem__defined
#define __Reply__exc_subsystem__defined
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
} __Reply__exception_raise_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[144];
} __Reply__exception_raise_state_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[144];
} __Reply__exception_raise_state_identity_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#endif /* !__Reply__exc_subsystem__defined */
/* union of all replies */
#ifndef __ReplyUnion__exc_subsystem__defined
#define __ReplyUnion__exc_subsystem__defined
union __ReplyUnion__exc_subsystem {
__Reply__exception_raise_t Reply_exception_raise;
__Reply__exception_raise_state_t Reply_exception_raise_state;
__Reply__exception_raise_state_identity_t Reply_exception_raise_state_identity;
};
#endif /* !__RequestUnion__exc_subsystem__defined */
#ifndef subsystem_to_name_map_exc
#define subsystem_to_name_map_exc \
{ "exception_raise", 2401 },\
{ "exception_raise_state", 2402 },\
{ "exception_raise_state_identity", 2403 }
#endif
#ifdef __AfterMigUserHeader
__AfterMigUserHeader
#endif /* __AfterMigUserHeader */
#endif /* _exc_user_ */

View File

@ -0,0 +1,412 @@
/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University 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 REGENTS 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 REGENTS 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 file was copied from libc/gen/nlist.c from Darwin's source code
* The version of nlist used as a base is from 10.5.2, libc-498
* http://www.opensource.apple.com/darwinsource/10.5.2/Libc-498/gen/nlist.c
*
* The full tarball is at:
* http://www.opensource.apple.com/darwinsource/tarballs/apsl/Libc-498.tar.gz
*
* I've modified it to be compatible with 64-bit images.
*/
#include "breakpad_nlist_64.h"
#include <fcntl.h>
#include <mach-o/nlist.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <mach/mach.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <TargetConditionals.h>
#include <unistd.h>
/* Stuff lifted from <a.out.h> and <sys/exec.h> since they are gone */
/*
* Header prepended to each a.out file.
*/
struct exec {
unsigned short a_machtype; /* machine type */
unsigned short a_magic; /* magic number */
unsigned long a_text; /* size of text segment */
unsigned long a_data; /* size of initialized data */
unsigned long a_bss; /* size of uninitialized data */
unsigned long a_syms; /* size of symbol table */
unsigned long a_entry; /* entry point */
unsigned long a_trsize; /* size of text relocation */
unsigned long a_drsize; /* size of data relocation */
};
#define OMAGIC 0407 /* old impure format */
#define NMAGIC 0410 /* read-only text */
#define ZMAGIC 0413 /* demand load format */
#define N_BADMAG(x) \
(((x).a_magic)!=OMAGIC && ((x).a_magic)!=NMAGIC && ((x).a_magic)!=ZMAGIC)
#define N_TXTOFF(x) \
((x).a_magic==ZMAGIC ? 0 : sizeof (struct exec))
#define N_SYMOFF(x) \
(N_TXTOFF(x) + (x).a_text+(x).a_data + (x).a_trsize+(x).a_drsize)
// Traits structs for specializing function templates to handle
// 32-bit/64-bit Mach-O files.
template<typename T>
struct MachBits {};
typedef struct nlist nlist32;
typedef struct nlist_64 nlist64;
template<>
struct MachBits<nlist32> {
typedef mach_header mach_header_type;
typedef uint32_t word_type;
static const uint32_t magic = MH_MAGIC;
};
template<>
struct MachBits<nlist64> {
typedef mach_header_64 mach_header_type;
typedef uint64_t word_type;
static const uint32_t magic = MH_MAGIC_64;
};
template<typename nlist_type>
int
__breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
cpu_type_t cpu_type);
/*
* nlist - retreive attributes from name list (string table version)
*/
template <typename nlist_type>
int breakpad_nlist_common(const char *name,
nlist_type *list,
const char **symbolNames,
cpu_type_t cpu_type) {
int fd = open(name, O_RDONLY, 0);
if (fd < 0)
return -1;
int n = __breakpad_fdnlist(fd, list, symbolNames, cpu_type);
close(fd);
return n;
}
int breakpad_nlist(const char *name,
struct nlist *list,
const char **symbolNames,
cpu_type_t cpu_type) {
return breakpad_nlist_common(name, list, symbolNames, cpu_type);
}
int breakpad_nlist(const char *name,
struct nlist_64 *list,
const char **symbolNames,
cpu_type_t cpu_type) {
return breakpad_nlist_common(name, list, symbolNames, cpu_type);
}
/* Note: __fdnlist() is called from kvm_nlist in libkvm's kvm.c */
template<typename nlist_type>
int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
cpu_type_t cpu_type) {
typedef typename MachBits<nlist_type>::mach_header_type mach_header_type;
typedef typename MachBits<nlist_type>::word_type word_type;
const uint32_t magic = MachBits<nlist_type>::magic;
int maxlen = 500;
int nreq = 0;
for (nlist_type* q = list;
symbolNames[q-list] && symbolNames[q-list][0];
q++, nreq++) {
q->n_type = 0;
q->n_value = 0;
q->n_desc = 0;
q->n_sect = 0;
q->n_un.n_strx = 0;
}
struct exec buf;
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf) ||
(N_BADMAG(buf) && *((long *)&buf) != magic &&
NXSwapBigLongToHost(*((long *)&buf)) != FAT_MAGIC) &&
/* The following is the big-endian ppc64 check */
(*((long*)&buf)) != FAT_MAGIC) {
return -1;
}
/* Deal with fat file if necessary */
unsigned arch_offset = 0;
if (NXSwapBigLongToHost(*((long *)&buf)) == FAT_MAGIC ||
/* The following is the big-endian ppc64 check */
*((unsigned int *)&buf) == FAT_MAGIC) {
/* Get host info */
host_t host = mach_host_self();
unsigned i = HOST_BASIC_INFO_COUNT;
struct host_basic_info hbi;
kern_return_t kr;
if ((kr = host_info(host, HOST_BASIC_INFO,
(host_info_t)(&hbi), &i)) != KERN_SUCCESS) {
return -1;
}
mach_port_deallocate(mach_task_self(), host);
/* Read in the fat header */
struct fat_header fh;
if (lseek(fd, 0, SEEK_SET) == -1) {
return -1;
}
if (read(fd, (char *)&fh, sizeof(fh)) != sizeof(fh)) {
return -1;
}
/* Convert fat_narchs to host byte order */
fh.nfat_arch = NXSwapBigIntToHost(fh.nfat_arch);
/* Read in the fat archs */
struct fat_arch *fat_archs =
(struct fat_arch *)malloc(fh.nfat_arch * sizeof(struct fat_arch));
if (fat_archs == NULL) {
return -1;
}
if (read(fd, (char *)fat_archs,
sizeof(struct fat_arch) * fh.nfat_arch) !=
(ssize_t)sizeof(struct fat_arch) * fh.nfat_arch) {
free(fat_archs);
return -1;
}
/*
* Convert archs to host byte ordering (a constraint of
* cpusubtype_getbestarch()
*/
for (unsigned i = 0; i < fh.nfat_arch; i++) {
fat_archs[i].cputype =
NXSwapBigIntToHost(fat_archs[i].cputype);
fat_archs[i].cpusubtype =
NXSwapBigIntToHost(fat_archs[i].cpusubtype);
fat_archs[i].offset =
NXSwapBigIntToHost(fat_archs[i].offset);
fat_archs[i].size =
NXSwapBigIntToHost(fat_archs[i].size);
fat_archs[i].align =
NXSwapBigIntToHost(fat_archs[i].align);
}
struct fat_arch *fap = NULL;
for (unsigned i = 0; i < fh.nfat_arch; i++) {
if (fat_archs[i].cputype == cpu_type) {
fap = &fat_archs[i];
break;
}
}
if (!fap) {
free(fat_archs);
return -1;
}
arch_offset = fap->offset;
free(fat_archs);
/* Read in the beginning of the architecture-specific file */
if (lseek(fd, arch_offset, SEEK_SET) == -1) {
return -1;
}
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf)) {
return -1;
}
}
off_t sa; /* symbol address */
off_t ss; /* start of strings */
register register_t n;
if (*((unsigned int *)&buf) == magic) {
if (lseek(fd, arch_offset, SEEK_SET) == -1) {
return -1;
}
mach_header_type mh;
if (read(fd, (char *)&mh, sizeof(mh)) != sizeof(mh)) {
return -1;
}
struct load_command *load_commands =
(struct load_command *)malloc(mh.sizeofcmds);
if (load_commands == NULL) {
return -1;
}
if (read(fd, (char *)load_commands, mh.sizeofcmds) !=
mh.sizeofcmds) {
free(load_commands);
return -1;
}
struct symtab_command *stp = NULL;
struct load_command *lcp = load_commands;
// iterate through all load commands, looking for
// LC_SYMTAB load command
for (long i = 0; i < mh.ncmds; i++) {
if (lcp->cmdsize % sizeof(word_type) != 0 ||
lcp->cmdsize <= 0 ||
(char *)lcp + lcp->cmdsize >
(char *)load_commands + mh.sizeofcmds) {
free(load_commands);
return -1;
}
if (lcp->cmd == LC_SYMTAB) {
if (lcp->cmdsize !=
sizeof(struct symtab_command)) {
free(load_commands);
return -1;
}
stp = (struct symtab_command *)lcp;
break;
}
lcp = (struct load_command *)
((char *)lcp + lcp->cmdsize);
}
if (stp == NULL) {
free(load_commands);
return -1;
}
// sa points to the beginning of the symbol table
sa = stp->symoff + arch_offset;
// ss points to the beginning of the string table
ss = stp->stroff + arch_offset;
// n is the number of bytes in the symbol table
// each symbol table entry is an nlist structure
n = stp->nsyms * sizeof(nlist_type);
free(load_commands);
} else {
sa = N_SYMOFF(buf) + arch_offset;
ss = sa + buf.a_syms + arch_offset;
n = buf.a_syms;
}
if (lseek(fd, sa, SEEK_SET) == -1) {
return -1;
}
// the algorithm here is to read the nlist entries in m-sized
// chunks into q. q is then iterated over. for each entry in q,
// use the string table index(q->n_un.n_strx) to read the symbol
// name, then scan the nlist entries passed in by the user(via p),
// and look for a match
while (n) {
nlist_type space[BUFSIZ/sizeof (nlist_type)];
register register_t m = sizeof (space);
if (n < m)
m = n;
if (read(fd, (char *)space, m) != m)
break;
n -= m;
long savpos = lseek(fd, 0, SEEK_CUR);
if (savpos == -1) {
return -1;
}
for (nlist_type* q = space; (m -= sizeof(nlist_type)) >= 0; q++) {
char nambuf[BUFSIZ];
if (q->n_un.n_strx == 0 || q->n_type & N_STAB)
continue;
// seek to the location in the binary where the symbol
// name is stored & read it into memory
if (lseek(fd, ss+q->n_un.n_strx, SEEK_SET) == -1) {
return -1;
}
if (read(fd, nambuf, maxlen+1) == -1) {
return -1;
}
const char *s2 = nambuf;
for (nlist_type *p = list;
symbolNames[p-list] && symbolNames[p-list][0];
p++) {
// get the symbol name the user has passed in that
// corresponds to the nlist entry that we're looking at
const char *s1 = symbolNames[p - list];
while (*s1) {
if (*s1++ != *s2++)
goto cont;
}
if (*s2)
goto cont;
p->n_value = q->n_value;
p->n_type = q->n_type;
p->n_desc = q->n_desc;
p->n_sect = q->n_sect;
p->n_un.n_strx = q->n_un.n_strx;
if (--nreq == 0)
return nreq;
break;
cont: ;
}
}
if (lseek(fd, savpos, SEEK_SET) == -1) {
return -1;
}
}
return nreq;
}

View File

@ -0,0 +1,47 @@
// 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.
// breakpad_nlist.h
//
// This file is meant to provide a header for clients of the modified
// nlist function implemented to work on 64-bit.
#ifndef CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__
#include <mach/machine.h>
int breakpad_nlist(const char *name,
struct nlist *list,
const char **symbolNames,
cpu_type_t cpu_type);
int breakpad_nlist(const char *name,
struct nlist_64 *list,
const char **symbolNames,
cpu_type_t cpu_type);
#endif /* CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__ */

View File

@ -0,0 +1,515 @@
// 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.
#include "client/mac/handler/dynamic_images.h"
extern "C" { // needed to compile on Leopard
#include <mach-o/nlist.h>
#include <stdlib.h>
#include <stdio.h>
}
#include "breakpad_nlist_64.h"
#include <assert.h>
#include <dlfcn.h>
#include <mach/mach_vm.h>
#include <sys/sysctl.h>
#include <algorithm>
#include <string>
#include <vector>
namespace google_breakpad {
using std::string;
using std::vector;
//==============================================================================
// Returns the size of the memory region containing |address| and the
// number of bytes from |address| to the end of the region.
// We potentially, will extend the size of the original
// region by the size of the following region if it's contiguous with the
// first in order to handle cases when we're reading strings and they
// straddle two vm regions.
//
static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task,
const uint64_t address,
mach_vm_size_t *size_to_end) {
mach_vm_address_t region_base = (mach_vm_address_t)address;
mach_vm_size_t region_size;
natural_t nesting_level = 0;
vm_region_submap_info_64 submap_info;
mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
// Get information about the vm region containing |address|
vm_region_recurse_info_t region_info;
region_info = reinterpret_cast<vm_region_recurse_info_t>(&submap_info);
kern_return_t result =
mach_vm_region_recurse(target_task,
&region_base,
&region_size,
&nesting_level,
region_info,
&info_count);
if (result == KERN_SUCCESS) {
// Get distance from |address| to the end of this region
*size_to_end = region_base + region_size -(mach_vm_address_t)address;
// If we want to handle strings as long as 4096 characters we may need
// to check if there's a vm region immediately following the first one.
// If so, we need to extend |*size_to_end| to go all the way to the end
// of the second region.
if (*size_to_end < 4096) {
// Second region starts where the first one ends
mach_vm_address_t region_base2 =
(mach_vm_address_t)(region_base + region_size);
mach_vm_size_t region_size2;
// Get information about the following vm region
result =
mach_vm_region_recurse(target_task,
&region_base2,
&region_size2,
&nesting_level,
region_info,
&info_count);
// Extend region_size to go all the way to the end of the 2nd region
if (result == KERN_SUCCESS
&& region_base2 == region_base + region_size) {
region_size += region_size2;
}
}
*size_to_end = region_base + region_size -(mach_vm_address_t)address;
} else {
region_size = 0;
*size_to_end = 0;
}
return region_size;
}
#define kMaxStringLength 8192
//==============================================================================
// Reads a NULL-terminated string from another task.
//
// Warning! This will not read any strings longer than kMaxStringLength-1
//
static string ReadTaskString(task_port_t target_task,
const uint64_t address) {
// The problem is we don't know how much to read until we know how long
// the string is. And we don't know how long the string is, until we've read
// the memory! So, we'll try to read kMaxStringLength bytes
// (or as many bytes as we can until we reach the end of the vm region).
mach_vm_size_t size_to_end;
GetMemoryRegionSize(target_task, address, &size_to_end);
if (size_to_end > 0) {
mach_vm_size_t size_to_read =
size_to_end > kMaxStringLength ? kMaxStringLength : size_to_end;
vector<uint8_t> bytes;
if (ReadTaskMemory(target_task, address, (size_t)size_to_read, bytes) !=
KERN_SUCCESS)
return string();
return string(reinterpret_cast<const char*>(&bytes[0]));
}
return string();
}
//==============================================================================
// Reads an address range from another task. The bytes read will be returned
// in bytes, which will be resized as necessary.
kern_return_t ReadTaskMemory(task_port_t target_task,
const uint64_t address,
size_t length,
vector<uint8_t> &bytes) {
int systemPageSize = getpagesize();
// use the negative of the page size for the mask to find the page address
mach_vm_address_t page_address = address & (-systemPageSize);
mach_vm_address_t last_page_address =
(address + length + (systemPageSize - 1)) & (-systemPageSize);
mach_vm_size_t page_size = last_page_address - page_address;
uint8_t* local_start;
uint32_t local_length;
kern_return_t r = mach_vm_read(target_task,
page_address,
page_size,
reinterpret_cast<vm_offset_t*>(&local_start),
&local_length);
if (r != KERN_SUCCESS)
return r;
bytes.resize(length);
memcpy(&bytes[0],
&local_start[(mach_vm_address_t)address - page_address],
length);
mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length);
return KERN_SUCCESS;
}
#pragma mark -
//==============================================================================
// Traits structs for specializing function templates to handle
// 32-bit/64-bit Mach-O files.
struct MachO32 {
typedef mach_header mach_header_type;
typedef segment_command mach_segment_command_type;
typedef dyld_image_info32 dyld_image_info;
typedef dyld_all_image_infos32 dyld_all_image_infos;
typedef struct nlist nlist_type;
static const uint32_t magic = MH_MAGIC;
static const uint32_t segment_load_command = LC_SEGMENT;
};
struct MachO64 {
typedef mach_header_64 mach_header_type;
typedef segment_command_64 mach_segment_command_type;
typedef dyld_image_info64 dyld_image_info;
typedef dyld_all_image_infos64 dyld_all_image_infos;
typedef struct nlist_64 nlist_type;
static const uint32_t magic = MH_MAGIC_64;
static const uint32_t segment_load_command = LC_SEGMENT_64;
};
template<typename MachBits>
bool FindTextSection(DynamicImage& image) {
typedef typename MachBits::mach_header_type mach_header_type;
typedef typename MachBits::mach_segment_command_type
mach_segment_command_type;
const mach_header_type* header =
reinterpret_cast<const mach_header_type*>(&image.header_[0]);
if(header->magic != MachBits::magic) {
return false;
}
const struct load_command *cmd =
reinterpret_cast<const struct load_command *>(header + 1);
bool found_text_section = false;
bool found_dylib_id_command = false;
for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) {
if (!found_text_section) {
if (cmd->cmd == MachBits::segment_load_command) {
const mach_segment_command_type *seg =
reinterpret_cast<const mach_segment_command_type *>(cmd);
if (!strcmp(seg->segname, "__TEXT")) {
image.vmaddr_ = seg->vmaddr;
image.vmsize_ = seg->vmsize;
image.slide_ = 0;
if (seg->fileoff == 0 && seg->filesize != 0) {
image.slide_ =
(uintptr_t)image.GetLoadAddress() - (uintptr_t)seg->vmaddr;
}
found_text_section = true;
}
}
}
if (!found_dylib_id_command) {
if (cmd->cmd == LC_ID_DYLIB) {
const struct dylib_command *dc =
reinterpret_cast<const struct dylib_command *>(cmd);
image.version_ = dc->dylib.current_version;
found_dylib_id_command = true;
}
}
if (found_dylib_id_command && found_text_section) {
return true;
}
cmd = reinterpret_cast<const struct load_command *>
(reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
}
return false;
}
//==============================================================================
// Initializes vmaddr_, vmsize_, and slide_
void DynamicImage::CalculateMemoryAndVersionInfo() {
// unless we can process the header, ensure that calls to
// IsValid() will return false
vmaddr_ = 0;
vmsize_ = 0;
slide_ = 0;
version_ = 0;
// The function template above does all the real work.
if (Is64Bit())
FindTextSection<MachO64>(*this);
else
FindTextSection<MachO32>(*this);
}
//==============================================================================
// The helper function template abstracts the 32/64-bit differences.
template<typename MachBits>
uint32_t GetFileTypeFromHeader(DynamicImage& image) {
typedef typename MachBits::mach_header_type mach_header_type;
const mach_header_type* header =
reinterpret_cast<const mach_header_type*>(&image.header_[0]);
return header->filetype;
}
uint32_t DynamicImage::GetFileType() {
if (Is64Bit())
return GetFileTypeFromHeader<MachO64>(*this);
return GetFileTypeFromHeader<MachO32>(*this);
}
#pragma mark -
//==============================================================================
// Loads information about dynamically loaded code in the given task.
DynamicImages::DynamicImages(mach_port_t task)
: task_(task),
cpu_type_(DetermineTaskCPUType(task)),
image_list_() {
ReadImageInfoForTask();
}
template<typename MachBits>
static uint64_t LookupSymbol(const char* symbol_name,
const char* filename,
cpu_type_t cpu_type) {
typedef typename MachBits::nlist_type nlist_type;
nlist_type symbol_info[8] = {};
const char *symbolNames[2] = { symbol_name, "\0" };
nlist_type &list = symbol_info[0];
int invalidEntriesCount = breakpad_nlist(filename,
&list,
symbolNames,
cpu_type);
if(invalidEntriesCount != 0) {
return 0;
}
assert(list.n_value);
return list.n_value;
}
uint64_t DynamicImages::GetDyldAllImageInfosPointer() {
const char *imageSymbolName = "_dyld_all_image_infos";
const char *dyldPath = "/usr/lib/dyld";
if (Is64Bit())
return LookupSymbol<MachO64>(imageSymbolName, dyldPath, cpu_type_);
return LookupSymbol<MachO32>(imageSymbolName, dyldPath, cpu_type_);
}
//==============================================================================
// This code was written using dyld_debug.c (from Darwin) as a guide.
template<typename MachBits>
void ReadImageInfo(DynamicImages& images,
uint64_t image_list_address) {
typedef typename MachBits::dyld_image_info dyld_image_info;
typedef typename MachBits::dyld_all_image_infos dyld_all_image_infos;
typedef typename MachBits::mach_header_type mach_header_type;
// Read the structure inside of dyld that contains information about
// loaded images. We're reading from the desired task's address space.
// Here we make the assumption that dyld loaded at the same address in
// the crashed process vs. this one. This is an assumption made in
// "dyld_debug.c" and is said to be nearly always valid.
vector<uint8_t> dyld_all_info_bytes;
if (ReadTaskMemory(images.task_,
image_list_address,
sizeof(dyld_all_image_infos),
dyld_all_info_bytes) != KERN_SUCCESS)
return;
dyld_all_image_infos *dyldInfo =
reinterpret_cast<dyld_all_image_infos*>(&dyld_all_info_bytes[0]);
// number of loaded images
int count = dyldInfo->infoArrayCount;
// Read an array of dyld_image_info structures each containing
// information about a loaded image.
vector<uint8_t> dyld_info_array_bytes;
if (ReadTaskMemory(images.task_,
dyldInfo->infoArray,
count * sizeof(dyld_image_info),
dyld_info_array_bytes) != KERN_SUCCESS)
return;
dyld_image_info *infoArray =
reinterpret_cast<dyld_image_info*>(&dyld_info_array_bytes[0]);
images.image_list_.reserve(count);
for (int i = 0; i < count; ++i) {
dyld_image_info &info = infoArray[i];
// First read just the mach_header from the image in the task.
vector<uint8_t> mach_header_bytes;
if (ReadTaskMemory(images.task_,
info.load_address_,
sizeof(mach_header_type),
mach_header_bytes) != KERN_SUCCESS)
continue; // bail on this dynamic image
mach_header_type *header =
reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]);
// Now determine the total amount necessary to read the header
// plus all of the load commands.
size_t header_size =
sizeof(mach_header_type) + header->sizeofcmds;
if (ReadTaskMemory(images.task_,
info.load_address_,
header_size,
mach_header_bytes) != KERN_SUCCESS)
continue;
header = reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]);
// Read the file name from the task's memory space.
string file_path;
if (info.file_path_) {
// Although we're reading kMaxStringLength bytes, it's copied in the
// the DynamicImage constructor below with the correct string length,
// so it's not really wasting memory.
file_path = ReadTaskString(images.task_, info.file_path_);
}
// Create an object representing this image and add it to our list.
DynamicImage *new_image;
new_image = new DynamicImage(&mach_header_bytes[0],
header_size,
info.load_address_,
file_path,
info.file_mod_date_,
images.task_,
images.cpu_type_);
if (new_image->IsValid()) {
images.image_list_.push_back(DynamicImageRef(new_image));
} else {
delete new_image;
}
}
// sorts based on loading address
sort(images.image_list_.begin(), images.image_list_.end());
// remove duplicates - this happens in certain strange cases
// You can see it in DashboardClient when Google Gadgets plugin
// is installed. Apple's crash reporter log and gdb "info shared"
// both show the same library multiple times at the same address
vector<DynamicImageRef>::iterator it = unique(images.image_list_.begin(),
images.image_list_.end());
images.image_list_.erase(it, images.image_list_.end());
}
void DynamicImages::ReadImageInfoForTask() {
uint64_t imageList = GetDyldAllImageInfosPointer();
if (imageList) {
if (Is64Bit())
ReadImageInfo<MachO64>(*this, imageList);
else
ReadImageInfo<MachO32>(*this, imageList);
}
}
//==============================================================================
DynamicImage *DynamicImages::GetExecutableImage() {
int executable_index = GetExecutableImageIndex();
if (executable_index >= 0) {
return GetImage(executable_index);
}
return NULL;
}
//==============================================================================
// returns -1 if failure to find executable
int DynamicImages::GetExecutableImageIndex() {
int image_count = GetImageCount();
for (int i = 0; i < image_count; ++i) {
DynamicImage *image = GetImage(i);
if (image->GetFileType() == MH_EXECUTE) {
return i;
}
}
return -1;
}
//==============================================================================
// static
cpu_type_t DynamicImages::DetermineTaskCPUType(task_t task) {
if (task == mach_task_self())
return GetNativeCPUType();
int mib[CTL_MAXNAME];
size_t mibLen = CTL_MAXNAME;
int err = sysctlnametomib("sysctl.proc_cputype", mib, &mibLen);
if (err == 0) {
assert(mibLen < CTL_MAXNAME);
pid_for_task(task, &mib[mibLen]);
mibLen += 1;
cpu_type_t cpu_type;
size_t cpuTypeSize = sizeof(cpu_type);
sysctl(mib, mibLen, &cpu_type, &cpuTypeSize, 0, 0);
return cpu_type;
}
return GetNativeCPUType();
}
} // namespace google_breakpad

View File

@ -0,0 +1,313 @@
// 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.
// dynamic_images.h
//
// Implements most of the function of the dyld API, but allowing an
// arbitrary task to be introspected, unlike the dyld API which
// only allows operation on the current task. The current implementation
// is limited to use by 32-bit tasks.
#ifndef CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__
#define CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__
#include <mach/mach.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <sys/types.h>
#include <string>
#include <vector>
namespace google_breakpad {
using std::string;
using std::vector;
//==============================================================================
// The memory layout of this struct matches the dyld_image_info struct
// defined in "dyld_gdb.h" in the darwin source.
typedef struct dyld_image_info32 {
uint32_t load_address_; // struct mach_header*
uint32_t file_path_; // char*
uint32_t file_mod_date_;
} dyld_image_info32;
typedef struct dyld_image_info64 {
uint64_t load_address_; // struct mach_header*
uint64_t file_path_; // char*
uint64_t file_mod_date_;
} dyld_image_info64;
//==============================================================================
// This is as defined in "dyld_gdb.h" in the darwin source.
// _dyld_all_image_infos (in dyld) is a structure of this type
// which will be used to determine which dynamic code has been loaded.
typedef struct dyld_all_image_infos32 {
uint32_t version; // == 1 in Mac OS X 10.4
uint32_t infoArrayCount;
uint32_t infoArray; // const struct dyld_image_info*
uint32_t notification;
bool processDetachedFromSharedRegion;
} dyld_all_image_infos32;
typedef struct dyld_all_image_infos64 {
uint32_t version; // == 1 in Mac OS X 10.4
uint32_t infoArrayCount;
uint64_t infoArray; // const struct dyld_image_info*
uint64_t notification;
bool processDetachedFromSharedRegion;
} dyld_all_image_infos64;
// some typedefs to isolate 64/32 bit differences
#ifdef __LP64__
typedef mach_header_64 breakpad_mach_header;
typedef segment_command_64 breakpad_mach_segment_command;
#else
typedef mach_header breakpad_mach_header;
typedef segment_command breakpad_mach_segment_command;
#endif
// Helper functions to deal with 32-bit/64-bit Mach-O differences.
class DynamicImage;
template<typename MachBits>
bool FindTextSection(DynamicImage& image);
template<typename MachBits>
uint32_t GetFileTypeFromHeader(DynamicImage& image);
//==============================================================================
// Represents a single dynamically loaded mach-o image
class DynamicImage {
public:
DynamicImage(uint8_t *header, // data is copied
size_t header_size, // includes load commands
uint64_t load_address,
string file_path,
uintptr_t image_mod_date,
mach_port_t task,
cpu_type_t cpu_type)
: header_(header, header + header_size),
header_size_(header_size),
load_address_(load_address),
vmaddr_(0),
vmsize_(0),
slide_(0),
version_(0),
file_path_(file_path),
file_mod_date_(image_mod_date),
task_(task),
cpu_type_(cpu_type) {
CalculateMemoryAndVersionInfo();
}
// Size of mach_header plus load commands
size_t GetHeaderSize() const {return header_.size();}
// Full path to mach-o binary
string GetFilePath() {return file_path_;}
uint64_t GetModDate() const {return file_mod_date_;}
// Actual address where the image was loaded
uint64_t GetLoadAddress() const {return load_address_;}
// Address where the image should be loaded
mach_vm_address_t GetVMAddr() const {return vmaddr_;}
// Difference between GetLoadAddress() and GetVMAddr()
ptrdiff_t GetVMAddrSlide() const {return slide_;}
// Size of the image
mach_vm_size_t GetVMSize() const {return vmsize_;}
// Task owning this loaded image
mach_port_t GetTask() {return task_;}
// CPU type of the task
cpu_type_t GetCPUType() {return cpu_type_;}
// filetype from the Mach-O header.
uint32_t GetFileType();
// Return true if the task is a 64-bit architecture.
bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; }
uint32_t GetVersion() {return version_;}
// For sorting
bool operator<(const DynamicImage &inInfo) {
return GetLoadAddress() < inInfo.GetLoadAddress();
}
// Sanity checking
bool IsValid() {return GetVMSize() != 0;}
private:
DynamicImage(const DynamicImage &);
DynamicImage &operator=(const DynamicImage &);
friend class DynamicImages;
template<typename MachBits>
friend bool FindTextSection(DynamicImage& image);
template<typename MachBits>
friend uint32_t GetFileTypeFromHeader(DynamicImage& image);
// Initializes vmaddr_, vmsize_, and slide_
void CalculateMemoryAndVersionInfo();
const vector<uint8_t> header_; // our local copy of the header
size_t header_size_; // mach_header plus load commands
uint64_t load_address_; // base address image is mapped into
mach_vm_address_t vmaddr_;
mach_vm_size_t vmsize_;
ptrdiff_t slide_;
uint32_t version_; // Dylib version
string file_path_; // path dyld used to load the image
uintptr_t file_mod_date_; // time_t of image file
mach_port_t task_;
cpu_type_t cpu_type_; // CPU type of task_
};
//==============================================================================
// DynamicImageRef is just a simple wrapper for a pointer to
// DynamicImage. The reason we use it instead of a simple typedef is so
// that we can use stl::sort() on a vector of DynamicImageRefs
// and simple class pointers can't implement operator<().
//
class DynamicImageRef {
public:
explicit DynamicImageRef(DynamicImage *inP) : p(inP) {}
// The copy constructor is required by STL
DynamicImageRef(const DynamicImageRef &inRef) : p(inRef.p) {}
bool operator<(const DynamicImageRef &inRef) const {
return (*const_cast<DynamicImageRef*>(this)->p)
< (*const_cast<DynamicImageRef&>(inRef).p);
}
bool operator==(const DynamicImageRef &inInfo) const {
return (*const_cast<DynamicImageRef*>(this)->p).GetLoadAddress() ==
(*const_cast<DynamicImageRef&>(inInfo)).GetLoadAddress();
}
// Be just like DynamicImage*
DynamicImage *operator->() {return p;}
operator DynamicImage*() {return p;}
private:
DynamicImage *p;
};
// Helper function to deal with 32-bit/64-bit Mach-O differences.
class DynamicImages;
template<typename MachBits>
void ReadImageInfo(DynamicImages& images, uint64_t image_list_address);
//==============================================================================
// An object of type DynamicImages may be created to allow introspection of
// an arbitrary task's dynamically loaded mach-o binaries. This makes the
// assumption that the current task has send rights to the target task.
class DynamicImages {
public:
explicit DynamicImages(mach_port_t task);
~DynamicImages() {
for (int i = 0; i < GetImageCount(); ++i) {
delete image_list_[i];
}
}
// Returns the number of dynamically loaded mach-o images.
int GetImageCount() const {return static_cast<int>(image_list_.size());}
// Returns an individual image.
DynamicImage *GetImage(int i) {
if (i < (int)image_list_.size()) {
return image_list_[i];
}
return NULL;
}
// Returns the image corresponding to the main executable.
DynamicImage *GetExecutableImage();
int GetExecutableImageIndex();
// Returns the task which we're looking at.
mach_port_t GetTask() const {return task_;}
// CPU type of the task
cpu_type_t GetCPUType() {return cpu_type_;}
// Return true if the task is a 64-bit architecture.
bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; }
// Determine the CPU type of the task being dumped.
static cpu_type_t DetermineTaskCPUType(task_t task);
// Get the native CPU type of this task.
static cpu_type_t GetNativeCPUType() {
#if defined(__i386__)
return CPU_TYPE_I386;
#elif defined(__x86_64__)
return CPU_TYPE_X86_64;
#elif defined(__ppc__)
return CPU_TYPE_POWERPC;
#elif defined(__ppc64__)
return CPU_TYPE_POWERPC64;
#else
#error "GetNativeCPUType not implemented for this architecture"
#endif
}
private:
template<typename MachBits>
friend void ReadImageInfo(DynamicImages& images, uint64_t image_list_address);
bool IsOurTask() {return task_ == mach_task_self();}
// Initialization
void ReadImageInfoForTask();
uint64_t GetDyldAllImageInfosPointer();
mach_port_t task_;
cpu_type_t cpu_type_; // CPU type of task_
vector<DynamicImageRef> image_list_;
};
// Fill bytes with the contents of memory at a particular
// location in another task.
kern_return_t ReadTaskMemory(task_port_t target_task,
const uint64_t address,
size_t length,
vector<uint8_t> &bytes);
} // namespace google_breakpad
#endif // CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__

View File

@ -0,0 +1,813 @@
// 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 <map>
#include <pthread.h>
#include "client/mac/handler/exception_handler.h"
#include "client/mac/handler/minidump_generator.h"
#include "common/mac/macho_utilities.h"
#include "common/mac/scoped_task_suspend-inl.h"
#ifndef USE_PROTECTED_ALLOCATIONS
#define USE_PROTECTED_ALLOCATIONS 0
#endif
// If USE_PROTECTED_ALLOCATIONS is activated then the
// gBreakpadAllocator needs to be setup in other code
// ahead of time. Please see ProtectedMemoryAllocator.h
// for more details.
#if USE_PROTECTED_ALLOCATIONS
#include "protected_memory_allocator.h"
extern ProtectedMemoryAllocator *gBreakpadAllocator;
#endif
namespace google_breakpad {
using std::map;
// These structures and techniques are illustrated in
// Mac OS X Internals, Amit Singh, ch 9.7
struct ExceptionMessage {
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
NDR_record_t ndr;
exception_type_t exception;
mach_msg_type_number_t code_count;
integer_t code[EXCEPTION_CODE_MAX];
char padding[512];
};
struct ExceptionParameters {
ExceptionParameters() : count(0) {}
mach_msg_type_number_t count;
exception_mask_t masks[EXC_TYPES_COUNT];
mach_port_t ports[EXC_TYPES_COUNT];
exception_behavior_t behaviors[EXC_TYPES_COUNT];
thread_state_flavor_t flavors[EXC_TYPES_COUNT];
};
struct ExceptionReplyMessage {
mach_msg_header_t header;
NDR_record_t ndr;
kern_return_t return_code;
};
// Only catch these three exceptions. The other ones are nebulously defined
// and may result in treating a non-fatal exception as fatal.
exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
extern "C"
{
// Forward declarations for functions that need "C" style compilation
boolean_t exc_server(mach_msg_header_t *request,
mach_msg_header_t *reply);
// This symbol must be visible to dlsym() - see
// http://code.google.com/p/google-breakpad/issues/detail?id=345 for details.
kern_return_t catch_exception_raise(mach_port_t target_port,
mach_port_t failed_thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count)
__attribute__((visibility("default")));
kern_return_t ForwardException(mach_port_t task,
mach_port_t failed_thread,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count);
kern_return_t exception_raise(mach_port_t target_port,
mach_port_t failed_thread,
mach_port_t task,
exception_type_t exception,
exception_data_t exception_code,
mach_msg_type_number_t exception_code_count);
kern_return_t
exception_raise_state(mach_port_t target_port,
mach_port_t failed_thread,
mach_port_t task,
exception_type_t exception,
exception_data_t exception_code,
mach_msg_type_number_t code_count,
thread_state_flavor_t *target_flavor,
thread_state_t in_thread_state,
mach_msg_type_number_t in_thread_state_count,
thread_state_t out_thread_state,
mach_msg_type_number_t *out_thread_state_count);
kern_return_t
exception_raise_state_identity(mach_port_t target_port,
mach_port_t failed_thread,
mach_port_t task,
exception_type_t exception,
exception_data_t exception_code,
mach_msg_type_number_t exception_code_count,
thread_state_flavor_t *target_flavor,
thread_state_t in_thread_state,
mach_msg_type_number_t in_thread_state_count,
thread_state_t out_thread_state,
mach_msg_type_number_t *out_thread_state_count);
kern_return_t breakpad_exception_raise_state(mach_port_t exception_port,
exception_type_t exception,
const exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
const thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
);
kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
);
kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count);
}
kern_return_t breakpad_exception_raise_state(mach_port_t exception_port,
exception_type_t exception,
const exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
const thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
)
{
return KERN_SUCCESS;
}
kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
)
{
return KERN_SUCCESS;
}
kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count) {
if (task != mach_task_self()) {
return KERN_FAILURE;
}
return ForwardException(task, failed_thread, exception, code, code_count);
}
ExceptionHandler::ExceptionHandler(const string &dump_path,
FilterCallback filter,
MinidumpCallback callback,
void *callback_context,
bool install_handler,
const char *port_name)
: dump_path_(),
filter_(filter),
callback_(callback),
callback_context_(callback_context),
directCallback_(NULL),
handler_thread_(NULL),
handler_port_(MACH_PORT_NULL),
previous_(NULL),
installed_exception_handler_(false),
is_in_teardown_(false),
last_minidump_write_result_(false),
use_minidump_write_mutex_(false) {
// This will update to the ID and C-string pointers
set_dump_path(dump_path);
MinidumpGenerator::GatherSystemInformation();
if (port_name)
crash_generation_client_.reset(new CrashGenerationClient(port_name));
Setup(install_handler);
}
// special constructor if we want to bypass minidump writing and
// simply get a callback with the exception information
ExceptionHandler::ExceptionHandler(DirectCallback callback,
void *callback_context,
bool install_handler)
: dump_path_(),
filter_(NULL),
callback_(NULL),
callback_context_(callback_context),
directCallback_(callback),
handler_thread_(NULL),
handler_port_(MACH_PORT_NULL),
previous_(NULL),
installed_exception_handler_(false),
is_in_teardown_(false),
last_minidump_write_result_(false),
use_minidump_write_mutex_(false) {
MinidumpGenerator::GatherSystemInformation();
Setup(install_handler);
}
ExceptionHandler::~ExceptionHandler() {
Teardown();
}
bool ExceptionHandler::WriteMinidump(bool write_exception_stream) {
// If we're currently writing, just return
if (use_minidump_write_mutex_)
return false;
use_minidump_write_mutex_ = true;
last_minidump_write_result_ = false;
// Lock the mutex. Since we just created it, this will return immediately.
if (pthread_mutex_lock(&minidump_write_mutex_) == 0) {
// Send an empty message to the handle port so that a minidump will
// be written
SendMessageToHandlerThread(write_exception_stream ?
kWriteDumpWithExceptionMessage :
kWriteDumpMessage);
// Wait for the minidump writer to complete its writing. It will unlock
// the mutex when completed
pthread_mutex_lock(&minidump_write_mutex_);
}
use_minidump_write_mutex_ = false;
UpdateNextID();
return last_minidump_write_result_;
}
// static
bool ExceptionHandler::WriteMinidump(const string &dump_path,
bool write_exception_stream,
MinidumpCallback callback,
void *callback_context) {
ExceptionHandler handler(dump_path, NULL, callback, callback_context, false,
NULL);
return handler.WriteMinidump(write_exception_stream);
}
// static
bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
mach_port_t child_blamed_thread,
const string &dump_path,
MinidumpCallback callback,
void *callback_context) {
ScopedTaskSuspend suspend(child);
MinidumpGenerator generator(child, MACH_PORT_NULL);
string dump_id;
string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id);
generator.SetExceptionInformation(EXC_BREAKPOINT,
#if defined (__i386__) || defined(__x86_64__)
EXC_I386_BPT,
#elif defined (__ppc__) || defined (__ppc64__)
EXC_PPC_BREAKPOINT,
#else
#error architecture not supported
#endif
0,
child_blamed_thread);
bool result = generator.Write(dump_filename.c_str());
if (callback) {
return callback(dump_path.c_str(), dump_id.c_str(),
callback_context, result);
}
return result;
}
bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
int exception_code,
int exception_subcode,
mach_port_t thread_name,
bool exit_after_write) {
bool result = false;
if (directCallback_) {
if (directCallback_(callback_context_,
exception_type,
exception_code,
exception_subcode,
thread_name) ) {
if (exit_after_write)
_exit(exception_type);
}
} else if (IsOutOfProcess()) {
if (exception_type && exception_code) {
// If this is a real exception, give the filter (if any) a chance to
// decide if this should be sent.
if (filter_ && !filter_(callback_context_))
return false;
return crash_generation_client_->RequestDumpForException(
exception_type,
exception_code,
exception_subcode,
thread_name);
}
} else {
string minidump_id;
// Putting the MinidumpGenerator in its own context will ensure that the
// destructor is executed, closing the newly created minidump file.
if (!dump_path_.empty()) {
MinidumpGenerator md;
if (exception_type && exception_code) {
// If this is a real exception, give the filter (if any) a chance to
// decide if this should be sent.
if (filter_ && !filter_(callback_context_))
return false;
md.SetExceptionInformation(exception_type, exception_code,
exception_subcode, thread_name);
}
result = md.Write(next_minidump_path_c_);
}
// Call user specified callback (if any)
if (callback_) {
// If the user callback returned true and we're handling an exception
// (rather than just writing out the file), then we should exit without
// forwarding the exception to the next handler.
if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
result)) {
if (exit_after_write)
_exit(exception_type);
}
}
}
return result;
}
kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count) {
// At this time, we should have called Uninstall() on the exception handler
// so that the current exception ports are the ones that we should be
// forwarding to.
ExceptionParameters current;
current.count = EXC_TYPES_COUNT;
mach_port_t current_task = mach_task_self();
kern_return_t result = task_get_exception_ports(current_task,
s_exception_mask,
current.masks,
&current.count,
current.ports,
current.behaviors,
current.flavors);
// Find the first exception handler that matches the exception
unsigned int found;
for (found = 0; found < current.count; ++found) {
if (current.masks[found] & (1 << exception)) {
break;
}
}
// Nothing to forward
if (found == current.count) {
fprintf(stderr, "** No previous ports for forwarding!! \n");
exit(KERN_FAILURE);
}
mach_port_t target_port = current.ports[found];
exception_behavior_t target_behavior = current.behaviors[found];
thread_state_flavor_t target_flavor = current.flavors[found];
mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX;
breakpad_thread_state_data_t thread_state;
switch (target_behavior) {
case EXCEPTION_DEFAULT:
result = exception_raise(target_port, failed_thread, task, exception,
code, code_count);
break;
case EXCEPTION_STATE:
result = thread_get_state(failed_thread, target_flavor, thread_state,
&thread_state_count);
if (result == KERN_SUCCESS)
result = exception_raise_state(target_port, failed_thread, task,
exception, code,
code_count, &target_flavor,
thread_state, thread_state_count,
thread_state, &thread_state_count);
if (result == KERN_SUCCESS)
result = thread_set_state(failed_thread, target_flavor, thread_state,
thread_state_count);
break;
case EXCEPTION_STATE_IDENTITY:
result = thread_get_state(failed_thread, target_flavor, thread_state,
&thread_state_count);
if (result == KERN_SUCCESS)
result = exception_raise_state_identity(target_port, failed_thread,
task, exception, code,
code_count, &target_flavor,
thread_state,
thread_state_count,
thread_state,
&thread_state_count);
if (result == KERN_SUCCESS)
result = thread_set_state(failed_thread, target_flavor, thread_state,
thread_state_count);
break;
default:
fprintf(stderr, "** Unknown exception behavior\n");
result = KERN_FAILURE;
break;
}
return result;
}
// Callback from exc_server()
kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count) {
if (task != mach_task_self()) {
return KERN_FAILURE;
}
return ForwardException(task, failed_thread, exception, code, code_count);
}
// static
void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
ExceptionHandler *self =
reinterpret_cast<ExceptionHandler *>(exception_handler_class);
ExceptionMessage receive;
// Wait for the exception info
while (1) {
receive.header.msgh_local_port = self->handler_port_;
receive.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(receive));
kern_return_t result = mach_msg(&(receive.header),
MACH_RCV_MSG | MACH_RCV_LARGE, 0,
receive.header.msgh_size,
self->handler_port_,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (result == KERN_SUCCESS) {
// Uninstall our handler so that we don't get in a loop if the process of
// writing out a minidump causes an exception. However, if the exception
// was caused by a fork'd process, don't uninstall things
// If the actual exception code is zero, then we're calling this handler
// in a way that indicates that we want to either exit this thread or
// generate a minidump
//
// While reporting, all threads (except this one) must be suspended
// to avoid misleading stacks. If appropriate they will be resumed
// afterwards.
if (!receive.exception) {
// Don't touch self, since this message could have been sent
// from its destructor.
if (receive.header.msgh_id == kShutdownMessage)
return NULL;
self->SuspendThreads();
#if USE_PROTECTED_ALLOCATIONS
if(gBreakpadAllocator)
gBreakpadAllocator->Unprotect();
#endif
mach_port_t thread = MACH_PORT_NULL;
int exception_type = 0;
int exception_code = 0;
if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) {
thread = receive.thread.name;
exception_type = EXC_BREAKPOINT;
#if defined (__i386__) || defined(__x86_64__)
exception_code = EXC_I386_BPT;
#elif defined (__ppc__) || defined (__ppc64__)
exception_code = EXC_PPC_BREAKPOINT;
#else
#error architecture not supported
#endif
}
// Write out the dump and save the result for later retrieval
self->last_minidump_write_result_ =
self->WriteMinidumpWithException(exception_type, exception_code,
0, thread,
false);
#if USE_PROTECTED_ALLOCATIONS
if(gBreakpadAllocator)
gBreakpadAllocator->Protect();
#endif
self->ResumeThreads();
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
// exceptions that occur in the parent process are caught and
// processed. If the exception was not caused by this task, we
// still need to call into the exception server and have it return
// KERN_FAILURE (see breakpad_exception_raise) in order for the kernel
// to move onto the host exception handler for the child task
if (receive.task.name == mach_task_self()) {
self->SuspendThreads();
#if USE_PROTECTED_ALLOCATIONS
if(gBreakpadAllocator)
gBreakpadAllocator->Unprotect();
#endif
int subcode = 0;
if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1)
subcode = receive.code[1];
// Generate the minidump with the exception data.
self->WriteMinidumpWithException(receive.exception, receive.code[0],
subcode, receive.thread.name, true);
self->UninstallHandler(true);
#if USE_PROTECTED_ALLOCATIONS
if(gBreakpadAllocator)
gBreakpadAllocator->Protect();
#endif
}
// Pass along the exception to the server, which will setup the
// message and call breakpad_exception_raise() and put the return
// code into the reply.
ExceptionReplyMessage reply;
if (!exc_server(&receive.header, &reply.header))
exit(1);
// Send a reply and exit
result = mach_msg(&(reply.header), MACH_SEND_MSG,
reply.header.msgh_size, 0, MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
}
}
}
return NULL;
}
bool ExceptionHandler::InstallHandler() {
try {
#if USE_PROTECTED_ALLOCATIONS
previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) )
ExceptionParameters();
#else
previous_ = new ExceptionParameters();
#endif
}
catch (std::bad_alloc) {
return false;
}
// Save the current exception ports so that we can forward to them
previous_->count = EXC_TYPES_COUNT;
mach_port_t current_task = mach_task_self();
kern_return_t result = task_get_exception_ports(current_task,
s_exception_mask,
previous_->masks,
&previous_->count,
previous_->ports,
previous_->behaviors,
previous_->flavors);
// Setup the exception ports on this task
if (result == KERN_SUCCESS)
result = task_set_exception_ports(current_task, s_exception_mask,
handler_port_, EXCEPTION_DEFAULT,
THREAD_STATE_NONE);
installed_exception_handler_ = (result == KERN_SUCCESS);
return installed_exception_handler_;
}
bool ExceptionHandler::UninstallHandler(bool in_exception) {
kern_return_t result = KERN_SUCCESS;
if (installed_exception_handler_) {
mach_port_t current_task = mach_task_self();
// Restore the previous ports
for (unsigned int i = 0; i < previous_->count; ++i) {
result = task_set_exception_ports(current_task, previous_->masks[i],
previous_->ports[i],
previous_->behaviors[i],
previous_->flavors[i]);
if (result != KERN_SUCCESS)
return false;
}
// this delete should NOT happen if an exception just occurred!
if (!in_exception) {
#if USE_PROTECTED_ALLOCATIONS
previous_->~ExceptionParameters();
#else
delete previous_;
#endif
}
previous_ = NULL;
installed_exception_handler_ = false;
}
return result == KERN_SUCCESS;
}
bool ExceptionHandler::Setup(bool install_handler) {
if (pthread_mutex_init(&minidump_write_mutex_, NULL))
return false;
// Create a receive right
mach_port_t current_task = mach_task_self();
kern_return_t result = mach_port_allocate(current_task,
MACH_PORT_RIGHT_RECEIVE,
&handler_port_);
// Add send right
if (result == KERN_SUCCESS)
result = mach_port_insert_right(current_task, handler_port_, handler_port_,
MACH_MSG_TYPE_MAKE_SEND);
if (install_handler && result == KERN_SUCCESS)
if (!InstallHandler())
return false;
if (result == KERN_SUCCESS) {
// Install the handler in its own thread, detached as we won't be joining.
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int thread_create_result = pthread_create(&handler_thread_, &attr,
&WaitForMessage, this);
pthread_attr_destroy(&attr);
result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS;
}
return result == KERN_SUCCESS ? true : false;
}
bool ExceptionHandler::Teardown() {
kern_return_t result = KERN_SUCCESS;
is_in_teardown_ = true;
if (!UninstallHandler(false))
return false;
// Send an empty message so that the handler_thread exits
if (SendMessageToHandlerThread(kShutdownMessage)) {
mach_port_t current_task = mach_task_self();
result = mach_port_deallocate(current_task, handler_port_);
if (result != KERN_SUCCESS)
return false;
} else {
return false;
}
handler_thread_ = NULL;
handler_port_ = NULL;
pthread_mutex_destroy(&minidump_write_mutex_);
return result == KERN_SUCCESS;
}
bool ExceptionHandler::SendMessageToHandlerThread(
HandlerThreadMessage message_id) {
ExceptionMessage msg;
memset(&msg, 0, sizeof(msg));
msg.header.msgh_id = message_id;
if (message_id == kWriteDumpMessage ||
message_id == kWriteDumpWithExceptionMessage) {
// Include this thread's port.
msg.thread.name = mach_thread_self();
msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND;
msg.thread.type = MACH_MSG_PORT_DESCRIPTOR;
}
msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding);
msg.header.msgh_remote_port = handler_port_;
msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
MACH_MSG_TYPE_MAKE_SEND_ONCE);
kern_return_t result = mach_msg(&(msg.header),
MACH_SEND_MSG | MACH_SEND_TIMEOUT,
msg.header.msgh_size, 0, 0,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
return result == KERN_SUCCESS;
}
void ExceptionHandler::UpdateNextID() {
next_minidump_path_ =
(MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_));
next_minidump_path_c_ = next_minidump_path_.c_str();
next_minidump_id_c_ = next_minidump_id_.c_str();
}
bool ExceptionHandler::SuspendThreads() {
thread_act_port_array_t threads_for_task;
mach_msg_type_number_t thread_count;
if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
return false;
// suspend all of the threads except for this one
for (unsigned int i = 0; i < thread_count; ++i) {
if (threads_for_task[i] != mach_thread_self()) {
if (thread_suspend(threads_for_task[i]))
return false;
}
}
return true;
}
bool ExceptionHandler::ResumeThreads() {
thread_act_port_array_t threads_for_task;
mach_msg_type_number_t thread_count;
if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
return false;
// resume all of the threads except for this one
for (unsigned int i = 0; i < thread_count; ++i) {
if (threads_for_task[i] != mach_thread_self()) {
if (thread_resume(threads_for_task[i]))
return false;
}
}
return true;
}
} // namespace google_breakpad

View File

@ -0,0 +1,259 @@
// 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.
// exception_handler.h: MacOS exception handler
// This class can install a Mach exception port handler to trap most common
// programming errors. If an exception occurs, a minidump file will be
// generated which contains detailed information about the process and the
// exception.
#ifndef CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__
#define CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__
#include <mach/mach.h>
#include <string>
#include "client/mac/crash_generation/crash_generation_client.h"
#include "processor/scoped_ptr.h"
namespace google_breakpad {
using std::string;
struct ExceptionParameters;
enum HandlerThreadMessage {
// Message ID telling the handler thread to write a dump.
kWriteDumpMessage = 0,
// Message ID telling the handler thread to write a dump and include
// an exception stream.
kWriteDumpWithExceptionMessage = 1,
// Message ID telling the handler thread to quit.
kShutdownMessage = 2
};
class ExceptionHandler {
public:
// A callback function to run before Breakpad performs any substantial
// processing of an exception. A FilterCallback is called before writing
// a minidump. context is the parameter supplied by the user as
// callback_context when the handler was created.
//
// If a FilterCallback returns true, Breakpad will continue processing,
// attempting to write a minidump. If a FilterCallback returns false, Breakpad
// will immediately report the exception as unhandled without writing a
// minidump, allowing another handler the opportunity to handle it.
typedef bool (*FilterCallback)(void *context);
// A callback function to run after the minidump has been written.
// |minidump_id| is a unique id for the dump, so the minidump
// file is <dump_dir>/<minidump_id>.dmp.
// |context| is the value passed into the constructor.
// |succeeded| indicates whether a minidump file was successfully written.
// Return true if the exception was fully handled and breakpad should exit.
// Return false to allow any other exception handlers to process the
// exception.
typedef bool (*MinidumpCallback)(const char *dump_dir,
const char *minidump_id,
void *context, bool succeeded);
// A callback function which will be called directly if an exception occurs.
// This bypasses the minidump file writing and simply gives the client
// the exception information.
typedef bool (*DirectCallback)( void *context,
int exception_type,
int exception_code,
int exception_subcode,
mach_port_t thread_name);
// Creates a new ExceptionHandler instance to handle writing minidumps.
// Minidump files will be written to dump_path, and the optional callback
// is called after writing the dump file, as described above.
// If install_handler is true, then a minidump will be written whenever
// an unhandled exception occurs. If it is false, minidumps will only
// be written when WriteMinidump is called.
// If port_name is non-NULL, attempt to perform out-of-process dump generation
// If port_name is NULL, in-process dump generation will be used.
ExceptionHandler(const string &dump_path,
FilterCallback filter, MinidumpCallback callback,
void *callback_context, bool install_handler,
const char *port_name);
// A special constructor if we want to bypass minidump writing and
// simply get a callback with the exception information.
ExceptionHandler(DirectCallback callback,
void *callback_context,
bool install_handler);
~ExceptionHandler();
// Get and set the minidump path.
string dump_path() const { return dump_path_; }
void set_dump_path(const string &dump_path) {
dump_path_ = dump_path;
dump_path_c_ = dump_path_.c_str();
UpdateNextID(); // Necessary to put dump_path_ in next_minidump_path_.
}
// Writes a minidump immediately. This can be used to capture the
// execution state independently of a crash. Returns true on success.
bool WriteMinidump() {
return WriteMinidump(false);
}
bool WriteMinidump(bool write_exception_stream);
// Convenience form of WriteMinidump which does not require an
// ExceptionHandler instance.
static bool WriteMinidump(const string &dump_path, MinidumpCallback callback,
void *callback_context) {
return WriteMinidump(dump_path, false, callback, callback_context);
}
static bool WriteMinidump(const string &dump_path,
bool write_exception_stream,
MinidumpCallback callback,
void *callback_context);
// Write a minidump of child immediately. This can be used to capture
// the execution state of a child process independently of a crash.
static bool WriteMinidumpForChild(mach_port_t child,
mach_port_t child_blamed_thread,
const std::string &dump_path,
MinidumpCallback callback,
void *callback_context);
// Returns whether out-of-process dump generation is used or not.
bool IsOutOfProcess() const {
return crash_generation_client_.get() != NULL;
}
private:
// Install the mach exception handler
bool InstallHandler();
// Uninstall the mach exception handler (if any)
bool UninstallHandler(bool in_exception);
// Setup the handler thread, and if |install_handler| is true, install the
// mach exception port handler
bool Setup(bool install_handler);
// Uninstall the mach exception handler (if any) and terminate the helper
// thread
bool Teardown();
// Send a mach message to the exception handler. Return true on
// success, false otherwise.
bool SendMessageToHandlerThread(HandlerThreadMessage message_id);
// All minidump writing goes through this one routine
bool WriteMinidumpWithException(int exception_type,
int exception_code,
int exception_subcode,
mach_port_t thread_name,
bool exit_after_write);
// When installed, this static function will be call from a newly created
// pthread with |this| as the argument
static void *WaitForMessage(void *exception_handler_class);
// disallow copy ctor and operator=
explicit ExceptionHandler(const ExceptionHandler &);
void operator=(const ExceptionHandler &);
// Generates a new ID and stores it in next_minidump_id_, and stores the
// path of the next minidump to be written in next_minidump_path_.
void UpdateNextID();
// These functions will suspend/resume all threads except for the
// reporting thread
bool SuspendThreads();
bool ResumeThreads();
// The destination directory for the minidump
string dump_path_;
// The basename of the next minidump w/o extension
string next_minidump_id_;
// The full path to the next minidump to be written, including extension
string next_minidump_path_;
// Pointers to the UTF-8 versions of above
const char *dump_path_c_;
const char *next_minidump_id_c_;
const char *next_minidump_path_c_;
// The callback function and pointer to be passed back after the minidump
// has been written
FilterCallback filter_;
MinidumpCallback callback_;
void *callback_context_;
// The callback function to be passed back when we don't want a minidump
// file to be written
DirectCallback directCallback_;
// The thread that is created for the handler
pthread_t handler_thread_;
// The port that is waiting on an exception message to be sent, if the
// handler is installed
mach_port_t handler_port_;
// These variables save the previous exception handler's data so that it
// can be re-installed when this handler is uninstalled
ExceptionParameters *previous_;
// True, if we've installed the exception handler
bool installed_exception_handler_;
// True, if we're in the process of uninstalling the exception handler and
// the thread.
bool is_in_teardown_;
// Save the last result of the last minidump
bool last_minidump_write_result_;
// A mutex for use when writing out a minidump that was requested on a
// thread other than the exception handler.
pthread_mutex_t minidump_write_mutex_;
// True, if we're using the mutext to indicate when mindump writing occurs
bool use_minidump_write_mutex_;
// Client for out-of-process dump generation.
scoped_ptr<CrashGenerationClient> crash_generation_client_;
};
} // namespace google_breakpad
#endif // CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,191 @@
// 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.
// minidump_generator.h: Create a minidump of the current MacOS process.
#ifndef CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__
#define CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__
#include <mach/mach.h>
#include <string>
#include "client/minidump_file_writer.h"
#include "common/memory.h"
#include "common/mac/macho_utilities.h"
#include "google_breakpad/common/minidump_format.h"
#include "dynamic_images.h"
namespace google_breakpad {
using std::string;
const u_int64_t TOP_OF_THREAD0_STACK_64BIT = 0x00007fff5fbff000LL;
const u_int32_t TOP_OF_THREAD0_STACK_32BIT = 0xbffff000;
// Use the REGISTER_FROM_THREADSTATE to access a register name from the
// breakpad_thread_state_t structure.
#if __DARWIN_UNIX03 || TARGET_CPU_X86_64 || TARGET_CPU_PPC64
// In The 10.5 SDK Headers Apple prepended __ to the variable names in the
// i386_thread_state_t structure. There's no good way to tell what version of
// the SDK we're compiling against so we just toggle on the same preprocessor
// symbol Apple's headers use.
#define REGISTER_FROM_THREADSTATE(a, b) ((a)->__ ## b)
#else
#define REGISTER_FROM_THREADSTATE(a, b) (a->b)
#endif
// Creates a minidump file of the current process. If there is exception data,
// use SetExceptionInformation() to add this to the minidump. The minidump
// file is generated by the Write() function.
// Usage:
// MinidumpGenerator minidump();
// minidump.Write("/tmp/minidump");
//
class MinidumpGenerator {
public:
MinidumpGenerator();
MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread);
~MinidumpGenerator();
// Return <dir>/<unique_name>.dmp
// Sets |unique_name| (if requested) to the unique name for the minidump
static string UniqueNameInDirectory(const string &dir, string *unique_name);
// Write out the minidump into |path|
// All of the components of |path| must exist and be writable
// Return true if successful, false otherwise
bool Write(const char *path);
// Specify some exception information, if applicable
void SetExceptionInformation(int type, int code, int subcode,
mach_port_t thread_name) {
exception_type_ = type;
exception_code_ = code;
exception_subcode_ = subcode;
exception_thread_ = thread_name;
}
// Gather system information. This should be call at least once before using
// the MinidumpGenerator class.
static void GatherSystemInformation();
private:
typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *);
// Stream writers
bool WriteThreadListStream(MDRawDirectory *thread_list_stream);
bool WriteMemoryListStream(MDRawDirectory *memory_list_stream);
bool WriteExceptionStream(MDRawDirectory *exception_stream);
bool WriteSystemInfoStream(MDRawDirectory *system_info_stream);
bool WriteModuleListStream(MDRawDirectory *module_list_stream);
bool WriteMiscInfoStream(MDRawDirectory *misc_info_stream);
bool WriteBreakpadInfoStream(MDRawDirectory *breakpad_info_stream);
// Helpers
u_int64_t CurrentPCForStack(breakpad_thread_state_data_t state);
bool GetThreadState(thread_act_t target_thread, thread_state_t state,
mach_msg_type_number_t *count);
bool WriteStackFromStartAddress(mach_vm_address_t start_addr,
MDMemoryDescriptor *stack_location);
bool WriteStack(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContext(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);
bool WriteCVRecord(MDRawModule *module, int cpu_type,
const char *module_path);
bool WriteModuleStream(unsigned int index, MDRawModule *module);
size_t CalculateStackSize(mach_vm_address_t start_addr);
int FindExecutableModule();
// Per-CPU implementations of these methods
bool WriteStackPPC(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContextPPC(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
u_int64_t CurrentPCForStackPPC(breakpad_thread_state_data_t state);
bool WriteStackPPC64(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContextPPC64(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
u_int64_t CurrentPCForStackPPC64(breakpad_thread_state_data_t state);
bool WriteStackX86(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContextX86(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
u_int64_t CurrentPCForStackX86(breakpad_thread_state_data_t state);
bool WriteStackX86_64(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContextX86_64(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
u_int64_t CurrentPCForStackX86_64(breakpad_thread_state_data_t state);
// disallow copy ctor and operator=
explicit MinidumpGenerator(const MinidumpGenerator &);
void operator=(const MinidumpGenerator &);
// Use this writer to put the data to disk
MinidumpFileWriter writer_;
// Exception information
int exception_type_;
int exception_code_;
int exception_subcode_;
mach_port_t exception_thread_;
mach_port_t crashing_task_;
mach_port_t handler_thread_;
// CPU type of the task being dumped.
cpu_type_t cpu_type_;
// System information
static char build_string_[16];
static int os_major_version_;
static int os_minor_version_;
static int os_build_number_;
// Information about dynamically loaded code
DynamicImages *dynamic_images_;
// PageAllocator makes it possible to allocate memory
// directly from the system, even while handling an exception.
mutable PageAllocator allocator_;
// Blocks of memory written to the dump. These are all currently
// written while writing the thread list stream, but saved here
// so a memory list stream can be written afterwards.
wasteful_vector<MDMemoryDescriptor> memory_blocks_;
};
} // namespace google_breakpad
#endif // CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__

View File

@ -0,0 +1,834 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
8BFC813F11FF9A58002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814411FF9A9C002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814511FF9A9D002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814811FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814911FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814A11FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814B11FF9B3F002CB4DC /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */; };
8BFC814C11FF9B3F002CB4DC /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; };
8BFC81A211FF9C2E002CB4DC /* CPlusTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */; };
8BFC81A311FF9C2F002CB4DC /* CPlusTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */; };
8BFC81AD11FF9C8A002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
8BFC81AE11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
8BFC81AF11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
8BFC81B011FF9C8D002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
9B35FF5A0B267D5F008DE8C7 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.c */; };
9B35FF5B0B267D5F008DE8C7 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; };
9B37CEEC0AF98ECD00FA4BD4 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */; };
9B7CA7700B12873A00CD3A1D /* minidump_file_writer-inl.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BE3C01E0B0CE329009892DF /* minidump_file_writer-inl.h */; };
9B7CA8540B12989000CD3A1D /* minidump_file_writer_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */; };
9B7CA8550B1298A100CD3A1D /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C230B01344C0055103E /* minidump_file_writer.cc */; };
9BC1D2940B336F2300F2A2B4 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.c */; };
9BC1D2950B336F2500F2A2B4 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; };
9BD82AC10B0029DF0055103E /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */; };
9BD82BFF0B01333D0055103E /* exception_handler_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82BFD0B01333D0055103E /* exception_handler_test.cc */; };
9BD82C020B01333D0055103E /* minidump_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82BFE0B01333D0055103E /* minidump_generator_test.cc */; };
9BD82C0D0B0133520055103E /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C090B0133520055103E /* exception_handler.cc */; };
9BD82C0E0B0133520055103E /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C0B0B0133520055103E /* minidump_generator.cc */; };
9BD82C0F0B0133520055103E /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C090B0133520055103E /* exception_handler.cc */; };
9BD82C100B0133520055103E /* exception_handler.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C0A0B0133520055103E /* exception_handler.h */; };
9BD82C110B0133520055103E /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C0B0B0133520055103E /* minidump_generator.cc */; };
9BD82C120B0133520055103E /* minidump_generator.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C0C0B0133520055103E /* minidump_generator.h */; };
9BD82C250B01344C0055103E /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C230B01344C0055103E /* minidump_file_writer.cc */; };
9BD82C260B01344C0055103E /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C230B01344C0055103E /* minidump_file_writer.cc */; };
9BD82C270B01344C0055103E /* minidump_file_writer.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C240B01344C0055103E /* minidump_file_writer.h */; };
9BD82C2D0B01345E0055103E /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C2B0B01345E0055103E /* string_utilities.cc */; };
9BD82C2E0B01345E0055103E /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C2B0B01345E0055103E /* string_utilities.cc */; };
9BD82C2F0B01345E0055103E /* string_utilities.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C2C0B01345E0055103E /* string_utilities.h */; };
D2F651000BEF947200920385 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; };
D2F651010BEF947200920385 /* file_id.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F650FB0BEF947200920385 /* file_id.h */; };
D2F651020BEF947200920385 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FC0BEF947200920385 /* macho_id.cc */; };
D2F651030BEF947200920385 /* macho_id.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F650FD0BEF947200920385 /* macho_id.h */; };
D2F651040BEF947200920385 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; };
D2F651050BEF947200920385 /* macho_utilities.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F650FF0BEF947200920385 /* macho_utilities.h */; };
D2F651090BEF949A00920385 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; };
D2F6510A0BEF949A00920385 /* dynamic_images.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F651080BEF949A00920385 /* dynamic_images.h */; };
D2F6510E0BEF94EB00920385 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F6510C0BEF94EB00920385 /* macho_walker.cc */; };
D2F6510F0BEF94EB00920385 /* macho_walker.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F6510D0BEF94EB00920385 /* macho_walker.h */; };
D2F651110BEF951700920385 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; };
D2F651130BEF951C00920385 /* string_conversion.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B35FF590B267D5F008DE8C7 /* string_conversion.h */; };
D2F651150BEF953000920385 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.c */; };
D2F651160BEF953100920385 /* convert_UTF.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B35FF570B267D5F008DE8C7 /* convert_UTF.h */; };
D2F6511B0BEF970E00920385 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; };
D2F6511D0BEF973500920385 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; };
D2F6511E0BEF973600920385 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FC0BEF947200920385 /* macho_id.cc */; };
D2F6511F0BEF973900920385 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; };
D2F651210BEF975400920385 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F6510C0BEF94EB00920385 /* macho_walker.cc */; };
F93A887D0E8B4C8C0026AF89 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F6510C0BEF94EB00920385 /* macho_walker.cc */; };
F93A887E0E8B4C8C0026AF89 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FC0BEF947200920385 /* macho_id.cc */; };
F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; };
F93A88800E8B4C8C0026AF89 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; };
F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9721F310E8B07E800D7E813 /* dwarftests.mm */; };
F93A88870E8B4C9A0026AF89 /* dump_syms.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9721F390E8B0D0D00D7E813 /* dump_syms.mm */; };
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F760E8B0DC700D7E813 /* bytereader.cc */; };
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */; };
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F780E8B0DC700D7E813 /* functioninfo.cc */; };
F93A888B0E8B4C9A0026AF89 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = F9721FA80E8B0E4800D7E813 /* md5.c */; };
F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; };
F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */; };
F982089C0DB3280D0017AECA /* breakpad_nlist_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */; };
F98208A30DB32CAE0017AECA /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
F9AE5B390DBFDBDB00505983 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; };
F9AE5B3A0DBFDBDB00505983 /* DynamicImagesTests.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */; };
F9B34E870DBC1E1600306484 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; };
F9C5A4220DB82DD800209C76 /* DynamicImagesTests.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
8DD76F690486A84900D96B5E /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
9BD82C100B0133520055103E /* exception_handler.h in CopyFiles */,
9BD82C120B0133520055103E /* minidump_generator.h in CopyFiles */,
9BD82C270B01344C0055103E /* minidump_file_writer.h in CopyFiles */,
9BD82C2F0B01345E0055103E /* string_utilities.h in CopyFiles */,
9B7CA7700B12873A00CD3A1D /* minidump_file_writer-inl.h in CopyFiles */,
D2F651010BEF947200920385 /* file_id.h in CopyFiles */,
D2F651030BEF947200920385 /* macho_id.h in CopyFiles */,
D2F651050BEF947200920385 /* macho_utilities.h in CopyFiles */,
D2F6510A0BEF949A00920385 /* dynamic_images.h in CopyFiles */,
D2F6510F0BEF94EB00920385 /* macho_walker.h in CopyFiles */,
D2F651130BEF951C00920385 /* string_conversion.h in CopyFiles */,
D2F651160BEF953100920385 /* convert_UTF.h in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Breakpad.xcconfig; path = ../../../common/mac/Breakpad.xcconfig; sourceTree = SOURCE_ROOT; };
8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadDebug.xcconfig; path = ../../../common/mac/BreakpadDebug.xcconfig; sourceTree = SOURCE_ROOT; };
8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadRelease.xcconfig; path = ../../../common/mac/BreakpadRelease.xcconfig; sourceTree = SOURCE_ROOT; };
8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; };
8BFC815411FF9B7F002CB4DC /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
8BFC819211FF9C23002CB4DC /* CPlusTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CPlusTest.framework; path = Library/Frameworks/CPlusTest.framework; sourceTree = DEVELOPER_DIR; };
8DD76F6C0486A84900D96B5E /* generator_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = generator_test; sourceTree = BUILT_PRODUCTS_DIR; };
9B35FF560B267D5F008DE8C7 /* convert_UTF.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = convert_UTF.c; path = ../../../common/convert_UTF.c; sourceTree = SOURCE_ROOT; };
9B35FF570B267D5F008DE8C7 /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = convert_UTF.h; path = ../../../common/convert_UTF.h; sourceTree = SOURCE_ROOT; };
9B35FF580B267D5F008DE8C7 /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = string_conversion.cc; path = ../../../common/string_conversion.cc; sourceTree = SOURCE_ROOT; };
9B35FF590B267D5F008DE8C7 /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = string_conversion.h; path = ../../../common/string_conversion.h; sourceTree = SOURCE_ROOT; };
9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
9B7CA84E0B1297F200CD3A1D /* unit_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unit_test; sourceTree = BUILT_PRODUCTS_DIR; };
9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer_unittest.cc; path = ../../minidump_file_writer_unittest.cc; sourceTree = "<group>"; };
9BD82A9B0B00267E0055103E /* handler_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = handler_test; sourceTree = BUILT_PRODUCTS_DIR; };
9BD82BFD0B01333D0055103E /* exception_handler_test.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = exception_handler_test.cc; sourceTree = SOURCE_ROOT; };
9BD82BFE0B01333D0055103E /* minidump_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_generator_test.cc; sourceTree = SOURCE_ROOT; };
9BD82C090B0133520055103E /* exception_handler.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = exception_handler.cc; sourceTree = SOURCE_ROOT; };
9BD82C0A0B0133520055103E /* exception_handler.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = exception_handler.h; sourceTree = SOURCE_ROOT; };
9BD82C0B0B0133520055103E /* minidump_generator.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_generator.cc; sourceTree = SOURCE_ROOT; };
9BD82C0C0B0133520055103E /* minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = minidump_generator.h; sourceTree = SOURCE_ROOT; };
9BD82C230B01344C0055103E /* minidump_file_writer.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer.cc; path = ../../minidump_file_writer.cc; sourceTree = SOURCE_ROOT; };
9BD82C240B01344C0055103E /* minidump_file_writer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = minidump_file_writer.h; path = ../../minidump_file_writer.h; sourceTree = SOURCE_ROOT; };
9BD82C2B0B01345E0055103E /* string_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = string_utilities.cc; path = ../../../common/mac/string_utilities.cc; sourceTree = SOURCE_ROOT; };
9BD82C2C0B01345E0055103E /* string_utilities.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = string_utilities.h; path = ../../../common/mac/string_utilities.h; sourceTree = SOURCE_ROOT; };
9BE3C01E0B0CE329009892DF /* minidump_file_writer-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "minidump_file_writer-inl.h"; path = "../../minidump_file_writer-inl.h"; sourceTree = SOURCE_ROOT; };
D2F650FA0BEF947200920385 /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = file_id.cc; path = ../../../common/mac/file_id.cc; sourceTree = SOURCE_ROOT; };
D2F650FB0BEF947200920385 /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = file_id.h; path = ../../../common/mac/file_id.h; sourceTree = SOURCE_ROOT; };
D2F650FC0BEF947200920385 /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_id.cc; path = ../../../common/mac/macho_id.cc; sourceTree = SOURCE_ROOT; };
D2F650FD0BEF947200920385 /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_id.h; path = ../../../common/mac/macho_id.h; sourceTree = SOURCE_ROOT; };
D2F650FE0BEF947200920385 /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_utilities.cc; path = ../../../common/mac/macho_utilities.cc; sourceTree = SOURCE_ROOT; };
D2F650FF0BEF947200920385 /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_utilities.h; path = ../../../common/mac/macho_utilities.h; sourceTree = SOURCE_ROOT; };
D2F651070BEF949A00920385 /* dynamic_images.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = dynamic_images.cc; sourceTree = "<group>"; };
D2F651080BEF949A00920385 /* dynamic_images.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dynamic_images.h; sourceTree = "<group>"; };
D2F6510C0BEF94EB00920385 /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_walker.cc; path = ../../../common/mac/macho_walker.cc; sourceTree = SOURCE_ROOT; };
D2F6510D0BEF94EB00920385 /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_walker.h; path = ../../../common/mac/macho_walker.h; sourceTree = SOURCE_ROOT; };
F917C4F70E03265A00F86017 /* breakpad_exc_server.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = breakpad_exc_server.c; sourceTree = "<group>"; };
F917C4F80E03265A00F86017 /* breakpad_exc_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_exc_server.h; sourceTree = "<group>"; };
F93A88750E8B4C700026AF89 /* octestcases.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = octestcases.octest; sourceTree = BUILT_PRODUCTS_DIR; };
F93A88760E8B4C700026AF89 /* obj-cTestCases-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "obj-cTestCases-Info.plist"; sourceTree = "<group>"; };
F9721F300E8B07E800D7E813 /* dwarftests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dwarftests.h; sourceTree = "<group>"; };
F9721F310E8B07E800D7E813 /* dwarftests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = dwarftests.mm; sourceTree = "<group>"; };
F9721F380E8B0CFC00D7E813 /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = SOURCE_ROOT; };
F9721F390E8B0D0D00D7E813 /* dump_syms.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.mm; path = ../../../common/mac/dump_syms.mm; sourceTree = SOURCE_ROOT; };
F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
F9721F760E8B0DC700D7E813 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; };
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; };
F9721F780E8B0DC700D7E813 /* functioninfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functioninfo.cc; path = ../../../common/dwarf/functioninfo.cc; sourceTree = SOURCE_ROOT; };
F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
F9721FA80E8B0E4800D7E813 /* md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.c; path = ../../../common/md5.c; sourceTree = SOURCE_ROOT; };
F982089A0DB3280D0017AECA /* breakpad_nlist_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_test.h; sourceTree = "<group>"; };
F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_test.cc; sourceTree = "<group>"; };
F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_64.cc; sourceTree = "<group>"; };
F98208A20DB32CAE0017AECA /* breakpad_nlist_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_64.h; sourceTree = "<group>"; };
F9AE19B50DB040E300C98454 /* minidump_tests32-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "minidump_tests32-Info.plist"; sourceTree = "<group>"; };
F9AE19C30DB04A9500C98454 /* minidump_tests64.cptest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = minidump_tests64.cptest; sourceTree = BUILT_PRODUCTS_DIR; };
F9AE5B330DBFDBA300505983 /* minidump_tests32.cptest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = minidump_tests32.cptest; sourceTree = BUILT_PRODUCTS_DIR; };
F9AE5B340DBFDBA300505983 /* minidump_tests64-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "minidump_tests64-Info.plist"; sourceTree = "<group>"; };
F9C5A4200DB82DD800209C76 /* DynamicImagesTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DynamicImagesTests.h; sourceTree = "<group>"; };
F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DynamicImagesTests.cc; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
8DD76F660486A84900D96B5E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9B37CEEC0AF98ECD00FA4BD4 /* CoreFoundation.framework in Frameworks */,
8BFC813F11FF9A58002CB4DC /* libcrypto.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9B7CA84C0B1297F200CD3A1D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8BFC814511FF9A9D002CB4DC /* libcrypto.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9BD82A990B00267E0055103E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9BD82AC10B0029DF0055103E /* CoreFoundation.framework in Frameworks */,
8BFC814411FF9A9C002CB4DC /* libcrypto.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F93A88720E8B4C700026AF89 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8BFC814A11FF9B13002CB4DC /* libcrypto.dylib in Frameworks */,
8BFC814B11FF9B3F002CB4DC /* SenTestingKit.framework in Frameworks */,
8BFC814C11FF9B3F002CB4DC /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F9AE19C00DB04A9500C98454 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8BFC814811FF9B13002CB4DC /* libcrypto.dylib in Frameworks */,
8BFC81A211FF9C2E002CB4DC /* CPlusTest.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F9AE5B300DBFDBA300505983 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */,
F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */,
8BFC814911FF9B13002CB4DC /* libcrypto.dylib in Frameworks */,
8BFC81A311FF9C2F002CB4DC /* CPlusTest.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
08FB7794FE84155DC02AAC07 /* MinidumpWriter */ = {
isa = PBXGroup;
children = (
8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */,
8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */,
8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */,
F9721FA80E8B0E4800D7E813 /* md5.c */,
F9721F760E8B0DC700D7E813 /* bytereader.cc */,
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */,
F9721F780E8B0DC700D7E813 /* functioninfo.cc */,
F9721F390E8B0D0D00D7E813 /* dump_syms.mm */,
F9721F380E8B0CFC00D7E813 /* dump_syms.h */,
F917C4F70E03265A00F86017 /* breakpad_exc_server.c */,
F917C4F80E03265A00F86017 /* breakpad_exc_server.h */,
F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */,
F98208A20DB32CAE0017AECA /* breakpad_nlist_64.h */,
D2F6510C0BEF94EB00920385 /* macho_walker.cc */,
D2F6510D0BEF94EB00920385 /* macho_walker.h */,
D2F651070BEF949A00920385 /* dynamic_images.cc */,
D2F651080BEF949A00920385 /* dynamic_images.h */,
D2F650FA0BEF947200920385 /* file_id.cc */,
D2F650FB0BEF947200920385 /* file_id.h */,
D2F650FC0BEF947200920385 /* macho_id.cc */,
D2F650FD0BEF947200920385 /* macho_id.h */,
D2F650FE0BEF947200920385 /* macho_utilities.cc */,
D2F650FF0BEF947200920385 /* macho_utilities.h */,
F9C5A41F0DB82DB000209C76 /* testcases */,
9BD82C040B0133420055103E /* Breakpad */,
08FB7795FE84155DC02AAC07 /* Source */,
9B37CEEA0AF98EB600FA4BD4 /* Frameworks */,
1AB674ADFE9D54B511CA2CBB /* Products */,
F9AE19B50DB040E300C98454 /* minidump_tests32-Info.plist */,
F9AE5B340DBFDBA300505983 /* minidump_tests64-Info.plist */,
F93A88760E8B4C700026AF89 /* obj-cTestCases-Info.plist */,
);
name = MinidumpWriter;
sourceTree = "<group>";
};
08FB7795FE84155DC02AAC07 /* Source */ = {
isa = PBXGroup;
children = (
9BD82BFD0B01333D0055103E /* exception_handler_test.cc */,
9BD82BFE0B01333D0055103E /* minidump_generator_test.cc */,
9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */,
);
name = Source;
sourceTree = "<group>";
};
1AB674ADFE9D54B511CA2CBB /* Products */ = {
isa = PBXGroup;
children = (
8DD76F6C0486A84900D96B5E /* generator_test */,
9BD82A9B0B00267E0055103E /* handler_test */,
9B7CA84E0B1297F200CD3A1D /* unit_test */,
F9AE19C30DB04A9500C98454 /* minidump_tests64.cptest */,
F9AE5B330DBFDBA300505983 /* minidump_tests32.cptest */,
F93A88750E8B4C700026AF89 /* octestcases.octest */,
);
name = Products;
sourceTree = "<group>";
};
9B37CEEA0AF98EB600FA4BD4 /* Frameworks */ = {
isa = PBXGroup;
children = (
8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */,
8BFC815411FF9B7F002CB4DC /* Carbon.framework */,
F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */,
F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */,
9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */,
8BFC819211FF9C23002CB4DC /* CPlusTest.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9BD82C040B0133420055103E /* Breakpad */ = {
isa = PBXGroup;
children = (
9B35FF560B267D5F008DE8C7 /* convert_UTF.c */,
9B35FF570B267D5F008DE8C7 /* convert_UTF.h */,
9B35FF580B267D5F008DE8C7 /* string_conversion.cc */,
9B35FF590B267D5F008DE8C7 /* string_conversion.h */,
9BD82C090B0133520055103E /* exception_handler.cc */,
9BD82C0A0B0133520055103E /* exception_handler.h */,
9BD82C0B0B0133520055103E /* minidump_generator.cc */,
9BD82C0C0B0133520055103E /* minidump_generator.h */,
9BD82C230B01344C0055103E /* minidump_file_writer.cc */,
9BE3C01E0B0CE329009892DF /* minidump_file_writer-inl.h */,
9BD82C240B01344C0055103E /* minidump_file_writer.h */,
9BD82C2B0B01345E0055103E /* string_utilities.cc */,
9BD82C2C0B01345E0055103E /* string_utilities.h */,
);
name = Breakpad;
sourceTree = "<group>";
};
F9C5A41F0DB82DB000209C76 /* testcases */ = {
isa = PBXGroup;
children = (
F982089A0DB3280D0017AECA /* breakpad_nlist_test.h */,
F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */,
F9C5A4200DB82DD800209C76 /* DynamicImagesTests.h */,
F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */,
F9721F300E8B07E800D7E813 /* dwarftests.h */,
F9721F310E8B07E800D7E813 /* dwarftests.mm */,
);
path = testcases;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
8DD76F620486A84900D96B5E /* generator_test */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "generator_test" */;
buildPhases = (
8DD76F640486A84900D96B5E /* Sources */,
8DD76F660486A84900D96B5E /* Frameworks */,
8DD76F690486A84900D96B5E /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = generator_test;
productInstallPath = "$(HOME)/bin";
productName = MinidumpWriter;
productReference = 8DD76F6C0486A84900D96B5E /* generator_test */;
productType = "com.apple.product-type.tool";
};
9B7CA84D0B1297F200CD3A1D /* unit_test */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9B7CA8500B12984300CD3A1D /* Build configuration list for PBXNativeTarget "unit_test" */;
buildPhases = (
9B7CA84B0B1297F200CD3A1D /* Sources */,
9B7CA84C0B1297F200CD3A1D /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = unit_test;
productName = "filewriter unit test";
productReference = 9B7CA84E0B1297F200CD3A1D /* unit_test */;
productType = "com.apple.product-type.tool";
};
9BD82A9A0B00267E0055103E /* handler_test */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9BD82AA60B0026BF0055103E /* Build configuration list for PBXNativeTarget "handler_test" */;
buildPhases = (
9BD82A980B00267E0055103E /* Sources */,
9BD82A990B00267E0055103E /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = handler_test;
productName = ExceptionTester;
productReference = 9BD82A9B0B00267E0055103E /* handler_test */;
productType = "com.apple.product-type.tool";
};
F93A88740E8B4C700026AF89 /* obj-c_TestCases */ = {
isa = PBXNativeTarget;
buildConfigurationList = F93A88790E8B4C700026AF89 /* Build configuration list for PBXNativeTarget "obj-c_TestCases" */;
buildPhases = (
F93A88700E8B4C700026AF89 /* Resources */,
F93A88710E8B4C700026AF89 /* Sources */,
F93A88720E8B4C700026AF89 /* Frameworks */,
F93A88730E8B4C700026AF89 /* ShellScript */,
);
buildRules = (
);
dependencies = (
);
name = "obj-c_TestCases";
productName = octestcases;
productReference = F93A88750E8B4C700026AF89 /* octestcases.octest */;
productType = "com.apple.product-type.bundle";
};
F9AE19C20DB04A9500C98454 /* minidump_tests64 */ = {
isa = PBXNativeTarget;
buildConfigurationList = F9AE19C70DB04AA200C98454 /* Build configuration list for PBXNativeTarget "minidump_tests64" */;
buildPhases = (
F9AE19BE0DB04A9500C98454 /* Resources */,
F9AE19BF0DB04A9500C98454 /* Sources */,
F9AE19C00DB04A9500C98454 /* Frameworks */,
F9AE19C10DB04A9500C98454 /* ShellScript */,
);
buildRules = (
);
dependencies = (
);
name = minidump_tests64;
productName = minidump_tests;
productReference = F9AE19C30DB04A9500C98454 /* minidump_tests64.cptest */;
productType = "com.apple.product-type.bundle";
};
F9AE5B320DBFDBA300505983 /* minidump_tests32 */ = {
isa = PBXNativeTarget;
buildConfigurationList = F9AE5B380DBFDBA300505983 /* Build configuration list for PBXNativeTarget "minidump_tests32" */;
buildPhases = (
F9AE5B2E0DBFDBA300505983 /* Resources */,
F9AE5B2F0DBFDBA300505983 /* Sources */,
F9AE5B300DBFDBA300505983 /* Frameworks */,
F9AE5B310DBFDBA300505983 /* ShellScript */,
);
buildRules = (
);
dependencies = (
);
name = minidump_tests32;
productName = Untitled;
productReference = F9AE5B330DBFDBA300505983 /* minidump_tests32.cptest */;
productType = "com.apple.product-type.bundle";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */;
compatibilityVersion = "Xcode 3.2";
hasScannedForEncodings = 1;
mainGroup = 08FB7794FE84155DC02AAC07 /* MinidumpWriter */;
projectDirPath = "";
projectRoot = "";
targets = (
8DD76F620486A84900D96B5E /* generator_test */,
9BD82A9A0B00267E0055103E /* handler_test */,
9B7CA84D0B1297F200CD3A1D /* unit_test */,
F9AE19C20DB04A9500C98454 /* minidump_tests64 */,
F9AE5B320DBFDBA300505983 /* minidump_tests32 */,
F93A88740E8B4C700026AF89 /* obj-c_TestCases */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
F93A88700E8B4C700026AF89 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
F9AE19BE0DB04A9500C98454 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
F9AE5B2E0DBFDBA300505983 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
F93A88730E8B4C700026AF89 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n";
};
F9AE19C10DB04A9500C98454 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n# Run gcov on the framework getting tested\nif [ \"${CONFIGURATION}\" = 'Coverage' ];\nthen\n FRAMEWORK_NAME=minidump_tests64\n FRAMEWORK_OBJ_DIR=${OBJROOT}/${PROJECT_NAME}.build/${CONFIGURATION}/${FRAMEWORK_NAME}.build/Objects-normal/${NATIVE_ARCH_ACTUAL}\n mkdir -p coverage\n pushd coverage\n echo find ${OBJROOT} -name *.gcda -exec gcov -o ${FRAMEWORK_OBJ_DIR} {} \\;\n find ${OBJROOT} -name *.gcda -exec gcov -o ${FRAMEWORK_OBJ_DIR} {} \\;\n popd\nfi ";
};
F9AE5B310DBFDBA300505983 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
8DD76F640486A84900D96B5E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9BD82C020B01333D0055103E /* minidump_generator_test.cc in Sources */,
9BD82C0F0B0133520055103E /* exception_handler.cc in Sources */,
9BD82C110B0133520055103E /* minidump_generator.cc in Sources */,
9BD82C260B01344C0055103E /* minidump_file_writer.cc in Sources */,
9BD82C2E0B01345E0055103E /* string_utilities.cc in Sources */,
D2F651000BEF947200920385 /* file_id.cc in Sources */,
D2F651020BEF947200920385 /* macho_id.cc in Sources */,
D2F651040BEF947200920385 /* macho_utilities.cc in Sources */,
D2F651090BEF949A00920385 /* dynamic_images.cc in Sources */,
D2F6510E0BEF94EB00920385 /* macho_walker.cc in Sources */,
D2F651110BEF951700920385 /* string_conversion.cc in Sources */,
D2F651150BEF953000920385 /* convert_UTF.c in Sources */,
8BFC81B011FF9C8D002CB4DC /* breakpad_nlist_64.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9B7CA84B0B1297F200CD3A1D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9B7CA8540B12989000CD3A1D /* minidump_file_writer_unittest.cc in Sources */,
9B7CA8550B1298A100CD3A1D /* minidump_file_writer.cc in Sources */,
9BC1D2940B336F2300F2A2B4 /* convert_UTF.c in Sources */,
9BC1D2950B336F2500F2A2B4 /* string_conversion.cc in Sources */,
8BFC81AE11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9BD82A980B00267E0055103E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9BD82BFF0B01333D0055103E /* exception_handler_test.cc in Sources */,
9BD82C0D0B0133520055103E /* exception_handler.cc in Sources */,
9BD82C0E0B0133520055103E /* minidump_generator.cc in Sources */,
9BD82C250B01344C0055103E /* minidump_file_writer.cc in Sources */,
9BD82C2D0B01345E0055103E /* string_utilities.cc in Sources */,
9B35FF5A0B267D5F008DE8C7 /* convert_UTF.c in Sources */,
9B35FF5B0B267D5F008DE8C7 /* string_conversion.cc in Sources */,
D2F6511B0BEF970E00920385 /* dynamic_images.cc in Sources */,
D2F6511D0BEF973500920385 /* file_id.cc in Sources */,
D2F6511E0BEF973600920385 /* macho_id.cc in Sources */,
D2F6511F0BEF973900920385 /* macho_utilities.cc in Sources */,
D2F651210BEF975400920385 /* macho_walker.cc in Sources */,
8BFC81AF11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F93A88710E8B4C700026AF89 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */,
F93A88870E8B4C9A0026AF89 /* dump_syms.mm in Sources */,
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */,
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */,
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */,
F93A888B0E8B4C9A0026AF89 /* md5.c in Sources */,
F93A887D0E8B4C8C0026AF89 /* macho_walker.cc in Sources */,
F93A887E0E8B4C8C0026AF89 /* macho_id.cc in Sources */,
F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */,
F93A88800E8B4C8C0026AF89 /* file_id.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F9AE19BF0DB04A9500C98454 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F9B34E870DBC1E1600306484 /* dynamic_images.cc in Sources */,
F982089C0DB3280D0017AECA /* breakpad_nlist_test.cc in Sources */,
F98208A30DB32CAE0017AECA /* breakpad_nlist_64.cc in Sources */,
F9C5A4220DB82DD800209C76 /* DynamicImagesTests.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F9AE5B2F0DBFDBA300505983 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F9AE5B390DBFDBDB00505983 /* dynamic_images.cc in Sources */,
F9AE5B3A0DBFDBDB00505983 /* DynamicImagesTests.cc in Sources */,
8BFC81AD11FF9C8A002CB4DC /* breakpad_nlist_64.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
1DEB923208733DC60010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
);
PRODUCT_NAME = generator_test;
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
};
name = Debug;
};
1DEB923308733DC60010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
);
PRODUCT_NAME = generator_test;
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
};
name = Release;
};
1DEB923608733DC60010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */;
buildSettings = {
};
name = Debug;
};
1DEB923708733DC60010E9CD /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */;
buildSettings = {
};
name = Release;
};
9B7CA8510B12984300CD3A1D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = unit_test;
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
};
name = Debug;
};
9B7CA8520B12984300CD3A1D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = unit_test;
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
};
name = Release;
};
9BD82AA70B0026BF0055103E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = handler_test;
USER_HEADER_SEARCH_PATHS = "../../.. $(inherited)";
};
name = Debug;
};
9BD82AA80B0026BF0055103E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = handler_test;
USER_HEADER_SEARCH_PATHS = "../../.. $(inherited)";
};
name = Release;
};
F93A88770E8B4C700026AF89 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
INFOPLIST_FILE = "obj-cTestCases-Info.plist";
PRODUCT_NAME = octestcases;
USER_HEADER_SEARCH_PATHS = "../../../..//**";
WRAPPER_EXTENSION = octest;
};
name = Debug;
};
F93A88780E8B4C700026AF89 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
INFOPLIST_FILE = "obj-cTestCases-Info.plist";
PRODUCT_NAME = octestcases;
USER_HEADER_SEARCH_PATHS = "../../../..//**";
WRAPPER_EXTENSION = octest;
};
name = Release;
};
F9AE19C40DB04A9500C98454 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
INFOPLIST_FILE = "minidump_tests64-Info.plist";
PRODUCT_NAME = minidump_tests64;
USER_HEADER_SEARCH_PATHS = "../../../**";
WRAPPER_EXTENSION = cptest;
};
name = Debug;
};
F9AE19C50DB04A9500C98454 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
INFOPLIST_FILE = "minidump_tests64-Info.plist";
PRODUCT_NAME = minidump_tests64;
USER_HEADER_SEARCH_PATHS = "../../../**";
WRAPPER_EXTENSION = cptest;
};
name = Release;
};
F9AE5B350DBFDBA300505983 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
INFOPLIST_FILE = "minidump_tests32-Info.plist";
PRODUCT_NAME = minidump_tests32;
USER_HEADER_SEARCH_PATHS = "../../../**";
WRAPPER_EXTENSION = cptest;
};
name = Debug;
};
F9AE5B370DBFDBA300505983 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
INFOPLIST_FILE = "minidump_tests32-Info.plist";
PRODUCT_NAME = minidump_tests32;
USER_HEADER_SEARCH_PATHS = "../../../**";
WRAPPER_EXTENSION = cptest;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "generator_test" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB923208733DC60010E9CD /* Debug */,
1DEB923308733DC60010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB923608733DC60010E9CD /* Debug */,
1DEB923708733DC60010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9B7CA8500B12984300CD3A1D /* Build configuration list for PBXNativeTarget "unit_test" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9B7CA8510B12984300CD3A1D /* Debug */,
9B7CA8520B12984300CD3A1D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9BD82AA60B0026BF0055103E /* Build configuration list for PBXNativeTarget "handler_test" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9BD82AA70B0026BF0055103E /* Debug */,
9BD82AA80B0026BF0055103E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F93A88790E8B4C700026AF89 /* Build configuration list for PBXNativeTarget "obj-c_TestCases" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F93A88770E8B4C700026AF89 /* Debug */,
F93A88780E8B4C700026AF89 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F9AE19C70DB04AA200C98454 /* Build configuration list for PBXNativeTarget "minidump_tests64" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F9AE19C40DB04A9500C98454 /* Debug */,
F9AE19C50DB04A9500C98454 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F9AE5B380DBFDBA300505983 /* Build configuration list for PBXNativeTarget "minidump_tests32" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F9AE5B350DBFDBA300505983 /* Debug */,
F9AE5B370DBFDBA300505983 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
}

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.google.breakpad.minidump_tests32</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,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>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.google.breakpad.minidump_tests64</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>
<key>CSResourcesFileMapped</key>
<string>yes</string>
</dict>
</plist>

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,92 @@
// 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.
//
// ProtectedMemoryAllocator
//
// See the header file for documentation
#include "protected_memory_allocator.h"
#include <assert.h>
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ProtectedMemoryAllocator::ProtectedMemoryAllocator(vm_size_t pool_size)
: pool_size_(pool_size),
next_alloc_offset_(0),
valid_(false) {
kern_return_t result = vm_allocate(mach_task_self(),
&base_address_,
pool_size,
TRUE
);
valid_ = (result == KERN_SUCCESS);
assert(valid_);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ProtectedMemoryAllocator::~ProtectedMemoryAllocator() {
vm_deallocate(mach_task_self(),
base_address_,
pool_size_
);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
char *ProtectedMemoryAllocator::Allocate(vm_size_t bytes) {
if (valid_ && next_alloc_offset_ + bytes <= pool_size_) {
char *p = (char*)base_address_ + next_alloc_offset_;
next_alloc_offset_ += bytes;
return p;
}
return NULL; // ran out of memory in our allocation block
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
kern_return_t ProtectedMemoryAllocator::Protect() {
kern_return_t result = vm_protect(mach_task_self(),
base_address_,
pool_size_,
FALSE,
VM_PROT_READ);
return result;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
kern_return_t ProtectedMemoryAllocator::Unprotect() {
kern_return_t result = vm_protect(mach_task_self(),
base_address_,
pool_size_,
FALSE,
VM_PROT_READ | VM_PROT_WRITE);
return result;
}

View File

@ -0,0 +1,85 @@
// 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.
//
// ProtectedMemoryAllocator
//
// A very simple allocator class which allows allocation, but not deallocation.
// The allocations can be made read-only with the Protect() method.
// This class is NOT useful as a general-purpose memory allocation system,
// since it does not allow deallocation. It is useful to use for a group
// of allocations which are created in the same time-frame and destroyed
// in the same time-frame. It is useful for making allocations of memory
// which will not need to change often once initialized. This memory can then
// be protected from memory smashers by calling the Protect() method.
#ifndef PROTECTED_MEMORY_ALLOCATOR_H__
#define PROTECTED_MEMORY_ALLOCATOR_H__
#include <mach/mach.h>
//
class ProtectedMemoryAllocator {
public:
ProtectedMemoryAllocator(vm_size_t pool_size);
~ProtectedMemoryAllocator();
// Returns a pointer to an allocation of size n within the pool.
// Fails by returning NULL is no more space is available.
// Please note that the pointers returned from this method should not
// be freed in any way (for example by calling free() on them ).
char * Allocate(vm_size_t n);
// Returns the base address of the allocation pool.
char * GetBaseAddress() { return (char*)base_address_; }
// Returns the size of the allocation pool, including allocated
// plus free space.
vm_size_t GetTotalSize() { return pool_size_; }
// Returns the number of bytes already allocated in the pool.
vm_size_t GetAllocatedSize() { return next_alloc_offset_; }
// Returns the number of bytes available for allocation.
vm_size_t GetFreeSize() { return pool_size_ - next_alloc_offset_; }
// Makes the entire allocation pool read-only including, of course,
// all allocations made from the pool.
kern_return_t Protect();
// Makes the entire allocation pool read/write.
kern_return_t Unprotect();
private:
vm_size_t pool_size_;
vm_address_t base_address_;
vm_size_t next_alloc_offset_;
bool valid_;
};
#endif // PROTECTED_MEMORY_ALLOCATOR_H__

View File

@ -0,0 +1,85 @@
// 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.
//
// DynamicImagesTests.cpp
// minidump_test
//
// Created by Neal Sidhwaney on 4/17/08.
// Copyright 2008 Google Inc. All rights reserved.
//
#include "client/mac/handler/testcases/DynamicImagesTests.h"
#include "client/mac/handler/dynamic_images.h"
DynamicImagesTests test2(TEST_INVOCATION(DynamicImagesTests,
ReadTaskMemoryTest));
DynamicImagesTests test3(TEST_INVOCATION(DynamicImagesTests,
ReadLibrariesFromLocalTaskTest));
DynamicImagesTests::DynamicImagesTests(TestInvocation *invocation)
: TestCase(invocation) {
}
DynamicImagesTests::~DynamicImagesTests() {
}
void DynamicImagesTests::ReadTaskMemoryTest() {
kern_return_t kr;
// pick test2 as a symbol we know to be valid to read
// anything will work, really
void *addr = reinterpret_cast<void*>(&test2);
void *buf;
fprintf(stderr, "reading 0x%p\n", addr);
buf = google_breakpad::ReadTaskMemory(mach_task_self(),
addr,
getpagesize(),
&kr);
CPTAssert(kr == KERN_SUCCESS);
CPTAssert(buf != NULL);
CPTAssert(0 == memcmp(buf, (const void*)addr, getpagesize()));
free(buf);
}
void DynamicImagesTests::ReadLibrariesFromLocalTaskTest() {
mach_port_t me = mach_task_self();
google_breakpad::DynamicImages *d = new google_breakpad::DynamicImages(me);
fprintf(stderr,"Local task image count: %d\n", d->GetImageCount());
d->TestPrint();
CPTAssert(d->GetImageCount() > 0);
}

View File

@ -0,0 +1,52 @@
// 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.
//
// DynamicImagesTests.h
// minidump_test
//
// Created by Neal Sidhwaney on 4/17/08.
// Copyright 2008 Google Inc. All rights reserved.
//
//
#ifndef _CLIENT_MAC_HANDLER_TESTCASES_DYNAMICIMAGESTESTS_H__
#define _CLIENT_MAC_HANDLER_TESTCASES_DYNAMICIMAGESTESTS_H__
#include <CPlusTest/CPlusTest.h>
class DynamicImagesTests : public TestCase {
public:
explicit DynamicImagesTests(TestInvocation* invocation);
virtual ~DynamicImagesTests();
void ReadTaskMemoryTest();
void ReadLibrariesFromLocalTaskTest();
};
#endif /* _CLIENT_MAC_HANDLER_TESTCASES_DYNAMICIMAGESTESTS_H__ */

View File

@ -0,0 +1,106 @@
// 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.
//
// breakpad_nlist_test.cc
// minidump_test
//
// Created by Neal Sidhwaney on 4/13/08.
// Copyright 2008 Google Inc. All rights reserved.
//
#include "client/mac/handler/testcases/breakpad_nlist_test.h"
#include <mach-o/nlist.h>
#include "client/mac/handler/breakpad_nlist_64.h"
BreakpadNlistTest test1(TEST_INVOCATION(BreakpadNlistTest, CompareToNM));
BreakpadNlistTest::BreakpadNlistTest(TestInvocation *invocation)
: TestCase(invocation) {
}
BreakpadNlistTest::~BreakpadNlistTest() {
}
void BreakpadNlistTest::CompareToNM() {
#if TARGET_CPU_X86_64
system("/usr/bin/nm -arch x86_64 /usr/lib/dyld > /tmp/dyld-namelist.txt");
#elif TARGET_CPU_PPC64
system("/usr/bin/nm -arch ppc64 /usr/lib/dyld > /tmp/dyld-namelist.txt");
#endif
FILE *fd = fopen("/tmp/dyld-namelist.txt", "rt");
char oneNMAddr[30];
char symbolType;
char symbolName[500];
while (!feof(fd)) {
fscanf(fd, "%s %c %s", oneNMAddr, &symbolType, symbolName);
breakpad_nlist symbolList[2];
breakpad_nlist &list = symbolList[0];
memset(symbolList, 0, sizeof(breakpad_nlist)*2);
const char *symbolNames[2];
symbolNames[0] = (const char*)symbolName;
symbolNames[1] = "\0";
breakpad_nlist_64("/usr/lib/dyld", &list, symbolNames);
uint64_t nmAddr = strtol(oneNMAddr, NULL, 16);
if (!IsSymbolMoreThanOnceInDyld(symbolName)) {
CPTAssert(nmAddr == symbolList[0].n_value);
}
}
fclose(fd);
}
bool BreakpadNlistTest::IsSymbolMoreThanOnceInDyld(const char *symbolName) {
// These are the symbols that occur more than once when nm dumps
// the symbol table of /usr/lib/dyld. Our nlist program returns
// the first address because it's doing a search so we need to exclude
// these from causing the test to fail
const char *multipleSymbols[] = {
"__Z41__static_initialization_and_destruction_0ii",
"___tcf_0",
"___tcf_1",
"_read_encoded_value_with_base",
"_read_sleb128",
"_read_uleb128",
"\0"};
bool found = false;
for (int i = 0; multipleSymbols[i][0]; i++) {
if (!strcmp(multipleSymbols[i], symbolName)) {
found = true;
break;
}
}
return found;
}

View File

@ -0,0 +1,62 @@
// 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.
//
// breakpad_nlist_test.h
// minidump_test
//
// Created by Neal Sidhwaney on 4/13/08.
// Copyright 2008 Google Inc. All rights reserved.
//
//
#ifndef CLIENT_MAC_HANDLER_TESTCASES_BREAKPAD_NLIST_TEST_H__
#define CLIENT_MAC_HANDLER_TESTCASES_BREAKPAD_NLIST_TEST_H__
#include <CPlusTest/CPlusTest.h>
class BreakpadNlistTest : public TestCase {
private:
// nm dumps multiple addresses for the same symbol in
// /usr/lib/dyld. So we track those so we don't report failures
// in mismatches between what our nlist returns and what nm has
// for the duplicate symbols.
bool IsSymbolMoreThanOnceInDyld(const char *symbolName);
public:
explicit BreakpadNlistTest(TestInvocation* invocation);
virtual ~BreakpadNlistTest();
/* This test case runs nm on /usr/lib/dyld and then compares the
output of every symbol to what our nlist implementation returns */
void CompareToNM();
};
#endif /* CLIENT_MAC_HANDLER_TESTCASES_BREAKPAD_NLIST_TEST_H__*/

View File

@ -0,0 +1,46 @@
// 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.
//
// dwarftests.h
// minidump_test
//
// Created by Neal Sidhwaney on 9/24/08.
// Copyright 2008 Google Inc. All rights reserved.
//
#import <SenTestingKit/SenTestingKit.h>
@interface dwarftests : SenTestCase {
}
- (void) testDWARFSymbolFileGeneration;
@end

View File

@ -0,0 +1,60 @@
// 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.
//
// dwarftests.m
// minidump_test
//
// Created by Neal Sidhwaney on 9/24/08.
// Copyright 2008 Google Inc. All rights reserved.
//
#import "dwarftests.h"
#import "dump_syms.h"
@implementation dwarftests
- (void) testDWARFSymbolFileGeneration {
NSString *inputBreakpadSymbolFile = @"testcases/testdata/dump_syms_i386_breakpad.sym";
NSString *outputBreakpadSymbolFile = @"/tmp/dump_syms_i386.breakpad";
DumpSymbols *dump = [[DumpSymbols alloc] initWithContentsOfFile:@"testcases/testdata/dump_syms_dwarf_data"];
STAssertNotNil(dump, @"DumpSymbols is nil");
[dump setArchitecture:@"i386"];
[dump writeSymbolFile:outputBreakpadSymbolFile];
NSData *d = [[NSData alloc] initWithContentsOfFile:inputBreakpadSymbolFile];
STAssertNotNil(d, @"Input breakpad symbol file not found");
NSData *d1 = [[NSData alloc] initWithContentsOfFile:outputBreakpadSymbolFile];
STAssertNotNil(d1, @"Output breakpad symbol file not found");
STAssertTrue([d isEqualToData:d1],
@"Symbol files were not equal!",nil);
}
@end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
<?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>CFBundleDisplayName</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>crash_report_sender</string>
<key>CFBundleIdentifier</key>
<string>com.Breakpad.${PRODUCT_NAME:identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSHasLocalizedDisplayName</key>
<true/>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

View File

@ -0,0 +1,140 @@
// 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"
#import "GTMDefines.h"
#define kClientIdPreferenceKey @"clientid"
extern NSString *const kGoogleServerType;
extern NSString *const kSocorroServerType;
extern NSString *const kDefaultServerType;
// We're sublcassing NSTextField in order to override a particular
// method (see the implementation) that lets us reject changes if they
// are longer than a particular length. Bindings would normally solve
// this problem, but when we implemented a validation method, and
// returned NO for strings that were too long, the UI was not updated
// right away, which was a poor user experience. The UI would be
// updated as soon as the text field lost first responder status,
// which isn't soon enough. It is a known bug that the UI KVO didn't
// work in the middle of a validation.
@interface LengthLimitingTextField : NSTextField {
@private
NSUInteger maximumLength_;
}
- (void)setMaximumLength:(NSUInteger)maxLength;
@end
@interface Reporter : NSObject {
@public
IBOutlet NSWindow *alertWindow_; // The alert window
// Grouping boxes used for resizing.
IBOutlet NSBox *headerBox_;
IBOutlet NSBox *preEmailBox_;
IBOutlet NSBox *emailSectionBox_;
// Localized elements (or things that need to be moved during localization).
IBOutlet NSTextField *dialogTitle_;
IBOutlet NSTextField *commentMessage_;
IBOutlet NSTextField *emailMessage_;
IBOutlet NSTextField *emailLabel_;
IBOutlet NSTextField *privacyLinkLabel_;
IBOutlet NSButton *sendButton_;
IBOutlet NSButton *cancelButton_;
IBOutlet LengthLimitingTextField *emailEntryField_;
IBOutlet LengthLimitingTextField *commentsEntryField_;
IBOutlet NSTextField *countdownLabel_;
IBOutlet NSView *privacyLinkArrow_;
// Text field bindings, for user input.
NSString *commentsValue_; // Comments from the user
NSString *emailValue_; // Email from the user
NSString *countdownMessage_; // Message indicating time
// left for input.
@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.
NSTimeInterval remainingDialogTime_; // Keeps track of how long
// we have until we cancel
// the dialog
NSTimer *messageTimer_; // Timer we use to update
// the dialog
NSMutableDictionary *serverDictionary_; // The dictionary mapping a
// server type name to a
// dictionary of server
// parameter names.
NSMutableDictionary *socorroDictionary_; // The dictionary for
// Socorro.
NSMutableDictionary *googleDictionary_; // The dictionary for
// Google.
NSMutableDictionary *extraServerVars_; // A dictionary containing
// extra key/value pairs
// that are uploaded to the
// crash server with the
// minidump.
}
// 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 Privacy Policy url 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 *)commentsValue;
- (void)setCommentsValue:(NSString *)value;
- (NSString *)emailValue;
- (void)setEmailValue:(NSString *)value;
- (NSString *)countdownMessage;
- (void)setCountdownMessage:(NSString *)value;
@end

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

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,261 @@
// 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"
#import "GTMDefines.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 {
NSInteger 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 = nil;
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 {
NSInteger tag = [sender tag];
if (tag == 1) {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:@selector(causeCrash) withObject:nil afterDelay:10.0];
[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)");
}
BreakpadAddUploadParameter(breakpad_,
@"MeaningOfLife",
@"42");
[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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,55 @@
<?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>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>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>BreakpadRequestEmail</key>
<string>YES</string>
<key>BreakpadRequestComments</key>
<string>YES</string>
<key>BreakpadVendor</key>
<string>Foo Bar Corp, Incorporated, LTD, LLC</string>
<key>BreakpadServerParameters</key>
<dict>
<key>Param1</key>
<string>Value1</string>
<key>Param2</key>
<string>Value2</string>
</dict>
<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.14145f;
}
@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,217 @@
// Copyright (c) 2009, 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.
//
// BreakpadFramework_Test.mm
// Test case file for Breakpad.h/mm.
//
#import "GTMSenTestCase.h"
#import "Breakpad.h"
#include <mach/mach.h>
@interface BreakpadFramework_Test : GTMTestCase {
@private
int last_exception_code_;
int last_exception_type_;
mach_port_t last_exception_thread_;
// We're not using Obj-C BOOL because we need to interop with
// Breakpad's callback.
bool shouldHandleException_;
}
// This method is used by a callback used by test cases to determine
// whether to return true or false to Breakpad when handling an
// exception.
- (bool)shouldHandleException;
// This method returns a minimal dictionary that has what
// Breakpad needs to initialize.
- (NSMutableDictionary *)breakpadInitializationDictionary;
// This method is used by the exception handling callback
// to communicate to test cases the properites of the last
// exception.
- (void)setLastExceptionType:(int)type andCode:(int)code
andThread:(mach_port_t)thread;
@end
// Callback for Breakpad exceptions
bool myBreakpadCallback(int exception_type,
int exception_code,
mach_port_t crashing_thread,
void *context);
bool myBreakpadCallback(int exception_type,
int exception_code,
mach_port_t crashing_thread,
void *context) {
BreakpadFramework_Test *testCaseClass =
(BreakpadFramework_Test *)context;
[testCaseClass setLastExceptionType:exception_type
andCode:exception_code
andThread:crashing_thread];
bool shouldHandleException =
[testCaseClass shouldHandleException];
NSLog(@"Callback returning %d", shouldHandleException);
return shouldHandleException;
}
const int kNoLastExceptionCode = -1;
const int kNoLastExceptionType = -1;
const mach_port_t kNoLastExceptionThread = MACH_PORT_NULL;
@implementation BreakpadFramework_Test
- (void) initializeExceptionStateVariables {
last_exception_code_ = kNoLastExceptionCode;
last_exception_type_ = kNoLastExceptionType;
last_exception_thread_ = kNoLastExceptionThread;
}
- (NSMutableDictionary *)breakpadInitializationDictionary {
NSMutableDictionary *breakpadParams =
[NSMutableDictionary dictionaryWithCapacity:3];
[breakpadParams setObject:@"UnitTests" forKey:@BREAKPAD_PRODUCT];
[breakpadParams setObject:@"1.0" forKey:@BREAKPAD_VERSION];
[breakpadParams setObject:@"http://staging" forKey:@BREAKPAD_URL];
return breakpadParams;
}
- (bool)shouldHandleException {
return shouldHandleException_;
}
- (void)setLastExceptionType:(int)type
andCode:(int)code
andThread:(mach_port_t)thread {
last_exception_type_ = type;
last_exception_code_ = code;
last_exception_thread_ = thread;
}
// Test that the parameters mark required actually enable Breakpad to
// be initialized.
- (void)testBreakpadInstantiationWithRequiredParameters {
BreakpadRef b = BreakpadCreate([self breakpadInitializationDictionary]);
STAssertNotNULL(b, @"BreakpadCreate failed with required parameters");
BreakpadRelease(b);
}
// Test that Breakpad fails to initialize cleanly when required
// parameters are not present.
- (void)testBreakpadInstantiationWithoutRequiredParameters {
NSMutableDictionary *breakpadDictionary =
[self breakpadInitializationDictionary];
// Skip setting version, so that BreakpadCreate fails.
[breakpadDictionary removeObjectForKey:@BREAKPAD_VERSION];
BreakpadRef b = BreakpadCreate(breakpadDictionary);
STAssertNULL(b, @"BreakpadCreate did not fail when missing a required"
" parameter!");
breakpadDictionary = [self breakpadInitializationDictionary];
// Now test with no product
[breakpadDictionary removeObjectForKey:@BREAKPAD_PRODUCT];
b = BreakpadCreate(breakpadDictionary);
STAssertNULL(b, @"BreakpadCreate did not fail when missing a required"
" parameter!");
breakpadDictionary = [self breakpadInitializationDictionary];
// Now test with no URL
[breakpadDictionary removeObjectForKey:@BREAKPAD_URL];
b = BreakpadCreate(breakpadDictionary);
STAssertNULL(b, @"BreakpadCreate did not fail when missing a required"
" parameter!");
BreakpadRelease(b);
}
// Test to ensure that when we call BreakpadAddUploadParameter,
// it's added to the dictionary correctly(this test depends on
// some internal details of Breakpad, namely, the special prefix
// that it uses to figure out which key/value pairs to upload).
- (void)testAddingBreakpadServerVariable {
NSMutableDictionary *breakpadDictionary =
[self breakpadInitializationDictionary];
BreakpadRef b = BreakpadCreate(breakpadDictionary);
STAssertNotNULL(b, @"BreakpadCreate failed with valid dictionary!");
BreakpadAddUploadParameter(b,
@"key",
@"value");
// Test that it did not add the key/value directly, e.g. without
// prepending the key with the prefix.
STAssertNil(BreakpadKeyValue(b, @"key"),
@"AddUploadParameter added key directly to dictionary"
" instead of prepending it!");
NSString *prependedKeyname =
[@BREAKPAD_SERVER_PARAMETER_PREFIX stringByAppendingString:@"key"];
STAssertEqualStrings(BreakpadKeyValue(b, prependedKeyname),
@"value",
@"Calling BreakpadAddUploadParameter did not prepend "
"key name");
BreakpadRelease(b);
}
// Test that when we do on-demand minidump generation,
// the exception code/type/thread are set properly.
- (void)testFilterCallbackReturnsFalse {
NSMutableDictionary *breakpadDictionary =
[self breakpadInitializationDictionary];
BreakpadRef b = BreakpadCreate(breakpadDictionary);
STAssertNotNULL(b, @"BreakpadCreate failed with valid dictionary!");
BreakpadSetFilterCallback(b, &myBreakpadCallback, self);
// This causes the callback to return false, meaning
// Breakpad won't take the exception
shouldHandleException_ = false;
[self initializeExceptionStateVariables];
STAssertEquals(last_exception_type_, kNoLastExceptionType,
@"Last exception type not initialized correctly.");
STAssertEquals(last_exception_code_, kNoLastExceptionCode,
@"Last exception code not initialized correctly.");
STAssertEquals(last_exception_thread_, kNoLastExceptionThread,
@"Last exception thread is not initialized correctly.");
// Cause Breakpad's exception handler to be invoked.
BreakpadGenerateAndSendReport(b);
STAssertEquals(last_exception_type_, 0,
@"Last exception type is not 0 for on demand");
STAssertEquals(last_exception_code_, 0,
@"Last exception code is not 0 for on demand");
STAssertEquals(last_exception_thread_, mach_thread_self(),
@"Last exception thread is not mach_thread_self() "
"for on demand");
}
@end

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

@ -0,0 +1,72 @@
// Copyright (c) 2010, 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 class for creating a temporary directory for unit tests
// that is deleted in the destructor.
#ifndef GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_AUTO_TEMPDIR
#define GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_AUTO_TEMPDIR
#include <dirent.h>
#include <sys/types.h>
#include <string>
namespace google_breakpad {
class AutoTempDir {
public:
AutoTempDir() {
char tempDir[16] = "/tmp/XXXXXXXXXX";
mkdtemp(tempDir);
path = tempDir;
}
~AutoTempDir() {
// First remove any files in the dir
DIR* dir = opendir(path.c_str());
if (!dir)
return;
dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
std::string entryPath = path + "/" + entry->d_name;
unlink(entryPath.c_str());
}
closedir(dir);
rmdir(path.c_str());
}
std::string path;
};
} // namespace google_breakpad
#endif // GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_AUTO_TEMPDIR

View File

@ -0,0 +1,345 @@
// Copyright (c) 2010, 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.
//
// crash_generation_server_test.cc
// Unit tests for CrashGenerationServer
#include <dirent.h>
#include <glob.h>
#include <stdint.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string>
#include "breakpad_googletest_includes.h"
#include "client/mac/crash_generation/client_info.h"
#include "client/mac/crash_generation/crash_generation_client.h"
#include "client/mac/crash_generation/crash_generation_server.h"
#include "client/mac/handler/exception_handler.h"
#include "client/mac/tests/auto_tempdir.h"
#include "client/mac/tests/spawn_child_process.h"
#include "google_breakpad/processor/minidump.h"
namespace google_breakpad {
// This acts as the log sink for INFO logging from the processor
// logging code. The logging output confuses XCode and makes it think
// there are unit test failures. testlogging.h handles the overriding.
std::ostringstream info_log;
}
namespace {
using std::string;
using google_breakpad::AutoTempDir;
using google_breakpad::ClientInfo;
using google_breakpad::CrashGenerationClient;
using google_breakpad::CrashGenerationServer;
using google_breakpad::ExceptionHandler;
using google_breakpad::Minidump;
using google_breakpad::MinidumpContext;
using google_breakpad::MinidumpException;
using google_breakpad::MinidumpModule;
using google_breakpad::MinidumpModuleList;
using google_breakpad::MinidumpSystemInfo;
using google_breakpad::MinidumpThread;
using google_breakpad::MinidumpThreadList;
using testing::Test;
using namespace google_breakpad_test;
class CrashGenerationServerTest : public Test {
public:
// The port name to receive messages on
char mach_port_name[128];
// Filename of the last dump that was generated
string last_dump_name;
// PID of the child process
pid_t child_pid;
// A temp dir
AutoTempDir temp_dir;
// Counter just to ensure that we don't hit the same port again
static int i;
void SetUp() {
sprintf(mach_port_name,
"com.google.breakpad.ServerTest.%d.%d", getpid(),
CrashGenerationServerTest::i++);
child_pid = (pid_t)-1;
}
};
int CrashGenerationServerTest::i = 0;
// Test that starting and stopping a server works
TEST_F(CrashGenerationServerTest, testStartStopServer) {
CrashGenerationServer server(mach_port_name,
NULL, // dump callback
NULL, // dump context
NULL, // exit callback
NULL, // exit context
false, // generate dumps
""); // dump path
ASSERT_TRUE(server.Start());
ASSERT_TRUE(server.Stop());
}
// Test that requesting a dump via CrashGenerationClient works
// Test without actually dumping
TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) {
CrashGenerationServer server(mach_port_name,
NULL, // dump callback
NULL, // dump context
NULL, // exit callback
NULL, // exit context
false, // don't generate dumps
temp_dir.path); // dump path
ASSERT_TRUE(server.Start());
pid_t pid = fork();
ASSERT_NE(-1, pid);
if (pid == 0) {
CrashGenerationClient client(mach_port_name);
bool result = client.RequestDump();
exit(result ? 0 : 1);
}
int ret;
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
EXPECT_TRUE(WIFEXITED(ret));
EXPECT_EQ(0, WEXITSTATUS(ret));
EXPECT_TRUE(server.Stop());
// check that no minidump was written
string pattern = temp_dir.path + "/*";
glob_t dirContents;
ret = glob(pattern.c_str(), GLOB_NOSORT, NULL, &dirContents);
EXPECT_EQ(GLOB_NOMATCH, ret);
if (ret != GLOB_NOMATCH)
globfree(&dirContents);
}
void dumpCallback(void *context, const ClientInfo &client_info,
const std::string &file_path) {
if (context) {
CrashGenerationServerTest* self =
reinterpret_cast<CrashGenerationServerTest*>(context);
if (!file_path.empty())
self->last_dump_name = file_path;
self->child_pid = client_info.pid();
}
}
void *RequestDump(void *context) {
CrashGenerationClient client((const char*)context);
bool result = client.RequestDump();
return (void*)(result ? 0 : 1);
}
// Test that actually writing a minidump works
TEST_F(CrashGenerationServerTest, testRequestDump) {
CrashGenerationServer server(mach_port_name,
dumpCallback, // dump callback
this, // dump context
NULL, // exit callback
NULL, // exit context
true, // generate dumps
temp_dir.path); // dump path
ASSERT_TRUE(server.Start());
pid_t pid = fork();
ASSERT_NE(-1, pid);
if (pid == 0) {
// Have to spawn off a separate thread to request the dump,
// because MinidumpGenerator assumes the handler thread is not
// the only thread
pthread_t thread;
if (pthread_create(&thread, NULL, RequestDump, (void*)mach_port_name) != 0)
exit(1);
void* result;
pthread_join(thread, &result);
exit(reinterpret_cast<intptr_t>(result));
}
int ret;
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
EXPECT_TRUE(WIFEXITED(ret));
EXPECT_EQ(0, WEXITSTATUS(ret));
EXPECT_TRUE(server.Stop());
// check that minidump was written
ASSERT_FALSE(last_dump_name.empty());
struct stat st;
EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
EXPECT_LT(0, st.st_size);
// check client's PID
ASSERT_EQ(pid, child_pid);
}
static void Crasher() {
int *a = (int*)0x42;
fprintf(stdout, "Going to crash...\n");
fprintf(stdout, "A = %d", *a);
}
// Test that crashing a child process with an OOP ExceptionHandler installed
// results in a minidump being written by the CrashGenerationServer in
// the parent.
TEST_F(CrashGenerationServerTest, testChildProcessCrash) {
CrashGenerationServer server(mach_port_name,
dumpCallback, // dump callback
this, // dump context
NULL, // exit callback
NULL, // exit context
true, // generate dumps
temp_dir.path); // dump path
ASSERT_TRUE(server.Start());
pid_t pid = fork();
ASSERT_NE(-1, pid);
if (pid == 0) {
// Instantiate an OOP exception handler.
ExceptionHandler eh("", NULL, NULL, NULL, true, mach_port_name);
Crasher();
// not reached
exit(0);
}
int ret;
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
EXPECT_FALSE(WIFEXITED(ret));
EXPECT_TRUE(server.Stop());
// check that minidump was written
ASSERT_FALSE(last_dump_name.empty());
struct stat st;
EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
EXPECT_LT(0, st.st_size);
// Read the minidump, sanity check some data.
Minidump minidump(last_dump_name.c_str());
ASSERT_TRUE(minidump.Read());
MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
ASSERT_TRUE(system_info);
const MDRawSystemInfo* raw_info = system_info->system_info();
ASSERT_TRUE(raw_info);
EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
MinidumpThreadList* thread_list = minidump.GetThreadList();
ASSERT_TRUE(thread_list);
ASSERT_EQ((unsigned int)1, thread_list->thread_count());
MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
ASSERT_TRUE(main_thread);
MinidumpContext* context = main_thread->GetContext();
ASSERT_TRUE(context);
EXPECT_EQ(kNativeContext, context->GetContextCPU());
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* main_module = module_list->GetMainModule();
ASSERT_TRUE(main_module);
EXPECT_EQ(GetExecutablePath(), main_module->code_file());
}
#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) && \
(defined(__x86_64__) || defined(__i386__))
// Test that crashing a child process of a different architecture
// produces a valid minidump.
TEST_F(CrashGenerationServerTest, testChildProcessCrashCrossArchitecture) {
CrashGenerationServer server(mach_port_name,
dumpCallback, // dump callback
this, // dump context
NULL, // exit callback
NULL, // exit context
true, // generate dumps
temp_dir.path); // dump path
ASSERT_TRUE(server.Start());
// Spawn a child process
string helper_path = GetHelperPath();
const char* argv[] = {
helper_path.c_str(),
"crash",
mach_port_name,
NULL
};
pid_t pid = spawn_child_process(argv);
ASSERT_NE(-1, pid);
int ret;
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
EXPECT_FALSE(WIFEXITED(ret));
EXPECT_TRUE(server.Stop());
// check that minidump was written
ASSERT_FALSE(last_dump_name.empty());
struct stat st;
EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
EXPECT_LT(0, st.st_size);
const MDCPUArchitecture kExpectedArchitecture =
#if defined(__x86_64__)
MD_CPU_ARCHITECTURE_X86
#elif defined(__i386__)
MD_CPU_ARCHITECTURE_AMD64
#endif
;
const u_int32_t kExpectedContext =
#if defined(__i386__)
MD_CONTEXT_AMD64
#elif defined(__x86_64__)
MD_CONTEXT_X86
#endif
;
// Read the minidump, sanity check some data.
Minidump minidump(last_dump_name.c_str());
ASSERT_TRUE(minidump.Read());
MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
ASSERT_TRUE(system_info);
const MDRawSystemInfo* raw_info = system_info->system_info();
ASSERT_TRUE(raw_info);
EXPECT_EQ(kExpectedArchitecture, raw_info->processor_architecture);
MinidumpThreadList* thread_list = minidump.GetThreadList();
ASSERT_TRUE(thread_list);
ASSERT_EQ((unsigned int)1, thread_list->thread_count());
MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
ASSERT_TRUE(main_thread);
MinidumpContext* context = main_thread->GetContext();
ASSERT_TRUE(context);
EXPECT_EQ(kExpectedContext, context->GetContextCPU());
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* main_module = module_list->GetMainModule();
ASSERT_TRUE(main_module);
EXPECT_EQ(helper_path, main_module->code_file());
}
#endif
} // namespace

View File

@ -0,0 +1,698 @@
// Copyright (c) 2010, 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.
// exception_handler_test.cc: Unit tests for google_breakpad::ExceptionHandler
#include <pthread.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include "breakpad_googletest_includes.h"
#include "client/mac/handler/exception_handler.h"
#include "client/mac/tests/auto_tempdir.h"
#include "common/mac/MachIPC.h"
#include "google_breakpad/processor/minidump.h"
namespace google_breakpad {
// This acts as the log sink for INFO logging from the processor
// logging code. The logging output confuses XCode and makes it think
// there are unit test failures. testlogging.h handles the overriding.
std::ostringstream info_log;
}
namespace {
using std::string;
using google_breakpad::AutoTempDir;
using google_breakpad::ExceptionHandler;
using google_breakpad::MachPortSender;
using google_breakpad::MachReceiveMessage;
using google_breakpad::MachSendMessage;
using google_breakpad::Minidump;
using google_breakpad::MinidumpContext;
using google_breakpad::MinidumpException;
using google_breakpad::MinidumpMemoryList;
using google_breakpad::MinidumpMemoryRegion;
using google_breakpad::ReceivePort;
using testing::Test;
class ExceptionHandlerTest : public Test {
public:
AutoTempDir tempDir;
string lastDumpName;
};
static void Crasher() {
int *a = (int*)0x42;
fprintf(stdout, "Going to crash...\n");
fprintf(stdout, "A = %d", *a);
}
static void SoonToCrash() {
Crasher();
}
static bool MDCallback(const char *dump_dir, const char *file_name,
void *context, bool success) {
string path(dump_dir);
path.append("/");
path.append(file_name);
path.append(".dmp");
int fd = *reinterpret_cast<int*>(context);
(void)write(fd, path.c_str(), path.length() + 1);
close(fd);
exit(0);
// not reached
return true;
}
TEST_F(ExceptionHandlerTest, InProcess) {
// Give the child process a pipe to report back on.
int fds[2];
ASSERT_EQ(0, pipe(fds));
// Fork off a child process so it can crash.
pid_t pid = fork();
if (pid == 0) {
// In the child process.
close(fds[0]);
ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
// crash
SoonToCrash();
// not reached
exit(1);
}
// In the parent process.
ASSERT_NE(-1, pid);
// Wait for the background process to return the minidump file.
close(fds[1]);
char minidump_file[PATH_MAX];
ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
ASSERT_NE(0, nbytes);
// Ensure that minidump file exists and is > 0 bytes.
struct stat st;
ASSERT_EQ(0, stat(minidump_file, &st));
ASSERT_LT(0, st.st_size);
// Child process should have exited with a zero status.
int ret;
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
EXPECT_NE(0, WIFEXITED(ret));
EXPECT_EQ(0, WEXITSTATUS(ret));
}
static bool DumpNameMDCallback(const char *dump_dir, const char *file_name,
void *context, bool success) {
ExceptionHandlerTest *self = reinterpret_cast<ExceptionHandlerTest*>(context);
if (dump_dir && file_name) {
self->lastDumpName = dump_dir;
self->lastDumpName += "/";
self->lastDumpName += file_name;
self->lastDumpName += ".dmp";
}
return true;
}
TEST_F(ExceptionHandlerTest, WriteMinidump) {
ExceptionHandler eh(tempDir.path, NULL, DumpNameMDCallback, this, true, NULL);
ASSERT_TRUE(eh.WriteMinidump());
// Ensure that minidump file exists and is > 0 bytes.
ASSERT_FALSE(lastDumpName.empty());
struct stat st;
ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
ASSERT_LT(0, st.st_size);
// The minidump should not contain an exception stream.
Minidump minidump(lastDumpName);
ASSERT_TRUE(minidump.Read());
MinidumpException* exception = minidump.GetException();
EXPECT_FALSE(exception);
}
TEST_F(ExceptionHandlerTest, WriteMinidumpWithException) {
ExceptionHandler eh(tempDir.path, NULL, DumpNameMDCallback, this, true, NULL);
ASSERT_TRUE(eh.WriteMinidump(true));
// Ensure that minidump file exists and is > 0 bytes.
ASSERT_FALSE(lastDumpName.empty());
struct stat st;
ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
ASSERT_LT(0, st.st_size);
// The minidump should contain an exception stream.
Minidump minidump(lastDumpName);
ASSERT_TRUE(minidump.Read());
MinidumpException* exception = minidump.GetException();
ASSERT_TRUE(exception);
const MDRawExceptionStream* raw_exception = exception->exception();
ASSERT_TRUE(raw_exception);
EXPECT_EQ(MD_EXCEPTION_MAC_BREAKPOINT,
raw_exception->exception_record.exception_code);
}
TEST_F(ExceptionHandlerTest, DumpChildProcess) {
const int kTimeoutMs = 2000;
// Create a mach port to receive the child task on.
char machPortName[128];
sprintf(machPortName, "ExceptionHandlerTest.%d", getpid());
ReceivePort parent_recv_port(machPortName);
// Give the child process a pipe to block on.
int fds[2];
ASSERT_EQ(0, pipe(fds));
// Fork off a child process to dump.
pid_t pid = fork();
if (pid == 0) {
// In the child process
close(fds[1]);
// Send parent process the task and thread ports.
MachSendMessage child_message(0);
child_message.AddDescriptor(mach_task_self());
child_message.AddDescriptor(mach_thread_self());
MachPortSender child_sender(machPortName);
if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS)
exit(1);
// Wait for the parent process.
uint8_t data;
read(fds[0], &data, 1);
exit(0);
}
// In the parent process.
ASSERT_NE(-1, pid);
close(fds[0]);
// Read the child's task and thread ports.
MachReceiveMessage child_message;
ASSERT_EQ(KERN_SUCCESS,
parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
mach_port_t child_task = child_message.GetTranslatedPort(0);
mach_port_t child_thread = child_message.GetTranslatedPort(1);
ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_thread);
// Write a minidump of the child process.
bool result = ExceptionHandler::WriteMinidumpForChild(child_task,
child_thread,
tempDir.path,
DumpNameMDCallback,
this);
ASSERT_EQ(true, result);
// Ensure that minidump file exists and is > 0 bytes.
ASSERT_FALSE(lastDumpName.empty());
struct stat st;
ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
ASSERT_LT(0, st.st_size);
// Unblock child process
uint8_t data = 1;
(void)write(fds[1], &data, 1);
// Child process should have exited with a zero status.
int ret;
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
EXPECT_NE(0, WIFEXITED(ret));
EXPECT_EQ(0, WEXITSTATUS(ret));
}
// Test that memory around the instruction pointer is written
// to the dump as a MinidumpMemoryRegion.
TEST_F(ExceptionHandlerTest, InstructionPointerMemory) {
// Give the child process a pipe to report back on.
int fds[2];
ASSERT_EQ(0, pipe(fds));
// These are defined here so the parent can use them to check the
// data from the minidump afterwards.
const u_int32_t kMemorySize = 256; // bytes
const int kOffset = kMemorySize / 2;
// This crashes with SIGILL on x86/x86-64/arm.
const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
pid_t pid = fork();
if (pid == 0) {
close(fds[0]);
ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
// Get some executable memory.
char* memory =
reinterpret_cast<char*>(mmap(NULL,
kMemorySize,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANON,
-1,
0));
if (!memory)
exit(0);
// Write some instructions that will crash. Put them in the middle
// of the block of memory, because the minidump should contain 128
// bytes on either side of the instruction pointer.
memcpy(memory + kOffset, instructions, sizeof(instructions));
// Now execute the instructions, which should crash.
typedef void (*void_function)(void);
void_function memory_function =
reinterpret_cast<void_function>(memory + kOffset);
memory_function();
// not reached
exit(1);
}
// In the parent process.
ASSERT_NE(-1, pid);
close(fds[1]);
// Wait for the background process to return the minidump file.
close(fds[1]);
char minidump_file[PATH_MAX];
ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
ASSERT_NE(0, nbytes);
// Ensure that minidump file exists and is > 0 bytes.
struct stat st;
ASSERT_EQ(0, stat(minidump_file, &st));
ASSERT_LT(0, st.st_size);
// Child process should have exited with a zero status.
int ret;
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
EXPECT_NE(0, WIFEXITED(ret));
EXPECT_EQ(0, WEXITSTATUS(ret));
// Read the minidump. Locate the exception record and the
// memory list, and then ensure that there is a memory region
// in the memory list that covers the instruction pointer from
// the exception record.
Minidump minidump(minidump_file);
ASSERT_TRUE(minidump.Read());
MinidumpException* exception = minidump.GetException();
MinidumpMemoryList* memory_list = minidump.GetMemoryList();
ASSERT_TRUE(exception);
ASSERT_TRUE(memory_list);
ASSERT_NE((unsigned int)0, memory_list->region_count());
MinidumpContext* context = exception->GetContext();
ASSERT_TRUE(context);
u_int64_t instruction_pointer;
switch (context->GetContextCPU()) {
case MD_CONTEXT_X86:
instruction_pointer = context->GetContextX86()->eip;
break;
case MD_CONTEXT_AMD64:
instruction_pointer = context->GetContextAMD64()->rip;
break;
case MD_CONTEXT_ARM:
instruction_pointer = context->GetContextARM()->iregs[15];
break;
default:
FAIL() << "Unknown context CPU: " << context->GetContextCPU();
break;
}
MinidumpMemoryRegion* region =
memory_list->GetMemoryRegionForAddress(instruction_pointer);
EXPECT_TRUE(region);
EXPECT_EQ(kMemorySize, region->GetSize());
const u_int8_t* bytes = region->GetMemory();
ASSERT_TRUE(bytes);
u_int8_t prefix_bytes[kOffset];
u_int8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
memset(prefix_bytes, 0, sizeof(prefix_bytes));
memset(suffix_bytes, 0, sizeof(suffix_bytes));
EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
suffix_bytes, sizeof(suffix_bytes)) == 0);
}
// Test that the memory region around the instruction pointer is
// bounded correctly on the low end.
TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
// Give the child process a pipe to report back on.
int fds[2];
ASSERT_EQ(0, pipe(fds));
// These are defined here so the parent can use them to check the
// data from the minidump afterwards.
const u_int32_t kMemorySize = 256; // bytes
const int kOffset = 0;
// This crashes with SIGILL on x86/x86-64/arm.
const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
pid_t pid = fork();
if (pid == 0) {
close(fds[0]);
ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
// Get some executable memory.
char* memory =
reinterpret_cast<char*>(mmap(NULL,
kMemorySize,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANON,
-1,
0));
if (!memory)
exit(0);
// Write some instructions that will crash. Put them at the start
// of the block of memory, to ensure that the memory bounding
// works properly.
memcpy(memory + kOffset, instructions, sizeof(instructions));
// Now execute the instructions, which should crash.
typedef void (*void_function)(void);
void_function memory_function =
reinterpret_cast<void_function>(memory + kOffset);
memory_function();
// not reached
exit(1);
}
// In the parent process.
ASSERT_NE(-1, pid);
close(fds[1]);
// Wait for the background process to return the minidump file.
close(fds[1]);
char minidump_file[PATH_MAX];
ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
ASSERT_NE(0, nbytes);
// Ensure that minidump file exists and is > 0 bytes.
struct stat st;
ASSERT_EQ(0, stat(minidump_file, &st));
ASSERT_LT(0, st.st_size);
// Child process should have exited with a zero status.
int ret;
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
EXPECT_NE(0, WIFEXITED(ret));
EXPECT_EQ(0, WEXITSTATUS(ret));
// Read the minidump. Locate the exception record and the
// memory list, and then ensure that there is a memory region
// in the memory list that covers the instruction pointer from
// the exception record.
Minidump minidump(minidump_file);
ASSERT_TRUE(minidump.Read());
MinidumpException* exception = minidump.GetException();
MinidumpMemoryList* memory_list = minidump.GetMemoryList();
ASSERT_TRUE(exception);
ASSERT_TRUE(memory_list);
ASSERT_NE((unsigned int)0, memory_list->region_count());
MinidumpContext* context = exception->GetContext();
ASSERT_TRUE(context);
u_int64_t instruction_pointer;
switch (context->GetContextCPU()) {
case MD_CONTEXT_X86:
instruction_pointer = context->GetContextX86()->eip;
break;
case MD_CONTEXT_AMD64:
instruction_pointer = context->GetContextAMD64()->rip;
break;
case MD_CONTEXT_ARM:
instruction_pointer = context->GetContextARM()->iregs[15];
break;
default:
FAIL() << "Unknown context CPU: " << context->GetContextCPU();
break;
}
MinidumpMemoryRegion* region =
memory_list->GetMemoryRegionForAddress(instruction_pointer);
EXPECT_TRUE(region);
EXPECT_EQ(kMemorySize / 2, region->GetSize());
const u_int8_t* bytes = region->GetMemory();
ASSERT_TRUE(bytes);
u_int8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
memset(suffix_bytes, 0, sizeof(suffix_bytes));
EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
suffix_bytes, sizeof(suffix_bytes)) == 0);
}
// Test that the memory region around the instruction pointer is
// bounded correctly on the high end.
TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
// Give the child process a pipe to report back on.
int fds[2];
ASSERT_EQ(0, pipe(fds));
// These are defined here so the parent can use them to check the
// data from the minidump afterwards.
// Use 4k here because the OS will hand out a single page even
// if a smaller size is requested, and this test wants to
// test the upper bound of the memory range.
const u_int32_t kMemorySize = 4096; // bytes
// This crashes with SIGILL on x86/x86-64/arm.
const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
const int kOffset = kMemorySize - sizeof(instructions);
pid_t pid = fork();
if (pid == 0) {
close(fds[0]);
ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
// Get some executable memory.
char* memory =
reinterpret_cast<char*>(mmap(NULL,
kMemorySize,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANON,
-1,
0));
if (!memory)
exit(0);
// Write some instructions that will crash. Put them at the start
// of the block of memory, to ensure that the memory bounding
// works properly.
memcpy(memory + kOffset, instructions, sizeof(instructions));
// Now execute the instructions, which should crash.
typedef void (*void_function)(void);
void_function memory_function =
reinterpret_cast<void_function>(memory + kOffset);
memory_function();
// not reached
exit(1);
}
// In the parent process.
ASSERT_NE(-1, pid);
close(fds[1]);
// Wait for the background process to return the minidump file.
close(fds[1]);
char minidump_file[PATH_MAX];
ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
ASSERT_NE(0, nbytes);
// Ensure that minidump file exists and is > 0 bytes.
struct stat st;
ASSERT_EQ(0, stat(minidump_file, &st));
ASSERT_LT(0, st.st_size);
// Child process should have exited with a zero status.
int ret;
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
EXPECT_NE(0, WIFEXITED(ret));
EXPECT_EQ(0, WEXITSTATUS(ret));
// Read the minidump. Locate the exception record and the
// memory list, and then ensure that there is a memory region
// in the memory list that covers the instruction pointer from
// the exception record.
Minidump minidump(minidump_file);
ASSERT_TRUE(minidump.Read());
MinidumpException* exception = minidump.GetException();
MinidumpMemoryList* memory_list = minidump.GetMemoryList();
ASSERT_TRUE(exception);
ASSERT_TRUE(memory_list);
ASSERT_NE((unsigned int)0, memory_list->region_count());
MinidumpContext* context = exception->GetContext();
ASSERT_TRUE(context);
u_int64_t instruction_pointer;
switch (context->GetContextCPU()) {
case MD_CONTEXT_X86:
instruction_pointer = context->GetContextX86()->eip;
break;
case MD_CONTEXT_AMD64:
instruction_pointer = context->GetContextAMD64()->rip;
break;
case MD_CONTEXT_ARM:
instruction_pointer = context->GetContextARM()->iregs[15];
break;
default:
FAIL() << "Unknown context CPU: " << context->GetContextCPU();
break;
}
MinidumpMemoryRegion* region =
memory_list->GetMemoryRegionForAddress(instruction_pointer);
EXPECT_TRUE(region);
const size_t kPrefixSize = 128; // bytes
EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
const u_int8_t* bytes = region->GetMemory();
ASSERT_TRUE(bytes);
u_int8_t prefix_bytes[kPrefixSize];
memset(prefix_bytes, 0, sizeof(prefix_bytes));
EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
EXPECT_TRUE(memcmp(bytes + kPrefixSize,
instructions, sizeof(instructions)) == 0);
}
// Ensure that an extra memory block doesn't get added when the
// instruction pointer is not in mapped memory.
TEST_F(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
// Give the child process a pipe to report back on.
int fds[2];
ASSERT_EQ(0, pipe(fds));
pid_t pid = fork();
if (pid == 0) {
close(fds[0]);
ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
// Try calling a NULL pointer.
typedef void (*void_function)(void);
void_function memory_function =
reinterpret_cast<void_function>(NULL);
memory_function();
// not reached
exit(1);
}
// In the parent process.
ASSERT_NE(-1, pid);
close(fds[1]);
// Wait for the background process to return the minidump file.
close(fds[1]);
char minidump_file[PATH_MAX];
ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
ASSERT_NE(0, nbytes);
// Ensure that minidump file exists and is > 0 bytes.
struct stat st;
ASSERT_EQ(0, stat(minidump_file, &st));
ASSERT_LT(0, st.st_size);
// Child process should have exited with a zero status.
int ret;
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
EXPECT_NE(0, WIFEXITED(ret));
EXPECT_EQ(0, WEXITSTATUS(ret));
// Read the minidump. Locate the exception record and the
// memory list, and then ensure that there is only one memory region
// in the memory list (the thread memory from the single thread).
Minidump minidump(minidump_file);
ASSERT_TRUE(minidump.Read());
MinidumpException* exception = minidump.GetException();
MinidumpMemoryList* memory_list = minidump.GetMemoryList();
ASSERT_TRUE(exception);
ASSERT_TRUE(memory_list);
ASSERT_EQ((unsigned int)1, memory_list->region_count());
}
static void *Junk(void *) {
sleep(1000000);
return NULL;
}
// Test that the memory list gets written correctly when multiple
// threads are running.
TEST_F(ExceptionHandlerTest, MemoryListMultipleThreads) {
// Give the child process a pipe to report back on.
int fds[2];
ASSERT_EQ(0, pipe(fds));
pid_t pid = fork();
if (pid == 0) {
close(fds[0]);
ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
// Run an extra thread so >2 memory regions will be written.
pthread_t junk_thread;
if (pthread_create(&junk_thread, NULL, Junk, NULL) == 0)
pthread_detach(junk_thread);
// Just crash.
Crasher();
// not reached
exit(1);
}
// In the parent process.
ASSERT_NE(-1, pid);
close(fds[1]);
// Wait for the background process to return the minidump file.
close(fds[1]);
char minidump_file[PATH_MAX];
ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
ASSERT_NE(0, nbytes);
// Ensure that minidump file exists and is > 0 bytes.
struct stat st;
ASSERT_EQ(0, stat(minidump_file, &st));
ASSERT_LT(0, st.st_size);
// Child process should have exited with a zero status.
int ret;
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
EXPECT_NE(0, WIFEXITED(ret));
EXPECT_EQ(0, WEXITSTATUS(ret));
// Read the minidump, and verify that the memory list can be read.
Minidump minidump(minidump_file);
ASSERT_TRUE(minidump.Read());
MinidumpMemoryList* memory_list = minidump.GetMemoryList();
ASSERT_TRUE(memory_list);
// Verify that there are three memory regions:
// one per thread, and one for the instruction pointer memory.
ASSERT_EQ((unsigned int)3, memory_list->region_count());
}
}

View File

@ -0,0 +1,319 @@
// Copyright (c) 2010, 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.
// minidump_generator_test.cc: Unit tests for google_breakpad::MinidumpGenerator
#include <AvailabilityMacros.h>
#ifndef MAC_OS_X_VERSION_10_6
#define MAC_OS_X_VERSION_10_6 1060
#endif
#include <sys/stat.h>
#include <unistd.h>
#include <string>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "client/mac/handler/minidump_generator.h"
#include "client/mac/tests/auto_tempdir.h"
#include "client/mac/tests/spawn_child_process.h"
#include "common/mac/MachIPC.h"
#include "google_breakpad/processor/minidump.h"
namespace google_breakpad {
// This acts as the log sink for INFO logging from the processor
// logging code. The logging output confuses XCode and makes it think
// there are unit test failures. testlogging.h handles the overriding.
std::ostringstream info_log;
}
namespace {
using std::string;
using std::vector;
using google_breakpad::AutoTempDir;
using google_breakpad::MinidumpGenerator;
using google_breakpad::MachPortSender;
using google_breakpad::MachReceiveMessage;
using google_breakpad::MachSendMessage;
using google_breakpad::Minidump;
using google_breakpad::MinidumpContext;
using google_breakpad::MinidumpException;
using google_breakpad::MinidumpModule;
using google_breakpad::MinidumpModuleList;
using google_breakpad::MinidumpSystemInfo;
using google_breakpad::MinidumpThread;
using google_breakpad::MinidumpThreadList;
using google_breakpad::ReceivePort;
using testing::Test;
using namespace google_breakpad_test;
class MinidumpGeneratorTest : public Test {
public:
AutoTempDir tempDir;
};
static void *Junk(void* data) {
bool* wait = reinterpret_cast<bool*>(data);
while (!*wait) {
usleep(10000);
}
return NULL;
}
TEST_F(MinidumpGeneratorTest, InProcess) {
MinidumpGenerator generator;
string dump_filename = MinidumpGenerator::UniqueNameInDirectory(tempDir.path,
NULL);
// Run an extra thread since MinidumpGenerator assumes there
// are 2 or more threads.
pthread_t junk_thread;
bool quit = false;
ASSERT_EQ(0, pthread_create(&junk_thread, NULL, Junk, &quit));
ASSERT_TRUE(generator.Write(dump_filename.c_str()));
// Ensure that minidump file exists and is > 0 bytes.
struct stat st;
ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
ASSERT_LT(0, st.st_size);
// join the background thread
quit = true;
pthread_join(junk_thread, NULL);
// Read the minidump, sanity check some data.
Minidump minidump(dump_filename.c_str());
ASSERT_TRUE(minidump.Read());
MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
ASSERT_TRUE(system_info);
const MDRawSystemInfo* raw_info = system_info->system_info();
ASSERT_TRUE(raw_info);
EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
MinidumpThreadList* thread_list = minidump.GetThreadList();
ASSERT_TRUE(thread_list);
ASSERT_EQ((unsigned int)1, thread_list->thread_count());
MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
ASSERT_TRUE(main_thread);
MinidumpContext* context = main_thread->GetContext();
ASSERT_TRUE(context);
EXPECT_EQ(kNativeContext, context->GetContextCPU());
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* main_module = module_list->GetMainModule();
ASSERT_TRUE(main_module);
EXPECT_EQ(GetExecutablePath(), main_module->code_file());
}
TEST_F(MinidumpGeneratorTest, OutOfProcess) {
const int kTimeoutMs = 2000;
// Create a mach port to receive the child task on.
char machPortName[128];
sprintf(machPortName, "MinidumpGeneratorTest.OutOfProcess.%d", getpid());
ReceivePort parent_recv_port(machPortName);
// Give the child process a pipe to block on.
int fds[2];
ASSERT_EQ(0, pipe(fds));
// Fork off a child process to dump.
pid_t pid = fork();
if (pid == 0) {
// In the child process
close(fds[1]);
// Send parent process the task port.
MachSendMessage child_message(0);
child_message.AddDescriptor(mach_task_self());
MachPortSender child_sender(machPortName);
if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS) {
fprintf(stderr, "Error sending message from child process!\n");
exit(1);
}
// Wait for the parent process.
uint8_t data;
read(fds[0], &data, 1);
exit(0);
}
// In the parent process.
ASSERT_NE(-1, pid);
close(fds[0]);
// Read the child's task port.
MachReceiveMessage child_message;
ASSERT_EQ(KERN_SUCCESS,
parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
mach_port_t child_task = child_message.GetTranslatedPort(0);
ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
// Write a minidump of the child process.
MinidumpGenerator generator(child_task, MACH_PORT_NULL);
string dump_filename = MinidumpGenerator::UniqueNameInDirectory(tempDir.path,
NULL);
ASSERT_TRUE(generator.Write(dump_filename.c_str()));
// Ensure that minidump file exists and is > 0 bytes.
struct stat st;
ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
ASSERT_LT(0, st.st_size);
// Unblock child process
uint8_t data = 1;
(void)write(fds[1], &data, 1);
// Child process should have exited with a zero status.
int ret;
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
EXPECT_NE(0, WIFEXITED(ret));
EXPECT_EQ(0, WEXITSTATUS(ret));
// Read the minidump, sanity check some data.
Minidump minidump(dump_filename.c_str());
ASSERT_TRUE(minidump.Read());
MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
ASSERT_TRUE(system_info);
const MDRawSystemInfo* raw_info = system_info->system_info();
ASSERT_TRUE(raw_info);
EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
MinidumpThreadList* thread_list = minidump.GetThreadList();
ASSERT_TRUE(thread_list);
ASSERT_EQ((unsigned int)1, thread_list->thread_count());
MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
ASSERT_TRUE(main_thread);
MinidumpContext* context = main_thread->GetContext();
ASSERT_TRUE(context);
EXPECT_EQ(kNativeContext, context->GetContextCPU());
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* main_module = module_list->GetMainModule();
ASSERT_TRUE(main_module);
EXPECT_EQ(GetExecutablePath(), main_module->code_file());
}
// This test fails on 10.5, but I don't have easy access to a 10.5 machine,
// so it's simpler to just limit it to 10.6 for now.
#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) && \
(defined(__x86_64__) || defined(__i386__))
TEST_F(MinidumpGeneratorTest, CrossArchitectureDump) {
const int kTimeoutMs = 5000;
// Create a mach port to receive the child task on.
char machPortName[128];
sprintf(machPortName,
"MinidumpGeneratorTest.CrossArchitectureDump.%d", getpid());
ReceivePort parent_recv_port(machPortName);
// Spawn a child process to dump.
string helper_path = GetHelperPath();
const char* argv[] = {
helper_path.c_str(),
machPortName,
NULL
};
pid_t pid = spawn_child_process(argv);
ASSERT_NE(-1, pid);
// Read the child's task port.
MachReceiveMessage child_message;
ASSERT_EQ(KERN_SUCCESS,
parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
mach_port_t child_task = child_message.GetTranslatedPort(0);
ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
// Write a minidump of the child process.
MinidumpGenerator generator(child_task, MACH_PORT_NULL);
string dump_filename = MinidumpGenerator::UniqueNameInDirectory(tempDir.path,
NULL);
ASSERT_TRUE(generator.Write(dump_filename.c_str()));
// Ensure that minidump file exists and is > 0 bytes.
struct stat st;
ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
ASSERT_LT(0, st.st_size);
// Kill child process.
kill(pid, SIGKILL);
int ret;
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
const MDCPUArchitecture kExpectedArchitecture =
#if defined(__x86_64__)
MD_CPU_ARCHITECTURE_X86
#elif defined(__i386__)
MD_CPU_ARCHITECTURE_AMD64
#endif
;
const u_int32_t kExpectedContext =
#if defined(__i386__)
MD_CONTEXT_AMD64
#elif defined(__x86_64__)
MD_CONTEXT_X86
#endif
;
// Read the minidump, sanity check some data.
Minidump minidump(dump_filename.c_str());
ASSERT_TRUE(minidump.Read());
MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
ASSERT_TRUE(system_info);
const MDRawSystemInfo* raw_info = system_info->system_info();
ASSERT_TRUE(raw_info);
EXPECT_EQ(kExpectedArchitecture, raw_info->processor_architecture);
MinidumpThreadList* thread_list = minidump.GetThreadList();
ASSERT_TRUE(thread_list);
ASSERT_EQ((unsigned int)1, thread_list->thread_count());
MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
ASSERT_TRUE(main_thread);
MinidumpContext* context = main_thread->GetContext();
ASSERT_TRUE(context);
EXPECT_EQ(kExpectedContext, context->GetContextCPU());
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* main_module = module_list->GetMainModule();
ASSERT_TRUE(main_module);
EXPECT_EQ(helper_path, main_module->code_file());
}
#endif // 10.6 && (x86-64 || i386)
}

View File

@ -0,0 +1,74 @@
// Copyright (c) 2010, 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.
// minidump_generator_test_helper.cc: A helper program that
// minidump_generator_test.cc can launch to test certain things
// that require a separate executable.
#include <unistd.h>
#include "client/mac/handler/exception_handler.h"
#include "common/mac/MachIPC.h"
using google_breakpad::MachPortSender;
using google_breakpad::MachReceiveMessage;
using google_breakpad::MachSendMessage;
using google_breakpad::ReceivePort;
int main(int argc, char** argv) {
if (argc < 2)
return 1;
if (strcmp(argv[1], "crash") != 0) {
const int kTimeoutMs = 2000;
// Send parent process the task and thread ports.
MachSendMessage child_message(0);
child_message.AddDescriptor(mach_task_self());
child_message.AddDescriptor(mach_thread_self());
MachPortSender child_sender(argv[1]);
if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS) {
fprintf(stderr, "Error sending message from child process!\n");
exit(1);
}
// Loop forever.
while (true) {
sleep(100);
}
} else if (argc == 3 && strcmp(argv[1], "crash") == 0) {
// Instantiate an OOP exception handler
google_breakpad::ExceptionHandler eh("", NULL, NULL, NULL, true, argv[2]);
// and crash.
int *a = (int*)0x42;
*a = 1;
}
return 0;
}

View File

@ -0,0 +1,149 @@
// Copyright (c) 2010, 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 functions for spawning a helper process using a different
// CPU architecture.
#ifndef GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_SPAWN_CHILD_PROCESS
#define GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_SPAWN_CHILD_PROCESS
#include <AvailabilityMacros.h>
#ifndef MAC_OS_X_VERSION_10_6
#define MAC_OS_X_VERSION_10_6 1060
#endif
#include <crt_externs.h>
#include <mach-o/dyld.h>
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
#include <spawn.h>
#endif
#include <string>
#include <vector>
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad_test {
using std::string;
using std::vector;
const MDCPUArchitecture kNativeArchitecture =
#if defined(__i386__)
MD_CPU_ARCHITECTURE_X86
#elif defined(__x86_64__)
MD_CPU_ARCHITECTURE_AMD64
#elif defined(__ppc__) || defined(__ppc64__)
MD_CPU_ARCHITECTURE_PPC
#else
#error "This file has not been ported to this CPU architecture."
#endif
;
const u_int32_t kNativeContext =
#if defined(__i386__)
MD_CONTEXT_X86
#elif defined(__x86_64__)
MD_CONTEXT_AMD64
#elif defined(__ppc__) || defined(__ppc64__)
MD_CONTEXT_PPC
#else
#error "This file has not been ported to this CPU architecture."
#endif
;
string GetExecutablePath() {
char self_path[PATH_MAX];
uint32_t size = sizeof(self_path);
if (_NSGetExecutablePath(self_path, &size) != 0)
return "";
return self_path;
}
string GetHelperPath() {
string helper_path(GetExecutablePath());
size_t pos = helper_path.rfind('/');
if (pos == string::npos)
return "";
helper_path.erase(pos + 1);
helper_path += "minidump_generator_test_helper";
return helper_path;
}
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
pid_t spawn_child_process(const char** argv) {
posix_spawnattr_t spawnattr;
if (posix_spawnattr_init(&spawnattr) != 0)
return (pid_t)-1;
cpu_type_t pref_cpu_types[2] = {
#if defined(__x86_64__)
CPU_TYPE_X86,
#elif defined(__i386__)
CPU_TYPE_X86_64,
#endif
CPU_TYPE_ANY
};
// Set spawn attributes.
size_t attr_count = sizeof(pref_cpu_types) / sizeof(pref_cpu_types[0]);
size_t attr_ocount = 0;
if (posix_spawnattr_setbinpref_np(&spawnattr,
attr_count,
pref_cpu_types,
&attr_ocount) != 0 ||
attr_ocount != attr_count) {
posix_spawnattr_destroy(&spawnattr);
return (pid_t)-1;
}
// Create an argv array.
vector<char*> argv_v;
while (*argv) {
argv_v.push_back(strdup(*argv));
argv++;
}
argv_v.push_back(NULL);
pid_t new_pid = 0;
int result = posix_spawnp(&new_pid, argv_v[0], NULL, &spawnattr,
&argv_v[0], *_NSGetEnviron());
posix_spawnattr_destroy(&spawnattr);
for (unsigned i = 0; i < argv_v.size(); i++) {
free(argv_v[i]);
}
return result == 0 ? new_pid : -1;
}
#endif
} // namespace google_breakpad_test
#endif // GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_SPAWN_CHILD_PROCESS

View File

@ -0,0 +1,9 @@
// This file exists to override the processor logging for unit tests,
// since it confuses XCode into thinking unit tests have failed.
#include <sstream>
namespace google_breakpad {
extern std::ostringstream info_log;
}
#define BPLOG_INFO_STREAM google_breakpad::info_log

View File

@ -0,0 +1,263 @@
// -*- mode: c++ -*-
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// byte_cursor.h: Classes for parsing values from a buffer of bytes.
// The ByteCursor class provides a convenient interface for reading
// fixed-size integers of arbitrary endianness, being thorough about
// checking for buffer overruns.
#ifndef COMMON_BYTE_CURSOR_H_
#define COMMON_BYTE_CURSOR_H_
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <string>
namespace google_breakpad {
// A buffer holding a series of bytes.
struct ByteBuffer {
ByteBuffer() : start(0), end(0) { }
ByteBuffer(const uint8_t *set_start, size_t set_size)
: start(set_start), end(set_start + set_size) { }
~ByteBuffer() { };
// Equality operators. Useful in unit tests, and when we're using
// ByteBuffers to refer to regions of a larger buffer.
bool operator==(const ByteBuffer &that) const {
return start == that.start && end == that.end;
}
bool operator!=(const ByteBuffer &that) const {
return start != that.start || end != that.end;
}
// Not C++ style guide compliant, but this definitely belongs here.
size_t Size() const {
assert(start <= end);
return end - start;
}
const uint8_t *start, *end;
};
// A cursor pointing into a ByteBuffer that can parse numbers of various
// widths and representations, strings, and data blocks, advancing through
// the buffer as it goes. All ByteCursor operations check that accesses
// haven't gone beyond the end of the enclosing ByteBuffer.
class ByteCursor {
public:
// Create a cursor reading bytes from the start of BUFFER. By default, the
// cursor reads multi-byte values in little-endian form.
ByteCursor(const ByteBuffer *buffer, bool big_endian = false)
: buffer_(buffer), here_(buffer->start),
big_endian_(big_endian), complete_(true) { }
// Accessor and setter for this cursor's endianness flag.
bool big_endian() const { return big_endian_; }
void set_big_endian(bool big_endian) { big_endian_ = big_endian; }
// Accessor and setter for this cursor's current position. The setter
// returns a reference to this cursor.
const uint8_t *here() const { return here_; }
ByteCursor &set_here(const uint8_t *here) {
assert(buffer_->start <= here && here <= buffer_->end);
here_ = here;
return *this;
}
// Return the number of bytes available to read at the cursor.
size_t Available() const { return size_t(buffer_->end - here_); }
// Return true if this cursor is at the end of its buffer.
bool AtEnd() const { return Available() == 0; }
// When used as a boolean value this cursor converts to true if all
// prior reads have been completed, or false if we ran off the end
// of the buffer.
operator bool() const { return complete_; }
// Read a SIZE-byte integer at this cursor, signed if IS_SIGNED is true,
// unsigned otherwise, using the cursor's established endianness, and set
// *RESULT to the number. If we read off the end of our buffer, clear
// this cursor's complete_ flag, and store a dummy value in *RESULT.
// Return a reference to this cursor.
template<typename T>
ByteCursor &Read(size_t size, bool is_signed, T *result) {
if (CheckAvailable(size)) {
T v = 0;
if (big_endian_) {
for (size_t i = 0; i < size; i++)
v = (v << 8) + here_[i];
} else {
// This loop condition looks weird, but size_t is unsigned, so
// decrementing i after it is zero yields the largest size_t value.
for (size_t i = size - 1; i < size; i--)
v = (v << 8) + here_[i];
}
if (is_signed && size < sizeof(T)) {
size_t sign_bit = (T)1 << (size * 8 - 1);
v = (v ^ sign_bit) - sign_bit;
}
here_ += size;
*result = v;
} else {
*result = (T) 0xdeadbeef;
}
return *this;
}
// Read an integer, using the cursor's established endianness and
// *RESULT's size and signedness, and set *RESULT to the number. If we
// read off the end of our buffer, clear this cursor's complete_ flag.
// Return a reference to this cursor.
template<typename T>
ByteCursor &operator>>(T &result) {
bool T_is_signed = (T)-1 < 0;
return Read(sizeof(T), T_is_signed, &result);
}
// Copy the SIZE bytes at the cursor to BUFFER, and advance this
// cursor to the end of them. If we read off the end of our buffer,
// clear this cursor's complete_ flag, and set *POINTER to NULL.
// Return a reference to this cursor.
ByteCursor &Read(uint8_t *buffer, size_t size) {
if (CheckAvailable(size)) {
memcpy(buffer, here_, size);
here_ += size;
}
return *this;
}
// Set STR to a copy of the '\0'-terminated string at the cursor. If the
// byte buffer does not contain a terminating zero, clear this cursor's
// complete_ flag, and set STR to the empty string. Return a reference to
// this cursor.
ByteCursor &CString(std::string *str) {
const uint8_t *end
= static_cast<const uint8_t *>(memchr(here_, '\0', Available()));
if (end) {
str->assign(reinterpret_cast<const char *>(here_), end - here_);
here_ = end + 1;
} else {
str->clear();
here_ = buffer_->end;
complete_ = false;
}
return *this;
}
// Like CString(STR), but extract the string from a fixed-width buffer
// LIMIT bytes long, which may or may not contain a terminating '\0'
// byte. Specifically:
//
// - If there are not LIMIT bytes available at the cursor, clear the
// cursor's complete_ flag and set STR to the empty string.
//
// - Otherwise, if the LIMIT bytes at the cursor contain any '\0'
// characters, set *STR to a copy of the bytes before the first '\0',
// and advance the cursor by LIMIT bytes.
//
// - Otherwise, set *STR to a copy of those LIMIT bytes, and advance the
// cursor by LIMIT bytes.
ByteCursor &CString(std::string *str, size_t limit) {
if (CheckAvailable(limit)) {
const uint8_t *end
= static_cast<const uint8_t *>(memchr(here_, '\0', limit));
if (end)
str->assign(reinterpret_cast<const char *>(here_), end - here_);
else
str->assign(reinterpret_cast<const char *>(here_), limit);
here_ += limit;
} else {
str->clear();
}
return *this;
}
// Set *POINTER to point to the SIZE bytes at the cursor, and advance
// this cursor to the end of them. If SIZE is omitted, don't move the
// cursor. If we read off the end of our buffer, clear this cursor's
// complete_ flag, and set *POINTER to NULL. Return a reference to this
// cursor.
ByteCursor &PointTo(const uint8_t **pointer, size_t size = 0) {
if (CheckAvailable(size)) {
*pointer = here_;
here_ += size;
} else {
*pointer = NULL;
}
return *this;
}
// Skip SIZE bytes at the cursor. If doing so would advance us off
// the end of our buffer, clear this cursor's complete_ flag, and
// set *POINTER to NULL. Return a reference to this cursor.
ByteCursor &Skip(size_t size) {
if (CheckAvailable(size))
here_ += size;
return *this;
}
private:
// If there are at least SIZE bytes available to read from the buffer,
// return true. Otherwise, set here_ to the end of the buffer, set
// complete_ to false, and return false.
bool CheckAvailable(size_t size) {
if (Available() >= size) {
return true;
} else {
here_ = buffer_->end;
complete_ = false;
return false;
}
}
// The buffer we're reading bytes from.
const ByteBuffer *buffer_;
// The next byte within buffer_ that we'll read.
const uint8_t *here_;
// True if we should read numbers in big-endian form; false if we
// should read in little-endian form.
bool big_endian_;
// True if we've been able to read all we've been asked to.
bool complete_;
};
} // namespace google_breakpad
#endif // COMMON_BYTE_CURSOR_H_

View File

@ -0,0 +1,175 @@
// Copyright 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.
#ifndef UTIL_DEBUGINFO_BYTEREADER_INL_H__
#define UTIL_DEBUGINFO_BYTEREADER_INL_H__
#include "common/dwarf/bytereader.h"
#include <assert.h>
namespace dwarf2reader {
inline uint8 ByteReader::ReadOneByte(const char* buffer) const {
return buffer[0];
}
inline uint16 ByteReader::ReadTwoBytes(const char* signed_buffer) const {
const unsigned char *buffer
= reinterpret_cast<const unsigned char *>(signed_buffer);
const uint16 buffer0 = buffer[0];
const uint16 buffer1 = buffer[1];
if (endian_ == ENDIANNESS_LITTLE) {
return buffer0 | buffer1 << 8;
} else {
return buffer1 | buffer0 << 8;
}
}
inline uint64 ByteReader::ReadFourBytes(const char* signed_buffer) const {
const unsigned char *buffer
= reinterpret_cast<const unsigned char *>(signed_buffer);
const uint32 buffer0 = buffer[0];
const uint32 buffer1 = buffer[1];
const uint32 buffer2 = buffer[2];
const uint32 buffer3 = buffer[3];
if (endian_ == ENDIANNESS_LITTLE) {
return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24;
} else {
return buffer3 | buffer2 << 8 | buffer1 << 16 | buffer0 << 24;
}
}
inline uint64 ByteReader::ReadEightBytes(const char* signed_buffer) const {
const unsigned char *buffer
= reinterpret_cast<const unsigned char *>(signed_buffer);
const uint64 buffer0 = buffer[0];
const uint64 buffer1 = buffer[1];
const uint64 buffer2 = buffer[2];
const uint64 buffer3 = buffer[3];
const uint64 buffer4 = buffer[4];
const uint64 buffer5 = buffer[5];
const uint64 buffer6 = buffer[6];
const uint64 buffer7 = buffer[7];
if (endian_ == ENDIANNESS_LITTLE) {
return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24 |
buffer4 << 32 | buffer5 << 40 | buffer6 << 48 | buffer7 << 56;
} else {
return buffer7 | buffer6 << 8 | buffer5 << 16 | buffer4 << 24 |
buffer3 << 32 | buffer2 << 40 | buffer1 << 48 | buffer0 << 56;
}
}
// Read an unsigned LEB128 number. Each byte contains 7 bits of
// information, plus one bit saying whether the number continues or
// not.
inline uint64 ByteReader::ReadUnsignedLEB128(const char* buffer,
size_t* len) const {
uint64 result = 0;
size_t num_read = 0;
unsigned int shift = 0;
unsigned char byte;
do {
byte = *buffer++;
num_read++;
result |= (static_cast<uint64>(byte & 0x7f)) << shift;
shift += 7;
} while (byte & 0x80);
*len = num_read;
return result;
}
// Read a signed LEB128 number. These are like regular LEB128
// numbers, except the last byte may have a sign bit set.
inline int64 ByteReader::ReadSignedLEB128(const char* buffer,
size_t* len) const {
int64 result = 0;
unsigned int shift = 0;
size_t num_read = 0;
unsigned char byte;
do {
byte = *buffer++;
num_read++;
result |= (static_cast<uint64>(byte & 0x7f) << shift);
shift += 7;
} while (byte & 0x80);
if ((shift < 8 * sizeof (result)) && (byte & 0x40))
result |= -((static_cast<int64>(1)) << shift);
*len = num_read;
return result;
}
inline uint64 ByteReader::ReadOffset(const char* buffer) const {
assert(this->offset_reader_);
return (this->*offset_reader_)(buffer);
}
inline uint64 ByteReader::ReadAddress(const char* buffer) const {
assert(this->address_reader_);
return (this->*address_reader_)(buffer);
}
inline void ByteReader::SetCFIDataBase(uint64 section_base,
const char *buffer_base) {
section_base_ = section_base;
buffer_base_ = buffer_base;
have_section_base_ = true;
}
inline void ByteReader::SetTextBase(uint64 text_base) {
text_base_ = text_base;
have_text_base_ = true;
}
inline void ByteReader::SetDataBase(uint64 data_base) {
data_base_ = data_base;
have_data_base_ = true;
}
inline void ByteReader::SetFunctionBase(uint64 function_base) {
function_base_ = function_base;
have_function_base_ = true;
}
inline void ByteReader::ClearFunctionBase() {
have_function_base_ = false;
}
} // namespace dwarf2reader
#endif // UTIL_DEBUGINFO_BYTEREADER_INL_H__

View File

@ -0,0 +1,245 @@
// Copyright (c) 2010 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 <assert.h>
#include <stdlib.h>
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/bytereader.h"
namespace dwarf2reader {
ByteReader::ByteReader(enum Endianness endian)
:offset_reader_(NULL), address_reader_(NULL), endian_(endian),
address_size_(0), offset_size_(0),
have_section_base_(), have_text_base_(), have_data_base_(),
have_function_base_() { }
ByteReader::~ByteReader() { }
void ByteReader::SetOffsetSize(uint8 size) {
offset_size_ = size;
assert(size == 4 || size == 8);
if (size == 4) {
this->offset_reader_ = &ByteReader::ReadFourBytes;
} else {
this->offset_reader_ = &ByteReader::ReadEightBytes;
}
}
void ByteReader::SetAddressSize(uint8 size) {
address_size_ = size;
assert(size == 4 || size == 8);
if (size == 4) {
this->address_reader_ = &ByteReader::ReadFourBytes;
} else {
this->address_reader_ = &ByteReader::ReadEightBytes;
}
}
uint64 ByteReader::ReadInitialLength(const char* start, size_t* len) {
const uint64 initial_length = ReadFourBytes(start);
start += 4;
// In DWARF2/3, if the initial length is all 1 bits, then the offset
// size is 8 and we need to read the next 8 bytes for the real length.
if (initial_length == 0xffffffff) {
SetOffsetSize(8);
*len = 12;
return ReadOffset(start);
} else {
SetOffsetSize(4);
*len = 4;
}
return initial_length;
}
bool ByteReader::ValidEncoding(DwarfPointerEncoding encoding) const {
if (encoding == DW_EH_PE_omit) return true;
if (encoding == DW_EH_PE_aligned) return true;
if ((encoding & 0x7) > DW_EH_PE_udata8)
return false;
if ((encoding & 0x70) > DW_EH_PE_funcrel)
return false;
return true;
}
bool ByteReader::UsableEncoding(DwarfPointerEncoding encoding) const {
switch (encoding & 0x70) {
case DW_EH_PE_absptr: return true;
case DW_EH_PE_pcrel: return have_section_base_;
case DW_EH_PE_textrel: return have_text_base_;
case DW_EH_PE_datarel: return have_data_base_;
case DW_EH_PE_funcrel: return have_function_base_;
default: return false;
}
}
uint64 ByteReader::ReadEncodedPointer(const char *buffer,
DwarfPointerEncoding encoding,
size_t *len) const {
// UsableEncoding doesn't approve of DW_EH_PE_omit, so we shouldn't
// see it here.
assert(encoding != DW_EH_PE_omit);
// The Linux Standards Base 4.0 does not make this clear, but the
// GNU tools (gcc/unwind-pe.h; readelf/dwarf.c; gdb/dwarf2-frame.c)
// agree that aligned pointers are always absolute, machine-sized,
// machine-signed pointers.
if (encoding == DW_EH_PE_aligned) {
assert(have_section_base_);
// We don't need to align BUFFER in *our* address space. Rather, we
// need to find the next position in our buffer that would be aligned
// when the .eh_frame section the buffer contains is loaded into the
// program's memory. So align assuming that buffer_base_ gets loaded at
// address section_base_, where section_base_ itself may or may not be
// aligned.
// First, find the offset to START from the closest prior aligned
// address.
uint64 skew = section_base_ & (AddressSize() - 1);
// Now find the offset from that aligned address to buffer.
uint64 offset = skew + (buffer - buffer_base_);
// Round up to the next boundary.
uint64 aligned = (offset + AddressSize() - 1) & -AddressSize();
// Convert back to a pointer.
const char *aligned_buffer = buffer_base_ + (aligned - skew);
// Finally, store the length and actually fetch the pointer.
*len = aligned_buffer - buffer + AddressSize();
return ReadAddress(aligned_buffer);
}
// Extract the value first, ignoring whether it's a pointer or an
// offset relative to some base.
uint64 offset;
switch (encoding & 0x0f) {
case DW_EH_PE_absptr:
// DW_EH_PE_absptr is weird, as it is used as a meaningful value for
// both the high and low nybble of encoding bytes. When it appears in
// the high nybble, it means that the pointer is absolute, not an
// offset from some base address. When it appears in the low nybble,
// as here, it means that the pointer is stored as a normal
// machine-sized and machine-signed address. A low nybble of
// DW_EH_PE_absptr does not imply that the pointer is absolute; it is
// correct for us to treat the value as an offset from a base address
// if the upper nybble is not DW_EH_PE_absptr.
offset = ReadAddress(buffer);
*len = AddressSize();
break;
case DW_EH_PE_uleb128:
offset = ReadUnsignedLEB128(buffer, len);
break;
case DW_EH_PE_udata2:
offset = ReadTwoBytes(buffer);
*len = 2;
break;
case DW_EH_PE_udata4:
offset = ReadFourBytes(buffer);
*len = 4;
break;
case DW_EH_PE_udata8:
offset = ReadEightBytes(buffer);
*len = 8;
break;
case DW_EH_PE_sleb128:
offset = ReadSignedLEB128(buffer, len);
break;
case DW_EH_PE_sdata2:
offset = ReadTwoBytes(buffer);
// Sign-extend from 16 bits.
offset = (offset ^ 0x8000) - 0x8000;
*len = 2;
break;
case DW_EH_PE_sdata4:
offset = ReadFourBytes(buffer);
// Sign-extend from 32 bits.
offset = (offset ^ 0x80000000ULL) - 0x80000000ULL;
*len = 4;
break;
case DW_EH_PE_sdata8:
// No need to sign-extend; this is the full width of our type.
offset = ReadEightBytes(buffer);
*len = 8;
break;
default:
abort();
}
// Find the appropriate base address.
uint64 base;
switch (encoding & 0x70) {
case DW_EH_PE_absptr:
base = 0;
break;
case DW_EH_PE_pcrel:
assert(have_section_base_);
base = section_base_ + (buffer - buffer_base_);
break;
case DW_EH_PE_textrel:
assert(have_text_base_);
base = text_base_;
break;
case DW_EH_PE_datarel:
assert(have_data_base_);
base = data_base_;
break;
case DW_EH_PE_funcrel:
assert(have_function_base_);
base = function_base_;
break;
default:
abort();
}
uint64 pointer = base + offset;
// Remove inappropriate upper bits.
if (AddressSize() == 4)
pointer = pointer & 0xffffffff;
else
assert(AddressSize() == sizeof(uint64));
return pointer;
}
} // namespace dwarf2reader

View File

@ -0,0 +1,310 @@
// -*- mode: C++ -*-
// Copyright (c) 2010 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef COMMON_DWARF_BYTEREADER_H__
#define COMMON_DWARF_BYTEREADER_H__
#include <string>
#include "common/dwarf/types.h"
#include "common/dwarf/dwarf2enums.h"
namespace dwarf2reader {
// We can't use the obvious name of LITTLE_ENDIAN and BIG_ENDIAN
// because it conflicts with a macro
enum Endianness {
ENDIANNESS_BIG,
ENDIANNESS_LITTLE
};
// A ByteReader knows how to read single- and multi-byte values of
// various endiannesses, sizes, and encodings, as used in DWARF
// debugging information and Linux C++ exception handling data.
class ByteReader {
public:
// Construct a ByteReader capable of reading one-, two-, four-, and
// eight-byte values according to ENDIANNESS, absolute machine-sized
// addresses, DWARF-style "initial length" values, signed and
// unsigned LEB128 numbers, and Linux C++ exception handling data's
// encoded pointers.
explicit ByteReader(enum Endianness endianness);
virtual ~ByteReader();
// Read a single byte from BUFFER and return it as an unsigned 8 bit
// number.
uint8 ReadOneByte(const char* buffer) const;
// Read two bytes from BUFFER and return them as an unsigned 16 bit
// number, using this ByteReader's endianness.
uint16 ReadTwoBytes(const char* buffer) const;
// Read four bytes from BUFFER and return them as an unsigned 32 bit
// number, using this ByteReader's endianness. This function returns
// a uint64 so that it is compatible with ReadAddress and
// ReadOffset. The number it returns will never be outside the range
// of an unsigned 32 bit integer.
uint64 ReadFourBytes(const char* buffer) const;
// Read eight bytes from BUFFER and return them as an unsigned 64
// bit number, using this ByteReader's endianness.
uint64 ReadEightBytes(const char* buffer) const;
// Read an unsigned LEB128 (Little Endian Base 128) number from
// BUFFER and return it as an unsigned 64 bit integer. Set LEN to
// the number of bytes read.
//
// The unsigned LEB128 representation of an integer N is a variable
// number of bytes:
//
// - If N is between 0 and 0x7f, then its unsigned LEB128
// representation is a single byte whose value is N.
//
// - Otherwise, its unsigned LEB128 representation is (N & 0x7f) |
// 0x80, followed by the unsigned LEB128 representation of N /
// 128, rounded towards negative infinity.
//
// In other words, we break VALUE into groups of seven bits, put
// them in little-endian order, and then write them as eight-bit
// bytes with the high bit on all but the last.
uint64 ReadUnsignedLEB128(const char* buffer, size_t* len) const;
// Read a signed LEB128 number from BUFFER and return it as an
// signed 64 bit integer. Set LEN to the number of bytes read.
//
// The signed LEB128 representation of an integer N is a variable
// number of bytes:
//
// - If N is between -0x40 and 0x3f, then its signed LEB128
// representation is a single byte whose value is N in two's
// complement.
//
// - Otherwise, its signed LEB128 representation is (N & 0x7f) |
// 0x80, followed by the signed LEB128 representation of N / 128,
// rounded towards negative infinity.
//
// In other words, we break VALUE into groups of seven bits, put
// them in little-endian order, and then write them as eight-bit
// bytes with the high bit on all but the last.
int64 ReadSignedLEB128(const char* buffer, size_t* len) const;
// Indicate that addresses on this architecture are SIZE bytes long. SIZE
// must be either 4 or 8. (DWARF allows addresses to be any number of
// bytes in length from 1 to 255, but we only support 32- and 64-bit
// addresses at the moment.) You must call this before using the
// ReadAddress member function.
//
// For data in a .debug_info section, or something that .debug_info
// refers to like line number or macro data, the compilation unit
// header's address_size field indicates the address size to use. Call
// frame information doesn't indicate its address size (a shortcoming of
// the spec); you must supply the appropriate size based on the
// architecture of the target machine.
void SetAddressSize(uint8 size);
// Return the current address size, in bytes. This is either 4,
// indicating 32-bit addresses, or 8, indicating 64-bit addresses.
uint8 AddressSize() const { return address_size_; }
// Read an address from BUFFER and return it as an unsigned 64 bit
// integer, respecting this ByteReader's endianness and address size. You
// must call SetAddressSize before calling this function.
uint64 ReadAddress(const char* buffer) const;
// DWARF actually defines two slightly different formats: 32-bit DWARF
// and 64-bit DWARF. This is *not* related to the size of registers or
// addresses on the target machine; it refers only to the size of section
// offsets and data lengths appearing in the DWARF data. One only needs
// 64-bit DWARF when the debugging data itself is larger than 4GiB.
// 32-bit DWARF can handle x86_64 or PPC64 code just fine, unless the
// debugging data itself is very large.
//
// DWARF information identifies itself as 32-bit or 64-bit DWARF: each
// compilation unit and call frame information entry begins with an
// "initial length" field, which, in addition to giving the length of the
// data, also indicates the size of section offsets and lengths appearing
// in that data. The ReadInitialLength member function, below, reads an
// initial length and sets the ByteReader's offset size as a side effect.
// Thus, in the normal process of reading DWARF data, the appropriate
// offset size is set automatically. So, you should only need to call
// SetOffsetSize if you are using the same ByteReader to jump from the
// midst of one block of DWARF data into another.
// Read a DWARF "initial length" field from START, and return it as
// an unsigned 64 bit integer, respecting this ByteReader's
// endianness. Set *LEN to the length of the initial length in
// bytes, either four or twelve. As a side effect, set this
// ByteReader's offset size to either 4 (if we see a 32-bit DWARF
// initial length) or 8 (if we see a 64-bit DWARF initial length).
//
// A DWARF initial length is either:
//
// - a byte count stored as an unsigned 32-bit value less than
// 0xffffff00, indicating that the data whose length is being
// measured uses the 32-bit DWARF format, or
//
// - The 32-bit value 0xffffffff, followed by a 64-bit byte count,
// indicating that the data whose length is being measured uses
// the 64-bit DWARF format.
uint64 ReadInitialLength(const char* start, size_t* len);
// Read an offset from BUFFER and return it as an unsigned 64 bit
// integer, respecting the ByteReader's endianness. In 32-bit DWARF, the
// offset is 4 bytes long; in 64-bit DWARF, the offset is eight bytes
// long. You must call ReadInitialLength or SetOffsetSize before calling
// this function; see the comments above for details.
uint64 ReadOffset(const char* buffer) const;
// Return the current offset size, in bytes.
// A return value of 4 indicates that we are reading 32-bit DWARF.
// A return value of 8 indicates that we are reading 64-bit DWARF.
uint8 OffsetSize() const { return offset_size_; }
// Indicate that section offsets and lengths are SIZE bytes long. SIZE
// must be either 4 (meaning 32-bit DWARF) or 8 (meaning 64-bit DWARF).
// Usually, you should not call this function yourself; instead, let a
// call to ReadInitialLength establish the data's offset size
// automatically.
void SetOffsetSize(uint8 size);
// The Linux C++ ABI uses a variant of DWARF call frame information
// for exception handling. This data is included in the program's
// address space as the ".eh_frame" section, and intepreted at
// runtime to walk the stack, find exception handlers, and run
// cleanup code. The format is mostly the same as DWARF CFI, with
// some adjustments made to provide the additional
// exception-handling data, and to make the data easier to work with
// in memory --- for example, to allow it to be placed in read-only
// memory even when describing position-independent code.
//
// In particular, exception handling data can select a number of
// different encodings for pointers that appear in the data, as
// described by the DwarfPointerEncoding enum. There are actually
// four axes(!) to the encoding:
//
// - The pointer size: pointers can be 2, 4, or 8 bytes long, or use
// the DWARF LEB128 encoding.
//
// - The pointer's signedness: pointers can be signed or unsigned.
//
// - The pointer's base address: the data stored in the exception
// handling data can be the actual address (that is, an absolute
// pointer), or relative to one of a number of different base
// addreses --- including that of the encoded pointer itself, for
// a form of "pc-relative" addressing.
//
// - The pointer may be indirect: it may be the address where the
// true pointer is stored. (This is used to refer to things via
// global offset table entries, program linkage table entries, or
// other tricks used in position-independent code.)
//
// There are also two options that fall outside that matrix
// altogether: the pointer may be omitted, or it may have padding to
// align it on an appropriate address boundary. (That last option
// may seem like it should be just another axis, but it is not.)
// Indicate that the exception handling data is loaded starting at
// SECTION_BASE, and that the start of its buffer in our own memory
// is BUFFER_BASE. This allows us to find the address that a given
// byte in our buffer would have when loaded into the program the
// data describes. We need this to resolve DW_EH_PE_pcrel pointers.
void SetCFIDataBase(uint64 section_base, const char *buffer_base);
// Indicate that the base address of the program's ".text" section
// is TEXT_BASE. We need this to resolve DW_EH_PE_textrel pointers.
void SetTextBase(uint64 text_base);
// Indicate that the base address for DW_EH_PE_datarel pointers is
// DATA_BASE. The proper value depends on the ABI; it is usually the
// address of the global offset table, held in a designated register in
// position-independent code. You will need to look at the startup code
// for the target system to be sure. I tried; my eyes bled.
void SetDataBase(uint64 data_base);
// Indicate that the base address for the FDE we are processing is
// FUNCTION_BASE. This is the start address of DW_EH_PE_funcrel
// pointers. (This encoding does not seem to be used by the GNU
// toolchain.)
void SetFunctionBase(uint64 function_base);
// Indicate that we are no longer processing any FDE, so any use of
// a DW_EH_PE_funcrel encoding is an error.
void ClearFunctionBase();
// Return true if ENCODING is a valid pointer encoding.
bool ValidEncoding(DwarfPointerEncoding encoding) const;
// Return true if we have all the information we need to read a
// pointer that uses ENCODING. This checks that the appropriate
// SetFooBase function for ENCODING has been called.
bool UsableEncoding(DwarfPointerEncoding encoding) const;
// Read an encoded pointer from BUFFER using ENCODING; return the
// absolute address it represents, and set *LEN to the pointer's
// length in bytes, including any padding for aligned pointers.
//
// This function calls 'abort' if ENCODING is invalid or refers to a
// base address this reader hasn't been given, so you should check
// with ValidEncoding and UsableEncoding first if you would rather
// die in a more helpful way.
uint64 ReadEncodedPointer(const char *buffer, DwarfPointerEncoding encoding,
size_t *len) const;
private:
// Function pointer type for our address and offset readers.
typedef uint64 (ByteReader::*AddressReader)(const char*) const;
// Read an offset from BUFFER and return it as an unsigned 64 bit
// integer. DWARF2/3 define offsets as either 4 or 8 bytes,
// generally depending on the amount of DWARF2/3 info present.
// This function pointer gets set by SetOffsetSize.
AddressReader offset_reader_;
// Read an address from BUFFER and return it as an unsigned 64 bit
// integer. DWARF2/3 allow addresses to be any size from 0-255
// bytes currently. Internally we support 4 and 8 byte addresses,
// and will CHECK on anything else.
// This function pointer gets set by SetAddressSize.
AddressReader address_reader_;
Endianness endian_;
uint8 address_size_;
uint8 offset_size_;
// Base addresses for Linux C++ exception handling data's encoded pointers.
bool have_section_base_, have_text_base_, have_data_base_;
bool have_function_base_;
uint64 section_base_, text_base_, data_base_, function_base_;
const char *buffer_base_;
};
} // namespace dwarf2reader
#endif // COMMON_DWARF_BYTEREADER_H__

View File

@ -0,0 +1,697 @@
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// bytereader_unittest.cc: Unit tests for dwarf2reader::ByteReader
#include <string>
#include "breakpad_googletest_includes.h"
#include "common/dwarf/bytereader.h"
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/cfi_assembler.h"
using dwarf2reader::ByteReader;
using dwarf2reader::DwarfPointerEncoding;
using dwarf2reader::ENDIANNESS_BIG;
using dwarf2reader::ENDIANNESS_LITTLE;
using google_breakpad::CFISection;
using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::kBigEndian;
using google_breakpad::test_assembler::kLittleEndian;
using google_breakpad::test_assembler::Section;
using std::string;
using testing::Test;
struct ReaderFixture {
string contents;
size_t pointer_size;
};
class Reader: public ReaderFixture, public Test { };
class ReaderDeathTest: public ReaderFixture, public Test { };
TEST_F(Reader, SimpleConstructor) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
CFISection section(kBigEndian, 4);
section
.D8(0xc0)
.D16(0xcf0d)
.D32(0x96fdd219)
.D64(0xbbf55fef0825f117ULL)
.ULEB128(0xa0927048ba8121afULL)
.LEB128(-0x4f337badf4483f83LL)
.D32(0xfec319c9);
ASSERT_TRUE(section.GetContents(&contents));
const char *data = contents.data();
EXPECT_EQ(0xc0U, reader.ReadOneByte(data));
EXPECT_EQ(0xcf0dU, reader.ReadTwoBytes(data + 1));
EXPECT_EQ(0x96fdd219U, reader.ReadFourBytes(data + 3));
EXPECT_EQ(0xbbf55fef0825f117ULL, reader.ReadEightBytes(data + 7));
size_t leb128_size;
EXPECT_EQ(0xa0927048ba8121afULL,
reader.ReadUnsignedLEB128(data + 15, &leb128_size));
EXPECT_EQ(10U, leb128_size);
EXPECT_EQ(-0x4f337badf4483f83LL,
reader.ReadSignedLEB128(data + 25, &leb128_size));
EXPECT_EQ(10U, leb128_size);
EXPECT_EQ(0xfec319c9, reader.ReadAddress(data + 35));
}
TEST_F(Reader, ValidEncodings) {
ByteReader reader(ENDIANNESS_LITTLE);
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_omit)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_aligned)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x05)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x07)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0d)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0f)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x51)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x60)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x70)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xf0)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xd0)));
}
TEST_F(ReaderDeathTest, DW_EH_PE_omit) {
static const char data[1] = { 42 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
EXPECT_DEATH(reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_omit,
&pointer_size),
"encoding != DW_EH_PE_omit");
}
TEST_F(Reader, DW_EH_PE_absptr4) {
static const char data[] = { 0x27, 0x57, 0xea, 0x40 };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
EXPECT_EQ(0x40ea5727U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_absptr,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_absptr8) {
static const char data[] = {
0x60, 0x27, 0x57, 0xea, 0x40, 0xc2, 0x98, 0x05, 0x01, 0x50
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0x010598c240ea5727ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_absptr,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_uleb128) {
static const char data[] = { 0x81, 0x84, 0x4c };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
EXPECT_EQ(0x130201U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_uleb128,
&pointer_size));
EXPECT_EQ(3U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata2) {
static const char data[] = { 0xf4, 0x8d };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
EXPECT_EQ(0xf48dU,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_udata2,
&pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata4) {
static const char data[] = { 0xb2, 0x68, 0xa5, 0x62, 0x8f, 0x8b };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(8);
EXPECT_EQ(0xa5628f8b,
reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_udata4,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata8Addr8) {
static const char data[] = {
0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0x8fed199f69047304ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata8Addr4) {
static const char data[] = {
0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
EXPECT_EQ(0x69047304ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sleb128) {
static const char data[] = { 0x42, 0xff, 0xfb, 0x73 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
EXPECT_EQ(-0x030201U & 0xffffffff,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sleb128,
&pointer_size));
EXPECT_EQ(3U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sdata2) {
static const char data[] = { 0xb9, 0xbf };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0xffffffffffffbfb9ULL,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_sdata2,
&pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sdata4) {
static const char data[] = { 0xa0, 0xca, 0xf2, 0xb8, 0xc2, 0xad };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0xffffffffadc2b8f2ULL,
reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_sdata4,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sdata8) {
static const char data[] = {
0xf6, 0x66, 0x57, 0x79, 0xe0, 0x0c, 0x9b, 0x26, 0x87
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0x87269b0ce0795766ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sdata8,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_pcrel) {
static const char data[] = { 0x4a, 0x8b, 0x1b, 0x14, 0xc8, 0xc4, 0x02, 0xce };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_pcrel
| dwarf2reader::DW_EH_PE_absptr);
reader.SetCFIDataBase(0x89951377, data);
EXPECT_EQ(0x89951377 + 3 + 0x14c8c402,
reader.ReadEncodedPointer(data + 3, encoding, &pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_textrel) {
static const char data[] = { 0xd9, 0x0d, 0x05, 0x17, 0xc9, 0x7a, 0x42, 0x1e };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
reader.SetTextBase(0xb91beaf0);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel
| dwarf2reader::DW_EH_PE_sdata2);
EXPECT_EQ((0xb91beaf0 + 0xffffc917) & 0xffffffff,
reader.ReadEncodedPointer(data + 3, encoding, &pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_datarel) {
static const char data[] = { 0x16, 0xf2, 0xbb, 0x82, 0x68, 0xa7, 0xbc, 0x39 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(8);
reader.SetDataBase(0xbef308bd25ce74f0ULL);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_datarel
| dwarf2reader::DW_EH_PE_sleb128);
EXPECT_EQ(0xbef308bd25ce74f0ULL + 0xfffffffffffa013bULL,
reader.ReadEncodedPointer(data + 2, encoding, &pointer_size));
EXPECT_EQ(3U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_funcrel) {
static const char data[] = { 0x84, 0xf8, 0x14, 0x01, 0x61, 0xd1, 0x48, 0xc9 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
reader.SetFunctionBase(0x823c3520);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_funcrel
| dwarf2reader::DW_EH_PE_udata2);
EXPECT_EQ(0x823c3520 + 0xd148,
reader.ReadEncodedPointer(data + 5, encoding, &pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST(UsableBase, CFI) {
static const char data[1] = { 0x42 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetCFIDataBase(0xb31cbd20, data);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, Text) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetTextBase(0xa899ccb9);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, Data) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetDataBase(0xf7b10bcd);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, Function) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetFunctionBase(0xc2c0ed81);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, ClearFunction) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetFunctionBase(0xc2c0ed81);
reader.ClearFunctionBase();
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
struct AlignedFixture {
AlignedFixture() : reader(ENDIANNESS_BIG) { reader.SetAddressSize(4); }
static const char data[10];
ByteReader reader;
size_t pointer_size;
};
const char AlignedFixture::data[10] = {
0xfe, 0x6e, 0x93, 0xd8, 0x34, 0xd5, 0x1c, 0xd3, 0xac, 0x2b
};
class Aligned: public AlignedFixture, public Test { };
TEST_F(Aligned, DW_EH_PE_aligned0) {
reader.SetCFIDataBase(0xb440305c, data);
EXPECT_EQ(0xfe6e93d8U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned1) {
reader.SetCFIDataBase(0xb440305d, data);
EXPECT_EQ(0xd834d51cU,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(7U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned2) {
reader.SetCFIDataBase(0xb440305e, data);
EXPECT_EQ(0x93d834d5U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(6U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned3) {
reader.SetCFIDataBase(0xb440305f, data);
EXPECT_EQ(0x6e93d834U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(5U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned11) {
reader.SetCFIDataBase(0xb4403061, data);
EXPECT_EQ(0xd834d51cU,
reader.ReadEncodedPointer(data + 1,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(6U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned30) {
reader.SetCFIDataBase(0xb4403063, data);
EXPECT_EQ(0x6e93d834U,
reader.ReadEncodedPointer(data + 1,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned23) {
reader.SetCFIDataBase(0xb4403062, data);
EXPECT_EQ(0x1cd3ac2bU,
reader.ReadEncodedPointer(data + 3,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(7U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned03) {
reader.SetCFIDataBase(0xb4403064, data);
EXPECT_EQ(0x34d51cd3U,
reader.ReadEncodedPointer(data + 3,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(5U, pointer_size);
}

View File

@ -0,0 +1,198 @@
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// cfi_assembler.cc: Implementation of google_breakpad::CFISection class.
// See cfi_assembler.h for details.
#include "common/dwarf/cfi_assembler.h"
#include <assert.h>
#include <stdlib.h>
namespace google_breakpad {
using dwarf2reader::DwarfPointerEncoding;
CFISection &CFISection::CIEHeader(u_int64_t code_alignment_factor,
int data_alignment_factor,
unsigned return_address_register,
u_int8_t version,
const string &augmentation,
bool dwarf64) {
assert(!entry_length_);
entry_length_ = new PendingLength();
in_fde_ = false;
if (dwarf64) {
D32(kDwarf64InitialLengthMarker);
D64(entry_length_->length);
entry_length_->start = Here();
D64(eh_frame_ ? kEHFrame64CIEIdentifier : kDwarf64CIEIdentifier);
} else {
D32(entry_length_->length);
entry_length_->start = Here();
D32(eh_frame_ ? kEHFrame32CIEIdentifier : kDwarf32CIEIdentifier);
}
D8(version);
AppendCString(augmentation);
ULEB128(code_alignment_factor);
LEB128(data_alignment_factor);
if (version == 1)
D8(return_address_register);
else
ULEB128(return_address_register);
return *this;
}
CFISection &CFISection::FDEHeader(Label cie_pointer,
u_int64_t initial_location,
u_int64_t address_range,
bool dwarf64) {
assert(!entry_length_);
entry_length_ = new PendingLength();
in_fde_ = true;
fde_start_address_ = initial_location;
if (dwarf64) {
D32(0xffffffff);
D64(entry_length_->length);
entry_length_->start = Here();
if (eh_frame_)
D64(Here() - cie_pointer);
else
D64(cie_pointer);
} else {
D32(entry_length_->length);
entry_length_->start = Here();
if (eh_frame_)
D32(Here() - cie_pointer);
else
D32(cie_pointer);
}
EncodedPointer(initial_location);
// The FDE length in an .eh_frame section uses the same encoding as the
// initial location, but ignores the base address (selected by the upper
// nybble of the encoding), as it's a length, not an address that can be
// made relative.
EncodedPointer(address_range,
DwarfPointerEncoding(pointer_encoding_ & 0x0f));
return *this;
}
CFISection &CFISection::FinishEntry() {
assert(entry_length_);
Align(address_size_, dwarf2reader::DW_CFA_nop);
entry_length_->length = Here() - entry_length_->start;
delete entry_length_;
entry_length_ = NULL;
in_fde_ = false;
return *this;
}
CFISection &CFISection::EncodedPointer(u_int64_t address,
DwarfPointerEncoding encoding,
const EncodedPointerBases &bases) {
// Omitted data is extremely easy to emit.
if (encoding == dwarf2reader::DW_EH_PE_omit)
return *this;
// If (encoding & dwarf2reader::DW_EH_PE_indirect) != 0, then we assume
// that ADDRESS is the address at which the pointer is stored --- in
// other words, that bit has no effect on how we write the pointer.
encoding = DwarfPointerEncoding(encoding & ~dwarf2reader::DW_EH_PE_indirect);
// Find the base address to which this pointer is relative. The upper
// nybble of the encoding specifies this.
u_int64_t base;
switch (encoding & 0xf0) {
case dwarf2reader::DW_EH_PE_absptr: base = 0; break;
case dwarf2reader::DW_EH_PE_pcrel: base = bases.cfi + Size(); break;
case dwarf2reader::DW_EH_PE_textrel: base = bases.text; break;
case dwarf2reader::DW_EH_PE_datarel: base = bases.data; break;
case dwarf2reader::DW_EH_PE_funcrel: base = fde_start_address_; break;
case dwarf2reader::DW_EH_PE_aligned: base = 0; break;
default: abort();
};
// Make ADDRESS relative. Yes, this is appropriate even for "absptr"
// values; see gcc/unwind-pe.h.
address -= base;
// Align the pointer, if required.
if ((encoding & 0xf0) == dwarf2reader::DW_EH_PE_aligned)
Align(AddressSize());
// Append ADDRESS to this section in the appropriate form. For the
// fixed-width forms, we don't need to differentiate between signed and
// unsigned encodings, because ADDRESS has already been extended to 64
// bits before it was passed to us.
switch (encoding & 0x0f) {
case dwarf2reader::DW_EH_PE_absptr:
Address(address);
break;
case dwarf2reader::DW_EH_PE_uleb128:
ULEB128(address);
break;
case dwarf2reader::DW_EH_PE_sleb128:
LEB128(address);
break;
case dwarf2reader::DW_EH_PE_udata2:
case dwarf2reader::DW_EH_PE_sdata2:
D16(address);
break;
case dwarf2reader::DW_EH_PE_udata4:
case dwarf2reader::DW_EH_PE_sdata4:
D32(address);
break;
case dwarf2reader::DW_EH_PE_udata8:
case dwarf2reader::DW_EH_PE_sdata8:
D64(address);
break;
default:
abort();
}
return *this;
};
const u_int32_t CFISection::kDwarf64InitialLengthMarker;
const u_int32_t CFISection::kDwarf32CIEIdentifier;
const u_int64_t CFISection::kDwarf64CIEIdentifier;
const u_int32_t CFISection::kEHFrame32CIEIdentifier;
const u_int64_t CFISection::kEHFrame64CIEIdentifier;
} // namespace google_breakpad

View File

@ -0,0 +1,269 @@
// -*- mode: C++ -*-
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// cfi_assembler.h: Define CFISection, a class for creating properly
// (and improperly) formatted DWARF CFI data for unit tests.
#ifndef PROCESSOR_CFI_ASSEMBLER_H_
#define PROCESSOR_CFI_ASSEMBLER_H_
#include <string>
#include "common/dwarf/dwarf2enums.h"
#include "common/test_assembler.h"
#include "google_breakpad/common/breakpad_types.h"
namespace google_breakpad {
using dwarf2reader::DwarfPointerEncoding;
using google_breakpad::test_assembler::Endianness;
using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::Section;
using std::string;
class CFISection: public Section {
public:
// CFI augmentation strings beginning with 'z', defined by the
// Linux/IA-64 C++ ABI, can specify interesting encodings for
// addresses appearing in FDE headers and call frame instructions (and
// for additional fields whose presence the augmentation string
// specifies). In particular, pointers can be specified to be relative
// to various base address: the start of the .text section, the
// location holding the address itself, and so on. These allow the
// frame data to be position-independent even when they live in
// write-protected pages. These variants are specified at the
// following two URLs:
//
// http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
// http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
//
// CFISection leaves the production of well-formed 'z'-augmented CIEs and
// FDEs to the user, but does provide EncodedPointer, to emit
// properly-encoded addresses for a given pointer encoding.
// EncodedPointer uses an instance of this structure to find the base
// addresses it should use; you can establish a default for all encoded
// pointers appended to this section with SetEncodedPointerBases.
struct EncodedPointerBases {
EncodedPointerBases() : cfi(), text(), data() { }
// The starting address of this CFI section in memory, for
// DW_EH_PE_pcrel. DW_EH_PE_pcrel pointers may only be used in data
// that has is loaded into the program's address space.
u_int64_t cfi;
// The starting address of this file's .text section, for DW_EH_PE_textrel.
u_int64_t text;
// The starting address of this file's .got or .eh_frame_hdr section,
// for DW_EH_PE_datarel.
u_int64_t data;
};
// Create a CFISection whose endianness is ENDIANNESS, and where
// machine addresses are ADDRESS_SIZE bytes long. If EH_FRAME is
// true, use the .eh_frame format, as described by the Linux
// Standards Base Core Specification, instead of the DWARF CFI
// format.
CFISection(Endianness endianness, size_t address_size,
bool eh_frame = false)
: Section(endianness), address_size_(address_size), eh_frame_(eh_frame),
pointer_encoding_(dwarf2reader::DW_EH_PE_absptr),
encoded_pointer_bases_(), entry_length_(NULL), in_fde_(false) {
// The 'start', 'Here', and 'Mark' members of a CFISection all refer
// to section offsets.
start() = 0;
}
// Return this CFISection's address size.
size_t AddressSize() const { return address_size_; }
// Return true if this CFISection uses the .eh_frame format, or
// false if it contains ordinary DWARF CFI data.
bool ContainsEHFrame() const { return eh_frame_; }
// Use ENCODING for pointers in calls to FDEHeader and EncodedPointer.
void SetPointerEncoding(DwarfPointerEncoding encoding) {
pointer_encoding_ = encoding;
}
// Use the addresses in BASES as the base addresses for encoded
// pointers in subsequent calls to FDEHeader or EncodedPointer.
// This function makes a copy of BASES.
void SetEncodedPointerBases(const EncodedPointerBases &bases) {
encoded_pointer_bases_ = bases;
}
// Append a Common Information Entry header to this section with the
// given values. If dwarf64 is true, use the 64-bit DWARF initial
// length format for the CIE's initial length. Return a reference to
// this section. You should call FinishEntry after writing the last
// instruction for the CIE.
//
// Before calling this function, you will typically want to use Mark
// or Here to make a label to pass to FDEHeader that refers to this
// CIE's position in the section.
CFISection &CIEHeader(u_int64_t code_alignment_factor,
int data_alignment_factor,
unsigned return_address_register,
u_int8_t version = 3,
const string &augmentation = "",
bool dwarf64 = false);
// Append a Frame Description Entry header to this section with the
// given values. If dwarf64 is true, use the 64-bit DWARF initial
// length format for the CIE's initial length. Return a reference to
// this section. You should call FinishEntry after writing the last
// instruction for the CIE.
//
// This function doesn't support entries that are longer than
// 0xffffff00 bytes. (The "initial length" is always a 32-bit
// value.) Nor does it support .debug_frame sections longer than
// 0xffffff00 bytes.
CFISection &FDEHeader(Label cie_pointer,
u_int64_t initial_location,
u_int64_t address_range,
bool dwarf64 = false);
// Note the current position as the end of the last CIE or FDE we
// started, after padding with DW_CFA_nops for alignment. This
// defines the label representing the entry's length, cited in the
// entry's header. Return a reference to this section.
CFISection &FinishEntry();
// Append the contents of BLOCK as a DW_FORM_block value: an
// unsigned LEB128 length, followed by that many bytes of data.
CFISection &Block(const string &block) {
ULEB128(block.size());
Append(block);
return *this;
}
// Append ADDRESS to this section, in the appropriate size and
// endianness. Return a reference to this section.
CFISection &Address(u_int64_t address) {
Section::Append(endianness(), address_size_, address);
return *this;
}
CFISection &Address(Label address) {
Section::Append(endianness(), address_size_, address);
return *this;
}
// Append ADDRESS to this section, using ENCODING and BASES. ENCODING
// defaults to this section's default encoding, established by
// SetPointerEncoding. BASES defaults to this section's bases, set by
// SetEncodedPointerBases. If the DW_EH_PE_indirect bit is set in the
// encoding, assume that ADDRESS is where the true address is stored.
// Return a reference to this section.
//
// (C++ doesn't let me use default arguments here, because I want to
// refer to members of *this in the default argument expression.)
CFISection &EncodedPointer(u_int64_t address) {
return EncodedPointer(address, pointer_encoding_, encoded_pointer_bases_);
}
CFISection &EncodedPointer(u_int64_t address, DwarfPointerEncoding encoding) {
return EncodedPointer(address, encoding, encoded_pointer_bases_);
}
CFISection &EncodedPointer(u_int64_t address, DwarfPointerEncoding encoding,
const EncodedPointerBases &bases);
// Restate some member functions, to keep chaining working nicely.
CFISection &Mark(Label *label) { Section::Mark(label); return *this; }
CFISection &D8(u_int8_t v) { Section::D8(v); return *this; }
CFISection &D16(u_int16_t v) { Section::D16(v); return *this; }
CFISection &D16(Label v) { Section::D16(v); return *this; }
CFISection &D32(u_int32_t v) { Section::D32(v); return *this; }
CFISection &D32(const Label &v) { Section::D32(v); return *this; }
CFISection &D64(u_int64_t v) { Section::D64(v); return *this; }
CFISection &D64(const Label &v) { Section::D64(v); return *this; }
CFISection &LEB128(long long v) { Section::LEB128(v); return *this; }
CFISection &ULEB128(u_int64_t v) { Section::ULEB128(v); return *this; }
private:
// A length value that we've appended to the section, but is not yet
// known. LENGTH is the appended value; START is a label referring
// to the start of the data whose length was cited.
struct PendingLength {
Label length;
Label start;
};
// Constants used in CFI/.eh_frame data:
// If the first four bytes of an "initial length" are this constant, then
// the data uses the 64-bit DWARF format, and the length itself is the
// subsequent eight bytes.
static const u_int32_t kDwarf64InitialLengthMarker = 0xffffffffU;
// The CIE identifier for 32- and 64-bit DWARF CFI and .eh_frame data.
static const u_int32_t kDwarf32CIEIdentifier = ~(u_int32_t)0;
static const u_int64_t kDwarf64CIEIdentifier = ~(u_int64_t)0;
static const u_int32_t kEHFrame32CIEIdentifier = 0;
static const u_int64_t kEHFrame64CIEIdentifier = 0;
// The size of a machine address for the data in this section.
size_t address_size_;
// If true, we are generating a Linux .eh_frame section, instead of
// a standard DWARF .debug_frame section.
bool eh_frame_;
// The encoding to use for FDE pointers.
DwarfPointerEncoding pointer_encoding_;
// The base addresses to use when emitting encoded pointers.
EncodedPointerBases encoded_pointer_bases_;
// The length value for the current entry.
//
// Oddly, this must be dynamically allocated. Labels never get new
// values; they only acquire constraints on the value they already
// have, or assert if you assign them something incompatible. So
// each header needs truly fresh Label objects to cite in their
// headers and track their positions. The alternative is explicit
// destructor invocation and a placement new. Ick.
PendingLength *entry_length_;
// True if we are currently emitting an FDE --- that is, we have
// called FDEHeader but have not yet called FinishEntry.
bool in_fde_;
// If in_fde_ is true, this is its starting address. We use this for
// emitting DW_EH_PE_funcrel pointers.
u_int64_t fde_start_address_;
};
} // namespace google_breakpad
#endif // PROCESSOR_CFI_ASSEMBLER_H_

Some files were not shown because too many files have changed in this diff Show More