Camino only - Update breakpad to r366 (no bug)

git-svn-id: svn://10.0.0.236/branches/CAMINO_2_0_BRANCH@257865 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
stuart.morgan%alumni.case.edu
2009-07-30 04:22:58 +00:00
parent 04af1b1447
commit 91617c71fb
26 changed files with 997 additions and 165 deletions

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 r344 from the svn repository at:
This is a pull of r366 from the svn repository at:
http://google-breakpad.googlecode.com/svn/trunk/
with the testing and non-mac platform directories stripped out.

View File

@@ -19,6 +19,9 @@
F945858E0F782333009A47BF /* PBXTargetDependency */,
F94585900F782336009A47BF /* PBXTargetDependency */,
F93DE3A70F830D1D00608B94 /* PBXTargetDependency */,
F95BB8B3101F94D300AA053B /* PBXTargetDependency */,
F95BB8B5101F94D300AA053B /* PBXTargetDependency */,
F95BB8B7101F94D300AA053B /* PBXTargetDependency */,
);
name = All;
productName = All;
@@ -30,6 +33,8 @@
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 */; };
F91AF5D00FD60393009D8BE2 /* BreakpadFramework_Test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F91AF5CF0FD60393009D8BE2 /* BreakpadFramework_Test.mm */; };
F91AF6210FD60784009D8BE2 /* Breakpad.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; };
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 */; };
@@ -86,6 +91,7 @@
F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; };
F93DE3410F82C68300608B94 /* exception_handler_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F93DE3400F82C68300608B94 /* exception_handler_test.cc */; };
F945849E0F280E3C009A47BF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F945849C0F280E3C009A47BF /* Localizable.strings */; };
F9B630A0100FF96B00D0F4AC /* goArrow.png in Resources */ = {isa = PBXBuildFile; fileRef = F9B6309F100FF96B00D0F4AC /* goArrow.png */; };
F9C44DB20EF07288003AEBAA /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DAC0EF07288003AEBAA /* Controller.m */; };
F9C44DB30EF07288003AEBAA /* crashduringload in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAD0EF07288003AEBAA /* crashduringload */; };
F9C44DB40EF07288003AEBAA /* crashInMain in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAE0EF07288003AEBAA /* crashInMain */; };
@@ -105,6 +111,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
F91AF6370FD60A74009D8BE2 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 8DC2EF4F0486A6940098B216;
remoteInfo = Breakpad;
};
F92C564D0ECD10E5009BE4BA /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
@@ -133,13 +146,6 @@
remoteGlobalIDString = F93803BD0F80820F004D428B;
remoteInfo = generator_test;
};
F93DE2FD0F82C3C900608B94 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
proxyType = 1;
remoteGlobalIDString = F93DE2D00F82A67300608B94;
remoteInfo = minidump_file_writer_unittest;
};
F93DE36F0F82CC1300608B94 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
@@ -189,6 +195,55 @@
remoteGlobalIDString = F9C44DA40EF060A8003AEBAA;
remoteInfo = BreakpadTest;
};
F95BB884101F949F00AA053B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* crash_report */;
remoteInfo = crash_report;
};
F95BB891101F94AC00AA053B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* dump_syms */;
remoteInfo = dump_syms;
};
F95BB89E101F94C000AA053B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* symupload */;
remoteInfo = symupload;
};
F95BB8A0101F94C000AA053B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 9BD835FB0B0544950055103E /* minidump_upload */;
remoteInfo = minidump_upload;
};
F95BB8B2101F94D300AA053B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* dump_syms */;
remoteInfo = dump_syms;
};
F95BB8B4101F94D300AA053B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* symupload */;
remoteInfo = symupload;
};
F95BB8B6101F94D300AA053B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* crash_report */;
remoteInfo = crash_report;
};
F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
@@ -228,6 +283,7 @@
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; };
F91AF5CF0FD60393009D8BE2 /* BreakpadFramework_Test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BreakpadFramework_Test.mm; path = tests/BreakpadFramework_Test.mm; 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; };
@@ -285,6 +341,10 @@
F93DE3400F82C68300608B94 /* exception_handler_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = exception_handler_test.cc; path = handler/exception_handler_test.cc; sourceTree = "<group>"; };
F945849D0F280E3C009A47BF /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/Localizable.strings; sourceTree = "<group>"; };
F945859D0F78241E009A47BF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Framework/Info.plist; sourceTree = "<group>"; };
F95BB87C101F949F00AA053B /* crash_report.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = crash_report.xcodeproj; path = ../../tools/mac/crash_report/crash_report.xcodeproj; sourceTree = SOURCE_ROOT; };
F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = dump_syms.xcodeproj; path = ../../tools/mac/dump_syms/dump_syms.xcodeproj; sourceTree = SOURCE_ROOT; };
F95BB894101F94C000AA053B /* symupload.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = symupload.xcodeproj; path = ../../tools/mac/symupload/symupload.xcodeproj; sourceTree = SOURCE_ROOT; };
F9B6309F100FF96B00D0F4AC /* goArrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = goArrow.png; path = sender/goArrow.png; sourceTree = "<group>"; };
F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BreakpadTest.app; sourceTree = BUILT_PRODUCTS_DIR; };
F9C44DAC0EF07288003AEBAA /* Controller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Controller.m; path = testapp/Controller.m; sourceTree = "<group>"; };
F9C44DAD0EF07288003AEBAA /* crashduringload */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = crashduringload; path = testapp/crashduringload; sourceTree = "<group>"; };
@@ -375,6 +435,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F91AF6210FD60784009D8BE2 /* Breakpad.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -400,6 +461,7 @@
0867D691FE84028FC02AAC07 /* Breakpad */ = {
isa = PBXGroup;
children = (
F95BB8A3101F94C300AA053B /* Tools */,
32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */,
F92C538D0ECCE6F2009BE4BA /* client */,
F92C53600ECCE3D6009BE4BA /* common */,
@@ -531,6 +593,7 @@
F92C56A60ECE04B6009BE4BA /* sender */ = {
isa = PBXGroup;
children = (
F9B6309F100FF96B00D0F4AC /* goArrow.png */,
F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */,
F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */,
F945849C0F280E3C009A47BF /* Localizable.strings */,
@@ -542,6 +605,41 @@
name = sender;
sourceTree = "<group>";
};
F95BB87D101F949F00AA053B /* Products */ = {
isa = PBXGroup;
children = (
F95BB885101F949F00AA053B /* crash_report */,
);
name = Products;
sourceTree = "<group>";
};
F95BB88A101F94AC00AA053B /* Products */ = {
isa = PBXGroup;
children = (
F95BB892101F94AC00AA053B /* dump_syms */,
);
name = Products;
sourceTree = "<group>";
};
F95BB895101F94C000AA053B /* Products */ = {
isa = PBXGroup;
children = (
F95BB89F101F94C000AA053B /* symupload */,
F95BB8A1101F94C000AA053B /* minidump_upload */,
);
name = Products;
sourceTree = "<group>";
};
F95BB8A3101F94C300AA053B /* Tools */ = {
isa = PBXGroup;
children = (
F95BB894101F94C000AA053B /* symupload.xcodeproj */,
F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */,
F95BB87C101F949F00AA053B /* crash_report.xcodeproj */,
);
name = Tools;
sourceTree = "<group>";
};
F9C44DAB0EF0726F003AEBAA /* testapp */ = {
isa = PBXGroup;
children = (
@@ -564,6 +662,7 @@
children = (
F9C77DE00F7DD7E30045F7DB /* SimpleStringDictionaryTest.h */,
F9C77DE10F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm */,
F91AF5CF0FD60393009D8BE2 /* BreakpadFramework_Test.mm */,
);
name = tests;
sourceTree = "<group>";
@@ -754,8 +853,8 @@
);
dependencies = (
F93DE2FC0F82C3C600608B94 /* PBXTargetDependency */,
F93DE2FE0F82C3C900608B94 /* PBXTargetDependency */,
F93DE3700F82CC1300608B94 /* PBXTargetDependency */,
F91AF6380FD60A74009D8BE2 /* PBXTargetDependency */,
);
name = UnitTests;
productName = UnitTests;
@@ -773,6 +872,20 @@
mainGroup = 0867D691FE84028FC02AAC07 /* Breakpad */;
productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = F95BB87D101F949F00AA053B /* Products */;
ProjectRef = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */;
},
{
ProductGroup = F95BB88A101F94AC00AA053B /* Products */;
ProjectRef = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */;
},
{
ProductGroup = F95BB895101F94C000AA053B /* Products */;
ProjectRef = F95BB894101F94C000AA053B /* symupload.xcodeproj */;
},
);
projectRoot = "";
targets = (
8DC2EF4F0486A6940098B216 /* Breakpad */,
@@ -789,6 +902,37 @@
};
/* End PBXProject section */
/* Begin PBXReferenceProxy section */
F95BB885101F949F00AA053B /* crash_report */ = {
isa = PBXReferenceProxy;
fileType = "compiled.mach-o.executable";
path = crash_report;
remoteRef = F95BB884101F949F00AA053B /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
F95BB892101F94AC00AA053B /* dump_syms */ = {
isa = PBXReferenceProxy;
fileType = "compiled.mach-o.executable";
path = dump_syms;
remoteRef = F95BB891101F94AC00AA053B /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
F95BB89F101F94C000AA053B /* symupload */ = {
isa = PBXReferenceProxy;
fileType = "compiled.mach-o.executable";
path = symupload;
remoteRef = F95BB89E101F94C000AA053B /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
F95BB8A1101F94C000AA053B /* minidump_upload */ = {
isa = PBXReferenceProxy;
fileType = "compiled.mach-o.executable";
path = minidump_upload;
remoteRef = F95BB8A0101F94C000AA053B /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
8DC2EF520486A6940098B216 /* Resources */ = {
isa = PBXResourcesBuildPhase;
@@ -808,6 +952,7 @@
4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */,
33880C800F9E097100817F82 /* InfoPlist.strings in Resources */,
3329D4ED0FA16D820007BBC5 /* Breakpad.nib in Resources */,
F9B630A0100FF96B00D0F4AC /* goArrow.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -871,7 +1016,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n\necho running minidump generator tests...\n\"${BUILT_PRODUCTS_DIR}/generator_test\"\necho Running minidump file writer tests...\n\"${BUILT_PRODUCTS_DIR}/minidump_file_writer_unittest\"\necho Running exception handler tests...\n\"${BUILT_PRODUCTS_DIR}/handler_test\"\n";
shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n\necho running minidump generator tests...\n\"${BUILT_PRODUCTS_DIR}/generator_test\"\necho Running exception handler tests...\n\"${BUILT_PRODUCTS_DIR}/handler_test\"\n";
};
/* End PBXShellScriptBuildPhase section */
@@ -991,12 +1136,18 @@
F9C77DE40F7DD82F0045F7DB /* SimpleStringDictionary.mm in Sources */,
F9C77DE20F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm in Sources */,
F9C77E130F7DDF810045F7DB /* GTMSenTestCase.m in Sources */,
F91AF5D00FD60393009D8BE2 /* BreakpadFramework_Test.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
F91AF6380FD60A74009D8BE2 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 8DC2EF4F0486A6940098B216 /* Breakpad */;
targetProxy = F91AF6370FD60A74009D8BE2 /* PBXContainerItemProxy */;
};
F92C564E0ECD10E5009BE4BA /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F92C563B0ECD10B3009BE4BA /* breakpadUtilities */;
@@ -1017,11 +1168,6 @@
target = F93803BD0F80820F004D428B /* generator_test */;
targetProxy = F93DE2FB0F82C3C600608B94 /* PBXContainerItemProxy */;
};
F93DE2FE0F82C3C900608B94 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F93DE2D00F82A67300608B94 /* minidump_file_writer_unittest */;
targetProxy = F93DE2FD0F82C3C900608B94 /* PBXContainerItemProxy */;
};
F93DE3700F82CC1300608B94 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F93DE32B0F82C55600608B94 /* handler_test */;
@@ -1057,6 +1203,21 @@
target = F9C44DA40EF060A8003AEBAA /* BreakpadTest */;
targetProxy = F945858F0F782336009A47BF /* PBXContainerItemProxy */;
};
F95BB8B3101F94D300AA053B /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = dump_syms;
targetProxy = F95BB8B2101F94D300AA053B /* PBXContainerItemProxy */;
};
F95BB8B5101F94D300AA053B /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = symupload;
targetProxy = F95BB8B4101F94D300AA053B /* PBXContainerItemProxy */;
};
F95BB8B7101F94D300AA053B /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = crash_report;
targetProxy = F95BB8B6101F94D300AA053B /* PBXContainerItemProxy */;
};
F9C44E1A0EF0790F003AEBAA /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 8DC2EF4F0486A6940098B216 /* Breakpad */;

View File

@@ -81,15 +81,16 @@ extern "C" {
#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"
#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_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.
@@ -98,7 +99,8 @@ extern "C" {
// (which means the next exception handler will take the exception)
typedef bool (*BreakpadFilterCallback)(int exception_type,
int exception_code,
mach_port_t crashing_thread);
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
@@ -179,8 +181,15 @@ typedef bool (*BreakpadFilterCallback)(int exception_type,
// 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.
//
// 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
@@ -207,19 +216,39 @@ typedef bool (*BreakpadFilterCallback)(int exception_type,
//=============================================================================
// The following are NOT user-supplied but are documented here for
// completeness. They are calculated by Breakpad during initialization &
// crash-dump generation.
// crash-dump generation, or entered in by the user.
//
// BREAKPAD_PROCESS_START_TIME The time the process started.
// BREAKPAD_PROCESS_START_TIME The time the process started.
//
// BREAKPAD_PROCESS_CRASH_TIME The time the process crashed.
// 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_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_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);
@@ -227,27 +256,49 @@ 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)
// 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);
BreakpadFilterCallback callback,
void *context);
// User defined key and value string storage
// All set keys will be uploaded with the minidump if a crash occurs
// Keys and Values are limited to 255 bytes (256 - 1 for terminator).
// NB this is BYTES not GLYPHS.
// Anything longer than 255 bytes will be truncated. Note that the string is
// converted to UTF8 before truncation, so any multibyte character that
// straddles the 255 byte limit will be mangled.
// 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.
// 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);

View File

@@ -154,8 +154,9 @@ class Breakpad {
void GenerateAndSendReport();
void SetFilterCallback(BreakpadFilterCallback callback) {
void SetFilterCallback(BreakpadFilterCallback callback, void *context) {
filter_callback_ = callback;
filter_callback_context_ = context;
}
private:
@@ -163,7 +164,8 @@ class Breakpad {
: handler_(NULL),
config_params_(NULL),
send_and_exit_(true),
filter_callback_(NULL) {
filter_callback_(NULL),
filter_callback_context_(NULL) {
inspector_path_[0] = 0;
}
@@ -175,10 +177,12 @@ class Breakpad {
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
@@ -194,8 +198,7 @@ class Breakpad {
bool send_and_exit_; // Exit after sending, if true
BreakpadFilterCallback filter_callback_;
unsigned int logFileCounter;
void *filter_callback_context_;
};
#pragma mark -
@@ -244,6 +247,7 @@ static BOOL IsDebuggerActive() {
bool Breakpad::ExceptionHandlerDirectCallback(void *context,
int exception_type,
int exception_code,
int exception_subcode,
mach_port_t crashing_thread) {
Breakpad *breakpad = (Breakpad *)context;
@@ -254,6 +258,7 @@ bool Breakpad::ExceptionHandlerDirectCallback(void *context,
return breakpad->HandleException( exception_type,
exception_code,
exception_subcode,
crashing_thread);
}
@@ -404,7 +409,6 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
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];
@@ -412,8 +416,10 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
[parameters objectForKey:@BREAKPAD_VENDOR];
NSString *dumpSubdirectory =
[parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
NSString *buildId =
[parameters objectForKey:@BREAKPAD_BUILD_ID];
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];
@@ -444,7 +450,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
vendor = @"Vendor not specified";
}
// Normalize the values
// Normalize the values.
if (skipConfirm) {
skipConfirm = [skipConfirm uppercaseString];
@@ -497,7 +503,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
[resourcePath stringByAppendingPathComponent:@"Inspector"];
}
// Verify that there is an Inspector tool
// Verify that there is an Inspector tool.
if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) {
DEBUGLOG(stderr, "Cannot find Inspector tool\n");
return false;
@@ -510,7 +516,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
reporterPathString = [[NSBundle bundleWithPath:reporterPathString] executablePath];
}
// Verify that there is a Reporter application
// Verify that there is a Reporter application.
if (![[NSFileManager defaultManager]
fileExistsAtPath:reporterPathString]) {
DEBUGLOG(stderr, "Cannot find Reporter tool\n");
@@ -521,10 +527,19 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
dumpSubdirectory = @"";
}
// The product and version are required values.
if (![product length] || ![version length]) {
DEBUGLOG(stderr,
"Missing required product or version subdirectory keys\n");
// 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;
}
@@ -555,8 +570,6 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
[dumpSubdirectory UTF8String]);
dictionary.SetKeyValue(BREAKPAD_BUILD_ID,
[buildId UTF8String]);
struct timeval tv;
gettimeofday(&tv, NULL);
char timeStartedString[32];
@@ -574,11 +587,15 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
}
}
if (reportEmail) {
dictionary.SetKeyValue(BREAKPAD_EMAIL,
[reportEmail UTF8String]);
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;
}
@@ -610,19 +627,23 @@ void Breakpad::RemoveKeyValue(NSString *key) {
//=============================================================================
void Breakpad::GenerateAndSendReport() {
HandleException(0, 0, 0);
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);
crashing_thread,
filter_callback_context_);
if (!should_handle) return false;
}
@@ -650,6 +671,7 @@ bool Breakpad::HandleException(int exception_type,
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));
@@ -769,7 +791,7 @@ BreakpadRef BreakpadCreate(NSDictionary *parameters) {
[pool release];
return (BreakpadRef)breakpad;
} catch(...) { // don't let exception leave this C API
} catch(...) { // don't let exceptions leave this C API
if (gKeyValueAllocator) {
gKeyValueAllocator->~ProtectedMemoryAllocator();
gKeyValueAllocator = NULL;
@@ -817,7 +839,7 @@ void BreakpadRelease(BreakpadRef ref) {
pthread_mutex_destroy(&gDictionaryMutex);
}
} catch(...) { // don't let exception leave this C API
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadRelease() : error\n");
}
}
@@ -833,11 +855,51 @@ void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
breakpad->SetKeyValue(key, value);
}
} catch(...) { // don't let exception leave this C API
} 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;
@@ -852,7 +914,7 @@ NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
value = breakpad->KeyValue(key);
} catch(...) { // don't let exception leave this C API
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadKeyValue() : error\n");
}
@@ -870,7 +932,7 @@ void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
breakpad->RemoveKeyValue(key);
}
} catch(...) { // don't let exception leave this C API
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
}
}
@@ -887,14 +949,15 @@ void BreakpadGenerateAndSendReport(BreakpadRef ref) {
breakpad->GenerateAndSendReport();
gBreakpadAllocator->Protect();
}
} catch(...) { // don't let exception leave this C API
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n");
}
}
//=============================================================================
void BreakpadSetFilterCallback(BreakpadRef ref,
BreakpadFilterCallback callback) {
BreakpadFilterCallback callback,
void *context) {
try {
Breakpad *breakpad = (Breakpad *)ref;
@@ -903,9 +966,9 @@ void BreakpadSetFilterCallback(BreakpadRef ref,
// share the dictionary mutex here (we really don't need a mutex)
ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator);
breakpad->SetFilterCallback(callback);
breakpad->SetFilterCallback(callback, context);
}
} catch(...) { // don't let exception leave this C API
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadSetFilterCallback() : error\n");
}
}
@@ -933,5 +996,4 @@ void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname) {
}
BreakpadSetKeyValue(ref, logFileKey, logPathname);
}

View File

@@ -56,6 +56,7 @@ enum {
struct InspectorInfo {
int exception_type;
int exception_code;
int exception_subcode;
unsigned int parameter_count; // key-value pairs
};
@@ -177,6 +178,7 @@ class Inspector {
int exception_type_;
int exception_code_;
int exception_subcode_;
mach_port_t remote_task_;
mach_port_t crashing_thread_;
mach_port_t handler_thread_;

View File

@@ -297,6 +297,7 @@ kern_return_t Inspector::ReadMessages() {
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());
@@ -310,6 +311,7 @@ kern_return_t Inspector::ReadMessages() {
#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_);
@@ -317,6 +319,12 @@ kern_return_t Inspector::ReadMessages() {
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.
@@ -327,6 +335,14 @@ kern_return_t Inspector::ReadMessages() {
if(result == KERN_SUCCESS) {
KeyValueMessageData &key_value_data =
(KeyValueMessageData&)*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 {
@@ -334,6 +350,11 @@ kern_return_t Inspector::ReadMessages() {
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;
@@ -377,7 +398,7 @@ bool Inspector::InspectTask() {
SetCrashTimeParameters();
// If the client app has not specified a minidump directory,
// use a default of Library/<kDefaultLibrarySubdirectory>/<Product Name>
if (0 == strlen(minidumpDirectory)) {
if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) {
NSArray *libraryDirectories =
NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask,
@@ -435,6 +456,7 @@ bool Inspector::InspectTask() {
if (exception_type_ && exception_code_) {
generator.SetExceptionInformation(exception_type_,
exception_code_,
exception_subcode_,
crashing_thread_);
}

View File

@@ -296,6 +296,7 @@ bool ExceptionHandler::WriteMinidump(const string &dump_path,
bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
int exception_code,
int exception_subcode,
mach_port_t thread_name) {
bool result = false;
@@ -303,6 +304,7 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
if (directCallback_(callback_context_,
exception_type,
exception_code,
exception_subcode,
thread_name) ) {
if (exception_type && exception_code)
_exit(exception_type);
@@ -320,7 +322,8 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
if (filter_ && !filter_(callback_context_))
return false;
md.SetExceptionInformation(exception_type, exception_code, thread_name);
md.SetExceptionInformation(exception_type, exception_code,
exception_subcode, thread_name);
}
result = md.Write(next_minidump_path_c_);
@@ -476,7 +479,7 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
// Write out the dump and save the result for later retrieval
self->last_minidump_write_result_ =
self->WriteMinidumpWithException(0, 0, 0);
self->WriteMinidumpWithException(0, 0, 0, 0);
self->UninstallHandler(false);
@@ -506,11 +509,15 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
gBreakpadAllocator->Unprotect();
#endif
// Generate the minidump with the exception data.
self->WriteMinidumpWithException(receive.exception, receive.code[0],
receive.thread.name);
int subcode = 0;
if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1)
subcode = receive.code[1];
self->UninstallHandler(true);
// Generate the minidump with the exception data.
self->WriteMinidumpWithException(receive.exception, receive.code[0],
subcode, receive.thread.name);
self->UninstallHandler(true);
#if USE_PROTECTED_ALLOCATIONS
if(gBreakpadAllocator)

View File

@@ -77,6 +77,7 @@ class ExceptionHandler {
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.
@@ -135,7 +136,7 @@ class ExceptionHandler {
// All minidump writing goes through this one routine
bool WriteMinidumpWithException(int exception_type, int exception_code,
mach_port_t thread_name);
int exception_subcode, mach_port_t thread_name);
// When installed, this static function will be call from a newly created
// pthread with |this| as the argument

View File

@@ -51,7 +51,7 @@ static void *SleepyFunction(void *) {
}
static void Crasher() {
int *a = NULL;
int *a = (int*)0x42;
fprintf(stdout, "Going to crash...\n");
fprintf(stdout, "A = %d", *a);

View File

@@ -54,6 +54,7 @@ namespace google_breakpad {
MinidumpGenerator::MinidumpGenerator()
: exception_type_(0),
exception_code_(0),
exception_subcode_(0),
exception_thread_(0),
crashing_task_(mach_task_self()),
handler_thread_(mach_thread_self()),
@@ -67,6 +68,7 @@ MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
mach_port_t handler_thread)
: exception_type_(0),
exception_code_(0),
exception_subcode_(0),
exception_thread_(0),
crashing_task_(crashing_task),
handler_thread_(handler_thread) {
@@ -594,7 +596,10 @@ MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) {
if (!WriteContext(state, &exception_ptr->thread_context))
return false;
exception_ptr->exception_record.exception_address = CurrentPCForStack(state);
if (exception_type_ == EXC_BAD_ACCESS)
exception_ptr->exception_record.exception_address = exception_subcode_;
else
exception_ptr->exception_record.exception_address = CurrentPCForStack(state);
return true;
}

View File

@@ -102,9 +102,11 @@ class MinidumpGenerator {
bool Write(const char *path);
// Specify some exception information, if applicable
void SetExceptionInformation(int type, int code, mach_port_t thread_name) {
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;
}
@@ -150,6 +152,7 @@ class MinidumpGenerator {
// 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_;

View File

@@ -4,6 +4,14 @@
<dict>
<key>IBClasses</key>
<array>
<dict>
<key>CLASS</key>
<string>LengthLimitingTextField</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>SUPERCLASS</key>
<string>NSTextField</string>
</dict>
<dict>
<key>ACTIONS</key>
<dict>
@@ -26,10 +34,14 @@
<string>NSButton</string>
<key>commentMessage_</key>
<string>NSTextField</string>
<key>commentsEntryField_</key>
<string>LengthLimitingTextField</string>
<key>countdownLabel_</key>
<string>NSTextField</string>
<key>dialogTitle_</key>
<string>NSTextField</string>
<key>emailEntryField_</key>
<string>NSView</string>
<string>LengthLimitingTextField</string>
<key>emailLabel_</key>
<string>NSTextField</string>
<key>emailMessage_</key>

View File

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

View File

@@ -41,6 +41,24 @@
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
unsigned int maximumLength_;
}
- (void) setMaximumLength:(unsigned int)maxLength;
@end
@interface Reporter : NSObject {
@public
IBOutlet NSWindow *alertWindow_; // The alert window
@@ -50,34 +68,47 @@ extern NSString *const kDefaultServerType;
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_;
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
// 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 URL
// parameter names
// dictionary of server
// parameter names.
NSMutableDictionary *socorroDictionary_; // The dictionary for
// Socorro
// Socorro.
NSMutableDictionary *googleDictionary_; // The dictionary for
// Google
// 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
@@ -95,9 +126,6 @@ extern NSString *const kDefaultServerType;
- (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 *)commentsValue;
- (void)setCommentsValue:(NSString *)value;
@@ -105,7 +133,7 @@ extern NSString *const kDefaultServerType;
- (NSString *)emailValue;
- (void)setEmailValue:(NSString *)value;
// Initialization helper to create dictionaries mapping Breakpad
// parameters to URL parameters
- (void)createServerParameterDictionaries;
- (NSString *)countdownMessage;
- (void)setCountdownMessage:(NSString *)value;
@end

View File

@@ -42,8 +42,11 @@
#define kLastSubmission @"LastSubmission"
const int kMinidumpFileLengthLimit = 800000;
const int kUserCommentsMaxLength = 1500;
const int kEmailMaxLength = 64;
#define kApplePrefsSyncExcludeAllKey @"com.apple.PreferenceSync.ExcludeAllSyncKeys"
#define kApplePrefsSyncExcludeAllKey \
@"com.apple.PreferenceSync.ExcludeAllSyncKeys"
NSString *const kGoogleServerType = @"google";
NSString *const kSocorroServerType = @"socorro";
@@ -115,7 +118,7 @@ NSString *const kDefaultServerType = @"google";
NSRect newFrame = NSMakeRect(oldFrame.origin.x, oldFrame.origin.y,
NSWidth(oldFrame), newSize.height);
[self setFrame:newFrame];
return newSize.height - NSHeight(oldFrame);
}
@@ -174,6 +177,9 @@ NSString *const kDefaultServerType = @"google";
// Returns YES if we should send the report without asking the user first.
- (BOOL)shouldSubmitSilently;
// Returns YES if the minidump was generated on demand.
- (BOOL)isOnDemand;
// Returns YES if we should ask the user to provide comments.
- (BOOL)shouldRequestComments;
@@ -187,11 +193,11 @@ NSString *const kDefaultServerType = @"google";
// Returns the short description of the crash, suitable for use as a dialog
// title (e.g., "The application Foo has quit unexpectedly").
- (NSString*)shortCrashDialogMessage;
- (NSString*)shortDialogMessage;
// Return explanatory text about the crash and the reporter, suitable for the
// body text of a dialog.
- (NSString*)explanatoryCrashDialogText;
- (NSString*)explanatoryDialogText;
// Returns the amount of time the UI should be shown before timing out.
- (NSTimeInterval)messageTimeout;
@@ -207,7 +213,7 @@ NSString *const kDefaultServerType = @"google";
- (void)removeEmailPrompt;
// Run an alert window with the given timeout. Returns
// NSAlertButtonDefault if the timeout is exceeded. A timeout of 0
// NSRunStoppedResponse 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;
@@ -217,7 +223,34 @@ NSString *const kDefaultServerType = @"google";
// Returns a dictionary that can be used to map Breakpad parameter names to
// URL parameter names.
- (NSDictionary *)dictionaryForServerType:(NSString *)serverType;
- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType;
// Helper method to set HTTP parameters based on server type. This is
// called right before the upload - crashParameters will contain, on exit,
// URL parameters that should be sent with the minidump.
- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters;
// Initialization helper to create dictionaries mapping Breakpad
// parameters to URL parameters
- (void)createServerParameterDictionaries;
// Accessor method for the URL parameter dictionary
- (NSMutableDictionary *)urlParameterDictionary;
// This method adds a key/value pair to the dictionary that
// will be uploaded to the crash server.
- (void)addServerParameter:(id)value forKey:(NSString *)key;
// This method is used to periodically update the UI with how many
// seconds are left in the dialog display.
- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer;
// When we receive this notification, it means that the user has
// begun editing the email address or comments field, and we disable
// the timers so that the user has as long as they want to type
// in their comments/email.
- (void)controlTextDidBeginEditing:(NSNotification *)aNotification;
@end
@implementation Reporter
@@ -244,6 +277,7 @@ NSString *const kDefaultServerType = @"google";
- (id)initWithConfigurationFD:(int)fd {
if ((self = [super init])) {
configFile_ = fd;
remainingDialogTime_ = 0;
}
// Because the reporter is embedded in the framework (and many copies
@@ -308,7 +342,24 @@ NSString *const kDefaultServerType = @"google";
id value = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
[parameters_ setObject:value ? value : data forKey:key];
// If the keyname is prefixed by BREAKPAD_SERVER_PARAMETER_PREFIX
// that indicates that it should be uploaded to the server along
// with the minidump, so we treat it specially.
if ([key hasPrefix:@BREAKPAD_SERVER_PARAMETER_PREFIX]) {
NSString *urlParameterKey =
[key substringFromIndex:[@BREAKPAD_SERVER_PARAMETER_PREFIX length]];
if ([urlParameterKey length]) {
if (value) {
[self addServerParameter:value
forKey:urlParameterKey];
} else {
[self addServerParameter:data
forKey:urlParameterKey];
}
}
} else {
[parameters_ setObject:(value ? value : data) forKey:key];
}
[value release];
}
@@ -511,8 +562,8 @@ NSString *const kDefaultServerType = @"google";
}
} else {
// Create an alert panel to tell the user something happened
NSPanel* alert = NSGetAlertPanel([self shortCrashDialogMessage],
[self explanatoryCrashDialogText],
NSPanel* alert = NSGetAlertPanel([self shortDialogMessage],
[self explanatoryDialogText],
NSLocalizedString(@"sendReportButton", @""),
NSLocalizedString(@"cancelButton", @""),
nil);
@@ -532,11 +583,11 @@ NSString *const kDefaultServerType = @"google";
// "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]];
[dialogTitle_ setStringValue:[self shortDialogMessage]];
// Localize the explanatory text field.
[commentMessage_ setStringValue:[NSString stringWithFormat:@"%@\n\n%@",
[self explanatoryCrashDialogText],
[self explanatoryDialogText],
NSLocalizedString(@"commentsMsg", @"")]];
float commentHeightDelta = [commentMessage_ breakpad_adjustHeightToFit];
[headerBox_ breakpad_shiftVertically:commentHeightDelta];
@@ -581,9 +632,13 @@ NSString *const kDefaultServerType = @"google";
- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout {
// Queue a |stopModal| message to be performed in |timeout| seconds.
if (timeout > 0.001) {
[NSApp performSelector:@selector(stopModal)
withObject:nil
afterDelay:timeout];
remainingDialogTime_ = timeout;
SEL updateSelector = @selector(updateSecondsLeftInDialogDisplay:);
messageTimer_ = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:updateSelector
userInfo:nil
repeats:YES];
}
// Run the window modally and wait for either a |stopModal| message or a
@@ -591,12 +646,6 @@ NSString *const kDefaultServerType = @"google";
[NSApp activateIgnoringOtherApps:YES];
int returnMethod = [NSApp runModalForWindow:window];
// Cancel the pending |stopModal| message.
if (returnMethod != NSRunStoppedResponse) {
[NSObject cancelPreviousPerformRequestsWithTarget:NSApp
selector:@selector(stopModal)
object:nil];
}
return returnMethod;
}
@@ -633,8 +682,10 @@ NSString *const kDefaultServerType = @"google";
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:)
// If the user has entered text on the comment field, don't end
// editing on "return".
if (control == commentsEntryField_ &&
commandSelector == @selector(insertNewline:)
&& [[textView string] length] > 0) {
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
@@ -642,6 +693,50 @@ doCommandBySelector:(SEL)commandSelector {
return result;
}
- (void)controlTextDidBeginEditing:(NSNotification *)aNotification {
[messageTimer_ invalidate];
[self setCountdownMessage:@""];
}
- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer {
remainingDialogTime_ -= 1;
NSString *countdownMessage;
NSString *formatString;
int displayedTimeLeft; // This can be either minutes or seconds.
if (remainingDialogTime_ > 59) {
// calculate minutes remaining for UI purposes
displayedTimeLeft = (remainingDialogTime_ / 60);
if (displayedTimeLeft == 1) {
formatString = NSLocalizedString(@"countdownMsgMinuteSingular", @"");
} else {
formatString = NSLocalizedString(@"countdownMsgMinutesPlural", @"");
}
} else {
displayedTimeLeft = remainingDialogTime_;
if (remainingDialogTime_ == 1) {
formatString = NSLocalizedString(@"countdownMsgSecondSingular", @"");
} else {
formatString = NSLocalizedString(@"countdownMsgSecondsPlural", @"");
}
}
countdownMessage = [NSString stringWithFormat:formatString,
displayedTimeLeft];
if (remainingDialogTime_ <= 30) {
[countdownLabel_ setTextColor:[NSColor redColor]];
}
[self setCountdownMessage:countdownMessage];
if (remainingDialogTime_ <= 0) {
[messageTimer_ invalidate];
[NSApp stopModal];
}
}
#pragma mark Accessors
#pragma mark -
//=============================================================================
@@ -668,6 +763,17 @@ doCommandBySelector:(SEL)commandSelector {
}
}
- (NSString *)countdownMessage {
return [[countdownMessage_ retain] autorelease];
}
- (void)setCountdownMessage:(NSString *)value {
if (countdownMessage_ != value) {
[countdownMessage_ release];
countdownMessage_ = [value copy];
}
}
#pragma mark -
//=============================================================================
- (BOOL)reportIntervalElapsed {
@@ -696,6 +802,11 @@ doCommandBySelector:(SEL)commandSelector {
return YES;
}
- (BOOL)isOnDemand {
return [[parameters_ objectForKey:@BREAKPAD_ON_DEMAND]
isEqualToString:@"YES"];
}
- (BOOL)shouldSubmitSilently {
return [[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM]
isEqualToString:@"YES"];
@@ -711,21 +822,40 @@ doCommandBySelector:(SEL)commandSelector {
isEqualToString:@"YES"];
}
- (NSString*)shortCrashDialogMessage {
- (NSString*)shortDialogMessage {
NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
if (![displayName length])
displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
return [NSString stringWithFormat:NSLocalizedString(@"headerFmt", @""),
displayName];
if ([self isOnDemand]) {
return [NSString
stringWithFormat:NSLocalizedString(@"noCrashDialogHeader", @""),
displayName];
} else {
return [NSString
stringWithFormat:NSLocalizedString(@"crashDialogHeader", @""),
displayName];
}
}
- (NSString*)explanatoryCrashDialogText {
- (NSString*)explanatoryDialogText {
NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
if (![displayName length])
displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
NSString *vendor = [parameters_ objectForKey:@BREAKPAD_VENDOR];
if (![vendor length])
vendor = @"unknown vendor";
return [NSString stringWithFormat:NSLocalizedString(@"msgFmt", @""), vendor];
if ([self isOnDemand]) {
return [NSString
stringWithFormat:NSLocalizedString(@"noCrashDialogMsg", @""),
vendor, displayName];
} else {
return [NSString
stringWithFormat:NSLocalizedString(@"crashDialogMsg", @""),
vendor];
}
}
- (NSTimeInterval)messageTimeout {
@@ -743,6 +873,7 @@ doCommandBySelector:(SEL)commandSelector {
serverDictionary_ = [[NSMutableDictionary alloc] init];
socorroDictionary_ = [[NSMutableDictionary alloc] init];
googleDictionary_ = [[NSMutableDictionary alloc] init];
extraServerVars_ = [[NSMutableDictionary alloc] init];
[serverDictionary_ setObject:socorroDictionary_ forKey:kSocorroServerType];
[serverDictionary_ setObject:googleDictionary_ forKey:kGoogleServerType];
@@ -752,8 +883,6 @@ doCommandBySelector:(SEL)commandSelector {
[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"
@@ -766,21 +895,23 @@ doCommandBySelector:(SEL)commandSelector {
forKey:@BREAKPAD_PRODUCT];
[socorroDictionary_ setObject:@"ProductName"
forKey:@BREAKPAD_PRODUCT];
[socorroDictionary_ setObject:@"BuildID"
forKey:@BREAKPAD_BUILD_ID];
}
- (NSDictionary *)dictionaryForServerType:(NSString *)serverType {
- (NSMutableDictionary *)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 {
- (NSMutableDictionary *)urlParameterDictionary {
NSString *serverType = [parameters_ objectForKey:@BREAKPAD_SERVER_TYPE];
NSDictionary *urlParameterNames = [self dictionaryForServerType:serverType];
return [self dictionaryForServerType:serverType];
}
- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters {
NSDictionary *urlParameterNames = [self urlParameterDictionary];
id key;
NSEnumerator *enumerator = [parameters_ keyEnumerator];
@@ -802,16 +933,31 @@ doCommandBySelector:(SEL)commandSelector {
forKey:urlParameter];
}
}
// Now, add the parameters that were added by the application.
enumerator = [extraServerVars_ keyEnumerator];
while ((key = [enumerator nextObject])) {
NSString *urlParameterName = (NSString *)key;
NSString *urlParameterValue =
[extraServerVars_ objectForKey:urlParameterName];
[crashParameters setObject:urlParameterValue
forKey:urlParameterName];
}
return YES;
}
- (void)addServerParameter:(id)value forKey:(NSString *)key {
[extraServerVars_ setObject:value forKey:key];
}
//=============================================================================
- (void)report {
NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]];
HTTPMultipartUpload *upload = [[HTTPMultipartUpload alloc] initWithURL:url];
NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary];
if (![self setPostParametersFromDictionary:uploadParameters]) {
if (![self populateServerDictionary:uploadParameters]) {
return;
}
@@ -886,9 +1032,81 @@ doCommandBySelector:(SEL)commandSelector {
[googleDictionary_ release];
[socorroDictionary_ release];
[serverDictionary_ release];
[extraServerVars_ release];
[super dealloc];
}
- (void)awakeFromNib {
[emailEntryField_ setMaximumLength:kEmailMaxLength];
[commentsEntryField_ setMaximumLength:kUserCommentsMaxLength];
}
@end
//=============================================================================
@implementation LengthLimitingTextField
- (void) setMaximumLength:(unsigned int)maxLength {
maximumLength_ = maxLength;
}
// This is the method we're overriding in NSTextField, which lets us
// limit the user's input if it makes the string too long.
- (BOOL) textView:(NSTextView *)textView
shouldChangeTextInRange:(NSRange)affectedCharRange
replacementString:(NSString *)replacementString {
// Sometimes the range comes in invalid, so reject if we can't
// figure out if the replacement text is too long.
if (affectedCharRange.location == NSNotFound) {
return NO;
}
// Figure out what the new string length would be, taking into
// account user selections.
int newStringLength =
[[textView string] length] - affectedCharRange.length +
[replacementString length];
if (newStringLength > maximumLength_) {
return NO;
} else {
return YES;
}
}
// Cut, copy, and paste have to be caught specifically since there is no menu.
- (BOOL)performKeyEquivalent:(NSEvent*)event {
// Only handle the key equivalent if |self| is the text field with focus.
NSText* fieldEditor = [self currentEditor];
if (fieldEditor != nil) {
// Check for a single "Command" modifier
unsigned int modifiers = [event modifierFlags];
modifiers &= NSDeviceIndependentModifierFlagsMask;
if (modifiers == NSCommandKeyMask) {
// Now, check for Select All, Cut, Copy, or Paste key equivalents.
NSString* characters = [event characters];
// Select All is Command-A.
if ([characters isEqualToString:@"a"]) {
[fieldEditor selectAll:self];
return YES;
// Cut is Command-X.
} else if ([characters isEqualToString:@"x"]) {
[fieldEditor cut:self];
return YES;
// Copy is Command-C.
} else if ([characters isEqualToString:@"c"]) {
[fieldEditor copy:self];
return YES;
// Paste is Command-V.
} else if ([characters isEqualToString:@"v"]) {
[fieldEditor paste:self];
return YES;
}
}
}
// Let the super class handle the rest (e.g. Command-Period will cancel).
return [super performKeyEquivalent:event];
}
@end
//=============================================================================

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -234,6 +234,9 @@
NSLog(@"Shouldn't find BreakpadKeyValue (key2)");
}
BreakpadAddUploadParameter(breakpad_,
@"MeaningOfLife",
@"42");
[NSThread detachNewThreadSelector:@selector(anotherThread)
toTarget:self withObject:nil];
@@ -244,8 +247,8 @@
// automated testing.
if ([args boolForKey:@"autocrash"]) {
BreakpadSetKeyValue(breakpad_,
@BREAKPAD_SKIP_CONFIRM,
@"YES");
@BREAKPAD_SKIP_CONFIRM,
@"YES");
[self causeCrash];
}

View File

@@ -42,6 +42,13 @@
<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>

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

@@ -321,13 +321,16 @@ void DumpFunctionMap(const dwarf2reader::FunctionMap function_map) {
//=============================================================================
- (BOOL)loadSymbolInfo:(void *)base offset:(uint32_t)offset {
BOOL loadedStabs = [self loadSTABSSymbolInfo:base offset:offset];
NSMutableDictionary *archSections = [sectionData_ objectForKey:architecture_];
BOOL loadedDWARF = NO;
if ([archSections objectForKey:@"__DWARF__debug_info"]) {
// Treat this this as debug information
return [self loadDWARFSymbolInfo:base offset:offset];
loadedDWARF = [self loadDWARFSymbolInfo:base offset:offset];
}
return [self loadSTABSSymbolInfo:base offset:offset];
return loadedDWARF || loadedStabs;
}
//=============================================================================
@@ -342,11 +345,15 @@ void DumpFunctionMap(const dwarf2reader::FunctionMap function_map) {
section *dbgInfoSection = [[archSections objectForKey:@"__DWARF__debug_info"] sectionPointer];
uint32_t debugInfoSize = SwapLongIfNeeded(dbgInfoSection->size);
// i think this will break if run on a big-endian machine
#if __BIG_ENDIAN__
dwarf2reader::ByteReader byte_reader(swap ?
dwarf2reader::ENDIANNESS_LITTLE :
dwarf2reader::ENDIANNESS_BIG);
#elif __LITTLE_ENDIAN__
dwarf2reader::ByteReader byte_reader(swap ?
dwarf2reader::ENDIANNESS_BIG :
dwarf2reader::ENDIANNESS_LITTLE);
#endif
uint64_t dbgOffset = 0;
dwarf2reader::SectionMap* oneArchitectureSectionMap = [self getSectionMapForArchitecture:architecture_];

View File

@@ -36,8 +36,9 @@
#include <string>
#include <vector>
#include "google_breakpad/processor/system_info.h"
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/processor/system_info.h"
#include "google_breakpad/processor/minidump.h"
namespace google_breakpad {
@@ -62,6 +63,9 @@ class ProcessState {
u_int64_t crash_address() const { return crash_address_; }
int requesting_thread() const { return requesting_thread_; }
const vector<CallStack*>* threads() const { return &threads_; }
const vector<MinidumpMemoryRegion*>* thread_memory_regions() const {
return &thread_memory_regions_;
}
const SystemInfo* system_info() const { return &system_info_; }
const CodeModules* modules() const { return modules_; }
@@ -101,6 +105,7 @@ class ProcessState {
// Stacks for each thread (except possibly the exception handler
// thread) at the time of the crash.
vector<CallStack*> threads_;
vector<MinidumpMemoryRegion*> thread_memory_regions_;
// OS and CPU information.
SystemInfo system_info_;

View File

@@ -206,6 +206,7 @@ ProcessResult MinidumpProcessor::Process(
interrupted = true;
}
process_state->threads_.push_back(stack.release());
process_state->thread_memory_regions_.push_back(thread_memory);
}
if (interrupted) {

View File

@@ -266,7 +266,6 @@ TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
} // namespace
int main(int argc, char *argv[]) {
BPLOG_INIT(&argc, &argv);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -59,6 +59,7 @@ using google_breakpad::BasicSourceLineResolver;
using google_breakpad::CallStack;
using google_breakpad::CodeModule;
using google_breakpad::CodeModules;
using google_breakpad::Minidump;
using google_breakpad::MinidumpProcessor;
using google_breakpad::OnDemandSymbolSupplier;
using google_breakpad::PathnameStripper;
@@ -73,6 +74,7 @@ typedef struct {
NSString *minidumpPath;
NSString *searchDir;
NSString *symbolSearchDir;
BOOL printThreadMemory;
} Options;
//=============================================================================
@@ -234,8 +236,14 @@ static void Start(Options *options) {
scoped_ptr<MinidumpProcessor>
minidump_processor(new MinidumpProcessor(symbol_supplier.get(), &resolver));
ProcessState process_state;
if (minidump_processor->Process(minidump_file, &process_state) !=
MinidumpProcessor::PROCESS_OK) {
scoped_ptr<Minidump> dump(new google_breakpad::Minidump(minidump_file));
if (!dump->Read()) {
fprintf(stderr, "Minidump %s could not be read\n", dump->path().c_str());
return;
}
if (minidump_processor->Process(dump.get(), &process_state) !=
google_breakpad::PROCESS_OK) {
fprintf(stderr, "MinidumpProcessor::Process failed\n");
return;
}
@@ -274,12 +282,20 @@ static void Start(Options *options) {
// Print all of the threads in the dump.
int thread_count = process_state.threads()->size();
const std::vector<google_breakpad::MinidumpMemoryRegion*>
*thread_memory_regions = process_state.thread_memory_regions();
for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
if (thread_index != requesting_thread) {
// Don't print the crash thread again, it was already printed.
printf("\n");
printf("Thread %d\n", thread_index);
PrintStack(process_state.threads()->at(thread_index), cpu);
google_breakpad::MinidumpMemoryRegion *thread_stack_bytes =
thread_memory_regions->at(thread_index);
if (options->printThreadMemory) {
thread_stack_bytes->Print();
}
}
}
@@ -303,9 +319,11 @@ static void Usage(int argc, const char *argv[]) {
"If modules cannot be found at the paths stored in the "
"minidump file, they will be searched for at "
"<module-search-dir>/<path-in-minidump-file>.\n");
fprintf(stderr, "Usage: %s [-s module-search-dir] [-S symbol-file-search-dir] minidump-file\n", argv[0]);
fprintf(stderr, "Usage: %s [-s module-search-dir] [-S symbol-file-search-dir] "
"minidump-file\n", argv[0]);
fprintf(stderr, "\t-s: Specify a search directory to use for missing modules\n"
"\t-S: Specify a search directory to use for symbol files\n"
"\t-S: Specify a search directory to use for symbol files\n"
"\t-t: Print thread stack memory in hex\n"
"\t-h: Usage\n"
"\t-?: Usage\n");
}
@@ -315,7 +333,7 @@ static void SetupOptions(int argc, const char *argv[], Options *options) {
extern int optind;
char ch;
while ((ch = getopt(argc, (char * const *)argv, "S:s:h?")) != -1) {
while ((ch = getopt(argc, (char * const *)argv, "S:s:ht?")) != -1) {
switch (ch) {
case 's':
options->searchDir = [[NSFileManager defaultManager]
@@ -329,6 +347,9 @@ static void SetupOptions(int argc, const char *argv[], Options *options) {
length:strlen(optarg)];
break;
case 't':
options->printThreadMemory = YES;
break;
case 'h':
case '?':
Usage(argc, argv);