Camino only - Bug 488513: Update breakpad to 335, and tweak our integration to account for the changes to the framework.

git-svn-id: svn://10.0.0.236/trunk@257079 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
stuart.morgan%alumni.case.edu
2009-04-29 18:14:57 +00:00
parent d065a53813
commit 9712cea8d4
19 changed files with 684 additions and 310 deletions

View File

@@ -2,12 +2,18 @@
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BreakpadBuildID</key>
<string>%PLATFORM_BUILD_ID%</string>
<key>BreakpadConfirmTimeout</key>
<string>0</string>
<key>BreakpadReportInterval</key>
<string>0</string>
<key>BreakpadRequestComments</key>
<string>YES</string>
<key>BreakpadSendAndExit</key>
<string>NO</string>
<key>BreakpadServerType</key>
<string>socorro</string>
<key>BreakpadURL</key>
<string>https://crash-reports.mozilla.com/submit</string>
<key>BreakpadVendor</key>

View File

@@ -2,6 +2,6 @@ Breakpad is an open-source library for crash reporting. See:
http://code.google.com/p/google-breakpad/
for more information.
This is a pull of r330 from the svn repository at:
This is a pull of r335 from the svn repository at:
http://google-breakpad.googlecode.com/svn/trunk/
with the non-mac platform directories stripped out.

View File

@@ -26,10 +26,10 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
3329D4ED0FA16D820007BBC5 /* Breakpad.nib in Resources */ = {isa = PBXBuildFile; fileRef = 3329D4EC0FA16D820007BBC5 /* Breakpad.nib */; };
33880C800F9E097100817F82 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 33880C7E0F9E097100817F82 /* InfoPlist.strings */; };
4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */; };
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
F9267CB30F844BDD00A827EC /* Breakpad.nib in Resources */ = {isa = PBXBuildFile; fileRef = F9267CB10F844BDD00A827EC /* Breakpad.nib */; };
F9286B3A0F7EB25800A4DCC8 /* InspectorMain.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */; };
F92C53B80ECCE7B3009BE4BA /* Inspector.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53B70ECCE7B3009BE4BA /* Inspector.mm */; };
F92C554C0ECCF534009BE4BA /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
@@ -224,10 +224,10 @@
0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpad_Prefix.pch; path = Framework/Breakpad_Prefix.pch; sourceTree = "<group>"; };
3329D4EC0FA16D820007BBC5 /* Breakpad.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = Breakpad.nib; path = sender/Breakpad.nib; sourceTree = "<group>"; };
33880C7F0F9E097100817F82 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = crash_report_sender.icns; path = sender/crash_report_sender.icns; sourceTree = "<group>"; };
8DC2EF5B0486A6940098B216 /* Breakpad.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Breakpad.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F9267CB20F844BDD00A827EC /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = sender/English.lproj/Breakpad.nib; sourceTree = "<group>"; };
F9286B380F7EB25800A4DCC8 /* Inspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Inspector.h; path = crash_generation/Inspector.h; sourceTree = "<group>"; };
F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = InspectorMain.mm; path = crash_generation/InspectorMain.mm; sourceTree = "<group>"; };
F92C53540ECCE349009BE4BA /* Inspector */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Inspector; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -531,13 +531,13 @@
F92C56A60ECE04B6009BE4BA /* sender */ = {
isa = PBXGroup;
children = (
F945849C0F280E3C009A47BF /* Localizable.strings */,
33880C7E0F9E097100817F82 /* InfoPlist.strings */,
4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */,
F92C56A20ECE04A7009BE4BA /* crash_report_sender-Info.plist */,
F9267CB10F844BDD00A827EC /* Breakpad.nib */,
F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */,
F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */,
F945849C0F280E3C009A47BF /* Localizable.strings */,
33880C7E0F9E097100817F82 /* InfoPlist.strings */,
3329D4EC0FA16D820007BBC5 /* Breakpad.nib */,
4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */,
F92C56A20ECE04A7009BE4BA /* crash_report_sender-Info.plist */,
);
name = sender;
sourceTree = "<group>";
@@ -806,8 +806,8 @@
files = (
F945849E0F280E3C009A47BF /* Localizable.strings in Resources */,
4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */,
F9267CB30F844BDD00A827EC /* Breakpad.nib in Resources */,
33880C800F9E097100817F82 /* InfoPlist.strings in Resources */,
3329D4ED0FA16D820007BBC5 /* Breakpad.nib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1078,14 +1078,6 @@
name = InfoPlist.strings;
sourceTree = "<group>";
};
F9267CB10F844BDD00A827EC /* Breakpad.nib */ = {
isa = PBXVariantGroup;
children = (
F9267CB20F844BDD00A827EC /* English */,
);
name = Breakpad.nib;
sourceTree = "<group>";
};
F945849C0F280E3C009A47BF /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (

View File

@@ -61,26 +61,35 @@ extern "C" {
// Specify some special keys to be used in the configuration file that is
// generated by Breakpad and consumed by the crash_sender.
#define BREAKPAD_PRODUCT_DISPLAY "BreakpadProductDisplay"
#define BREAKPAD_PRODUCT "BreakpadProduct"
#define BREAKPAD_VENDOR "BreakpadVendor"
#define BREAKPAD_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_LOGFILE_KEY_PREFIX "BreakpadAppLogFile"
#define BREAKPAD_EMAIL "BreakpadEmail"
#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments"
#define BREAKPAD_COMMENTS "BreakpadComments"
#define BREAKPAD_REQUEST_EMAIL "BreakpadRequestEmail"
#define BREAKPAD_EMAIL "BreakpadEmail"
#define BREAKPAD_SERVER_TYPE "BreakpadServerType"
// TODO(nealsid) find a better way to support server-specific
// parameters without having to rebuild Breakpad
#define BREAKPAD_BUILD_ID "BreakpadBuildID"
// 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"
// 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.
@@ -91,9 +100,9 @@ typedef bool (*BreakpadFilterCallback)(int exception_type,
int exception_code,
mach_port_t crashing_thread);
// Create a new BreakpadRef object and install it as an
// exception handler. The |parameters| will typically be the contents
// of your bundle's Info.plist.
// 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:
@@ -127,6 +136,12 @@ typedef bool (*BreakpadFilterCallback)(int exception_type,
// 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.
@@ -149,19 +164,35 @@ typedef bool (*BreakpadFilterCallback)(int exception_type,
// should be uploaded at crash time.
//
// BREAKPAD_REQUEST_COMMENTS If true, the message dialog will have a
// text box for the user to enter comments as
// well as a name and email address.
// text box for the user to enter comments.
// Default: NO
//
// The BREAKPAD_PRODUCT, BREAKPAD_VERSION, and BREAKPAD_URL are
// 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_BUILD_ID A string parameter indicating build id.
// Optional.
//=============================================================================
// 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. If the BREAKPAD_PRODUCT or BREAKPAD_VERSION are
// ultimately undefined, BreakpadCreate() will fail. You have been
// warned.
// 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
// 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
@@ -173,6 +204,22 @@ typedef bool (*BreakpadFilterCallback)(int exception_type,
// 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.
//
// 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.
// Returns a new BreakpadRef object on success, NULL otherwise.
BreakpadRef BreakpadCreate(NSDictionary *parameters);

View File

@@ -369,7 +369,6 @@ bool Breakpad::Initialize(NSDictionary *parameters) {
new (gBreakpadAllocator->Allocate(sizeof(google_breakpad::ExceptionHandler)))
google_breakpad::ExceptionHandler(
Breakpad::ExceptionHandlerDirectCallback, this, true);
return true;
}
@@ -389,6 +388,10 @@ Breakpad::~Breakpad() {
//=============================================================================
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];
@@ -398,25 +401,25 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
[parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION];
NSString *reporterPathString =
[parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION];
NSString *skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM];
NSString *sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT];
NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT];
NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES];
NSString *logFileTailSize = [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE];
NSString *reportEmail = [parameters objectForKey:@BREAKPAD_EMAIL];
NSString *requestUserText =
[parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS];
NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL];
NSString *vendor =
[parameters objectForKey:@BREAKPAD_VENDOR];
NSString *dumpSubdirectory =
[parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
// If these two are not already set(skipConfirm and sendAndExit can
// come from user defaults and take priority)
NSString *buildId =
[parameters objectForKey:@BREAKPAD_BUILD_ID];
// These may have been set above as user prefs, which take priority.
if (!skipConfirm) {
skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM];
skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM];
}
if (!sendAndExit) {
sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT];
sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT];
}
if (!product)
@@ -431,6 +434,9 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
if (!interval)
interval = @"3600";
if (!timeout)
timeout = @"300";
if (!logFileTailSize)
logFileTailSize = @"200000";
@@ -528,12 +534,14 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
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,
@@ -542,16 +550,27 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
[logFileTailSize UTF8String]);
dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS,
[requestUserText UTF8String]);
dictionary.SetKeyValue(BREAKPAD_VENDOR,
[vendor UTF8String]);
dictionary.SetKeyValue(BREAKPAD_REQUEST_EMAIL, [requestEmail UTF8String]);
dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]);
dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
[dumpSubdirectory UTF8String]);
dictionary.SetKeyValue(BREAKPAD_BUILD_ID,
[buildId UTF8String]);
struct timeval tv;
gettimeofday(&tv, NULL);
char timeStartedString[32];
sprintf(timeStartedString, "%d", 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]);
dictionary.SetKeyValue(logFileKey,
[[logFilePaths objectAtIndex:i]
fileSystemRepresentation]);
}
}

View File

@@ -88,7 +88,7 @@ kern_return_t OnDemandServer::Initialize(const char *server_command,
&service_port_);
if (kr != KERN_SUCCESS) {
//PRINT_MACH_RESULT(kr, "bootstrap_create_service() : ");
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_);

View File

@@ -171,6 +171,8 @@ class Inspector {
kern_return_t SendAcknowledgement();
void LaunchReporter(const char *inConfigFilePath);
void SetCrashTimeParameters();
mach_port_t service_rcv_port_;
int exception_type_;

View File

@@ -160,8 +160,15 @@ void ConfigFile::WriteFile(const SimpleStringDictionary *configurationParameters
sizeof(config_file_path_));
config_file_ = mkstemp(config_file_path_);
if (config_file_ == -1)
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;
@@ -177,6 +184,10 @@ void ConfigFile::WriteFile(const SimpleStringDictionary *configurationParameters
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)
@@ -321,15 +332,41 @@ kern_return_t Inspector::ReadMessages() {
}
//=============================================================================
// 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, "%d", processUptime);
config_params_.SetKeyValue(BREAKPAD_PROCESS_UP_TIME, processUptimeString);
}
sprintf(processCrashtimeString, "%d", 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, "Suspsended 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 (0 == strlen(minidumpDirectory)) {
@@ -350,6 +387,9 @@ bool Inspector::InspectTask() {
minidumpDir = [[NSString stringWithUTF8String:minidumpDirectory]
stringByExpandingTildeInPath];
}
DEBUGLOG(stderr,
"Writing minidump to directory (%s)\n",
[minidumpDir UTF8String]);
MinidumpLocation minidumpLocation(minidumpDir);
@@ -368,14 +408,18 @@ bool Inspector::InspectTask() {
NSString *minidumpPath = [NSString stringWithFormat:@"%s/%s.dmp",
minidumpLocation.GetPath(), minidumpLocation.GetID()];
DEBUGLOG(stderr,
"minidump path (%s)\n",
[minidumpPath UTF8String]);
bool result = generator.Write([minidumpPath fileSystemRepresentation]);
DEBUGLOG(stderr, "Inspector: finished writing minidump file: %s\n",
[minidumpPath fileSystemRepresentation]);
DEBUGLOG(stderr, "Wrote minidump - %s\n", result ? "OK" : "FAILED");
// let the task continue
task_resume(remote_task_);
DEBUGLOG(stderr, "Resumed remote task\n");
return result;
}

View File

@@ -28,15 +28,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/*
g++ -framework CoreFoundation -I../../.. \
../../minidump_file_writer.cc \
../../../common/convert_UTF.c \
../../../common/string_conversion.cc \
../../../common/mac/string_utilities.cc \
exception_handler.cc \
minidump_generator.cc \
exception_handler_test.cc \
-o exception_handler_test
g++ -framework CoreFoundation -I../../.. ../../minidump_file_writer.cc ../../../common/convert_UTF.c ../../../common/string_conversion.cc ../../../common/mac/string_utilities.cc exception_handler.cc minidump_generator.cc exception_handler_test.cc -o exception_handler_test -mmacosx-version-min=10.4 ../../../common/mac/file_id.cc dynamic_images.cc ../../../common/mac/macho_id.cc ../../../common/mac/macho_walker.cc -lcrypto ../../../common/mac/macho_utilities.cc
*/
#include <pthread.h>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBClasses</key>
<array>
<dict>
<key>ACTIONS</key>
<dict>
<key>cancel</key>
<string>id</string>
<key>sendReport</key>
<string>id</string>
<key>showPrivacyPolicy</key>
<string>id</string>
</dict>
<key>CLASS</key>
<string>Reporter</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>OUTLETS</key>
<dict>
<key>alertWindow_</key>
<string>NSWindow</string>
<key>cancelButton_</key>
<string>NSButton</string>
<key>commentMessage_</key>
<string>NSTextField</string>
<key>dialogTitle_</key>
<string>NSTextField</string>
<key>emailEntryField_</key>
<string>NSView</string>
<key>emailLabel_</key>
<string>NSTextField</string>
<key>emailMessage_</key>
<string>NSTextField</string>
<key>emailSectionBox_</key>
<string>NSBox</string>
<key>headerBox_</key>
<string>NSBox</string>
<key>preEmailBox_</key>
<string>NSBox</string>
<key>privacyLinkArrow_</key>
<string>NSView</string>
<key>privacyLinkLabel_</key>
<string>NSTextField</string>
<key>sendButton_</key>
<string>NSButton</string>
</dict>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
</array>
<key>IBVersion</key>
<string>1</string>
</dict>
</plist>

View File

@@ -3,9 +3,9 @@
<plist version="1.0">
<dict>
<key>IBFramework Version</key>
<string>670</string>
<string>672</string>
<key>IBLastKnownRelativeProjectPath</key>
<string>../../Breakpad.xcodeproj</string>
<string>../Breakpad.xcodeproj</string>
<key>IBOldestOS</key>
<integer>5</integer>
<key>IBOpenObjects</key>
@@ -13,7 +13,7 @@
<integer>2</integer>
</array>
<key>IBSystem Version</key>
<string>9F33</string>
<string>9G55</string>
<key>targetFramework</key>
<string>IBCocoaFramework</string>
</dict>

View File

@@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBClasses</key>
<array>
<dict>
<key>ACTIONS</key>
<dict>
<key>cancel</key>
<string>id</string>
<key>sendReport</key>
<string>id</string>
<key>showPrivacyPolicy</key>
<string>id</string>
</dict>
<key>CLASS</key>
<string>Reporter</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>OUTLETS</key>
<dict>
<key>alertWindow</key>
<string>NSWindow</string>
</dict>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
</array>
<key>IBVersion</key>
<string>1</string>
</dict>
</plist>

View File

@@ -38,22 +38,46 @@
#define kClientIdPreferenceKey @"clientid"
extern NSString *const kGoogleServerType;
extern NSString *const kSocorroServerType;
extern NSString *const kDefaultServerType;
@interface Reporter : NSObject {
@public
IBOutlet NSWindow *alertWindow; // The alert window
IBOutlet NSWindow *alertWindow_; // The alert window
// Values bound in the XIB
NSString *headerMessage_; // Message notifying of the crash
NSString *reportMessage_; // Message explaining the crash report
NSString *commentsValue_; // Comments from the user
NSString *emailMessage_; // Message requesting user email
NSString *emailValue_; // Email from the user
// 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 NSView *emailEntryField_;
IBOutlet NSView *privacyLinkArrow_;
// Text field bindings, for user input.
NSString *commentsValue_; // Comments from the user
NSString *emailValue_; // Email from the user
@private
int configFile_; // File descriptor for config file
NSMutableDictionary *parameters_; // Key value pairs of data (STRONG)
NSData *minidumpContents_; // The data in the minidump (STRONG)
NSData *logFileData_; // An NSdata for the tar, bz2'd log file
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
NSMutableDictionary *serverDictionary_; // The dictionary mapping a
// server type name to a
// dictionary of URL
// parameter names
NSMutableDictionary *socorroDictionary_; // The dictionary for
// Socorro
NSMutableDictionary *googleDictionary_; // The dictionary for
// Google
}
// Stops the modal panel with an NSAlertDefaultReturn value. This is the action
@@ -62,28 +86,26 @@
// Stops the modal panel with an NSAlertAlternateReturn value. This is the
// action invoked by the "Cancel" button.
- (IBAction)cancel:(id)sender;
// Opens the Google Privacy Policy in the default web browser.
// 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;
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView
doCommandBySelector:(SEL)commandSelector;
// Helper method to set HTTP parameters based on server type
- (BOOL)setPostParametersFromDictionary:(NSMutableDictionary *)crashParameters;
// Accessors to make bindings work
- (NSString *)headerMessage;
- (void)setHeaderMessage:(NSString *)value;
- (NSString *)reportMessage;
- (void)setReportMessage:(NSString *)value;
- (NSString *)commentsValue;
- (void)setCommentsValue:(NSString *)value;
- (NSString *)emailMessage;
- (void)setEmailMessage:(NSString *)value;
- (NSString *)emailValue;
- (void)setEmailValue:(NSString *)value;
// Initialization helper to create dictionaries mapping Breakpad
// parameters to URL parameters
- (void)createServerParameterDictionaries;
@end

View File

@@ -39,11 +39,122 @@
#import "crash_report_sender.h"
#import "common/mac/GTMLogger.h"
#define kLastSubmission @"LastSubmission"
const int kMinidumpFileLengthLimit = 800000;
#define kApplePrefsSyncExcludeAllKey @"com.apple.PreferenceSync.ExcludeAllSyncKeys"
NSString *const kGoogleServerType = @"google";
NSString *const kSocorroServerType = @"socorro";
NSString *const kDefaultServerType = @"google";
#pragma mark -
@interface NSView (ResizabilityExtentions)
// Shifts the view vertically by the given amount.
- (void)breakpad_shiftVertically:(float)offset;
// Shifts the view horizontally by the given amount.
- (void)breakpad_shiftHorizontally:(float)offset;
@end
@implementation NSView (ResizabilityExtentions)
- (void)breakpad_shiftVertically:(float)offset {
NSPoint origin = [self frame].origin;
origin.y += offset;
[self setFrameOrigin:origin];
}
- (void)breakpad_shiftHorizontally:(float)offset {
NSPoint origin = [self frame].origin;
origin.x += offset;
[self setFrameOrigin:origin];
}
@end
@interface NSWindow (ResizabilityExtentions)
// Adjusts the window height by heightDelta relative to its current height,
// keeping all the content at the same size.
- (void)breakpad_adjustHeight:(float)heightDelta;
@end
@implementation NSWindow (ResizabilityExtentions)
- (void)breakpad_adjustHeight:(float)heightDelta {
[[self contentView] setAutoresizesSubviews:NO];
NSRect windowFrame = [self frame];
windowFrame.size.height += heightDelta;
[self setFrame:windowFrame display:YES];
// For some reason the content view is resizing, but not adjusting its origin,
// so correct it manually.
[[self contentView] setFrameOrigin:NSMakePoint(0, 0)];
[[self contentView] setAutoresizesSubviews:YES];
}
@end
@interface NSTextField (ResizabilityExtentions)
// Grows or shrinks the height of the field to the minimum required to show the
// current text, preserving the existing width and origin.
// Returns the change in height.
- (float)breakpad_adjustHeightToFit;
// Grows or shrinks the width of the field to the minimum required to show the
// current text, preserving the existing height and origin.
// Returns the change in width.
- (float)breakpad_adjustWidthToFit;
@end
@implementation NSTextField (ResizabilityExtentions)
- (float)breakpad_adjustHeightToFit {
NSRect oldFrame = [self frame];
// sizeToFit will blow out the width rather than making the field taller, so
// we do it manually.
NSSize newSize = [[self cell] cellSizeForBounds:oldFrame];
NSRect newFrame = NSMakeRect(oldFrame.origin.x, oldFrame.origin.y,
NSWidth(oldFrame), newSize.height);
[self setFrame:newFrame];
return newSize.height - NSHeight(oldFrame);
}
- (float)breakpad_adjustWidthToFit {
NSRect oldFrame = [self frame];
[self sizeToFit];
return NSWidth([self frame]) - NSWidth(oldFrame);
}
@end
@interface NSButton (ResizabilityExtentions)
// Resizes to fit the label using IB-style size-to-fit metrics and enforcing a
// minimum width of 70, while preserving the right edge location.
// Returns the change in width.
- (float)breakpad_smartSizeToFit;
@end
@implementation NSButton (ResizabilityExtentions)
- (float)breakpad_smartSizeToFit {
NSRect oldFrame = [self frame];
[self sizeToFit];
NSRect newFrame = [self frame];
// sizeToFit gives much worse results that IB's Size to Fit option. This is
// the amount of padding IB adds over a sizeToFit, empirically determined.
const float kExtraPaddingAmount = 12;
const float kMinButtonWidth = 70; // The default button size in IB.
newFrame.size.width = NSWidth(newFrame) + kExtraPaddingAmount;
if (NSWidth(newFrame) < kMinButtonWidth)
newFrame.size.width = kMinButtonWidth;
// Preserve the right edge location.
newFrame.origin.x = NSMaxX(oldFrame) - NSWidth(newFrame);
[self setFrame:newFrame];
return NSWidth(newFrame) - NSWidth(oldFrame);
}
@end
#pragma mark -
@interface Reporter(PrivateMethods)
+ (uid_t)consoleUID;
@@ -56,15 +167,57 @@ const int kMinidumpFileLengthLimit = 800000;
- (BOOL)readMinidumpData;
- (BOOL)readLogFileData;
- (BOOL)askUserPermissionToSend:(BOOL)shouldSubmitReport;
- (BOOL)shouldSubmitReport;
// Returns YES if it has been long enough since the last report that we should
// submit a report for this crash.
- (BOOL)reportIntervalElapsed;
// Run an alert window with the given timeout. Returns NSAlertButtonDefault if
// the timeout is exceeded. A timeout of 0 queues the message immediately in the
// modal run loop.
// Returns YES if we should send the report without asking the user first.
- (BOOL)shouldSubmitSilently;
// Returns YES if we should ask the user to provide comments.
- (BOOL)shouldRequestComments;
// Returns YES if we should ask the user to provide an email address.
- (BOOL)shouldRequestEmail;
// Shows UI to the user to ask for permission to send and any extra information
// we've been instructed to request. Returns YES if the user allows the report
// to be sent.
- (BOOL)askUserPermissionToSend;
// Returns the short description of the crash, suitable for use as a dialog
// title (e.g., "The application Foo has quit unexpectedly").
- (NSString*)shortCrashDialogMessage;
// Return explanatory text about the crash and the reporter, suitable for the
// body text of a dialog.
- (NSString*)explanatoryCrashDialogText;
// Returns the amount of time the UI should be shown before timing out.
- (NSTimeInterval)messageTimeout;
// Preps the comment-prompting alert window for display:
// * localizes all the elements
// * resizes and adjusts layout as necessary for localization
// * removes the email section if includeEmail is NO
- (void)configureAlertWindowIncludingEmail:(BOOL)includeEmail;
// Rmevoes the email section of the dialog, adjusting the rest of the window
// as necessary.
- (void)removeEmailPrompt;
// Run an alert window with the given timeout. Returns
// NSAlertButtonDefault if the timeout is exceeded. A timeout of 0
// queues the message immediately in the modal run loop.
- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout;
// Returns a unique client id (user-specific), creating a persistent
// one in the user defaults, if necessary.
- (NSString*)clientID;
// Returns a dictionary that can be used to map Breakpad parameter names to
// URL parameter names.
- (NSDictionary *)dictionaryForServerType:(NSString *)serverType;
@end
@implementation Reporter
@@ -102,6 +255,8 @@ const int kMinidumpFileLengthLimit = 800000;
[ud setBool:YES forKey:kApplePrefsSyncExcludeAllKey];
}
[self createServerParameterDictionaries];
return self;
}
@@ -328,106 +483,39 @@ const int kMinidumpFileLengthLimit = 800000;
}
//=============================================================================
- (BOOL)askUserPermissionToSend:(BOOL)shouldSubmitReport {
// Send without confirmation
if ([[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM] isEqualToString:@"YES"]) {
GTMLoggerDebug(@"Skipping confirmation and sending report");
return YES;
}
// Determine if we should create a text box for user feedback
BOOL shouldRequestComments =
[[parameters_ objectForKey:@BREAKPAD_REQUEST_COMMENTS]
isEqual:@"YES"];
NSString *display = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
if (![display length])
display = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
NSString *vendor = [parameters_ objectForKey:@BREAKPAD_VENDOR];
if (![vendor length])
vendor = @"Vendor not specified";
NSBundle *bundle = [NSBundle mainBundle];
[self setHeaderMessage:[NSString stringWithFormat:
NSLocalizedStringFromTableInBundle(@"headerFmt", nil,
bundle,
@""), display]];
NSString *defaultButtonTitle = nil;
NSString *otherButtonTitle = nil;
NSTimeInterval timeout = 60.0; // timeout value for the user notification
// Get the localized alert strings
// If we're going to submit a report, prompt the user if this is okay.
// Otherwise, just let them know that the app crashed.
if (shouldSubmitReport) {
NSString *msgFormat = NSLocalizedStringFromTableInBundle(@"msg",
nil,
bundle, @"");
[self setReportMessage:[NSString stringWithFormat:msgFormat, vendor]];
defaultButtonTitle = NSLocalizedStringFromTableInBundle(@"sendReportButton",
nil, bundle, @"");
otherButtonTitle = NSLocalizedStringFromTableInBundle(@"cancelButton", nil,
bundle, @"");
// Nominally use the report interval
timeout = [[parameters_ objectForKey:@BREAKPAD_REPORT_INTERVAL]
floatValue];
} else {
[self setReportMessage:NSLocalizedStringFromTableInBundle(@"noSendMsg", nil,
bundle, @"")];
defaultButtonTitle = NSLocalizedStringFromTableInBundle(@"noSendButton",
nil, bundle, @"");
timeout = 60.0;
}
// show the notification for at least one minute
if (timeout < 60.0) {
timeout = 60.0;
}
- (BOOL)askUserPermissionToSend {
// Initialize Cocoa, needed to display the alert
NSApplicationLoad();
// Get the timeout value for the notification.
NSTimeInterval timeout = [self messageTimeout];
int buttonPressed = NSAlertAlternateReturn;
if (shouldRequestComments) {
// Determine whether we should create a text box for user feedback.
if ([self shouldRequestComments]) {
BOOL didLoadNib = [NSBundle loadNibNamed:@"Breakpad" owner:self];
if (didLoadNib) {
// Append the request for comments to the |reportMessage| string.
NSString *commentsMessage =
NSLocalizedStringFromTableInBundle(@"commentsMsg", nil, bundle, @"");
[self setReportMessage:[NSString stringWithFormat:@"%@\n\n%@",
[self reportMessage],
commentsMessage]];
if (!didLoadNib) {
return NO;
}
// Add the request for email address.
[self setEmailMessage:
NSLocalizedStringFromTableInBundle(@"emailMsg", nil, bundle, @"")];
[self configureAlertWindowIncludingEmail:[self shouldRequestEmail]];
// Run the alert
buttonPressed = [self runModalWindow:alertWindow withTimeout:timeout];
buttonPressed = [self runModalWindow:alertWindow_ withTimeout:timeout];
// Extract info from the user into the parameters_ dictionary
if ([self commentsValue]) {
[parameters_ setObject:[self commentsValue]
forKey:@BREAKPAD_COMMENTS];
}
if ([self emailValue]) {
[parameters_ setObject:[self emailValue]
forKey:@BREAKPAD_EMAIL];
}
// Extract info from the user into the parameters_ dictionary
if ([self commentsValue]) {
[parameters_ setObject:[self commentsValue] forKey:@BREAKPAD_COMMENTS];
}
if ([self emailValue]) {
[parameters_ setObject:[self emailValue] forKey:@BREAKPAD_EMAIL];
}
} else {
// Create an alert panel to tell the user something happened
NSPanel* alert = NSGetAlertPanel([self headerMessage],
[self reportMessage],
defaultButtonTitle,
otherButtonTitle, nil);
NSPanel* alert = NSGetAlertPanel([self shortCrashDialogMessage],
[self explanatoryCrashDialogText],
NSLocalizedString(@"sendReportButton", @""),
NSLocalizedString(@"cancelButton", @""),
nil);
// Pop the alert with an automatic timeout, and wait for the response
buttonPressed = [self runModalWindow:alert withTimeout:timeout];
@@ -438,11 +526,65 @@ const int kMinidumpFileLengthLimit = 800000;
return buttonPressed == NSAlertDefaultReturn;
}
- (void)configureAlertWindowIncludingEmail:(BOOL)includeEmail {
// Swap in localized values, making size adjustments to impacted elements as
// we go. Remember that the origin is in the bottom left, so elements above
// "fall" as text areas are shrunk from their overly-large IB sizes.
// Localize the header. No resizing needed, as it has plenty of room.
[dialogTitle_ setStringValue:[self shortCrashDialogMessage]];
// Localize the explanatory text field.
[commentMessage_ setStringValue:[NSString stringWithFormat:@"%@\n\n%@",
[self explanatoryCrashDialogText],
NSLocalizedString(@"commentsMsg", @"")]];
float commentHeightDelta = [commentMessage_ breakpad_adjustHeightToFit];
[headerBox_ breakpad_shiftVertically:commentHeightDelta];
[alertWindow_ breakpad_adjustHeight:commentHeightDelta];
// Either localize the email explanation field or remove the whole email
// section depending on whether or not we are asking for email.
if (includeEmail) {
[emailMessage_ setStringValue:NSLocalizedString(@"emailMsg", @"")];
float emailHeightDelta = [emailMessage_ breakpad_adjustHeightToFit];
[preEmailBox_ breakpad_shiftVertically:emailHeightDelta];
[alertWindow_ breakpad_adjustHeight:emailHeightDelta];
} else {
[self removeEmailPrompt]; // Handles necessary resizing.
}
// Localize the email label, and shift the associated text field.
[emailLabel_ setStringValue:NSLocalizedString(@"emailLabel", @"")];
float emailLabelWidthDelta = [emailLabel_ breakpad_adjustWidthToFit];
[emailEntryField_ breakpad_shiftHorizontally:emailLabelWidthDelta];
// Localize the privacy policy label, and keep it right-aligned to the arrow.
[privacyLinkLabel_ setStringValue:NSLocalizedString(@"privacyLabel", @"")];
float privacyLabelWidthDelta = [privacyLinkLabel_ breakpad_adjustWidthToFit];
[privacyLinkLabel_ breakpad_shiftHorizontally:(-privacyLabelWidthDelta)];
// Localize the buttons, and keep the cancel button at the right distance.
[sendButton_ setTitle:NSLocalizedString(@"sendReportButton", @"")];
float sendButtonWidthDelta = [sendButton_ breakpad_smartSizeToFit];
[cancelButton_ breakpad_shiftHorizontally:(-sendButtonWidthDelta)];
[cancelButton_ setTitle:NSLocalizedString(@"cancelButton", @"")];
[cancelButton_ breakpad_smartSizeToFit];
}
- (void)removeEmailPrompt {
[emailSectionBox_ setHidden:YES];
float emailSectionHeight = NSHeight([emailSectionBox_ frame]);
[preEmailBox_ breakpad_shiftVertically:(-emailSectionHeight)];
[alertWindow_ breakpad_adjustHeight:(-emailSectionHeight)];
}
- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout {
// Queue a |stopModal| message to be performed in |timeout| seconds.
[NSApp performSelector:@selector(stopModal)
withObject:nil
afterDelay:timeout];
if (timeout > 0.001) {
[NSApp performSelector:@selector(stopModal)
withObject:nil
afterDelay:timeout];
}
// Run the window modally and wait for either a |stopModal| message or a
// button click.
@@ -459,7 +601,11 @@ const int kMinidumpFileLengthLimit = 800000;
}
- (IBAction)sendReport:(id)sender {
[alertWindow orderOut:self];
// Force the text fields to end editing so text for the currently focused
// field will be commited.
[alertWindow_ makeFirstResponder:alertWindow_];
[alertWindow_ orderOut:self];
// Use NSAlertDefaultReturn so that the return value of |runModalWithWindow|
// matches the AppKit function NSRunAlertPanel()
[NSApp stopModalWithCode:NSAlertDefaultReturn];
@@ -468,7 +614,7 @@ const int kMinidumpFileLengthLimit = 800000;
// UI Button Actions
//=============================================================================
- (IBAction)cancel:(id)sender {
[alertWindow orderOut:self];
[alertWindow_ orderOut:self];
// Use NSAlertDefaultReturn so that the return value of |runModalWithWindow|
// matches the AppKit function NSRunAlertPanel()
[NSApp stopModalWithCode:NSAlertAlternateReturn];
@@ -477,15 +623,15 @@ const int kMinidumpFileLengthLimit = 800000;
- (IBAction)showPrivacyPolicy:(id)sender {
// Get the localized privacy policy URL and open it in the default browser.
NSURL* privacyPolicyURL =
[NSURL URLWithString:NSLocalizedStringFromTableInBundle(
@"privacyPolicyURL", nil, [NSBundle mainBundle], @"")];
[NSURL URLWithString:NSLocalizedString(@"privacyPolicyURL", @"")];
[[NSWorkspace sharedWorkspace] openURL:privacyPolicyURL];
}
// Text Field Delegate Methods
//=============================================================================
- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView
doCommandBySelector:(SEL)commandSelector {
- (BOOL) control:(NSControl*)control
textView:(NSTextView*)textView
doCommandBySelector:(SEL)commandSelector {
BOOL result = NO;
// If the user has entered text, don't end editing on "return"
if (commandSelector == @selector(insertNewline:)
@@ -496,29 +642,9 @@ const int kMinidumpFileLengthLimit = 800000;
return result;
}
// Accessors
#pragma mark Accessors
#pragma mark -
//=============================================================================
- (NSString *)headerMessage {
return [[headerMessage_ retain] autorelease];
}
- (void)setHeaderMessage:(NSString *)value {
if (headerMessage_ != value) {
[headerMessage_ autorelease];
headerMessage_ = [value copy];
}
}
- (NSString *)reportMessage {
return [[reportMessage_ retain] autorelease];
}
- (void)setReportMessage:(NSString *)value {
if (reportMessage_ != value) {
[reportMessage_ autorelease];
reportMessage_ = [value copy];
}
}
- (NSString *)commentsValue {
return [[commentsValue_ retain] autorelease];
@@ -526,35 +652,25 @@ const int kMinidumpFileLengthLimit = 800000;
- (void)setCommentsValue:(NSString *)value {
if (commentsValue_ != value) {
[commentsValue_ autorelease];
[commentsValue_ release];
commentsValue_ = [value copy];
}
}
- (NSString *)emailMessage {
return [[emailMessage_ retain] autorelease];
}
- (void)setEmailMessage:(NSString *)value {
if (emailMessage_ != value) {
[emailMessage_ autorelease];
emailMessage_ = [value copy];
}
}
- (NSString *)emailValue {
return [[emailValue_ retain] autorelease];
}
- (void)setEmailValue:(NSString *)value {
if (emailValue_ != value) {
[emailValue_ autorelease];
[emailValue_ release];
emailValue_ = [value copy];
}
}
#pragma mark -
//=============================================================================
- (BOOL)shouldSubmitReport {
- (BOOL)reportIntervalElapsed {
float interval = [[parameters_ objectForKey:@BREAKPAD_REPORT_INTERVAL]
floatValue];
NSString *program = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
@@ -580,51 +696,127 @@ const int kMinidumpFileLengthLimit = 800000;
return YES;
}
- (BOOL)shouldSubmitSilently {
return [[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM]
isEqualToString:@"YES"];
}
- (BOOL)shouldRequestComments {
return [[parameters_ objectForKey:@BREAKPAD_REQUEST_COMMENTS]
isEqualToString:@"YES"];
}
- (BOOL)shouldRequestEmail {
return [[parameters_ objectForKey:@BREAKPAD_REQUEST_EMAIL]
isEqualToString:@"YES"];
}
- (NSString*)shortCrashDialogMessage {
NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
if (![displayName length])
displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
return [NSString stringWithFormat:NSLocalizedString(@"headerFmt", @""),
displayName];
}
- (NSString*)explanatoryCrashDialogText {
NSString *vendor = [parameters_ objectForKey:@BREAKPAD_VENDOR];
if (![vendor length])
vendor = @"unknown vendor";
return [NSString stringWithFormat:NSLocalizedString(@"msgFmt", @""), vendor];
}
- (NSTimeInterval)messageTimeout {
// Get the timeout value for the notification.
NSTimeInterval timeout = [[parameters_ objectForKey:@BREAKPAD_CONFIRM_TIMEOUT]
floatValue];
// Require a timeout of at least a minute (except 0, which means no timeout).
if (timeout > 0.001 && timeout < 60.0) {
timeout = 60.0;
}
return timeout;
}
- (void)createServerParameterDictionaries {
serverDictionary_ = [[NSMutableDictionary alloc] init];
socorroDictionary_ = [[NSMutableDictionary alloc] init];
googleDictionary_ = [[NSMutableDictionary alloc] init];
[serverDictionary_ setObject:socorroDictionary_ forKey:kSocorroServerType];
[serverDictionary_ setObject:googleDictionary_ forKey:kGoogleServerType];
[googleDictionary_ setObject:@"ptime" forKey:@BREAKPAD_PROCESS_UP_TIME];
[googleDictionary_ setObject:@"email" forKey:@BREAKPAD_EMAIL];
[googleDictionary_ setObject:@"comments" forKey:@BREAKPAD_COMMENTS];
[googleDictionary_ setObject:@"prod" forKey:@BREAKPAD_PRODUCT];
[googleDictionary_ setObject:@"ver" forKey:@BREAKPAD_VERSION];
// TODO: just for testing, google's server doesn't support it
[googleDictionary_ setObject:@"buildid" forKey:@BREAKPAD_BUILD_ID];
[socorroDictionary_ setObject:@"Comments" forKey:@BREAKPAD_COMMENTS];
[socorroDictionary_ setObject:@"CrashTime"
forKey:@BREAKPAD_PROCESS_CRASH_TIME];
[socorroDictionary_ setObject:@"StartupTime"
forKey:@BREAKPAD_PROCESS_START_TIME];
[socorroDictionary_ setObject:@"Version"
forKey:@BREAKPAD_VERSION];
[socorroDictionary_ setObject:@"ProductName"
forKey:@BREAKPAD_PRODUCT];
[socorroDictionary_ setObject:@"ProductName"
forKey:@BREAKPAD_PRODUCT];
[socorroDictionary_ setObject:@"BuildID"
forKey:@BREAKPAD_BUILD_ID];
}
- (NSDictionary *)dictionaryForServerType:(NSString *)serverType {
if (serverType == nil || [serverType length] == 0) {
return [serverDictionary_ objectForKey:kDefaultServerType];
}
return [serverDictionary_ objectForKey:serverType];
}
// Helper method to set HTTP parameters based on server type
- (BOOL)setPostParametersFromDictionary:(NSMutableDictionary *)crashParameters {
NSString *serverType = [parameters_ objectForKey:@BREAKPAD_SERVER_TYPE];
NSDictionary *urlParameterNames = [self dictionaryForServerType:serverType];
id key;
NSEnumerator *enumerator = [parameters_ keyEnumerator];
while ((key = [enumerator nextObject])) {
// The key from parameters_ corresponds to a key in
// urlParameterNames. The value in parameters_ gets stored in
// crashParameters with a key that is the value in
// urlParameterNames.
// For instance, if parameters_ has [PRODUCT_NAME => "FOOBAR"] and
// urlParameterNames has [PRODUCT_NAME => "pname"] the final HTTP
// URL parameter becomes [pname => "FOOBAR"].
NSString *breakpadParameterName = (NSString *)key;
NSString *urlParameter = [urlParameterNames
objectForKey:breakpadParameterName];
if (urlParameter) {
[crashParameters setObject:[parameters_ objectForKey:key]
forKey:urlParameter];
}
}
return YES;
}
//=============================================================================
- (void)report {
NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]];
HTTPMultipartUpload *upload = [[HTTPMultipartUpload alloc] initWithURL:url];
NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary];
// Set the known parameters. This should be kept up to date with the
// parameters defined in the Breakpad.h list of parameters. The intent
// is so that if there's a parameter that's not in this list, we consider
// it to be a "user-defined" parameter and we'll upload it to the server.
NSSet *knownParameters =
[NSSet setWithObjects:@kReporterMinidumpDirectoryKey,
@kReporterMinidumpIDKey, @BREAKPAD_PRODUCT_DISPLAY,
@BREAKPAD_PRODUCT, @BREAKPAD_VERSION, @BREAKPAD_URL,
@BREAKPAD_REPORT_INTERVAL, @BREAKPAD_SKIP_CONFIRM,
@BREAKPAD_SEND_AND_EXIT, @BREAKPAD_REPORTER_EXE_LOCATION,
@BREAKPAD_INSPECTOR_LOCATION, @BREAKPAD_LOGFILES,
@BREAKPAD_LOGFILE_UPLOAD_SIZE, @BREAKPAD_EMAIL,
@BREAKPAD_REQUEST_COMMENTS, @BREAKPAD_COMMENTS,
@BREAKPAD_VENDOR, nil];
// Add parameters
[uploadParameters setObject:[parameters_ objectForKey:@BREAKPAD_PRODUCT]
forKey:@"prod"];
[uploadParameters setObject:[parameters_ objectForKey:@BREAKPAD_VERSION]
forKey:@"ver"];
if ([parameters_ objectForKey:@BREAKPAD_EMAIL]) {
[uploadParameters setObject:[parameters_ objectForKey:@BREAKPAD_EMAIL] forKey:@"email"];
if (![self setPostParametersFromDictionary:uploadParameters]) {
return;
}
NSString* comments = [parameters_ objectForKey:@BREAKPAD_COMMENTS];
if (comments != nil) {
[uploadParameters setObject:comments forKey:@"comments"];
}
// Add any user parameters
NSArray *parameterKeys = [parameters_ allKeys];
int keyCount = [parameterKeys count];
for (int i = 0; i < keyCount; ++i) {
NSString *key = [parameterKeys objectAtIndex:i];
if (![knownParameters containsObject:key] &&
![key hasPrefix:@BREAKPAD_LOGFILE_KEY_PREFIX])
[uploadParameters setObject:[parameters_ objectForKey:key] forKey:key];
}
[upload setParameters:uploadParameters];
// Add minidump file
if (minidumpContents_) {
[upload addFileContents:minidumpContents_ name:@"upload_file_minidump"];
@@ -657,13 +849,13 @@ const int kMinidumpFileLengthLimit = 800000;
const char *dest = [destString fileSystemRepresentation];
if (rename(src, dest) == 0) {
fprintf(stderr, "Breakpad Reporter: Renamed %s to %s after successful " \
"upload\n",src, dest);
GTMLoggerInfo(@"Breakpad Reporter: Renamed %s to %s after successful " \
"upload",src, dest);
}
else {
// can't rename - don't worry - it's not important for users
fprintf(stderr, "Breakpad Reporter: successful upload report ID = %s\n",
reportID );
GTMLoggerDebug(@"Breakpad Reporter: successful upload report ID = %s\n",
reportID );
}
[result release];
}
@@ -691,13 +883,22 @@ const int kMinidumpFileLengthLimit = 800000;
[parameters_ release];
[minidumpContents_ release];
[logFileData_ release];
[googleDictionary_ release];
[socorroDictionary_ release];
[serverDictionary_ release];
[super dealloc];
}
@end
//=============================================================================
int main(int argc, const char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
#if DEBUG
// Log to stderr in debug builds.
[GTMLogger setSharedLogger:[GTMLogger standardLoggerWithStderr]];
#endif
GTMLoggerDebug(@"Reporter Launched, argc=%d", argc);
// The expectation is that there will be one argument which is the path
// to the configuration file
if (argc != 2) {
@@ -707,11 +908,20 @@ int main(int argc, const char *argv[]) {
// Open the file before (potentially) switching to console user
int configFile = open(argv[1], O_RDONLY, 0600);
if (configFile == -1) {
GTMLoggerDebug(@"Couldn't open config file %s - %s",
argv[1],
strerror(errno));
}
// we want to avoid a build-up of old config files even if they
// have been incorrectly written by the framework
unlink(argv[1]);
if (configFile == -1) {
GTMLoggerDebug(@"Couldn't unlink config file %s - %s",
argv[1],
strerror(errno));
exit(1);
}
@@ -719,6 +929,7 @@ int main(int argc, const char *argv[]) {
// Gather the configuration data
if (![reporter readConfigurationData]) {
GTMLoggerDebug(@"reporter readConfigurationData failed");
exit(1);
}
@@ -729,12 +940,17 @@ int main(int argc, const char *argv[]) {
[reporter readLogFileData];
// only submit a report if we have not recently crashed in the past
BOOL shouldSubmitReport = [reporter shouldSubmitReport];
BOOL shouldSubmitReport = [reporter reportIntervalElapsed];
BOOL okayToSend = NO;
// ask user if we should send
if (shouldSubmitReport) {
okayToSend = [reporter askUserPermissionToSend:shouldSubmitReport];
if ([reporter shouldSubmitSilently]) {
GTMLoggerDebug(@"Skipping confirmation and sending report");
okayToSend = YES;
} else {
okayToSend = [reporter askUserPermissionToSend];
}
}
// If we're running as root, switch over to nobody
@@ -742,23 +958,37 @@ int main(int argc, const char *argv[]) {
struct passwd *pw = getpwnam("nobody");
// If we can't get a non-root uid, don't send the report
if (!pw)
if (!pw) {
GTMLoggerDebug(@"!pw - %s", strerror(errno));
exit(0);
}
if (setgid(pw->pw_gid) == -1)
if (setgid(pw->pw_gid) == -1) {
GTMLoggerDebug(@"setgid(pw->pw_gid) == -1 - %s", strerror(errno));
exit(0);
}
if (setuid(pw->pw_uid) == -1)
if (setuid(pw->pw_uid) == -1) {
GTMLoggerDebug(@"setuid(pw->pw_uid) == -1 - %s", strerror(errno));
exit(0);
}
}
else {
GTMLoggerDebug(@"getuid() !=0 || geteuid() != 0");
}
if (okayToSend && shouldSubmitReport) {
GTMLoggerDebug(@"Sending Report");
[reporter report];
GTMLoggerDebug(@"Report Sent!");
} else {
GTMLoggerDebug(@"Not sending crash report okayToSend=%d, "\
"shouldSubmitReport=%d", okayToSend, shouldSubmitReport);
}
GTMLoggerDebug(@"Exiting with no errors");
// Cleanup
[reporter release];
[pool release];
return 0;
}

View File

@@ -25,7 +25,7 @@
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>BreakpadProductDisplay</key>
<string>Google Breakpad Tester</string>
<string>Breakpad Tester</string>
<key>BreakpadProduct</key>
<string>Breakpad_Tester</string>
<key>BreakpadVersion</key>
@@ -36,12 +36,12 @@
<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>BreakpadMinidumpLocation</key>
<string>Breakpad Test Application/Crash Dumps</string>
<key>LSUIElement</key>
<string>1</string>
</dict>

View File

@@ -71,13 +71,9 @@ int main(int argc, const char *argv[])
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSDictionary* info = [[NSBundle mainBundle] infoDictionary];
BreakpadRef breakpad = BreakpadCreate(info);
// Add fields for Socorro
BreakpadSetKeyValue(breakpad, @"BuildID", [info valueForKey:@"MozillaBuildID"]);
BreakpadSetKeyValue(breakpad, @"ProductName", [info valueForKey:@"CFBundleName"]);
BreakpadSetKeyValue(breakpad, @"Version", [info valueForKey:@"CFBundleShortVersionString"]);
BreakpadSetKeyValue(breakpad, @"StartupTime", [NSString stringWithFormat:@"%ld", time(NULL)]);
// XXX lie for now, to get things working.
BreakpadSetKeyValue(breakpad, @"CrashTime", [NSString stringWithFormat:@"%ld", (time(NULL)+1)]);
// Breakpad uses CFBundleVersion, but we want our short version instead.
BreakpadSetKeyValue(breakpad, @BREAKPAD_VERSION,
[info valueForKey:@"CFBundleShortVersionString"]);
[pool release];
SetupRuntimeOptions(argc, argv);