aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/mac/Breakpad.xcodeproj/project.pbxproj195
-rw-r--r--src/client/mac/crash_generation/client_info.h43
-rw-r--r--src/client/mac/crash_generation/crash_generation_client.cc74
-rw-r--r--src/client/mac/crash_generation/crash_generation_client.h65
-rw-r--r--src/client/mac/crash_generation/crash_generation_server.cc158
-rw-r--r--src/client/mac/crash_generation/crash_generation_server.h132
-rw-r--r--src/client/mac/handler/exception_handler.cc22
-rw-r--r--src/client/mac/handler/exception_handler.h16
-rw-r--r--src/client/mac/tests/crash_generation_server_test.cc217
-rw-r--r--src/client/mac/tests/exception_handler_test.cc5
-rw-r--r--src/common/mac/MachIPC.h8
-rw-r--r--src/common/mac/MachIPC.mm5
-rw-r--r--src/common/mac/scoped_task_suspend-inl.h56
13 files changed, 984 insertions, 12 deletions
diff --git a/src/client/mac/Breakpad.xcodeproj/project.pbxproj b/src/client/mac/Breakpad.xcodeproj/project.pbxproj
index 6e26ec93..b6bb5fe7 100644
--- a/src/client/mac/Breakpad.xcodeproj/project.pbxproj
+++ b/src/client/mac/Breakpad.xcodeproj/project.pbxproj
@@ -51,6 +51,9 @@
8B4BDABE12012CEF009C7060 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B4BDAA7120124EA009C7060 /* libcrypto.dylib */; };
8B4BDAC512012D05009C7060 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B4BDAA7120124EA009C7060 /* libcrypto.dylib */; };
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
+ D24BBBFD121050F000F3D417 /* breakpadUtilities.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; };
+ D24BBD291211EDB100F3D417 /* MachIPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53790ECCE635009BE4BA /* MachIPC.mm */; };
+ D24BBD321212CACF00F3D417 /* MachIPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53790ECCE635009BE4BA /* MachIPC.mm */; };
D2A5DD301188633800081F03 /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; };
D2A5DD401188640400081F03 /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; };
D2A5DD411188642E00081F03 /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; };
@@ -59,6 +62,34 @@
D2F9A44012131F65002747C1 /* gtest_main.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A43E12131F65002747C1 /* gtest_main.cc */; };
D2F9A44112131F65002747C1 /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A43F12131F65002747C1 /* gtest-all.cc */; };
D2F9A44412131F84002747C1 /* libgtest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D2F9A41512131EF0002747C1 /* libgtest.a */; };
+ D2F9A4C9121336C7002747C1 /* client_info.h in Headers */ = {isa = PBXBuildFile; fileRef = D2F9A4C4121336C7002747C1 /* client_info.h */; };
+ D2F9A4CA121336C7002747C1 /* crash_generation_client.h in Headers */ = {isa = PBXBuildFile; fileRef = D2F9A4C5121336C7002747C1 /* crash_generation_client.h */; };
+ D2F9A4CB121336C7002747C1 /* crash_generation_client.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */; };
+ D2F9A4CC121336C7002747C1 /* crash_generation_server.h in Headers */ = {isa = PBXBuildFile; fileRef = D2F9A4C7121336C7002747C1 /* crash_generation_server.h */; };
+ D2F9A4CD121336C7002747C1 /* crash_generation_server.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C8121336C7002747C1 /* crash_generation_server.cc */; };
+ D2F9A4DF12133AD9002747C1 /* crash_generation_client.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */; };
+ D2F9A4E012133AD9002747C1 /* crash_generation_server.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C8121336C7002747C1 /* crash_generation_server.cc */; };
+ D2F9A4E112133AE2002747C1 /* crash_generation_client.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */; };
+ D2F9A4E212133AE2002747C1 /* crash_generation_server.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C8121336C7002747C1 /* crash_generation_server.cc */; };
+ D2F9A52E121383A1002747C1 /* crash_generation_client.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */; };
+ D2F9A52F121383A1002747C1 /* crash_generation_server.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C8121336C7002747C1 /* crash_generation_server.cc */; };
+ D2F9A530121383A1002747C1 /* MachIPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53790ECCE635009BE4BA /* MachIPC.mm */; };
+ D2F9A531121383A1002747C1 /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; };
+ D2F9A532121383A1002747C1 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; };
+ D2F9A533121383A1002747C1 /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; };
+ D2F9A534121383A1002747C1 /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */; };
+ D2F9A535121383A1002747C1 /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; };
+ D2F9A536121383A1002747C1 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.c */; };
+ D2F9A537121383A1002747C1 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; };
+ D2F9A538121383A1002747C1 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; };
+ D2F9A539121383A1002747C1 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; };
+ D2F9A53A121383A1002747C1 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; };
+ D2F9A53B121383A1002747C1 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; };
+ D2F9A53C121383A1002747C1 /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; };
+ D2F9A53F121383A1002747C1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
+ D2F9A540121383A1002747C1 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B4BDAA7120124EA009C7060 /* libcrypto.dylib */; };
+ D2F9A541121383A1002747C1 /* libgtest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D2F9A41512131EF0002747C1 /* libgtest.a */; };
+ D2F9A553121383DC002747C1 /* crash_generation_server_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4CE121336F7002747C1 /* crash_generation_server_test.cc */; };
F91AF5D00FD60393009D8BE2 /* BreakpadFramework_Test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F91AF5CF0FD60393009D8BE2 /* BreakpadFramework_Test.mm */; };
F91AF6210FD60784009D8BE2 /* Breakpad.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; };
F9286B3A0F7EB25800A4DCC8 /* InspectorMain.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */; };
@@ -280,9 +311,23 @@
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
proxyType = 1;
- remoteGlobalIDString = D2F9A41412131EF0002747C1 /* gtest */;
+ remoteGlobalIDString = D2F9A41412131EF0002747C1;
remoteInfo = gtest;
};
+ D2F9A52C121383A1002747C1 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = D2F9A41412131EF0002747C1;
+ remoteInfo = gtest;
+ };
+ D2F9A5DE12142A6A002747C1 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = D2F9A52A121383A1002747C1;
+ remoteInfo = crash_generation_server_test;
+ };
F91AF6370FD60A74009D8BE2 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0867D690FE84028FC02AAC07 /* Project object */;
@@ -467,6 +512,13 @@
D2F9A43C12131F55002747C1 /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "../../testing/src/gmock-all.cc"; sourceTree = SOURCE_ROOT; };
D2F9A43E12131F65002747C1 /* gtest_main.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = gtest_main.cc; path = ../../testing/gtest/src/gtest_main.cc; sourceTree = "<group>"; };
D2F9A43F12131F65002747C1 /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "../../testing/gtest/src/gtest-all.cc"; sourceTree = "<group>"; };
+ D2F9A4C4121336C7002747C1 /* client_info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = client_info.h; path = crash_generation/client_info.h; sourceTree = "<group>"; };
+ D2F9A4C5121336C7002747C1 /* crash_generation_client.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crash_generation_client.h; path = crash_generation/crash_generation_client.h; sourceTree = "<group>"; };
+ D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = crash_generation_client.cc; path = crash_generation/crash_generation_client.cc; sourceTree = "<group>"; };
+ D2F9A4C7121336C7002747C1 /* crash_generation_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crash_generation_server.h; path = crash_generation/crash_generation_server.h; sourceTree = "<group>"; };
+ D2F9A4C8121336C7002747C1 /* crash_generation_server.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = crash_generation_server.cc; path = crash_generation/crash_generation_server.cc; sourceTree = "<group>"; };
+ D2F9A4CE121336F7002747C1 /* crash_generation_server_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = crash_generation_server_test.cc; path = tests/crash_generation_server_test.cc; sourceTree = "<group>"; };
+ D2F9A546121383A1002747C1 /* crash_generation_server_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = crash_generation_server_test; sourceTree = BUILT_PRODUCTS_DIR; };
DE43467411C72855004F095F /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = sender/da.lproj/Localizable.strings; sourceTree = "<group>"; };
DE43467511C72857004F095F /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = sender/de.lproj/Localizable.strings; sourceTree = "<group>"; };
DE43467611C7285B004F095F /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = sender/es.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -589,6 +641,16 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ D2F9A53E121383A1002747C1 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D2F9A53F121383A1002747C1 /* Foundation.framework in Frameworks */,
+ D2F9A540121383A1002747C1 /* libcrypto.dylib in Frameworks */,
+ D2F9A541121383A1002747C1 /* libgtest.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F92C53520ECCE349009BE4BA /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -661,6 +723,7 @@
F91AF6210FD60784009D8BE2 /* Breakpad.framework in Frameworks */,
8B3101EA11F0CDE300FCF3E4 /* SenTestingKit.framework in Frameworks */,
8B3102EB11F0D78000FCF3E4 /* Foundation.framework in Frameworks */,
+ D24BBBFD121050F000F3D417 /* breakpadUtilities.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -680,6 +743,7 @@
F93DE2D10F82A67300608B94 /* minidump_file_writer_unittest */,
F93DE32C0F82C55600608B94 /* handler_test */,
D2F9A41512131EF0002747C1 /* libgtest.a */,
+ D2F9A546121383A1002747C1 /* crash_generation_server_test */,
);
name = Products;
sourceTree = "<group>";
@@ -814,6 +878,11 @@
F92C53B50ECCE799009BE4BA /* crash_generation */ = {
isa = PBXGroup;
children = (
+ D2F9A4C4121336C7002747C1 /* client_info.h */,
+ D2F9A4C5121336C7002747C1 /* crash_generation_client.h */,
+ D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */,
+ D2F9A4C7121336C7002747C1 /* crash_generation_server.h */,
+ D2F9A4C8121336C7002747C1 /* crash_generation_server.cc */,
F9286B380F7EB25800A4DCC8 /* Inspector.h */,
F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */,
F92C53B70ECCE7B3009BE4BA /* Inspector.mm */,
@@ -917,6 +986,7 @@
F9C77DDF0F7DD7CF0045F7DB /* tests */ = {
isa = PBXGroup;
children = (
+ D2F9A4CE121336F7002747C1 /* crash_generation_server_test.cc */,
D2F9A3D41212F87C002747C1 /* exception_handler_test.cc */,
F9C77DE00F7DD7E30045F7DB /* SimpleStringDictionaryTest.h */,
F9C77DE10F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm */,
@@ -943,6 +1013,9 @@
files = (
F92C55D00ECD0064009BE4BA /* Breakpad.h in Headers */,
F92C56330ECD0DF1009BE4BA /* OnDemandServer.h in Headers */,
+ D2F9A4C9121336C7002747C1 /* client_info.h in Headers */,
+ D2F9A4CA121336C7002747C1 /* crash_generation_client.h in Headers */,
+ D2F9A4CC121336C7002747C1 /* crash_generation_server.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1003,6 +1076,23 @@
productReference = D2F9A41512131EF0002747C1 /* libgtest.a */;
productType = "com.apple.product-type.library.static";
};
+ D2F9A52A121383A1002747C1 /* crash_generation_server_test */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = D2F9A542121383A1002747C1 /* Build configuration list for PBXNativeTarget "crash_generation_server_test" */;
+ buildPhases = (
+ D2F9A52D121383A1002747C1 /* Sources */,
+ D2F9A53E121383A1002747C1 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ D2F9A52B121383A1002747C1 /* PBXTargetDependency */,
+ );
+ name = crash_generation_server_test;
+ productName = handler_test;
+ productReference = D2F9A546121383A1002747C1 /* crash_generation_server_test */;
+ productType = "com.apple.product-type.tool";
+ };
F92C53530ECCE349009BE4BA /* Inspector */ = {
isa = PBXNativeTarget;
buildConfigurationList = F92C53580ECCE36D009BE4BA /* Build configuration list for PBXNativeTarget "Inspector" */;
@@ -1138,6 +1228,7 @@
F93DE2FC0F82C3C600608B94 /* PBXTargetDependency */,
F93DE3700F82CC1300608B94 /* PBXTargetDependency */,
F91AF6380FD60A74009D8BE2 /* PBXTargetDependency */,
+ D2F9A5DF12142A6A002747C1 /* PBXTargetDependency */,
);
name = UnitTests;
productName = UnitTests;
@@ -1196,6 +1287,7 @@
F93DE2D00F82A67300608B94 /* minidump_file_writer_unittest */,
F93DE32B0F82C55600608B94 /* handler_test */,
D2F9A41412131EF0002747C1 /* gtest */,
+ D2F9A52A121383A1002747C1 /* crash_generation_server_test */,
);
};
/* End PBXProject section */
@@ -1412,7 +1504,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n\necho running minidump generator tests...\n\"${BUILT_PRODUCTS_DIR}/generator_test\"\necho Running exception handler tests...\n\"${BUILT_PRODUCTS_DIR}/handler_test\"\n";
+ shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n\necho running minidump generator tests...\n\"${BUILT_PRODUCTS_DIR}/generator_test\"\necho Running exception handler tests...\n\"${BUILT_PRODUCTS_DIR}/handler_test\"\necho Running crash generation server tests...\n\"${BUILT_PRODUCTS_DIR}/crash_generation_server_test\"\n";
};
/* End PBXShellScriptBuildPhase section */
@@ -1425,6 +1517,8 @@
F92C56630ECD1179009BE4BA /* exception_handler.cc in Sources */,
F92C55D10ECD0064009BE4BA /* Breakpad.mm in Sources */,
F92C56340ECD0DF1009BE4BA /* OnDemandServer.mm in Sources */,
+ D2F9A4CB121336C7002747C1 /* crash_generation_client.cc in Sources */,
+ D2F9A4CD121336C7002747C1 /* crash_generation_server.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1438,6 +1532,29 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ D2F9A52D121383A1002747C1 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ D2F9A553121383DC002747C1 /* crash_generation_server_test.cc in Sources */,
+ D2F9A52E121383A1002747C1 /* crash_generation_client.cc in Sources */,
+ D2F9A52F121383A1002747C1 /* crash_generation_server.cc in Sources */,
+ D2F9A530121383A1002747C1 /* MachIPC.mm in Sources */,
+ D2F9A531121383A1002747C1 /* breakpad_nlist_64.cc in Sources */,
+ D2F9A532121383A1002747C1 /* dynamic_images.cc in Sources */,
+ D2F9A533121383A1002747C1 /* exception_handler.cc in Sources */,
+ D2F9A534121383A1002747C1 /* minidump_generator.cc in Sources */,
+ D2F9A535121383A1002747C1 /* minidump_file_writer.cc in Sources */,
+ D2F9A536121383A1002747C1 /* convert_UTF.c in Sources */,
+ D2F9A537121383A1002747C1 /* string_conversion.cc in Sources */,
+ D2F9A538121383A1002747C1 /* file_id.cc in Sources */,
+ D2F9A539121383A1002747C1 /* macho_id.cc in Sources */,
+ D2F9A53A121383A1002747C1 /* macho_utilities.cc in Sources */,
+ D2F9A53B121383A1002747C1 /* macho_walker.cc in Sources */,
+ D2F9A53C121383A1002747C1 /* string_utilities.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F92C53510ECCE349009BE4BA /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -1481,6 +1598,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ D2F9A4DF12133AD9002747C1 /* crash_generation_client.cc in Sources */,
+ D2F9A4E012133AD9002747C1 /* crash_generation_server.cc in Sources */,
+ D24BBD291211EDB100F3D417 /* MachIPC.mm in Sources */,
D2A5DD401188640400081F03 /* breakpad_nlist_64.cc in Sources */,
F93803CD0F8083B7004D428B /* dynamic_images.cc in Sources */,
F93803CE0F8083B7004D428B /* exception_handler.cc in Sources */,
@@ -1512,6 +1632,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ D2F9A4E112133AE2002747C1 /* crash_generation_client.cc in Sources */,
+ D2F9A4E212133AE2002747C1 /* crash_generation_server.cc in Sources */,
+ D24BBD321212CACF00F3D417 /* MachIPC.mm in Sources */,
D2A5DD411188642E00081F03 /* breakpad_nlist_64.cc in Sources */,
F93DE3350F82C66B00608B94 /* dynamic_images.cc in Sources */,
F93DE3360F82C66B00608B94 /* exception_handler.cc in Sources */,
@@ -1587,6 +1710,16 @@
target = D2F9A41412131EF0002747C1 /* gtest */;
targetProxy = D2F9A44212131F80002747C1 /* PBXContainerItemProxy */;
};
+ D2F9A52B121383A1002747C1 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = D2F9A41412131EF0002747C1 /* gtest */;
+ targetProxy = D2F9A52C121383A1002747C1 /* PBXContainerItemProxy */;
+ };
+ D2F9A5DF12142A6A002747C1 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = D2F9A52A121383A1002747C1 /* crash_generation_server_test */;
+ targetProxy = D2F9A5DE12142A6A002747C1 /* PBXContainerItemProxy */;
+ };
F91AF6380FD60A74009D8BE2 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 8DC2EF4F0486A6940098B216 /* Breakpad */;
@@ -1821,6 +1954,51 @@
};
name = Release;
};
+ D2F9A543121383A1002747C1 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ HEADER_SEARCH_PATHS = (
+ ../..,
+ ../../testing,
+ ../../testing/include,
+ ../../testing/gtest,
+ ../../testing/gtest/include,
+ );
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)/build/Debug\"",
+ );
+ PRODUCT_NAME = crash_generation_server_test;
+ };
+ name = Debug;
+ };
+ D2F9A544121383A1002747C1 /* Debug With Code Coverage */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ HEADER_SEARCH_PATHS = ../..;
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\\\"$(SRCROOT)/build/Debug\\\"",
+ );
+ PRODUCT_NAME = handler_test;
+ };
+ name = "Debug With Code Coverage";
+ };
+ D2F9A545121383A1002747C1 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ HEADER_SEARCH_PATHS = ../..;
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\\\"$(SRCROOT)/build/Debug\\\"",
+ );
+ PRODUCT_NAME = handler_test;
+ };
+ name = Release;
+ };
F92C53560ECCE34A009BE4BA /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1921,6 +2099,8 @@
isa = XCBuildConfiguration;
buildSettings = {
DEBUG_INFORMATION_FORMAT = dwarf;
+ GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
HEADER_SEARCH_PATHS = (
../..,
../../testing,
@@ -2028,6 +2208,7 @@
"\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
);
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ HEADER_SEARCH_PATHS = ../..;
INFOPLIST_FILE = "UnitTests-Info.plist";
PRODUCT_NAME = UnitTests;
WRAPPER_EXTENSION = octest;
@@ -2155,6 +2336,16 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ D2F9A542121383A1002747C1 /* Build configuration list for PBXNativeTarget "crash_generation_server_test" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ D2F9A543121383A1002747C1 /* Debug */,
+ D2F9A544121383A1002747C1 /* Debug With Code Coverage */,
+ D2F9A545121383A1002747C1 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
F92C53580ECCE36D009BE4BA /* Build configuration list for PBXNativeTarget "Inspector" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/src/client/mac/crash_generation/client_info.h b/src/client/mac/crash_generation/client_info.h
new file mode 100644
index 00000000..035f5421
--- /dev/null
+++ b/src/client/mac/crash_generation/client_info.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
+#define CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
+
+namespace google_breakpad {
+
+struct ClientInfo {
+ int exception_type;
+ int exception_code;
+ int exception_subcode;
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
diff --git a/src/client/mac/crash_generation/crash_generation_client.cc b/src/client/mac/crash_generation/crash_generation_client.cc
new file mode 100644
index 00000000..0f21ef17
--- /dev/null
+++ b/src/client/mac/crash_generation/crash_generation_client.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "client/mac/crash_generation/crash_generation_client.h"
+
+#include "client/mac/crash_generation/client_info.h"
+#include "client/mac/crash_generation/crash_generation_server.h"
+#include "common/mac/MachIPC.h"
+
+namespace google_breakpad {
+
+bool CrashGenerationClient::RequestDumpForException(
+ int exception_type,
+ int exception_code,
+ int exception_subcode,
+ mach_port_t crashing_thread) {
+ // The server will send a message to this port indicating that it
+ // has finished its work.
+ ReceivePort acknowledge_port;
+
+ MachSendMessage message(kDumpRequestMessage);
+ message.AddDescriptor(mach_task_self()); // this task
+ message.AddDescriptor(crashing_thread); // crashing thread
+ message.AddDescriptor(mach_thread_self()); // handler thread
+ message.AddDescriptor(acknowledge_port.GetPort()); // message receive port
+
+ ClientInfo info;
+ info.exception_type = exception_type;
+ info.exception_code = exception_code;
+ info.exception_subcode = exception_subcode;
+ message.SetData(&info, sizeof(info));
+
+ const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
+ kern_return_t result = sender_.SendMessage(message, kSendTimeoutMs);
+ if (result != KERN_SUCCESS)
+ return false;
+
+ // Give the server slightly longer to reply since it has to
+ // inspect this task and write the minidump.
+ const mach_msg_timeout_t kReceiveTimeoutMs = 5 * 1000;
+ MachReceiveMessage acknowledge_message;
+ result = acknowledge_port.WaitForMessage(&acknowledge_message,
+ kReceiveTimeoutMs);
+
+ return result == KERN_SUCCESS;
+}
+
+} // namespace google_breakpad
diff --git a/src/client/mac/crash_generation/crash_generation_client.h b/src/client/mac/crash_generation/crash_generation_client.h
new file mode 100644
index 00000000..527f577a
--- /dev/null
+++ b/src/client/mac/crash_generation/crash_generation_client.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
+#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
+
+#include "common/mac/MachIPC.h"
+
+namespace google_breakpad {
+
+class CrashGenerationClient {
+ public:
+ explicit CrashGenerationClient(const char* mach_port_name)
+ : sender_(mach_port_name) {
+ }
+
+ // Request the crash server to generate a dump.
+ //
+ // Return true if the dump was successful; false otherwise.
+ bool RequestDumpForException(int exception_type,
+ int exception_code,
+ int exception_subcode,
+ mach_port_t crashing_thread);
+
+ bool RequestDump() {
+ return RequestDumpForException(0, 0, 0, MACH_PORT_NULL);
+ }
+
+ private:
+ MachPortSender sender_;
+
+ // Prevent copy construction and assignment.
+ CrashGenerationClient(const CrashGenerationClient&);
+ CrashGenerationClient& operator=(const CrashGenerationClient&);
+};
+
+} // namespace google_breakpad
+
+#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
diff --git a/src/client/mac/crash_generation/crash_generation_server.cc b/src/client/mac/crash_generation/crash_generation_server.cc
new file mode 100644
index 00000000..417dac6a
--- /dev/null
+++ b/src/client/mac/crash_generation/crash_generation_server.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "client/mac/crash_generation/crash_generation_server.h"
+
+#include "client/mac/crash_generation/client_info.h"
+#include "client/mac/handler/minidump_generator.h"
+#include "common/mac/scoped_task_suspend-inl.h"
+
+namespace google_breakpad {
+
+CrashGenerationServer::CrashGenerationServer(
+ const char *mach_port_name,
+ OnClientDumpRequestCallback dump_callback,
+ void *dump_context,
+ OnClientExitingCallback exit_callback,
+ void *exit_context,
+ bool generate_dumps,
+ const std::string &dump_path)
+ : dump_callback_(dump_callback),
+ dump_context_(dump_context),
+ exit_callback_(exit_callback),
+ exit_context_(exit_context),
+ generate_dumps_(generate_dumps),
+ dump_dir_(dump_path.empty() ? "/tmp" : dump_path),
+ started_(false),
+ receive_port_(mach_port_name),
+ mach_port_name_(mach_port_name) {
+}
+
+CrashGenerationServer::~CrashGenerationServer() {
+ if (started_)
+ Stop();
+}
+
+bool CrashGenerationServer::Start() {
+ int thread_create_result = pthread_create(&server_thread_, NULL,
+ &WaitForMessages, this);
+ started_ = thread_create_result == 0;
+ return started_;
+}
+
+bool CrashGenerationServer::Stop() {
+ if (!started_)
+ return false;
+
+ // Send a quit message to the background thread, and then join it.
+ MachPortSender sender(mach_port_name_.c_str());
+ MachSendMessage quit_message(kQuitMessage);
+ const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
+ kern_return_t result = sender.SendMessage(quit_message, kSendTimeoutMs);
+ if (result == KERN_SUCCESS) {
+ int thread_join_result = pthread_join(server_thread_, NULL);
+ started_ = thread_join_result != 0;
+ }
+
+ return !started_;
+}
+
+// static
+void *CrashGenerationServer::WaitForMessages(void *server) {
+ CrashGenerationServer *self =
+ reinterpret_cast<CrashGenerationServer*>(server);
+ while (self->WaitForOneMessage()) {}
+ return NULL;
+}
+
+bool CrashGenerationServer::WaitForOneMessage() {
+ MachReceiveMessage message;
+ kern_return_t result = receive_port_.WaitForMessage(&message,
+ MACH_MSG_TIMEOUT_NONE);
+ if (result == KERN_SUCCESS) {
+ switch (message.GetMessageID()) {
+ case kDumpRequestMessage: {
+ ClientInfo &info = (ClientInfo &)*message.GetData();
+
+ mach_port_t remote_task = message.GetTranslatedPort(0);
+ mach_port_t crashing_thread = message.GetTranslatedPort(1);
+ mach_port_t handler_thread = message.GetTranslatedPort(2);
+ mach_port_t ack_port = message.GetTranslatedPort(3);
+
+ bool result;
+ std::string dump_path;
+ if (generate_dumps_) {
+ ScopedTaskSuspend suspend(remote_task);
+
+ MinidumpGenerator generator(remote_task, handler_thread);
+ dump_path = generator.UniqueNameInDirectory(dump_dir_, NULL);
+
+ if (info.exception_type && info.exception_code) {
+ generator.SetExceptionInformation(info.exception_type,
+ info.exception_code,
+ info.exception_subcode,
+ crashing_thread);
+ }
+ result = generator.Write(dump_path.c_str());
+ } else {
+ result = true;
+ }
+
+ if (result && dump_callback_) {
+ dump_callback_(dump_context_, info, dump_path);
+ }
+
+ // TODO(ted): support a way for the client to send additional data,
+ // perhaps with a callback so users of the server can read the data
+ // themselves?
+
+ if (ack_port != MACH_PORT_DEAD && ack_port != MACH_PORT_NULL) {
+ MachPortSender sender(ack_port);
+ MachSendMessage ack_message(kAcknowledgementMessage);
+ const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
+
+ sender.SendMessage(ack_message, kSendTimeoutMs);
+ }
+
+ if (exit_callback_) {
+ exit_callback_(exit_context_, info);
+ }
+ break;
+ }
+ case kQuitMessage:
+ return false;
+ }
+ } else { // result != KERN_SUCCESS
+ mach_error("WaitForMessage", result);
+ return false;
+ }
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/src/client/mac/crash_generation/crash_generation_server.h b/src/client/mac/crash_generation/crash_generation_server.h
new file mode 100644
index 00000000..1f2695c4
--- /dev/null
+++ b/src/client/mac/crash_generation/crash_generation_server.h
@@ -0,0 +1,132 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
+#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
+
+#include <string>
+
+#include "common/mac/MachIPC.h"
+
+namespace google_breakpad {
+
+class ClientInfo;
+
+// Messages the server can read via its mach port
+enum {
+ kDumpRequestMessage = 1,
+ kAcknowledgementMessage = 2,
+ kQuitMessage = 3
+};
+
+class CrashGenerationServer {
+ public:
+ // WARNING: callbacks may be invoked on a different thread
+ // than that which creates the CrashGenerationServer. They must
+ // be thread safe.
+ typedef void (*OnClientDumpRequestCallback)(void *context,
+ const ClientInfo &client_info,
+ const std::string &file_path);
+
+ typedef void (*OnClientExitingCallback)(void *context,
+ const ClientInfo &client_info);
+
+ // Create an instance with the given parameters.
+ //
+ // mach_port_name: Named server port to listen on.
+ // dump_callback: Callback for a client crash dump request.
+ // dump_context: Context for client crash dump request callback.
+ // exit_callback: Callback for client process exit.
+ // exit_context: Context for client exit callback.
+ // generate_dumps: Whether to automatically generate dumps.
+ // Client code of this class might want to generate dumps explicitly
+ // in the crash dump request callback. In that case, false can be
+ // passed for this parameter.
+ // dump_path: Path for generating dumps; required only if true is
+ // passed for generateDumps parameter; NULL can be passed otherwise.
+ CrashGenerationServer(const char *mach_port_name,
+ OnClientDumpRequestCallback dump_callback,
+ void *dump_context,
+ OnClientExitingCallback exit_callback,
+ void *exit_context,
+ bool generate_dumps,
+ const std::string &dump_path);
+
+ ~CrashGenerationServer();
+
+ // Perform initialization steps needed to start listening to clients.
+ //
+ // Return true if initialization is successful; false otherwise.
+ bool Start();
+
+ // Stop the server.
+ bool Stop();
+
+ private:
+ // Return a unique filename at which a minidump can be written.
+ bool MakeMinidumpFilename(std::string &outFilename);
+
+ // Loop reading client messages and responding to them until
+ // a quit message is received.
+ static void *WaitForMessages(void *server);
+
+ // Wait for a single client message and respond to it. Returns false
+ // if a quit message was received or if an error occurred.
+ bool WaitForOneMessage();
+
+ OnClientDumpRequestCallback dump_callback_;
+ void *dump_context_;
+
+ OnClientExitingCallback exit_callback_;
+ void *exit_context_;
+
+ bool generate_dumps_;
+
+ std::string dump_dir_;
+
+ bool started_;
+
+ // The mach port that receives requests to dump from child processes.
+ ReceivePort receive_port_;
+
+ // The name of the mach port. Stored so the Stop method can message
+ // the background thread to shut it down.
+ std::string mach_port_name_;
+
+ // The thread that waits on the receive port.
+ pthread_t server_thread_;
+
+ // Disable copy constructor and operator=.
+ CrashGenerationServer(const CrashGenerationServer&);
+ CrashGenerationServer& operator=(const CrashGenerationServer&);
+};
+
+} // namespace google_breakpad
+
+#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
diff --git a/src/client/mac/handler/exception_handler.cc b/src/client/mac/handler/exception_handler.cc
index 80535318..ccf1ff99 100644
--- a/src/client/mac/handler/exception_handler.cc
+++ b/src/client/mac/handler/exception_handler.cc
@@ -221,7 +221,8 @@ ExceptionHandler::ExceptionHandler(const string &dump_path,
FilterCallback filter,
MinidumpCallback callback,
void *callback_context,
- bool install_handler)
+ bool install_handler,
+ const char *port_name)
: dump_path_(),
filter_(filter),
callback_(callback),
@@ -237,6 +238,8 @@ ExceptionHandler::ExceptionHandler(const string &dump_path,
// This will update to the ID and C-string pointers
set_dump_path(dump_path);
MinidumpGenerator::GatherSystemInformation();
+ if (port_name)
+ crash_generation_client_.reset(new CrashGenerationClient(port_name));
Setup(install_handler);
}
@@ -293,7 +296,8 @@ bool ExceptionHandler::WriteMinidump() {
bool ExceptionHandler::WriteMinidump(const string &dump_path,
MinidumpCallback callback,
void *callback_context) {
- ExceptionHandler handler(dump_path, NULL, callback, callback_context, false);
+ ExceptionHandler handler(dump_path, NULL, callback, callback_context, false,
+ NULL);
return handler.WriteMinidump();
}
@@ -312,6 +316,18 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
if (exception_type && exception_code)
_exit(exception_type);
}
+ } else if (IsOutOfProcess()) {
+ if (exception_type && exception_code) {
+ // If this is a real exception, give the filter (if any) a chance to
+ // decide if this should be sent.
+ if (filter_ && !filter_(callback_context_))
+ return false;
+ return crash_generation_client_->RequestDumpForException(
+ exception_type,
+ exception_code,
+ exception_subcode,
+ thread_name);
+ }
} else {
string minidump_id;
@@ -321,7 +337,7 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
MinidumpGenerator md;
if (exception_type && exception_code) {
// If this is a real exception, give the filter (if any) a chance to
- // decided if this should be sent
+ // decide if this should be sent.
if (filter_ && !filter_(callback_context_))
return false;
diff --git a/src/client/mac/handler/exception_handler.h b/src/client/mac/handler/exception_handler.h
index 896eecda..ee3e2e1d 100644
--- a/src/client/mac/handler/exception_handler.h
+++ b/src/client/mac/handler/exception_handler.h
@@ -40,6 +40,9 @@
#include <string>
+#include "client/mac/crash_generation/crash_generation_client.h"
+#include "processor/scoped_ptr.h"
+
namespace google_breakpad {
using std::string;
@@ -86,9 +89,12 @@ class ExceptionHandler {
// If install_handler is true, then a minidump will be written whenever
// an unhandled exception occurs. If it is false, minidumps will only
// be written when WriteMinidump is called.
+ // If port_name is non-NULL, attempt to perform out-of-process dump generation
+ // If port_name is NULL, in-process dump generation will be used.
ExceptionHandler(const string &dump_path,
FilterCallback filter, MinidumpCallback callback,
- void *callback_context, bool install_handler);
+ void *callback_context, bool install_handler,
+ const char *port_name);
// A special constructor if we want to bypass minidump writing and
// simply get a callback with the exception information.
@@ -115,6 +121,11 @@ class ExceptionHandler {
static bool WriteMinidump(const string &dump_path, MinidumpCallback callback,
void *callback_context);
+ // Returns whether out-of-process dump generation is used or not.
+ bool IsOutOfProcess() const {
+ return crash_generation_client_.get() != NULL;
+ }
+
private:
// Install the mach exception handler
bool InstallHandler();
@@ -206,6 +217,9 @@ class ExceptionHandler {
// True, if we're using the mutext to indicate when mindump writing occurs
bool use_minidump_write_mutex_;
+
+ // Client for out-of-process dump generation.
+ scoped_ptr<CrashGenerationClient> crash_generation_client_;
};
} // namespace google_breakpad
diff --git a/src/client/mac/tests/crash_generation_server_test.cc b/src/client/mac/tests/crash_generation_server_test.cc
new file mode 100644
index 00000000..6f225337
--- /dev/null
+++ b/src/client/mac/tests/crash_generation_server_test.cc
@@ -0,0 +1,217 @@
+// Copyright (c) 2010, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// crash_generation_server_test.cc
+// Unit tests for CrashGenerationServer
+
+#include <dirent.h>
+#include <glob.h>
+#include <stdint.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "client/mac/crash_generation/client_info.h"
+#include "client/mac/crash_generation/crash_generation_client.h"
+#include "client/mac/crash_generation/crash_generation_server.h"
+#include "client/mac/handler/exception_handler.h"
+#include "client/mac/tests/auto_tempdir.h"
+
+namespace {
+using std::string;
+using google_breakpad::AutoTempDir;
+using google_breakpad::ClientInfo;
+using google_breakpad::CrashGenerationClient;
+using google_breakpad::CrashGenerationServer;
+using google_breakpad::ExceptionHandler;
+using testing::Test;
+
+class CrashGenerationServerTest : public Test {
+public:
+ // The port name to receive messages on
+ char mach_port_name[128];
+ // Filename of the last dump that was generated
+ string last_dump_name;
+ // A temp dir
+ AutoTempDir temp_dir;
+ // Counter just to ensure that we don't hit the same port again
+ static int i;
+
+ void SetUp() {
+ sprintf(mach_port_name,
+ "com.google.breakpad.ServerTest.%d.%d", getpid(),
+ CrashGenerationServerTest::i++);
+ }
+};
+int CrashGenerationServerTest::i = 0;
+
+// Test that starting and stopping a server works
+TEST_F(CrashGenerationServerTest, testStartStopServer) {
+ CrashGenerationServer server(mach_port_name,
+ NULL, // dump callback
+ NULL, // dump context
+ NULL, // exit callback
+ NULL, // exit context
+ false, // generate dumps
+ ""); // dump path
+ ASSERT_TRUE(server.Start());
+ ASSERT_TRUE(server.Stop());
+}
+
+// Test that requesting a dump via CrashGenerationClient works
+// Test without actually dumping
+TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) {
+ CrashGenerationServer server(mach_port_name,
+ NULL, // dump callback
+ NULL, // dump context
+ NULL, // exit callback
+ NULL, // exit context
+ false, // don't generate dumps
+ temp_dir.path); // dump path
+ ASSERT_TRUE(server.Start());
+
+ pid_t pid = fork();
+ ASSERT_NE(-1, pid);
+ if (pid == 0) {
+ CrashGenerationClient client(mach_port_name);
+ bool result = client.RequestDump();
+ exit(result ? 0 : 1);
+ }
+
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_TRUE(WIFEXITED(ret));
+ EXPECT_EQ(0, WEXITSTATUS(ret));
+ EXPECT_TRUE(server.Stop());
+ // check that no minidump was written
+ string pattern = temp_dir.path + "/*";
+ glob_t dirContents;
+ ret = glob(pattern.c_str(), GLOB_NOSORT, NULL, &dirContents);
+ EXPECT_EQ(GLOB_NOMATCH, ret);
+ if (ret != GLOB_NOMATCH)
+ globfree(&dirContents);
+}
+
+void dumpCallback(void *context, const ClientInfo &client_info,
+ const std::string &file_path) {
+ if (context) {
+ CrashGenerationServerTest* self =
+ reinterpret_cast<CrashGenerationServerTest*>(context);
+ if (!file_path.empty())
+ self->last_dump_name = file_path;
+ }
+}
+
+void *RequestDump(void *context) {
+ CrashGenerationClient client((const char*)context);
+ bool result = client.RequestDump();
+ return (void*)(result ? 0 : 1);
+}
+
+// Test that actually writing a minidump works
+TEST_F(CrashGenerationServerTest, testRequestDump) {
+ CrashGenerationServer server(mach_port_name,
+ dumpCallback, // dump callback
+ this, // dump context
+ NULL, // exit callback
+ NULL, // exit context
+ true, // generate dumps
+ temp_dir.path); // dump path
+ ASSERT_TRUE(server.Start());
+
+ pid_t pid = fork();
+ ASSERT_NE(-1, pid);
+ if (pid == 0) {
+ // Have to spawn off a separate thread to request the dump,
+ // because MinidumpGenerator assumes the handler thread is not
+ // the only thread
+ pthread_t thread;
+ if (pthread_create(&thread, NULL, RequestDump, (void*)mach_port_name) != 0)
+ exit(1);
+ void* result;
+ pthread_join(thread, &result);
+ exit(reinterpret_cast<intptr_t>(result));
+ }
+
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_TRUE(WIFEXITED(ret));
+ EXPECT_EQ(0, WEXITSTATUS(ret));
+ EXPECT_TRUE(server.Stop());
+ // check that minidump was written
+ ASSERT_FALSE(last_dump_name.empty());
+ struct stat st;
+ EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
+ EXPECT_LT(0, st.st_size);
+}
+
+static void Crasher() {
+ int *a = (int*)0x42;
+
+ fprintf(stdout, "Going to crash...\n");
+ fprintf(stdout, "A = %d", *a);
+}
+
+// Test that crashing a child process with an OOP ExceptionHandler installed
+// results in a minidump being written by the CrashGenerationServer in
+// the parent.
+TEST_F(CrashGenerationServerTest, testChildProcessCrash) {
+ CrashGenerationServer server(mach_port_name,
+ dumpCallback, // dump callback
+ this, // dump context
+ NULL, // exit callback
+ NULL, // exit context
+ true, // generate dumps
+ temp_dir.path); // dump path
+ ASSERT_TRUE(server.Start());
+
+ pid_t pid = fork();
+ ASSERT_NE(-1, pid);
+ if (pid == 0) {
+ // Instantiate an OOP exception handler.
+ ExceptionHandler eh("", NULL, NULL, NULL, true, mach_port_name);
+ Crasher();
+ // not reached
+ exit(0);
+ }
+
+ int ret;
+ ASSERT_EQ(pid, waitpid(pid, &ret, 0));
+ EXPECT_FALSE(WIFEXITED(ret));
+ EXPECT_TRUE(server.Stop());
+ // check that minidump was written
+ ASSERT_FALSE(last_dump_name.empty());
+ struct stat st;
+ EXPECT_EQ(0, stat(last_dump_name.c_str(), &st));
+ EXPECT_LT(0, st.st_size);
+}
+
+} // namespace
diff --git a/src/client/mac/tests/exception_handler_test.cc b/src/client/mac/tests/exception_handler_test.cc
index 4a63df36..6ba244dd 100644
--- a/src/client/mac/tests/exception_handler_test.cc
+++ b/src/client/mac/tests/exception_handler_test.cc
@@ -36,6 +36,7 @@
#include "client/mac/handler/exception_handler.h"
#include "client/mac/tests/auto_tempdir.h"
+namespace {
using std::string;
using google_breakpad::AutoTempDir;
using google_breakpad::ExceptionHandler;
@@ -76,7 +77,7 @@ TEST(ExceptionHandler, InProcess) {
pid_t pid = fork();
if (pid == 0) {
close(fds[0]);
- ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true);
+ ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
// crash
SoonToCrash();
// not reached
@@ -99,3 +100,5 @@ TEST(ExceptionHandler, InProcess) {
EXPECT_NE(0, WIFEXITED(ret));
EXPECT_EQ(0, WEXITSTATUS(ret));
}
+
+}
diff --git a/src/common/mac/MachIPC.h b/src/common/mac/MachIPC.h
index 40ef8541..4df85fcf 100644
--- a/src/common/mac/MachIPC.h
+++ b/src/common/mac/MachIPC.h
@@ -255,11 +255,11 @@ class MachSendMessage : public MachMessage {
class ReceivePort {
public:
// Creates a new mach port for receiving messages and registers a name for it
- ReceivePort(const char *receive_port_name);
+ explicit 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);
+ explicit ReceivePort(mach_port_t receive_port);
// Create a new mach port for receiving messages
ReceivePort();
@@ -285,11 +285,11 @@ class ReceivePort {
class MachPortSender {
public:
// get a port with send rights corresponding to a named registered service
- MachPortSender(const char *receive_port_name);
+ explicit MachPortSender(const char *receive_port_name);
// Given an already existing mach port, use it.
- MachPortSender(mach_port_t send_port);
+ explicit MachPortSender(mach_port_t send_port);
kern_return_t SendMessage(MachSendMessage &message,
mach_msg_timeout_t timeout);
diff --git a/src/common/mac/MachIPC.mm b/src/common/mac/MachIPC.mm
index c451edb7..35952e7e 100644
--- a/src/common/mac/MachIPC.mm
+++ b/src/common/mac/MachIPC.mm
@@ -240,8 +240,11 @@ kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
out_message->head.msgh_reserved = 0;
out_message->head.msgh_id = 0;
+ mach_msg_option_t options = MACH_RCV_MSG;
+ if (timeout != MACH_MSG_TIMEOUT_NONE)
+ options |= MACH_RCV_TIMEOUT;
kern_return_t result = mach_msg(&out_message->head,
- MACH_RCV_MSG | MACH_RCV_TIMEOUT,
+ options,
0,
sizeof(MachMessage),
port_,
diff --git a/src/common/mac/scoped_task_suspend-inl.h b/src/common/mac/scoped_task_suspend-inl.h
new file mode 100644
index 00000000..d6d1bef9
--- /dev/null
+++ b/src/common/mac/scoped_task_suspend-inl.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2010 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Inline implementation of ScopedTaskSuspend, which suspends a Mach
+// task for the duration of its scope.
+
+#ifndef GOOGLE_BREAKPAD_COMMON_MAC_SCOPED_TASK_SUSPEND_H_
+#define GOOGLE_BREAKPAD_COMMON_MAC_SCOPED_TASK_SUSPEND_H_
+
+#include <mach/mach.h>
+
+namespace google_breakpad {
+
+class ScopedTaskSuspend {
+ public:
+ explicit ScopedTaskSuspend(mach_port_t target) : target_(target) {
+ task_suspend(target_);
+ }
+
+ ~ScopedTaskSuspend() {
+ task_resume(target_);
+ }
+
+ private:
+ mach_port_t target_;
+};
+
+} // namespace google_breakpad
+
+#endif // GOOGLE_BREAKPAD_COMMON_MAC_SCOPED_TASK_SUSPEND_H_