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:
@@ -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.
|
||||
|
||||
@@ -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 */;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
//=============================================================================
|
||||
|
||||
BIN
mozilla/camino/google-breakpad/src/client/mac/sender/goArrow.png
Normal file
BIN
mozilla/camino/google-breakpad/src/client/mac/sender/goArrow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -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_];
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user