aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornealsid <nealsid@4c0a9323-5329-0410-9bdc-e9ce6186880e>2009-04-01 03:18:49 +0000
committernealsid <nealsid@4c0a9323-5329-0410-9bdc-e9ce6186880e>2009-04-01 03:18:49 +0000
commit3ebdb1bd7ae38bf0fb205dfaa2f5fde3d67ea141 (patch)
tree0217ab93423465d8931af3a6fce65caccb5cc5ba
parentissue 305 - breakpad Linux handler doesn't build with compiler built from lat... (diff)
downloadbreakpad-3ebdb1bd7ae38bf0fb205dfaa2f5fde3d67ea141.tar.xz
Open sourcing the Breakpad framework from Google.
A=many, many people R=nealsid, jeremy moskovich(from Chromium project) git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@322 4c0a9323-5329-0410-9bdc-e9ce6186880e
-rw-r--r--src/client/mac/Breakpad.xcodeproj/project.pbxproj1937
-rw-r--r--src/client/mac/Framework/Breakpad.h181
-rw-r--r--src/client/mac/Framework/Breakpad.mm915
-rw-r--r--src/client/mac/Framework/Breakpad_Prefix.pch8
-rw-r--r--src/client/mac/Framework/English.lproj/InfoPlist.stringsbin0 -> 92 bytes
-rw-r--r--src/client/mac/Framework/Info.plist26
-rw-r--r--src/client/mac/Framework/OnDemandServer.h146
-rw-r--r--src/client/mac/Framework/OnDemandServer.mm145
-rw-r--r--src/client/mac/UnitTests-Info.plist20
-rw-r--r--src/client/mac/crash_generation/Inspector.h200
-rw-r--r--src/client/mac/crash_generation/Inspector.mm431
-rw-r--r--src/client/mac/crash_generation/InspectorMain.mm65
-rw-r--r--src/client/mac/gcov/libgcov.abin0 -> 35048 bytes
-rw-r--r--src/client/mac/handler/exception_handler.cc1
-rw-r--r--src/client/mac/handler/exception_handler_test.cc10
-rw-r--r--src/client/mac/handler/minidump_generator_test.cc7
-rw-r--r--src/client/mac/sender/English.lproj/Breakpad.xib834
-rw-r--r--src/client/mac/sender/English.lproj/Localizable.stringsbin0 -> 1360 bytes
-rw-r--r--src/client/mac/sender/ReporterIcon.graffle2489
-rw-r--r--src/client/mac/sender/ReporterIcon.icnsbin0 -> 170816 bytes
-rw-r--r--src/client/mac/sender/crash_report_sender-Info.plist24
-rw-r--r--src/client/mac/sender/crash_report_sender.h89
-rw-r--r--src/client/mac/sender/crash_report_sender.m762
-rw-r--r--src/client/mac/testapp/Controller.h65
-rw-r--r--src/client/mac/testapp/Controller.m257
-rw-r--r--src/client/mac/testapp/English.lproj/InfoPlist.stringsbin0 -> 192 bytes
-rw-r--r--src/client/mac/testapp/English.lproj/MainMenu.nib/classes.nib47
-rw-r--r--src/client/mac/testapp/English.lproj/MainMenu.nib/info.nib22
-rw-r--r--src/client/mac/testapp/English.lproj/MainMenu.nib/keyedobjects.nibbin0 -> 34374 bytes
-rw-r--r--src/client/mac/testapp/Info.plist46
-rw-r--r--src/client/mac/testapp/TestClass.h37
-rw-r--r--src/client/mac/testapp/TestClass.mm95
-rw-r--r--src/client/mac/testapp/bomb.icnsbin0 -> 23659 bytes
-rwxr-xr-xsrc/client/mac/testapp/crashInMainbin0 -> 12588 bytes
-rwxr-xr-xsrc/client/mac/testapp/crashduringloadbin0 -> 12588 bytes
-rw-r--r--src/client/mac/testapp/main.m34
-rw-r--r--src/client/mac/tests/SimpleStringDictionaryTest.h40
-rw-r--r--src/client/mac/tests/SimpleStringDictionaryTest.mm243
-rw-r--r--src/client/minidump_file_writer_unittest.cc28
-rw-r--r--src/common/mac/GTMDefines.h241
-rw-r--r--src/common/mac/GTMGarbageCollection.h72
-rw-r--r--src/common/mac/GTMLogger.h458
-rw-r--r--src/common/mac/GTMLogger.m445
-rw-r--r--src/common/mac/MachIPC.h304
-rw-r--r--src/common/mac/MachIPC.mm297
-rw-r--r--src/common/mac/SimpleStringDictionary.h195
-rw-r--r--src/common/mac/SimpleStringDictionary.mm131
-rw-r--r--src/common/mac/testing/GTMSenTestCase.h1004
-rw-r--r--src/common/mac/testing/GTMSenTestCase.m366
49 files changed, 12695 insertions, 22 deletions
diff --git a/src/client/mac/Breakpad.xcodeproj/project.pbxproj b/src/client/mac/Breakpad.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..1794529a
--- /dev/null
+++ b/src/client/mac/Breakpad.xcodeproj/project.pbxproj
@@ -0,0 +1,1937 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 44;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ F94585840F782326009A47BF /* All */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = F94585930F78235C009A47BF /* Build configuration list for PBXAggregateTarget "All" */;
+ buildPhases = (
+ );
+ dependencies = (
+ F94585880F78232B009A47BF /* PBXTargetDependency */,
+ F945858A0F78232E009A47BF /* PBXTargetDependency */,
+ F945858C0F782330009A47BF /* PBXTargetDependency */,
+ F945858E0F782333009A47BF /* PBXTargetDependency */,
+ F94585900F782336009A47BF /* PBXTargetDependency */,
+ F93DE3A70F830D1D00608B94 /* PBXTargetDependency */,
+ );
+ name = All;
+ productName = All;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 4084699D0F5D9CF900FDCA37 /* ReporterIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4084699C0F5D9CF900FDCA37 /* ReporterIcon.icns */; };
+ 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.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 */; };
+ F92C55D00ECD0064009BE4BA /* Breakpad.h in Headers */ = {isa = PBXBuildFile; fileRef = F92C55CE0ECD0064009BE4BA /* Breakpad.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ F92C55D10ECD0064009BE4BA /* Breakpad.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C55CF0ECD0064009BE4BA /* Breakpad.mm */; };
+ F92C56330ECD0DF1009BE4BA /* OnDemandServer.h in Headers */ = {isa = PBXBuildFile; fileRef = F92C56310ECD0DF1009BE4BA /* OnDemandServer.h */; };
+ F92C56340ECD0DF1009BE4BA /* OnDemandServer.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C56320ECD0DF1009BE4BA /* OnDemandServer.mm */; };
+ F92C563F0ECD10CA009BE4BA /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.c */; };
+ F92C56400ECD10CA009BE4BA /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; };
+ F92C56410ECD10CA009BE4BA /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; };
+ F92C56420ECD10CA009BE4BA /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; };
+ F92C56430ECD10CA009BE4BA /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; };
+ F92C56440ECD10CA009BE4BA /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; };
+ F92C56450ECD10CA009BE4BA /* MachIPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53790ECCE635009BE4BA /* MachIPC.mm */; };
+ F92C56460ECD10CA009BE4BA /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; };
+ F92C56470ECD10CA009BE4BA /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */; };
+ F92C56480ECD10CA009BE4BA /* SimpleStringDictionary.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53810ECCE635009BE4BA /* SimpleStringDictionary.mm */; };
+ F92C56490ECD10CA009BE4BA /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; };
+ F92C564A0ECD10CA009BE4BA /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; };
+ F92C564C0ECD10DD009BE4BA /* breakpadUtilities.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; };
+ F92C56570ECD113E009BE4BA /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C554A0ECCF530009BE4BA /* Carbon.framework */; };
+ F92C565C0ECD1158009BE4BA /* breakpadUtilities.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; };
+ F92C565F0ECD116B009BE4BA /* protected_memory_allocator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53720ECCE3FD009BE4BA /* protected_memory_allocator.cc */; };
+ F92C56630ECD1179009BE4BA /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; };
+ F92C56650ECD1185009BE4BA /* breakpadUtilities.dylib in Resources */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; };
+ F92C568A0ECD15F9009BE4BA /* Inspector in Resources */ = {isa = PBXBuildFile; fileRef = F92C53540ECCE349009BE4BA /* Inspector */; };
+ F92C56A90ECE04C5009BE4BA /* crash_report_sender.m in Sources */ = {isa = PBXBuildFile; fileRef = F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */; };
+ F93803CD0F8083B7004D428B /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; };
+ F93803CE0F8083B7004D428B /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; };
+ F93803CF0F8083B7004D428B /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */; };
+ F93803D00F8083B7004D428B /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; };
+ F93803D10F8083B7004D428B /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.c */; };
+ F93803D20F8083B7004D428B /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; };
+ F93803D30F8083B7004D428B /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; };
+ F93803D40F8083B7004D428B /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; };
+ F93803D50F8083B7004D428B /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; };
+ F93803D60F8083B7004D428B /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; };
+ F93803D70F8083B7004D428B /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; };
+ F93803DA0F8083D8004D428B /* minidump_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F93803D90F8083D8004D428B /* minidump_generator_test.cc */; };
+ F93DE2D80F82A70E00608B94 /* minidump_file_writer_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = F93DE2D70F82A70E00608B94 /* minidump_file_writer_unittest.cc */; };
+ F93DE2D90F82A73500608B94 /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; };
+ F93DE2DA0F82A73500608B94 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.c */; };
+ F93DE2DB0F82A73500608B94 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; };
+ F93DE3350F82C66B00608B94 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; };
+ F93DE3360F82C66B00608B94 /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; };
+ F93DE3370F82C66B00608B94 /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */; };
+ F93DE3380F82C66B00608B94 /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; };
+ F93DE3390F82C66B00608B94 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.c */; };
+ F93DE33A0F82C66B00608B94 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; };
+ F93DE33B0F82C66B00608B94 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; };
+ F93DE33C0F82C66B00608B94 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; };
+ F93DE33D0F82C66B00608B94 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; };
+ F93DE33E0F82C66B00608B94 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; };
+ 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 */; };
+ F94586220F7842CF009A47BF /* Breakpad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 40F2AEEB0F4F2F55004510E8 /* Breakpad.xib */; };
+ F9C444530F7981E600991B96 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F9C444510F7981E600991B96 /* InfoPlist.strings */; };
+ 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 */; };
+ F9C44DB60EF07288003AEBAA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DB00EF07288003AEBAA /* main.m */; };
+ F9C44DB70EF07288003AEBAA /* TestClass.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DB10EF07288003AEBAA /* TestClass.mm */; };
+ F9C44DBC0EF072A0003AEBAA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */; };
+ F9C44DBD0EF072A0003AEBAA /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DBA0EF072A0003AEBAA /* MainMenu.nib */; };
+ F9C44E000EF077CD003AEBAA /* Breakpad.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; };
+ F9C44E3C0EF08B12003AEBAA /* Breakpad.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; };
+ F9C44E980EF09F56003AEBAA /* crash_report_sender.app in Resources */ = {isa = PBXBuildFile; fileRef = F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */; };
+ F9C44EA20EF09F93003AEBAA /* HTTPMultipartUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */; };
+ F9C44EE50EF0A006003AEBAA /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */; };
+ F9C44EE90EF0A3C1003AEBAA /* GTMLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44EE80EF0A3C1003AEBAA /* GTMLogger.m */; };
+ F9C77DE20F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9C77DE10F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm */; };
+ F9C77DE40F7DD82F0045F7DB /* SimpleStringDictionary.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53810ECCE635009BE4BA /* SimpleStringDictionary.mm */; };
+ F9C77E130F7DDF810045F7DB /* GTMSenTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C77E120F7DDF810045F7DB /* GTMSenTestCase.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ F92C564D0ECD10E5009BE4BA /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F92C563B0ECD10B3009BE4BA;
+ remoteInfo = breakpadUtilities;
+ };
+ F92C56850ECD15EF009BE4BA /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F92C563B0ECD10B3009BE4BA;
+ remoteInfo = breakpadUtilities;
+ };
+ F92C56870ECD15F1009BE4BA /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F92C53530ECCE349009BE4BA;
+ remoteInfo = Inspector;
+ };
+ F93DE2FB0F82C3C600608B94 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F93803BD0F80820F004D428B /* generator_test */;
+ remoteInfo = generator_test;
+ };
+ F93DE2FD0F82C3C900608B94 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F93DE2D00F82A67300608B94 /* minidump_file_writer_unittest */;
+ remoteInfo = minidump_file_writer_unittest;
+ };
+ F93DE36F0F82CC1300608B94 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F93DE32B0F82C55600608B94 /* handler_test */;
+ remoteInfo = handler_test;
+ };
+ F93DE3A60F830D1D00608B94 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F9C77DD90F7DD5CF0045F7DB /* UnitTests */;
+ remoteInfo = UnitTests;
+ };
+ F94585870F78232B009A47BF /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 8DC2EF4F0486A6940098B216;
+ remoteInfo = Breakpad;
+ };
+ F94585890F78232E009A47BF /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F92C53530ECCE349009BE4BA;
+ remoteInfo = Inspector;
+ };
+ F945858B0F782330009A47BF /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F92C563B0ECD10B3009BE4BA;
+ remoteInfo = breakpadUtilities;
+ };
+ F945858D0F782333009A47BF /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F92C569F0ECE04A7009BE4BA;
+ remoteInfo = crash_report_sender;
+ };
+ F945858F0F782336009A47BF /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F9C44DA40EF060A8003AEBAA;
+ remoteInfo = BreakpadTest;
+ };
+ F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 8DC2EF4F0486A6940098B216;
+ remoteInfo = Breakpad;
+ };
+ F9C44E960EF09F4B003AEBAA /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F92C569F0ECE04A7009BE4BA;
+ remoteInfo = crash_report_sender;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ F9C44E410EF08B17003AEBAA /* Copy Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ F9C44E3C0EF08B12003AEBAA /* Breakpad.framework in Copy Frameworks */,
+ );
+ name = "Copy Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+ 0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
+ 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
+ 32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpad_Prefix.pch; path = Framework/Breakpad_Prefix.pch; sourceTree = "<group>"; };
+ 4084699C0F5D9CF900FDCA37 /* ReporterIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = ReporterIcon.icns; path = sender/ReporterIcon.icns; sourceTree = "<group>"; };
+ 40F2AEEC0F4F2F55004510E8 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = sender/English.lproj/Breakpad.xib; sourceTree = "<group>"; };
+ 8DC2EF5B0486A6940098B216 /* Breakpad.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Breakpad.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 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; };
+ F92C53670ECCE3FD009BE4BA /* breakpad_exc_server.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = breakpad_exc_server.c; path = handler/breakpad_exc_server.c; sourceTree = SOURCE_ROOT; };
+ F92C53680ECCE3FD009BE4BA /* breakpad_exc_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = breakpad_exc_server.h; path = handler/breakpad_exc_server.h; sourceTree = SOURCE_ROOT; };
+ F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = breakpad_nlist_64.cc; path = handler/breakpad_nlist_64.cc; sourceTree = SOURCE_ROOT; };
+ F92C536A0ECCE3FD009BE4BA /* breakpad_nlist_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = breakpad_nlist_64.h; path = handler/breakpad_nlist_64.h; sourceTree = SOURCE_ROOT; };
+ F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dynamic_images.cc; path = handler/dynamic_images.cc; sourceTree = SOURCE_ROOT; };
+ F92C536C0ECCE3FD009BE4BA /* dynamic_images.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dynamic_images.h; path = handler/dynamic_images.h; sourceTree = SOURCE_ROOT; };
+ F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = exception_handler.cc; path = handler/exception_handler.cc; sourceTree = SOURCE_ROOT; };
+ F92C536E0ECCE3FD009BE4BA /* exception_handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = exception_handler.h; path = handler/exception_handler.h; sourceTree = SOURCE_ROOT; };
+ F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_generator.cc; path = handler/minidump_generator.cc; sourceTree = SOURCE_ROOT; };
+ F92C53700ECCE3FD009BE4BA /* minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = minidump_generator.h; path = handler/minidump_generator.h; sourceTree = SOURCE_ROOT; };
+ F92C53720ECCE3FD009BE4BA /* protected_memory_allocator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = protected_memory_allocator.cc; path = handler/protected_memory_allocator.cc; sourceTree = SOURCE_ROOT; };
+ F92C53730ECCE3FD009BE4BA /* protected_memory_allocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = protected_memory_allocator.h; path = handler/protected_memory_allocator.h; sourceTree = SOURCE_ROOT; };
+ F92C53740ECCE635009BE4BA /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = file_id.cc; path = ../../common/mac/file_id.cc; sourceTree = SOURCE_ROOT; };
+ F92C53750ECCE635009BE4BA /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = file_id.h; path = ../../common/mac/file_id.h; sourceTree = SOURCE_ROOT; };
+ F92C53760ECCE635009BE4BA /* HTTPMultipartUpload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTTPMultipartUpload.h; path = ../../common/mac/HTTPMultipartUpload.h; sourceTree = SOURCE_ROOT; };
+ F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HTTPMultipartUpload.m; path = ../../common/mac/HTTPMultipartUpload.m; sourceTree = SOURCE_ROOT; };
+ F92C53780ECCE635009BE4BA /* MachIPC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MachIPC.h; path = ../../common/mac/MachIPC.h; sourceTree = SOURCE_ROOT; };
+ F92C53790ECCE635009BE4BA /* MachIPC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MachIPC.mm; path = ../../common/mac/MachIPC.mm; sourceTree = SOURCE_ROOT; };
+ F92C537A0ECCE635009BE4BA /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_id.cc; path = ../../common/mac/macho_id.cc; sourceTree = SOURCE_ROOT; };
+ F92C537B0ECCE635009BE4BA /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_id.h; path = ../../common/mac/macho_id.h; sourceTree = SOURCE_ROOT; };
+ F92C537C0ECCE635009BE4BA /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_utilities.cc; path = ../../common/mac/macho_utilities.cc; sourceTree = SOURCE_ROOT; };
+ F92C537D0ECCE635009BE4BA /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_utilities.h; path = ../../common/mac/macho_utilities.h; sourceTree = SOURCE_ROOT; };
+ F92C537E0ECCE635009BE4BA /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_walker.cc; path = ../../common/mac/macho_walker.cc; sourceTree = SOURCE_ROOT; };
+ F92C537F0ECCE635009BE4BA /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_walker.h; path = ../../common/mac/macho_walker.h; sourceTree = SOURCE_ROOT; };
+ F92C53800ECCE635009BE4BA /* SimpleStringDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SimpleStringDictionary.h; path = ../../common/mac/SimpleStringDictionary.h; sourceTree = SOURCE_ROOT; };
+ F92C53810ECCE635009BE4BA /* SimpleStringDictionary.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SimpleStringDictionary.mm; path = ../../common/mac/SimpleStringDictionary.mm; sourceTree = SOURCE_ROOT; };
+ F92C53820ECCE635009BE4BA /* string_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = string_utilities.cc; path = ../../common/mac/string_utilities.cc; sourceTree = SOURCE_ROOT; };
+ F92C53830ECCE635009BE4BA /* string_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = string_utilities.h; path = ../../common/mac/string_utilities.h; sourceTree = SOURCE_ROOT; };
+ F92C53850ECCE6AD009BE4BA /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = string_conversion.cc; path = ../../common/string_conversion.cc; sourceTree = SOURCE_ROOT; };
+ F92C53860ECCE6AD009BE4BA /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = string_conversion.h; path = ../../common/string_conversion.h; sourceTree = SOURCE_ROOT; };
+ F92C53870ECCE6C0009BE4BA /* convert_UTF.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = convert_UTF.c; path = ../../common/convert_UTF.c; sourceTree = SOURCE_ROOT; };
+ F92C53880ECCE6C0009BE4BA /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = convert_UTF.h; path = ../../common/convert_UTF.h; sourceTree = SOURCE_ROOT; };
+ F92C538E0ECCE70A009BE4BA /* minidump_file_writer-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "minidump_file_writer-inl.h"; path = "../minidump_file_writer-inl.h"; sourceTree = SOURCE_ROOT; };
+ F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer.cc; path = ../minidump_file_writer.cc; sourceTree = SOURCE_ROOT; };
+ F92C53900ECCE70A009BE4BA /* minidump_file_writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = minidump_file_writer.h; path = ../minidump_file_writer.h; sourceTree = SOURCE_ROOT; };
+ F92C53B70ECCE7B3009BE4BA /* Inspector.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Inspector.mm; path = crash_generation/Inspector.mm; sourceTree = SOURCE_ROOT; };
+ F92C554A0ECCF530009BE4BA /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = "<absolute>"; };
+ F92C55CE0ECD0064009BE4BA /* Breakpad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpad.h; path = Framework/Breakpad.h; sourceTree = "<group>"; };
+ F92C55CF0ECD0064009BE4BA /* Breakpad.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Breakpad.mm; path = Framework/Breakpad.mm; sourceTree = "<group>"; };
+ F92C56310ECD0DF1009BE4BA /* OnDemandServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OnDemandServer.h; path = Framework/OnDemandServer.h; sourceTree = "<group>"; };
+ F92C56320ECD0DF1009BE4BA /* OnDemandServer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = OnDemandServer.mm; path = Framework/OnDemandServer.mm; sourceTree = "<group>"; };
+ F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = breakpadUtilities.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
+ F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = crash_report_sender.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ F92C56A20ECE04A7009BE4BA /* crash_report_sender-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "crash_report_sender-Info.plist"; path = "sender/crash_report_sender-Info.plist"; sourceTree = "<group>"; };
+ F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crash_report_sender.h; path = sender/crash_report_sender.h; sourceTree = "<group>"; };
+ F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = crash_report_sender.m; path = sender/crash_report_sender.m; sourceTree = "<group>"; };
+ F93803BE0F80820F004D428B /* generator_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = generator_test; sourceTree = BUILT_PRODUCTS_DIR; };
+ F93803D90F8083D8004D428B /* minidump_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_generator_test.cc; path = handler/minidump_generator_test.cc; sourceTree = "<group>"; };
+ F93DE2D10F82A67300608B94 /* minidump_file_writer_unittest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = minidump_file_writer_unittest; sourceTree = BUILT_PRODUCTS_DIR; };
+ F93DE2D70F82A70E00608B94 /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer_unittest.cc; path = ../minidump_file_writer_unittest.cc; sourceTree = SOURCE_ROOT; };
+ F93DE32C0F82C55600608B94 /* handler_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = handler_test; sourceTree = BUILT_PRODUCTS_DIR; };
+ 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 = 4; 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>"; };
+ F9C444520F7981E600991B96 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = Framework/English.lproj/InfoPlist.strings; 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>"; };
+ F9C44DAE0EF07288003AEBAA /* crashInMain */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = crashInMain; path = testapp/crashInMain; sourceTree = "<group>"; };
+ F9C44DAF0EF07288003AEBAA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = testapp/Info.plist; sourceTree = "<group>"; };
+ F9C44DB00EF07288003AEBAA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = testapp/main.m; sourceTree = "<group>"; };
+ F9C44DB10EF07288003AEBAA /* TestClass.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TestClass.mm; path = testapp/TestClass.mm; sourceTree = "<group>"; };
+ F9C44DB90EF072A0003AEBAA /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = testapp/English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ F9C44DBB0EF072A0003AEBAA /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = testapp/English.lproj/MainMenu.nib; sourceTree = "<group>"; };
+ F9C44DBF0EF0778F003AEBAA /* Controller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Controller.h; path = testapp/Controller.h; sourceTree = "<group>"; };
+ F9C44DC00EF0778F003AEBAA /* TestClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestClass.h; path = testapp/TestClass.h; sourceTree = "<group>"; };
+ F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = "<absolute>"; };
+ F9C44EE70EF0A3C1003AEBAA /* GTMLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMLogger.h; path = ../../common/mac/GTMLogger.h; sourceTree = SOURCE_ROOT; };
+ F9C44EE80EF0A3C1003AEBAA /* GTMLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMLogger.m; path = ../../common/mac/GTMLogger.m; sourceTree = SOURCE_ROOT; };
+ F9C77DDA0F7DD5CF0045F7DB /* UnitTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.octest; sourceTree = BUILT_PRODUCTS_DIR; };
+ F9C77DDB0F7DD5CF0045F7DB /* UnitTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "UnitTests-Info.plist"; sourceTree = "<group>"; };
+ F9C77DE00F7DD7E30045F7DB /* SimpleStringDictionaryTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SimpleStringDictionaryTest.h; path = tests/SimpleStringDictionaryTest.h; sourceTree = "<group>"; };
+ F9C77DE10F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SimpleStringDictionaryTest.mm; path = tests/SimpleStringDictionaryTest.mm; sourceTree = "<group>"; };
+ F9C77E110F7DDF810045F7DB /* GTMSenTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMSenTestCase.h; path = ../../common/mac/testing/GTMSenTestCase.h; sourceTree = SOURCE_ROOT; };
+ F9C77E120F7DDF810045F7DB /* GTMSenTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMSenTestCase.m; path = ../../common/mac/testing/GTMSenTestCase.m; sourceTree = SOURCE_ROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 8DC2EF560486A6940098B216 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F92C565C0ECD1158009BE4BA /* breakpadUtilities.dylib in Frameworks */,
+ 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F92C53520ECCE349009BE4BA /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F92C564C0ECD10DD009BE4BA /* breakpadUtilities.dylib in Frameworks */,
+ F92C554C0ECCF534009BE4BA /* Foundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F92C563A0ECD10B3009BE4BA /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F92C56570ECD113E009BE4BA /* Carbon.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F92C569E0ECE04A7009BE4BA /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9C44EE50EF0A006003AEBAA /* SystemConfiguration.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F93803BC0F80820F004D428B /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F93DE2CF0F82A67300608B94 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F93DE32A0F82C55600608B94 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9C44DA30EF060A8003AEBAA /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9C44E000EF077CD003AEBAA /* Breakpad.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9C77DD70F7DD5CF0045F7DB /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 034768DFFF38A50411DB9C8B /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 8DC2EF5B0486A6940098B216 /* Breakpad.framework */,
+ F92C53540ECCE349009BE4BA /* Inspector */,
+ F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */,
+ F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */,
+ F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */,
+ F9C77DDA0F7DD5CF0045F7DB /* UnitTests.octest */,
+ F93803BE0F80820F004D428B /* generator_test */,
+ F93DE2D10F82A67300608B94 /* minidump_file_writer_unittest */,
+ F93DE32C0F82C55600608B94 /* handler_test */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 0867D691FE84028FC02AAC07 /* Breakpad */ = {
+ isa = PBXGroup;
+ children = (
+ 32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */,
+ F92C538D0ECCE6F2009BE4BA /* client */,
+ F92C53600ECCE3D6009BE4BA /* common */,
+ 0867D69AFE84028FC02AAC07 /* Frameworks */,
+ 034768DFFF38A50411DB9C8B /* Products */,
+ F92C56A20ECE04A7009BE4BA /* crash_report_sender-Info.plist */,
+ F9C77DDB0F7DD5CF0045F7DB /* UnitTests-Info.plist */,
+ );
+ name = Breakpad;
+ sourceTree = "<group>";
+ };
+ 0867D69AFE84028FC02AAC07 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */,
+ F92C554A0ECCF530009BE4BA /* Carbon.framework */,
+ 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */,
+ 0867D6A5FE840307C02AAC07 /* AppKit.framework */,
+ 0867D69BFE84028FC02AAC07 /* Foundation.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ F92C53590ECCE3BB009BE4BA /* handler */ = {
+ isa = PBXGroup;
+ children = (
+ F93DE3400F82C68300608B94 /* exception_handler_test.cc */,
+ F93803D90F8083D8004D428B /* minidump_generator_test.cc */,
+ F92C53670ECCE3FD009BE4BA /* breakpad_exc_server.c */,
+ F92C53680ECCE3FD009BE4BA /* breakpad_exc_server.h */,
+ F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */,
+ F92C536A0ECCE3FD009BE4BA /* breakpad_nlist_64.h */,
+ F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */,
+ F92C536C0ECCE3FD009BE4BA /* dynamic_images.h */,
+ F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */,
+ F92C536E0ECCE3FD009BE4BA /* exception_handler.h */,
+ F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */,
+ F92C53700ECCE3FD009BE4BA /* minidump_generator.h */,
+ F92C53720ECCE3FD009BE4BA /* protected_memory_allocator.cc */,
+ F92C53730ECCE3FD009BE4BA /* protected_memory_allocator.h */,
+ );
+ name = handler;
+ sourceTree = "<group>";
+ };
+ F92C53600ECCE3D6009BE4BA /* common */ = {
+ isa = PBXGroup;
+ children = (
+ F92C53870ECCE6C0009BE4BA /* convert_UTF.c */,
+ F92C53880ECCE6C0009BE4BA /* convert_UTF.h */,
+ F92C53850ECCE6AD009BE4BA /* string_conversion.cc */,
+ F92C53860ECCE6AD009BE4BA /* string_conversion.h */,
+ F92C53840ECCE68D009BE4BA /* mac */,
+ );
+ name = common;
+ sourceTree = "<group>";
+ };
+ F92C53840ECCE68D009BE4BA /* mac */ = {
+ isa = PBXGroup;
+ children = (
+ F9C77E0F0F7DDF650045F7DB /* testing */,
+ F9C44EE70EF0A3C1003AEBAA /* GTMLogger.h */,
+ F9C44EE80EF0A3C1003AEBAA /* GTMLogger.m */,
+ F92C53740ECCE635009BE4BA /* file_id.cc */,
+ F92C53750ECCE635009BE4BA /* file_id.h */,
+ F92C53760ECCE635009BE4BA /* HTTPMultipartUpload.h */,
+ F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */,
+ F92C53780ECCE635009BE4BA /* MachIPC.h */,
+ F92C53790ECCE635009BE4BA /* MachIPC.mm */,
+ F92C537A0ECCE635009BE4BA /* macho_id.cc */,
+ F92C537B0ECCE635009BE4BA /* macho_id.h */,
+ F92C537C0ECCE635009BE4BA /* macho_utilities.cc */,
+ F92C537D0ECCE635009BE4BA /* macho_utilities.h */,
+ F92C537E0ECCE635009BE4BA /* macho_walker.cc */,
+ F92C537F0ECCE635009BE4BA /* macho_walker.h */,
+ F92C53800ECCE635009BE4BA /* SimpleStringDictionary.h */,
+ F92C53810ECCE635009BE4BA /* SimpleStringDictionary.mm */,
+ F92C53820ECCE635009BE4BA /* string_utilities.cc */,
+ F92C53830ECCE635009BE4BA /* string_utilities.h */,
+ );
+ name = mac;
+ sourceTree = "<group>";
+ };
+ F92C538D0ECCE6F2009BE4BA /* client */ = {
+ isa = PBXGroup;
+ children = (
+ F92C53990ECCE78E009BE4BA /* mac */,
+ F92C538E0ECCE70A009BE4BA /* minidump_file_writer-inl.h */,
+ F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */,
+ F92C53900ECCE70A009BE4BA /* minidump_file_writer.h */,
+ F93DE2D70F82A70E00608B94 /* minidump_file_writer_unittest.cc */,
+ );
+ name = client;
+ sourceTree = "<group>";
+ };
+ F92C53990ECCE78E009BE4BA /* mac */ = {
+ isa = PBXGroup;
+ children = (
+ F9C77DDF0F7DD7CF0045F7DB /* tests */,
+ F9C44DAB0EF0726F003AEBAA /* testapp */,
+ F92C56A60ECE04B6009BE4BA /* sender */,
+ F92C55CD0ECD0053009BE4BA /* Framework */,
+ F92C53B50ECCE799009BE4BA /* crash_generation */,
+ F92C53590ECCE3BB009BE4BA /* handler */,
+ );
+ name = mac;
+ sourceTree = "<group>";
+ };
+ F92C53B50ECCE799009BE4BA /* crash_generation */ = {
+ isa = PBXGroup;
+ children = (
+ F9286B380F7EB25800A4DCC8 /* Inspector.h */,
+ F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */,
+ F92C53B70ECCE7B3009BE4BA /* Inspector.mm */,
+ );
+ name = crash_generation;
+ sourceTree = "<group>";
+ };
+ F92C55CD0ECD0053009BE4BA /* Framework */ = {
+ isa = PBXGroup;
+ children = (
+ F9C444510F7981E600991B96 /* InfoPlist.strings */,
+ F945859D0F78241E009A47BF /* Info.plist */,
+ F92C56310ECD0DF1009BE4BA /* OnDemandServer.h */,
+ F92C56320ECD0DF1009BE4BA /* OnDemandServer.mm */,
+ F92C55CE0ECD0064009BE4BA /* Breakpad.h */,
+ F92C55CF0ECD0064009BE4BA /* Breakpad.mm */,
+ );
+ name = Framework;
+ sourceTree = "<group>";
+ };
+ F92C56A60ECE04B6009BE4BA /* sender */ = {
+ isa = PBXGroup;
+ children = (
+ 4084699C0F5D9CF900FDCA37 /* ReporterIcon.icns */,
+ 40F2AEEB0F4F2F55004510E8 /* Breakpad.xib */,
+ F945849C0F280E3C009A47BF /* Localizable.strings */,
+ F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */,
+ F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */,
+ );
+ name = sender;
+ sourceTree = "<group>";
+ };
+ F9C44DAB0EF0726F003AEBAA /* testapp */ = {
+ isa = PBXGroup;
+ children = (
+ F9C44DBF0EF0778F003AEBAA /* Controller.h */,
+ F9C44DC00EF0778F003AEBAA /* TestClass.h */,
+ F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */,
+ F9C44DBA0EF072A0003AEBAA /* MainMenu.nib */,
+ F9C44DAC0EF07288003AEBAA /* Controller.m */,
+ F9C44DAD0EF07288003AEBAA /* crashduringload */,
+ F9C44DAE0EF07288003AEBAA /* crashInMain */,
+ F9C44DAF0EF07288003AEBAA /* Info.plist */,
+ F9C44DB00EF07288003AEBAA /* main.m */,
+ F9C44DB10EF07288003AEBAA /* TestClass.mm */,
+ );
+ name = testapp;
+ sourceTree = "<group>";
+ };
+ F9C77DDF0F7DD7CF0045F7DB /* tests */ = {
+ isa = PBXGroup;
+ children = (
+ F9C77DE00F7DD7E30045F7DB /* SimpleStringDictionaryTest.h */,
+ F9C77DE10F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm */,
+ );
+ name = tests;
+ sourceTree = "<group>";
+ };
+ F9C77E0F0F7DDF650045F7DB /* testing */ = {
+ isa = PBXGroup;
+ children = (
+ F9C77E110F7DDF810045F7DB /* GTMSenTestCase.h */,
+ F9C77E120F7DDF810045F7DB /* GTMSenTestCase.m */,
+ );
+ name = testing;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 8DC2EF500486A6940098B216 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F92C55D00ECD0064009BE4BA /* Breakpad.h in Headers */,
+ F92C56330ECD0DF1009BE4BA /* OnDemandServer.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F92C56380ECD10B3009BE4BA /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ 8DC2EF4F0486A6940098B216 /* Breakpad */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */;
+ buildPhases = (
+ F97A0E850ED4EC15008784D3 /* Change install name of breakpadUtilities */,
+ 8DC2EF500486A6940098B216 /* Headers */,
+ 8DC2EF520486A6940098B216 /* Resources */,
+ 8DC2EF540486A6940098B216 /* Sources */,
+ 8DC2EF560486A6940098B216 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ F92C56860ECD15EF009BE4BA /* PBXTargetDependency */,
+ F92C56880ECD15F1009BE4BA /* PBXTargetDependency */,
+ F9C44E970EF09F4B003AEBAA /* PBXTargetDependency */,
+ );
+ name = Breakpad;
+ productInstallPath = "$(HOME)/Library/Frameworks";
+ productName = Breakpad;
+ productReference = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+ F92C53530ECCE349009BE4BA /* Inspector */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F92C53580ECCE36D009BE4BA /* Build configuration list for PBXNativeTarget "Inspector" */;
+ buildPhases = (
+ F94584840F27FB40009A47BF /* Change install name of breakpadUtilities */,
+ F92C53510ECCE349009BE4BA /* Sources */,
+ F92C53520ECCE349009BE4BA /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ F92C564E0ECD10E5009BE4BA /* PBXTargetDependency */,
+ );
+ name = Inspector;
+ productName = Inspector;
+ productReference = F92C53540ECCE349009BE4BA /* Inspector */;
+ productType = "com.apple.product-type.tool";
+ };
+ F92C563B0ECD10B3009BE4BA /* breakpadUtilities */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F92C56670ECD11A3009BE4BA /* Build configuration list for PBXNativeTarget "breakpadUtilities" */;
+ buildPhases = (
+ F92C56380ECD10B3009BE4BA /* Headers */,
+ F92C56390ECD10B3009BE4BA /* Sources */,
+ F92C563A0ECD10B3009BE4BA /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = breakpadUtilities;
+ productName = breakpadUtilities;
+ productReference = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */;
+ productType = "com.apple.product-type.library.dynamic";
+ };
+ F92C569F0ECE04A7009BE4BA /* crash_report_sender */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F92C56A50ECE04A8009BE4BA /* Build configuration list for PBXNativeTarget "crash_report_sender" */;
+ buildPhases = (
+ F92C569C0ECE04A7009BE4BA /* Resources */,
+ F92C569D0ECE04A7009BE4BA /* Sources */,
+ F92C569E0ECE04A7009BE4BA /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = crash_report_sender;
+ productName = crash_report_sender;
+ productReference = F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */;
+ productType = "com.apple.product-type.application";
+ };
+ F93803BD0F80820F004D428B /* generator_test */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F93803C40F80822E004D428B /* Build configuration list for PBXNativeTarget "generator_test" */;
+ buildPhases = (
+ F93803BB0F80820F004D428B /* Sources */,
+ F93803BC0F80820F004D428B /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = generator_test;
+ productName = generator_test;
+ productReference = F93803BE0F80820F004D428B /* generator_test */;
+ productType = "com.apple.product-type.tool";
+ };
+ F93DE2D00F82A67300608B94 /* minidump_file_writer_unittest */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F93DE2D60F82A67700608B94 /* Build configuration list for PBXNativeTarget "minidump_file_writer_unittest" */;
+ buildPhases = (
+ F93DE2CE0F82A67300608B94 /* Sources */,
+ F93DE2CF0F82A67300608B94 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = minidump_file_writer_unittest;
+ productName = minidump_file_writer_unittest;
+ productReference = F93DE2D10F82A67300608B94 /* minidump_file_writer_unittest */;
+ productType = "com.apple.product-type.tool";
+ };
+ F93DE32B0F82C55600608B94 /* handler_test */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F93DE3320F82C5D800608B94 /* Build configuration list for PBXNativeTarget "handler_test" */;
+ buildPhases = (
+ F93DE3290F82C55600608B94 /* Sources */,
+ F93DE32A0F82C55600608B94 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = handler_test;
+ productName = handler_test;
+ productReference = F93DE32C0F82C55600608B94 /* handler_test */;
+ productType = "com.apple.product-type.tool";
+ };
+ F9C44DA40EF060A8003AEBAA /* BreakpadTest */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F9C44DAA0EF060A9003AEBAA /* Build configuration list for PBXNativeTarget "BreakpadTest" */;
+ buildPhases = (
+ F9C44DA10EF060A8003AEBAA /* Resources */,
+ F9C44DA20EF060A8003AEBAA /* Sources */,
+ F9C44DA30EF060A8003AEBAA /* Frameworks */,
+ F9C44E410EF08B17003AEBAA /* Copy Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ F9C44E1A0EF0790F003AEBAA /* PBXTargetDependency */,
+ );
+ name = BreakpadTest;
+ productName = BreakpadTest;
+ productReference = F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */;
+ productType = "com.apple.product-type.application";
+ };
+ F9C77DD90F7DD5CF0045F7DB /* UnitTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F9C77DDE0F7DD5D00045F7DB /* Build configuration list for PBXNativeTarget "UnitTests" */;
+ buildPhases = (
+ F9C77DD50F7DD5CF0045F7DB /* Resources */,
+ F9C77DD60F7DD5CF0045F7DB /* Sources */,
+ F9C77DD70F7DD5CF0045F7DB /* Frameworks */,
+ F9C77DD80F7DD5CF0045F7DB /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ F93DE2FC0F82C3C600608B94 /* PBXTargetDependency */,
+ F93DE2FE0F82C3C900608B94 /* PBXTargetDependency */,
+ F93DE3700F82CC1300608B94 /* PBXTargetDependency */,
+ );
+ name = UnitTests;
+ productName = UnitTests;
+ productReference = F9C77DDA0F7DD5CF0045F7DB /* UnitTests.octest */;
+ productType = "com.apple.product-type.bundle";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 0867D690FE84028FC02AAC07 /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Breakpad" */;
+ compatibilityVersion = "Xcode 3.0";
+ hasScannedForEncodings = 1;
+ mainGroup = 0867D691FE84028FC02AAC07 /* Breakpad */;
+ productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 8DC2EF4F0486A6940098B216 /* Breakpad */,
+ F92C53530ECCE349009BE4BA /* Inspector */,
+ F92C563B0ECD10B3009BE4BA /* breakpadUtilities */,
+ F92C569F0ECE04A7009BE4BA /* crash_report_sender */,
+ F9C44DA40EF060A8003AEBAA /* BreakpadTest */,
+ F94585840F782326009A47BF /* All */,
+ F9C77DD90F7DD5CF0045F7DB /* UnitTests */,
+ F93803BD0F80820F004D428B /* generator_test */,
+ F93DE2D00F82A67300608B94 /* minidump_file_writer_unittest */,
+ F93DE32B0F82C55600608B94 /* handler_test */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 8DC2EF520486A6940098B216 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9C44E980EF09F56003AEBAA /* crash_report_sender.app in Resources */,
+ F92C568A0ECD15F9009BE4BA /* Inspector in Resources */,
+ F92C56650ECD1185009BE4BA /* breakpadUtilities.dylib in Resources */,
+ F9C444530F7981E600991B96 /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F92C569C0ECE04A7009BE4BA /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F94586220F7842CF009A47BF /* Breakpad.xib in Resources */,
+ F945849E0F280E3C009A47BF /* Localizable.strings in Resources */,
+ 4084699D0F5D9CF900FDCA37 /* ReporterIcon.icns in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9C44DA10EF060A8003AEBAA /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9C44DB30EF07288003AEBAA /* crashduringload in Resources */,
+ F9C44DB40EF07288003AEBAA /* crashInMain in Resources */,
+ F9C44DBC0EF072A0003AEBAA /* InfoPlist.strings in Resources */,
+ F9C44DBD0EF072A0003AEBAA /* MainMenu.nib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9C77DD50F7DD5CF0045F7DB /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ F94584840F27FB40009A47BF /* Change install name of breakpadUtilities */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Change install name of breakpadUtilities";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "install_name_tool -id \"@executable_path/../Resources/breakpadUtilities.dylib\" ${BUILT_PRODUCTS_DIR}/breakpadUtilities.dylib\n";
+ };
+ F97A0E850ED4EC15008784D3 /* Change install name of breakpadUtilities */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Change install name of breakpadUtilities";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "#!/bin/bash\ninstall_name_tool -id \"@executable_path/../Frameworks/Breakpad.framework/Resources/breakpadUtilities.dylib\" ${BUILT_PRODUCTS_DIR}/breakpadUtilities.dylib\n";
+ };
+ F9C77DD80F7DD5CF0045F7DB /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n\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";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 8DC2EF540486A6940098B216 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F92C565F0ECD116B009BE4BA /* protected_memory_allocator.cc in Sources */,
+ F92C56630ECD1179009BE4BA /* exception_handler.cc in Sources */,
+ F92C55D10ECD0064009BE4BA /* Breakpad.mm in Sources */,
+ F92C56340ECD0DF1009BE4BA /* OnDemandServer.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F92C53510ECCE349009BE4BA /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F92C53B80ECCE7B3009BE4BA /* Inspector.mm in Sources */,
+ F9286B3A0F7EB25800A4DCC8 /* InspectorMain.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F92C56390ECD10B3009BE4BA /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F92C563F0ECD10CA009BE4BA /* convert_UTF.c in Sources */,
+ F92C56400ECD10CA009BE4BA /* dynamic_images.cc in Sources */,
+ F92C56410ECD10CA009BE4BA /* file_id.cc in Sources */,
+ F92C56420ECD10CA009BE4BA /* macho_id.cc in Sources */,
+ F92C56430ECD10CA009BE4BA /* macho_utilities.cc in Sources */,
+ F92C56440ECD10CA009BE4BA /* macho_walker.cc in Sources */,
+ F92C56450ECD10CA009BE4BA /* MachIPC.mm in Sources */,
+ F92C56460ECD10CA009BE4BA /* minidump_file_writer.cc in Sources */,
+ F92C56470ECD10CA009BE4BA /* minidump_generator.cc in Sources */,
+ F92C56480ECD10CA009BE4BA /* SimpleStringDictionary.mm in Sources */,
+ F92C56490ECD10CA009BE4BA /* string_utilities.cc in Sources */,
+ F92C564A0ECD10CA009BE4BA /* string_conversion.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F92C569D0ECE04A7009BE4BA /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9C44EA20EF09F93003AEBAA /* HTTPMultipartUpload.m in Sources */,
+ F92C56A90ECE04C5009BE4BA /* crash_report_sender.m in Sources */,
+ F9C44EE90EF0A3C1003AEBAA /* GTMLogger.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F93803BB0F80820F004D428B /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F93803CD0F8083B7004D428B /* dynamic_images.cc in Sources */,
+ F93803CE0F8083B7004D428B /* exception_handler.cc in Sources */,
+ F93803CF0F8083B7004D428B /* minidump_generator.cc in Sources */,
+ F93803D00F8083B7004D428B /* minidump_file_writer.cc in Sources */,
+ F93803D10F8083B7004D428B /* convert_UTF.c in Sources */,
+ F93803D20F8083B7004D428B /* string_conversion.cc in Sources */,
+ F93803D30F8083B7004D428B /* file_id.cc in Sources */,
+ F93803D40F8083B7004D428B /* macho_id.cc in Sources */,
+ F93803D50F8083B7004D428B /* macho_utilities.cc in Sources */,
+ F93803D60F8083B7004D428B /* macho_walker.cc in Sources */,
+ F93803D70F8083B7004D428B /* string_utilities.cc in Sources */,
+ F93803DA0F8083D8004D428B /* minidump_generator_test.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F93DE2CE0F82A67300608B94 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F93DE2D90F82A73500608B94 /* minidump_file_writer.cc in Sources */,
+ F93DE2DA0F82A73500608B94 /* convert_UTF.c in Sources */,
+ F93DE2DB0F82A73500608B94 /* string_conversion.cc in Sources */,
+ F93DE2D80F82A70E00608B94 /* minidump_file_writer_unittest.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F93DE3290F82C55600608B94 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F93DE3350F82C66B00608B94 /* dynamic_images.cc in Sources */,
+ F93DE3360F82C66B00608B94 /* exception_handler.cc in Sources */,
+ F93DE3370F82C66B00608B94 /* minidump_generator.cc in Sources */,
+ F93DE3380F82C66B00608B94 /* minidump_file_writer.cc in Sources */,
+ F93DE3390F82C66B00608B94 /* convert_UTF.c in Sources */,
+ F93DE33A0F82C66B00608B94 /* string_conversion.cc in Sources */,
+ F93DE33B0F82C66B00608B94 /* file_id.cc in Sources */,
+ F93DE33C0F82C66B00608B94 /* macho_id.cc in Sources */,
+ F93DE33D0F82C66B00608B94 /* macho_utilities.cc in Sources */,
+ F93DE33E0F82C66B00608B94 /* macho_walker.cc in Sources */,
+ F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */,
+ F93DE3410F82C68300608B94 /* exception_handler_test.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9C44DA20EF060A8003AEBAA /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9C44DB20EF07288003AEBAA /* Controller.m in Sources */,
+ F9C44DB60EF07288003AEBAA /* main.m in Sources */,
+ F9C44DB70EF07288003AEBAA /* TestClass.mm in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9C77DD60F7DD5CF0045F7DB /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9C77DE40F7DD82F0045F7DB /* SimpleStringDictionary.mm in Sources */,
+ F9C77DE20F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm in Sources */,
+ F9C77E130F7DDF810045F7DB /* GTMSenTestCase.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ F92C564E0ECD10E5009BE4BA /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F92C563B0ECD10B3009BE4BA /* breakpadUtilities */;
+ targetProxy = F92C564D0ECD10E5009BE4BA /* PBXContainerItemProxy */;
+ };
+ F92C56860ECD15EF009BE4BA /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F92C563B0ECD10B3009BE4BA /* breakpadUtilities */;
+ targetProxy = F92C56850ECD15EF009BE4BA /* PBXContainerItemProxy */;
+ };
+ F92C56880ECD15F1009BE4BA /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F92C53530ECCE349009BE4BA /* Inspector */;
+ targetProxy = F92C56870ECD15F1009BE4BA /* PBXContainerItemProxy */;
+ };
+ F93DE2FC0F82C3C600608B94 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ 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 */;
+ targetProxy = F93DE36F0F82CC1300608B94 /* PBXContainerItemProxy */;
+ };
+ F93DE3A70F830D1D00608B94 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F9C77DD90F7DD5CF0045F7DB /* UnitTests */;
+ targetProxy = F93DE3A60F830D1D00608B94 /* PBXContainerItemProxy */;
+ };
+ F94585880F78232B009A47BF /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 8DC2EF4F0486A6940098B216 /* Breakpad */;
+ targetProxy = F94585870F78232B009A47BF /* PBXContainerItemProxy */;
+ };
+ F945858A0F78232E009A47BF /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F92C53530ECCE349009BE4BA /* Inspector */;
+ targetProxy = F94585890F78232E009A47BF /* PBXContainerItemProxy */;
+ };
+ F945858C0F782330009A47BF /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F92C563B0ECD10B3009BE4BA /* breakpadUtilities */;
+ targetProxy = F945858B0F782330009A47BF /* PBXContainerItemProxy */;
+ };
+ F945858E0F782333009A47BF /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F92C569F0ECE04A7009BE4BA /* crash_report_sender */;
+ targetProxy = F945858D0F782333009A47BF /* PBXContainerItemProxy */;
+ };
+ F94585900F782336009A47BF /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F9C44DA40EF060A8003AEBAA /* BreakpadTest */;
+ targetProxy = F945858F0F782336009A47BF /* PBXContainerItemProxy */;
+ };
+ F9C44E1A0EF0790F003AEBAA /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 8DC2EF4F0486A6940098B216 /* Breakpad */;
+ targetProxy = F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */;
+ };
+ F9C44E970EF09F4B003AEBAA /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F92C569F0ECE04A7009BE4BA /* crash_report_sender */;
+ targetProxy = F9C44E960EF09F4B003AEBAA /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 40F2AEEB0F4F2F55004510E8 /* Breakpad.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 40F2AEEC0F4F2F55004510E8 /* English */,
+ );
+ name = Breakpad.xib;
+ sourceTree = "<group>";
+ };
+ F945849C0F280E3C009A47BF /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F945849D0F280E3C009A47BF /* English */,
+ );
+ name = Localizable.strings;
+ sourceTree = "<group>";
+ };
+ F9C444510F7981E600991B96 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F9C444520F7981E600991B96 /* English */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F9C44DB90EF072A0003AEBAA /* English */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ F9C44DBA0EF072A0003AEBAA /* MainMenu.nib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F9C44DBB0EF072A0003AEBAA /* English */,
+ );
+ name = MainMenu.nib;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 1DEB91AE08733DA50010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = Framework/Breakpad_Prefix.pch;
+ HEADER_SEARCH_PATHS = "../..//**";
+ INFOPLIST_FILE = Framework/Info.plist;
+ INSTALL_PATH = "@executable_path/../Frameworks";
+ PRODUCT_NAME = Breakpad;
+ WRAPPER_EXTENSION = framework;
+ };
+ name = Debug;
+ };
+ 1DEB91AF08733DA50010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = Framework/Breakpad_Prefix.pch;
+ HEADER_SEARCH_PATHS = "../..//**";
+ INFOPLIST_FILE = Framework/Info.plist;
+ INSTALL_PATH = "@executable_path/../Frameworks";
+ PRODUCT_NAME = Breakpad;
+ SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk";
+ WRAPPER_EXTENSION = framework;
+ };
+ name = Release;
+ };
+ 1DEB91B208733DA50010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ONLY_ACTIVE_ARCH_PRE_XCODE_3_1)";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ ONLY_ACTIVE_ARCH_PRE_XCODE_3_1 = "$(NATIVE_ARCH)";
+ PREBINDING = NO;
+ SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk";
+ };
+ name = Debug;
+ };
+ 1DEB91B308733DA50010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)";
+ ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = "../..//**";
+ PREBINDING = NO;
+ SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk";
+ };
+ name = Release;
+ };
+ F92C53560ECCE34A009BE4BA /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ HEADER_SEARCH_PATHS = "../..//**";
+ INSTALL_PATH = /usr/local/bin;
+ OTHER_LDFLAGS = (
+ "-lcrypto",
+ "$(inherited)",
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = Inspector;
+ SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk";
+ };
+ name = Debug;
+ };
+ F92C53570ECCE34A009BE4BA /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = YES;
+ DEAD_CODE_STRIPPING = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ HEADER_SEARCH_PATHS = "../..//**";
+ INSTALL_PATH = /usr/local/bin;
+ LD_GENERATE_MAP_FILE = YES;
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-lcrypto",
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = Inspector;
+ SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk";
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ F92C563D0ECD10B3009BE4BA /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ HEADER_SEARCH_PATHS = "../..//**";
+ INSTALL_PATH = /usr/local/lib;
+ LD_DYLIB_INSTALL_NAME = "@executable_path/../Resources/$(EXECUTABLE_PATH)";
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-headerpad_max_install_names",
+ "-lcrypto",
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = breakpadUtilities;
+ SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk";
+ };
+ name = Debug;
+ };
+ F92C563E0ECD10B3009BE4BA /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ HEADER_SEARCH_PATHS = "../..//**";
+ INSTALL_PATH = /usr/local/lib;
+ LD_DYLIB_INSTALL_NAME = "@executable_path/../Resources/$(EXECUTABLE_PATH)";
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-headerpad_max_install_names",
+ "-lcrypto",
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = breakpadUtilities;
+ SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk";
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ F92C56A30ECE04A8009BE4BA /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ HEADER_SEARCH_PATHS = "../..//**";
+ INFOPLIST_FILE = "sender/crash_report_sender-Info.plist";
+ INSTALL_PATH = "$(HOME)/Applications";
+ OTHER_LDFLAGS = (
+ "-framework",
+ Foundation,
+ "-framework",
+ AppKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = crash_report_sender;
+ };
+ name = Debug;
+ };
+ F92C56A40ECE04A8009BE4BA /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ HEADER_SEARCH_PATHS = "../..//**";
+ INFOPLIST_FILE = "sender/crash_report_sender-Info.plist";
+ INSTALL_PATH = "$(HOME)/Applications";
+ OTHER_LDFLAGS = (
+ "-framework",
+ Foundation,
+ "-framework",
+ AppKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = crash_report_sender;
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ F93803C00F808210004D428B /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ INSTALL_PATH = /usr/local/bin;
+ OTHER_LDFLAGS = (
+ "-lcrypto",
+ "-framework",
+ Foundation,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = generator_test;
+ USER_HEADER_SEARCH_PATHS = ../../;
+ };
+ name = Debug;
+ };
+ F93803C10F808210004D428B /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ GCC_MODEL_TUNING = G5;
+ INSTALL_PATH = /usr/local/bin;
+ OTHER_LDFLAGS = (
+ "-lcrypto",
+ "-framework",
+ Foundation,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = generator_test;
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ F93DE2D30F82A67400608B94 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_CHAR_IS_UNSIGNED_CHAR = YES;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ INSTALL_PATH = /usr/local/bin;
+ OTHER_LDFLAGS = (
+ "-framework",
+ Foundation,
+ "-framework",
+ AppKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = minidump_file_writer_unittest;
+ USER_HEADER_SEARCH_PATHS = ../../;
+ };
+ name = Debug;
+ };
+ F93DE2D40F82A67400608B94 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ GCC_MODEL_TUNING = G5;
+ INSTALL_PATH = /usr/local/bin;
+ OTHER_LDFLAGS = (
+ "-framework",
+ Foundation,
+ "-framework",
+ AppKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = minidump_file_writer_unittest;
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ F93DE32E0F82C55700608B94 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ INSTALL_PATH = /usr/local/bin;
+ OTHER_LDFLAGS = (
+ "-lcrypto",
+ "-framework",
+ Foundation,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = handler_test;
+ USER_HEADER_SEARCH_PATHS = ../../;
+ };
+ name = Debug;
+ };
+ F93DE32F0F82C55700608B94 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ GCC_MODEL_TUNING = G5;
+ INSTALL_PATH = /usr/local/bin;
+ OTHER_LDFLAGS = (
+ "-lcrypto",
+ "-framework",
+ Foundation,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = handler_test;
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ F93DE3B90F830E7000608B94 /* Debug With Code Coverage */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ONLY_ACTIVE_ARCH_PRE_XCODE_3_1)";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ ONLY_ACTIVE_ARCH_PRE_XCODE_3_1 = "$(NATIVE_ARCH)";
+ PREBINDING = NO;
+ SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk";
+ };
+ name = "Debug With Code Coverage";
+ };
+ F93DE3BA0F830E7000608B94 /* Debug With Code Coverage */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_VERSION = A;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = Framework/Breakpad_Prefix.pch;
+ HEADER_SEARCH_PATHS = "../..//**";
+ INFOPLIST_FILE = Framework/Info.plist;
+ INSTALL_PATH = "@executable_path/../Frameworks";
+ PRODUCT_NAME = Breakpad;
+ WRAPPER_EXTENSION = framework;
+ };
+ name = "Debug With Code Coverage";
+ };
+ F93DE3BB0F830E7000608B94 /* Debug With Code Coverage */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ HEADER_SEARCH_PATHS = "../..//**";
+ INSTALL_PATH = /usr/local/bin;
+ OTHER_LDFLAGS = (
+ "-lcrypto",
+ "$(inherited)",
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = Inspector;
+ SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk";
+ };
+ name = "Debug With Code Coverage";
+ };
+ F93DE3BC0F830E7000608B94 /* Debug With Code Coverage */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ HEADER_SEARCH_PATHS = "../..//**";
+ INSTALL_PATH = /usr/local/lib;
+ LD_DYLIB_INSTALL_NAME = "@executable_path/../Resources/$(EXECUTABLE_PATH)";
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-headerpad_max_install_names",
+ "-lcrypto",
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = breakpadUtilities;
+ SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk";
+ };
+ name = "Debug With Code Coverage";
+ };
+ F93DE3BD0F830E7000608B94 /* Debug With Code Coverage */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ HEADER_SEARCH_PATHS = "../..//**";
+ INFOPLIST_FILE = "sender/crash_report_sender-Info.plist";
+ INSTALL_PATH = "$(HOME)/Applications";
+ OTHER_LDFLAGS = (
+ "-framework",
+ Foundation,
+ "-framework",
+ AppKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = crash_report_sender;
+ };
+ name = "Debug With Code Coverage";
+ };
+ F93DE3BE0F830E7000608B94 /* Debug With Code Coverage */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/build/$(CONFIGURATION)";
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h";
+ INFOPLIST_FILE = testapp/Info.plist;
+ INSTALL_PATH = "$(HOME)/Applications";
+ OTHER_LDFLAGS = (
+ "-framework",
+ Foundation,
+ "-framework",
+ AppKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = BreakpadTest;
+ };
+ name = "Debug With Code Coverage";
+ };
+ F93DE3BF0F830E7000608B94 /* Debug With Code Coverage */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ PRODUCT_NAME = All;
+ };
+ name = "Debug With Code Coverage";
+ };
+ F93DE3C00F830E7000608B94 /* Debug With Code Coverage */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_GENERATE_TEST_COVERAGE_FILES = YES;
+ GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Cocoa.framework/Headers/Cocoa.h";
+ INFOPLIST_FILE = "UnitTests-Info.plist";
+ INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles";
+ LIBRARY_SEARCH_PATHS = ./gcov;
+ OTHER_LDFLAGS = (
+ "-lgcov",
+ "-framework",
+ Cocoa,
+ "-framework",
+ SenTestingKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = UnitTests;
+ USER_HEADER_SEARCH_PATHS = "../../ ../../common/mac/**";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = "Debug With Code Coverage";
+ };
+ F93DE3C10F830E7000608B94 /* Debug With Code Coverage */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_GENERATE_TEST_COVERAGE_FILES = YES;
+ GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ INSTALL_PATH = /usr/local/bin;
+ LIBRARY_SEARCH_PATHS = ./gcov;
+ OTHER_LDFLAGS = (
+ "-lgcov",
+ "-lcrypto",
+ "-framework",
+ Foundation,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = generator_test;
+ USER_HEADER_SEARCH_PATHS = ../../;
+ };
+ name = "Debug With Code Coverage";
+ };
+ F93DE3C20F830E7000608B94 /* Debug With Code Coverage */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_CHAR_IS_UNSIGNED_CHAR = YES;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_GENERATE_TEST_COVERAGE_FILES = YES;
+ GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ INSTALL_PATH = /usr/local/bin;
+ LIBRARY_SEARCH_PATHS = ./gcov;
+ OTHER_LDFLAGS = (
+ "-lgcov",
+ "-framework",
+ Foundation,
+ "-framework",
+ AppKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = minidump_file_writer_unittest;
+ USER_HEADER_SEARCH_PATHS = ../../;
+ };
+ name = "Debug With Code Coverage";
+ };
+ F93DE3C30F830E7000608B94 /* Debug With Code Coverage */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_GENERATE_TEST_COVERAGE_FILES = YES;
+ GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ INSTALL_PATH = /usr/local/bin;
+ LIBRARY_SEARCH_PATHS = ./gcov;
+ OTHER_LDFLAGS = (
+ "-lcrypto",
+ "-lgcov",
+ "-framework",
+ Foundation,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = handler_test;
+ USER_HEADER_SEARCH_PATHS = ../../;
+ };
+ name = "Debug With Code Coverage";
+ };
+ F94585850F782326009A47BF /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ PRODUCT_NAME = All;
+ };
+ name = Debug;
+ };
+ F94585860F782326009A47BF /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ PRODUCT_NAME = All;
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ F9C44DA80EF060A8003AEBAA /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/build/$(CONFIGURATION)";
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h";
+ INFOPLIST_FILE = testapp/Info.plist;
+ INSTALL_PATH = "$(HOME)/Applications";
+ OTHER_LDFLAGS = (
+ "-framework",
+ Foundation,
+ "-framework",
+ AppKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = BreakpadTest;
+ };
+ name = Debug;
+ };
+ F9C44DA90EF060A8003AEBAA /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/build/$(CONFIGURATION)";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ INFOPLIST_FILE = testapp/Info.plist;
+ INSTALL_PATH = "$(HOME)/Applications";
+ OTHER_LDFLAGS = (
+ "-framework",
+ Foundation,
+ "-framework",
+ AppKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = BreakpadTest;
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+ F9C77DDC0F7DD5D00045F7DB /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Cocoa.framework/Headers/Cocoa.h";
+ INFOPLIST_FILE = "UnitTests-Info.plist";
+ INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles";
+ OTHER_LDFLAGS = (
+ "-framework",
+ Cocoa,
+ "-framework",
+ SenTestingKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = UnitTests;
+ USER_HEADER_SEARCH_PATHS = "../../ ../../common/mac/**";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Debug;
+ };
+ F9C77DDD0F7DD5D00045F7DB /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_MODEL_TUNING = G5;
+ INFOPLIST_FILE = "UnitTests-Info.plist";
+ INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles";
+ OTHER_LDFLAGS = (
+ "-framework",
+ Cocoa,
+ "-framework",
+ SenTestingKit,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = UnitTests;
+ WRAPPER_EXTENSION = octest;
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB91AE08733DA50010E9CD /* Debug */,
+ F93DE3BA0F830E7000608B94 /* Debug With Code Coverage */,
+ 1DEB91AF08733DA50010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Breakpad" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB91B208733DA50010E9CD /* Debug */,
+ F93DE3B90F830E7000608B94 /* Debug With Code Coverage */,
+ 1DEB91B308733DA50010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F92C53580ECCE36D009BE4BA /* Build configuration list for PBXNativeTarget "Inspector" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F92C53560ECCE34A009BE4BA /* Debug */,
+ F93DE3BB0F830E7000608B94 /* Debug With Code Coverage */,
+ F92C53570ECCE34A009BE4BA /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F92C56670ECD11A3009BE4BA /* Build configuration list for PBXNativeTarget "breakpadUtilities" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F92C563D0ECD10B3009BE4BA /* Debug */,
+ F93DE3BC0F830E7000608B94 /* Debug With Code Coverage */,
+ F92C563E0ECD10B3009BE4BA /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F92C56A50ECE04A8009BE4BA /* Build configuration list for PBXNativeTarget "crash_report_sender" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F92C56A30ECE04A8009BE4BA /* Debug */,
+ F93DE3BD0F830E7000608B94 /* Debug With Code Coverage */,
+ F92C56A40ECE04A8009BE4BA /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F93803C40F80822E004D428B /* Build configuration list for PBXNativeTarget "generator_test" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F93803C00F808210004D428B /* Debug */,
+ F93DE3C10F830E7000608B94 /* Debug With Code Coverage */,
+ F93803C10F808210004D428B /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F93DE2D60F82A67700608B94 /* Build configuration list for PBXNativeTarget "minidump_file_writer_unittest" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F93DE2D30F82A67400608B94 /* Debug */,
+ F93DE3C20F830E7000608B94 /* Debug With Code Coverage */,
+ F93DE2D40F82A67400608B94 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F93DE3320F82C5D800608B94 /* Build configuration list for PBXNativeTarget "handler_test" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F93DE32E0F82C55700608B94 /* Debug */,
+ F93DE3C30F830E7000608B94 /* Debug With Code Coverage */,
+ F93DE32F0F82C55700608B94 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F94585930F78235C009A47BF /* Build configuration list for PBXAggregateTarget "All" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F94585850F782326009A47BF /* Debug */,
+ F93DE3BF0F830E7000608B94 /* Debug With Code Coverage */,
+ F94585860F782326009A47BF /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F9C44DAA0EF060A9003AEBAA /* Build configuration list for PBXNativeTarget "BreakpadTest" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F9C44DA80EF060A8003AEBAA /* Debug */,
+ F93DE3BE0F830E7000608B94 /* Debug With Code Coverage */,
+ F9C44DA90EF060A8003AEBAA /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F9C77DDE0F7DD5D00045F7DB /* Build configuration list for PBXNativeTarget "UnitTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F9C77DDC0F7DD5D00045F7DB /* Debug */,
+ F93DE3C00F830E7000608B94 /* Debug With Code Coverage */,
+ F9C77DDD0F7DD5D00045F7DB /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 0867D690FE84028FC02AAC07 /* Project object */;
+}
diff --git a/src/client/mac/Framework/Breakpad.h b/src/client/mac/Framework/Breakpad.h
new file mode 100644
index 00000000..05005a99
--- /dev/null
+++ b/src/client/mac/Framework/Breakpad.h
@@ -0,0 +1,181 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Framework to provide a simple C API to crash reporting for
+// applications. By default, if any machine-level exception (e.g.,
+// EXC_BAD_ACCESS) occurs, it will be handled by the BreakpadRef
+// object as follows:
+//
+// 1. Create a minidump file (see Breakpad for details)
+// 2. Prompt the user (using CFUserNotification)
+// 3. Invoke a command line reporting tool to send the minidump to a
+// server
+//
+// By specifying parameters to the BreakpadCreate function, you can
+// modify the default behavior to suit your needs and wants and
+// desires.
+
+typedef void *BreakpadRef;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Foundation/Foundation.h>
+
+ // Keys for configuration file
+#define kReporterMinidumpDirectoryKey "MinidumpDir"
+#define kReporterMinidumpIDKey "MinidumpID"
+
+// Specify some special keys to be used in the configuration file that is
+// generated by Breakpad and consumed by the crash_sender.
+#define BREAKPAD_PRODUCT_DISPLAY "BreakpadProductDisplay"
+#define BREAKPAD_PRODUCT "BreakpadProduct"
+#define BREAKPAD_VENDOR "BreakpadVendor"
+#define BREAKPAD_VERSION "BreakpadVersion"
+#define BREAKPAD_URL "BreakpadURL"
+#define BREAKPAD_REPORT_INTERVAL "BreakpadReportInterval"
+#define BREAKPAD_SKIP_CONFIRM "BreakpadSkipConfirm"
+#define BREAKPAD_SEND_AND_EXIT "BreakpadSendAndExit"
+#define BREAKPAD_INSPECTOR_LOCATION "BreakpadInspectorLocation"
+
+#define BREAKPAD_REPORTER_EXE_LOCATION \
+ "BreakpadReporterExeLocation"
+#define BREAKPAD_LOGFILES "BreakpadLogFiles"
+#define BREAKPAD_LOGFILE_UPLOAD_SIZE "BreakpadLogFileTailSize"
+#define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile"
+#define BREAKPAD_EMAIL "BreakpadEmail"
+#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments"
+#define BREAKPAD_COMMENTS "BreakpadComments"
+
+// Optional user-defined function to dec to decide if we should handle
+// this crash or forward it along.
+// Return true if you want Breakpad to handle it.
+// Return false if you want Breakpad to skip it
+// The exception handler always returns false, as if SEND_AND_EXIT were false
+// (which means the next exception handler will take the exception)
+typedef bool (*BreakpadFilterCallback)(int exception_type,
+ int exception_code,
+ mach_port_t crashing_thread);
+
+// Create a new BreakpadRef object and install it as an
+// exception handler. The |parameters| will typically be the contents
+// of your bundle's Info.plist.
+//
+// You can also specify these additional keys for customizable behavior:
+// Key: Value:
+// BREAKPAD_PRODUCT Product name (e.g., "MyAwesomeProduct")
+// This one is used as the key to identify
+// the product when uploading
+// BREAKPAD_PRODUCT_DISPLAY This is the display name, e.g. a pretty
+// name for the product when the crash_sender
+// pops up UI for the user. Falls back to
+// BREAKPAD_PRODUCT if not specified.
+// BREAKPAD_VERSION Product version (e.g., 1.2.3), used
+// as metadata for crash report
+// BREAKPAD_VENDOR Vendor named, used in UI (e.g. the Xxxx
+// foo bar company product widget has crashed)
+// BREAKPAD_URL URL destination for reporting
+// BREAKPAD_REPORT_INTERVAL # of seconds between sending
+// reports. If an additional report is
+// generated within this time, it will
+// be ignored. Default: 3600sec.
+// Specify 0 to send all reports.
+// BREAKPAD_SKIP_CONFIRM If true, the reporter will send the report
+// without any user intervention.
+// BREAKPAD_SEND_AND_EXIT If true, the handler will exit after sending.
+// This will prevent any other handler (e.g.,
+// CrashReporter) from getting the crash.
+// BREAKPAD_INSPECTOR_LOCATION The full path to the Inspector executable.
+// BREAKPAD_REPORTER_EXE_LOCATION The full path to the Reporter/sender
+// executable.
+// BREAKPAD_LOGFILES Indicates an array of log file paths that
+// should be uploaded at crash time
+// BREAKPAD_REQUEST_COMMENTS If true, the message dialog will have a
+// text box for the user to enter comments as
+// well as a name and email address.
+// BREAKPAD_COMMENTS The text the user provided as comments.
+//
+// The BREAKPAD_PRODUCT and BREAKPAD_VERSION are required to have non-
+// NULL values. By default, the BREAKPAD_PRODUCT will be the
+// CFBundleName and the BREAKPAD_VERSION will be the CFBundleVersion
+// when these keys are present in the bundle's Info.plist. If the
+// BREAKPAD_PRODUCT or BREAKPAD_VERSION are ultimately undefined,
+// BreakpadCreate() will fail. You have been warned.
+//
+// If you are running in a debugger, breakpad will not install, unless the
+// BREAKPAD_IGNORE_DEBUGGER envionment variable is set and/or non-zero.
+//
+// The BREAKPAD_SKIP_CONFIRM and BREAKPAD_SEND_AND_EXIT default
+// values are NO and YES. However, they can be controlled by setting their
+// values in a user or global plist.
+//
+// It's easiest to use Breakpad via the Framework, but if you're compiling the
+// code in directly, BREAKPAD_INSPECTOR_LOCATION and
+// BREAKPAD_REPORTER_EXE_LOCATION allow you to specify custom paths
+// to the helper executables.
+//
+
+// Returns a new BreakpadRef object on success, NULL otherwise.
+BreakpadRef BreakpadCreate(NSDictionary *parameters);
+
+// Uninstall and release the data associated with |ref|.
+void BreakpadRelease(BreakpadRef ref);
+
+// Clients may set an optional callback which gets called when a crash occurs.
+// The callback function should return |true| if we should handle the crash,
+// generate a crash report, etc. or |false| if we should ignore it and forward
+// the crash (normally to CrashReporter)
+void BreakpadSetFilterCallback(BreakpadRef ref,
+ BreakpadFilterCallback callback);
+
+// User defined key and value string storage
+// All set keys will be uploaded with the minidump if a crash occurs
+// Keys and Values are limited to 255 bytes (256 - 1 for terminator).
+// NB this is BYTES not GLYPHS.
+// Anything longer than 255 bytes will be truncated. Note that the string is
+// converted to UTF8 before truncation, so any multibyte character that
+// straddles the 255 byte limit will be mangled.
+//
+// A maximum number of 64 key/value pairs are supported. An assert() will fire
+// if more than this number are set.
+void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value);
+NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key);
+void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key);
+
+// Add a log file for Breakpad to read and send upon crash dump
+void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname);
+
+// Generate a minidump and send
+void BreakpadGenerateAndSendReport(BreakpadRef ref);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/src/client/mac/Framework/Breakpad.mm b/src/client/mac/Framework/Breakpad.mm
new file mode 100644
index 00000000..52301c72
--- /dev/null
+++ b/src/client/mac/Framework/Breakpad.mm
@@ -0,0 +1,915 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#define VERBOSE 0
+
+#if VERBOSE
+ static bool gDebugLog = true;
+#else
+ static bool gDebugLog = false;
+#endif
+
+#define DEBUGLOG if (gDebugLog) fprintf
+#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
+
+#import "client/mac/Framework/Breakpad.h"
+#import "client/mac/crash_generation/Inspector.h"
+#import "client/mac/Framework/OnDemandServer.h"
+#import "client/mac/handler/protected_memory_allocator.h"
+#import "common/mac/MachIPC.h"
+#import "common/mac/SimpleStringDictionary.h"
+
+#import <sys/stat.h>
+#import <sys/sysctl.h>
+
+#import <Foundation/Foundation.h>
+
+#import "exception_handler.h"
+#import "string_utilities.h"
+
+using google_breakpad::KeyValueEntry;
+using google_breakpad::SimpleStringDictionary;
+using google_breakpad::SimpleStringDictionaryIterator;
+
+//=============================================================================
+// We want any memory allocations which are used by breakpad during the
+// exception handling process (after a crash has happened) to be read-only
+// to prevent them from being smashed before a crash occurs. Unfortunately
+// we cannot protect against smashes to our exception handling thread's
+// stack.
+//
+// NOTE: Any memory allocations which are not used during the exception
+// handling process may be allocated in the normal ways.
+//
+// The ProtectedMemoryAllocator class provides an Allocate() method which
+// we'll using in conjunction with placement operator new() to control
+// allocation of C++ objects. Note that we don't use operator delete()
+// but instead call the objects destructor directly: object->~ClassName();
+//
+ProtectedMemoryAllocator *gMasterAllocator = NULL;
+ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
+ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
+
+// Mutex for thread-safe access to the key/value dictionary used by breakpad.
+// It's a global instead of an instance variable of Breakpad
+// since it can't live in a protected memory area.
+pthread_mutex_t gDictionaryMutex;
+
+//=============================================================================
+// Stack-based object for thread-safe access to a memory-protected region.
+// It's assumed that normally the memory block (allocated by the allocator)
+// is protected (read-only). Creating a stack-based instance of
+// ProtectedMemoryLocker will unprotect this block after taking the lock.
+// Its destructor will first re-protect the memory then release the lock.
+class ProtectedMemoryLocker {
+public:
+ // allocator may be NULL, in which case no Protect() or Unprotect() calls
+ // will be made, but a lock will still be taken
+ ProtectedMemoryLocker(pthread_mutex_t *mutex,
+ ProtectedMemoryAllocator *allocator)
+ : mutex_(mutex), allocator_(allocator) {
+ // Lock the mutex
+ assert(pthread_mutex_lock(mutex_) == 0);
+
+ // Unprotect the memory
+ if (allocator_ ) {
+ allocator_->Unprotect();
+ }
+ }
+
+ ~ProtectedMemoryLocker() {
+ // First protect the memory
+ if (allocator_) {
+ allocator_->Protect();
+ }
+
+ // Then unlock the mutex
+ assert(pthread_mutex_unlock(mutex_) == 0);
+ };
+
+private:
+ // Keep anybody from ever creating one of these things not on the stack.
+ ProtectedMemoryLocker() { }
+ ProtectedMemoryLocker(const ProtectedMemoryLocker&);
+ ProtectedMemoryLocker & operator=(ProtectedMemoryLocker&);
+
+ pthread_mutex_t *mutex_;
+ ProtectedMemoryAllocator *allocator_;
+};
+
+//=============================================================================
+class Breakpad {
+ public:
+ // factory method
+ static Breakpad *Create(NSDictionary *parameters) {
+ // Allocate from our special allocation pool
+ Breakpad *breakpad =
+ new (gBreakpadAllocator->Allocate(sizeof(Breakpad)))
+ Breakpad();
+
+ if (!breakpad)
+ return NULL;
+
+ if (!breakpad->Initialize(parameters)) {
+ // Don't use operator delete() here since we allocated from special pool
+ breakpad->~Breakpad();
+ return NULL;
+ }
+
+ return breakpad;
+ }
+
+ ~Breakpad();
+
+ void SetKeyValue(NSString *key, NSString *value);
+ NSString *KeyValue(NSString *key);
+ void RemoveKeyValue(NSString *key);
+
+ void GenerateAndSendReport();
+
+ void SetFilterCallback(BreakpadFilterCallback callback) {
+ filter_callback_ = callback;
+ }
+
+ private:
+ Breakpad()
+ : handler_(NULL),
+ config_params_(NULL),
+ send_and_exit_(true),
+ filter_callback_(NULL) {
+ inspector_path_[0] = 0;
+ }
+
+ bool Initialize(NSDictionary *parameters);
+
+ bool ExtractParameters(NSDictionary *parameters);
+
+ // Dispatches to HandleException()
+ static bool ExceptionHandlerDirectCallback(void *context,
+ int exception_type,
+ int exception_code,
+ mach_port_t crashing_thread);
+
+ bool HandleException(int exception_type,
+ int exception_code,
+ mach_port_t crashing_thread);
+
+ // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's
+ // MachineExceptions.h, we have to explicitly name the handler.
+ google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG)
+
+ char inspector_path_[PATH_MAX]; // Path to inspector tool
+
+ SimpleStringDictionary *config_params_; // Create parameters (STRONG)
+
+ OnDemandServer inspector_;
+
+ bool send_and_exit_; // Exit after sending, if true
+
+ BreakpadFilterCallback filter_callback_;
+
+ unsigned int logFileCounter;
+};
+
+#pragma mark -
+#pragma mark Helper functions
+
+//=============================================================================
+// Helper functions
+
+//=============================================================================
+static BOOL IsDebuggerActive() {
+ BOOL result = NO;
+ NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
+
+ // We check both defaults and the environment variable here
+
+ BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER];
+
+ if (!ignoreDebugger) {
+ char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER);
+ ignoreDebugger = (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0;
+ }
+
+ if (!ignoreDebugger) {
+ pid_t pid = getpid();
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+ int mibSize = sizeof(mib) / sizeof(int);
+ size_t actualSize;
+
+ if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) {
+ struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize);
+
+ if (info) {
+ // This comes from looking at the Darwin xnu Kernel
+ if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0)
+ result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO;
+
+ free(info);
+ }
+ }
+ }
+
+ return result;
+}
+
+//=============================================================================
+bool Breakpad::ExceptionHandlerDirectCallback(void *context,
+ int exception_type,
+ int exception_code,
+ mach_port_t crashing_thread) {
+ Breakpad *breakpad = (Breakpad *)context;
+
+ // If our context is damaged or something, just return false to indicate that
+ // the handler should continue without us.
+ if (!breakpad)
+ return false;
+
+ return breakpad->HandleException( exception_type,
+ exception_code,
+ crashing_thread);
+}
+
+//=============================================================================
+#pragma mark -
+
+#include <mach-o/dyld.h>
+
+//=============================================================================
+// Returns the pathname to the Resources directory for this version of
+// Breakpad which we are now running.
+//
+// Don't make the function static, since _dyld_lookup_and_bind_fully needs a
+// simple non-static C name
+//
+extern "C" {
+NSString * GetResourcePath();
+NSString * GetResourcePath() {
+ NSString *resourcePath = nil;
+
+ // If there are multiple breakpads installed then calling bundleWithIdentifier
+ // will not work properly, so only use that as a backup plan.
+ // We want to find the bundle containing the code where this function lives
+ // and work from there
+ //
+
+ // Get the pathname to the code which contains this function
+ void *address = nil;
+ NSModule module = nil;
+ _dyld_lookup_and_bind_fully("_GetResourcePath",
+ &address,
+ &module);
+
+ if (module && address) {
+ const char* moduleName = NSNameOfModule(module);
+ if (moduleName) {
+ // The "Resources" directory should be in the same directory as the
+ // executable code, since that's how the Breakpad framework is built.
+ resourcePath = [NSString stringWithUTF8String:moduleName];
+ resourcePath = [resourcePath stringByDeletingLastPathComponent];
+ resourcePath = [resourcePath stringByAppendingPathComponent:@"Resources/"];
+ } else {
+ DEBUGLOG(stderr, "Missing moduleName\n");
+ }
+ } else {
+ DEBUGLOG(stderr, "Could not find GetResourcePath\n");
+ // fallback plan
+ NSBundle *bundle =
+ [NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"];
+ resourcePath = [bundle resourcePath];
+ }
+
+ return resourcePath;
+}
+} // extern "C"
+
+//=============================================================================
+bool Breakpad::Initialize(NSDictionary *parameters) {
+ // Initialize
+ config_params_ = NULL;
+ handler_ = NULL;
+
+ // Check for debugger
+ if (IsDebuggerActive()) {
+ DEBUGLOG(stderr, "Debugger is active: Not installing handler\n");
+ return true;
+ }
+
+ // Gather any user specified parameters
+ if (!ExtractParameters(parameters)) {
+ return false;
+ }
+
+ // Get path to Inspector executable.
+ NSString *inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION);
+
+ // Standardize path (resolve symlinkes, etc.) and escape spaces
+ inspectorPathString = [inspectorPathString stringByStandardizingPath];
+ inspectorPathString = [[inspectorPathString componentsSeparatedByString:@" "]
+ componentsJoinedByString:@"\\ "];
+
+ // Create an on-demand server object representing the Inspector.
+ // In case of a crash, we simply need to call the LaunchOnDemand()
+ // method on it, then send a mach message to its service port.
+ // It will then launch and perform a process inspection of our crashed state.
+ // See the HandleException() method for the details.
+#define RECEIVE_PORT_NAME "com.Breakpad.Inspector"
+
+ name_t portName;
+ snprintf(portName, sizeof(name_t), "%s%d", RECEIVE_PORT_NAME, getpid());
+
+ // Save the location of the Inspector
+ strlcpy(inspector_path_, [inspectorPathString fileSystemRepresentation],
+ sizeof(inspector_path_));
+
+ // Append a single command-line argument to the Inspector path
+ // representing the bootstrap name of the launch-on-demand receive port.
+ // When the Inspector is launched, it can use this to lookup the port
+ // by calling bootstrap_check_in().
+ strlcat(inspector_path_, " ", sizeof(inspector_path_));
+ strlcat(inspector_path_, portName, sizeof(inspector_path_));
+
+ kern_return_t kr = inspector_.Initialize(inspector_path_,
+ portName,
+ true); // shutdown on exit
+
+ if (kr != KERN_SUCCESS) {
+ return false;
+ }
+
+ // Create the handler (allocating it in our special protected pool)
+ handler_ =
+ new (gBreakpadAllocator->Allocate(sizeof(google_breakpad::ExceptionHandler)))
+ google_breakpad::ExceptionHandler(
+ Breakpad::ExceptionHandlerDirectCallback, this, true);
+
+ return true;
+}
+
+//=============================================================================
+Breakpad::~Breakpad() {
+ // Note that we don't use operator delete() on these pointers,
+ // since they were allocated by ProtectedMemoryAllocator objects.
+ //
+ if (config_params_) {
+ config_params_->~SimpleStringDictionary();
+ }
+
+ if (handler_)
+ handler_->~ExceptionHandler();
+}
+
+//=============================================================================
+bool Breakpad::ExtractParameters(NSDictionary *parameters) {
+ NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
+ NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
+ NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
+ NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
+ NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
+ NSString *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL];
+ NSString *inspectorPathString =
+ [parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION];
+ NSString *reporterPathString =
+ [parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION];
+ NSString *skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM];
+ NSString *sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT];
+ NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES];
+ NSString *logFileTailSize = [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE];
+ NSString *reportEmail = [parameters objectForKey:@BREAKPAD_EMAIL];
+ NSString *requestUserText =
+ [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS];
+ NSString *vendor =
+ [parameters objectForKey:@BREAKPAD_VENDOR];
+
+ // If these two are not already set(skipConfirm and sendAndExit can
+ // come from user defaults and take priority)
+ if (!skipConfirm) {
+ skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM];
+ }
+ if (!sendAndExit) {
+ sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT];
+ }
+
+ if (!product)
+ product = [parameters objectForKey:@"CFBundleName"];
+
+ if (!display)
+ display = product;
+
+ if (!version)
+ version = [parameters objectForKey:@"CFBundleVersion"];
+
+ if (!interval)
+ interval = @"3600";
+
+ if (!logFileTailSize)
+ logFileTailSize = @"200000";
+
+ if (!vendor) {
+ vendor = @"Vendor not specified";
+ }
+
+ // Normalize the values
+ if (skipConfirm) {
+ skipConfirm = [skipConfirm uppercaseString];
+
+ if ([skipConfirm isEqualToString:@"YES"] ||
+ [skipConfirm isEqualToString:@"TRUE"] ||
+ [skipConfirm isEqualToString:@"1"])
+ skipConfirm = @"YES";
+ else
+ skipConfirm = @"NO";
+ } else {
+ skipConfirm = @"NO";
+ }
+
+ send_and_exit_ = true;
+ if (sendAndExit) {
+ sendAndExit = [sendAndExit uppercaseString];
+
+ if ([sendAndExit isEqualToString:@"NO"] ||
+ [sendAndExit isEqualToString:@"FALSE"] ||
+ [sendAndExit isEqualToString:@"0"])
+ send_and_exit_ = false;
+ }
+
+ if (requestUserText) {
+ requestUserText = [requestUserText uppercaseString];
+
+ if ([requestUserText isEqualToString:@"YES"] ||
+ [requestUserText isEqualToString:@"TRUE"] ||
+ [requestUserText isEqualToString:@"1"])
+ requestUserText = @"YES";
+ else
+ requestUserText = @"NO";
+ } else {
+ requestUserText = @"NO";
+ }
+
+ // Find the helper applications if not specified in user config.
+ NSString *resourcePath = nil;
+ if (!inspectorPathString || !reporterPathString) {
+ resourcePath = GetResourcePath();
+ if (!resourcePath) {
+ DEBUGLOG(stderr, "Could not get resource path\n");
+ return false;
+ }
+ }
+
+ // Find Inspector.
+ if (!inspectorPathString) {
+ inspectorPathString =
+ [resourcePath stringByAppendingPathComponent:@"Inspector"];
+ }
+
+ // Verify that there is an Inspector tool
+ if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) {
+ DEBUGLOG(stderr, "Cannot find Inspector tool\n");
+ return false;
+ }
+
+ // Find Reporter.
+ if (!reporterPathString) {
+ reporterPathString =
+ [resourcePath stringByAppendingPathComponent:@"crash_report_sender.app"];
+ reporterPathString = [[NSBundle bundleWithPath:reporterPathString] executablePath];
+ }
+
+ // Verify that there is a Reporter application
+ if (![[NSFileManager defaultManager]
+ fileExistsAtPath:reporterPathString]) {
+ DEBUGLOG(stderr, "Cannot find Reporter tool\n");
+ return false;
+ }
+
+ // The product and version are required values
+ if (![product length] || ![version length]) {
+ DEBUGLOG(stderr, "Missing required product and/or version keys\n");
+ return false;
+ }
+
+ config_params_ =
+ new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
+ SimpleStringDictionary();
+
+ SimpleStringDictionary &dictionary = *config_params_;
+
+ dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
+ dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]);
+ dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]);
+ dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]);
+ dictionary.SetKeyValue(BREAKPAD_REPORT_INTERVAL, [interval UTF8String]);
+ dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM, [skipConfirm UTF8String]);
+ dictionary.SetKeyValue(BREAKPAD_INSPECTOR_LOCATION,
+ [inspectorPathString fileSystemRepresentation]);
+ dictionary.SetKeyValue(BREAKPAD_REPORTER_EXE_LOCATION,
+ [reporterPathString fileSystemRepresentation]);
+ dictionary.SetKeyValue(BREAKPAD_LOGFILE_UPLOAD_SIZE,
+ [logFileTailSize UTF8String]);
+ dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS,
+ [requestUserText UTF8String]);
+ dictionary.SetKeyValue(BREAKPAD_VENDOR,
+ [vendor UTF8String]);
+
+ if (logFilePaths) {
+ char logFileKey[255];
+ for(unsigned int i = 0; i < [logFilePaths count]; i++) {
+ sprintf(logFileKey,"%s%d", BREAKPAD_LOGFILE_KEY_PREFIX, i);
+ dictionary.SetKeyValue(logFileKey, [[logFilePaths objectAtIndex:i] fileSystemRepresentation]);
+ }
+ }
+
+ if (reportEmail) {
+ dictionary.SetKeyValue(BREAKPAD_EMAIL,
+ [reportEmail UTF8String]);
+ }
+#if 0 // for testing
+ BreakpadSetKeyValue(this, @"UserKey1", @"User Value 1");
+ BreakpadSetKeyValue(this, @"UserKey2", @"User Value 2");
+ BreakpadSetKeyValue(this, @"UserKey3", @"User Value 3");
+ BreakpadSetKeyValue(this, @"UserKey4", @"User Value 4");
+#endif
+
+ return true;
+}
+
+//=============================================================================
+void Breakpad::SetKeyValue(NSString *key, NSString *value) {
+ // We allow nil values. This is the same as removing the keyvalue.
+ if (!config_params_ || !key)
+ return;
+
+ config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
+}
+
+//=============================================================================
+NSString * Breakpad::KeyValue(NSString *key) {
+ if (!config_params_ || !key)
+ return nil;
+
+ const char *value = config_params_->GetValueForKey([key UTF8String]);
+ return value ? [NSString stringWithUTF8String:value] : nil;
+}
+
+//=============================================================================
+void Breakpad::RemoveKeyValue(NSString *key) {
+ if (!config_params_ || !key)
+ return;
+
+ config_params_->RemoveKey([key UTF8String]);
+}
+
+//=============================================================================
+void Breakpad::GenerateAndSendReport() {
+ HandleException(0, 0, 0);
+}
+
+//=============================================================================
+bool Breakpad::HandleException(int exception_type,
+ int exception_code,
+ mach_port_t crashing_thread) {
+ DEBUGLOG(stderr, "Breakpad: an exception occurred\n");
+
+ if (filter_callback_) {
+ bool should_handle = filter_callback_(exception_type,
+ exception_code,
+ crashing_thread);
+ if (!should_handle) return false;
+ }
+
+ // We need to reset the memory protections to be read/write,
+ // since LaunchOnDemand() requires changing state.
+ gBreakpadAllocator->Unprotect();
+ // Configure the server to launch when we message the service port.
+ // The reason we do this here, rather than at startup, is that we
+ // can leak a bootstrap service entry if this method is called and
+ // there never ends up being a crash.
+ inspector_.LaunchOnDemand();
+ gBreakpadAllocator->Protect();
+
+ // The Inspector should send a message to this port to verify it
+ // received our information and has finished the inspection.
+ ReceivePort acknowledge_port;
+
+ // Send initial information to the Inspector.
+ MachSendMessage message(kMsgType_InspectorInitialInfo);
+ message.AddDescriptor(mach_task_self()); // our task
+ message.AddDescriptor(crashing_thread); // crashing thread
+ message.AddDescriptor(mach_thread_self()); // exception-handling thread
+ message.AddDescriptor(acknowledge_port.GetPort());// message receive port
+
+ InspectorInfo info;
+ info.exception_type = exception_type;
+ info.exception_code = exception_code;
+ info.parameter_count = config_params_->GetCount();
+ message.SetData(&info, sizeof(info));
+
+ MachPortSender sender(inspector_.GetServicePort());
+
+ kern_return_t result = sender.SendMessage(message, 2000);
+
+ if (result == KERN_SUCCESS) {
+ // Now, send a series of key-value pairs to the Inspector.
+ const KeyValueEntry *entry = NULL;
+ SimpleStringDictionaryIterator iter(*config_params_);
+
+ while ( (entry = iter.Next()) ) {
+ KeyValueMessageData keyvalue_data(*entry);
+
+ MachSendMessage keyvalue_message(kMsgType_InspectorKeyValuePair);
+ keyvalue_message.SetData(&keyvalue_data, sizeof(keyvalue_data));
+
+ result = sender.SendMessage(keyvalue_message, 2000);
+
+ if (result != KERN_SUCCESS) {
+ break;
+ }
+ }
+
+ if (result == KERN_SUCCESS) {
+ // Wait for acknowledgement that the inspection has finished.
+ MachReceiveMessage acknowledge_messsage;
+ result = acknowledge_port.WaitForMessage(&acknowledge_messsage, 2000);
+ }
+ }
+
+#if VERBOSE
+ PRINT_MACH_RESULT(result, "Breakpad: SendMessage ");
+ printf("Breakpad: Inspector service port = %#x\n",
+ inspector_.GetServicePort());
+#endif
+
+ // If we don't want any forwarding, return true here to indicate that we've
+ // processed things as much as we want.
+ if (send_and_exit_)
+ return true;
+
+ return false;
+}
+
+//=============================================================================
+//=============================================================================
+
+#pragma mark -
+#pragma mark Public API
+
+//=============================================================================
+BreakpadRef BreakpadCreate(NSDictionary *parameters) {
+ try {
+ // This is confusing. Our two main allocators for breakpad memory are:
+ // - gKeyValueAllocator for the key/value memory
+ // - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other
+ // breakpad allocations which are accessed at exception handling time.
+ //
+ // But in order to avoid these two allocators themselves from being smashed,
+ // we'll protect them as well by allocating them with gMasterAllocator.
+ //
+ // gMasterAllocator itself will NOT be protected, but this doesn't matter,
+ // since once it does its allocations and locks the memory, smashes to itself
+ // don't affect anything we care about.
+ gMasterAllocator =
+ new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
+
+ gKeyValueAllocator =
+ new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
+ ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
+
+ // Create a mutex for use in accessing the SimpleStringDictionary
+ int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
+ if (mutexResult != 0) {
+ throw mutexResult; // caught down below
+ }
+
+ // With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
+ // Let's round up to the nearest page size.
+ //
+ int breakpad_pool_size = 4096;
+
+ /*
+ sizeof(Breakpad)
+ + sizeof(google_breakpad::ExceptionHandler)
+ + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
+ */
+
+ gBreakpadAllocator =
+ new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
+ ProtectedMemoryAllocator(breakpad_pool_size);
+
+ // Stack-based autorelease pool for Breakpad::Create() obj-c code.
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ Breakpad *breakpad = Breakpad::Create(parameters);
+
+ if (breakpad) {
+ // Make read-only to protect against memory smashers
+ gMasterAllocator->Protect();
+ gKeyValueAllocator->Protect();
+ gBreakpadAllocator->Protect();
+ } else {
+ [pool release];
+#ifdef __EXCEPTIONS
+ throw(-1);
+#else
+ return NULL;
+#endif
+ }
+
+ // Can uncomment this line to figure out how much space was actually
+ // allocated using this allocator
+ // printf("gBreakpadAllocator allocated size = %d\n",
+ // gBreakpadAllocator->GetAllocatedSize() );
+
+ [pool release];
+ return (BreakpadRef)breakpad;
+ } catch(...) { // don't let exception leave this C API
+ if (gKeyValueAllocator) {
+ gKeyValueAllocator->~ProtectedMemoryAllocator();
+ gKeyValueAllocator = NULL;
+ }
+
+ if (gBreakpadAllocator) {
+ gBreakpadAllocator->~ProtectedMemoryAllocator();
+ gBreakpadAllocator = NULL;
+ }
+
+ delete gMasterAllocator;
+ gMasterAllocator = NULL;
+ }
+
+ return NULL;
+}
+
+//=============================================================================
+void BreakpadRelease(BreakpadRef ref) {
+ try {
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (gMasterAllocator) {
+ gMasterAllocator->Unprotect();
+ gKeyValueAllocator->Unprotect();
+ gBreakpadAllocator->Unprotect();
+
+ breakpad->~Breakpad();
+
+ // Unfortunately, it's not possible to deallocate this stuff
+ // because the exception handling thread is still finishing up
+ // asynchronously at this point... OK, it could be done with
+ // locks, etc. But since BreakpadRelease() should usually only
+ // be called right before the process exits, it's not worth
+ // deallocating this stuff.
+#if 0
+ gKeyValueAllocator->~ProtectedMemoryAllocator();
+ gBreakpadAllocator->~ProtectedMemoryAllocator();
+ delete gMasterAllocator;
+
+ gMasterAllocator = NULL;
+ gKeyValueAllocator = NULL;
+ gBreakpadAllocator = NULL;
+#endif
+
+ pthread_mutex_destroy(&gDictionaryMutex);
+ }
+ } catch(...) { // don't let exception leave this C API
+ fprintf(stderr, "BreakpadRelease() : error\n");
+ }
+}
+
+//=============================================================================
+void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
+ try {
+ // Not called at exception time
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (breakpad && key && gKeyValueAllocator) {
+ ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
+
+ breakpad->SetKeyValue(key, value);
+ }
+ } catch(...) { // don't let exception leave this C API
+ fprintf(stderr, "BreakpadSetKeyValue() : error\n");
+ }
+}
+
+//=============================================================================
+NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
+ NSString *value = nil;
+
+ try {
+ // Not called at exception time
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (!breakpad || !key || !gKeyValueAllocator)
+ return nil;
+
+ ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
+
+ value = breakpad->KeyValue(key);
+ } catch(...) { // don't let exception leave this C API
+ fprintf(stderr, "BreakpadKeyValue() : error\n");
+ }
+
+ return value;
+}
+
+//=============================================================================
+void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
+ try {
+ // Not called at exception time
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (breakpad && key && gKeyValueAllocator) {
+ ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
+
+ breakpad->RemoveKeyValue(key);
+ }
+ } catch(...) { // don't let exception leave this C API
+ fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
+ }
+}
+
+//=============================================================================
+void BreakpadGenerateAndSendReport(BreakpadRef ref) {
+ try {
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (breakpad && gKeyValueAllocator) {
+ ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
+
+ gBreakpadAllocator->Unprotect();
+ breakpad->GenerateAndSendReport();
+ gBreakpadAllocator->Protect();
+ }
+ } catch(...) { // don't let exception leave this C API
+ fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n");
+ }
+}
+
+//=============================================================================
+void BreakpadSetFilterCallback(BreakpadRef ref,
+ BreakpadFilterCallback callback) {
+
+ try {
+ Breakpad *breakpad = (Breakpad *)ref;
+
+ if (breakpad && gBreakpadAllocator) {
+ // share the dictionary mutex here (we really don't need a mutex)
+ ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator);
+
+ breakpad->SetFilterCallback(callback);
+ }
+ } catch(...) { // don't let exception leave this C API
+ fprintf(stderr, "BreakpadSetFilterCallback() : error\n");
+ }
+}
+
+//============================================================================
+void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname) {
+ int logFileCounter = 0;
+
+ NSString *logFileKey = [NSString stringWithFormat:@"%@%d",
+ @BREAKPAD_LOGFILE_KEY_PREFIX,
+ logFileCounter];
+
+ NSString *existingLogFilename = nil;
+ existingLogFilename = BreakpadKeyValue(ref, logFileKey);
+ // Find the first log file key that we can use by testing for existence
+ while (existingLogFilename) {
+ if ([existingLogFilename isEqualToString:logPathname]) {
+ return;
+ }
+ logFileCounter++;
+ logFileKey = [NSString stringWithFormat:@"%@%d",
+ @BREAKPAD_LOGFILE_KEY_PREFIX,
+ logFileCounter];
+ existingLogFilename = BreakpadKeyValue(ref, logFileKey);
+ }
+
+ BreakpadSetKeyValue(ref, logFileKey, logPathname);
+
+}
diff --git a/src/client/mac/Framework/Breakpad_Prefix.pch b/src/client/mac/Framework/Breakpad_Prefix.pch
new file mode 100644
index 00000000..72986663
--- /dev/null
+++ b/src/client/mac/Framework/Breakpad_Prefix.pch
@@ -0,0 +1,8 @@
+//
+// Prefix header for all source files of the 'Breakpad' target in the
+// 'Breakpad' project.
+//
+
+#ifdef __OBJC__
+ #import <Cocoa/Cocoa.h>
+#endif
diff --git a/src/client/mac/Framework/English.lproj/InfoPlist.strings b/src/client/mac/Framework/English.lproj/InfoPlist.strings
new file mode 100644
index 00000000..5e45963c
--- /dev/null
+++ b/src/client/mac/Framework/English.lproj/InfoPlist.strings
Binary files differ
diff --git a/src/client/mac/Framework/Info.plist b/src/client/mac/Framework/Info.plist
new file mode 100644
index 00000000..0ec80c38
--- /dev/null
+++ b/src/client/mac/Framework/Info.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.yourcompany.${PRODUCT_NAME:identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>NSPrincipalClass</key>
+ <string></string>
+</dict>
+</plist>
diff --git a/src/client/mac/Framework/OnDemandServer.h b/src/client/mac/Framework/OnDemandServer.h
new file mode 100644
index 00000000..9d8bc961
--- /dev/null
+++ b/src/client/mac/Framework/OnDemandServer.h
@@ -0,0 +1,146 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <iostream>
+#import <mach/mach.h>
+#import <servers/bootstrap.h>
+#import <stdio.h>
+#import <stdlib.h>
+#import <sys/stat.h>
+#import <unistd.h>
+
+//==============================================================================
+// class OnDemandServer :
+// A basic on-demand server launcher supporting a single named service port
+//
+// Example Usage :
+//
+// kern_return_t result;
+// OnDemandServer *server = OnDemandServer::Create("/tmp/myserver",
+// "com.MyCompany.MyServiceName",
+// true,
+// &result);
+//
+// if (server) {
+// server->LaunchOnDemand();
+// mach_port_t service_port = GetServicePort();
+//
+// // Send a mach message to service_port and "myserver" will be launched
+// }
+//
+//
+// ---- Now in the server code ----
+//
+// // "myserver" should get the service port and read the message which
+// // launched it:
+// mach_port_t service_rcv_port_;
+// kern_return_t kr = bootstrap_check_in(bootstrap_port,
+// "com.MyCompany.MyServiceName",
+// &service_rcv_port_);
+// // mach_msg() read service_rcv_port_ ....
+//
+// ....
+//
+// // Later "myserver" may want to unregister the service if it doesn't
+// // want its bootstrap service to stick around after it exits.
+//
+// // DO NOT use mach_port_deallocate() here -- it will fail and the
+// // following bootstrap_register() will also fail leaving our service
+// // name hanging around forever (until reboot)
+// kern_return_t kr = mach_port_destroy(mach_task_self(), service_rcv_port_);
+//
+// kr = bootstrap_register(bootstrap_port,
+// "com.MyCompany.MyServiceName",
+// MACH_PORT_NULL);
+
+class OnDemandServer {
+ public:
+ // must call Initialize() to be useful
+ OnDemandServer()
+ : server_port_(MACH_PORT_NULL),
+ service_port_(MACH_PORT_NULL),
+ unregister_on_cleanup_(true) {
+ }
+
+ // Creates the bootstrap server and service
+ kern_return_t Initialize(const char *server_command,
+ const char *service_name,
+ bool unregister_on_cleanup);
+
+ // Returns an OnDemandServer object if successful, or NULL if there's
+ // an error. The error result will be returned in out_result.
+ //
+ // server_command : the full path name including optional command-line
+ // arguments to the executable representing the server
+ //
+ // service_name : represents service name
+ // something like "com.company.ServiceName"
+ //
+ // unregister_on_cleanup : if true, unregisters the service name
+ // when the OnDemandServer is deleted -- unregistering will
+ // ONLY be possible if LaunchOnDemand() has NOT been called.
+ // If false, then the service will continue to be registered
+ // even after the current process quits.
+ //
+ // out_result : if non-NULL, returns the result
+ // this value will be KERN_SUCCESS if Create() returns non-NULL
+ //
+ static OnDemandServer *Create(const char *server_command,
+ const char *service_name,
+ bool unregister_on_cleanup,
+ kern_return_t *out_result);
+
+ // Cleans up and if LaunchOnDemand() has not yet been called then
+ // the bootstrap service will be unregistered.
+ ~OnDemandServer();
+
+ // This must be called if we intend to commit to launching the server
+ // by sending a mach message to our service port. Do not call it otherwise
+ // or it will be difficult (impossible?) to unregister the service name.
+ void LaunchOnDemand();
+
+ // This is the port we need to send a mach message to after calling
+ // LaunchOnDemand(). Sending a message causing an immediate launch
+ // of the server
+ mach_port_t GetServicePort() { return service_port_; };
+
+ private:
+ // Disallow copy constructor
+ OnDemandServer(const OnDemandServer&);
+
+ // Cleans up and if LaunchOnDemand() has not yet been called then
+ // the bootstrap service will be unregistered.
+ void Unregister();
+
+ name_t service_name_;
+
+ mach_port_t server_port_;
+ mach_port_t service_port_;
+ bool unregister_on_cleanup_;
+};
diff --git a/src/client/mac/Framework/OnDemandServer.mm b/src/client/mac/Framework/OnDemandServer.mm
new file mode 100644
index 00000000..32ac1faa
--- /dev/null
+++ b/src/client/mac/Framework/OnDemandServer.mm
@@ -0,0 +1,145 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "OnDemandServer.h"
+
+#if DEBUG
+ #define PRINT_MACH_RESULT(result_, message_) \
+ printf(message_"%s (%d)\n", mach_error_string(result_), result_ );
+#else
+ #define PRINT_MACH_RESULT(result_, message_)
+#endif
+
+//==============================================================================
+OnDemandServer *OnDemandServer::Create(const char *server_command,
+ const char *service_name,
+ bool unregister_on_cleanup,
+ kern_return_t *out_result) {
+ OnDemandServer *server = new OnDemandServer();
+
+ if (!server) return NULL;
+
+ kern_return_t result = server->Initialize(server_command,
+ service_name,
+ unregister_on_cleanup);
+
+ if (out_result) {
+ *out_result = result;
+ }
+
+ if (result == KERN_SUCCESS) {
+ return server;
+ }
+
+ delete server;
+ return NULL;
+};
+
+//==============================================================================
+kern_return_t OnDemandServer::Initialize(const char *server_command,
+ const char *service_name,
+ bool unregister_on_cleanup) {
+ unregister_on_cleanup_ = unregister_on_cleanup;
+
+ kern_return_t kr =
+ bootstrap_create_server(bootstrap_port,
+ const_cast<char*>(server_command),
+ geteuid(), // server uid
+ true,
+ &server_port_);
+
+ if (kr != KERN_SUCCESS) {
+ PRINT_MACH_RESULT(kr, "bootstrap_create_server() : ");
+ return kr;
+ }
+
+ strlcpy(service_name_, service_name, sizeof(service_name_));
+
+ // Create a service called service_name, and return send rights to
+ // that port in service_port_.
+ kr = bootstrap_create_service(server_port_,
+ const_cast<char*>(service_name),
+ &service_port_);
+
+ if (kr != KERN_SUCCESS) {
+ //PRINT_MACH_RESULT(kr, "bootstrap_create_service() : ");
+
+ // perhaps the service has already been created - try to look it up
+ kr = bootstrap_look_up(bootstrap_port, (char*)service_name, &service_port_);
+
+ if (kr != KERN_SUCCESS) {
+ PRINT_MACH_RESULT(kr, "bootstrap_look_up() : ");
+ Unregister(); // clean up server port
+ return kr;
+ }
+ }
+
+ return KERN_SUCCESS;
+}
+
+//==============================================================================
+OnDemandServer::~OnDemandServer() {
+ if (unregister_on_cleanup_) {
+ Unregister();
+ }
+}
+
+//==============================================================================
+void OnDemandServer::LaunchOnDemand() {
+ // We need to do this, since the launched server is another process
+ // and holding on to this port delays launching until the current process
+ // exits!
+ mach_port_deallocate(mach_task_self(), server_port_);
+ server_port_ = NULL;
+
+ // Now, the service is still registered and all we need to do is send
+ // a mach message to the service port in order to launch the server.
+}
+
+//==============================================================================
+void OnDemandServer::Unregister() {
+ if (service_port_ != MACH_PORT_NULL) {
+ mach_port_deallocate(mach_task_self(), service_port_);
+ service_port_ = MACH_PORT_NULL;
+ }
+
+ if (server_port_ != MACH_PORT_NULL) {
+ // unregister the service
+ kern_return_t kr = bootstrap_register(server_port_,
+ service_name_,
+ MACH_PORT_NULL);
+
+ if (kr != KERN_SUCCESS) {
+ PRINT_MACH_RESULT(kr, "Breakpad UNREGISTER : bootstrap_register() : ");
+ }
+
+ mach_port_deallocate(mach_task_self(), server_port_);
+ server_port_ = MACH_PORT_NULL;
+ }
+}
diff --git a/src/client/mac/UnitTests-Info.plist b/src/client/mac/UnitTests-Info.plist
new file mode 100644
index 00000000..65013556
--- /dev/null
+++ b/src/client/mac/UnitTests-Info.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.yourcompany.${PRODUCT_NAME:identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+</dict>
+</plist>
diff --git a/src/client/mac/crash_generation/Inspector.h b/src/client/mac/crash_generation/Inspector.h
new file mode 100644
index 00000000..38d441fb
--- /dev/null
+++ b/src/client/mac/crash_generation/Inspector.h
@@ -0,0 +1,200 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Interface file between the Breakpad.framework and
+// the Inspector process.
+
+#import "common/mac/SimpleStringDictionary.h"
+
+#import <Foundation/Foundation.h>
+#import "client/mac/handler/minidump_generator.h"
+
+#define VERBOSE 0
+
+extern bool gDebugLog;
+
+#define DEBUGLOG if (gDebugLog) fprintf
+
+// Types of mach messsages (message IDs)
+enum {
+ kMsgType_InspectorInitialInfo = 0, // data is InspectorInfo
+ kMsgType_InspectorKeyValuePair = 1, // data is KeyValueMessageData
+ kMsgType_InspectorAcknowledgement = 2 // no data sent
+};
+
+// Initial information sent from the crashed process by
+// Breakpad.framework to the Inspector process
+// The mach message with this struct as data will also include
+// several descriptors for sending mach port rights to the crashed
+// task, etc.
+struct InspectorInfo {
+ int exception_type;
+ int exception_code;
+ unsigned int parameter_count; // key-value pairs
+};
+
+// Key/value message data to be sent to the Inspector
+struct KeyValueMessageData {
+ public:
+ KeyValueMessageData() {}
+ KeyValueMessageData(const google_breakpad::KeyValueEntry &inEntry) {
+ strlcpy(key, inEntry.GetKey(), sizeof(key) );
+ strlcpy(value, inEntry.GetValue(), sizeof(value) );
+ }
+
+ char key[google_breakpad::KeyValueEntry::MAX_STRING_STORAGE_SIZE];
+ char value[google_breakpad::KeyValueEntry::MAX_STRING_STORAGE_SIZE];
+};
+
+using std::string;
+using google_breakpad::MinidumpGenerator;
+
+namespace google_breakpad {
+
+static BOOL EnsureDirectoryPathExists(NSString *dirPath);
+
+//=============================================================================
+class ConfigFile {
+ public:
+ ConfigFile() {
+ config_file_ = -1;
+ config_file_path_[0] = 0;
+ has_created_file_ = false;
+ };
+
+ ~ConfigFile() {
+ };
+
+ void WriteFile(const SimpleStringDictionary *configurationParameters,
+ const char *dump_dir,
+ const char *minidump_id);
+
+ const char *GetFilePath() { return config_file_path_; }
+
+ void Unlink() {
+ if (config_file_ != -1)
+ unlink(config_file_path_);
+
+ config_file_ = -1;
+ }
+
+ private:
+ BOOL WriteData(const void *data, size_t length);
+
+ BOOL AppendConfigData(const char *key,
+ const void *data,
+ size_t length);
+
+ BOOL AppendConfigString(const char *key,
+ const char *value);
+
+ int config_file_; // descriptor for config file
+ char config_file_path_[PATH_MAX]; // Path to configuration file
+ bool has_created_file_;
+};
+
+//=============================================================================
+class MinidumpLocation {
+ public:
+ MinidumpLocation() {
+ NSString *minidumpDirBase = NSHomeDirectory();
+ NSString *minidumpDir;
+
+ // Put root processes at root
+ if (geteuid() == 0)
+ minidumpDirBase = @"/";
+
+ minidumpDir =
+ [minidumpDirBase stringByAppendingPathComponent:@"Library/Logs/Google"];
+
+ // Ensure that the path exists. Fallback to /tmp if unable to locate path.
+ if (!EnsureDirectoryPathExists(minidumpDir)) {
+ DEBUGLOG(stderr, "Unable to create: %s\n", [minidumpDir UTF8String]);
+ minidumpDir = @"/tmp";
+ }
+
+ strlcpy(minidump_dir_path_, [minidumpDir fileSystemRepresentation],
+ sizeof(minidump_dir_path_));
+
+ // now generate a unique ID
+ string dump_path(minidump_dir_path_);
+ string next_minidump_id;
+
+ string next_minidump_path_ =
+ (MinidumpGenerator::UniqueNameInDirectory(dump_path, &next_minidump_id));
+
+ strlcpy(minidump_id_, next_minidump_id.c_str(), sizeof(minidump_id_));
+ };
+
+ const char *GetPath() { return minidump_dir_path_; }
+ const char *GetID() { return minidump_id_; }
+
+ private:
+ char minidump_dir_path_[PATH_MAX]; // Path to minidump directory
+ char minidump_id_[128];
+};
+
+//=============================================================================
+class Inspector {
+ public:
+ Inspector() {};
+
+ // given a bootstrap service name, receives mach messages
+ // from a crashed process, then inspects it, creates a minidump file
+ // and asks the user if he wants to upload it to a server.
+ void Inspect(const char *receive_port_name);
+
+ private:
+ kern_return_t ServiceCheckIn(const char *receive_port_name);
+ kern_return_t ServiceCheckOut(const char *receive_port_name);
+
+ kern_return_t ReadMessages();
+
+ bool InspectTask();
+ kern_return_t SendAcknowledgement();
+ void LaunchReporter(const char *inConfigFilePath);
+
+ mach_port_t service_rcv_port_;
+
+ int exception_type_;
+ int exception_code_;
+ mach_port_t remote_task_;
+ mach_port_t crashing_thread_;
+ mach_port_t handler_thread_;
+ mach_port_t ack_port_;
+
+ SimpleStringDictionary config_params_;
+
+ ConfigFile config_file_;
+};
+
+
+} // namespace google_breakpad
+
+
diff --git a/src/client/mac/crash_generation/Inspector.mm b/src/client/mac/crash_generation/Inspector.mm
new file mode 100644
index 00000000..f659b669
--- /dev/null
+++ b/src/client/mac/crash_generation/Inspector.mm
@@ -0,0 +1,431 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Utility that can inspect another process and write a crash dump
+
+#import <cstdio>
+#import <iostream>
+#import <stdio.h>
+#import <string.h>
+#import <string>
+
+#import "client/mac/crash_generation/Inspector.h"
+
+#import "client/mac/Framework/Breakpad.h"
+#import "client/mac/handler/minidump_generator.h"
+
+#import "common/mac/SimpleStringDictionary.h"
+#import "common/mac/MachIPC.h"
+
+#import <Foundation/Foundation.h>
+
+#if VERBOSE
+ bool gDebugLog = true;
+#else
+ bool gDebugLog = false;
+#endif
+
+namespace google_breakpad {
+
+//=============================================================================
+static BOOL EnsureDirectoryPathExists(NSString *dirPath) {
+ NSFileManager *mgr = [NSFileManager defaultManager];
+
+ // If we got a relative path, prepend the current directory
+ if (![dirPath isAbsolutePath])
+ dirPath = [[mgr currentDirectoryPath] stringByAppendingPathComponent:dirPath];
+
+ NSString *path = dirPath;
+
+ // Ensure that no file exists within the path which would block creation
+ while (1) {
+ BOOL isDir;
+ if ([mgr fileExistsAtPath:path isDirectory:&isDir]) {
+ if (isDir)
+ break;
+
+ return NO;
+ }
+
+ path = [path stringByDeletingLastPathComponent];
+ }
+
+ // Path now contains the first valid directory (or is empty)
+ if (![path length])
+ return NO;
+
+ NSString *common =
+ [dirPath commonPrefixWithString:path options:NSLiteralSearch];
+
+ // If everything is good
+ if ([common isEqualToString:dirPath])
+ return YES;
+
+ // Break up the difference into components
+ NSString *diff = [dirPath substringFromIndex:[common length] + 1];
+ NSArray *components = [diff pathComponents];
+ unsigned count = [components count];
+
+ // Rebuild the path one component at a time
+ NSDictionary *attrs =
+ [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750]
+ forKey:NSFilePosixPermissions];
+ path = common;
+ for (unsigned i = 0; i < count; ++i) {
+ path = [path stringByAppendingPathComponent:[components objectAtIndex:i]];
+
+ if (![mgr createDirectoryAtPath:path attributes:attrs])
+ return NO;
+ }
+
+ return YES;
+}
+
+//=============================================================================
+BOOL ConfigFile::WriteData(const void *data, size_t length) {
+ size_t result = write(config_file_, data, length);
+
+ return result == length;
+}
+
+//=============================================================================
+BOOL ConfigFile::AppendConfigData(const char *key,
+ const void *data, size_t length) {
+ assert(config_file_ != -1);
+
+ if (!key) {
+ DEBUGLOG(stderr, "Breakpad: Missing Key\n");
+ return NO;
+ }
+
+ if (!data) {
+ DEBUGLOG(stderr, "Breakpad: Missing data for key: %s\n", key ? key :
+ "<Unknown Key>");
+ return NO;
+ }
+
+ // Write the key, \n, length of data (ascii integer), \n, data
+ char buffer[16];
+ char nl = '\n';
+ BOOL result = WriteData(key, strlen(key));
+
+ snprintf(buffer, sizeof(buffer) - 1, "\n%lu\n", length);
+ result &= WriteData(buffer, strlen(buffer));
+ result &= WriteData(data, length);
+ result &= WriteData(&nl, 1);
+ return result;
+}
+
+//=============================================================================
+BOOL ConfigFile::AppendConfigString(const char *key,
+ const char *value) {
+ return AppendConfigData(key, value, strlen(value));
+}
+
+//=============================================================================
+void ConfigFile::WriteFile(const SimpleStringDictionary *configurationParameters,
+ const char *dump_dir,
+ const char *minidump_id) {
+
+ assert(config_file_ == -1);
+
+ // Open and write out configuration file preamble
+ strlcpy(config_file_path_, "/tmp/Config-XXXXXX",
+ sizeof(config_file_path_));
+ config_file_ = mkstemp(config_file_path_);
+
+ if (config_file_ == -1)
+ return;
+
+ has_created_file_ = true;
+
+ // Add the minidump dir
+ AppendConfigString(kReporterMinidumpDirectoryKey, dump_dir);
+ AppendConfigString(kReporterMinidumpIDKey, minidump_id);
+
+ // Write out the configuration parameters
+ BOOL result = YES;
+ const SimpleStringDictionary &dictionary = *configurationParameters;
+
+ const KeyValueEntry *entry = NULL;
+ SimpleStringDictionaryIterator iter(dictionary);
+
+ while ((entry = iter.Next())) {
+ result = AppendConfigString(entry->GetKey(), entry->GetValue());
+
+ if (!result)
+ break;
+ }
+
+ close(config_file_);
+ config_file_ = -1;
+}
+
+//=============================================================================
+void Inspector::Inspect(const char *receive_port_name) {
+ kern_return_t result = ServiceCheckIn(receive_port_name);
+
+ if (result == KERN_SUCCESS) {
+ result = ReadMessages();
+
+ if (result == KERN_SUCCESS) {
+ // Inspect the task and write a minidump file.
+ InspectTask();
+
+ // Send acknowledgement to the crashed process that the inspection
+ // has finished. It will then be able to cleanly exit.
+ if (SendAcknowledgement() == KERN_SUCCESS) {
+ // Ask the user if he wants to upload the crash report to a server,
+ // and do so if he agrees.
+ LaunchReporter(config_file_.GetFilePath());
+ }
+
+ // Now that we're done reading messages, cleanup the service, but only
+ // if there was an actual exception
+ // Otherwise, it means the dump was generated on demand and the process
+ // lives on, and we might be needed again in the future.
+ if (exception_code_) {
+ ServiceCheckOut(receive_port_name);
+ }
+ } else {
+ PRINT_MACH_RESULT(result, "Inspector: WaitForMessage()");
+ }
+ }
+}
+
+//=============================================================================
+kern_return_t Inspector::ServiceCheckIn(const char *receive_port_name) {
+ // We need to get the mach port representing this service, so we can
+ // get information from the crashed process.
+ kern_return_t kr = bootstrap_check_in(bootstrap_port,
+ (char*)receive_port_name,
+ &service_rcv_port_);
+
+ if (kr != KERN_SUCCESS) {
+#if VERBOSE
+ PRINT_MACH_RESULT(kr, "Inspector: bootstrap_check_in()");
+#endif
+ }
+
+ return kr;
+}
+
+//=============================================================================
+kern_return_t Inspector::ServiceCheckOut(const char *receive_port_name) {
+ // We're done receiving mach messages from the crashed process,
+ // so clean up a bit.
+ kern_return_t kr;
+
+ // DO NOT use mach_port_deallocate() here -- it will fail and the
+ // following bootstrap_register() will also fail leaving our service
+ // name hanging around forever (until reboot)
+ kr = mach_port_destroy(mach_task_self(), service_rcv_port_);
+
+ if (kr != KERN_SUCCESS) {
+ PRINT_MACH_RESULT(kr,
+ "Inspector: UNREGISTERING: service_rcv_port mach_port_deallocate()");
+ return kr;
+ }
+
+ // Unregister the service associated with the receive port.
+ kr = bootstrap_register(bootstrap_port,
+ (char*)receive_port_name,
+ MACH_PORT_NULL);
+
+ if (kr != KERN_SUCCESS) {
+ PRINT_MACH_RESULT(kr, "Inspector: UNREGISTERING: bootstrap_register()");
+ }
+
+ return kr;
+}
+
+//=============================================================================
+kern_return_t Inspector::ReadMessages() {
+ // Wait for an initial message from the crashed process containing basic
+ // information about the crash.
+ ReceivePort receive_port(service_rcv_port_);
+
+ MachReceiveMessage message;
+ kern_return_t result = receive_port.WaitForMessage(&message, 1000);
+
+ if (result == KERN_SUCCESS) {
+ InspectorInfo &info = (InspectorInfo &)*message.GetData();
+ exception_type_ = info.exception_type;
+ exception_code_ = info.exception_code;
+
+#if VERBOSE
+ printf("message ID = %d\n", message.GetMessageID());
+#endif
+
+ remote_task_ = message.GetTranslatedPort(0);
+ crashing_thread_ = message.GetTranslatedPort(1);
+ handler_thread_ = message.GetTranslatedPort(2);
+ ack_port_ = message.GetTranslatedPort(3);
+
+#if VERBOSE
+ printf("exception_type = %d\n", exception_type_);
+ printf("exception_code = %d\n", exception_code_);
+ printf("remote_task = %d\n", remote_task_);
+ printf("crashing_thread = %d\n", crashing_thread_);
+ printf("handler_thread = %d\n", handler_thread_);
+ printf("ack_port_ = %d\n", ack_port_);
+ printf("parameter count = %d\n", info.parameter_count);
+#endif
+
+ // The initial message contains the number of key value pairs that
+ // we are expected to read.
+ // Read each key/value pair, one mach message per key/value pair.
+ for (unsigned int i = 0; i < info.parameter_count; ++i) {
+ MachReceiveMessage message;
+ result = receive_port.WaitForMessage(&message, 1000);
+
+ if(result == KERN_SUCCESS) {
+ KeyValueMessageData &key_value_data =
+ (KeyValueMessageData&)*message.GetData();
+
+ config_params_.SetKeyValue(key_value_data.key, key_value_data.value);
+ } else {
+ PRINT_MACH_RESULT(result, "Inspector: key/value message");
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+//=============================================================================
+bool Inspector::InspectTask() {
+ // keep the task quiet while we're looking at it
+ task_suspend(remote_task_);
+
+ MinidumpLocation minidumpLocation;
+
+ config_file_.WriteFile( &config_params_,
+ minidumpLocation.GetPath(),
+ minidumpLocation.GetID());
+
+
+ MinidumpGenerator generator(remote_task_, handler_thread_);
+
+ if (exception_type_ && exception_code_) {
+ generator.SetExceptionInformation(exception_type_,
+ exception_code_,
+ crashing_thread_);
+ }
+
+ NSString *minidumpPath = [NSString stringWithFormat:@"%s/%s.dmp",
+ minidumpLocation.GetPath(), minidumpLocation.GetID()];
+
+ bool result = generator.Write([minidumpPath fileSystemRepresentation]);
+
+ DEBUGLOG(stderr, "Inspector: finished writing minidump file: %s\n",
+ [minidumpPath fileSystemRepresentation]);
+
+ // let the task continue
+ task_resume(remote_task_);
+
+ return result;
+}
+
+//=============================================================================
+// The crashed task needs to be told that the inspection has finished.
+// It will wait on a mach port (with timeout) until we send acknowledgement.
+kern_return_t Inspector::SendAcknowledgement() {
+ if (ack_port_ != MACH_PORT_DEAD) {
+ MachPortSender sender(ack_port_);
+ MachSendMessage ack_message(kMsgType_InspectorAcknowledgement);
+
+ DEBUGLOG(stderr, "Inspector: trying to send acknowledgement to port %d\n",
+ ack_port_);
+
+ kern_return_t result = sender.SendMessage(ack_message, 2000);
+
+#if VERBOSE
+ PRINT_MACH_RESULT(result, "Inspector: sent acknowledgement");
+#endif
+
+ return result;
+ }
+
+ DEBUGLOG(stderr, "Inspector: port translation failure!\n");
+ return KERN_INVALID_NAME;
+}
+
+//=============================================================================
+void Inspector::LaunchReporter(const char *inConfigFilePath) {
+ // Extract the path to the reporter executable.
+ const char *reporterExecutablePath =
+ config_params_.GetValueForKey(BREAKPAD_REPORTER_EXE_LOCATION);
+ DEBUGLOG(stderr, "reporter path = %s\n", reporterExecutablePath);
+
+ // Setup and launch the crash dump sender.
+ const char *argv[3];
+ argv[0] = reporterExecutablePath;
+ argv[1] = inConfigFilePath;
+ argv[2] = NULL;
+
+ // Launch the reporter
+ pid_t pid = fork();
+
+ // If we're in the child, load in our new executable and run.
+ // The parent will not wait for the child to complete.
+ if (pid == 0) {
+ execv(argv[0], (char * const *)argv);
+ config_file_.Unlink(); // launch failed - get rid of config file
+ DEBUGLOG(stderr, "Inspector: unable to launch reporter app\n");
+ _exit(1);
+ }
+
+ // Wait until the Reporter child process exits.
+ //
+
+ // We'll use a timeout of one minute.
+ int timeoutCount = 60; // 60 seconds
+
+ while (timeoutCount-- > 0) {
+ int status;
+ pid_t result = waitpid(pid, &status, WNOHANG);
+
+ if (result == 0) {
+ // The child has not yet finished.
+ sleep(1);
+ } else if (result == -1) {
+ DEBUGLOG(stderr, "Inspector: waitpid error (%d) waiting for reporter app\n",
+ errno);
+ break;
+ } else {
+ // child has finished
+ break;
+ }
+ }
+}
+
+} // namespace google_breakpad
+
diff --git a/src/client/mac/crash_generation/InspectorMain.mm b/src/client/mac/crash_generation/InspectorMain.mm
new file mode 100644
index 00000000..137c6a1e
--- /dev/null
+++ b/src/client/mac/crash_generation/InspectorMain.mm
@@ -0,0 +1,65 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Main driver for Inspector
+
+#import "client/mac/crash_generation/Inspector.h"
+#import <Cocoa/Cocoa.h>
+
+namespace google_breakpad {
+
+//=============================================================================
+extern "C" {
+
+int main(int argc, char *const argv[]) {
+#if DEBUG
+ // Since we're launched on-demand, this is necessary to see debugging
+ // output in the console window.
+ freopen("/dev/console", "w", stdout);
+ freopen("/dev/console", "w", stderr);
+#endif
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ if (argc != 2) {
+ exit(0);
+ }
+ // Our first command-line argument contains the name of the service
+ // that we're providing.
+ google_breakpad::Inspector inspector;
+ inspector.Inspect(argv[1]);
+
+ [pool release];
+
+ return 0;
+}
+
+} // extern "C"
+
+} // namespace google_breakpad
diff --git a/src/client/mac/gcov/libgcov.a b/src/client/mac/gcov/libgcov.a
new file mode 100644
index 00000000..f45a58d7
--- /dev/null
+++ b/src/client/mac/gcov/libgcov.a
Binary files differ
diff --git a/src/client/mac/handler/exception_handler.cc b/src/client/mac/handler/exception_handler.cc
index 60fde473..fd6ea836 100644
--- a/src/client/mac/handler/exception_handler.cc
+++ b/src/client/mac/handler/exception_handler.cc
@@ -490,7 +490,6 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
if (self->use_minidump_write_mutex_)
pthread_mutex_unlock(&self->minidump_write_mutex_);
} else {
-
// When forking a child process with the exception handler installed,
// if the child crashes, it will send the exception back to the parent
// process. The check for task == self_task() ensures that only
diff --git a/src/client/mac/handler/exception_handler_test.cc b/src/client/mac/handler/exception_handler_test.cc
index 59944377..d84f1997 100644
--- a/src/client/mac/handler/exception_handler_test.cc
+++ b/src/client/mac/handler/exception_handler_test.cc
@@ -55,6 +55,7 @@ static void *SleepyFunction(void *) {
while (1) {
sleep(10000);
}
+ return NULL;
}
static void Crasher() {
@@ -77,15 +78,14 @@ bool MDCallback(const char *dump_dir, const char *file_name,
fprintf(stdout, "Minidump: %s\n", path.c_str());
// Indicate that we've handled the callback
- return true;
+ exit(0);
}
int main(int argc, char * const argv[]) {
char buffer[PATH_MAX];
- struct passwd *user = getpwuid(getuid());
// Home dir
- snprintf(buffer, sizeof(buffer), "/Users/%s/Desktop/", user->pw_name);
+ snprintf(buffer, sizeof(buffer), "/tmp/");
string path(buffer);
ExceptionHandler eh(path, NULL, MDCallback, NULL, true);
@@ -97,8 +97,8 @@ int main(int argc, char * const argv[]) {
perror("pthread_create");
}
- // Dump a test
- eh.WriteMinidump();
+// // Dump a test
+// eh.WriteMinidump();
// Test the handler
SoonToCrash();
diff --git a/src/client/mac/handler/minidump_generator_test.cc b/src/client/mac/handler/minidump_generator_test.cc
index 62530832..3a77beb4 100644
--- a/src/client/mac/handler/minidump_generator_test.cc
+++ b/src/client/mac/handler/minidump_generator_test.cc
@@ -45,14 +45,13 @@ static bool doneWritingReport = false;
static void *Reporter(void *) {
char buffer[PATH_MAX];
MinidumpGenerator md;
- struct passwd *user = getpwuid(getuid());
// Write it to the desktop
snprintf(buffer,
sizeof(buffer),
- "/Users/%s/Desktop/test.dmp",
- user->pw_name);
-
+ "/tmp/test.dmp");
+
+
fprintf(stdout, "Writing %s\n", buffer);
unlink(buffer);
md.Write(buffer);
diff --git a/src/client/mac/sender/English.lproj/Breakpad.xib b/src/client/mac/sender/English.lproj/Breakpad.xib
new file mode 100644
index 00000000..7b5053a5
--- /dev/null
+++ b/src/client/mac/sender/English.lproj/Breakpad.xib
@@ -0,0 +1,834 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.02">
+ <data>
+ <int key="IBDocument.SystemTarget">1050</int>
+ <string key="IBDocument.SystemVersion">9F33</string>
+ <string key="IBDocument.InterfaceBuilderVersion">670</string>
+ <string key="IBDocument.AppKitVersion">949.34</string>
+ <string key="IBDocument.HIToolboxVersion">352.00</string>
+ <object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <integer value="2"/>
+ </object>
+ <object class="NSArray" key="IBDocument.PluginDependencies">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ <object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSCustomObject" id="1001">
+ <string key="NSClassName">Reporter</string>
+ </object>
+ <object class="NSCustomObject" id="1003">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1004">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSWindowTemplate" id="1005">
+ <int key="NSWindowStyleMask">1</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{196, 157}, {484, 353}}</string>
+ <int key="NSWTFlags">536871936</int>
+ <string key="NSWindowTitle"/>
+ <string key="NSWindowClass">NSWindow</string>
+ <nil key="NSViewClass"/>
+ <string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ <object class="NSView" key="NSWindowView" id="1006">
+ <reference key="NSNextResponder"/>
+ <int key="NSvFlags">264</int>
+ <object class="NSMutableArray" key="NSSubviews">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSTextField" id="52942477">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">266</int>
+ <string key="NSFrame">{{89, 279}, {378, 54}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="237530958">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">272629760</int>
+ <string type="base64-UTF8" key="NSContents">RE8gTk9UIExPQ0FMSVpFLiBUaGUgPFJlYWxseSBMb25nIENvbXBhbnkgTmFtZT4gcHJvZ3JhbSA8UmVh
+bGx5IExvbmcgQXBwIE5hbWUgSGVyZT4gaGFzIHVuZXhwZWN0ZWRseSBxdWl0LiA</string>
+ <object class="NSFont" key="NSSupport">
+ <string key="NSName">LucidaGrande-Bold</string>
+ <double key="NSSize">1.400000e+01</double>
+ <int key="NSfFlags">16</int>
+ </object>
+ <reference key="NSControlView" ref="52942477"/>
+ <object class="NSColor" key="NSBackgroundColor" id="738791573">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MC42NjY2NjY2OQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor" id="909242260">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">controlTextColor</string>
+ <object class="NSColor" key="NSColor" id="488201583">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MAA</bytes>
+ </object>
+ </object>
+ </object>
+ </object>
+ <object class="NSImageView" id="131954859">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">268</int>
+ <object class="NSMutableSet" key="NSDragTypes">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="set.sortedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>Apple PDF pasteboard type</string>
+ <string>Apple PICT pasteboard type</string>
+ <string>Apple PNG pasteboard type</string>
+ <string>NSFilenamesPboardType</string>
+ <string>NeXT Encapsulated PostScript v1.2 pasteboard type</string>
+ <string>NeXT TIFF v4.0 pasteboard type</string>
+ </object>
+ </object>
+ <string key="NSFrame">{{20, 269}, {64, 64}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSImageCell" key="NSCell" id="863447683">
+ <int key="NSCellFlags">130560</int>
+ <int key="NSCellFlags2">33554432</int>
+ <object class="NSCustomResource" key="NSContents">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSApplicationIcon</string>
+ </object>
+ <int key="NSAlign">0</int>
+ <int key="NSScale">0</int>
+ <int key="NSStyle">0</int>
+ <bool key="NSAnimates">NO</bool>
+ </object>
+ <bool key="NSEditable">YES</bool>
+ </object>
+ <object class="NSTextField" id="547141541">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">266</int>
+ <string key="NSFrame">{{17, 191}, {450, 70}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="857260085">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">272760832</int>
+ <string type="base64-UTF8" key="NSContents">RE8gTk9UIExPQ0FMSVpFLiBUaGUgc3lzdGVtIGFuZCBvdGhlciBhcHBsaWNhdGlvbnMgaGF2ZSBub3Qg
+YmVlbiBhZmZlY3RlZC4gQSByZXBvcnQgaGFzIGJlZW4gY3JlYXRlZCB0aGF0IHlvdSBjYW4gc2VuZCB0
+byA8UmVhbGx5IExvbmcgQ29tcGFueSBOYW1lPiB0byBoZWxwIGlkZW50aWZ5IHRoZSBwcm9ibGVtLgoK
+UGxlYXNlIGhlbHAgdXMgZml4IHRoZSBwcm9ibGVtIGJ5IGRlc2NyaWJpbmcgd2hhdCBoYXBwZW5lZCBi
+ZWZvcmUgdGhlIGNyYXNoLg</string>
+ <object class="NSFont" key="NSSupport" id="26">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">1.100000e+01</double>
+ <int key="NSfFlags">3100</int>
+ </object>
+ <reference key="NSControlView" ref="547141541"/>
+ <reference key="NSBackgroundColor" ref="738791573"/>
+ <reference key="NSTextColor" ref="909242260"/>
+ </object>
+ </object>
+ <object class="NSTextField" id="637803025">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">290</int>
+ <string key="NSFrame">{{17, 85}, {450, 28}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="862965674">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">272760832</int>
+ <string key="NSContents">DO NOT LOCALIZE. Providing your email address is optional and will allow us contact you in case we need more details.</string>
+ <reference key="NSSupport" ref="26"/>
+ <reference key="NSControlView" ref="637803025"/>
+ <reference key="NSBackgroundColor" ref="738791573"/>
+ <reference key="NSTextColor" ref="909242260"/>
+ </object>
+ </object>
+ <object class="NSButton" id="506323760">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">289</int>
+ <string key="NSFrame">{{353, 12}, {117, 32}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="555878343">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">Send Report</string>
+ <object class="NSFont" key="NSSupport" id="890637240">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">1.300000e+01</double>
+ <int key="NSfFlags">1044</int>
+ </object>
+ <reference key="NSControlView" ref="506323760"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <reference key="NSAlternateImage" ref="890637240"/>
+ <string key="NSAlternateContents"/>
+ <string type="base64-UTF8" key="NSKeyEquivalent">DQ</string>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSButton" id="1066255572">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">289</int>
+ <string key="NSFrame">{{243, 12}, {110, 32}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="910201563">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">134217728</int>
+ <string key="NSContents">Cancel</string>
+ <reference key="NSSupport" ref="890637240"/>
+ <reference key="NSControlView" ref="1066255572"/>
+ <int key="NSButtonFlags">-2038284033</int>
+ <int key="NSButtonFlags2">129</int>
+ <reference key="NSAlternateImage" ref="890637240"/>
+ <string key="NSAlternateContents"/>
+ <string type="base64-UTF8" key="NSKeyEquivalent">Gw</string>
+ <int key="NSPeriodicDelay">200</int>
+ <int key="NSPeriodicInterval">25</int>
+ </object>
+ </object>
+ <object class="NSTextField" id="834332784">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">290</int>
+ <string key="NSFrame">{{59, 58}, {195, 19}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="454024475">
+ <int key="NSCellFlags">-1804468671</int>
+ <int key="NSCellFlags2">272761856</int>
+ <string key="NSContents">preston.jackson@gmail.com</string>
+ <reference key="NSSupport" ref="26"/>
+ <string key="NSPlaceholderString">optional</string>
+ <reference key="NSControlView" ref="834332784"/>
+ <bool key="NSDrawsBackground">YES</bool>
+ <object class="NSColor" key="NSBackgroundColor" id="181834562">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">textBackgroundColor</string>
+ <object class="NSColor" key="NSColor">
+ <int key="NSColorSpace">3</int>
+ <bytes key="NSWhite">MQA</bytes>
+ </object>
+ </object>
+ <object class="NSColor" key="NSTextColor" id="332510420">
+ <int key="NSColorSpace">6</int>
+ <string key="NSCatalogName">System</string>
+ <string key="NSColorName">textColor</string>
+ <reference key="NSColor" ref="488201583"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSTextField" id="1001751188">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">292</int>
+ <string key="NSFrame">{{17, 60}, {37, 14}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="220926093">
+ <int key="NSCellFlags">68288064</int>
+ <int key="NSCellFlags2">71435264</int>
+ <string key="NSContents">Email:</string>
+ <reference key="NSSupport" ref="26"/>
+ <reference key="NSControlView" ref="1001751188"/>
+ <reference key="NSBackgroundColor" ref="738791573"/>
+ <reference key="NSTextColor" ref="909242260"/>
+ </object>
+ </object>
+ <object class="NSButton" id="645396128">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">289</int>
+ <string key="NSFrame">{{352, 59}, {16, 17}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSButtonCell" key="NSCell" id="776409024">
+ <int key="NSCellFlags">-2080244224</int>
+ <int key="NSCellFlags2">262144</int>
+ <string key="NSContents">Privacy Policy</string>
+ <object class="NSFont" key="NSSupport">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">9.000000e+00</double>
+ <int key="NSfFlags">3614</int>
+ </object>
+ <reference key="NSControlView" ref="645396128"/>
+ <int key="NSButtonFlags">-2040250113</int>
+ <int key="NSButtonFlags2">36</int>
+ <object class="NSCustomResource" key="NSNormalImage">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSFollowLinkFreestandingTemplate</string>
+ </object>
+ <string key="NSAlternateContents"/>
+ <string key="NSKeyEquivalent"/>
+ <int key="NSPeriodicDelay">400</int>
+ <int key="NSPeriodicInterval">75</int>
+ </object>
+ </object>
+ <object class="NSTextField" id="271427294">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">289</int>
+ <string key="NSFrame">{{259, 60}, {89, 14}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="718717750">
+ <int key="NSCellFlags">68288064</int>
+ <int key="NSCellFlags2">71435264</int>
+ <string key="NSContents">Privacy Policy</string>
+ <reference key="NSSupport" ref="26"/>
+ <reference key="NSControlView" ref="271427294"/>
+ <reference key="NSBackgroundColor" ref="738791573"/>
+ <reference key="NSTextColor" ref="909242260"/>
+ </object>
+ </object>
+ <object class="NSTextField" id="652207274">
+ <reference key="NSNextResponder" ref="1006"/>
+ <int key="NSvFlags">274</int>
+ <string key="NSFrame">{{20, 124}, {444, 61}}</string>
+ <reference key="NSSuperview" ref="1006"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="748395329">
+ <int key="NSCellFlags">341966337</int>
+ <int key="NSCellFlags2">272760832</int>
+ <string key="NSContents">DO NOT LOCALIZE: Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 </string>
+ <reference key="NSSupport" ref="26"/>
+ <reference key="NSControlView" ref="652207274"/>
+ <bool key="NSDrawsBackground">YES</bool>
+ <reference key="NSBackgroundColor" ref="181834562"/>
+ <reference key="NSTextColor" ref="332510420"/>
+ </object>
+ </object>
+ </object>
+ <string key="NSFrameSize">{484, 353}</string>
+ <reference key="NSSuperview"/>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {1680, 1028}}</string>
+ <string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string>
+ </object>
+ </object>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <object class="NSMutableArray" key="connectionRecords">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">alertWindow</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1005"/>
+ </object>
+ <int key="connectionID">42</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">sendReport:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="506323760"/>
+ </object>
+ <int key="connectionID">45</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">cancel:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="1066255572"/>
+ </object>
+ <int key="connectionID">46</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showPrivacyPolicy:</string>
+ <reference key="source" ref="1001"/>
+ <reference key="destination" ref="645396128"/>
+ </object>
+ <int key="connectionID">53</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: headerMessage</string>
+ <reference key="source" ref="52942477"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="52942477"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">value: headerMessage</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">headerMessage</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">85</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: reportMessage</string>
+ <reference key="source" ref="547141541"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="547141541"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">value: reportMessage</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">reportMessage</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">86</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: emailMessage</string>
+ <reference key="source" ref="637803025"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="637803025"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">value: emailMessage</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">emailMessage</string>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">87</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: emailValue</string>
+ <reference key="source" ref="834332784"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="834332784"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">value: emailValue</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">emailValue</string>
+ <object class="NSDictionary" key="NSOptions">
+ <string key="NS.key.0">NSNullPlaceholder</string>
+ <string key="NS.object.0">optional</string>
+ </object>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">90</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">initialFirstResponder</string>
+ <reference key="source" ref="1005"/>
+ <reference key="destination" ref="506323760"/>
+ </object>
+ <int key="connectionID">91</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBBindingConnection" key="connection">
+ <string key="label">value: commentsValue</string>
+ <reference key="source" ref="652207274"/>
+ <reference key="destination" ref="1001"/>
+ <object class="NSNibBindingConnector" key="connector">
+ <reference key="NSSource" ref="652207274"/>
+ <reference key="NSDestination" ref="1001"/>
+ <string key="NSLabel">value: commentsValue</string>
+ <string key="NSBinding">value</string>
+ <string key="NSKeyPath">commentsValue</string>
+ <object class="NSDictionary" key="NSOptions">
+ <string key="NS.key.0">NSNullPlaceholder</string>
+ <string key="NS.object.0">optional comments</string>
+ </object>
+ <int key="NSNibBindingConnectorVersion">2</int>
+ </object>
+ </object>
+ <int key="connectionID">124</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">nextKeyView</string>
+ <reference key="source" ref="834332784"/>
+ <reference key="destination" ref="506323760"/>
+ </object>
+ <int key="connectionID">125</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">nextKeyView</string>
+ <reference key="source" ref="652207274"/>
+ <reference key="destination" ref="834332784"/>
+ </object>
+ <int key="connectionID">126</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">nextKeyView</string>
+ <reference key="source" ref="506323760"/>
+ <reference key="destination" ref="652207274"/>
+ </object>
+ <int key="connectionID">127</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="652207274"/>
+ <reference key="destination" ref="1001"/>
+ </object>
+ <int key="connectionID">128</int>
+ </object>
+ </object>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <object class="NSArray" key="orderedObjects">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <object class="NSArray" key="object" id="1002">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <reference key="children" ref="1000"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1001"/>
+ <reference key="parent" ref="1002"/>
+ <string type="base64-UTF8" key="objectName">RmlsZSdzIE93bmVyA</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1003"/>
+ <reference key="parent" ref="1002"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1004"/>
+ <reference key="parent" ref="1002"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">1</int>
+ <reference key="object" ref="1005"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="1006"/>
+ </object>
+ <reference key="parent" ref="1002"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">2</int>
+ <reference key="object" ref="1006"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="131954859"/>
+ <reference ref="52942477"/>
+ <reference ref="652207274"/>
+ <reference ref="637803025"/>
+ <reference ref="1001751188"/>
+ <reference ref="834332784"/>
+ <reference ref="271427294"/>
+ <reference ref="645396128"/>
+ <reference ref="506323760"/>
+ <reference ref="1066255572"/>
+ <reference ref="547141541"/>
+ </object>
+ <reference key="parent" ref="1005"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">3</int>
+ <reference key="object" ref="52942477"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="237530958"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">4</int>
+ <reference key="object" ref="237530958"/>
+ <reference key="parent" ref="52942477"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">6</int>
+ <reference key="object" ref="131954859"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="863447683"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">7</int>
+ <reference key="object" ref="863447683"/>
+ <reference key="parent" ref="131954859"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">8</int>
+ <reference key="object" ref="547141541"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="857260085"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">9</int>
+ <reference key="object" ref="857260085"/>
+ <reference key="parent" ref="547141541"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">12</int>
+ <reference key="object" ref="506323760"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="555878343"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">13</int>
+ <reference key="object" ref="555878343"/>
+ <reference key="parent" ref="506323760"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">14</int>
+ <reference key="object" ref="1066255572"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="910201563"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">15</int>
+ <reference key="object" ref="910201563"/>
+ <reference key="parent" ref="1066255572"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">18</int>
+ <reference key="object" ref="834332784"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="454024475"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">19</int>
+ <reference key="object" ref="454024475"/>
+ <reference key="parent" ref="834332784"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">20</int>
+ <reference key="object" ref="1001751188"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="220926093"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">21</int>
+ <reference key="object" ref="220926093"/>
+ <reference key="parent" ref="1001751188"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">48</int>
+ <reference key="object" ref="645396128"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="776409024"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">49</int>
+ <reference key="object" ref="776409024"/>
+ <reference key="parent" ref="645396128"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">58</int>
+ <reference key="object" ref="637803025"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="862965674"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">59</int>
+ <reference key="object" ref="862965674"/>
+ <reference key="parent" ref="637803025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">66</int>
+ <reference key="object" ref="271427294"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="718717750"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">67</int>
+ <reference key="object" ref="718717750"/>
+ <reference key="parent" ref="271427294"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">116</int>
+ <reference key="object" ref="652207274"/>
+ <object class="NSMutableArray" key="children">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <reference ref="748395329"/>
+ </object>
+ <reference key="parent" ref="1006"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">117</int>
+ <reference key="object" ref="748395329"/>
+ <reference key="parent" ref="652207274"/>
+ </object>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="flattenedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>-1.IBPluginDependency</string>
+ <string>-2.IBPluginDependency</string>
+ <string>-3.IBPluginDependency</string>
+ <string>1.IBEditorWindowLastContentRect</string>
+ <string>1.IBPluginDependency</string>
+ <string>1.IBViewEditorWindowController.showingBoundsRectangles</string>
+ <string>1.IBViewEditorWindowController.showingLayoutRectangles</string>
+ <string>1.IBWindowTemplateEditedContentRect</string>
+ <string>1.NSWindowTemplate.visibleAtLaunch</string>
+ <string>1.WindowOrigin</string>
+ <string>1.editorWindowContentRectSynchronizationRect</string>
+ <string>116.IBPluginDependency</string>
+ <string>117.IBPluginDependency</string>
+ <string>12.IBPluginDependency</string>
+ <string>13.IBPluginDependency</string>
+ <string>14.IBPluginDependency</string>
+ <string>15.IBPluginDependency</string>
+ <string>18.IBPluginDependency</string>
+ <string>19.IBPluginDependency</string>
+ <string>2.IBPluginDependency</string>
+ <string>2.IBUserGuides</string>
+ <string>20.IBPluginDependency</string>
+ <string>21.IBPluginDependency</string>
+ <string>3.IBPluginDependency</string>
+ <string>4.IBPluginDependency</string>
+ <string>48.IBPluginDependency</string>
+ <string>49.IBPluginDependency</string>
+ <string>58.IBPluginDependency</string>
+ <string>59.IBPluginDependency</string>
+ <string>6.IBPluginDependency</string>
+ <string>66.IBPluginDependency</string>
+ <string>67.IBPluginDependency</string>
+ <string>7.IBPluginDependency</string>
+ <string>8.IBPluginDependency</string>
+ <string>9.IBPluginDependency</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>{{641, 409}, {484, 353}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <integer value="0" id="6"/>
+ <reference ref="6"/>
+ <string>{{641, 409}, {484, 353}}</string>
+ <reference ref="6"/>
+ <string>{196, 240}</string>
+ <string>{{357, 418}, {480, 270}}</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSMutableArray">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBUserGuide">
+ <reference key="view" ref="1006"/>
+ <float key="location">1.120000e+02</float>
+ <int key="affinity">1</int>
+ </object>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="unlocalizedProperties">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="activeLocalization"/>
+ <object class="NSMutableDictionary" key="localizations">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ </object>
+ </object>
+ <nil key="sourceID"/>
+ <int key="maxID">128</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <object class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="IBPartialClassDescription">
+ <string key="className">Reporter</string>
+ <string key="superclassName">NSObject</string>
+ <object class="NSMutableDictionary" key="actions">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <object class="NSMutableArray" key="dict.sortedKeys">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>cancel:</string>
+ <string>sendReport:</string>
+ <string>showPrivacyPolicy:</string>
+ </object>
+ <object class="NSMutableArray" key="dict.values">
+ <bool key="EncodedWithXMLCoder">YES</bool>
+ <string>id</string>
+ <string>id</string>
+ <string>id</string>
+ </object>
+ </object>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">alertWindow</string>
+ <string key="NS.object.0">NSWindow</string>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">sender/crash_report_sender.h</string>
+ </object>
+ </object>
+ </object>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.LastKnownRelativeProjectPath">../../Breakpad.xcodeproj</string>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ </data>
+</archive>
diff --git a/src/client/mac/sender/English.lproj/Localizable.strings b/src/client/mac/sender/English.lproj/Localizable.strings
new file mode 100644
index 00000000..25ddc731
--- /dev/null
+++ b/src/client/mac/sender/English.lproj/Localizable.strings
Binary files differ
diff --git a/src/client/mac/sender/ReporterIcon.graffle b/src/client/mac/sender/ReporterIcon.graffle
new file mode 100644
index 00000000..62cee85a
--- /dev/null
+++ b/src/client/mac/sender/ReporterIcon.graffle
@@ -0,0 +1,2489 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>ActiveLayerIndex</key>
+ <integer>0</integer>
+ <key>ApplicationVersion</key>
+ <array>
+ <string>com.omnigroup.OmniGrafflePro</string>
+ <string>137.6.0.106738</string>
+ </array>
+ <key>AutoAdjust</key>
+ <false/>
+ <key>BackgroundGraphic</key>
+ <dict>
+ <key>Bounds</key>
+ <string>{{0, 0}, {512, 512}}</string>
+ <key>Class</key>
+ <string>SolidGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>CalisMTBol</string>
+ <key>Size</key>
+ <real>112</real>
+ </dict>
+ <key>ID</key>
+ <integer>2</integer>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0</string>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.852018</string>
+ <key>r</key>
+ <string>0.998962</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ </dict>
+ <key>CanvasOrigin</key>
+ <string>{0, 0}</string>
+ <key>CanvasSize</key>
+ <string>{512, 512}</string>
+ <key>ColumnAlign</key>
+ <integer>1</integer>
+ <key>ColumnSpacing</key>
+ <real>36</real>
+ <key>CreationDate</key>
+ <string>2008-11-14 16:58:15 -0700</string>
+ <key>Creator</key>
+ <string>Preston Jackson</string>
+ <key>DisplayScale</key>
+ <string>1 pt = 1 px</string>
+ <key>FileType</key>
+ <string>flat</string>
+ <key>GraphDocumentVersion</key>
+ <integer>6</integer>
+ <key>GraphicsList</key>
+ <array>
+ <dict>
+ <key>Bounds</key>
+ <string>{{33.9443, 35.3885}, {444.111, 437.112}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>CalisMTBol</string>
+ <key>Size</key>
+ <real>112</real>
+ </dict>
+ <key>ID</key>
+ <integer>31</integer>
+ <key>Rotation</key>
+ <real>270</real>
+ <key>Shape</key>
+ <string>Bezier</string>
+ <key>ShapeData</key>
+ <dict>
+ <key>UnitPoints</key>
+ <array>
+ <string>{-0.5, -0.439247}</string>
+ <string>{-0.5, -0.485429}</string>
+ <string>{-0.446294, -0.512626}</string>
+ <string>{-0.409932, -0.494153}</string>
+ <string>{-0.373569, -0.47568}</string>
+ <string>{0.436363, -0.0733799}</string>
+ <string>{0.472729, -0.0549059}</string>
+ <string>{0.50909, -0.0364333}</string>
+ <string>{0.509091, 0.0364345}</string>
+ <string>{0.472729, 0.0549059}</string>
+ <string>{0.436368, 0.0733802}</string>
+ <string>{-0.373569, 0.475681}</string>
+ <string>{-0.409932, 0.494153}</string>
+ <string>{-0.446294, 0.512626}</string>
+ <string>{-0.500001, 0.485429}</string>
+ <string>{-0.5, 0.439247}</string>
+ <string>{-0.49998, 0.393072}</string>
+ <string>{-0.500002, -0.393066}</string>
+ </array>
+ </dict>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.770962</string>
+ <key>r</key>
+ <string>0.997971</string>
+ </dict>
+ <key>Draws</key>
+ <string>NO</string>
+ <key>FillType</key>
+ <integer>3</integer>
+ <key>GradientCenter</key>
+ <string>{-0.609524, 0}</string>
+ <key>GradientColor</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.911574</string>
+ <key>r</key>
+ <string>0.998779</string>
+ </dict>
+ <key>MiddleFraction</key>
+ <real>0.6111111044883728</real>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.43</string>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Draws</key>
+ <string>NO</string>
+ <key>Fuzziness</key>
+ <real>7.2213706970214844</real>
+ <key>ShadowVector</key>
+ <string>{0, 6}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.766903</string>
+ <key>r</key>
+ <string>0.997925</string>
+ </dict>
+ <key>Width</key>
+ <real>7</real>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>0</integer>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ <key>TextPlacement</key>
+ <integer>0</integer>
+ <key>TextRelativeArea</key>
+ <string>{{0.06, 0.17}, {0.88, 0.5}}</string>
+ <key>TextRotation</key>
+ <real>90</real>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{3.89085, 67.8908}, {404.218, 332}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>CalisMTBol</string>
+ <key>Size</key>
+ <real>112</real>
+ </dict>
+ <key>ID</key>
+ <integer>30</integer>
+ <key>Rotation</key>
+ <real>270</real>
+ <key>Shape</key>
+ <string>Bezier</string>
+ <key>ShapeData</key>
+ <dict>
+ <key>UnitPoints</key>
+ <array>
+ <string>{-0.5, -0.5}</string>
+ <string>{-0.459695, -0.475464}</string>
+ <string>{0.429465, 0.0537758}</string>
+ <string>{0.469773, 0.0783133}</string>
+ <string>{0.510074, 0.102849}</string>
+ <string>{0.510077, 0.198357}</string>
+ <string>{0.469773, 0.222892}</string>
+ <string>{0.429473, 0.247428}</string>
+ <string>{-0.00521517, 0.499998}</string>
+ <string>{-0.00521785, 0.5}</string>
+ <string>{-0.00521713, -0.113381}</string>
+ <string>{-0.44962, -0.458615}</string>
+ </array>
+ </dict>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0</string>
+ <key>b</key>
+ <string>1</string>
+ <key>g</key>
+ <string>1</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ <key>FillType</key>
+ <integer>2</integer>
+ <key>GradientAngle</key>
+ <real>180</real>
+ <key>GradientCenter</key>
+ <string>{-0.609524, 0}</string>
+ <key>GradientColor</key>
+ <dict>
+ <key>a</key>
+ <string>0.5</string>
+ <key>w</key>
+ <string>1</string>
+ </dict>
+ <key>MiddleFraction</key>
+ <real>0.6111111044883728</real>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.51</string>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Draws</key>
+ <string>NO</string>
+ <key>Fuzziness</key>
+ <real>3.3371961116790771</real>
+ <key>ShadowVector</key>
+ <string>{0, 2}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.766903</string>
+ <key>r</key>
+ <string>0.997925</string>
+ </dict>
+ <key>Draws</key>
+ <string>NO</string>
+ <key>Width</key>
+ <real>2</real>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>0</integer>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ <key>TextPlacement</key>
+ <integer>0</integer>
+ <key>TextRelativeArea</key>
+ <string>{{0.06, 0.17}, {0.88, 0.5}}</string>
+ <key>TextRotation</key>
+ <real>90</real>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{33.9443, 35.3886}, {444.112, 437.111}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>CalisMTBol</string>
+ <key>Size</key>
+ <real>112</real>
+ </dict>
+ <key>ID</key>
+ <integer>29</integer>
+ <key>Rotation</key>
+ <real>270</real>
+ <key>Shape</key>
+ <string>Bezier</string>
+ <key>ShapeData</key>
+ <dict>
+ <key>UnitPoints</key>
+ <array>
+ <string>{-0.5, -0.439247}</string>
+ <string>{-0.500001, -0.485429}</string>
+ <string>{-0.446295, -0.512626}</string>
+ <string>{-0.409932, -0.494153}</string>
+ <string>{-0.373568, -0.475681}</string>
+ <string>{0.436363, -0.0733802}</string>
+ <string>{0.472729, -0.0549062}</string>
+ <string>{0.509089, -0.0364334}</string>
+ <string>{0.509092, 0.0364341}</string>
+ <string>{0.472729, 0.0549056}</string>
+ <string>{0.436369, 0.0733803}</string>
+ <string>{-0.373568, 0.475681}</string>
+ <string>{-0.409932, 0.494153}</string>
+ <string>{-0.446294, 0.512626}</string>
+ <string>{-0.500001, 0.485428}</string>
+ <string>{-0.5, 0.439248}</string>
+ <string>{-0.499978, 0.39307}</string>
+ <string>{-0.500003, -0.393066}</string>
+ </array>
+ </dict>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.2</string>
+ <key>b</key>
+ <string>1</string>
+ <key>g</key>
+ <string>1</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ <key>FillType</key>
+ <integer>2</integer>
+ <key>GradientAngle</key>
+ <real>90</real>
+ <key>GradientCenter</key>
+ <string>{-0.609524, 0}</string>
+ <key>GradientColor</key>
+ <dict>
+ <key>a</key>
+ <string>0</string>
+ <key>w</key>
+ <string>1</string>
+ </dict>
+ <key>MiddleFraction</key>
+ <real>0.6111111044883728</real>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.51</string>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Draws</key>
+ <string>NO</string>
+ <key>Fuzziness</key>
+ <real>3.3371961116790771</real>
+ <key>ShadowVector</key>
+ <string>{0, 2}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.766903</string>
+ <key>r</key>
+ <string>0.997925</string>
+ </dict>
+ <key>Draws</key>
+ <string>NO</string>
+ <key>Width</key>
+ <real>2</real>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>0</integer>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ <key>TextPlacement</key>
+ <integer>0</integer>
+ <key>TextRelativeArea</key>
+ <string>{{0.06, 0.17}, {0.88, 0.5}}</string>
+ <key>TextRotation</key>
+ <real>90</real>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{176, 102.384}, {158.841, 537.616}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>CalisMTBol</string>
+ <key>Size</key>
+ <real>425</real>
+ </dict>
+ <key>ID</key>
+ <integer>26</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>0</integer>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf350
+{\fonttbl\f0\fnil\fcharset0 CalistoMT;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\b\fs850 \cf1 !}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{176, 104}, {158.841, 537.616}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.749523</string>
+ <key>r</key>
+ <string>0.997726</string>
+ </dict>
+ <key>Font</key>
+ <string>CalisMTBol</string>
+ <key>Size</key>
+ <real>425</real>
+ </dict>
+ <key>ID</key>
+ <integer>27</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>0</integer>
+ <key>RTFD</key>
+ <data>
+ BAtzdHJlYW10eXBlZIHoA4QBQISEhBJOU0F0dHJpYnV0
+ ZWRTdHJpbmcAhIQITlNPYmplY3QAhZKEhIQITlNTdHJp
+ bmcBlIQBKwEhhoQCaUkBAZKEhIQMTlNEaWN0aW9uYXJ5
+ AJSEAWkEkoSWlhBOU1BhcmFncmFwaFN0eWxlhpKEhIQQ
+ TlNQYXJhZ3JhcGhTdHlsZQCUhARDQ0BTAgCEhIQHTlNB
+ cnJheQCUmQyShISECU5TVGV4dFRhYgCUhAJDZgAchpKE
+ n54AOIaShJ+eAFSGkoSfngBwhpKEn54AgYwAhpKEn54A
+ gagAhpKEn54AgcQAhpKEn54AgeAAhpKEn54AgfwAhpKE
+ n54AgRgBhpKEn54AgTQBhpKEn54AgVABhoYAhpKElpYG
+ TlNGb250hpKEhIQGTlNGb250HpSZIIQFWzMyY10GAAAA
+ FgAAAP/+QwBhAGwAaQBzAE0AVABCAG8AbAAAAIQBZoGp
+ AYQBYwCiAaIAogCGkoSWlg1OU1N0cm9rZVdpZHRohpKE
+ hIQITlNOdW1iZXIAhIQHTlNWYWx1ZQCUhAEqhIQBZKYD
+ hpKElpYHTlNDb2xvcoaShISEB05TQ29sb3IAlKIChARm
+ ZmZmAYN4dz8/AAGGhoY=
+ </data>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf350
+{\fonttbl\f0\fnil\fcharset0 CalistoMT;}
+{\colortbl;\red255\green255\blue255;\red254\green191\blue0;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural
+
+\f0\b\fs850 \cf2 \outl\strokewidth60 \strokec2 !}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{33.9441, 35.3884}, {444.112, 437.111}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>CalisMTBol</string>
+ <key>Size</key>
+ <real>112</real>
+ </dict>
+ <key>ID</key>
+ <integer>16</integer>
+ <key>Rotation</key>
+ <real>270</real>
+ <key>Shape</key>
+ <string>Bezier</string>
+ <key>ShapeData</key>
+ <dict>
+ <key>UnitPoints</key>
+ <array>
+ <string>{-0.5, -0.439247}</string>
+ <string>{-0.5, -0.485429}</string>
+ <string>{-0.446295, -0.512626}</string>
+ <string>{-0.409933, -0.494153}</string>
+ <string>{-0.373569, -0.47568}</string>
+ <string>{0.436363, -0.073379}</string>
+ <string>{0.472729, -0.0549049}</string>
+ <string>{0.50909, -0.0364324}</string>
+ <string>{0.509091, 0.0364344}</string>
+ <string>{0.472729, 0.0549058}</string>
+ <string>{0.436368, 0.0733801}</string>
+ <string>{-0.373569, 0.47568}</string>
+ <string>{-0.409933, 0.494153}</string>
+ <string>{-0.446295, 0.512626}</string>
+ <string>{-0.500001, 0.485429}</string>
+ <string>{-0.5, 0.439247}</string>
+ <string>{-0.49998, 0.393072}</string>
+ <string>{-0.500002, -0.393066}</string>
+ </array>
+ </dict>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.770962</string>
+ <key>r</key>
+ <string>0.997971</string>
+ </dict>
+ <key>FillType</key>
+ <integer>3</integer>
+ <key>GradientCenter</key>
+ <string>{-0.609524, 0}</string>
+ <key>GradientColor</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.911574</string>
+ <key>r</key>
+ <string>0.998779</string>
+ </dict>
+ <key>MiddleFraction</key>
+ <real>0.6111111044883728</real>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.9</string>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Fuzziness</key>
+ <real>8.0632610321044922</real>
+ <key>ShadowVector</key>
+ <string>{0, 9}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.766903</string>
+ <key>r</key>
+ <string>0.997925</string>
+ </dict>
+ <key>Draws</key>
+ <string>NO</string>
+ <key>Width</key>
+ <real>2</real>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>0</integer>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ <key>TextPlacement</key>
+ <integer>0</integer>
+ <key>TextRelativeArea</key>
+ <string>{{0.06, 0.17}, {0.88, 0.5}}</string>
+ <key>TextRotation</key>
+ <real>90</real>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ </array>
+ <key>GridInfo</key>
+ <dict>
+ <key>GridSpacing</key>
+ <real>4</real>
+ <key>ShowsGrid</key>
+ <string>YES</string>
+ <key>SnapsToGrid</key>
+ <string>YES</string>
+ </dict>
+ <key>GuidesLocked</key>
+ <string>NO</string>
+ <key>GuidesVisible</key>
+ <string>YES</string>
+ <key>HPages</key>
+ <integer>1</integer>
+ <key>ImageCounter</key>
+ <integer>2</integer>
+ <key>KeepToScale</key>
+ <false/>
+ <key>Layers</key>
+ <array>
+ <dict>
+ <key>Lock</key>
+ <string>NO</string>
+ <key>Name</key>
+ <string>Layer 1</string>
+ <key>Print</key>
+ <string>YES</string>
+ <key>View</key>
+ <string>YES</string>
+ </dict>
+ </array>
+ <key>LayoutInfo</key>
+ <dict>
+ <key>Animate</key>
+ <string>NO</string>
+ <key>circoMinDist</key>
+ <real>18</real>
+ <key>circoSeparation</key>
+ <real>0.0</real>
+ <key>layoutEngine</key>
+ <string>dot</string>
+ <key>neatoSeparation</key>
+ <real>0.0</real>
+ <key>twopiSeparation</key>
+ <real>0.0</real>
+ </dict>
+ <key>LinksVisible</key>
+ <string>NO</string>
+ <key>MagnetsVisible</key>
+ <string>NO</string>
+ <key>MasterSheets</key>
+ <array/>
+ <key>ModificationDate</key>
+ <string>2008-11-17 11:41:28 -0700</string>
+ <key>Modifier</key>
+ <string>Preston Jackson</string>
+ <key>NotesVisible</key>
+ <string>NO</string>
+ <key>Orientation</key>
+ <integer>2</integer>
+ <key>OriginVisible</key>
+ <string>NO</string>
+ <key>PageBreaks</key>
+ <string>YES</string>
+ <key>PrintInfo</key>
+ <dict>
+ <key>NSBottomMargin</key>
+ <array>
+ <string>float</string>
+ <string>41</string>
+ </array>
+ <key>NSLeftMargin</key>
+ <array>
+ <string>float</string>
+ <string>18</string>
+ </array>
+ <key>NSPaperSize</key>
+ <array>
+ <string>size</string>
+ <string>{612, 792}</string>
+ </array>
+ <key>NSRightMargin</key>
+ <array>
+ <string>float</string>
+ <string>18</string>
+ </array>
+ <key>NSTopMargin</key>
+ <array>
+ <string>float</string>
+ <string>18</string>
+ </array>
+ </dict>
+ <key>PrintOnePage</key>
+ <false/>
+ <key>QuickLookPreview</key>
+ <data>
+ JVBERi0xLjMKJcTl8uXrp/Og0MTGCjQgMCBvYmoKPDwgL0xlbmd0aCA1IDAgUiAvRmls
+ dGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAGVlktvJEUQhO/9K5IbHFyuR9brioEV
+ nFh5JM5o5MWsPIA9IP4+X2b12gPrBbHWSt3lzkdFREb6Ud7Ko0R+asr+/+lOfpBfJIYW
+ /Z/8LNc35yTHsyROh/ZYxyTgfJTrN/zip7NcxVD3r1+e9oQbCd/J9fd3T8e7337/48cH
+ eSLjm3O2wEfR2ry8tiJ5hDJSVEk9pF7jkONJrr89JfnqV/p8uz1KK1YpJynNHlKVk2gJ
+ JZWqdqZz9iklBc21ZmkxlJoin0/vsEonTo6b9lC79iQzh6azSc7FYjINZWIkk4MCKtrG
+ ejhyRpZeRYcSNIbk7oXmtk5m4mRaD/NvYcOK1bKnpnkuu4qt6jqEVujLmtuOvFgdrXv7
+ mcjWQxut71ds5LcbAIADIcTkDgpt4TKmyigWoXNzVAYtfYzdUe5fOT25ACxMtZAQiPeX
+ xEVTSCkNeaBLDbXWDhwfnxH1QHJX0sfiulDDhhpMgMnUJZAGDLkCea3T6b+9T3K+N/pf
+ F6qL8+ZW0hYDjM4ESlFubyTlAFd/kvfwRKilj3IFRdTQHJsk6EwzW5UvDwBY1xf2cNVL
+ SDWiyTa2AyL8JgXr8fBOPv/sCzm8l68PNERtwm0wGIb4yTrK2LiYt3+rI5+uY1df7JW8
+ CD9tS/XNGUdxFSUs1e+yiQPuXPUMyVI9lL2qeh2bq16Rjet+qRdVLcWrceySz8+S30+A
+ zyTPTNiYWMQSe10Z64vY+/OoudZNus9dudwRqE+rVVty97v63bZd7iZHL7PkjnfYe5xw
+ vvTOAJtW5+gMv3vFB8RetF6yzYQ5x3/L08wKeQZ3t1pin5Fp1GpD0ORKy7AnlLN/kbPS
+ 2ofZwIlqwA1G35aT5d3JyGncLARwMKZb0Tt2gIAHLOBGpTJExgtaxZ/MjxbK+B8mYdQ0
+ 5QuYoSumBgvBBXEsP0n9khlidnI8xrK6LZqBzVm2bFzEhIjMiIwcPyGGeQqjdjrwT7h+
+ LYHiADxbwGHg6+Uux+3+4u1/I6yj5DSiaKw0CBBXpRSDluldCFM4zgHvPa9zujJygMR3
+ RXlB+JWt1t0PvmNg35PwHxsOE4mw1Weu0cykNci2JJjJhX+sVUm1pt4BgIOOr6HBGsLd
+ eYUt0uRFYFIEgAl4n6yrBqw6QuzKxtA0wdf4g/OZ2QWMAd4DfUgXOqHaYjtc4/Gjshmh
+ y/PP/YQ62VDzj4dlZttYGh2ZHAwCzaCeVcoaJty3VGm2b4bnZwuhC2LommlOA9lxF2ub
+ WDS6QrjdWjcjNZJ3Uzh/OyA6IjK7cIVwj0t8fPwuD05ya6b+F7C1v1cKZW5kc3RyZWFt
+ CmVuZG9iago1IDAgb2JqCjk4MwplbmRvYmoKMiAwIG9iago8PCAvVHlwZSAvUGFnZSAv
+ UGFyZW50IDMgMCBSIC9SZXNvdXJjZXMgNiAwIFIgL0NvbnRlbnRzIDQgMCBSIC9NZWRp
+ YUJveCBbMCAwIDUxMiA1MTJdCj4+CmVuZG9iago2IDAgb2JqCjw8IC9Qcm9jU2V0IFsg
+ L1BERiAvVGV4dCAvSW1hZ2VCIC9JbWFnZUMgL0ltYWdlSSBdIC9Db2xvclNwYWNlIDw8
+ IC9DczIgMTIgMCBSCi9DczEgNyAwIFIgPj4gL0V4dEdTdGF0ZSA8PCAvR3MxIDE3IDAg
+ UiAvR3MyIDE4IDAgUiA+PiAvRm9udCA8PCAvRjEuMCAxMSAwIFIKPj4gL1hPYmplY3Qg
+ PDwgL0ltMiAxMyAwIFIgL0ltMSA4IDAgUiAvSW0zIDE1IDAgUiA+PiAvU2hhZGluZyA8
+ PCAvU2gxIDEwIDAgUgo+PiA+PgplbmRvYmoKMTAgMCBvYmoKPDwgL0NvbG9yU3BhY2Ug
+ NyAwIFIgL1NoYWRpbmdUeXBlIDMgL0Nvb3JkcyBbIC0yNzEuMzA2MyAwIDAgLTI3MS4z
+ MDYzIDAgNTQwLjI2NApdIC9Eb21haW4gWyAwIDEgXSAvRXh0ZW5kIFsgZmFsc2UgZmFs
+ c2UgXSAvRnVuY3Rpb24gMTkgMCBSID4+CmVuZG9iagoxMyAwIG9iago8PCAvTGVuZ3Ro
+ IDE0IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2UgL1dpZHRoIDI1NiAv
+ SGVpZ2h0IDI1NiAvQ29sb3JTcGFjZQo3IDAgUiAvU01hc2sgMjAgMCBSIC9CaXRzUGVy
+ Q29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB7dABAQAA
+ CAKg/p+2Bx4QJpBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB
+ AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg
+ wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM
+ GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB
+ AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg
+ wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM
+ GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB
+ AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg
+ wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM
+ GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB
+ AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg
+ wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM
+ GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB
+ AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg
+ wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM
+ GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB
+ AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDDQBg4DBgwYMGDA
+ wNjAA65NNU0KZW5kc3RyZWFtCmVuZG9iagoxNCAwIG9iago4ODMKZW5kb2JqCjggMCBv
+ YmoKPDwgL0xlbmd0aCA5IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2Ug
+ L1dpZHRoIDkxMiAvSGVpZ2h0IDkyNiAvQ29sb3JTcGFjZQoyMiAwIFIgL1NNYXNrIDIz
+ IDAgUiAvQml0c1BlckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0
+ cmVhbQp4Ae3QgQAAAADDoPlTH+SFUGHAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY
+ MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED
+ BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA
+ wMvAAKraAAEKZW5kc3RyZWFtCmVuZG9iago5IDAgb2JqCjExMDcwCmVuZG9iagoxNSAw
+ IG9iago8PCAvTGVuZ3RoIDE2IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1h
+ Z2UgL1dpZHRoIDI1NiAvSGVpZ2h0IDI1NiAvQ29sb3JTcGFjZQo3IDAgUiAvU01hc2sg
+ MjUgMCBSIC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4K
+ c3RyZWFtCngB7dKBDQAgDMMw/n+6SHBGvA+aeXMKFAucd8XlNiuw8U9BuQD/5e/bzj8D
+ 5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+
+ vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889A
+ uQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/
+ bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQ
+ LsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv
+ 284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyU
+ C/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7
+ tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPl
+ AvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+
+ 7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5
+ AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79v
+ O/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1Au
+ wH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/b
+ zj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL
+ 8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2
+ 889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC
+ /Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t
+ /DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA
+ /+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287
+ /wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7A
+ f/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vO
+ PwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvw
+ X/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu3f/wUgwjJ6CmVu
+ ZHN0cmVhbQplbmRvYmoKMTYgMCBvYmoKMTIxNAplbmRvYmoKMjMgMCBvYmoKPDwgL0xl
+ bmd0aCAyNCAwIFIgL1R5cGUgL1hPYmplY3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCA5
+ MTIgL0hlaWdodCA5MjYgL0NvbG9yU3BhY2UKL0RldmljZUdyYXkgL0JpdHNQZXJDb21w
+ b25lbnQgOCAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHsnYlfTV37/288
+ j4xJUVJRGtCEoggliZCxyFQUlUiGNCgJIXOFyFTKlCljmUMlUaZK4XY/N31fv//jd621
+ 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
+ 5IiRegajjceajhs/cbKF5RRriMrp021nzJg5c6YdPmgByVkAHH/GDFvb6dOnTbWZYmUx
+ edIEM1OTMYYGo3R1ZNFIdjl6BI4sJGlEwtbHwEGwGzlcR1ffYLTRGIhJs/ETJk6abGFh
+ YWlpZWU1BR+0gOQsAI5vZWkJMTB50sTx5uMgFoGMo/RGjtCmuhHY2NPRCEEJi1aGkb8P
+ GjwUQhIoCTFpCEFpYmoKcWlmZm5uPh4ftIDkLACOb25mNm6cqanJ2DHGRqMNRunr6kAw
+ wkJ1IFmpqiAaCSXZiKSQJCEJmBypq6c/ygDC0tDIyMgYnjH4oAUkZgHi98ZGRoaGo0cb
+ QCjqQSwOBzIOgZQqKcdRUTSSiKS7kb/++huEJFByyFCtYRCUI3QgLHX19PT0uWcUPmgB
+ aViAc3lwf13dkSMhEodr01gEMrJo7FndSMOQ9y8MJJmQBDEJQQlRCWFJn+H4oAWkaAHG
+ /YcN09ICLA5mYpGsU3s4icOLQ94faUhCGR1gEjj5+8CBgwYNGjx48BD5MxQfTbSA/APh
+ n2QW+JkPJXvJEIhCiMNBA4GKvw8Y0B9iEZapqiUjLyJh3UqyO/36wdr1t/79IS5JYOKD
+ FpCyBSAKIBZ+I6FIY7Entzf4wdfdn2lQsmH5Kzy/4YMWkKgFiP+TOCSRCFjs0a3G7uKv
+ m39Ofh0eGAc+aAHpWoAJA3VDsZugxH+MFkALoAXQAmgBtABaAC2AFkALoAXQAmgBtABa
+ AC2AFkALoAXQAmgBtABaoFct8P/b5umcCmVuZHN0cmVhbQplbmRvYmoKMjQgMCBvYmoK
+ NDQ4NjUKZW5kb2JqCjIwIDAgb2JqCjw8IC9MZW5ndGggMjEgMCBSIC9UeXBlIC9YT2Jq
+ ZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMjU2IC9IZWlnaHQgMjU2IC9Db2xvclNw
+ YWNlCi9EZXZpY2VHcmF5IC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVE
+ ZWNvZGUgPj4Kc3RyZWFtCngB7dCFQQIAAABBsTswEAxsBQWD/XdzkL8b4Vartj8MGDBg
+ IDnwiwEDBgwkB34wYMCAgeTANwYMGDCQHFhiwIABA8mBBQYMGDCQHPjCgAEDBpIDnxgw
+ YMCAgeDAHAMGDBhIDswwYMCAgeTABwYMGDCQHHjHgAEDBpIDbxgwYMBAcuAVAwYMGEgO
+ vGDAgAEDBoIDzxgwYMBAcuAJAwYMGEgOPGLAgAEDyYEHDBgwYCA5MMWAAQMGkgP3GDBg
+ wEBy4A4DBgwYSA7cYsCAAQMGggM3GDBgwEByYIIBAwYMJAfGGDBgIDpwjQED2YERBgxk
+ B64wYCA7cIkBA9mBCwyEB84xYCA7MMSAgezAGQYMZAdOMWAgO3CCAQPZgWMMGMgOHGHA
+ QHbgEAMGsgMHGDCQHdjHQHhgDwMGsgO7GDCQHdjBgIHswDYGDGQHtjBgIDuwiQED2YEN
+ DBjIDqxjwEB2YIABA9mBNQyEB/4BLgXylgplbmRzdHJlYW0KZW5kb2JqCjIxIDAgb2Jq
+ CjM2NgplbmRvYmoKMjUgMCBvYmoKPDwgL0xlbmd0aCAyNiAwIFIgL1R5cGUgL1hPYmpl
+ Y3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCAyNTYgL0hlaWdodCAyNTYgL0NvbG9yU3Bh
+ Y2UKL0RldmljZUdyYXkgL0JpdHNQZXJDb21wb25lbnQgOCAvRmlsdGVyIC9GbGF0ZURl
+ Y29kZSA+PgpzdHJlYW0KeAHt3Qs/k2EYB+A5j2HOZpjz+TxzNkQSSSJJJJEkkkgStq/e
+ B+n6CPtte9/nuQ//K51KPT483N//vbv7c3v7++bm1/X11dXPy8sfFxffz8/Pzr6dnn49
+ OflyfPz56Ojw8NPBwcf9/Q97e7u773d23m1vv93aerO5ubHxen391dray9XVFysry8vP
+ l5aeLS4+XViYn38yNzc7OzOTTCanp6empiYnJyYmxsfHxsZGR0dGEonE8HA8Hh8aGhwc
+ HBjo7+/v6+vt7e3p6e7u6urq7Ozo6Ghvb2tra21taWlpbm5qampsbGiIxWL19XV1dbW1
+ 0Wi0piYSiVRXV1VVVVZWVJSXl5eVlZaWlpSEw+Hi4qKiosLCUChUUJCfHwwG8/Jyc3Nz
+ crKzs7OyMjMzMzICgbTP7/v3+/f/9/zz/Pf+8/53/nH+c/51/nf/cf9z/3X/V/9Q/1H/
+ Uv9T/1T/Vf9W/9f/0P/R/9L/0//U/9X/1v83/2D+w/yL+R/zT+a/zL+Z/zP/aP7T/Kv5
+ X/PP5r/Nv5v/t/9g/8P+i/0f+0/2v+y/2f+z/2j/0/6r/V/7z/a/7b/b/5d/IP9B/oX8
+ D/kn8l/k38j/kX8k/0n+lfwv+Wfy3+Tfyf+Tfyj/Uf6l/E/5p/Jf5d/K/5V/LP9Z/rX8
+ b/nn8t/l38v/5x/wH/gX/A/+Cf+Ff8P/4R/xn/hX/C/+Gf+Nf8f/4x/yH/mX/E/+Kf+V
+ f8v/5R/zn/nX/G/+Of89IxBIp1L8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8
+ d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/
+ zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf
+ +e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47
+ /53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/n
+ vwfSqdTjw3/sv/8DK6twSQplbmRzdHJlYW0KZW5kb2JqCjI2IDAgb2JqCjgxNwplbmRv
+ YmoKMTcgMCBvYmoKPDwgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMCA+PgplbmRvYmoKMTgg
+ MCBvYmoKPDwgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgplbmRvYmoKMjcgMCBvYmoK
+ PDwgL0xlbmd0aCAyOCAwIFIgL04gMSAvQWx0ZXJuYXRlIC9EZXZpY2VHcmF5IC9GaWx0
+ ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4AYVST0gUURz+zTYShIhBhXiIdwoJlSms
+ rKDadnVZlW1bldKiGGffuqOzM9Ob2TXFkwRdojx1D6JjdOzQoZuXosCsS9cgqSAIPHXo
+ +83s6iiEb3k73/v9/X7fe0RtnabvOylBVHNDlSulp25OTYuDHylFHdROWKYV+OlicYyx
+ 67mSv7vX1mfS2LLex7V2+/Y9tZVlYCHqLba3EPohkWYAH5mfKGWAs8Adlq/YPgE8WA6s
+ GvAjogMPmrkw09GcdKWyLZFT5qIoKq9iO0mu+/m5xr6LtYmD/lyPZtaOvbPqqtFM1LT3
+ RKG8D65EGc9fVPZsNRSnDeOcSEMaKfKu1d8rTMcRkSsQSgZSNWS5n2pOnXXgdRi7XbqT
+ 4/j2EKU+yWCoibXpspkdhX0AdirL7BDwBejxsmIP54F7Yf9bUcOTwCdhP2SHedatH/YX
+ rlPge4Q9NeDOFK7F8dqKH14tAUP3VCNojHNNxNPXOXOkiO8x1BmY90Y5pgsxd5aqEzeA
+ O2EfWapmCrFd+67qJe57AnfT4zvRmzkLXKAcSXKxFdkU0DwJWBR9i7BJDjw+zh5V4Heo
+ mMAcuYnczSj3HtURG2ejUoFWeo1Xxk/jufHF+GVsGM+Afqx213t8/+njFXXXtj48+Y16
+ 3DmuvZ0bVWFWcWUL3f/HMoSP2Sc5psHToVlYa9h25A+azEywDCjEfwU+l/qSE1Xc1e7t
+ uEUSzFA+LGwluktUbinU6j2DSqwcK9gAdnCSxCxaHLhTa7o5eHfYInpt+U1XsuuG/vr2
+ evva8h5tyqgpKBPNs0RmlLFbo+TdeNv9ZpERnzg6vue9ilrJ/klFED+FOVoq8hRV9FZQ
+ 1sRvZw5+G7Z+XD+l5/VB/TwJPa2f0a/ooxG+DHRJz8JzUR+jSfCwaSHiEqCKgzPUTlRj
+ jQPiKfHytFtkkf0PQBn9ZgplbmRzdHJlYW0KZW5kb2JqCjI4IDAgb2JqCjcwNAplbmRv
+ YmoKMTIgMCBvYmoKWyAvSUNDQmFzZWQgMjcgMCBSIF0KZW5kb2JqCjI5IDAgb2JqCjw8
+ IC9MZW5ndGggMzAgMCBSIC9OIDMgL0FsdGVybmF0ZSAvRGV2aWNlUkdCIC9GaWx0ZXIg
+ L0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Aa2TzWsTQRjGn02QCtZQi0jx4oJSPERdkhbb
+ W9t8SOwSlySlfhw02d1soslm3d1ErR561H+gFEQQPOjBmxc9tSeR4ieC9OBdUU9a6qGU
+ 9Z0Zd4Ng8eK7zMxvH555Z+adXSC+UXWcVgxA2/bd0qkZ+ey58/LAOiQcQgJJyFXdc6Y1
+ TSXLDrH5gdwU74+xXF9iz7c+fdtz58Li7bXDSw9bO0wK5YRLCwJSkoT9luApxjXBFcbX
+ fMcnT4Ox3qgaxLeIk26llCF+RJywBD9lXBP8gnFPt9jcdWLFNpo2ENtNPGGYnk48RWwZ
+ nt4mpjwS2u0O5Y+znEd1x6W58bfER1hdaKS4mgAmv5J+r69dPAE8WQMOaH1tdBkYvgQ8
+ m+xrP97wWkkjNa+eTvF00uBBYNdqEHwPgAHyb78Ogq3lINheojU2gJWi3nV73EsblF4B
+ /3oXZxbZgXd0Bgp+R39nURfuUoAHq0BlEVBpvEvj6E9g301AA+lUpnQ6bKKGJAN7szlV
+ lVNjynhem+fKf+zarS7dFY9h6gftWvEMjSPUPju+RpsS7PXKuZDrzXwhZKOanQ15oZEp
+ hlx386WQL1dPswPynKY9Vw7ZafFv//daM5Hf9HKRZ6FRYWfmHrdbmgv5Smc28htmNtqb
+ 3Sqy/4n7m34h2j+yyEGlR0YKY1AwjjyVfV58kzQDQ4+B+0PKyXR55eNLJvwRvnmd33Wm
+ 49xwm1bDl6fpjzSTcsHWjyfllKJM4BeDarM/CmVuZHN0cmVhbQplbmRvYmoKMzAgMCBv
+ YmoKNTY1CmVuZG9iagoyMiAwIG9iagpbIC9JQ0NCYXNlZCAyOSAwIFIgXQplbmRvYmoK
+ MzEgMCBvYmoKPDwgL0xlbmd0aCAzMiAwIFIgL04gMyAvQWx0ZXJuYXRlIC9EZXZpY2VS
+ R0IgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBhZRNSBRhGMf/s40EsQbR
+ lwjF0MEkVCYLUgLT9StTtmXVTAlinX13nRxnp5ndLUUihOiYdYwuVkSHiE7hoUOnOkQE
+ mXWJoKNFEAVeIrb/O5O7Y1S+MDO/eZ7/+3y9wwBVj1KOY0U0YMrOu8nemHZ6dEzb/BpV
+ qEYUXCnDczoSiQGfqZXP9Wv1LRRpWWqUsdb7NnyrdpkQUDQqd2QDPix5PODjki/knTw1
+ ZyQbE6k02SE3uEPJTvIt8tZsiMdDnBaeAVS1U5MzHJdxIjvILUUjK2M+IOt22rTJ76U9
+ 7RlT1LDfyDc5C9q48v1A2x5g04uKbcwDHtwDdtdVbPU1wM4RYPFQxfY96c9H2fXKyxxq
+ 9sMp0Rhr+lAqfa8DNt8Afl4vlX7cLpV+3mEO1vHUMgpu0deyMOUlENQb7Gb85Br9i4Oe
+ fFULsMA5jmwB+q8ANz8C+x8C2x8DiWpgqBWRy2w3uPLiIucCdOacadfMTuS1Zl0/onXw
+ aIXWZxtNDVrKsjTf5Wmu8IRbFOkmTFkFztlf23iPCnt4kE/2F7kkvO7frMylU12cJZrY
+ 1qe06OomN5DvZ8yePnI9r/cZt2c4YOWAme8bCjhyyrbiPBepidTY4/GTZMZXVCcfk/OQ
+ POcVB2VM334udSJBrqU9OZnrl5pd3Ns+MzHEM5KsWDMTnfHf/MYtJGXefdTcdSz/m2dt
+ kWcYhQUBEzbvNjQk0YsYGuHARQ4ZekwqTFqlX9BqwsPkX5UWEuVdFhW9WOGeFX/PeRS4
+ W8Y/hVgccw3lCJr+Tv+iL+sL+l3983xtob7imXPPmsara18ZV2aW1ci4QY0yvqwpiG+w
+ 2g56LWRpneIV9OSV9Y3h6jL2fG3Zo8kc4mp8NdSlCGVqxDjjya5l90WyxTfh51vL9q/p
+ Uft89klNJdeyunhmKfp8NlwNa/+zq2DSsqvw5I2QLjxroe5VD6p9aovaCk09prarbWoX
+ 346qA+Udw5yViQus22X1KfZgY5reyklXZovg38Ivhv+lXmEL1zQ0+Q9NuLmMaQnfEdw2
+ cIeU/8NfswMN3gplbmRzdHJlYW0KZW5kb2JqCjMyIDAgb2JqCjc5MgplbmRvYmoKNyAw
+ IG9iagpbIC9JQ0NCYXNlZCAzMSAwIFIgXQplbmRvYmoKMTkgMCBvYmoKPDwgL0xlbmd0
+ aCAzMyAwIFIgL0Z1bmN0aW9uVHlwZSAwIC9CaXRzUGVyU2FtcGxlIDggL1NpemUgWyAx
+ MzY1IF0gL0RvbWFpbgpbIDAgMSBdIC9SYW5nZSBbIDAgMSAwIDEgMCAxIF0gL0ZpbHRl
+ ciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBzcLXcQJAEAXByT8vjLDCO4HwIoobfRPA
+ K7arW4f22V1akT1anX3ae/t85hfWOcAih1jnCIscY50TLHKKdX5jkTMsco51LrDIJda5
+ wiLXWOcGQ7cYusPEPYYeMPEHQ4+YeMLQXww9Y+IFQ6+YeMPQO4Y+MPGJoX+Y+MLAf+tF
+ vN4KZW5kc3RyZWFtCmVuZG9iagozMyAwIG9iagoxMzAKZW5kb2JqCjMgMCBvYmoKPDwg
+ L1R5cGUgL1BhZ2VzIC9NZWRpYUJveCBbMCAwIDUxMiA1MTJdIC9Db3VudCAxIC9LaWRz
+ IFsgMiAwIFIgXSA+PgplbmRvYmoKMzQgMCBvYmoKPDwgL1R5cGUgL0NhdGFsb2cgL1Bh
+ Z2VzIDMgMCBSIC9WZXJzaW9uIC8xLjQgPj4KZW5kb2JqCjM1IDAgb2JqCjw8IC9MZW5n
+ dGggMzYgMCBSIC9MZW5ndGgxIDUwMzIgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3Ry
+ ZWFtCngB7TiNfxNVtufcOzOZNkDT0pbQAJl0aJWm2QIKlEJpaJKWNkILVDepsCb9kADF
+ Flo+n9Iqq2L4sK7a1d+u6K6rFaw6bZENRZevxdUVfn6ivLfig4X14z0R9Af7VoHOO5Nq
+ BZd9f8Gbk3vn3nPOveeej3vu3AACwBBoAw7u2mXhJpBhGmEOU7HWrmpR3tm342NqnwaQ
+ V9zetGhZ6hcziZ4QABDKFzWsvT0yatrbAEN/RzyvR+rDdWeXqscBhjVQf3KEEMPGCEQf
+ ZtDHRpa1rJH3Yxf1X6e+3NBYG4Y6SKc+8YC0LLymiT0q3k39Y9RX7ggvqx+59bl11P8H
+ 9dWmxuYWvQpuB0gaRf3sphX1TWeOuN6jvpfWEyYcEhjPEJCoADgMjNgLILbBGCojeA9k
+ 6hf0v1L5zCj9s4j2NCT31wDwEmpvi4+/qhJP0OyeAZT+L56rBlyzMzAQDE2Ncu2nHMxQ
+ AltABAsI8Az1zsJxuA8klGEyJEM+vIAfgQcq4R4shJ8TTwaUwQuwGz7ARfolEPSgfgCW
+ QidOIv0zoAAWwF44T3x/gp9CGNZAK96j30XWSYQxkAk3kDVXwDo4SRwJkAY2yMQsNo0f
+ gxE0sgHaYAPsFir0Z/XjNCaBoAKeheegH6sxou/STxEmH6aCD+bDHcT7AIqYJo7R/6Kf
+ 18/DRFrnQlhE8yyHx2AXmnA4OvEPrIh3wDjSsxGaIAod8Ev4M2bjm7xS3w4uggkwh+a7
+ FWpozi3QB2/ANyixGraGvcB28RX8ogDCUfFtyae/ph+hyBXBSmMKaMRtFE+Lad4H4R34
+ EP4KX8HXmIaZOAEn4wwsw6WsQTJLP9X30BgzjAaFbJADebTSAigmmEe2uBMehS54BV4l
+ OABfokJwHcE4vBWfwR48xZKYwg6zd9g3PJ/7+aOCVVgo3Ck+Jp6UcnSPvosslQJ2UGEK
+ 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
+ Q1aLMVmjIaszPu5HsroMcqchq8uQ1WXI6nR3xmWhb/H8YrJVoFuG4qBnwcC7l5kTyT0h
+ myNYHPfTNId1va2PXcACMNMpMoQ+PoZSMVzomumaSSQP88RJw4zvku9I1vXTHLY+LGAX
+ 4iQLoZMpaPAHLxmtZmdzc/PKFqpaVkLLSoJm4miOU5zQMsBMwfi/LcjbTgplbmRzdHJl
+ YW0KZW5kb2JqCjM2IDAgb2JqCjM0NTYKZW5kb2JqCjM3IDAgb2JqCjw8IC9UeXBlIC9G
+ b250RGVzY3JpcHRvciAvQXNjZW50IDc1MSAvQ2FwSGVpZ2h0IDY5NSAvRGVzY2VudCAt
+ MzE5IC9GbGFncyAzMgovRm9udEJCb3ggWy0xNzYgLTIyNyAxMDc2IDkxM10gL0ZvbnRO
+ YW1lIC9EUUJQSVYrQ2FsaXNNVEJvbCAvSXRhbGljQW5nbGUgMAovU3RlbVYgMCAvTGVh
+ ZGluZyAxMTEgL01heFdpZHRoIDEwOTQgL1hIZWlnaHQgNDcxIC9Gb250RmlsZTIgMzUg
+ MCBSID4+CmVuZG9iagozOCAwIG9iagpbIDM0NCBdCmVuZG9iagoxMSAwIG9iago8PCAv
+ VHlwZSAvRm9udCAvU3VidHlwZSAvVHJ1ZVR5cGUgL0Jhc2VGb250IC9EUUJQSVYrQ2Fs
+ aXNNVEJvbCAvRm9udERlc2NyaXB0b3IKMzcgMCBSIC9XaWR0aHMgMzggMCBSIC9GaXJz
+ dENoYXIgMzMgL0xhc3RDaGFyIDMzIC9FbmNvZGluZyAvTWFjUm9tYW5FbmNvZGluZwo+
+ PgplbmRvYmoKMSAwIG9iago8PCAvVGl0bGUgKFVudGl0bGVkKSAvQXV0aG9yIChQcmVz
+ dG9uIEphY2tzb24pIC9DcmVhdG9yIChPbW5pR3JhZmZsZSBQcm9mZXNzaW9uYWwpCi9Q
+ cm9kdWNlciAoTWFjIE9TIFggMTAuNS41IFF1YXJ0eiBQREZDb250ZXh0KSAvQ3JlYXRp
+ b25EYXRlIChEOjIwMDgxMTE3MTg0NzE0WjAwJzAwJykKL01vZERhdGUgKEQ6MjAwODEx
+ MTcxODQ3MTRaMDAnMDAnKSA+PgplbmRvYmoKeHJlZgowIDM5CjAwMDAwMDAwMDAgNjU1
+ MzUgZiAKMDAwMDA2OTExNCAwMDAwMCBuIAowMDAwMDAxMDk4IDAwMDAwIG4gCjAwMDAw
+ NjQ5NjMgMDAwMDAgbiAKMDAwMDAwMDAyMiAwMDAwMCBuIAowMDAwMDAxMDc5IDAwMDAw
+ IG4gCjAwMDAwMDEyMDIgMDAwMDAgbiAKMDAwMDA2NDYxNCAwMDAwMCBuIAowMDAwMDAy
+ Njk4IDAwMDAwIG4gCjAwMDAwMTM5NDkgMDAwMDAgbiAKMDAwMDAwMTQ1NSAwMDAwMCBu
+ IAowMDAwMDY4OTM5IDAwMDAwIG4gCjAwMDAwNjI5MzcgMDAwMDAgbiAKMDAwMDAwMTYx
+ MyAwMDAwMCBuIAowMDAwMDAyNjc4IDAwMDAwIG4gCjAwMDAwMTM5NzAgMDAwMDAgbiAK
+ MDAwMDAxNTM2NiAwMDAwMCBuIAowMDAwMDYyMDE5IDAwMDAwIG4gCjAwMDAwNjIwNjQg
+ MDAwMDAgbiAKMDAwMDA2NDY1MCAwMDAwMCBuIAowMDAwMDYwNDQ4IDAwMDAwIG4gCjAw
+ MDAwNjA5ODggMDAwMDAgbiAKMDAwMDA2MzY2MiAwMDAwMCBuIAowMDAwMDE1Mzg3IDAw
+ MDAwIG4gCjAwMDAwNjA0MjYgMDAwMDAgbiAKMDAwMDA2MTAwOCAwMDAwMCBuIAowMDAw
+ MDYxOTk5IDAwMDAwIG4gCjAwMDAwNjIxMDkgMDAwMDAgbiAKMDAwMDA2MjkxNyAwMDAw
+ MCBuIAowMDAwMDYyOTc0IDAwMDAwIG4gCjAwMDAwNjM2NDIgMDAwMDAgbiAKMDAwMDA2
+ MzY5OSAwMDAwMCBuIAowMDAwMDY0NTk0IDAwMDAwIG4gCjAwMDAwNjQ5NDMgMDAwMDAg
+ biAKMDAwMDA2NTA0NiAwMDAwMCBuIAowMDAwMDY1MTEwIDAwMDAwIG4gCjAwMDAwNjg2
+ NTYgMDAwMDAgbiAKMDAwMDA2ODY3NyAwMDAwMCBuIAowMDAwMDY4OTE1IDAwMDAwIG4g
+ CnRyYWlsZXIKPDwgL1NpemUgMzkgL1Jvb3QgMzQgMCBSIC9JbmZvIDEgMCBSIC9JRCBb
+ IDw0OWU2MjQzZGUwYzBiMTQ0NmRmMDQzNjRjNzc1ZGNlZj4KPDQ5ZTYyNDNkZTBjMGIx
+ NDQ2ZGYwNDM2NGM3NzVkY2VmPiBdID4+CnN0YXJ0eHJlZgo2OTMzNgolJUVPRgoxIDAg
+ b2JqCjw8L0F1dGhvciAoUHJlc3RvbiBKYWNrc29uKS9DcmVhdGlvbkRhdGUgKEQ6MjAw
+ ODExMTQyMzU4MDBaKS9DcmVhdG9yIChPbW5pR3JhZmZsZSBQcm9mZXNzaW9uYWwgNS4x
+ IHJjIDEpL01vZERhdGUgKEQ6MjAwODExMTcxODQxMDBaKS9Qcm9kdWNlciAoTWFjIE9T
+ IFggMTAuNS41IFF1YXJ0eiBQREZDb250ZXh0KS9UaXRsZSAoUmVwb3J0ZXJJY29uLmdy
+ YWZmbGUpPj4KZW5kb2JqCnhyZWYKMSAxCjAwMDAwNzAyNzQgMDAwMDAgbiAKdHJhaWxl
+ cgo8PC9JRCBbPDQ5ZTYyNDNkZTBjMGIxNDQ2ZGYwNDM2NGM3NzVkY2VmPiA8NDllNjI0
+ M2RlMGMwYjE0NDZkZjA0MzY0Yzc3NWRjZWY+XSAvSW5mbyAxIDAgUiAvUHJldiA2OTMz
+ NiAvUm9vdCAzNCAwIFIgL1NpemUgMzk+PgpzdGFydHhyZWYKNzA0OTgKJSVFT0YK
+ </data>
+ <key>QuickLookThumbnail</key>
+ <data>
+ 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
+ </data>
+ <key>ReadOnly</key>
+ <string>NO</string>
+ <key>RowAlign</key>
+ <integer>1</integer>
+ <key>RowSpacing</key>
+ <real>36</real>
+ <key>SheetTitle</key>
+ <string>Canvas 1</string>
+ <key>SmartAlignmentGuidesActive</key>
+ <string>NO</string>
+ <key>SmartDistanceGuidesActive</key>
+ <string>NO</string>
+ <key>UniqueID</key>
+ <integer>1</integer>
+ <key>UseEntirePage</key>
+ <false/>
+ <key>VPages</key>
+ <integer>1</integer>
+ <key>WindowInfo</key>
+ <dict>
+ <key>CurrentSheet</key>
+ <integer>0</integer>
+ <key>ExpandedCanvases</key>
+ <array>
+ <dict>
+ <key>name</key>
+ <string>Canvas 1</string>
+ </dict>
+ </array>
+ <key>Frame</key>
+ <string>{{2002, 20}, {1215, 1180}}</string>
+ <key>ListView</key>
+ <true/>
+ <key>OutlineWidth</key>
+ <integer>142</integer>
+ <key>RightSidebar</key>
+ <false/>
+ <key>Sidebar</key>
+ <true/>
+ <key>SidebarWidth</key>
+ <integer>157</integer>
+ <key>VisibleRegion</key>
+ <string>{{-4.5, 0.5}, {522, 535.5}}</string>
+ <key>Zoom</key>
+ <real>2</real>
+ <key>ZoomValues</key>
+ <array>
+ <array>
+ <string>Canvas 1</string>
+ <real>2</real>
+ <real>4</real>
+ </array>
+ </array>
+ </dict>
+ <key>saveQuickLookFiles</key>
+ <string>YES</string>
+</dict>
+</plist>
diff --git a/src/client/mac/sender/ReporterIcon.icns b/src/client/mac/sender/ReporterIcon.icns
new file mode 100644
index 00000000..e8c21242
--- /dev/null
+++ b/src/client/mac/sender/ReporterIcon.icns
Binary files differ
diff --git a/src/client/mac/sender/crash_report_sender-Info.plist b/src/client/mac/sender/crash_report_sender-Info.plist
new file mode 100644
index 00000000..1ded1261
--- /dev/null
+++ b/src/client/mac/sender/crash_report_sender-Info.plist
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.Breakpad.${PRODUCT_NAME:identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+</dict>
+</plist>
diff --git a/src/client/mac/sender/crash_report_sender.h b/src/client/mac/sender/crash_report_sender.h
new file mode 100644
index 00000000..76e6d3ea
--- /dev/null
+++ b/src/client/mac/sender/crash_report_sender.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This component uses the HTTPMultipartUpload of the breakpad project to send
+// the minidump and associated data to the crash reporting servers.
+// It will perform throttling based on the parameters passed to it and will
+// prompt the user to send the minidump.
+
+#include <Foundation/Foundation.h>
+
+#include "client/mac/Framework/Breakpad.h"
+
+#define kClientIdPreferenceKey @"clientid"
+
+@interface Reporter : NSObject {
+ @public
+ IBOutlet NSWindow *alertWindow; // The alert window
+
+ // Values bound in the XIB
+ NSString *headerMessage_; // Message notifying of the crash
+ NSString *reportMessage_; // Message explaining the crash report
+ NSString *commentsValue_; // Comments from the user
+ NSString *emailMessage_; // Message requesting user email
+ NSString *emailValue_; // Email from the user
+
+ @private
+ int configFile_; // File descriptor for config file
+ NSMutableDictionary *parameters_; // Key value pairs of data (STRONG)
+ NSData *minidumpContents_; // The data in the minidump (STRONG)
+ NSData *logFileData_; // An NSdata for the tar, bz2'd log file
+}
+
+// Stops the modal panel with an NSAlertDefaultReturn value. This is the action
+// invoked by the "Send Report" button.
+- (IBAction)sendReport:(id)sender;
+// Stops the modal panel with an NSAlertAlternateReturn value. This is the
+// action invoked by the "Cancel" button.
+- (IBAction)cancel:(id)sender;
+// Opens the Google Privacy Policy in the default web browser.
+- (IBAction)showPrivacyPolicy:(id)sender;
+
+// Delegate methods for the NSTextField for comments. We want to capture the
+// Return key and use it to send the message when no text has been entered.
+// Otherwise, we want Return to add a carriage return to the comments field.
+- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView
+ doCommandBySelector:(SEL)commandSelector;
+
+// Accessors to make bindings work
+- (NSString *)headerMessage;
+- (void)setHeaderMessage:(NSString *)value;
+
+- (NSString *)reportMessage;
+- (void)setReportMessage:(NSString *)value;
+
+- (NSString *)commentsValue;
+- (void)setCommentsValue:(NSString *)value;
+
+- (NSString *)emailMessage;
+- (void)setEmailMessage:(NSString *)value;
+
+- (NSString *)emailValue;
+- (void)setEmailValue:(NSString *)value;
+@end
diff --git a/src/client/mac/sender/crash_report_sender.m b/src/client/mac/sender/crash_report_sender.m
new file mode 100644
index 00000000..5d1b1e51
--- /dev/null
+++ b/src/client/mac/sender/crash_report_sender.m
@@ -0,0 +1,762 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <pwd.h>
+#import <sys/stat.h>
+#import <unistd.h>
+
+#import <Cocoa/Cocoa.h>
+#import <SystemConfiguration/SystemConfiguration.h>
+
+#import "common/mac/HTTPMultipartUpload.h"
+
+#import "crash_report_sender.h"
+#import "common/mac/GTMLogger.h"
+
+#define kLastSubmission @"LastSubmission"
+const int kMinidumpFileLengthLimit = 800000;
+
+#define kApplePrefsSyncExcludeAllKey @"com.apple.PreferenceSync.ExcludeAllSyncKeys"
+
+@interface Reporter(PrivateMethods)
++ (uid_t)consoleUID;
+
+- (id)initWithConfigurationFD:(int)fd;
+
+- (NSString *)readString;
+- (NSData *)readData:(ssize_t)length;
+
+- (BOOL)readConfigurationData;
+- (BOOL)readMinidumpData;
+- (BOOL)readLogFileData;
+
+- (BOOL)askUserPermissionToSend:(BOOL)shouldSubmitReport;
+- (BOOL)shouldSubmitReport;
+
+// Run an alert window with the given timeout. Returns NSAlertButtonDefault if
+// the timeout is exceeded. A timeout of 0 queues the message immediately in the
+// modal run loop.
+- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout;
+
+- (NSString*)clientID;
+@end
+
+@implementation Reporter
+//=============================================================================
++ (uid_t)consoleUID {
+ SCDynamicStoreRef store =
+ SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("Reporter"), NULL, NULL);
+ uid_t uid = -2; // Default to "nobody"
+ if (store) {
+ CFStringRef user = SCDynamicStoreCopyConsoleUser(store, &uid, NULL);
+
+ if (user)
+ CFRelease(user);
+ else
+ uid = -2;
+
+ CFRelease(store);
+ }
+
+ return uid;
+}
+
+//=============================================================================
+- (id)initWithConfigurationFD:(int)fd {
+ if ((self = [super init])) {
+ configFile_ = fd;
+ }
+
+ // Because the reporter is embedded in the framework (and many copies
+ // of the framework may exist) its not completely certain that the OS
+ // will obey the com.apple.PreferenceSync.ExcludeAllSyncKeys in our
+ // Info.plist. To make sure, also set the key directly if needed.
+ NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
+ if (![ud boolForKey:kApplePrefsSyncExcludeAllKey]) {
+ [ud setBool:YES forKey:kApplePrefsSyncExcludeAllKey];
+ }
+
+ return self;
+}
+
+//=============================================================================
+- (NSString *)readString {
+ NSMutableString *str = [NSMutableString stringWithCapacity:32];
+ char ch[2] = { 0 };
+
+ while (read(configFile_, &ch[0], 1) == 1) {
+ if (ch[0] == '\n') {
+ // Break if this is the first newline after reading some other string
+ // data.
+ if ([str length])
+ break;
+ } else {
+ [str appendString:[NSString stringWithUTF8String:ch]];
+ }
+ }
+
+ return str;
+}
+
+//=============================================================================
+- (NSData *)readData:(ssize_t)length {
+ NSMutableData *data = [NSMutableData dataWithLength:length];
+ char *bytes = (char *)[data bytes];
+
+ if (read(configFile_, bytes, length) != length)
+ return nil;
+
+ return data;
+}
+
+//=============================================================================
+- (BOOL)readConfigurationData {
+ parameters_ = [[NSMutableDictionary alloc] init];
+
+ while (1) {
+ NSString *key = [self readString];
+
+ if (![key length])
+ break;
+
+ // Read the data. Try to convert to a UTF-8 string, or just save
+ // the data
+ NSString *lenStr = [self readString];
+ ssize_t len = [lenStr intValue];
+ NSData *data = [self readData:len];
+ id value = [[NSString alloc] initWithData:data
+ encoding:NSUTF8StringEncoding];
+
+ [parameters_ setObject:value ? value : data forKey:key];
+ [value release];
+ }
+
+ // generate a unique client ID based on this host's MAC address
+ // then add a key/value pair for it
+ NSString *clientID = [self clientID];
+ [parameters_ setObject:clientID forKey:@"guid"];
+
+ close(configFile_);
+ configFile_ = -1;
+
+ return YES;
+}
+
+// Per user per machine
+- (NSString *)clientID {
+ NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
+ NSString *crashClientID = [ud stringForKey:kClientIdPreferenceKey];
+ if (crashClientID) {
+ return crashClientID;
+ }
+
+ // Otherwise, if we have no client id, generate one!
+ srandom([[NSDate date] timeIntervalSince1970]);
+ long clientId1 = random();
+ long clientId2 = random();
+ long clientId3 = random();
+ crashClientID = [NSString stringWithFormat:@"%x%x%x",
+ clientId1, clientId2, clientId3];
+
+ [ud setObject:crashClientID forKey:kClientIdPreferenceKey];
+ [ud synchronize];
+ return crashClientID;
+}
+
+//=============================================================================
+- (BOOL)readLogFileData {
+ unsigned int logFileCounter = 0;
+
+ NSString *logPath;
+ int logFileTailSize = [[parameters_ objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE]
+ intValue];
+
+ NSMutableArray *logFilenames; // An array of NSString, one per log file
+ logFilenames = [[NSMutableArray alloc] init];
+
+ char tmpDirTemplate[80] = "/tmp/CrashUpload-XXXXX";
+ char *tmpDir = mkdtemp(tmpDirTemplate);
+
+ // Construct key names for the keys we expect to contain log file paths
+ for(logFileCounter = 0;; logFileCounter++) {
+ NSString *logFileKey = [NSString stringWithFormat:@"%@%d",
+ @BREAKPAD_LOGFILE_KEY_PREFIX,
+ logFileCounter];
+
+ logPath = [parameters_ objectForKey:logFileKey];
+
+ // They should all be consecutive, so if we don't find one, assume
+ // we're done
+
+ if (!logPath) {
+ break;
+ }
+
+ NSData *entireLogFile = [[NSData alloc] initWithContentsOfFile:logPath];
+
+ if (entireLogFile == nil) {
+ continue;
+ }
+
+ NSRange fileRange;
+
+ // Truncate the log file, only if necessary
+
+ if ([entireLogFile length] <= logFileTailSize) {
+ fileRange = NSMakeRange(0, [entireLogFile length]);
+ } else {
+ fileRange = NSMakeRange([entireLogFile length] - logFileTailSize,
+ logFileTailSize);
+ }
+
+ char tmpFilenameTemplate[100];
+
+ // Generate a template based on the log filename
+ sprintf(tmpFilenameTemplate,"%s/%s-XXXX", tmpDir,
+ [[logPath lastPathComponent] fileSystemRepresentation]);
+
+ char *tmpFile = mktemp(tmpFilenameTemplate);
+
+ NSData *logSubdata = [entireLogFile subdataWithRange:fileRange];
+ NSString *tmpFileString = [NSString stringWithUTF8String:tmpFile];
+ [logSubdata writeToFile:tmpFileString atomically:NO];
+
+ [logFilenames addObject:[tmpFileString lastPathComponent]];
+ [entireLogFile release];
+ }
+
+ if ([logFilenames count] == 0) {
+ [logFilenames release];
+ logFileData_ = nil;
+ return NO;
+ }
+
+ // now, bzip all files into one
+ NSTask *tarTask = [[NSTask alloc] init];
+
+ [tarTask setCurrentDirectoryPath:[NSString stringWithUTF8String:tmpDir]];
+ [tarTask setLaunchPath:@"/usr/bin/tar"];
+
+ NSMutableArray *bzipArgs = [NSMutableArray arrayWithObjects:@"-cjvf",
+ @"log.tar.bz2",nil];
+ [bzipArgs addObjectsFromArray:logFilenames];
+
+ [logFilenames release];
+
+ [tarTask setArguments:bzipArgs];
+ [tarTask launch];
+ [tarTask waitUntilExit];
+ [tarTask release];
+
+ NSString *logTarFile = [NSString stringWithFormat:@"%s/log.tar.bz2",tmpDir];
+ logFileData_ = [[NSData alloc] initWithContentsOfFile:logTarFile];
+ if (logFileData_ == nil) {
+ GTMLoggerDebug(@"Cannot find temp tar log file: %@", logTarFile);
+ return NO;
+ }
+ return YES;
+
+}
+
+//=============================================================================
+- (BOOL)readMinidumpData {
+ NSString *minidumpDir = [parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
+ NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey];
+
+ if (![minidumpID length])
+ return NO;
+
+ NSString *path = [minidumpDir stringByAppendingPathComponent:minidumpID];
+ path = [path stringByAppendingPathExtension:@"dmp"];
+
+ // check the size of the minidump and limit it to a reasonable size
+ // before attempting to load into memory and upload
+ const char *fileName = [path fileSystemRepresentation];
+ struct stat fileStatus;
+
+ BOOL success = YES;
+
+ if (!stat(fileName, &fileStatus)) {
+ if (fileStatus.st_size > kMinidumpFileLengthLimit) {
+ fprintf(stderr, "Breakpad Reporter: minidump file too large " \
+ "to upload : %d\n", (int)fileStatus.st_size);
+ success = NO;
+ }
+ } else {
+ fprintf(stderr, "Breakpad Reporter: unable to determine minidump " \
+ "file length\n");
+ success = NO;
+ }
+
+ if (success) {
+ minidumpContents_ = [[NSData alloc] initWithContentsOfFile:path];
+ success = ([minidumpContents_ length] ? YES : NO);
+ }
+
+ if (!success) {
+ // something wrong with the minidump file -- delete it
+ unlink(fileName);
+ }
+
+ return success;
+}
+
+//=============================================================================
+- (BOOL)askUserPermissionToSend:(BOOL)shouldSubmitReport {
+ // Send without confirmation
+ if ([[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM] isEqualToString:@"YES"]) {
+ GTMLoggerDebug(@"Skipping confirmation and sending report");
+ return YES;
+ }
+
+ // Determine if we should create a text box for user feedback
+ BOOL shouldRequestComments =
+ [[parameters_ objectForKey:@BREAKPAD_REQUEST_COMMENTS]
+ isEqual:@"YES"];
+
+ NSString *display = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
+
+ if (![display length])
+ display = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
+
+ NSString *vendor = [parameters_ objectForKey:@BREAKPAD_VENDOR];
+
+ if (![vendor length])
+ vendor = @"Vendor not specified";
+
+ NSBundle *bundle = [NSBundle mainBundle];
+ [self setHeaderMessage:[NSString stringWithFormat:
+ NSLocalizedStringFromTableInBundle(@"headerFmt", nil,
+ bundle,
+ @""), vendor, display]];
+ NSString *defaultButtonTitle = nil;
+ NSString *otherButtonTitle = nil;
+ NSTimeInterval timeout = 60.0; // timeout value for the user notification
+
+ // Get the localized alert strings
+ // If we're going to submit a report, prompt the user if this is okay.
+ // Otherwise, just let them know that the app crashed.
+
+ if (shouldSubmitReport) {
+ NSString *msgFormat = NSLocalizedStringFromTableInBundle(@"msg",
+ nil,
+ bundle, @"");
+
+ [self setReportMessage:[NSString stringWithFormat:msgFormat, vendor]];
+
+ defaultButtonTitle = NSLocalizedStringFromTableInBundle(@"sendReportButton",
+ nil, bundle, @"");
+ otherButtonTitle = NSLocalizedStringFromTableInBundle(@"cancelButton", nil,
+ bundle, @"");
+
+ // Nominally use the report interval
+ timeout = [[parameters_ objectForKey:@BREAKPAD_REPORT_INTERVAL]
+ floatValue];
+ } else {
+ [self setReportMessage:NSLocalizedStringFromTableInBundle(@"noSendMsg", nil,
+ bundle, @"")];
+ defaultButtonTitle = NSLocalizedStringFromTableInBundle(@"noSendButton",
+ nil, bundle, @"");
+ timeout = 60.0;
+ }
+ // show the notification for at least one minute
+ if (timeout < 60.0) {
+ timeout = 60.0;
+ }
+
+ // Initialize Cocoa, needed to display the alert
+ NSApplicationLoad();
+
+ int buttonPressed = NSAlertAlternateReturn;
+
+ if (shouldRequestComments) {
+ BOOL didLoadNib = [NSBundle loadNibNamed:@"Breakpad" owner:self];
+ if (didLoadNib) {
+ // Append the request for comments to the |reportMessage| string.
+ NSString *commentsMessage =
+ NSLocalizedStringFromTableInBundle(@"commentsMsg", nil, bundle, @"");
+ [self setReportMessage:[NSString stringWithFormat:@"%@\n\n%@",
+ [self reportMessage],
+ commentsMessage]];
+
+ // Add the request for email address.
+ [self setEmailMessage:
+ NSLocalizedStringFromTableInBundle(@"emailMsg", nil, bundle, @"")];
+
+ // Run the alert
+ buttonPressed = [self runModalWindow:alertWindow withTimeout:timeout];
+
+ // Extract info from the user into the parameters_ dictionary
+ if ([self commentsValue]) {
+ [parameters_ setObject:[self commentsValue]
+ forKey:@BREAKPAD_COMMENTS];
+ }
+ if ([self emailValue]) {
+ [parameters_ setObject:[self emailValue]
+ forKey:@BREAKPAD_EMAIL];
+ }
+ }
+ } else {
+ // Create an alert panel to tell the user something happened
+ NSPanel* alert = NSGetAlertPanel([self headerMessage],
+ [self reportMessage],
+ defaultButtonTitle,
+ otherButtonTitle, nil);
+
+ // Pop the alert with an automatic timeout, and wait for the response
+ buttonPressed = [self runModalWindow:alert withTimeout:timeout];
+
+ // Release the panel memory
+ NSReleaseAlertPanel(alert);
+ }
+ return buttonPressed == NSAlertDefaultReturn;
+}
+
+- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout {
+ // Queue a |stopModal| message to be performed in |timeout| seconds.
+ [NSApp performSelector:@selector(stopModal)
+ withObject:nil
+ afterDelay:timeout];
+
+ // Run the window modally and wait for either a |stopModal| message or a
+ // button click.
+ [NSApp activateIgnoringOtherApps:YES];
+ int returnMethod = [NSApp runModalForWindow:window];
+
+ // Cancel the pending |stopModal| message.
+ if (returnMethod != NSRunStoppedResponse) {
+ [NSObject cancelPreviousPerformRequestsWithTarget:NSApp
+ selector:@selector(stopModal)
+ object:nil];
+ }
+ return returnMethod;
+}
+
+- (IBAction)sendReport:(id)sender {
+ [alertWindow orderOut:self];
+ // Use NSAlertDefaultReturn so that the return value of |runModalWithWindow|
+ // matches the AppKit function NSRunAlertPanel()
+ [NSApp stopModalWithCode:NSAlertDefaultReturn];
+}
+
+// UI Button Actions
+//=============================================================================
+- (IBAction)cancel:(id)sender {
+ [alertWindow orderOut:self];
+ // Use NSAlertDefaultReturn so that the return value of |runModalWithWindow|
+ // matches the AppKit function NSRunAlertPanel()
+ [NSApp stopModalWithCode:NSAlertAlternateReturn];
+}
+
+- (IBAction)showPrivacyPolicy:(id)sender {
+ // Get the localized privacy policy URL and open it in the default browser.
+ NSURL* privacyPolicyURL =
+ [NSURL URLWithString:NSLocalizedStringFromTableInBundle(
+ @"privacyPolicyURL", nil, [NSBundle mainBundle], @"")];
+ [[NSWorkspace sharedWorkspace] openURL:privacyPolicyURL];
+}
+
+// Text Field Delegate Methods
+//=============================================================================
+- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView
+ doCommandBySelector:(SEL)commandSelector {
+ BOOL result = NO;
+ // If the user has entered text, don't end editing on "return"
+ if (commandSelector == @selector(insertNewline:)
+ && [[textView string] length] > 0) {
+ [textView insertNewlineIgnoringFieldEditor:self];
+ result = YES;
+ }
+ return result;
+}
+
+// Accessors
+//=============================================================================
+- (NSString *)headerMessage {
+ return [[headerMessage_ retain] autorelease];
+}
+
+- (void)setHeaderMessage:(NSString *)value {
+ if (headerMessage_ != value) {
+ [headerMessage_ autorelease];
+ headerMessage_ = [value copy];
+ }
+}
+
+- (NSString *)reportMessage {
+ return [[reportMessage_ retain] autorelease];
+}
+
+- (void)setReportMessage:(NSString *)value {
+ if (reportMessage_ != value) {
+ [reportMessage_ autorelease];
+ reportMessage_ = [value copy];
+ }
+}
+
+- (NSString *)commentsValue {
+ return [[commentsValue_ retain] autorelease];
+}
+
+- (void)setCommentsValue:(NSString *)value {
+ if (commentsValue_ != value) {
+ [commentsValue_ autorelease];
+ commentsValue_ = [value copy];
+ }
+}
+
+- (NSString *)emailMessage {
+ return [[emailMessage_ retain] autorelease];
+}
+
+- (void)setEmailMessage:(NSString *)value {
+ if (emailMessage_ != value) {
+ [emailMessage_ autorelease];
+ emailMessage_ = [value copy];
+ }
+}
+
+- (NSString *)emailValue {
+ return [[emailValue_ retain] autorelease];
+}
+
+- (void)setEmailValue:(NSString *)value {
+ if (emailValue_ != value) {
+ [emailValue_ autorelease];
+ emailValue_ = [value copy];
+ }
+}
+
+//=============================================================================
+- (BOOL)shouldSubmitReport {
+ float interval = [[parameters_ objectForKey:@BREAKPAD_REPORT_INTERVAL]
+ floatValue];
+ NSString *program = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
+ NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
+ NSMutableDictionary *programDict =
+ [NSMutableDictionary dictionaryWithDictionary:[ud dictionaryForKey:program]];
+ NSNumber *lastTimeNum = [programDict objectForKey:kLastSubmission];
+ NSTimeInterval lastTime = lastTimeNum ? [lastTimeNum floatValue] : 0;
+ NSTimeInterval now = CFAbsoluteTimeGetCurrent();
+ NSTimeInterval spanSeconds = (now - lastTime);
+
+ [programDict setObject:[NSNumber numberWithFloat:now] forKey:kLastSubmission];
+ [ud setObject:programDict forKey:program];
+ [ud synchronize];
+
+ // If we've specified an interval and we're within that time, don't ask the
+ // user if we should report
+ GTMLoggerDebug(@"Reporter Interval: %f", interval);
+ if (interval > spanSeconds) {
+ GTMLoggerDebug(@"Within throttling interval, not sending report");
+ return NO;
+ }
+ return YES;
+}
+
+//=============================================================================
+- (void)report {
+ NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]];
+ HTTPMultipartUpload *upload = [[HTTPMultipartUpload alloc] initWithURL:url];
+ NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary];
+
+ // Set the known parameters. This should be kept up to date with the
+ // parameters defined in the Breakpad.h list of parameters. The intent
+ // is so that if there's a parameter that's not in this list, we consider
+ // it to be a "user-defined" parameter and we'll upload it to the server.
+ NSSet *knownParameters =
+ [NSSet setWithObjects:@kReporterMinidumpDirectoryKey,
+ @kReporterMinidumpIDKey, @BREAKPAD_PRODUCT_DISPLAY,
+ @BREAKPAD_PRODUCT, @BREAKPAD_VERSION, @BREAKPAD_URL,
+ @BREAKPAD_REPORT_INTERVAL, @BREAKPAD_SKIP_CONFIRM,
+ @BREAKPAD_SEND_AND_EXIT, @BREAKPAD_REPORTER_EXE_LOCATION,
+ @BREAKPAD_INSPECTOR_LOCATION, @BREAKPAD_LOGFILES,
+ @BREAKPAD_LOGFILE_UPLOAD_SIZE, @BREAKPAD_EMAIL,
+ @BREAKPAD_REQUEST_COMMENTS, @BREAKPAD_COMMENTS,
+ @BREAKPAD_VENDOR, nil];
+
+ // Add parameters
+ [uploadParameters setObject:[parameters_ objectForKey:@BREAKPAD_PRODUCT]
+ forKey:@"prod"];
+ [uploadParameters setObject:[parameters_ objectForKey:@BREAKPAD_VERSION]
+ forKey:@"ver"];
+
+ if ([parameters_ objectForKey:@BREAKPAD_EMAIL]) {
+ [uploadParameters setObject:[parameters_ objectForKey:@BREAKPAD_EMAIL] forKey:@"email"];
+ }
+
+ NSString* comments = [parameters_ objectForKey:@BREAKPAD_COMMENTS];
+ if (comments != nil) {
+ [uploadParameters setObject:comments forKey:@"comments"];
+ }
+ // Add any user parameters
+ NSArray *parameterKeys = [parameters_ allKeys];
+ int keyCount = [parameterKeys count];
+ for (int i = 0; i < keyCount; ++i) {
+ NSString *key = [parameterKeys objectAtIndex:i];
+ if (![knownParameters containsObject:key] &&
+ ![key hasPrefix:@BREAKPAD_LOGFILE_KEY_PREFIX])
+ [uploadParameters setObject:[parameters_ objectForKey:key] forKey:key];
+ }
+ [upload setParameters:uploadParameters];
+ // Add minidump file
+ if (minidumpContents_) {
+ [upload addFileContents:minidumpContents_ name:@"upload_file_minidump"];
+
+ // Send it
+ NSError *error = nil;
+ NSData *data = [upload send:&error];
+ NSString *result = [[NSString alloc] initWithData:data
+ encoding:NSUTF8StringEncoding];
+ const char *reportID = "ERR";
+
+ if (error)
+ fprintf(stderr, "Breakpad Reporter: Send Error: %s\n",
+ [[error description] UTF8String]);
+ else
+ reportID = [result UTF8String];
+
+ // rename the minidump file according to the id returned from the server
+ NSString *minidumpDir = [parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
+ NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey];
+
+ NSString *srcString = [NSString stringWithFormat:@"%@/%@.dmp",
+ minidumpDir, minidumpID];
+ NSString *destString = [NSString stringWithFormat:@"%@/%s.dmp",
+ minidumpDir, reportID];
+
+ const char *src = [srcString fileSystemRepresentation];
+ const char *dest = [destString fileSystemRepresentation];
+
+ if (rename(src, dest) == 0) {
+ fprintf(stderr, "Breakpad Reporter: Renamed %s to %s after successful " \
+ "upload\n",src, dest);
+ }
+ else {
+ // can't rename - don't worry - it's not important for users
+ fprintf(stderr, "Breakpad Reporter: successful upload report ID = %s\n",
+ reportID );
+ }
+ [result release];
+ }
+
+ if (logFileData_) {
+ HTTPMultipartUpload *logUpload = [[HTTPMultipartUpload alloc] initWithURL:url];
+
+ [uploadParameters setObject:@"log" forKey:@"type"];
+ [logUpload setParameters:uploadParameters];
+ [logUpload addFileContents:logFileData_ name:@"log"];
+
+ NSError *error = nil;
+ NSData *data = [logUpload send:&error];
+ NSString *result = [[NSString alloc] initWithData:data
+ encoding:NSUTF8StringEncoding];
+ [result release];
+ [logUpload release];
+ }
+
+ [upload release];
+}
+
+//=============================================================================
+- (void)dealloc {
+ [parameters_ release];
+ [minidumpContents_ release];
+ [logFileData_ release];
+ [super dealloc];
+}
+@end
+
+//=============================================================================
+int main(int argc, const char *argv[]) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ // The expectation is that there will be one argument which is the path
+ // to the configuration file
+ if (argc != 2) {
+ exit(1);
+ }
+
+ // Open the file before (potentially) switching to console user
+ int configFile = open(argv[1], O_RDONLY, 0600);
+
+ // we want to avoid a build-up of old config files even if they
+ // have been incorrectly written by the framework
+ unlink(argv[1]);
+
+ if (configFile == -1) {
+ exit(1);
+ }
+
+ Reporter *reporter = [[Reporter alloc] initWithConfigurationFD:configFile];
+
+ // Gather the configuration data
+ if (![reporter readConfigurationData]) {
+ exit(1);
+ }
+
+ // Read the minidump into memory before we (potentially) switch from the
+ // root user
+ [reporter readMinidumpData];
+
+ [reporter readLogFileData];
+
+ // only submit a report if we have not recently crashed in the past
+ BOOL shouldSubmitReport = [reporter shouldSubmitReport];
+ BOOL okayToSend = NO;
+
+ // ask user if we should send
+ if (shouldSubmitReport) {
+ okayToSend = [reporter askUserPermissionToSend:shouldSubmitReport];
+ }
+
+ // If we're running as root, switch over to nobody
+ if (getuid() == 0 || geteuid() == 0) {
+ struct passwd *pw = getpwnam("nobody");
+
+ // If we can't get a non-root uid, don't send the report
+ if (!pw)
+ exit(0);
+
+ if (setgid(pw->pw_gid) == -1)
+ exit(0);
+
+ if (setuid(pw->pw_uid) == -1)
+ exit(0);
+ }
+
+ if (okayToSend && shouldSubmitReport) {
+ [reporter report];
+ }
+
+ // Cleanup
+ [reporter release];
+ [pool release];
+
+ return 0;
+}
diff --git a/src/client/mac/testapp/Controller.h b/src/client/mac/testapp/Controller.h
new file mode 100644
index 00000000..7b3be2d6
--- /dev/null
+++ b/src/client/mac/testapp/Controller.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Cocoa/Cocoa.h>
+
+#import <Breakpad/Breakpad.h>
+
+enum BreakpadForkBehavior {
+ DONOTHING = 0,
+ UNINSTALL,
+ RESETEXCEPTIONPORT
+};
+
+enum BreakpadForkTestCrashPoint {
+ DURINGLAUNCH = 5,
+ AFTERLAUNCH = 6,
+ BETWEENFORKEXEC = 7
+};
+
+@interface Controller : NSObject {
+ IBOutlet NSWindow *window_;
+ IBOutlet NSWindow *forkTestOptions_;
+
+ BreakpadRef breakpad_;
+
+ enum BreakpadForkBehavior bpForkOption;
+
+ BOOL useVFork;
+ enum BreakpadForkTestCrashPoint progCrashPoint;
+}
+
+- (IBAction)crash:(id)sender;
+- (IBAction)forkTestOptions:(id)sender;
+- (IBAction)forkTestGo:(id)sender;
+- (IBAction)showForkTestWindow:(id) sender;
+- (void)generateReportWithoutCrash:(id)sender;
+- (void)awakeFromNib;
+
+@end
diff --git a/src/client/mac/testapp/Controller.m b/src/client/mac/testapp/Controller.m
new file mode 100644
index 00000000..e548c917
--- /dev/null
+++ b/src/client/mac/testapp/Controller.m
@@ -0,0 +1,257 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Breakpad/Breakpad.h>
+
+#import "Controller.h"
+#import "TestClass.h"
+#include <unistd.h>
+#include <mach/mach.h>
+
+@implementation Controller
+
+- (void)causeCrash {
+ float *aPtr = nil;
+ NSLog(@"Crash!");
+ NSLog(@"Bad programmer: %f", *aPtr);
+}
+
+- (void)generateReportWithoutCrash:(id)sender {
+ BreakpadGenerateAndSendReport(breakpad_);
+}
+
+- (IBAction)showForkTestWindow:(id) sender {
+ [forkTestOptions_ setIsVisible:YES];
+}
+
+- (IBAction)forkTestOptions:(id)sender {
+ int tag = [[sender selectedCell] tag];
+ NSLog(@"sender tag: %d", tag);
+ if (tag <= 2) {
+ bpForkOption = tag;
+ }
+
+ if (tag == 3) {
+ useVFork = NO;
+ }
+
+ if (tag == 4) {
+ useVFork = YES;
+ }
+
+ if (tag >= 5 && tag <= 7) {
+ progCrashPoint = tag;
+ }
+
+}
+
+- (IBAction)forkTestGo:(id)sender {
+
+ NSString *resourcePath = [[NSBundle bundleForClass:
+ [self class]] resourcePath];
+ NSString *execProgname;
+ if (progCrashPoint == DURINGLAUNCH) {
+ execProgname = [resourcePath stringByAppendingString:@"/crashduringload"];
+ } else if (progCrashPoint == AFTERLAUNCH) {
+ execProgname = [resourcePath stringByAppendingString:@"/crashInMain"];
+ }
+
+ const char *progName = NULL;
+ if (progCrashPoint != BETWEENFORKEXEC) {
+ progName = [execProgname UTF8String];
+ }
+
+ int pid;
+
+ if (bpForkOption == UNINSTALL) {
+ BreakpadRelease(breakpad_);
+ }
+
+ if (useVFork) {
+ pid = vfork();
+ } else {
+ pid = fork();
+ }
+
+ if (pid == 0) {
+ sleep(3);
+ NSLog(@"Child continuing");
+ FILE *fd = fopen("/tmp/childlog.txt","wt");
+ kern_return_t kr;
+ if (bpForkOption == RESETEXCEPTIONPORT) {
+ kr = task_set_exception_ports(mach_task_self(),
+ EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION |
+ EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT,
+ MACH_PORT_NULL,
+ EXCEPTION_DEFAULT,
+ THREAD_STATE_NONE);
+ fprintf(fd,"task_set_exception_ports returned %d\n", kr);
+ }
+
+ if (progCrashPoint == BETWEENFORKEXEC) {
+ fprintf(fd,"crashing post-fork\n");
+ int *a = NULL;
+ printf("%d\n",*a++);
+ }
+
+ fprintf(fd,"about to call exec with %s\n", progName);
+ fclose(fd);
+ int i = execl(progName, progName, NULL);
+ fprintf(fd, "exec returned! %d\n", i);
+ fclose(fd);
+ }
+}
+
+- (IBAction)crash:(id)sender {
+ int tag = [sender tag];
+
+ if (tag == 1) {
+ [NSObject cancelPreviousPerformRequestsWithTarget:self];
+ [self performSelector:@selector(causeCrash) withObject:nil afterDelay:10];
+ [sender setState:NSOnState];
+ return;
+ }
+
+ if (tag == 2 && breakpad_) {
+ BreakpadRelease(breakpad_);
+ breakpad_ = NULL;
+ return;
+ }
+
+ [self causeCrash];
+}
+
+- (void)anotherThread {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ TestClass *tc = [[TestClass alloc] init];
+
+ [tc wait];
+
+ [pool release];
+}
+
+- (void)awakeFromNib {
+ NSBundle *bundle = [NSBundle mainBundle];
+ NSDictionary *info = [bundle infoDictionary];
+
+
+ breakpad_ = BreakpadCreate(info);
+
+ // Do some unit tests with keys
+ // first a series of bogus values
+ BreakpadSetKeyValue(breakpad_, nil, @"bad2");
+ BreakpadSetKeyValue(nil, @"bad3", @"bad3");
+
+ // Now some good ones
+ BreakpadSetKeyValue(breakpad_,@"key1", @"value1");
+ BreakpadSetKeyValue(breakpad_,@"key2", @"value2");
+ BreakpadSetKeyValue(breakpad_,@"key3", @"value3");
+
+ // Look for a bogus one that we didn't try to set
+ NSString *test = BreakpadKeyValue(breakpad_, @"bad4");
+ if (test) {
+ NSLog(@"Bad BreakpadKeyValue (bad4)");
+ }
+
+ // Look for a bogus one we did try to set
+ test = BreakpadKeyValue(breakpad_, @"bad1");
+ if (test) {
+ NSLog(@"Bad BreakpadKeyValue (bad1)");
+ }
+
+ // Test some bad args for BreakpadKeyValue
+ test = BreakpadKeyValue(nil, @"bad5");
+ if (test) {
+ NSLog(@"Bad BreakpadKeyValue (bad5)");
+ }
+
+ test = BreakpadKeyValue(breakpad_, nil);
+ if (test) {
+ NSLog(@"Bad BreakpadKeyValue (nil)");
+ }
+
+ // Find some we did set
+ test = BreakpadKeyValue(breakpad_, @"key1");
+ if (![test isEqualToString:@"value1"]) {
+ NSLog(@"Can't find BreakpadKeyValue (key1)");
+ }
+ test = BreakpadKeyValue(breakpad_, @"key2");
+ if (![test isEqualToString:@"value2"]) {
+ NSLog(@"Can't find BreakpadKeyValue (key2)");
+ }
+ test = BreakpadKeyValue(breakpad_, @"key3");
+ if (![test isEqualToString:@"value3"]) {
+ NSLog(@"Can't find BreakpadKeyValue (key3)");
+ }
+
+ // Bad args for BreakpadRemoveKeyValue
+ BreakpadRemoveKeyValue(nil, @"bad6");
+ BreakpadRemoveKeyValue(breakpad_, nil);
+
+ // Remove one that is valid
+ BreakpadRemoveKeyValue(breakpad_, @"key3");
+
+ // Try and find it
+ test = BreakpadKeyValue(breakpad_, @"key3");
+ if (test) {
+ NSLog(@"Shouldn't find BreakpadKeyValue (key3)");
+ }
+
+ // Try and remove it again
+ BreakpadRemoveKeyValue(breakpad_, @"key3");
+
+ // Try removal by setting to nil
+ BreakpadSetKeyValue(breakpad_,@"key2", nil);
+ // Try and find it
+ test = BreakpadKeyValue(breakpad_, @"key2");
+ if (test) {
+ NSLog(@"Shouldn't find BreakpadKeyValue (key2)");
+ }
+
+ [NSThread detachNewThreadSelector:@selector(anotherThread)
+ toTarget:self withObject:nil];
+
+ NSUserDefaults *args = [NSUserDefaults standardUserDefaults];
+
+ // If the user specified autocrash on the command line, toggle
+ // Breakpad to not confirm and crash immediately. This is for
+ // automated testing.
+ if ([args boolForKey:@"autocrash"]) {
+ BreakpadSetKeyValue(breakpad_,
+ @BREAKPAD_SKIP_CONFIRM,
+ @"YES");
+ [self causeCrash];
+ }
+
+ progCrashPoint = DURINGLAUNCH;
+ [window_ center];
+ [window_ makeKeyAndOrderFront:self];
+}
+
+@end
diff --git a/src/client/mac/testapp/English.lproj/InfoPlist.strings b/src/client/mac/testapp/English.lproj/InfoPlist.strings
new file mode 100644
index 00000000..b8c6c6bf
--- /dev/null
+++ b/src/client/mac/testapp/English.lproj/InfoPlist.strings
Binary files differ
diff --git a/src/client/mac/testapp/English.lproj/MainMenu.nib/classes.nib b/src/client/mac/testapp/English.lproj/MainMenu.nib/classes.nib
new file mode 100644
index 00000000..ad86b716
--- /dev/null
+++ b/src/client/mac/testapp/English.lproj/MainMenu.nib/classes.nib
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBClasses</key>
+ <array>
+ <dict>
+ <key>ACTIONS</key>
+ <dict>
+ <key>crash</key>
+ <string>id</string>
+ <key>forkTestGo</key>
+ <string>id</string>
+ <key>forkTestOptions</key>
+ <string>id</string>
+ <key>generateReportWithoutCrash</key>
+ <string>id</string>
+ <key>showForkTestWindow</key>
+ <string>id</string>
+ </dict>
+ <key>CLASS</key>
+ <string>Controller</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>OUTLETS</key>
+ <dict>
+ <key>forkTestOptions_</key>
+ <string>NSWindow</string>
+ <key>window_</key>
+ <string>NSWindow</string>
+ </dict>
+ <key>SUPERCLASS</key>
+ <string>NSObject</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>FirstResponder</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSObject</string>
+ </dict>
+ </array>
+ <key>IBVersion</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/src/client/mac/testapp/English.lproj/MainMenu.nib/info.nib b/src/client/mac/testapp/English.lproj/MainMenu.nib/info.nib
new file mode 100644
index 00000000..839043e0
--- /dev/null
+++ b/src/client/mac/testapp/English.lproj/MainMenu.nib/info.nib
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBFramework Version</key>
+ <string>670</string>
+ <key>IBLastKnownRelativeProjectPath</key>
+ <string>../GoogleBreakpadTest.xcodeproj</string>
+ <key>IBOldestOS</key>
+ <integer>5</integer>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>221</integer>
+ <integer>29</integer>
+ <integer>2</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>9F33</string>
+ <key>targetFramework</key>
+ <string>IBCocoaFramework</string>
+</dict>
+</plist>
diff --git a/src/client/mac/testapp/English.lproj/MainMenu.nib/keyedobjects.nib b/src/client/mac/testapp/English.lproj/MainMenu.nib/keyedobjects.nib
new file mode 100644
index 00000000..1c6baaf7
--- /dev/null
+++ b/src/client/mac/testapp/English.lproj/MainMenu.nib/keyedobjects.nib
Binary files differ
diff --git a/src/client/mac/testapp/Info.plist b/src/client/mac/testapp/Info.plist
new file mode 100644
index 00000000..76d70f21
--- /dev/null
+++ b/src/client/mac/testapp/Info.plist
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string>bomb</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.Google.BreakpadTest</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+ <key>BreakpadProductDisplay</key>
+ <string>Google Breakpad Tester</string>
+ <key>BreakpadProduct</key>
+ <string>Breakpad_Tester</string>
+ <key>BreakpadVersion</key>
+ <string>1.2.3.4</string>
+ <key>BreakpadReportInterval</key>
+ <string>10</string>
+ <key>BreakpadSkipConfirm</key>
+ <string>NO</string>
+ <key>BreakpadSendAndExit</key>
+ <string>YES</string>
+ <key>BreakpadRequestComments</key>
+ <string>YES</string>
+ <key>BreakpadVendor</key>
+ <string>Foo Bar Corp, Incorporated, LTD, LLC</string>
+ <key>LSUIElement</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/src/client/mac/testapp/TestClass.h b/src/client/mac/testapp/TestClass.h
new file mode 100644
index 00000000..0a6d736d
--- /dev/null
+++ b/src/client/mac/testapp/TestClass.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Cocoa/Cocoa.h>
+
+@interface TestClass : NSObject {
+}
+
+- (void)wait;
+
+@end
diff --git a/src/client/mac/testapp/TestClass.mm b/src/client/mac/testapp/TestClass.mm
new file mode 100644
index 00000000..cade633e
--- /dev/null
+++ b/src/client/mac/testapp/TestClass.mm
@@ -0,0 +1,95 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <unistd.h>
+
+#import "TestClass.h"
+
+struct AStruct {
+ int x;
+ float y;
+ double z;
+};
+
+class InternalTestClass {
+ public:
+ InternalTestClass(int a) : a_(a) {}
+ ~InternalTestClass() {}
+
+ void snooze(float a);
+ void snooze(int a);
+ int snooze(int a, float b);
+
+ protected:
+ int a_;
+ AStruct s_;
+
+ static void InternalFunction(AStruct &s);
+ static float kStaticFloatValue;
+};
+
+void InternalTestClass::snooze(float a) {
+ InternalFunction(s_);
+ sleep(a_ * a);
+}
+
+void InternalTestClass::snooze(int a) {
+ InternalFunction(s_);
+ sleep(a_ * a);
+}
+
+int InternalTestClass::snooze(int a, float b) {
+ InternalFunction(s_);
+ sleep(a_ * a * b);
+
+ return 33;
+}
+
+void InternalTestClass::InternalFunction(AStruct &s) {
+ s.x = InternalTestClass::kStaticFloatValue;
+}
+
+float InternalTestClass::kStaticFloatValue = 42;
+
+static float PlainOldFunction() {
+ return 3.14145;
+}
+
+@implementation TestClass
+
+- (void)wait {
+ InternalTestClass t(10);
+ float z = PlainOldFunction();
+
+ while (1) {
+ t.snooze(z);
+ }
+}
+
+@end
diff --git a/src/client/mac/testapp/bomb.icns b/src/client/mac/testapp/bomb.icns
new file mode 100644
index 00000000..c360dbf6
--- /dev/null
+++ b/src/client/mac/testapp/bomb.icns
Binary files differ
diff --git a/src/client/mac/testapp/crashInMain b/src/client/mac/testapp/crashInMain
new file mode 100755
index 00000000..03bb3172
--- /dev/null
+++ b/src/client/mac/testapp/crashInMain
Binary files differ
diff --git a/src/client/mac/testapp/crashduringload b/src/client/mac/testapp/crashduringload
new file mode 100755
index 00000000..5ca9debb
--- /dev/null
+++ b/src/client/mac/testapp/crashduringload
Binary files differ
diff --git a/src/client/mac/testapp/main.m b/src/client/mac/testapp/main.m
new file mode 100644
index 00000000..1ed19bf9
--- /dev/null
+++ b/src/client/mac/testapp/main.m
@@ -0,0 +1,34 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Cocoa/Cocoa.h>
+
+int main(int argc, char *argv[]) {
+ return NSApplicationMain(argc, (const char **) argv);
+}
diff --git a/src/client/mac/tests/SimpleStringDictionaryTest.h b/src/client/mac/tests/SimpleStringDictionaryTest.h
new file mode 100644
index 00000000..1f48517f
--- /dev/null
+++ b/src/client/mac/tests/SimpleStringDictionaryTest.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <GTMSenTestCase.h>
+#import "SimpleStringDictionary.h"
+
+@interface SimpleStringDictionaryTest : GTMTestCase {
+
+}
+
+- (void)testKeyValueEntry;
+- (void)testSimpleStringDictionary;
+- (void)testSimpleStringDictionaryIterator;
+@end
diff --git a/src/client/mac/tests/SimpleStringDictionaryTest.mm b/src/client/mac/tests/SimpleStringDictionaryTest.mm
new file mode 100644
index 00000000..94179cd2
--- /dev/null
+++ b/src/client/mac/tests/SimpleStringDictionaryTest.mm
@@ -0,0 +1,243 @@
+// Copyright (c) 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import "SimpleStringDictionaryTest.h"
+#import "SimpleStringDictionary.h"
+
+using google_breakpad::KeyValueEntry;
+using google_breakpad::SimpleStringDictionary;
+using google_breakpad::SimpleStringDictionaryIterator;
+
+@implementation SimpleStringDictionaryTest
+
+//==============================================================================
+- (void)testKeyValueEntry {
+ KeyValueEntry entry;
+
+ // Verify that initial state is correct
+ STAssertFalse(entry.IsActive(), @"Initial key value entry is active!");
+ STAssertEquals(strlen(entry.GetKey()), (size_t)0, @"Empty key value did not "
+ @"have length 0");
+ STAssertEquals(strlen(entry.GetValue()), (size_t)0, @"Empty key value did not "
+ @"have length 0");
+
+ // Try setting a key/value and then verify
+ entry.SetKeyValue("key1", "value1");
+ STAssertEqualCStrings(entry.GetKey(), "key1", @"key was not equal to key1");
+ STAssertEqualCStrings(entry.GetValue(), "value1", @"value was not equal");
+
+ // Try setting a new value
+ entry.SetValue("value3");
+
+ // Make sure the new value took
+ STAssertEqualCStrings(entry.GetValue(), "value3", @"value was not equal");
+
+ // Make sure the key didn't change
+ STAssertEqualCStrings(entry.GetKey(), "key1", @"key changed after setting "
+ @"value!");
+
+ // Try setting a new key/value and then verify
+ entry.SetKeyValue("key2", "value2");
+ STAssertEqualCStrings(entry.GetKey(), "key2", @"New key was not equal to "
+ @"key2");
+ STAssertEqualCStrings(entry.GetValue(), "value2", @"New value was not equal "
+ @"to value2");
+
+ // Clear the entry and verify the key and value are empty strings
+ entry.Clear();
+ STAssertFalse(entry.IsActive(), @"Key value clear did not clear object");
+ STAssertEquals(strlen(entry.GetKey()), (size_t)0, @"Length of cleared key "
+ @"was not 0");
+ STAssertEquals(strlen(entry.GetValue()), (size_t)0, @"Length of cleared "
+ @"value was not 0!");
+}
+
+- (void)testEmptyKeyValueCombos {
+ KeyValueEntry entry;
+ entry.SetKeyValue(NULL, NULL);
+ STAssertEqualCStrings(entry.GetKey(), "", @"Setting NULL key did not return "
+ @"empty key!");
+ STAssertEqualCStrings(entry.GetValue(), "", @"Setting NULL value did not "
+ @"set empty string value!");
+}
+
+
+//==============================================================================
+- (void)testSimpleStringDictionary {
+ // Make a new dictionary
+ SimpleStringDictionary *dict = new SimpleStringDictionary();
+ STAssertTrue(dict != NULL, nil);
+
+ // try passing in NULL for key
+ //dict->SetKeyValue(NULL, "bad"); // causes assert() to fire
+
+ // Set three distinct values on three keys
+ dict->SetKeyValue("key1", "value1");
+ dict->SetKeyValue("key2", "value2");
+ dict->SetKeyValue("key3", "value3");
+
+ STAssertTrue(!strcmp(dict->GetValueForKey("key1"), "value1"), nil);
+ STAssertTrue(!strcmp(dict->GetValueForKey("key2"), "value2"), nil);
+ STAssertTrue(!strcmp(dict->GetValueForKey("key3"), "value3"), nil);
+ STAssertEquals(dict->GetCount(), 3, @"GetCount did not return 3");
+ // try an unknown key
+ STAssertTrue(dict->GetValueForKey("key4") == NULL, nil);
+
+ // try a NULL key
+ //STAssertTrue(dict->GetValueForKey(NULL) == NULL, nil); // asserts
+
+ // Remove a key
+ dict->RemoveKey("key3");
+
+ // Now make sure it's not there anymore
+ STAssertTrue(dict->GetValueForKey("key3") == NULL, nil);
+
+ // Remove a NULL key
+ //dict->RemoveKey(NULL); // will cause assert() to fire
+
+ // Remove by setting value to NULL
+ dict->SetKeyValue("key2", NULL);
+
+ // Now make sure it's not there anymore
+ STAssertTrue(dict->GetValueForKey("key2") == NULL, nil);
+}
+
+//==============================================================================
+// The idea behind this test is to add a bunch of values to the dictionary,
+// remove some in the middle, then add a few more in. We then create a
+// SimpleStringDictionaryIterator and iterate through the dictionary, taking
+// note of the key/value pairs we see. We then verify that it iterates
+// through exactly the number of key/value pairs we expect, and that they
+// match one-for-one with what we would expect. In all cases we're setting
+// key value pairs of the form:
+//
+// key<n>/value<n> (like key0/value0, key17,value17, etc.)
+//
+- (void)testSimpleStringDictionaryIterator {
+ SimpleStringDictionary *dict = new SimpleStringDictionary();
+ STAssertTrue(dict != NULL, nil);
+
+ char key[KeyValueEntry::MAX_STRING_STORAGE_SIZE];
+ char value[KeyValueEntry::MAX_STRING_STORAGE_SIZE];
+
+ const int kDictionaryCapacity = SimpleStringDictionary::MAX_NUM_ENTRIES;
+ const int kPartitionIndex = kDictionaryCapacity - 5;
+
+ // We assume at least this size in the tests below
+ STAssertTrue(kDictionaryCapacity >= 64, nil);
+
+ // We'll keep track of the number of key/value pairs we think should
+ // be in the dictionary
+ int expectedDictionarySize = 0;
+
+ // Set a bunch of key/value pairs like key0/value0, key1/value1, ...
+ for (int i = 0; i < kPartitionIndex; ++i) {
+ sprintf(key, "key%d", i);
+ sprintf(value, "value%d", i);
+ dict->SetKeyValue(key, value);
+ }
+ expectedDictionarySize = kPartitionIndex;
+
+ // set a couple of the keys twice (with the same value) - should be nop
+ dict->SetKeyValue("key2", "value2");
+ dict->SetKeyValue("key4", "value4");
+ dict->SetKeyValue("key15", "value15");
+
+ // Remove some random elements in the middle
+ dict->RemoveKey("key7");
+ dict->RemoveKey("key18");
+ dict->RemoveKey("key23");
+ dict->RemoveKey("key31");
+ expectedDictionarySize -= 4; // we just removed four key/value pairs
+
+ // Set some more key/value pairs like key59/value59, key60/value60, ...
+ for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) {
+ sprintf(key, "key%d", i);
+ sprintf(value, "value%d", i);
+ dict->SetKeyValue(key, value);
+ }
+ expectedDictionarySize += kDictionaryCapacity - kPartitionIndex;
+
+ // Now create an iterator on the dictionary
+ SimpleStringDictionaryIterator iter(*dict);
+
+ // We then verify that it iterates through exactly the number of
+ // key/value pairs we expect, and that they match one-for-one with what we
+ // would expect. The ordering of the iteration does not matter...
+
+ // used to keep track of number of occurrences found for key/value pairs
+ int count[kDictionaryCapacity];
+ memset(count, 0, sizeof(count));
+
+ int totalCount = 0;
+
+ const KeyValueEntry *entry;
+
+ while ((entry = iter.Next())) {
+ totalCount++;
+
+ // Extract keyNumber from a string of the form key<keyNumber>
+ int keyNumber;
+ sscanf(entry->GetKey(), "key%d", &keyNumber);
+
+ // Extract valueNumber from a string of the form value<valueNumber>
+ int valueNumber;
+ sscanf(entry->GetValue(), "value%d", &valueNumber);
+
+ // The value number should equal the key number since that's how we set them
+ STAssertTrue(keyNumber == valueNumber, nil);
+
+ // Key and value numbers should be in proper range:
+ // 0 <= keyNumber < kDictionaryCapacity
+ bool isKeyInGoodRange =
+ (keyNumber >= 0 && keyNumber < kDictionaryCapacity);
+ bool isValueInGoodRange =
+ (valueNumber >= 0 && valueNumber < kDictionaryCapacity);
+ STAssertTrue(isKeyInGoodRange, nil);
+ STAssertTrue(isValueInGoodRange, nil);
+
+ if (isKeyInGoodRange && isValueInGoodRange) {
+ ++count[keyNumber];
+ }
+ }
+
+ // Make sure each of the key/value pairs showed up exactly one time, except
+ // for the ones which we removed.
+ for (int i = 0; i < kDictionaryCapacity; ++i) {
+ // Skip over key7, key18, key23, and key31, since we removed them
+ if (!(i == 7 || i == 18 || i == 23 || i == 31)) {
+ STAssertTrue(count[i] == 1, nil);
+ }
+ }
+
+ // Make sure the number of iterations matches the expected dictionary size.
+ STAssertTrue(totalCount == expectedDictionarySize, nil);
+}
+
+@end
diff --git a/src/client/minidump_file_writer_unittest.cc b/src/client/minidump_file_writer_unittest.cc
index 8eadff46..a83307d9 100644
--- a/src/client/minidump_file_writer_unittest.cc
+++ b/src/client/minidump_file_writer_unittest.cc
@@ -126,17 +126,22 @@ static bool CompareFile(const char *path) {
0x00000007, 0x06000007, 0x00000008, 0x07000008, 0x00000009, 0x08000009,
0x0000000a, 0x0900000a, 0x0000000b, 0x00000000
#else
- 0x0000beef, 0x0000001e, 0x00000018, 0x00000020, 0x00000038, 0x00000000,
- 0x00000018, 0x00690046, 0x00730072, 0x00200074, 0x00740053, 0x00690072,
- 0x0067006e, 0x00000000, 0x0000001a, 0x00650053, 0x006f0063, 0x0064006e,
- 0x00530020, 0x00720074, 0x006e0069, 0x00000067, 0x0001da00, 0x00000002,
- 0x0002da01, 0x00000003, 0x0003da02, 0x00000004, 0x0004da03, 0x00000005,
- 0x0005da04, 0x00000006, 0x0006da05, 0x00000007, 0x0007da06, 0x00000008,
- 0x0008da07, 0x00000009, 0x0009da08, 0x0000000a, 0x000ada09, 0x0000000b,
- 0x0000000a, 0x00018700, 0x00000002, 0x00028701, 0x00000003, 0x00038702,
- 0x00000004, 0x00048703, 0x00000005, 0x00058704, 0x00000006, 0x00068705,
- 0x00000007, 0x00078706, 0x00000008, 0x00088707, 0x00000009, 0x00098708,
- 0x0000000a, 0x000a8709, 0x0000000b, 0x00000000,
+ 0x0000beef, 0x0000001e, 0x00000018, 0x00000020,
+ 0x00000038, 0x00000000, 0x00000018, 0x00690046,
+ 0x00730072, 0x00200074, 0x00740053, 0x00690072,
+ 0x0067006e, 0x00000000, 0x0000001a, 0x00650053,
+ 0x006f0063, 0x0064006e, 0x00530020, 0x00720074,
+ 0x006e0069, 0x00000067, 0x00011e00, 0x00000002,
+ 0x00021e01, 0x00000003, 0x00031e02, 0x00000004,
+ 0x00041e03, 0x00000005, 0x00051e04, 0x00000006,
+ 0x00061e05, 0x00000007, 0x00071e06, 0x00000008,
+ 0x00081e07, 0x00000009, 0x00091e08, 0x0000000a,
+ 0x000a1e09, 0x0000000b, 0x0000000a, 0x00011c00,
+ 0x00000002, 0x00021c01, 0x00000003, 0x00031c02,
+ 0x00000004, 0x00041c03, 0x00000005, 0x00051c04,
+ 0x00000006, 0x00061c05, 0x00000007, 0x00071c06,
+ 0x00000008, 0x00081c07, 0x00000009, 0x00091c08,
+ 0x0000000a, 0x000a1c09, 0x0000000b, 0x00000000,
#endif
};
unsigned int expected_byte_count = sizeof(expected);
@@ -156,7 +161,6 @@ static bool CompareFile(const char *path) {
printf("%d\n",b1 - (char*)buffer);
-
ASSERT_EQ(memcmp(buffer, expected, expected_byte_count), 0);
return true;
}
diff --git a/src/common/mac/GTMDefines.h b/src/common/mac/GTMDefines.h
new file mode 100644
index 00000000..b88193cd
--- /dev/null
+++ b/src/common/mac/GTMDefines.h
@@ -0,0 +1,241 @@
+//
+// GTMDefines.h
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+// ============================================================================
+
+#include <AvailabilityMacros.h>
+#include <TargetConditionals.h>
+
+// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs
+#ifndef MAC_OS_X_VERSION_10_5
+ #define MAC_OS_X_VERSION_10_5 1050
+#endif
+#ifndef MAC_OS_X_VERSION_10_6
+ #define MAC_OS_X_VERSION_10_6 1060
+#endif
+
+// ----------------------------------------------------------------------------
+// CPP symbols that can be overridden in a prefix to control how the toolbox
+// is compiled.
+// ----------------------------------------------------------------------------
+
+
+// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and
+// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens
+// when a validation fails. If you implement your own validators, you may want
+// to control their internals using the same macros for consistency.
+#ifndef GTM_CONTAINERS_VALIDATION_FAILED_ASSERT
+ #define GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 0
+#endif
+
+// Give ourselves a consistent way to do inlines. Apple's macros even use
+// a few different actual definitions, so we're based off of the foundation
+// one.
+#if !defined(GTM_INLINE)
+ #if defined (__GNUC__) && (__GNUC__ == 4)
+ #define GTM_INLINE static __inline__ __attribute__((always_inline))
+ #else
+ #define GTM_INLINE static __inline__
+ #endif
+#endif
+
+// Give ourselves a consistent way of doing externs that links up nicely
+// when mixing objc and objc++
+#if !defined (GTM_EXTERN)
+ #if defined __cplusplus
+ #define GTM_EXTERN extern "C"
+ #else
+ #define GTM_EXTERN extern
+ #endif
+#endif
+
+// Give ourselves a consistent way of exporting things if we have visibility
+// set to hidden.
+#if !defined (GTM_EXPORT)
+ #define GTM_EXPORT __attribute__((visibility("default")))
+#endif
+
+// _GTMDevLog & _GTMDevAssert
+//
+// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for
+// developer level errors. This implementation simply macros to NSLog/NSAssert.
+// It is not intended to be a general logging/reporting system.
+//
+// Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert
+// for a little more background on the usage of these macros.
+//
+// _GTMDevLog log some error/problem in debug builds
+// _GTMDevAssert assert if conditon isn't met w/in a method/function
+// in all builds.
+//
+// To replace this system, just provide different macro definitions in your
+// prefix header. Remember, any implementation you provide *must* be thread
+// safe since this could be called by anything in what ever situtation it has
+// been placed in.
+//
+
+// We only define the simple macros if nothing else has defined this.
+#ifndef _GTMDevLog
+
+#ifdef DEBUG
+ #define _GTMDevLog(...) NSLog(__VA_ARGS__)
+#else
+ #define _GTMDevLog(...) do { } while (0)
+#endif
+
+#endif // _GTMDevLog
+
+// Declared here so that it can easily be used for logging tracking if
+// necessary. See GTMUnitTestDevLog.h for details.
+@class NSString;
+GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
+
+#ifndef _GTMDevAssert
+// we directly invoke the NSAssert handler so we can pass on the varargs
+// (NSAssert doesn't have a macro we can use that takes varargs)
+#if !defined(NS_BLOCK_ASSERTIONS)
+ #define _GTMDevAssert(condition, ...) \
+ do { \
+ if (!(condition)) { \
+ [[NSAssertionHandler currentHandler] \
+ handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \
+ file:[NSString stringWithUTF8String:__FILE__] \
+ lineNumber:__LINE__ \
+ description:__VA_ARGS__]; \
+ } \
+ } while(0)
+#else // !defined(NS_BLOCK_ASSERTIONS)
+ #define _GTMDevAssert(condition, ...) do { } while (0)
+#endif // !defined(NS_BLOCK_ASSERTIONS)
+
+#endif // _GTMDevAssert
+
+// _GTMCompileAssert
+// _GTMCompileAssert is an assert that is meant to fire at compile time if you
+// want to check things at compile instead of runtime. For example if you
+// want to check that a wchar is 4 bytes instead of 2 you would use
+// _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X)
+// Note that the second "arg" is not in quotes, and must be a valid processor
+// symbol in it's own right (no spaces, punctuation etc).
+
+// Wrapping this in an #ifndef allows external groups to define their own
+// compile time assert scheme.
+#ifndef _GTMCompileAssert
+ // We got this technique from here:
+ // http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html
+
+ #define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg
+ #define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg)
+ #define _GTMCompileAssert(test, msg) \
+ typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
+#endif // _GTMCompileAssert
+
+// Macro to allow fast enumeration when building for 10.5 or later, and
+// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration
+// does keys, so pick the right thing, nothing is done on the FastEnumeration
+// side to be sure you're getting what you wanted.
+#ifndef GTM_FOREACH_OBJECT
+ #if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
+ #define GTM_FOREACH_OBJECT(element, collection) \
+ for (element in collection)
+ #define GTM_FOREACH_KEY(element, collection) \
+ for (element in collection)
+ #else
+ #define GTM_FOREACH_OBJECT(element, collection) \
+ for (NSEnumerator * _ ## element ## _enum = [collection objectEnumerator]; \
+ (element = [_ ## element ## _enum nextObject]) != nil; )
+ #define GTM_FOREACH_KEY(element, collection) \
+ for (NSEnumerator * _ ## element ## _enum = [collection keyEnumerator]; \
+ (element = [_ ## element ## _enum nextObject]) != nil; )
+ #endif
+#endif
+
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// CPP symbols defined based on the project settings so the GTM code has
+// simple things to test against w/o scattering the knowledge of project
+// setting through all the code.
+// ----------------------------------------------------------------------------
+
+// Provide a single constant CPP symbol that all of GTM uses for ifdefing
+// iPhone code.
+#if TARGET_OS_IPHONE // iPhone SDK
+ // For iPhone specific stuff
+ #define GTM_IPHONE_SDK 1
+ #if TARGET_IPHONE_SIMULATOR
+ #define GTM_IPHONE_SIMULATOR 1
+ #else
+ #define GTM_IPHONE_DEVICE 1
+ #endif // TARGET_IPHONE_SIMULATOR
+#else
+ // For MacOS specific stuff
+ #define GTM_MACOS_SDK 1
+#endif
+
+// Provide a symbol to include/exclude extra code for GC support. (This mainly
+// just controls the inclusion of finalize methods).
+#ifndef GTM_SUPPORT_GC
+ #if GTM_IPHONE_SDK
+ // iPhone never needs GC
+ #define GTM_SUPPORT_GC 0
+ #else
+ // We can't find a symbol to tell if GC is supported/required, so best we
+ // do on Mac targets is include it if we're on 10.5 or later.
+ #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
+ #define GTM_SUPPORT_GC 0
+ #else
+ #define GTM_SUPPORT_GC 1
+ #endif
+ #endif
+#endif
+
+// To simplify support for 64bit (and Leopard in general), we provide the type
+// defines for non Leopard SDKs
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
+ // NSInteger/NSUInteger and Max/Mins
+ #ifndef NSINTEGER_DEFINED
+ #if __LP64__ || NS_BUILD_32_LIKE_64
+ typedef long NSInteger;
+ typedef unsigned long NSUInteger;
+ #else
+ typedef int NSInteger;
+ typedef unsigned int NSUInteger;
+ #endif
+ #define NSIntegerMax LONG_MAX
+ #define NSIntegerMin LONG_MIN
+ #define NSUIntegerMax ULONG_MAX
+ #define NSINTEGER_DEFINED 1
+ #endif // NSINTEGER_DEFINED
+ // CGFloat
+ #ifndef CGFLOAT_DEFINED
+ #if defined(__LP64__) && __LP64__
+ // This really is an untested path (64bit on Tiger?)
+ typedef double CGFloat;
+ #define CGFLOAT_MIN DBL_MIN
+ #define CGFLOAT_MAX DBL_MAX
+ #define CGFLOAT_IS_DOUBLE 1
+ #else /* !defined(__LP64__) || !__LP64__ */
+ typedef float CGFloat;
+ #define CGFLOAT_MIN FLT_MIN
+ #define CGFLOAT_MAX FLT_MAX
+ #define CGFLOAT_IS_DOUBLE 0
+ #endif /* !defined(__LP64__) || !__LP64__ */
+ #define CGFLOAT_DEFINED 1
+ #endif // CGFLOAT_DEFINED
+#endif // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
diff --git a/src/common/mac/GTMGarbageCollection.h b/src/common/mac/GTMGarbageCollection.h
new file mode 100644
index 00000000..93d4efab
--- /dev/null
+++ b/src/common/mac/GTMGarbageCollection.h
@@ -0,0 +1,72 @@
+//
+// GTMGarbageCollection.h
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import <Foundation/Foundation.h>
+
+#import "GTMDefines.h"
+
+// This allows us to easily move our code from GC to non GC.
+// They are no-ops unless we are require Leopard or above.
+// See
+// http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection/index.html
+// and
+// http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection/Articles/gcCoreFoundation.html#//apple_ref/doc/uid/TP40006687-SW1
+// for details.
+
+#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) && !GTM_IPHONE_SDK
+// General use would be to call this through GTMCFAutorelease
+// but there may be a reason the you want to make something collectable
+// but not autoreleased, especially in pure GC code where you don't
+// want to bother with the nop autorelease. Done as a define instead of an
+// inline so that tools like Clang's scan-build don't report code as leaking.
+#define GTMNSMakeCollectable(cf) ((id)NSMakeCollectable(cf))
+
+// GTMNSMakeUncollectable is for global maps, etc. that we don't
+// want released ever. You should still retain these in non-gc code.
+GTM_INLINE void GTMNSMakeUncollectable(id object) {
+ [[NSGarbageCollector defaultCollector] disableCollectorForPointer:object];
+}
+
+// Hopefully no code really needs this, but GTMIsGarbageCollectionEnabled is
+// a common way to check at runtime if GC is on.
+// There are some places where GC doesn't work w/ things w/in Apple's
+// frameworks, so this is here so GTM unittests and detect it, and not run
+// individual tests to work around bugs in Apple's frameworks.
+GTM_INLINE BOOL GTMIsGarbageCollectionEnabled(void) {
+ return ([NSGarbageCollector defaultCollector] != nil);
+}
+
+#else
+
+#define GTMNSMakeCollectable(cf) ((id)(cf))
+
+GTM_INLINE void GTMNSMakeUncollectable(id object) {
+}
+
+GTM_INLINE BOOL GTMIsGarbageCollectionEnabled(void) {
+ return NO;
+}
+
+#endif
+
+// GTMCFAutorelease makes a CF object collectable in GC mode, or adds it
+// to the autorelease pool in non-GC mode. Either way it is taken care
+// of. Done as a define instead of an inline so that tools like Clang's
+// scan-build don't report code as leaking.
+#define GTMCFAutorelease(cf) ([GTMNSMakeCollectable(cf) autorelease])
+
diff --git a/src/common/mac/GTMLogger.h b/src/common/mac/GTMLogger.h
new file mode 100644
index 00000000..1626b1b6
--- /dev/null
+++ b/src/common/mac/GTMLogger.h
@@ -0,0 +1,458 @@
+//
+// GTMLogger.h
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+// Key Abstractions
+// ----------------
+//
+// This file declares multiple classes and protocols that are used by the
+// GTMLogger logging system. The 4 main abstractions used in this file are the
+// following:
+//
+// * logger (GTMLogger) - The main logging class that users interact with. It
+// has methods for logging at different levels and uses a log writer, a log
+// formatter, and a log filter to get the job done.
+//
+// * log writer (GTMLogWriter) - Writes a given string to some log file, where
+// a "log file" can be a physical file on disk, a POST over HTTP to some URL,
+// or even some in-memory structure (e.g., a ring buffer).
+//
+// * log formatter (GTMLogFormatter) - Given a format string and arguments as
+// a va_list, returns a single formatted NSString. A "formatted string" could
+// be a string with the date prepended, a string with values in a CSV format,
+// or even a string of XML.
+//
+// * log filter (GTMLogFilter) - Given a formatted log message as an NSString
+// and the level at which the message is to be logged, this class will decide
+// whether the given message should be logged or not. This is a flexible way
+// to filter out messages logged at a certain level, messages that contain
+// certain text, or filter nothing out at all. This gives the caller the
+// flexibility to dynamically enable debug logging in Release builds.
+//
+// This file also declares some classes to handle the common log writer, log
+// formatter, and log filter cases. Callers can also create their own writers,
+// formatters, and filters and they can even build them on top of the ones
+// declared here. Keep in mind that your custom writer/formatter/filter may be
+// called from multiple threads, so it must be thread-safe.
+
+#import <Foundation/Foundation.h>
+
+// Predeclaration of used protocols that are declared later in this file.
+@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter;
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
+#define CHECK_FORMAT_NSSTRING(a, b) __attribute__((format(__NSString__, a, b)))
+#else
+#define CHECK_FORMAT_NSSTRING(a, b)
+#endif
+
+// GTMLogger
+//
+// GTMLogger is the primary user-facing class for an object-oriented logging
+// system. It is built on the concept of log formatters (GTMLogFormatter), log
+// writers (GTMLogWriter), and log filters (GTMLogFilter). When a message is
+// sent to a GTMLogger to log a message, the message is formatted using the log
+// formatter, then the log filter is consulted to see if the message should be
+// logged, and if so, the message is sent to the log writer to be written out.
+//
+// GTMLogger is intended to be a flexible and thread-safe logging solution. Its
+// flexibility comes from the fact that GTMLogger instances can be customized
+// with user defined formatters, filters, and writers. And these writers,
+// filters, and formatters can be combined, stacked, and customized in arbitrary
+// ways to suit the needs at hand. For example, multiple writers can be used at
+// the same time, and a GTMLogger instance can even be used as another
+// GTMLogger's writer. This allows for arbitrarily deep logging trees.
+//
+// A standard GTMLogger uses a writer that sends messages to standard out, a
+// formatter that smacks a timestamp and a few other bits of interesting
+// information on the message, and a filter that filters out debug messages from
+// release builds. Using the standard log settings, a log message will look like
+// the following:
+//
+// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo=<Foo: 0x123>
+//
+// The output contains the date and time of the log message, the name of the
+// process followed by its process ID/thread ID, the log level at which the
+// message was logged (in the previous example the level was 1:
+// kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in
+// this case, the log message was @"foo=%@", foo).
+//
+// Multiple instances of GTMLogger can be created, each configured their own
+// way. Though GTMLogger is not a singleton (in the GoF sense), it does provide
+// access to a shared (i.e., globally accessible) GTMLogger instance. This makes
+// it convenient for all code in a process to use the same GTMLogger instance.
+// The shared GTMLogger instance can also be configured in an arbitrary, and
+// these configuration changes will affect all code that logs through the shared
+// instance.
+
+//
+// Log Levels
+// ----------
+// GTMLogger has 3 different log levels: Debug, Info, and Error. GTMLogger
+// doesn't take any special action based on the log level; it simply forwards
+// this information on to formatters, filters, and writers, each of which may
+// optionally take action based on the level. Since log level filtering is
+// performed at runtime, log messages are typically not filtered out at compile
+// time. The exception to this rule is that calls to the GTMLoggerDebug() macro
+// *ARE* filtered out of non-DEBUG builds. This is to be backwards compatible
+// with behavior that many developers are currently used to. Note that this
+// means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but
+// [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out.
+//
+// Standard loggers are created with the GTMLogLevelFilter log filter, which
+// filters out certain log messages based on log level, and some other settings.
+//
+// In addition to the -logDebug:, -logInfo:, and -logError: methods defined on
+// GTMLogger itself, there are also C macros that make usage of the shared
+// GTMLogger instance very convenient. These macros are:
+//
+// GTMLoggerDebug(...)
+// GTMLoggerInfo(...)
+// GTMLoggerError(...)
+//
+// Again, a notable feature of these macros is that GTMLogDebug() calls *will be
+// compiled out of non-DEBUG builds*.
+//
+// Standard Loggers
+// ----------------
+// GTMLogger has the concept of "standard loggers". A standard logger is simply
+// a logger that is pre-configured with some standard/common writer, formatter,
+// and filter combination. Standard loggers are created using the creation
+// methods beginning with "standard". The alternative to a standard logger is a
+// regular logger, which will send messages to stdout, with no special
+// formatting, and no filtering.
+//
+// How do I use GTMLogger?
+// ----------------------
+// The typical way you will want to use GTMLogger is to simply use the
+// GTMLogger*() macros for logging from code. That way we can easily make
+// changes to the GTMLogger class and simply update the macros accordingly. Only
+// your application startup code (perhaps, somewhere in main()) should use the
+// GTMLogger class directly in order to configure the shared logger, which all
+// of the code using the macros will be using. Again, this is just the typical
+// situation.
+//
+// To be complete, there are cases where you may want to use GTMLogger directly,
+// or even create separate GTMLogger instances for some reason. That's fine,
+// too.
+//
+// Examples
+// --------
+// The following show some common GTMLogger use cases.
+//
+// 1. You want to log something as simply as possible. Also, this call will only
+// appear in debug builds. In non-DEBUG builds it will be completely removed.
+//
+// GTMLoggerDebug(@"foo = %@", foo);
+//
+// 2. The previous example is similar to the following. The major difference is
+// that the previous call (example 1) will be compiled out of Release builds
+// but this statement will not be compiled out.
+//
+// [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo];
+//
+// 3. Send all logging output from the shared logger to a file. We do this by
+// creating an NSFileHandle for writing associated with a file, and setting
+// that file handle as the logger's writer.
+//
+// NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log"
+// create:YES];
+// [[GTMLogger sharedLogger] setWriter:f];
+// GTMLoggerError(@"hi"); // This will be sent to /tmp/f.log
+//
+// 4. Create a new GTMLogger that will log to a file. This example differs from
+// the previous one because here we create a new GTMLogger that is different
+// from the shared logger.
+//
+// GTMLogger *logger = [GTMLogger standardLoggerWithPath:@"/tmp/temp.log"];
+// [logger logInfo:@"hi temp log file"];
+//
+// 5. Create a logger that writes to stdout and does NOT do any formatting to
+// the log message. This might be useful, for example, when writing a help
+// screen for a command-line tool to standard output.
+//
+// GTMLogger *logger = [GTMLogger logger];
+// [logger logInfo:@"%@ version 0.1 usage", progName];
+//
+// 6. Send log output to stdout AND to a log file. The trick here is that
+// NSArrays function as composite log writers, which means when an array is
+// set as the log writer, it forwards all logging messages to all of its
+// contained GTMLogWriters.
+//
+// // Create array of GTMLogWriters
+// NSArray *writers = [NSArray arrayWithObjects:
+// [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES],
+// [NSFileHandle fileHandleWithStandardOutput], nil];
+//
+// GTMLogger *logger = [GTMLogger standardLogger];
+// [logger setWriter:writers];
+// [logger logInfo:@"hi"]; // Output goes to stdout and /tmp/f.log
+//
+// For futher details on log writers, formatters, and filters, see the
+// documentation below.
+//
+// NOTE: GTMLogger is application level logging. By default it does nothing
+// with _GTMDevLog/_GTMDevAssert (see GTMDefines.h). An application can choose
+// to bridge _GTMDevLog/_GTMDevAssert to GTMLogger by providing macro
+// definitions in its prefix header (see GTMDefines.h for how one would do
+// that).
+//
+@interface GTMLogger : NSObject {
+ @private
+ id<GTMLogWriter> writer_;
+ id<GTMLogFormatter> formatter_;
+ id<GTMLogFilter> filter_;
+}
+
+//
+// Accessors for the shared logger instance
+//
+
+// Returns a shared/global standard GTMLogger instance. Callers should typically
+// use this method to get a GTMLogger instance, unless they explicitly want
+// their own instance to configure for their own needs. This is the only method
+// that returns a shared instance; all the rest return new GTMLogger instances.
++ (id)sharedLogger;
+
+// Sets the shared logger instance to |logger|. Future calls to +sharedLogger
+// will return |logger| instead.
++ (void)setSharedLogger:(GTMLogger *)logger;
+
+//
+// Creation methods
+//
+
+// Returns a new autoreleased GTMLogger instance that will log to stdout, using
+// the GTMLogStandardFormatter, and the GTMLogLevelFilter filter.
++ (id)standardLogger;
+
+// Same as +standardLogger, but logs to stderr.
++ (id)standardLoggerWithStderr;
+
+// Returns a new standard GTMLogger instance with a log writer that will
+// write to the file at |path|, and will use the GTMLogStandardFormatter and
+// GTMLogLevelFilter classes. If |path| does not exist, it will be created.
++ (id)standardLoggerWithPath:(NSString *)path;
+
+// Returns an autoreleased GTMLogger instance that will use the specified
+// |writer|, |formatter|, and |filter|.
++ (id)loggerWithWriter:(id<GTMLogWriter>)writer
+ formatter:(id<GTMLogFormatter>)formatter
+ filter:(id<GTMLogFilter>)filter;
+
+// Returns an autoreleased GTMLogger instance that logs to stdout, with the
+// basic formatter, and no filter. The returned logger differs from the logger
+// returned by +standardLogger because this one does not do any filtering and
+// does not do any special log formatting; this is the difference between a
+// "regular" logger and a "standard" logger.
++ (id)logger;
+
+// Designated initializer. This method returns a GTMLogger initialized with the
+// specified |writer|, |formatter|, and |filter|. See the setter methods below
+// for what values will be used if nil is passed for a parameter.
+- (id)initWithWriter:(id<GTMLogWriter>)writer
+ formatter:(id<GTMLogFormatter>)formatter
+ filter:(id<GTMLogFilter>)filter;
+
+//
+// Logging methods
+//
+
+// Logs a message at the debug level (kGTMLoggerLevelDebug).
+- (void)logDebug:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
+// Logs a message at the info level (kGTMLoggerLevelInfo).
+- (void)logInfo:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
+// Logs a message at the error level (kGTMLoggerLevelError).
+- (void)logError:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
+// Logs a message at the assert level (kGTMLoggerLevelAssert).
+- (void)logAssert:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
+
+
+//
+// Accessors
+//
+
+// Accessor methods for the log writer. If the log writer is set to nil,
+// [NSFileHandle fileHandleWithStandardOutput] is used.
+- (id<GTMLogWriter>)writer;
+- (void)setWriter:(id<GTMLogWriter>)writer;
+
+// Accessor methods for the log formatter. If the log formatter is set to nil,
+// GTMLogBasicFormatter is used. This formatter will format log messages in a
+// plain printf style.
+- (id<GTMLogFormatter>)formatter;
+- (void)setFormatter:(id<GTMLogFormatter>)formatter;
+
+// Accessor methods for the log filter. If the log filter is set to nil,
+// GTMLogNoFilter is used, which allows all log messages through.
+- (id<GTMLogFilter>)filter;
+- (void)setFilter:(id<GTMLogFilter>)filter;
+
+@end // GTMLogger
+
+
+// Helper functions that are used by the convenience GTMLogger*() macros that
+// enable the logging of function names.
+@interface GTMLogger (GTMLoggerMacroHelpers)
+- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ...
+ CHECK_FORMAT_NSSTRING(2, 3);
+- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ...
+ CHECK_FORMAT_NSSTRING(2, 3);
+- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ...
+ CHECK_FORMAT_NSSTRING(2, 3);
+- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ...
+ CHECK_FORMAT_NSSTRING(2, 3);
+@end // GTMLoggerMacroHelpers
+
+
+// Convenience macros that log to the shared GTMLogger instance. These macros
+// are how users should typically log to GTMLogger. Notice that GTMLoggerDebug()
+// calls will be compiled out of non-Debug builds.
+#define GTMLoggerDebug(...) \
+ [[GTMLogger sharedLogger] logFuncDebug:__func__ msg:__VA_ARGS__]
+#define GTMLoggerInfo(...) \
+ [[GTMLogger sharedLogger] logFuncInfo:__func__ msg:__VA_ARGS__]
+#define GTMLoggerError(...) \
+ [[GTMLogger sharedLogger] logFuncError:__func__ msg:__VA_ARGS__]
+#define GTMLoggerAssert(...) \
+ [[GTMLogger sharedLogger] logFuncAssert:__func__ msg:__VA_ARGS__]
+
+// If we're not in a debug build, remove the GTMLoggerDebug statements. This
+// makes calls to GTMLoggerDebug "compile out" of Release builds
+#ifndef DEBUG
+#undef GTMLoggerDebug
+#define GTMLoggerDebug(...) do {} while(0)
+#endif
+
+// Log levels.
+typedef enum {
+ kGTMLoggerLevelUnknown,
+ kGTMLoggerLevelDebug,
+ kGTMLoggerLevelInfo,
+ kGTMLoggerLevelError,
+ kGTMLoggerLevelAssert,
+} GTMLoggerLevel;
+
+
+//
+// Log Writers
+//
+
+// Protocol to be implemented by a GTMLogWriter instance.
+@protocol GTMLogWriter <NSObject>
+// Writes the given log message to where the log writer is configured to write.
+- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level;
+@end // GTMLogWriter
+
+
+// Simple category on NSFileHandle that makes NSFileHandles valid log writers.
+// This is convenient because something like, say, +fileHandleWithStandardError
+// now becomes a valid log writer. Log messages are written to the file handle
+// with a newline appended.
+@interface NSFileHandle (GTMFileHandleLogWriter) <GTMLogWriter>
+// Opens the file at |path| in append mode, and creates the file with |mode|
+// if it didn't previously exist.
++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode;
+@end // NSFileHandle
+
+
+// This category makes NSArray a GTMLogWriter that can be composed of other
+// GTMLogWriters. This is the classic Composite GoF design pattern. When the
+// GTMLogWriter -logMessage:level: message is sent to the array, the array
+// forwards the message to all of its elements that implement the GTMLogWriter
+// protocol.
+//
+// This is useful in situations where you would like to send log output to
+// multiple log writers at the same time. Simply create an NSArray of the log
+// writers you wish to use, then set the array as the "writer" for your
+// GTMLogger instance.
+@interface NSArray (GTMArrayCompositeLogWriter) <GTMLogWriter>
+@end // GTMArrayCompositeLogWriter
+
+
+// This category adapts the GTMLogger interface so that it can be used as a log
+// writer; it's an "adapter" in the GoF Adapter pattern sense.
+//
+// This is useful when you want to configure a logger to log to a specific
+// writer with a specific formatter and/or filter. But you want to also compose
+// that with a different log writer that may have its own formatter and/or
+// filter.
+@interface GTMLogger (GTMLoggerLogWriter) <GTMLogWriter>
+@end // GTMLoggerLogWriter
+
+
+//
+// Log Formatters
+//
+
+// Protocol to be implemented by a GTMLogFormatter instance.
+@protocol GTMLogFormatter <NSObject>
+// Returns a formatted string using the format specified in |fmt| and the va
+// args specified in |args|.
+- (NSString *)stringForFunc:(NSString *)func
+ withFormat:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level;
+@end // GTMLogFormatter
+
+
+// A basic log formatter that formats a string the same way that NSLog (or
+// printf) would. It does not do anything fancy, nor does it add any data of its
+// own.
+@interface GTMLogBasicFormatter : NSObject <GTMLogFormatter>
+@end // GTMLogBasicFormatter
+
+
+// A log formatter that formats the log string like the basic formatter, but
+// also prepends a timestamp and some basic process info to the message, as
+// shown in the following sample output.
+// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] log mesage here
+@interface GTMLogStandardFormatter : GTMLogBasicFormatter {
+ @private
+ NSDateFormatter *dateFormatter_; // yyyy-MM-dd HH:mm:ss.SSS
+ NSString *pname_;
+ pid_t pid_;
+}
+@end // GTMLogStandardFormatter
+
+
+//
+// Log Filters
+//
+
+// Protocol to be imlemented by a GTMLogFilter instance.
+@protocol GTMLogFilter <NSObject>
+// Returns YES if |msg| at |level| should be filtered out; NO otherwise.
+- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level;
+@end // GTMLogFilter
+
+
+// A log filter that filters messages at the kGTMLoggerLevelDebug level out of
+// non-debug builds. Messages at the kGTMLoggerLevelInfo level are also filtered
+// out of non-debug builds unless GTMVerboseLogging is set in the environment or
+// the processes's defaults. Messages at the kGTMLoggerLevelError level are
+// never filtered.
+@interface GTMLogLevelFilter : NSObject <GTMLogFilter>
+@end // GTMLogLevelFilter
+
+
+// A simple log filter that does NOT filter anything out;
+// -filterAllowsMessage:level will always return YES. This can be a convenient
+// way to enable debug-level logging in release builds (if you so desire).
+@interface GTMLogNoFilter : NSObject <GTMLogFilter>
+@end // GTMLogNoFilter
+
diff --git a/src/common/mac/GTMLogger.m b/src/common/mac/GTMLogger.m
new file mode 100644
index 00000000..de941d2e
--- /dev/null
+++ b/src/common/mac/GTMLogger.m
@@ -0,0 +1,445 @@
+//
+// GTMLogger.m
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMLogger.h"
+#import "GTMGarbageCollection.h"
+#import <fcntl.h>
+#import <unistd.h>
+#import <stdlib.h>
+#import <pthread.h>
+
+
+// Define a trivial assertion macro to avoid dependencies
+#ifdef DEBUG
+ #define GTMLOGGER_ASSERT(expr) assert(expr)
+#else
+ #define GTMLOGGER_ASSERT(expr)
+#endif
+
+
+@interface GTMLogger (PrivateMethods)
+
+- (void)logInternalFunc:(const char *)func
+ format:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level;
+
+@end
+
+
+// Reference to the shared GTMLogger instance. This is not a singleton, it's
+// just an easy reference to one shared instance.
+static GTMLogger *gSharedLogger = nil;
+
+
+@implementation GTMLogger
+
+// Returns a pointer to the shared logger instance. If none exists, a standard
+// logger is created and returned.
++ (id)sharedLogger {
+ @synchronized(self) {
+ if (gSharedLogger == nil) {
+ gSharedLogger = [[self standardLogger] retain];
+ }
+ GTMLOGGER_ASSERT(gSharedLogger != nil);
+ }
+ return [[gSharedLogger retain] autorelease];
+}
+
++ (void)setSharedLogger:(GTMLogger *)logger {
+ @synchronized(self) {
+ [gSharedLogger autorelease];
+ gSharedLogger = [logger retain];
+ }
+}
+
++ (id)standardLogger {
+ id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput];
+ id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init] autorelease];
+ id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease];
+ return [self loggerWithWriter:writer formatter:fr filter:filter];
+}
+
++ (id)standardLoggerWithStderr {
+ id me = [self standardLogger];
+ [me setWriter:[NSFileHandle fileHandleWithStandardError]];
+ return me;
+}
+
++ (id)standardLoggerWithPath:(NSString *)path {
+ NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644];
+ if (fh == nil) return nil;
+ id me = [self standardLogger];
+ [me setWriter:fh];
+ return me;
+}
+
++ (id)loggerWithWriter:(id<GTMLogWriter>)writer
+ formatter:(id<GTMLogFormatter>)formatter
+ filter:(id<GTMLogFilter>)filter {
+ return [[[self alloc] initWithWriter:writer
+ formatter:formatter
+ filter:filter] autorelease];
+}
+
++ (id)logger {
+ return [[[self alloc] init] autorelease];
+}
+
+- (id)init {
+ return [self initWithWriter:nil formatter:nil filter:nil];
+}
+
+- (id)initWithWriter:(id<GTMLogWriter>)writer
+ formatter:(id<GTMLogFormatter>)formatter
+ filter:(id<GTMLogFilter>)filter {
+ if ((self = [super init])) {
+ [self setWriter:writer];
+ [self setFormatter:formatter];
+ [self setFilter:filter];
+ GTMLOGGER_ASSERT(formatter_ != nil);
+ GTMLOGGER_ASSERT(filter_ != nil);
+ GTMLOGGER_ASSERT(writer_ != nil);
+ }
+ return self;
+}
+
+- (void)dealloc {
+ GTMLOGGER_ASSERT(writer_ != nil);
+ GTMLOGGER_ASSERT(formatter_ != nil);
+ GTMLOGGER_ASSERT(filter_ != nil);
+ [writer_ release];
+ [formatter_ release];
+ [filter_ release];
+ [super dealloc];
+}
+
+- (id<GTMLogWriter>)writer {
+ GTMLOGGER_ASSERT(writer_ != nil);
+ return [[writer_ retain] autorelease];
+}
+
+- (void)setWriter:(id<GTMLogWriter>)writer {
+ @synchronized(self) {
+ [writer_ autorelease];
+ if (writer == nil)
+ writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain];
+ else
+ writer_ = [writer retain];
+ }
+ GTMLOGGER_ASSERT(writer_ != nil);
+}
+
+- (id<GTMLogFormatter>)formatter {
+ GTMLOGGER_ASSERT(formatter_ != nil);
+ return [[formatter_ retain] autorelease];
+}
+
+- (void)setFormatter:(id<GTMLogFormatter>)formatter {
+ @synchronized(self) {
+ [formatter_ autorelease];
+ if (formatter == nil)
+ formatter_ = [[GTMLogBasicFormatter alloc] init];
+ else
+ formatter_ = [formatter retain];
+ }
+ GTMLOGGER_ASSERT(formatter_ != nil);
+}
+
+- (id<GTMLogFilter>)filter {
+ GTMLOGGER_ASSERT(filter_ != nil);
+ return [[filter_ retain] autorelease];
+}
+
+- (void)setFilter:(id<GTMLogFilter>)filter {
+ @synchronized(self) {
+ [filter_ autorelease];
+ if (filter == nil)
+ filter_ = [[GTMLogNoFilter alloc] init];
+ else
+ filter_ = [filter retain];
+ }
+ GTMLOGGER_ASSERT(filter_ != nil);
+}
+
+- (void)logDebug:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelDebug];
+ va_end(args);
+}
+
+- (void)logInfo:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelInfo];
+ va_end(args);
+}
+
+- (void)logError:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelError];
+ va_end(args);
+}
+
+- (void)logAssert:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelAssert];
+ va_end(args);
+}
+
+@end // GTMLogger
+
+
+@implementation GTMLogger (GTMLoggerMacroHelpers)
+
+- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelDebug];
+ va_end(args);
+}
+
+- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelInfo];
+ va_end(args);
+}
+
+- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelError];
+ va_end(args);
+}
+
+- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... {
+ va_list args;
+ va_start(args, fmt);
+ [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelAssert];
+ va_end(args);
+}
+
+@end // GTMLoggerMacroHelpers
+
+
+@implementation GTMLogger (PrivateMethods)
+
+- (void)logInternalFunc:(const char *)func
+ format:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level {
+ GTMLOGGER_ASSERT(formatter_ != nil);
+ GTMLOGGER_ASSERT(filter_ != nil);
+ GTMLOGGER_ASSERT(writer_ != nil);
+
+ NSString *fname = func ? [NSString stringWithUTF8String:func] : nil;
+ NSString *msg = [formatter_ stringForFunc:fname
+ withFormat:fmt
+ valist:args
+ level:level];
+ if (msg && [filter_ filterAllowsMessage:msg level:level])
+ [writer_ logMessage:msg level:level];
+}
+
+@end // PrivateMethods
+
+
+@implementation NSFileHandle (GTMFileHandleLogWriter)
+
++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode {
+ int fd = -1;
+ if (path) {
+ int flags = O_WRONLY | O_APPEND | O_CREAT;
+ fd = open([path fileSystemRepresentation], flags, mode);
+ }
+ if (fd == -1) return nil;
+ return [[[self alloc] initWithFileDescriptor:fd
+ closeOnDealloc:YES] autorelease];
+}
+
+- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+ @synchronized(self) {
+ NSString *line = [NSString stringWithFormat:@"%@\n", msg];
+ [self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
+ }
+}
+
+@end // GTMFileHandleLogWriter
+
+
+@implementation NSArray (GTMArrayCompositeLogWriter)
+
+- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+ @synchronized(self) {
+ id<GTMLogWriter> child = nil;
+ GTM_FOREACH_OBJECT(child, self) {
+ if ([child conformsToProtocol:@protocol(GTMLogWriter)])
+ [child logMessage:msg level:level];
+ }
+ }
+}
+
+@end // GTMArrayCompositeLogWriter
+
+
+@implementation GTMLogger (GTMLoggerLogWriter)
+
+- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+ switch (level) {
+ case kGTMLoggerLevelDebug:
+ [self logDebug:@"%@", msg];
+ break;
+ case kGTMLoggerLevelInfo:
+ [self logInfo:@"%@", msg];
+ break;
+ case kGTMLoggerLevelError:
+ [self logError:@"%@", msg];
+ break;
+ case kGTMLoggerLevelAssert:
+ [self logAssert:@"%@", msg];
+ break;
+ default:
+ // Ignore the message.
+ break;
+ }
+}
+
+@end // GTMLoggerLogWriter
+
+
+@implementation GTMLogBasicFormatter
+
+- (NSString *)stringForFunc:(NSString *)func
+ withFormat:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level {
+ // Performance note: since we always have to create a new NSString from the
+ // returned CFStringRef, we may want to do a quick check here to see if |fmt|
+ // contains a '%', and if not, simply return 'fmt'.
+ CFStringRef cfmsg = NULL;
+ cfmsg = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault,
+ NULL, // format options
+ (CFStringRef)fmt,
+ args);
+ return GTMCFAutorelease(cfmsg);
+}
+
+@end // GTMLogBasicFormatter
+
+
+@implementation GTMLogStandardFormatter
+
+- (id)init {
+ if ((self = [super init])) {
+ dateFormatter_ = [[NSDateFormatter alloc] init];
+ [dateFormatter_ setFormatterBehavior:NSDateFormatterBehavior10_4];
+ [dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"];
+ pname_ = [[[NSProcessInfo processInfo] processName] copy];
+ pid_ = [[NSProcessInfo processInfo] processIdentifier];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [dateFormatter_ release];
+ [pname_ release];
+ [super dealloc];
+}
+
+- (NSString *)stringForFunc:(NSString *)func
+ withFormat:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level {
+ GTMLOGGER_ASSERT(dateFormatter_ != nil);
+ NSString *tstamp = nil;
+ @synchronized (dateFormatter_) {
+ tstamp = [dateFormatter_ stringFromDate:[NSDate date]];
+ }
+ return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@",
+ tstamp, pname_, pid_, pthread_self(),
+ level, (func ? func : @"(no func)"),
+ [super stringForFunc:func withFormat:fmt valist:args level:level]];
+}
+
+@end // GTMLogStandardFormatter
+
+
+@implementation GTMLogLevelFilter
+
+// Check the environment and the user preferences for the GTMVerboseLogging key
+// to see if verbose logging has been enabled. The environment variable will
+// override the defaults setting, so check the environment first.
+// COV_NF_START
+static BOOL IsVerboseLoggingEnabled(void) {
+ static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging";
+ static char *env = NULL;
+ if (env == NULL)
+ env = getenv([kVerboseLoggingKey UTF8String]);
+
+ if (env && env[0]) {
+ return (strtol(env, NULL, 10) != 0);
+ }
+
+ return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey];
+}
+// COV_NF_END
+
+// In DEBUG builds, log everything. If we're not in a debug build we'll assume
+// that we're in a Release build.
+- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+#if DEBUG
+ return YES;
+#endif
+
+ BOOL allow = YES;
+
+ switch (level) {
+ case kGTMLoggerLevelDebug:
+ allow = NO;
+ break;
+ case kGTMLoggerLevelInfo:
+ allow = (IsVerboseLoggingEnabled() == YES);
+ break;
+ case kGTMLoggerLevelError:
+ allow = YES;
+ break;
+ case kGTMLoggerLevelAssert:
+ allow = YES;
+ break;
+ default:
+ allow = YES;
+ break;
+ }
+
+ return allow;
+}
+
+@end // GTMLogLevelFilter
+
+
+@implementation GTMLogNoFilter
+
+- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
+ return YES; // Allow everything through
+}
+
+@end // GTMLogNoFilter
diff --git a/src/common/mac/MachIPC.h b/src/common/mac/MachIPC.h
new file mode 100644
index 00000000..89924123
--- /dev/null
+++ b/src/common/mac/MachIPC.h
@@ -0,0 +1,304 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// MachIPC.h
+//
+// Some helpful wrappers for using Mach IPC calls
+
+#ifndef MACH_IPC_H__
+#define MACH_IPC_H__
+
+#import <mach/mach.h>
+#import <mach/message.h>
+#import <servers/bootstrap.h>
+#import <sys/types.h>
+
+#import <CoreServices/CoreServices.h>
+
+//==============================================================================
+// DISCUSSION:
+//
+// The three main classes of interest are
+//
+// MachMessage: a wrapper for a mach message of the following form
+// mach_msg_header_t
+// mach_msg_body_t
+// optional descriptors
+// optional extra message data
+//
+// MachReceiveMessage and MachSendMessage subclass MachMessage
+// and are used instead of MachMessage which is an abstract base class
+//
+// ReceivePort:
+// Represents a mach port for which we have receive rights
+//
+// MachPortSender:
+// Represents a mach port for which we have send rights
+//
+// Here's an example to receive a message on a server port:
+//
+// // This creates our named server port
+// ReceivePort receivePort("com.Google.MyService");
+//
+// MachReceiveMessage message;
+// kern_return_t result = receivePort.WaitForMessage(&message, 0);
+//
+// if (result == KERN_SUCCESS && message.GetMessageID() == 57) {
+// mach_port_t task = message.GetTranslatedPort(0);
+// mach_port_t thread = message.GetTranslatedPort(1);
+//
+// char *messageString = message.GetData();
+//
+// printf("message string = %s\n", messageString);
+// }
+//
+// Here is an example of using these classes to send a message to this port:
+//
+// // send to already named port
+// MachPortSender sender("com.Google.MyService");
+// MachSendMessage message(57); // our message ID is 57
+//
+// // add some ports to be translated for us
+// message.AddDescriptor(mach_task_self()); // our task
+// message.AddDescriptor(mach_thread_self()); // this thread
+//
+// char messageString[] = "Hello server!\n";
+// message.SetData(messageString, strlen(messageString)+1);
+//
+// kern_return_t result = sender.SendMessage(message, 1000); // timeout 1000ms
+//
+
+#define PRINT_MACH_RESULT(result_, message_) \
+ printf(message_" %s (%d)\n", mach_error_string(result_), result_ );
+
+//==============================================================================
+// A wrapper class for mach_msg_port_descriptor_t (with same memory layout)
+// with convenient constructors and accessors
+class MachMsgPortDescriptor : public mach_msg_port_descriptor_t {
+ public:
+ // General-purpose constructor
+ MachMsgPortDescriptor(mach_port_t in_name,
+ mach_msg_type_name_t in_disposition) {
+ name = in_name;
+ pad1 = 0;
+ pad2 = 0;
+ disposition = in_disposition;
+ type = MACH_MSG_PORT_DESCRIPTOR;
+ }
+
+ // For passing send rights to a port
+ MachMsgPortDescriptor(mach_port_t in_name) {
+ name = in_name;
+ pad1 = 0;
+ pad2 = 0;
+ disposition = MACH_MSG_TYPE_COPY_SEND;
+ type = MACH_MSG_PORT_DESCRIPTOR;
+ }
+
+ // Copy constructor
+ MachMsgPortDescriptor(const MachMsgPortDescriptor& desc) {
+ name = desc.name;
+ pad1 = desc.pad1;
+ pad2 = desc.pad2;
+ disposition = desc.disposition;
+ type = desc.type;
+ }
+
+ mach_port_t GetMachPort() const {
+ return name;
+ }
+
+ mach_msg_type_name_t GetDisposition() const {
+ return disposition;
+ }
+
+ // We're just a simple wrapper for mach_msg_port_descriptor_t
+ // and have the same memory layout
+ operator mach_msg_port_descriptor_t&() {
+ return *this;
+ }
+
+ // For convenience
+ operator mach_port_t() const {
+ return GetMachPort();
+ }
+};
+
+//==============================================================================
+// MachMessage: a wrapper for a mach message
+// (mach_msg_header_t, mach_msg_body_t, extra data)
+//
+// This considerably simplifies the construction of a message for sending
+// and the getting at relevant data and descriptors for the receiver.
+//
+// Currently the combined size of the descriptors plus data must be
+// less than 1024. But as a benefit no memory allocation is necessary.
+//
+// TODO: could consider adding malloc() support for very large messages
+//
+// A MachMessage object is used by ReceivePort::WaitForMessage
+// and MachPortSender::SendMessage
+//
+class MachMessage {
+ public:
+
+ // The receiver of the message can retrieve the raw data this way
+ u_int8_t *GetData() {
+ return GetDataLength() > 0 ? GetDataPacket()->data : NULL;
+ }
+
+ u_int32_t GetDataLength() {
+ return EndianU32_LtoN(GetDataPacket()->data_length);
+ }
+
+ // The message ID may be used as a code identifying the type of message
+ void SetMessageID(int32_t message_id) {
+ GetDataPacket()->id = EndianU32_NtoL(message_id);
+ }
+
+ int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); }
+
+ // Adds a descriptor (typically a mach port) to be translated
+ // returns true if successful, otherwise not enough space
+ bool AddDescriptor(const MachMsgPortDescriptor &desc);
+
+ int GetDescriptorCount() const { return body.msgh_descriptor_count; }
+ MachMsgPortDescriptor *GetDescriptor(int n);
+
+ // Convenience method which gets the mach port described by the descriptor
+ mach_port_t GetTranslatedPort(int n);
+
+ // A simple message is one with no descriptors
+ bool IsSimpleMessage() const { return GetDescriptorCount() == 0; }
+
+ // Sets raw data for the message (returns false if not enough space)
+ bool SetData(void *data, int32_t data_length);
+
+ protected:
+ // Consider this an abstract base class - must create an actual instance
+ // of MachReceiveMessage or MachSendMessage
+
+ MachMessage() {
+ memset(this, 0, sizeof(MachMessage));
+ }
+
+ friend class ReceivePort;
+ friend class MachPortSender;
+
+ // Represents raw data in our message
+ struct MessageDataPacket {
+ int32_t id; // little-endian
+ int32_t data_length; // little-endian
+ u_int8_t data[1]; // actual size limited by sizeof(MachMessage)
+ };
+
+ MessageDataPacket* GetDataPacket();
+
+ void SetDescriptorCount(int n);
+ void SetDescriptor(int n, const MachMsgPortDescriptor &desc);
+
+ // Returns total message size setting msgh_size in the header to this value
+ int CalculateSize();
+
+ mach_msg_header_t head;
+ mach_msg_body_t body;
+ u_int8_t padding[1024]; // descriptors and data may be embedded here
+};
+
+//==============================================================================
+// MachReceiveMessage and MachSendMessage are useful to separate the idea
+// of a mach message being sent and being received, and adds increased type
+// safety:
+// ReceivePort::WaitForMessage() only accepts a MachReceiveMessage
+// MachPortSender::SendMessage() only accepts a MachSendMessage
+
+//==============================================================================
+class MachReceiveMessage : public MachMessage {
+ public:
+ MachReceiveMessage() : MachMessage() {};
+};
+
+//==============================================================================
+class MachSendMessage : public MachMessage {
+ public:
+ MachSendMessage(int32_t message_id);
+};
+
+//==============================================================================
+// Represents a mach port for which we have receive rights
+class ReceivePort {
+ public:
+ // Creates a new mach port for receiving messages and registers a name for it
+ ReceivePort(const char *receive_port_name);
+
+ // Given an already existing mach port, use it. We take ownership of the
+ // port and deallocate it in our destructor.
+ ReceivePort(mach_port_t receive_port);
+
+ // Create a new mach port for receiving messages
+ ReceivePort();
+
+ ~ReceivePort();
+
+ // Waits on the mach port until message received or timeout
+ kern_return_t WaitForMessage(MachReceiveMessage *out_message,
+ mach_msg_timeout_t timeout);
+
+ // The underlying mach port that we wrap
+ mach_port_t GetPort() const { return port_; }
+
+ private:
+ ReceivePort(const ReceivePort&); // disable copy c-tor
+
+ mach_port_t port_;
+ kern_return_t init_result_;
+};
+
+//==============================================================================
+// Represents a mach port for which we have send rights
+class MachPortSender {
+ public:
+ // get a port with send rights corresponding to a named registered service
+ MachPortSender(const char *receive_port_name);
+
+
+ // Given an already existing mach port, use it.
+ MachPortSender(mach_port_t send_port);
+
+ kern_return_t SendMessage(MachSendMessage &message,
+ mach_msg_timeout_t timeout);
+
+ private:
+ MachPortSender(const MachPortSender&); // disable copy c-tor
+
+ mach_port_t send_port_;
+ kern_return_t init_result_;
+};
+
+#endif // MACH_IPC_H__
diff --git a/src/common/mac/MachIPC.mm b/src/common/mac/MachIPC.mm
new file mode 100644
index 00000000..9e521e48
--- /dev/null
+++ b/src/common/mac/MachIPC.mm
@@ -0,0 +1,297 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// MachIPC.mm
+// Wrapper for mach IPC calls
+
+#import <stdio.h>
+#import "MachIPC.h"
+
+//==============================================================================
+MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() {
+ head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+
+ // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage()
+ head.msgh_local_port = MACH_PORT_NULL;
+ head.msgh_reserved = 0;
+ head.msgh_id = 0;
+
+ SetDescriptorCount(0); // start out with no descriptors
+
+ SetMessageID(message_id);
+ SetData(NULL, 0); // client may add data later
+}
+
+//==============================================================================
+// returns true if successful
+bool MachMessage::SetData(void *data,
+ int32_t data_length) {
+ // first check to make sure we have enough space
+ int size = CalculateSize();
+ int new_size = size + data_length;
+
+ if ((unsigned)new_size > sizeof(MachMessage)) {
+ return false; // not enough space
+ }
+
+ GetDataPacket()->data_length = EndianU32_NtoL(data_length);
+ if (data) memcpy(GetDataPacket()->data, data, data_length);
+
+ CalculateSize();
+
+ return true;
+}
+
+//==============================================================================
+// calculates and returns the total size of the message
+// Currently, the entire message MUST fit inside of the MachMessage
+// messsage size <= sizeof(MachMessage)
+int MachMessage::CalculateSize() {
+ int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
+
+ // add space for MessageDataPacket
+ int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3;
+ size += 2*sizeof(int32_t) + alignedDataLength;
+
+ // add space for descriptors
+ size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor);
+
+ head.msgh_size = size;
+
+ return size;
+}
+
+//==============================================================================
+MachMessage::MessageDataPacket *MachMessage::GetDataPacket() {
+ int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount();
+ MessageDataPacket *packet =
+ reinterpret_cast<MessageDataPacket*>(padding + desc_size);
+
+ return packet;
+}
+
+//==============================================================================
+void MachMessage::SetDescriptor(int n,
+ const MachMsgPortDescriptor &desc) {
+ MachMsgPortDescriptor *desc_array =
+ reinterpret_cast<MachMsgPortDescriptor*>(padding);
+ desc_array[n] = desc;
+}
+
+//==============================================================================
+// returns true if successful otherwise there was not enough space
+bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) {
+ // first check to make sure we have enough space
+ int size = CalculateSize();
+ int new_size = size + sizeof(MachMsgPortDescriptor);
+
+ if ((unsigned)new_size > sizeof(MachMessage)) {
+ return false; // not enough space
+ }
+
+ // unfortunately, we need to move the data to allow space for the
+ // new descriptor
+ u_int8_t *p = reinterpret_cast<u_int8_t*>(GetDataPacket());
+ bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t));
+
+ SetDescriptor(GetDescriptorCount(), desc);
+ SetDescriptorCount(GetDescriptorCount() + 1);
+
+ CalculateSize();
+
+ return true;
+}
+
+//==============================================================================
+void MachMessage::SetDescriptorCount(int n) {
+ body.msgh_descriptor_count = n;
+
+ if (n > 0) {
+ head.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
+ } else {
+ head.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX;
+ }
+}
+
+//==============================================================================
+MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) {
+ if (n < GetDescriptorCount()) {
+ MachMsgPortDescriptor *desc =
+ reinterpret_cast<MachMsgPortDescriptor*>(padding);
+ return desc + n;
+ }
+
+ return nil;
+}
+
+//==============================================================================
+mach_port_t MachMessage::GetTranslatedPort(int n) {
+ if (n < GetDescriptorCount()) {
+ return GetDescriptor(n)->GetMachPort();
+ }
+ return MACH_PORT_NULL;
+}
+
+#pragma mark -
+
+//==============================================================================
+// create a new mach port for receiving messages and register a name for it
+ReceivePort::ReceivePort(const char *receive_port_name) {
+ mach_port_t current_task = mach_task_self();
+
+ init_result_ = mach_port_allocate(current_task,
+ MACH_PORT_RIGHT_RECEIVE,
+ &port_);
+
+ if (init_result_ != KERN_SUCCESS)
+ return;
+
+ init_result_ = mach_port_insert_right(current_task,
+ port_,
+ port_,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ if (init_result_ != KERN_SUCCESS)
+ return;
+
+ mach_port_t bootstrap_port = 0;
+ init_result_ = task_get_bootstrap_port(current_task, &bootstrap_port);
+
+ if (init_result_ != KERN_SUCCESS)
+ return;
+
+ init_result_ = bootstrap_register(bootstrap_port,
+ const_cast<char*>(receive_port_name),
+ port_);
+}
+
+//==============================================================================
+// create a new mach port for receiving messages
+ReceivePort::ReceivePort() {
+ mach_port_t current_task = mach_task_self();
+
+ init_result_ = mach_port_allocate(current_task,
+ MACH_PORT_RIGHT_RECEIVE,
+ &port_);
+
+ if (init_result_ != KERN_SUCCESS)
+ return;
+
+ init_result_ = mach_port_insert_right(current_task,
+ port_,
+ port_,
+ MACH_MSG_TYPE_MAKE_SEND);
+}
+
+//==============================================================================
+// Given an already existing mach port, use it. We take ownership of the
+// port and deallocate it in our destructor.
+ReceivePort::ReceivePort(mach_port_t receive_port)
+ : port_(receive_port),
+ init_result_(KERN_SUCCESS) {
+}
+
+//==============================================================================
+ReceivePort::~ReceivePort() {
+ if (init_result_ == KERN_SUCCESS)
+ mach_port_deallocate(mach_task_self(), port_);
+}
+
+//==============================================================================
+kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
+ mach_msg_timeout_t timeout) {
+ if (!out_message) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ // return any error condition encountered in constructor
+ if (init_result_ != KERN_SUCCESS)
+ return init_result_;
+
+ out_message->head.msgh_bits = 0;
+ out_message->head.msgh_local_port = port_;
+ out_message->head.msgh_remote_port = MACH_PORT_NULL;
+ out_message->head.msgh_reserved = 0;
+ out_message->head.msgh_id = 0;
+
+ kern_return_t result = mach_msg(&out_message->head,
+ MACH_RCV_MSG | MACH_RCV_TIMEOUT,
+ 0,
+ sizeof(MachMessage),
+ port_,
+ timeout, // timeout in ms
+ MACH_PORT_NULL);
+
+ return result;
+}
+
+#pragma mark -
+
+//==============================================================================
+// get a port with send rights corresponding to a named registered service
+MachPortSender::MachPortSender(const char *receive_port_name) {
+ mach_port_t bootstrap_port = 0;
+ init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
+
+ if (init_result_ != KERN_SUCCESS)
+ return;
+
+ init_result_ = bootstrap_look_up(bootstrap_port,
+ const_cast<char*>(receive_port_name),
+ &send_port_);
+}
+
+//==============================================================================
+MachPortSender::MachPortSender(mach_port_t send_port)
+ : send_port_(send_port),
+ init_result_(KERN_SUCCESS) {
+}
+
+//==============================================================================
+kern_return_t MachPortSender::SendMessage(MachSendMessage &message,
+ mach_msg_timeout_t timeout) {
+ if (message.head.msgh_size == 0) {
+ return KERN_INVALID_VALUE; // just for safety -- never should occur
+ };
+
+ if (init_result_ != KERN_SUCCESS)
+ return init_result_;
+
+ message.head.msgh_remote_port = send_port_;
+
+ kern_return_t result = mach_msg(&message.head,
+ MACH_SEND_MSG | MACH_SEND_TIMEOUT,
+ message.head.msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ timeout, // timeout in ms
+ MACH_PORT_NULL);
+
+ return result;
+}
diff --git a/src/common/mac/SimpleStringDictionary.h b/src/common/mac/SimpleStringDictionary.h
new file mode 100644
index 00000000..3d6c27bd
--- /dev/null
+++ b/src/common/mac/SimpleStringDictionary.h
@@ -0,0 +1,195 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// SimpleStringDictionary.h
+//
+
+#ifndef SimpleStringDictionary_H__
+#define SimpleStringDictionary_H__
+
+#import <string>
+#import <vector>
+
+namespace google_breakpad {
+
+//==============================================================================
+// SimpleStringDictionary (and associated class KeyValueEntry) implement a very
+// basic dictionary container class. It has the property of not making any
+// memory allocations when getting and setting values. But it is not very
+// efficient, with calls to get and set values operating in linear time.
+// It has the additional limitation of having a fairly small fixed capacity of
+// SimpleStringDictionary::MAX_NUM_ENTRIES entries. An assert() will fire if
+// the client attempts to set more than this number of key/value pairs.
+// Ordinarilly a C++ programmer would use something like the std::map template
+// class, or on the Macintosh would often choose CFDictionary or NSDictionary.
+// But these dictionary classes may call malloc() during get and set operations.
+// Google Breakpad requires that no memory allocations be made in code running
+// in its exception handling thread, so it uses SimpleStringDictionary as the
+// underlying implementation for the GoogleBreakpad.framework APIs:
+// GoogleBreakpadSetKeyValue(), GoogleBreakpadKeyValue(), and
+// GoogleBreakpadRemoveKeyValue()
+//
+
+//==============================================================================
+// KeyValueEntry
+//
+// A helper class used by SimpleStringDictionary representing a single
+// storage cell for a key/value pair. Each key and value string are
+// limited to MAX_STRING_STORAGE_SIZE-1 bytes (not glyphs). This class
+// performs no memory allocations. It has methods for setting and getting
+// key and value strings.
+//
+class KeyValueEntry {
+ public:
+ KeyValueEntry() {
+ Clear();
+ }
+
+ KeyValueEntry(const char *key, const char *value) {
+ SetKeyValue(key, value);
+ }
+
+ void SetKeyValue(const char *key, const char *value) {
+ if (!key) {
+ key = "";
+ }
+ if (!value) {
+ value = "";
+ }
+
+ strlcpy(key_, key, sizeof(key_));
+ strlcpy(value_, value, sizeof(value_));
+ }
+
+ void SetValue(const char *value) {
+ if (!value) {
+ value = "";
+ }
+ strlcpy(value_, value, sizeof(value_));
+ };
+
+ // Removes the key/value
+ void Clear() {
+ memset(key_, 0, sizeof(key_));
+ memset(value_, 0, sizeof(value_));
+ }
+
+ bool IsActive() const { return key_[0] != '\0'; }
+ const char *GetKey() const { return key_; }
+ const char *GetValue() const { return value_; }
+
+ // Don't change this without considering the fixed size
+ // of MachMessage (in MachIPC.h)
+ // (see also struct KeyValueMessageData in Inspector.h)
+ enum {MAX_STRING_STORAGE_SIZE = 256};
+
+ private:
+ char key_[MAX_STRING_STORAGE_SIZE];
+ char value_[MAX_STRING_STORAGE_SIZE];
+};
+
+//==============================================================================
+// This class is not an efficient dictionary, but for the purposes of breakpad
+// will be just fine. We're just dealing with ten or so distinct
+// key/value pairs. The idea is to avoid any malloc() or free() calls
+// in certain important methods to be called when a process is in a
+// crashed state. Each key and value string are limited to
+// KeyValueEntry::MAX_STRING_STORAGE_SIZE-1 bytes (not glyphs). Strings passed
+// in exceeding this length will be truncated.
+//
+class SimpleStringDictionary {
+ public:
+ SimpleStringDictionary() {}; // entries will all be cleared
+
+ // Returns the number of active key/value pairs. The upper limit for this
+ // is MAX_NUM_ENTRIES.
+ int GetCount() const;
+
+ // Given |key|, returns its corresponding |value|.
+ // If |key| is NULL, an assert will fire or NULL will be returned. If |key|
+ // is not found or is an empty string, NULL is returned.
+ const char *GetValueForKey(const char *key);
+
+ // Stores a string |value| represented by |key|. If |key| is NULL or an empty
+ // string, this will assert (or do nothing). If |value| is NULL then
+ // the |key| will be removed. An empty string is OK for |value|.
+ void SetKeyValue(const char *key, const char *value);
+
+ // Given |key|, removes any associated value. It will assert (or do nothing)
+ // if NULL is passed in. It will do nothing if |key| is not found.
+ void RemoveKey(const char *key);
+
+ // This is the maximum number of key/value pairs which may be set in the
+ // dictionary. An assert may fire if more values than this are set.
+ // Don't change this without also changing comment in GoogleBreakpad.h
+ enum {MAX_NUM_ENTRIES = 64};
+
+ private:
+ friend class SimpleStringDictionaryIterator;
+
+ const KeyValueEntry *GetEntry(int i) const;
+
+ KeyValueEntry entries_[MAX_NUM_ENTRIES];
+};
+
+//==============================================================================
+class SimpleStringDictionaryIterator {
+ public:
+ SimpleStringDictionaryIterator(const SimpleStringDictionary &dict)
+ : dict_(dict), i_(0) {
+ }
+
+ // Initializes iterator to the beginning (may later call Next() )
+ void Start() {
+ i_ = 0;
+ }
+
+ // like the nextObject method of NSEnumerator (in Cocoa)
+ // returns NULL when there are no more entries
+ //
+ const KeyValueEntry* Next() {
+ for (; i_ < SimpleStringDictionary::MAX_NUM_ENTRIES; ++i_) {
+ const KeyValueEntry *entry = dict_.GetEntry(i_);
+ if (entry->IsActive()) {
+ i_++; // move to next entry for next time
+ return entry;
+ }
+ }
+
+ return NULL; // reached end of array
+ }
+
+ private:
+ const SimpleStringDictionary& dict_;
+ int i_;
+};
+
+} // namespace google_breakpad
+
+#endif // SimpleStringDictionary_H__
diff --git a/src/common/mac/SimpleStringDictionary.mm b/src/common/mac/SimpleStringDictionary.mm
new file mode 100644
index 00000000..567855e0
--- /dev/null
+++ b/src/common/mac/SimpleStringDictionary.mm
@@ -0,0 +1,131 @@
+// Copyright (c) 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// SimpleStringDictionary.mm
+// Simple string dictionary that does not allocate memory
+//
+
+#import "SimpleStringDictionary.h"
+
+namespace google_breakpad {
+
+//==============================================================================
+const KeyValueEntry *SimpleStringDictionary::GetEntry(int i) const {
+ return (i >= 0 && i < MAX_NUM_ENTRIES) ? &entries_[i] : NULL;
+}
+
+//==============================================================================
+int SimpleStringDictionary::GetCount() const {
+ int count = 0;
+ for (int i = 0; i < MAX_NUM_ENTRIES; ++i) {
+ if (entries_[i].IsActive() ) {
+ ++count;
+ }
+ }
+
+ return count;
+}
+
+//==============================================================================
+const char *SimpleStringDictionary::GetValueForKey(const char *key) {
+ assert(key);
+ if (!key)
+ return NULL;
+
+ for (int i = 0; i < MAX_NUM_ENTRIES; ++i) {
+ KeyValueEntry &entry = entries_[i];
+ if (entry.IsActive() && !strcmp(entry.GetKey(), key)) {
+ return entry.GetValue();
+ }
+ }
+
+ return NULL;
+}
+
+//==============================================================================
+void SimpleStringDictionary::SetKeyValue(const char *key,
+ const char *value) {
+ if (!value) {
+ RemoveKey(key);
+ return;
+ }
+
+ // key must not be NULL
+ assert(key);
+ if (!key)
+ return;
+
+ // key must not be empty string
+ assert(key[0] != '\0');
+ if (key[0] == '\0')
+ return;
+
+ int free_index = -1;
+
+ // check if key already exists
+ for (int i = 0; i < MAX_NUM_ENTRIES; ++i) {
+ KeyValueEntry &entry = entries_[i];
+
+ if (entry.IsActive()) {
+ if (!strcmp(entry.GetKey(), key)) {
+ entry.SetValue(value);
+ return;
+ }
+ } else {
+ // Make a note of an empty slot
+ if (free_index == -1) {
+ free_index = i;
+ }
+ }
+ }
+
+ // check if we've run out of space
+ assert(free_index != -1);
+
+ // Put new key into an empty slot (if found)
+ if (free_index != -1) {
+ entries_[free_index].SetKeyValue(key, value);
+ }
+}
+
+//==============================================================================
+void SimpleStringDictionary::RemoveKey(const char *key) {
+ assert(key);
+ if (!key)
+ return;
+
+ for (int i = 0; i < MAX_NUM_ENTRIES; ++i) {
+ if (!strcmp(entries_[i].GetKey(), key)) {
+ entries_[i].Clear();
+ return;
+ }
+ }
+}
+
+} // namespace google_breakpad
diff --git a/src/common/mac/testing/GTMSenTestCase.h b/src/common/mac/testing/GTMSenTestCase.h
new file mode 100644
index 00000000..d425f597
--- /dev/null
+++ b/src/common/mac/testing/GTMSenTestCase.h
@@ -0,0 +1,1004 @@
+//
+// GTMSenTestCase.h
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+// Portions of this file fall under the following license, marked with
+// SENTE_BEGIN - SENTE_END
+//
+// Copyright (c) 1997-2005, Sen:te (Sente SA). All rights reserved.
+//
+// Use of this source code is governed by the following license:
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// (1) Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// (2) Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 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 Sente SA 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.
+//
+// Note: this license is equivalent to the FreeBSD license.
+//
+// This notice may not be removed from this file.
+
+// Some extra test case macros that would have been convenient for SenTestingKit
+// to provide. I didn't stick GTM in front of the Macro names, so that they would
+// be easy to remember.
+
+#import "GTMDefines.h"
+
+#if (!GTM_IPHONE_SDK)
+#import <SenTestingKit/SenTestingKit.h>
+#else
+#import <Foundation/Foundation.h>
+NSString *STComposeString(NSString *, ...);
+#endif
+
+// Generates a failure when a1 != noErr
+// Args:
+// a1: should be either an OSErr or an OSStatus
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define STAssertNoErr(a1, description, ...) \
+do { \
+ @try {\
+ OSStatus a1value = (a1); \
+ if (a1value != noErr) { \
+ NSString *_expression = [NSString stringWithFormat:@"Expected noErr, got %ld for (%s)", a1value, #a1]; \
+ if (description) { \
+ _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \
+ } \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:_expression]]; \
+ } \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == noErr fails", #a1] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+// Generates a failure when a1 != a2
+// Args:
+// a1: received value. Should be either an OSErr or an OSStatus
+// a2: expected value. Should be either an OSErr or an OSStatus
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define STAssertErr(a1, a2, description, ...) \
+do { \
+ @try {\
+ OSStatus a1value = (a1); \
+ OSStatus a2value = (a2); \
+ if (a1value != a2value) { \
+ NSString *_expression = [NSString stringWithFormat:@"Expected %s(%ld) but got %ld for (%s)", #a2, a2value, a1value, #a1]; \
+ if (description) { \
+ _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \
+ } \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:_expression]]; \
+ } \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s) fails", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+
+// Generates a failure when a1 is NULL
+// Args:
+// a1: should be a pointer (use STAssertNotNil for an object)
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define STAssertNotNULL(a1, description, ...) \
+do { \
+ @try {\
+ const void* a1value = (a1); \
+ if (a1value == NULL) { \
+ NSString *_expression = [NSString stringWithFormat:@"(%s) != NULL", #a1]; \
+ if (description) { \
+ _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \
+ } \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:_expression]]; \
+ } \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != NULL fails", #a1] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+// Generates a failure when a1 is not NULL
+// Args:
+// a1: should be a pointer (use STAssertNil for an object)
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define STAssertNULL(a1, description, ...) \
+do { \
+ @try {\
+ const void* a1value = (a1); \
+ if (a1value != NULL) { \
+ NSString *_expression = [NSString stringWithFormat:@"(%s) == NULL", #a1]; \
+ if (description) { \
+ _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \
+ } \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:_expression]]; \
+ } \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == NULL fails", #a1] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+// Generates a failure when a1 is equal to a2. This test is for C scalars,
+// structs and unions.
+// Args:
+// a1: argument 1
+// a2: argument 2
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define STAssertNotEquals(a1, a2, description, ...) \
+do { \
+ @try {\
+ if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:[[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \
+ } else { \
+ __typeof__(a1) a1value = (a1); \
+ __typeof__(a2) a2value = (a2); \
+ NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \
+ NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \
+ if ([a1encoded isEqualToValue:a2encoded]) { \
+ NSString *_expression = [NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2]; \
+ if (description) { \
+ _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \
+ } \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:_expression]]; \
+ } \
+ } \
+ } \
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+// Generates a failure when a1 is equal to a2. This test is for objects.
+// Args:
+// a1: argument 1. object.
+// a2: argument 2. object.
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define STAssertNotEqualObjects(a1, a2, desc, ...) \
+do { \
+ @try {\
+ id a1value = (a1); \
+ id a2value = (a2); \
+ if ( (@encode(__typeof__(a1value)) == @encode(id)) && \
+ (@encode(__typeof__(a2value)) == @encode(id)) && \
+ ![(id)a1value isEqual:(id)a2value] ) continue; \
+ NSString *_expression = [NSString stringWithFormat:@"%s('%@') != %s('%@')", #a1, [a1 description], #a2, [a2 description]]; \
+ if (desc) { \
+ _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(desc, ##__VA_ARGS__)]; \
+ } \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:_expression]]; \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(desc, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+// Generates a failure when a1 is not 'op' to a2. This test is for C scalars.
+// Args:
+// a1: argument 1
+// a2: argument 2
+// op: operation
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define STAssertOperation(a1, a2, op, description, ...) \
+do { \
+ @try {\
+ if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:[[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \
+ } else { \
+ __typeof__(a1) a1value = (a1); \
+ __typeof__(a2) a2value = (a2); \
+ if (!(a1value op a2value)) { \
+ double a1DoubleValue = a1value; \
+ double a2DoubleValue = a2value; \
+ NSString *_expression = [NSString stringWithFormat:@"%s (%lg) %s %s (%lg)", #a1, a1DoubleValue, #op, #a2, a2DoubleValue]; \
+ if (description) { \
+ _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \
+ } \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:_expression]]; \
+ } \
+ } \
+ } \
+ @catch (id anException) {\
+ [self failWithException:[NSException \
+ failureInRaise:[NSString stringWithFormat:@"(%s) %s (%s)", #a1, #op, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+// Generates a failure when a1 is not > a2. This test is for C scalars.
+// Args:
+// a1: argument 1
+// a2: argument 2
+// op: operation
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define STAssertGreaterThan(a1, a2, description, ...) \
+ STAssertOperation(a1, a2, >, description, ##__VA_ARGS__)
+
+// Generates a failure when a1 is not >= a2. This test is for C scalars.
+// Args:
+// a1: argument 1
+// a2: argument 2
+// op: operation
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define STAssertGreaterThanOrEqual(a1, a2, description, ...) \
+ STAssertOperation(a1, a2, >=, description, ##__VA_ARGS__)
+
+// Generates a failure when a1 is not < a2. This test is for C scalars.
+// Args:
+// a1: argument 1
+// a2: argument 2
+// op: operation
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define STAssertLessThan(a1, a2, description, ...) \
+ STAssertOperation(a1, a2, <, description, ##__VA_ARGS__)
+
+// Generates a failure when a1 is not <= a2. This test is for C scalars.
+// Args:
+// a1: argument 1
+// a2: argument 2
+// op: operation
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define STAssertLessThanOrEqual(a1, a2, description, ...) \
+ STAssertOperation(a1, a2, <=, description, ##__VA_ARGS__)
+
+// Generates a failure when string a1 is not equal to string a2. This call
+// differs from STAssertEqualObjects in that strings that are different in
+// composition (precomposed vs decomposed) will compare equal if their final
+// representation is equal.
+// ex O + umlaut decomposed is the same as O + umlaut composed.
+// Args:
+// a1: string 1
+// a2: string 2
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define STAssertEqualStrings(a1, a2, description, ...) \
+do { \
+ @try {\
+ id a1value = (a1); \
+ id a2value = (a2); \
+ if (a1value == a2value) continue; \
+ if ([a1value isKindOfClass:[NSString class]] && \
+ [a2value isKindOfClass:[NSString class]] && \
+ [a1value compare:a2value options:0] == NSOrderedSame) continue; \
+ [self failWithException:[NSException failureInEqualityBetweenObject: a1value \
+ andObject: a2value \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+// Generates a failure when string a1 is equal to string a2. This call
+// differs from STAssertEqualObjects in that strings that are different in
+// composition (precomposed vs decomposed) will compare equal if their final
+// representation is equal.
+// ex O + umlaut decomposed is the same as O + umlaut composed.
+// Args:
+// a1: string 1
+// a2: string 2
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define STAssertNotEqualStrings(a1, a2, description, ...) \
+do { \
+ @try {\
+ id a1value = (a1); \
+ id a2value = (a2); \
+ if ([a1value isKindOfClass:[NSString class]] && \
+ [a2value isKindOfClass:[NSString class]] && \
+ [a1value compare:a2value options:0] != NSOrderedSame) continue; \
+ [self failWithException:[NSException failureInEqualityBetweenObject: a1value \
+ andObject: a2value \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+// Generates a failure when c-string a1 is not equal to c-string a2.
+// Args:
+// a1: string 1
+// a2: string 2
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define STAssertEqualCStrings(a1, a2, description, ...) \
+do { \
+ @try {\
+ const char* a1value = (a1); \
+ const char* a2value = (a2); \
+ if (a1value == a2value) continue; \
+ if (strcmp(a1value, a2value) == 0) continue; \
+ [self failWithException:[NSException failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \
+ andObject: [NSString stringWithUTF8String:a2value] \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+// Generates a failure when c-string a1 is equal to c-string a2.
+// Args:
+// a1: string 1
+// a2: string 2
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define STAssertNotEqualCStrings(a1, a2, description, ...) \
+do { \
+ @try {\
+ const char* a1value = (a1); \
+ const char* a2value = (a2); \
+ if (strcmp(a1value, a2value) != 0) continue; \
+ [self failWithException:[NSException failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \
+ andObject: [NSString stringWithUTF8String:a2value] \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+#if GTM_IPHONE_SDK
+
+// SENTE_BEGIN
+/*" Generates a failure when !{ [a1 isEqualTo:a2] } is false
+ (or one is nil and the other is not).
+ _{a1 The object on the left.}
+ _{a2 The object on the right.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertEqualObjects(a1, a2, description, ...) \
+do { \
+ @try {\
+ id a1value = (a1); \
+ id a2value = (a2); \
+ if (a1value == a2value) continue; \
+ if ( (@encode(__typeof__(a1value)) == @encode(id)) && \
+ (@encode(__typeof__(a2value)) == @encode(id)) && \
+ [(id)a1value isEqual: (id)a2value] ) continue; \
+ [self failWithException:[NSException failureInEqualityBetweenObject: a1value \
+ andObject: a2value \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+
+/*" Generates a failure when a1 is not equal to a2. This test is for
+ C scalars, structs and unions.
+ _{a1 The argument on the left.}
+ _{a2 The argument on the right.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertEquals(a1, a2, description, ...) \
+do { \
+ @try {\
+ if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \
+ } else { \
+ __typeof__(a1) a1value = (a1); \
+ __typeof__(a2) a2value = (a2); \
+ NSValue *a1encoded = [NSValue value:&a1value withObjCType: @encode(__typeof__(a1))]; \
+ NSValue *a2encoded = [NSValue value:&a2value withObjCType: @encode(__typeof__(a2))]; \
+ if (![a1encoded isEqualToValue:a2encoded]) { \
+ [self failWithException:[NSException failureInEqualityBetweenValue: a1encoded \
+ andValue: a2encoded \
+ withAccuracy: nil \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+ } \
+ } \
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+#define STAbsoluteDifference(left,right) (MAX(left,right)-MIN(left,right))
+
+
+/*" Generates a failure when a1 is not equal to a2 within + or - accuracy is false.
+ This test is for scalars such as floats and doubles where small differences
+ could make these items not exactly equal, but also works for all scalars.
+ _{a1 The scalar on the left.}
+ _{a2 The scalar on the right.}
+ _{accuracy The maximum difference between a1 and a2 for these values to be
+ considered equal.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+
+#define STAssertEqualsWithAccuracy(a1, a2, accuracy, description, ...) \
+do { \
+ @try {\
+ if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
+ [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:[[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \
+ } else { \
+ __typeof__(a1) a1value = (a1); \
+ __typeof__(a2) a2value = (a2); \
+ __typeof__(accuracy) accuracyvalue = (accuracy); \
+ if (STAbsoluteDifference(a1value, a2value) > accuracyvalue) { \
+ NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \
+ NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \
+ NSValue *accuracyencoded = [NSValue value:&accuracyvalue withObjCType:@encode(__typeof__(accuracy))]; \
+ [self failWithException:[NSException failureInEqualityBetweenValue: a1encoded \
+ andValue: a2encoded \
+ withAccuracy: accuracyencoded \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+ } \
+ } \
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+
+
+/*" Generates a failure unconditionally.
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STFail(description, ...) \
+[self failWithException:[NSException failureInFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]
+
+
+
+/*" Generates a failure when a1 is not nil.
+ _{a1 An object.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertNil(a1, description, ...) \
+do { \
+ @try {\
+ id a1value = (a1); \
+ if (a1value != nil) { \
+ NSString *_a1 = [NSString stringWithUTF8String: #a1]; \
+ NSString *_expression = [NSString stringWithFormat:@"((%@) == nil)", _a1]; \
+ [self failWithException:[NSException failureInCondition: _expression \
+ isTrue: NO \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == nil fails", #a1] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+
+/*" Generates a failure when a1 is nil.
+ _{a1 An object.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertNotNil(a1, description, ...) \
+do { \
+ @try {\
+ id a1value = (a1); \
+ if (a1value == nil) { \
+ NSString *_a1 = [NSString stringWithUTF8String: #a1]; \
+ NSString *_expression = [NSString stringWithFormat:@"((%@) != nil)", _a1]; \
+ [self failWithException:[NSException failureInCondition: _expression \
+ isTrue: NO \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+ }\
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != nil fails", #a1] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while(0)
+
+
+/*" Generates a failure when expression evaluates to false.
+ _{expr The expression that is tested.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertTrue(expr, description, ...) \
+do { \
+ BOOL _evaluatedExpression = (expr);\
+ if (!_evaluatedExpression) {\
+ NSString *_expression = [NSString stringWithUTF8String: #expr];\
+ [self failWithException:[NSException failureInCondition: _expression \
+ isTrue: NO \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+} while (0)
+
+
+/*" Generates a failure when expression evaluates to false and in addition will
+ generate error messages if an exception is encountered.
+ _{expr The expression that is tested.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertTrueNoThrow(expr, description, ...) \
+do { \
+ @try {\
+ BOOL _evaluatedExpression = (expr);\
+ if (!_evaluatedExpression) {\
+ NSString *_expression = [NSString stringWithUTF8String: #expr];\
+ [self failWithException:[NSException failureInCondition: _expression \
+ isTrue: NO \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+ } \
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) ", #expr] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while (0)
+
+
+/*" Generates a failure when the expression evaluates to true.
+ _{expr The expression that is tested.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertFalse(expr, description, ...) \
+do { \
+ BOOL _evaluatedExpression = (expr);\
+ if (_evaluatedExpression) {\
+ NSString *_expression = [NSString stringWithUTF8String: #expr];\
+ [self failWithException:[NSException failureInCondition: _expression \
+ isTrue: YES \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+} while (0)
+
+
+/*" Generates a failure when the expression evaluates to true and in addition
+ will generate error messages if an exception is encountered.
+ _{expr The expression that is tested.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertFalseNoThrow(expr, description, ...) \
+do { \
+ @try {\
+ BOOL _evaluatedExpression = (expr);\
+ if (_evaluatedExpression) {\
+ NSString *_expression = [NSString stringWithUTF8String: #expr];\
+ [self failWithException:[NSException failureInCondition: _expression \
+ isTrue: YES \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ } \
+ } \
+ @catch (id anException) {\
+ [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"!(%s) ", #expr] \
+ exception:anException \
+ inFile:[NSString stringWithUTF8String:__FILE__] \
+ atLine:__LINE__ \
+ withDescription:STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while (0)
+
+
+/*" Generates a failure when expression does not throw an exception.
+ _{expression The expression that is evaluated.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.
+"*/
+#define STAssertThrows(expr, description, ...) \
+do { \
+ @try { \
+ (expr);\
+ } \
+ @catch (id anException) { \
+ continue; \
+ }\
+ [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: nil \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+} while (0)
+
+
+/*" Generates a failure when expression does not throw an exception of a
+ specific class.
+ _{expression The expression that is evaluated.}
+ _{specificException The specified class of the exception.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertThrowsSpecific(expr, specificException, description, ...) \
+do { \
+ @try { \
+ (expr);\
+ } \
+ @catch (specificException *anException) { \
+ continue; \
+ }\
+ @catch (id anException) {\
+ NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\
+ [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: anException \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+ continue; \
+ }\
+ NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\
+ [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: nil \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+} while (0)
+
+
+/*" Generates a failure when expression does not throw an exception of a
+ specific class with a specific name. Useful for those frameworks like
+ AppKit or Foundation that throw generic NSException w/specific names
+ (NSInvalidArgumentException, etc).
+ _{expression The expression that is evaluated.}
+ _{specificException The specified class of the exception.}
+ _{aName The name of the specified exception.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+
+"*/
+#define STAssertThrowsSpecificNamed(expr, specificException, aName, description, ...) \
+do { \
+ @try { \
+ (expr);\
+ } \
+ @catch (specificException *anException) { \
+ if ([aName isEqualToString: [anException name]]) continue; \
+ NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description);\
+ [self failWithException: \
+ [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: anException \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+ continue; \
+ }\
+ @catch (id anException) {\
+ NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\
+ [self failWithException: \
+ [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: anException \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+ continue; \
+ }\
+ NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\
+ [self failWithException: \
+ [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: nil \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+} while (0)
+
+
+/*" Generates a failure when expression does throw an exception.
+ _{expression The expression that is evaluated.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertNoThrow(expr, description, ...) \
+do { \
+ @try { \
+ (expr);\
+ } \
+ @catch (id anException) { \
+ [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: anException \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+} while (0)
+
+
+/*" Generates a failure when expression does throw an exception of the specitied
+ class. Any other exception is okay (i.e. does not generate a failure).
+ _{expression The expression that is evaluated.}
+ _{specificException The specified class of the exception.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+"*/
+#define STAssertNoThrowSpecific(expr, specificException, description, ...) \
+do { \
+ @try { \
+ (expr);\
+ } \
+ @catch (specificException *anException) { \
+ [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: anException \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(description, ##__VA_ARGS__)]]; \
+ }\
+ @catch (id anythingElse) {\
+ ; \
+ }\
+} while (0)
+
+
+/*" Generates a failure when expression does throw an exception of a
+ specific class with a specific name. Useful for those frameworks like
+ AppKit or Foundation that throw generic NSException w/specific names
+ (NSInvalidArgumentException, etc).
+ _{expression The expression that is evaluated.}
+ _{specificException The specified class of the exception.}
+ _{aName The name of the specified exception.}
+ _{description A format string as in the printf() function. Can be nil or
+ an empty string but must be present.}
+ _{... A variable number of arguments to the format string. Can be absent.}
+
+"*/
+#define STAssertNoThrowSpecificNamed(expr, specificException, aName, description, ...) \
+do { \
+ @try { \
+ (expr);\
+ } \
+ @catch (specificException *anException) { \
+ if ([aName isEqualToString: [anException name]]) { \
+ NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description);\
+ [self failWithException: \
+ [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \
+ exception: anException \
+ inFile: [NSString stringWithUTF8String:__FILE__] \
+ atLine: __LINE__ \
+ withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \
+ } \
+ continue; \
+ }\
+ @catch (id anythingElse) {\
+ ; \
+ }\
+} while (0)
+
+
+
+@interface NSException (GTMSenTestAdditions)
++ (NSException *)failureInFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ...;
++ (NSException *)failureInCondition:(NSString *)condition
+ isTrue:(BOOL)isTrue
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ...;
++ (NSException *)failureInEqualityBetweenObject:(id)left
+ andObject:(id)right
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ...;
++ (NSException *)failureInEqualityBetweenValue:(NSValue *)left
+ andValue:(NSValue *)right
+ withAccuracy:(NSValue *)accuracy
+ inFile:(NSString *)filename
+ atLine:(int) ineNumber
+ withDescription:(NSString *)formatString, ...;
++ (NSException *)failureInRaise:(NSString *)expression
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ...;
++ (NSException *)failureInRaise:(NSString *)expression
+ exception:(NSException *)exception
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ...;
+@end
+
+// SENTE_END
+
+@interface SenTestCase : NSObject {
+ SEL currentSelector_;
+}
+
+- (void)setUp;
+- (void)invokeTest;
+- (void)tearDown;
+- (void)performTest:(SEL)sel;
+- (void)failWithException:(NSException*)exception;
+@end
+
+GTM_EXTERN NSString *const SenTestFailureException;
+
+GTM_EXTERN NSString *const SenTestFilenameKey;
+GTM_EXTERN NSString *const SenTestLineNumberKey;
+
+#endif // GTM_IPHONE_SDK
+
+// All unittest cases in GTM should inherit from GTMTestCase. It makes sure
+// to set up our logging system correctly to verify logging calls.
+// See GTMUnitTestDevLog.h for details
+@interface GTMTestCase : SenTestCase
+@end
diff --git a/src/common/mac/testing/GTMSenTestCase.m b/src/common/mac/testing/GTMSenTestCase.m
new file mode 100644
index 00000000..99b9db07
--- /dev/null
+++ b/src/common/mac/testing/GTMSenTestCase.m
@@ -0,0 +1,366 @@
+//
+// GTMSenTestCase.m
+//
+// Copyright 2007-2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GTMSenTestCase.h"
+#import <unistd.h>
+
+#if !GTM_IPHONE_SDK
+#import "GTMGarbageCollection.h"
+#endif // !GTM_IPHONE_SDK
+
+#if GTM_IPHONE_SDK
+#import <stdarg.h>
+
+@interface NSException (GTMSenTestPrivateAdditions)
++ (NSException *)failureInFile:(NSString *)filename
+ atLine:(int)lineNumber
+ reason:(NSString *)reason;
+@end
+
+@implementation NSException (GTMSenTestPrivateAdditions)
++ (NSException *)failureInFile:(NSString *)filename
+ atLine:(int)lineNumber
+ reason:(NSString *)reason {
+ NSDictionary *userInfo =
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithInteger:lineNumber], SenTestLineNumberKey,
+ filename, SenTestFilenameKey,
+ nil];
+
+ return [self exceptionWithName:SenTestFailureException
+ reason:reason
+ userInfo:userInfo];
+}
+@end
+
+@implementation NSException (GTMSenTestAdditions)
+
++ (NSException *)failureInFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... {
+
+ NSString *testDescription = @"";
+ if (formatString) {
+ va_list vl;
+ va_start(vl, formatString);
+ testDescription =
+ [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ }
+
+ NSString *reason = testDescription;
+
+ return [self failureInFile:filename atLine:lineNumber reason:reason];
+}
+
++ (NSException *)failureInCondition:(NSString *)condition
+ isTrue:(BOOL)isTrue
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... {
+
+ NSString *testDescription = @"";
+ if (formatString) {
+ va_list vl;
+ va_start(vl, formatString);
+ testDescription =
+ [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ }
+
+ NSString *reason = [NSString stringWithFormat:@"'%@' should be %s. %@",
+ condition, isTrue ? "TRUE" : "FALSE", testDescription];
+
+ return [self failureInFile:filename atLine:lineNumber reason:reason];
+}
+
++ (NSException *)failureInEqualityBetweenObject:(id)left
+ andObject:(id)right
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... {
+
+ NSString *testDescription = @"";
+ if (formatString) {
+ va_list vl;
+ va_start(vl, formatString);
+ testDescription =
+ [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ }
+
+ NSString *reason =
+ [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@",
+ [left description], [right description], testDescription];
+
+ return [self failureInFile:filename atLine:lineNumber reason:reason];
+}
+
++ (NSException *)failureInEqualityBetweenValue:(NSValue *)left
+ andValue:(NSValue *)right
+ withAccuracy:(NSValue *)accuracy
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... {
+
+ NSString *testDescription = @"";
+ if (formatString) {
+ va_list vl;
+ va_start(vl, formatString);
+ testDescription =
+ [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ }
+
+ NSString *reason;
+ if (accuracy) {
+ reason =
+ [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@",
+ left, right, testDescription];
+ } else {
+ reason =
+ [NSString stringWithFormat:@"'%@' should be equal to '%@' +/-'%@'. %@",
+ left, right, accuracy, testDescription];
+ }
+
+ return [self failureInFile:filename atLine:lineNumber reason:reason];
+}
+
++ (NSException *)failureInRaise:(NSString *)expression
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... {
+
+ NSString *testDescription = @"";
+ if (formatString) {
+ va_list vl;
+ va_start(vl, formatString);
+ testDescription =
+ [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ }
+
+ NSString *reason = [NSString stringWithFormat:@"'%@' should raise. %@",
+ expression, testDescription];
+
+ return [self failureInFile:filename atLine:lineNumber reason:reason];
+}
+
++ (NSException *)failureInRaise:(NSString *)expression
+ exception:(NSException *)exception
+ inFile:(NSString *)filename
+ atLine:(int)lineNumber
+ withDescription:(NSString *)formatString, ... {
+
+ NSString *testDescription = @"";
+ if (formatString) {
+ va_list vl;
+ va_start(vl, formatString);
+ testDescription =
+ [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ }
+
+ NSString *reason;
+ if ([[exception name] isEqualToString:SenTestFailureException]) {
+ // it's our exception, assume it has the right description on it.
+ reason = [exception reason];
+ } else {
+ // not one of our exception, use the exceptions reason and our description
+ reason = [NSString stringWithFormat:@"'%@' raised '%@'. %@",
+ expression, [exception reason], testDescription];
+ }
+
+ return [self failureInFile:filename atLine:lineNumber reason:reason];
+}
+
+@end
+
+NSString *STComposeString(NSString *formatString, ...) {
+ NSString *reason = @"";
+ if (formatString) {
+ va_list vl;
+ va_start(vl, formatString);
+ reason =
+ [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
+ va_end(vl);
+ }
+ return reason;
+}
+
+NSString *const SenTestFailureException = @"SenTestFailureException";
+NSString *const SenTestFilenameKey = @"SenTestFilenameKey";
+NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
+
+@interface SenTestCase (SenTestCasePrivate)
+// our method of logging errors
++ (void)printException:(NSException *)exception fromTestName:(NSString *)name;
+@end
+
+@implementation SenTestCase
+- (void)failWithException:(NSException*)exception {
+ [exception raise];
+}
+
+- (void)setUp {
+}
+
+- (void)performTest:(SEL)sel {
+ currentSelector_ = sel;
+ @try {
+ [self invokeTest];
+ } @catch (NSException *exception) {
+ [[self class] printException:exception
+ fromTestName:NSStringFromSelector(sel)];
+ [exception raise];
+ }
+}
+
++ (void)printException:(NSException *)exception fromTestName:(NSString *)name {
+ NSDictionary *userInfo = [exception userInfo];
+ NSString *filename = [userInfo objectForKey:SenTestFilenameKey];
+ NSNumber *lineNumber = [userInfo objectForKey:SenTestLineNumberKey];
+ NSString *className = NSStringFromClass([self class]);
+ if ([filename length] == 0) {
+ filename = @"Unknown.m";
+ }
+ fprintf(stderr, "%s:%ld: error: -[%s %s] : %s\n",
+ [filename UTF8String],
+ (long)[lineNumber integerValue],
+ [className UTF8String],
+ [name UTF8String],
+ [[exception reason] UTF8String]);
+ fflush(stderr);
+}
+
+- (void)invokeTest {
+ NSException *e = nil;
+ @try {
+ // Wrap things in autorelease pools because they may
+ // have an STMacro in their dealloc which may get called
+ // when the pool is cleaned up
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ // We don't log exceptions here, instead we let the person that called
+ // this log the exception. This ensures they are only logged once but the
+ // outer layers get the exceptions to report counts, etc.
+ @try {
+ [self setUp];
+ @try {
+ [self performSelector:currentSelector_];
+ } @catch (NSException *exception) {
+ e = [exception retain];
+ }
+ [self tearDown];
+ } @catch (NSException *exception) {
+ e = [exception retain];
+ }
+ [pool release];
+ } @catch (NSException *exception) {
+ e = [exception retain];
+ }
+ if (e) {
+ [e autorelease];
+ [e raise];
+ }
+}
+
+- (void)tearDown {
+}
+
+- (NSString *)description {
+ // This matches the description OCUnit would return to you
+ return [NSString stringWithFormat:@"-[%@ %@]", [self class],
+ NSStringFromSelector(currentSelector_)];
+}
+@end
+
+#endif // GTM_IPHONE_SDK
+
+@implementation GTMTestCase : SenTestCase
+- (void)invokeTest {
+ Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog");
+ if (devLogClass) {
+ [devLogClass performSelector:@selector(enableTracking)];
+ [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)];
+
+ }
+ [super invokeTest];
+ if (devLogClass) {
+ [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)];
+ [devLogClass performSelector:@selector(disableTracking)];
+ }
+}
+@end
+
+// Leak detection
+#if !GTM_IPHONE_DEVICE
+// Don't want to get leaks on the iPhone Device as the device doesn't
+// have 'leaks'. The simulator does though.
+
+// COV_NF_START
+// We don't have leak checking on by default, so this won't be hit.
+static void _GTMRunLeaks(void) {
+ // This is an atexit handler. It runs leaks for us to check if we are
+ // leaking anything in our tests.
+ const char* cExclusionsEnv = getenv("GTM_LEAKS_SYMBOLS_TO_IGNORE");
+ NSMutableString *exclusions = [NSMutableString string];
+ if (cExclusionsEnv) {
+ NSString *exclusionsEnv = [NSString stringWithUTF8String:cExclusionsEnv];
+ NSArray *exclusionsArray = [exclusionsEnv componentsSeparatedByString:@","];
+ NSString *exclusion;
+ NSCharacterSet *wcSet = [NSCharacterSet whitespaceCharacterSet];
+ GTM_FOREACH_OBJECT(exclusion, exclusionsArray) {
+ exclusion = [exclusion stringByTrimmingCharactersInSet:wcSet];
+ [exclusions appendFormat:@"-exclude \"%@\" ", exclusion];
+ }
+ }
+ NSString *string
+ = [NSString stringWithFormat:@"/usr/bin/leaks %@%d"
+ @"| /usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'",
+ exclusions, getpid()];
+ int ret = system([string UTF8String]);
+ if (ret) {
+ fprintf(stderr, "%s:%d: Error: Unable to run leaks. 'system' returned: %d",
+ __FILE__, __LINE__, ret);
+ fflush(stderr);
+ }
+}
+// COV_NF_END
+
+static __attribute__((constructor)) void _GTMInstallLeaks(void) {
+ BOOL checkLeaks = YES;
+#if !GTM_IPHONE_SDK
+ checkLeaks = GTMIsGarbageCollectionEnabled() ? NO : YES;
+#endif // !GTM_IPHONE_SDK
+ if (checkLeaks) {
+ checkLeaks = getenv("GTM_ENABLE_LEAKS") ? YES : NO;
+ if (checkLeaks) {
+ // COV_NF_START
+ // We don't have leak checking on by default, so this won't be hit.
+ fprintf(stderr, "Leak Checking Enabled\n");
+ fflush(stderr);
+ int ret = atexit(&_GTMRunLeaks);
+ _GTMDevAssert(ret == 0,
+ @"Unable to install _GTMRunLeaks as an atexit handler (%d)",
+ errno);
+ // COV_NF_END
+ }
+ }
+}
+
+#endif // !GTM_IPHONE_DEVICE