mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-10 06:36:02 +00:00
The badAccess test may return different mach exception codes depending on optimization levels. Simplify by this by turning off optimization for this particular test. Also removes testSegv, which was duplicative and not really testing SIGSEGV. Change-Id: Idb92731da6a86545ed83c2bbdd200a0b792c579f Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3403040 Reviewed-by: Mark Mentovai <mark@chromium.org> Commit-Queue: Justin Cohen <justincohen@chromium.org>
358 lines
11 KiB
Plaintext
358 lines
11 KiB
Plaintext
// Copyright 2020 The Crashpad Authors. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#import "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>
|
|
#include <unistd.h>
|
|
|
|
#include <vector>
|
|
|
|
#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"
|
|
|
|
#if !defined(__has_feature) || !__has_feature(objc_arc)
|
|
#error "This file requires ARC support."
|
|
#endif
|
|
|
|
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);
|
|
}
|
|
|
|
std::unique_ptr<crashpad::ProcessSnapshotMinidump>
|
|
GetProcessSnapshotMinidumpFromSinglePending() {
|
|
std::vector<Report> pending_reports;
|
|
OperationStatus status = GetPendingReports(&pending_reports);
|
|
if (status != crashpad::CrashReportDatabase::kNoError ||
|
|
pending_reports.size() != 1) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto reader = std::make_unique<crashpad::FileReader>();
|
|
auto process_snapshot = std::make_unique<crashpad::ProcessSnapshotMinidump>();
|
|
if (!reader->Open(pending_reports[0].file_path) ||
|
|
!process_snapshot->Initialize(reader.get())) {
|
|
return nullptr;
|
|
}
|
|
return process_snapshot;
|
|
}
|
|
|
|
[[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.
|
|
std::map<std::string, std::string> annotations = {
|
|
{"prod", "xcuitest"}, {"ver", "1"}, {"plat", "iOS"}, {"crashpad", "yes"}};
|
|
|
|
NSArray<NSString*>* arguments = [[NSProcessInfo processInfo] arguments];
|
|
if ([arguments containsObject:@"--alternate-client-annotations"]) {
|
|
annotations = {{"prod", "some_app"},
|
|
{"ver", "42"},
|
|
{"plat", "macOS"},
|
|
{"crashpad", "no"}};
|
|
}
|
|
if (client_.StartCrashpadInProcessHandler(
|
|
GetDatabaseDir(), "", annotations)) {
|
|
client_.ProcessIntermediateDumps();
|
|
}
|
|
|
|
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
|
[self.window makeKeyAndVisible];
|
|
self.window.backgroundColor = UIColor.greenColor;
|
|
|
|
CPTestCrashViewController* controller =
|
|
[[CPTestCrashViewController alloc] init];
|
|
self.window.rootViewController = controller;
|
|
|
|
// Start up EDO.
|
|
[EDOHostService serviceWithPort:12345
|
|
rootObject:[[CPTestSharedObject alloc] init]
|
|
queue:dispatch_get_main_queue()];
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (void)processIntermediateDumps {
|
|
client_.ProcessIntermediateDumps();
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation CPTestSharedObject
|
|
|
|
- (NSString*)testEDO {
|
|
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();
|
|
}
|
|
|
|
- (bool)pendingReportException:(NSNumber**)exception {
|
|
auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending();
|
|
if (!process_snapshot || !process_snapshot->Exception()->Exception())
|
|
return false;
|
|
*exception = [NSNumber
|
|
numberWithUnsignedInt:process_snapshot->Exception()->Exception()];
|
|
return true;
|
|
}
|
|
|
|
- (bool)pendingReportExceptionInfo:(NSNumber**)exception_info {
|
|
auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending();
|
|
if (!process_snapshot || !process_snapshot->Exception()->ExceptionInfo())
|
|
return false;
|
|
|
|
*exception_info = [NSNumber
|
|
numberWithUnsignedInt:process_snapshot->Exception()->ExceptionInfo()];
|
|
return true;
|
|
}
|
|
|
|
- (NSDictionary*)getAnnotations {
|
|
auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending();
|
|
if (!process_snapshot)
|
|
return @{};
|
|
|
|
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];
|
|
}
|
|
|
|
- (NSDictionary*)getProcessAnnotations {
|
|
auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending();
|
|
if (!process_snapshot)
|
|
return @{};
|
|
|
|
NSDictionary* dict = [@{} mutableCopy];
|
|
for (const auto& kv : process_snapshot->AnnotationsSimpleMap()) {
|
|
[dict setValue:@(kv.second.c_str()) forKey:@(kv.first.c_str())];
|
|
}
|
|
|
|
return [dict passByValue];
|
|
}
|
|
|
|
// Use [[clang::optnone]] here to get consistent exception codes, otherwise the
|
|
// exception can change depending on optimization level.
|
|
- (void)crashBadAccess [[clang::optnone]] {
|
|
strcpy(nullptr, "bla");
|
|
}
|
|
|
|
- (void)crashKillAbort {
|
|
kill(getpid(), SIGABRT);
|
|
}
|
|
|
|
- (void)crashTrap {
|
|
__builtin_trap();
|
|
}
|
|
|
|
- (void)crashAbort {
|
|
abort();
|
|
}
|
|
|
|
- (void)crashException {
|
|
std::vector<int> empty_vector = {};
|
|
empty_vector.at(42);
|
|
}
|
|
|
|
- (void)crashNSException {
|
|
// EDO has its own sinkhole, so dispatch this away.
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
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 = @[];
|
|
[empty_array objectAtIndex:42];
|
|
} @catch (NSException* exception) {
|
|
} @finally {
|
|
}
|
|
}
|
|
|
|
- (void)crashCoreAutoLayoutSinkhole {
|
|
// EDO has its own sinkhole, so dispatch this away.
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
UIView* unattachedView = [[UIView alloc] init];
|
|
UIWindow* window = [UIApplication sharedApplication].windows[0];
|
|
[NSLayoutConstraint activateConstraints:@[
|
|
[window.rootViewController.view.bottomAnchor
|
|
constraintEqualToAnchor:unattachedView.bottomAnchor],
|
|
]];
|
|
});
|
|
}
|
|
|
|
- (void)crashRecursion {
|
|
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
|