mirror of
https://github.com/chromium/crashpad.git
synced 2025-01-14 01:08:01 +08:00
ios: Update XCUITests to check various crash type exception codes.
Bug: crashpad:31 Change-Id: I804def3be0050b3e6f15d7d77d0b70184c380673 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3087722 Commit-Queue: Justin Cohen <justincohen@chromium.org> Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
15c4663b8c
commit
fad3bdcde0
@ -244,10 +244,10 @@ crashpad_static_library("snapshot") {
|
||||
public_deps = [ ":context" ]
|
||||
|
||||
deps = [
|
||||
"$mini_chromium_source_parent:base",
|
||||
"../client:common",
|
||||
"../compat",
|
||||
"../minidump:format",
|
||||
"$mini_chromium_source_parent:base",
|
||||
"../util",
|
||||
]
|
||||
|
||||
@ -318,8 +318,8 @@ static_library("test_support") {
|
||||
public_deps = [ ":snapshot" ]
|
||||
|
||||
deps = [
|
||||
"../compat",
|
||||
"$mini_chromium_source_parent:base",
|
||||
"../compat",
|
||||
"../util",
|
||||
]
|
||||
|
||||
@ -429,13 +429,13 @@ source_set("snapshot_test") {
|
||||
|
||||
deps = [
|
||||
":test_support",
|
||||
"$mini_chromium_source_parent:base",
|
||||
"../client:common",
|
||||
"../compat",
|
||||
"../minidump:format",
|
||||
"../test",
|
||||
"../third_party/googletest:googlemock",
|
||||
"../third_party/googletest:googletest",
|
||||
"$mini_chromium_source_parent:base",
|
||||
"../util",
|
||||
]
|
||||
|
||||
@ -489,8 +489,8 @@ crashpad_loadable_module("crashpad_snapshot_test_module") {
|
||||
testonly = true
|
||||
sources = [ "crashpad_info_client_options_test_module.cc" ]
|
||||
deps = [
|
||||
"../client",
|
||||
"$mini_chromium_source_parent:base",
|
||||
"../client",
|
||||
]
|
||||
}
|
||||
|
||||
@ -533,7 +533,7 @@ if ((crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) &&
|
||||
}
|
||||
}
|
||||
|
||||
if (crashpad_is_mac) {
|
||||
if (crashpad_is_mac || crashpad_is_ios) {
|
||||
crashpad_loadable_module("crashpad_snapshot_test_module_crashy_initializer") {
|
||||
testonly = true
|
||||
sources = [
|
||||
@ -552,9 +552,9 @@ if (crashpad_is_win) {
|
||||
testonly = true
|
||||
sources = [ "win/crashpad_snapshot_test_annotations.cc" ]
|
||||
deps = [
|
||||
"$mini_chromium_source_parent:base",
|
||||
"../client",
|
||||
"../compat",
|
||||
"$mini_chromium_source_parent:base",
|
||||
]
|
||||
}
|
||||
|
||||
@ -562,9 +562,9 @@ if (crashpad_is_win) {
|
||||
testonly = true
|
||||
sources = [ "win/crashpad_snapshot_test_crashing_child.cc" ]
|
||||
deps = [
|
||||
"$mini_chromium_source_parent:base",
|
||||
"../client",
|
||||
"../compat",
|
||||
"$mini_chromium_source_parent:base",
|
||||
"../util",
|
||||
]
|
||||
}
|
||||
@ -573,9 +573,9 @@ if (crashpad_is_win) {
|
||||
testonly = true
|
||||
sources = [ "win/crashpad_snapshot_test_dump_without_crashing.cc" ]
|
||||
deps = [
|
||||
"$mini_chromium_source_parent:base",
|
||||
"../client",
|
||||
"../compat",
|
||||
"$mini_chromium_source_parent:base",
|
||||
"../util",
|
||||
]
|
||||
}
|
||||
@ -584,9 +584,9 @@ if (crashpad_is_win) {
|
||||
testonly = true
|
||||
sources = [ "win/crashpad_snapshot_test_extra_memory_ranges.cc" ]
|
||||
deps = [
|
||||
"$mini_chromium_source_parent:base",
|
||||
"../client",
|
||||
"../compat",
|
||||
"$mini_chromium_source_parent:base",
|
||||
]
|
||||
}
|
||||
|
||||
@ -594,9 +594,9 @@ if (crashpad_is_win) {
|
||||
testonly = true
|
||||
sources = [ "win/crashpad_snapshot_test_image_reader.cc" ]
|
||||
deps = [
|
||||
"$mini_chromium_source_parent:base",
|
||||
"../client",
|
||||
"../compat",
|
||||
"$mini_chromium_source_parent:base",
|
||||
"../util",
|
||||
]
|
||||
if (crashpad_is_in_chromium) {
|
||||
@ -613,8 +613,8 @@ if (crashpad_is_win) {
|
||||
testonly = true
|
||||
sources = [ "win/crashpad_snapshot_test_image_reader_module.cc" ]
|
||||
deps = [
|
||||
"../client",
|
||||
"$mini_chromium_source_parent:base",
|
||||
"../client",
|
||||
]
|
||||
if (crashpad_is_in_chromium) {
|
||||
if (symbol_level == 0) {
|
||||
|
@ -72,6 +72,7 @@ source_set("xcuitests") {
|
||||
"../../build:ios_xctest",
|
||||
"../../test/ios/host:app_shared_sources",
|
||||
"../../third_party/edo",
|
||||
"../../util",
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -13,38 +13,34 @@
|
||||
// limitations under the License.
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#include <objc/runtime.h>
|
||||
|
||||
#import "Service/Sources/EDOClientService.h"
|
||||
#import "test/ios/host/cptest_shared_object.h"
|
||||
#include "util/mach/exception_types.h"
|
||||
#include "util/mach/mach_extensions.h"
|
||||
|
||||
#if !defined(__has_feature) || !__has_feature(objc_arc)
|
||||
#error "This file requires ARC support."
|
||||
#endif
|
||||
|
||||
@interface CPTestTestCase : XCTestCase {
|
||||
XCUIApplication* _app;
|
||||
XCUIApplication* app_;
|
||||
CPTestSharedObject* rootObject_;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPTestTestCase
|
||||
|
||||
- (void)handleCrashUnderSymbol:(id)arg1 {
|
||||
// For now, do nothing. In the future this can be something testable.
|
||||
}
|
||||
|
||||
+ (void)setUp {
|
||||
// Swizzle away the handleCrashUnderSymbol callback. Without this, any time
|
||||
// the host app is intentionally crashed, the test is immediately failed.
|
||||
SEL originalSelector = NSSelectorFromString(@"handleCrashUnderSymbol:");
|
||||
SEL swizzledSelector = @selector(handleCrashUnderSymbol:);
|
||||
|
||||
Method originalMethod = class_getInstanceMethod(
|
||||
objc_getClass("XCUIApplicationImpl"), originalSelector);
|
||||
Method swizzledMethod =
|
||||
class_getInstanceMethod([self class], swizzledSelector);
|
||||
|
||||
method_exchangeImplementations(originalMethod, swizzledMethod);
|
||||
|
||||
// Override EDO default error handler. Without this, the default EDO error
|
||||
@ -54,188 +50,186 @@
|
||||
});
|
||||
}
|
||||
|
||||
// This gets called after tearDown, so there's no straightforward way to
|
||||
// test that this is called. However, not swizzling this out will cause every
|
||||
// crashing test to fail.
|
||||
- (void)handleCrashUnderSymbol:(id)arg1 {
|
||||
}
|
||||
|
||||
- (void)setUp {
|
||||
_app = [[XCUIApplication alloc] init];
|
||||
[_app launch];
|
||||
app_ = [[XCUIApplication alloc] init];
|
||||
[app_ launch];
|
||||
rootObject_ = [EDOClientService rootObjectWithPort:12345];
|
||||
[rootObject_ clearPendingReports];
|
||||
XCTAssertEqual([rootObject_ pendingReportCount], 0);
|
||||
XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);
|
||||
}
|
||||
|
||||
- (void)verifyCrashReportException:(int)exception {
|
||||
// Confirm the app is not running.
|
||||
XCTAssertTrue([app_ waitForState:XCUIApplicationStateNotRunning timeout:15]);
|
||||
XCTAssertTrue(app_.state == XCUIApplicationStateNotRunning);
|
||||
|
||||
// Restart app to get the report signal.
|
||||
[app_ launch];
|
||||
XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);
|
||||
rootObject_ = [EDOClientService rootObjectWithPort:12345];
|
||||
XCTAssertEqual([rootObject_ pendingReportCount], 1);
|
||||
XCTAssertEqual([rootObject_ pendingReportException], exception);
|
||||
}
|
||||
|
||||
- (void)testEDO {
|
||||
CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
|
||||
NSString* result = [rootObject testEDO];
|
||||
NSString* result = [rootObject_ testEDO];
|
||||
XCTAssertEqualObjects(result, @"crashpad");
|
||||
}
|
||||
|
||||
- (void)testSegv {
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
|
||||
// Crash the app.
|
||||
CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
|
||||
[rootObject crashSegv];
|
||||
|
||||
// Confirm the app is not running.
|
||||
XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
|
||||
|
||||
// TODO: Query the app for crash data
|
||||
[_app launch];
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
[rootObject_ crashSegv];
|
||||
#if defined(NDEBUG)
|
||||
#if TARGET_OS_SIMULATOR
|
||||
[self verifyCrashReportException:SIGINT];
|
||||
#else
|
||||
[self verifyCrashReportException:SIGABRT];
|
||||
#endif
|
||||
#else
|
||||
[self verifyCrashReportException:SIGHUP];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)testKillAbort {
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
|
||||
// Crash the app.
|
||||
CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
|
||||
[rootObject crashKillAbort];
|
||||
|
||||
// Confirm the app is not running.
|
||||
XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
|
||||
|
||||
// TODO: Query the app for crash data
|
||||
[_app launch];
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
[rootObject_ crashKillAbort];
|
||||
[self verifyCrashReportException:SIGABRT];
|
||||
}
|
||||
|
||||
- (void)testTrap {
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
|
||||
// Crash the app.
|
||||
CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
|
||||
[rootObject crashTrap];
|
||||
|
||||
// Confirm the app is not running.
|
||||
XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
|
||||
|
||||
// TODO: Query the app for crash data
|
||||
[_app launch];
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
[rootObject_ crashTrap];
|
||||
#if TARGET_OS_SIMULATOR
|
||||
[self verifyCrashReportException:SIGINT];
|
||||
#else
|
||||
[self verifyCrashReportException:SIGABRT];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)testAbort {
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
|
||||
// Crash the app.
|
||||
CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
|
||||
[rootObject crashAbort];
|
||||
|
||||
// Confirm the app is not running.
|
||||
XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
|
||||
|
||||
// TODO: Query the app for crash data
|
||||
[_app launch];
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
[rootObject_ crashAbort];
|
||||
[self verifyCrashReportException:SIGABRT];
|
||||
}
|
||||
|
||||
- (void)testBadAccess {
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
|
||||
// Crash the app.
|
||||
CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
|
||||
[rootObject crashBadAccess];
|
||||
|
||||
// Confirm the app is not running.
|
||||
XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
|
||||
|
||||
// TODO: Query the app for crash data
|
||||
[_app launch];
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
[rootObject_ crashBadAccess];
|
||||
#if defined(NDEBUG)
|
||||
#if TARGET_OS_SIMULATOR
|
||||
[self verifyCrashReportException:SIGINT];
|
||||
#else
|
||||
[self verifyCrashReportException:SIGABRT];
|
||||
#endif
|
||||
#else
|
||||
[self verifyCrashReportException:SIGHUP];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)testException {
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
|
||||
// Crash the app.
|
||||
CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
|
||||
[rootObject crashException];
|
||||
|
||||
// Confirm the app is not running.
|
||||
XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
|
||||
|
||||
// TODO: Query the app for crash data
|
||||
[_app launch];
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
[rootObject_ crashException];
|
||||
[self verifyCrashReportException:SIGABRT];
|
||||
}
|
||||
|
||||
- (void)testNSException {
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
|
||||
// Crash the app.
|
||||
CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
|
||||
[rootObject crashNSException];
|
||||
|
||||
// Confirm the app is not running.
|
||||
XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
|
||||
|
||||
// TODO: Query the app for crash data
|
||||
[_app launch];
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
[rootObject_ crashNSException];
|
||||
[self verifyCrashReportException:crashpad::kMachExceptionFromNSException];
|
||||
NSDictionary* dict = [rootObject_ getAnnotations];
|
||||
NSString* userInfo =
|
||||
[dict[@"objects"][0] valueForKeyPath:@"exceptionUserInfo"];
|
||||
XCTAssertTrue([userInfo containsString:@"Error Object=<CPTestSharedObject"]);
|
||||
XCTAssertTrue([[dict[@"objects"][1] valueForKeyPath:@"exceptionReason"]
|
||||
isEqualToString:@"Intentionally throwing error."]);
|
||||
XCTAssertTrue([[dict[@"objects"][2] valueForKeyPath:@"exceptionName"]
|
||||
isEqualToString:@"NSInternalInconsistencyException"]);
|
||||
}
|
||||
|
||||
- (void)testCrashUnreocgnizedSelectorAfterDelay {
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
|
||||
// Crash the app.
|
||||
CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
|
||||
[rootObject crashUnreocgnizedSelectorAfterDelay];
|
||||
|
||||
// Confirm the app is not running.
|
||||
XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
|
||||
|
||||
// TODO: Query the app for crash data
|
||||
[_app launch];
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
- (void)testcrashUnrecognizedSelectorAfterDelay {
|
||||
[rootObject_ crashUnrecognizedSelectorAfterDelay];
|
||||
[self verifyCrashReportException:crashpad::kMachExceptionFromNSException];
|
||||
NSDictionary* dict = [rootObject_ getAnnotations];
|
||||
XCTAssertTrue([[dict[@"objects"][0] valueForKeyPath:@"exceptionReason"]
|
||||
containsString:
|
||||
@"CPTestSharedObject does_not_exist]: unrecognized selector"]);
|
||||
XCTAssertTrue([[dict[@"objects"][1] valueForKeyPath:@"exceptionName"]
|
||||
isEqualToString:@"NSInvalidArgumentException"]);
|
||||
}
|
||||
|
||||
- (void)testCatchUIGestureEnvironmentNSException {
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
|
||||
// Tap the button with the string UIGestureEnvironmentException.
|
||||
[_app.buttons[@"UIGestureEnvironmentException"] tap];
|
||||
|
||||
// Confirm the app is not running.
|
||||
XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
|
||||
|
||||
// TODO: Query the app for crash data
|
||||
[_app launch];
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
[app_.buttons[@"UIGestureEnvironmentException"] tap];
|
||||
[self verifyCrashReportException:crashpad::kMachExceptionFromNSException];
|
||||
NSDictionary* dict = [rootObject_ getAnnotations];
|
||||
XCTAssertTrue([[dict[@"objects"][0] valueForKeyPath:@"exceptionReason"]
|
||||
containsString:@"NSArray0 objectAtIndex:]: index 42 beyond bounds"]);
|
||||
XCTAssertTrue([[dict[@"objects"][1] valueForKeyPath:@"exceptionName"]
|
||||
isEqualToString:@"NSRangeException"]);
|
||||
}
|
||||
|
||||
- (void)testCatchNSException {
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
[rootObject_ catchNSException];
|
||||
|
||||
// The app should not crash
|
||||
CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
|
||||
[rootObject catchNSException];
|
||||
XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);
|
||||
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
// No report should be generated.
|
||||
[rootObject_ processIntermediateDumps];
|
||||
XCTAssertEqual([rootObject_ pendingReportCount], 0);
|
||||
}
|
||||
|
||||
- (void)testRecursion {
|
||||
// TODO(justincohen): Crashpad iOS does not currently support stack type
|
||||
// crashes.
|
||||
return;
|
||||
[rootObject_ crashRecursion];
|
||||
[self verifyCrashReportException:SIGHUP];
|
||||
}
|
||||
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
- (void)testCrashWithCrashInfoMessage {
|
||||
if (@available(iOS 15.0, *)) {
|
||||
// Figure out how to test this on iOS15.
|
||||
return;
|
||||
}
|
||||
[rootObject_ crashWithCrashInfoMessage];
|
||||
[self verifyCrashReportException:SIGHUP];
|
||||
NSDictionary* dict = [rootObject_ getAnnotations];
|
||||
NSString* dyldMessage = dict[@"vector"][0];
|
||||
XCTAssertTrue([dyldMessage isEqualToString:@"dyld: in dlsym()"]);
|
||||
}
|
||||
|
||||
// Crash the app.
|
||||
CPTestSharedObject* rootObject = [EDOClientService rootObjectWithPort:12345];
|
||||
[rootObject crashRecursion];
|
||||
// TODO(justincohen): Codesign crashy_initializer.so so it can run on devices.
|
||||
#if !TARGET_OS_SIMULATOR
|
||||
- (void)testCrashWithDyldErrorString {
|
||||
if (@available(iOS 15.0, *)) {
|
||||
// iOS 15 uses dyld4, which doesn't use CRSetCrashLogMessage2
|
||||
return;
|
||||
}
|
||||
[rootObject_ crashWithDyldErrorString];
|
||||
[self verifyCrashReportException:SIGINT];
|
||||
NSArray* vector = [rootObject_ getAnnotations][@"vector"];
|
||||
// This message is set by dyld-353.2.1/src/ImageLoaderMachO.cpp
|
||||
// ImageLoaderMachO::doInitialization().
|
||||
NSString* module = @"crashpad_snapshot_test_module_crashy_initializer.so";
|
||||
XCTAssertTrue([vector[0] hasSuffix:module]);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Confirm the app is not running.
|
||||
XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
|
||||
- (void)testCrashWithAnnotations {
|
||||
[rootObject_ crashWithAnnotations];
|
||||
[self verifyCrashReportException:SIGABRT];
|
||||
NSDictionary* dict = [rootObject_ getAnnotations];
|
||||
NSDictionary* simpleMap = dict[@"simplemap"];
|
||||
XCTAssertTrue([simpleMap[@"#TEST# empty_value"] isEqualToString:@""]);
|
||||
XCTAssertTrue([simpleMap[@"#TEST# key"] isEqualToString:@"value"]);
|
||||
XCTAssertTrue([simpleMap[@"#TEST# longer"] isEqualToString:@"shorter"]);
|
||||
XCTAssertTrue([simpleMap[@"#TEST# pad"] isEqualToString:@"crash"]);
|
||||
XCTAssertTrue([simpleMap[@"#TEST# x"] isEqualToString:@"y"]);
|
||||
|
||||
// TODO: Query the app for crash data
|
||||
[_app launch];
|
||||
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
|
||||
XCTAssertTrue([[dict[@"objects"][0] valueForKeyPath:@"#TEST# same-name"]
|
||||
isEqualToString:@"same-name 4"]);
|
||||
XCTAssertTrue([[dict[@"objects"][1] valueForKeyPath:@"#TEST# same-name"]
|
||||
isEqualToString:@"same-name 3"]);
|
||||
XCTAssertTrue([[dict[@"objects"][2] valueForKeyPath:@"#TEST# one"]
|
||||
isEqualToString:@"moocow"]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -42,6 +42,7 @@ static_library("app_host_sources") {
|
||||
":app_shared_sources",
|
||||
"../../../build:ios_enable_arc",
|
||||
"../../../client",
|
||||
"../../../snapshot",
|
||||
"../../../third_party/edo",
|
||||
]
|
||||
frameworks = [
|
||||
@ -50,8 +51,21 @@ static_library("app_host_sources") {
|
||||
]
|
||||
}
|
||||
|
||||
# TODO(justincohen): Codesign crashy_initializer.so so it can run on devices.
|
||||
bundle_data("crashy_module_bundle") {
|
||||
testonly = true
|
||||
sources =
|
||||
[ "$root_out_dir/crashpad_snapshot_test_module_crashy_initializer.so" ]
|
||||
outputs = [ "{{bundle_contents_dir}}/crashpad_snapshot_test_module_crashy_initializer.so" ]
|
||||
public_deps =
|
||||
[ "../../../snapshot:crashpad_snapshot_test_module_crashy_initializer" ]
|
||||
}
|
||||
|
||||
ios_app_bundle("ios_crash_xcuitests") {
|
||||
info_plist = "Info.plist"
|
||||
testonly = true
|
||||
deps = [ ":app_host_sources" ]
|
||||
deps = [
|
||||
":app_host_sources",
|
||||
":crashy_module_bundle",
|
||||
]
|
||||
}
|
||||
|
@ -13,8 +13,11 @@
|
||||
// limitations under the License.
|
||||
|
||||
#import "test/ios/host/cptest_application_delegate.h"
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <dlfcn.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach-o/dyld_images.h>
|
||||
#include <mach-o/nlist.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -24,7 +27,15 @@
|
||||
|
||||
#import "Service/Sources/EDOHostNamingService.h"
|
||||
#import "Service/Sources/EDOHostService.h"
|
||||
#import "Service/Sources/NSObject+EDOValueObject.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "client/annotation.h"
|
||||
#include "client/annotation_list.h"
|
||||
#include "client/crash_report_database.h"
|
||||
#include "client/crashpad_client.h"
|
||||
#include "client/crashpad_info.h"
|
||||
#include "client/simple_string_dictionary.h"
|
||||
#include "snapshot/minidump/process_snapshot_minidump.h"
|
||||
#import "test/ios/host/cptest_crash_view_controller.h"
|
||||
#import "test/ios/host/cptest_shared_object.h"
|
||||
|
||||
@ -32,15 +43,58 @@
|
||||
#error "This file requires ARC support."
|
||||
#endif
|
||||
|
||||
@implementation CPTestApplicationDelegate
|
||||
using OperationStatus = crashpad::CrashReportDatabase::OperationStatus;
|
||||
using Report = crashpad::CrashReportDatabase::Report;
|
||||
|
||||
namespace {
|
||||
|
||||
base::FilePath GetDatabaseDir() {
|
||||
base::FilePath database_dir([NSFileManager.defaultManager
|
||||
URLsForDirectory:NSDocumentDirectory
|
||||
inDomains:NSUserDomainMask]
|
||||
.lastObject.path.UTF8String);
|
||||
return database_dir.Append("crashpad");
|
||||
}
|
||||
|
||||
std::unique_ptr<crashpad::CrashReportDatabase> GetDatabase() {
|
||||
base::FilePath database_dir = GetDatabaseDir();
|
||||
std::unique_ptr<crashpad::CrashReportDatabase> database =
|
||||
crashpad::CrashReportDatabase::Initialize(database_dir);
|
||||
return database;
|
||||
}
|
||||
|
||||
OperationStatus GetPendingReports(std::vector<Report>* pending_reports) {
|
||||
std::unique_ptr<crashpad::CrashReportDatabase> database(GetDatabase());
|
||||
return database->GetPendingReports(pending_reports);
|
||||
}
|
||||
|
||||
[[clang::optnone]] void recurse(int counter) {
|
||||
// Fill up the stack faster.
|
||||
int arr[1024];
|
||||
arr[0] = counter;
|
||||
if (counter > INT_MAX)
|
||||
return;
|
||||
recurse(++counter);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@interface CPTestApplicationDelegate ()
|
||||
- (void)processIntermediateDumps;
|
||||
@end
|
||||
|
||||
@implementation CPTestApplicationDelegate {
|
||||
crashpad::CrashpadClient client_;
|
||||
}
|
||||
|
||||
@synthesize window = _window;
|
||||
|
||||
- (BOOL)application:(UIApplication*)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
|
||||
// Start up crashpad.
|
||||
crashpad::CrashpadClient client;
|
||||
client.StartCrashpadInProcessHandler(base::FilePath(), "", {});
|
||||
if (client_.StartCrashpadInProcessHandler(GetDatabaseDir(), "", {})) {
|
||||
client_.ProcessIntermediateDumps();
|
||||
}
|
||||
|
||||
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||
[self.window makeKeyAndVisible];
|
||||
@ -54,9 +108,14 @@
|
||||
[EDOHostService serviceWithPort:12345
|
||||
rootObject:[[CPTestSharedObject alloc] init]
|
||||
queue:dispatch_get_main_queue()];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)processIntermediateDumps {
|
||||
client_.ProcessIntermediateDumps();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPTestSharedObject
|
||||
@ -65,6 +124,85 @@
|
||||
return @"crashpad";
|
||||
}
|
||||
|
||||
- (void)processIntermediateDumps {
|
||||
CPTestApplicationDelegate* delegate =
|
||||
(CPTestApplicationDelegate*)UIApplication.sharedApplication.delegate;
|
||||
[delegate processIntermediateDumps];
|
||||
}
|
||||
|
||||
- (void)clearPendingReports {
|
||||
std::unique_ptr<crashpad::CrashReportDatabase> database(GetDatabase());
|
||||
std::vector<crashpad::CrashReportDatabase::Report> pending_reports;
|
||||
database->GetPendingReports(&pending_reports);
|
||||
for (auto report : pending_reports) {
|
||||
database->DeleteReport(report.uuid);
|
||||
}
|
||||
}
|
||||
|
||||
- (int)pendingReportCount {
|
||||
std::vector<Report> pending_reports;
|
||||
OperationStatus status = GetPendingReports(&pending_reports);
|
||||
if (status != crashpad::CrashReportDatabase::kNoError) {
|
||||
return -1;
|
||||
}
|
||||
return pending_reports.size();
|
||||
}
|
||||
|
||||
- (int)pendingReportException {
|
||||
std::vector<Report> pending_reports;
|
||||
OperationStatus status = GetPendingReports(&pending_reports);
|
||||
if (status != crashpad::CrashReportDatabase::kNoError ||
|
||||
pending_reports.size() != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto reader = std::make_unique<crashpad::FileReader>();
|
||||
reader->Open(pending_reports[0].file_path);
|
||||
crashpad::ProcessSnapshotMinidump process_snapshot;
|
||||
process_snapshot.Initialize(reader.get());
|
||||
return static_cast<int>(process_snapshot.Exception()->Exception());
|
||||
}
|
||||
|
||||
- (NSDictionary*)getAnnotations {
|
||||
std::vector<Report> pending_reports;
|
||||
OperationStatus status = GetPendingReports(&pending_reports);
|
||||
if (status != crashpad::CrashReportDatabase::kNoError ||
|
||||
pending_reports.size() != 1) {
|
||||
return @{};
|
||||
}
|
||||
|
||||
auto reader = std::make_unique<crashpad::FileReader>();
|
||||
reader->Open(pending_reports[0].file_path);
|
||||
crashpad::ProcessSnapshotMinidump process_snapshot;
|
||||
process_snapshot.Initialize(reader.get());
|
||||
|
||||
NSDictionary* dict = @{
|
||||
@"simplemap" : [@{} mutableCopy],
|
||||
@"vector" : [@[] mutableCopy],
|
||||
@"objects" : [@[] mutableCopy]
|
||||
};
|
||||
for (const auto* module : process_snapshot.Modules()) {
|
||||
for (const auto& kv : module->AnnotationsSimpleMap()) {
|
||||
[dict[@"simplemap"] setValue:@(kv.second.c_str())
|
||||
forKey:@(kv.first.c_str())];
|
||||
}
|
||||
for (const std::string& annotation : module->AnnotationsVector()) {
|
||||
[dict[@"vector"] addObject:@(annotation.c_str())];
|
||||
}
|
||||
for (const auto& annotation : module->AnnotationObjects()) {
|
||||
if (annotation.type !=
|
||||
static_cast<uint16_t>(crashpad::Annotation::Type::kString)) {
|
||||
continue;
|
||||
}
|
||||
std::string value(reinterpret_cast<const char*>(annotation.value.data()),
|
||||
annotation.value.size());
|
||||
[dict[@"objects"]
|
||||
addObject:@{@(annotation.name.c_str()) : @(value.c_str())}];
|
||||
}
|
||||
}
|
||||
return [dict passByValue];
|
||||
}
|
||||
|
||||
- (void)crashBadAccess {
|
||||
strcpy(nullptr, "bla");
|
||||
}
|
||||
@ -92,13 +230,25 @@
|
||||
}
|
||||
|
||||
- (void)crashNSException {
|
||||
// EDO has its own sinkhole.
|
||||
// EDO has its own sinkhole, so dispatch this away.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSArray* empty_array = @[];
|
||||
[empty_array objectAtIndex:42];
|
||||
NSError* error = [NSError errorWithDomain:@"com.crashpad.xcuitests"
|
||||
code:200
|
||||
userInfo:@{@"Error Object" : self}];
|
||||
|
||||
[[NSException exceptionWithName:NSInternalInconsistencyException
|
||||
reason:@"Intentionally throwing error."
|
||||
userInfo:@{NSUnderlyingErrorKey : error}] raise];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)crashUnrecognizedSelectorAfterDelay {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||
[self performSelector:@selector(does_not_exist) withObject:nil afterDelay:1];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
- (void)catchNSException {
|
||||
@try {
|
||||
NSArray* empty_array = @[];
|
||||
@ -108,19 +258,53 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)crashUnreocgnizedSelectorAfterDelay {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||
[self performSelector:@selector(does_not_exist) withObject:nil afterDelay:1];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
- (void)recurse {
|
||||
[self recurse];
|
||||
}
|
||||
|
||||
- (void)crashRecursion {
|
||||
[self recurse];
|
||||
recurse(0);
|
||||
}
|
||||
|
||||
- (void)crashWithCrashInfoMessage {
|
||||
dlsym(nullptr, nullptr);
|
||||
}
|
||||
|
||||
- (void)crashWithDyldErrorString {
|
||||
std::string crashy_initializer =
|
||||
base::SysNSStringToUTF8([[NSBundle mainBundle]
|
||||
pathForResource:@"crashpad_snapshot_test_module_crashy_initializer"
|
||||
ofType:@"so"]);
|
||||
dlopen(crashy_initializer.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||
}
|
||||
|
||||
- (void)crashWithAnnotations {
|
||||
// This is “leaked” to crashpad_info.
|
||||
crashpad::SimpleStringDictionary* simple_annotations =
|
||||
new crashpad::SimpleStringDictionary();
|
||||
simple_annotations->SetKeyValue("#TEST# pad", "break");
|
||||
simple_annotations->SetKeyValue("#TEST# key", "value");
|
||||
simple_annotations->SetKeyValue("#TEST# pad", "crash");
|
||||
simple_annotations->SetKeyValue("#TEST# x", "y");
|
||||
simple_annotations->SetKeyValue("#TEST# longer", "shorter");
|
||||
simple_annotations->SetKeyValue("#TEST# empty_value", "");
|
||||
|
||||
crashpad::CrashpadInfo* crashpad_info =
|
||||
crashpad::CrashpadInfo::GetCrashpadInfo();
|
||||
|
||||
crashpad_info->set_simple_annotations(simple_annotations);
|
||||
|
||||
crashpad::AnnotationList::Register(); // This is “leaked” to crashpad_info.
|
||||
|
||||
static crashpad::StringAnnotation<32> test_annotation_one{"#TEST# one"};
|
||||
static crashpad::StringAnnotation<32> test_annotation_two{"#TEST# two"};
|
||||
static crashpad::StringAnnotation<32> test_annotation_three{
|
||||
"#TEST# same-name"};
|
||||
static crashpad::StringAnnotation<32> test_annotation_four{
|
||||
"#TEST# same-name"};
|
||||
|
||||
test_annotation_one.Set("moocow");
|
||||
test_annotation_two.Set("this will be cleared");
|
||||
test_annotation_three.Set("same-name 3");
|
||||
test_annotation_four.Set("same-name 4");
|
||||
test_annotation_two.Clear();
|
||||
abort();
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -58,7 +58,6 @@
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = UIColor.redColor;
|
||||
}
|
||||
|
||||
|
@ -18,9 +18,30 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface CPTestSharedObject : NSObject
|
||||
|
||||
// Returns the string "crashpad" for testing EDO.
|
||||
- (NSString*)testEDO;
|
||||
|
||||
// Tell Crashpad to process intermediate dumps.
|
||||
- (void)processIntermediateDumps;
|
||||
|
||||
// Clear pending reports from Crashpad database.
|
||||
- (void)clearPendingReports;
|
||||
|
||||
// Returns the number of pending reports, or -1 if there's an error getting
|
||||
// report.
|
||||
- (int)pendingReportCount;
|
||||
|
||||
// Returns exception code when there's a single pending report, or -1 if there's
|
||||
// a different number of pending reports.
|
||||
- (int)pendingReportException;
|
||||
|
||||
// Return an NSDictionary with a dictionary named "simplemap", an array named
|
||||
// "vector" and an array named "objects", representing the combination of all
|
||||
// modules AnnotationsSimpleMap, AnnotationsVector and AnnotationObjects
|
||||
// (strings only) respectively.
|
||||
- (NSDictionary*)getAnnotations;
|
||||
|
||||
// Triggers an EXC_BAD_ACCESS exception and crash.
|
||||
- (void)crashBadAccess;
|
||||
|
||||
@ -43,13 +64,22 @@
|
||||
- (void)crashNSException;
|
||||
|
||||
// Trigger an unrecognized selector after delay.
|
||||
- (void)crashUnreocgnizedSelectorAfterDelay;
|
||||
- (void)crashUnrecognizedSelectorAfterDelay;
|
||||
|
||||
// Trigger a caught NSxception.
|
||||
// Trigger a caught NSException, this will not crash
|
||||
- (void)catchNSException;
|
||||
|
||||
// Trigger a crash with an infinite recursion.
|
||||
- (void)crashRecursion;
|
||||
|
||||
// Trigger a crash dlsym that contains a crash_info message.
|
||||
- (void)crashWithCrashInfoMessage;
|
||||
|
||||
// Trigger an error that will to the dyld error string `_error_string`
|
||||
- (void)crashWithDyldErrorString;
|
||||
|
||||
// Trigger a crash after writing various annotations.
|
||||
- (void)crashWithAnnotations;
|
||||
@end
|
||||
|
||||
#endif // CRASHPAD_TEST_IOS_HOST_SHARED_OBJECT_H_
|
||||
|
Loading…
x
Reference in New Issue
Block a user