ios: Add ability to reset Crashpad client for iOS tests.

Change-Id: I83df67d77367ef01731bd9af015605cfa19e972e
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3418581
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Justin Cohen <justincohen@chromium.org>
This commit is contained in:
Justin Cohen 2022-02-02 12:24:47 -05:00 committed by Crashpad LUCI CQ
parent 36ad571862
commit 55eb7a2eaf
4 changed files with 84 additions and 35 deletions

View File

@ -558,6 +558,11 @@ class CrashpadClient {
static void DumpWithoutCrashAndDeferProcessingAtPath(
NativeCPUContext* context,
const base::FilePath path);
//! \brief Unregister the Crashpad client. Intended to be used by tests so
//! multiple Crashpad clients can be started and stopped. Not expected to
//! be used in a shipping application.
static void ResetForTesting();
#endif
#if BUILDFLAG(IS_APPLE) || DOXYGEN

View File

@ -60,8 +60,14 @@ class CrashHandler : public Thread,
CrashHandler& operator=(const CrashHandler&) = delete;
static CrashHandler* Get() {
static CrashHandler* instance = new CrashHandler();
return instance;
if (!instance_)
instance_ = new CrashHandler();
return instance_;
}
static void ResetForTesting() {
delete instance_;
instance_ = nullptr;
}
bool Initialize(const base::FilePath& database,
@ -148,6 +154,12 @@ class CrashHandler : public Thread,
private:
CrashHandler() = default;
~CrashHandler() {
UninstallObjcExceptionPreprocessor();
Signals::InstallDefaultHandler(SIGABRT);
UninstallMachExceptionHandler();
}
bool InstallMachExceptionHandler() {
exception_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
if (!exception_port_.is_valid()) {
@ -180,15 +192,22 @@ class CrashHandler : public Thread,
return false;
}
mach_handler_running_ = true;
Start();
return true;
}
void UninstallMachExceptionHandler() {
mach_handler_running_ = false;
exception_port_.reset();
Join();
}
// Thread:
void ThreadMain() override {
UniversalMachExcServer universal_mach_exc_server(this);
while (true) {
while (mach_handler_running_) {
mach_msg_return_t mr =
MachMessageServer::Run(&universal_mach_exc_server,
exception_port_.get(),
@ -196,7 +215,10 @@ class CrashHandler : public Thread,
MachMessageServer::kPersistent,
MachMessageServer::kReceiveLargeIgnore,
kMachMessageTimeoutWaitIndefinitely);
MACH_CHECK(mr == MACH_SEND_INVALID_DEST, mr) << "MachMessageServer::Run";
MACH_CHECK(mr == (mach_handler_running_ ? MACH_SEND_INVALID_DEST
: MACH_RCV_PORT_CHANGED),
mr)
<< "MachMessageServer::Run";
}
}
@ -313,9 +335,13 @@ class CrashHandler : public Thread,
struct sigaction old_action_ = {};
internal::InProcessHandler in_process_handler_;
internal::IOSSystemDataCollector system_data_;
static CrashHandler* instance_;
bool mach_handler_running_ = false;
InitializationStateDcheck initialized_;
};
CrashHandler* CrashHandler::instance_ = nullptr;
} // namespace
CrashpadClient::CrashpadClient() {}
@ -380,4 +406,10 @@ void CrashpadClient::DumpWithoutCrashAndDeferProcessingAtPath(
crash_handler->DumpWithoutCrashAtPath(context, path);
}
void CrashpadClient::ResetForTesting() {
CrashHandler* crash_handler = CrashHandler::Get();
DCHECK(crash_handler);
crash_handler->ResetForTesting();
}
} // namespace crashpad

View File

@ -30,45 +30,61 @@ namespace crashpad {
namespace test {
namespace {
using CrashpadIOSClient = PlatformTest;
class CrashpadIOSClient : public testing::Test {
protected:
// testing::Test:
void SetUp() override {
ASSERT_TRUE(client_.StartCrashpadInProcessHandler(
base::FilePath(database_dir.path()), "", {}));
database_ = CrashReportDatabase::Initialize(database_dir.path());
}
void TearDown() override { client_.ResetForTesting(); }
auto& Client() { return client_; }
auto& Database() { return database_; }
private:
std::unique_ptr<CrashReportDatabase> database_;
CrashpadClient client_;
ScopedTempDir database_dir;
};
TEST_F(CrashpadIOSClient, DumpWithoutCrash) {
CrashpadClient client;
ScopedTempDir database_dir;
ASSERT_TRUE(client.StartCrashpadInProcessHandler(
base::FilePath(database_dir.path()), "", {}));
std::unique_ptr<CrashReportDatabase> database =
CrashReportDatabase::Initialize(database_dir.path());
std::vector<CrashReportDatabase::Report> reports;
EXPECT_EQ(database->GetPendingReports(&reports),
EXPECT_EQ(Database()->GetPendingReports(&reports),
CrashReportDatabase::kNoError);
ASSERT_EQ(reports.size(), 0u);
CRASHPAD_SIMULATE_CRASH();
reports.clear();
EXPECT_EQ(database->GetPendingReports(&reports),
EXPECT_EQ(Database()->GetPendingReports(&reports),
CrashReportDatabase::kNoError);
ASSERT_EQ(reports.size(), 1u);
}
TEST_F(CrashpadIOSClient, DumpWithoutCrashAndDefer) {
std::vector<CrashReportDatabase::Report> reports;
CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING();
reports.clear();
EXPECT_EQ(database->GetPendingReports(&reports),
EXPECT_EQ(Database()->GetPendingReports(&reports),
CrashReportDatabase::kNoError);
ASSERT_EQ(reports.size(), 0u);
Client().ProcessIntermediateDumps();
EXPECT_EQ(Database()->GetPendingReports(&reports),
CrashReportDatabase::kNoError);
ASSERT_EQ(reports.size(), 1u);
client.ProcessIntermediateDumps();
reports.clear();
EXPECT_EQ(database->GetPendingReports(&reports),
CrashReportDatabase::kNoError);
ASSERT_EQ(reports.size(), 2u);
}
TEST_F(CrashpadIOSClient, DumpWithoutCrashAndDeferAtPath) {
std::vector<CrashReportDatabase::Report> reports;
ScopedTempDir crash_dir;
UUID uuid;
uuid.InitializeWithNew();
CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING_AT_PATH(
crash_dir.path().Append(uuid.ToString()));
reports.clear();
EXPECT_EQ(database->GetPendingReports(&reports),
EXPECT_EQ(Database()->GetPendingReports(&reports),
CrashReportDatabase::kNoError);
ASSERT_EQ(reports.size(), 2u);
ASSERT_EQ(reports.size(), 0u);
NSError* error = nil;
NSArray* paths = [[NSFileManager defaultManager]
@ -76,12 +92,12 @@ TEST_F(CrashpadIOSClient, DumpWithoutCrash) {
crash_dir.path().value())
error:&error];
ASSERT_EQ([paths count], 1u);
client.ProcessIntermediateDump(
Client().ProcessIntermediateDump(
crash_dir.path().Append([paths[0] fileSystemRepresentation]));
reports.clear();
EXPECT_EQ(database->GetPendingReports(&reports),
EXPECT_EQ(Database()->GetPendingReports(&reports),
CrashReportDatabase::kNoError);
ASSERT_EQ(reports.size(), 3u);
ASSERT_EQ(reports.size(), 1u);
}
// This test is covered by a similar XCUITest, but for development purposes it's
@ -89,10 +105,6 @@ TEST_F(CrashpadIOSClient, DumpWithoutCrash) {
// to correctly run this in Google Test. Leave the test here, disabled, for use
// during development only.
TEST_F(CrashpadIOSClient, DISABLED_ThrowNSException) {
CrashpadClient client;
ScopedTempDir database_dir;
ASSERT_TRUE(client.StartCrashpadInProcessHandler(
base::FilePath(database_dir.path()), "", {}));
[NSException raise:@"GoogleTestNSException" format:@"ThrowException"];
}
@ -101,10 +113,6 @@ TEST_F(CrashpadIOSClient, DISABLED_ThrowNSException) {
// to correctly run this in Google Test. Leave the test here, disabled, for use
// during development only.
TEST_F(CrashpadIOSClient, DISABLED_ThrowException) {
CrashpadClient client;
ScopedTempDir database_dir;
ASSERT_TRUE(client.StartCrashpadInProcessHandler(
base::FilePath(database_dir.path()), "", {}));
std::vector<int> empty_vector;
empty_vector.at(42);
}

View File

@ -59,6 +59,10 @@ class ObjcExceptionDelegate {
//! signal handler. It should only be installed once.
void InstallObjcExceptionPreprocessor(ObjcExceptionDelegate* delegate);
//! \brief Uninstalls the Objective-C exception preprocessor. Expected to be
//! used by tests only.
void UninstallObjcExceptionPreprocessor();
} // namespace crashpad
#endif // CRASHPAD_UTIL_IOS_EXCEPTION_PROCESSOR_H_