diff options
-rw-r--r-- | src/client/mac/Breakpad.xcodeproj/project.pbxproj | 195 | ||||
-rw-r--r-- | src/client/mac/crash_generation/client_info.h | 43 | ||||
-rw-r--r-- | src/client/mac/crash_generation/crash_generation_client.cc | 74 | ||||
-rw-r--r-- | src/client/mac/crash_generation/crash_generation_client.h | 65 | ||||
-rw-r--r-- | src/client/mac/crash_generation/crash_generation_server.cc | 158 | ||||
-rw-r--r-- | src/client/mac/crash_generation/crash_generation_server.h | 132 | ||||
-rw-r--r-- | src/client/mac/handler/exception_handler.cc | 22 | ||||
-rw-r--r-- | src/client/mac/handler/exception_handler.h | 16 | ||||
-rw-r--r-- | src/client/mac/tests/crash_generation_server_test.cc | 217 | ||||
-rw-r--r-- | src/client/mac/tests/exception_handler_test.cc | 5 | ||||
-rw-r--r-- | src/common/mac/MachIPC.h | 8 | ||||
-rw-r--r-- | src/common/mac/MachIPC.mm | 5 | ||||
-rw-r--r-- | src/common/mac/scoped_task_suspend-inl.h | 56 |
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_ |