From cfe842c07491ddef431f30ed03da4a939d6c16ba Mon Sep 17 00:00:00 2001 From: Justin Cohen Date: Mon, 17 Mar 2025 18:35:30 -0400 Subject: [PATCH] ios: Add more tests for extra_memory_ranges and custom user streams. Adds two end-to-end tests for iOS, testing the newly ported custom user streams and extra memory ranges. Change-Id: Ia0721e394898aac5bec7b58d3750818615cca0c5 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/6362132 Commit-Queue: Justin Cohen Reviewed-by: Joshua Peraza --- client/crashpad_client.h | 2 +- client/ios_handler/in_process_handler_test.cc | 12 +-- test/ios/crash_type_xctest.mm | 43 +++++++++- test/ios/host/BUILD.gn | 1 + test/ios/host/cptest_application_delegate.mm | 84 ++++++++++++++++++- test/ios/host/cptest_shared_object.h | 14 +++- 6 files changed, 144 insertions(+), 12 deletions(-) diff --git a/client/crashpad_client.h b/client/crashpad_client.h index b089892b..c4b2bf85 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -538,7 +538,7 @@ class CrashpadClient { //! added to the minidump. static void ProcessIntermediateDumps( const std::map& annotations = {}, - const UserStreamDataSources* user_stream_sources = {}); + const UserStreamDataSources* user_stream_sources = nullptr); //! \brief Requests that the handler convert a single intermediate dump at \a //! file generated by DumpWithoutCrashAndDeferProcessingAtPath into a diff --git a/client/ios_handler/in_process_handler_test.cc b/client/ios_handler/in_process_handler_test.cc index cd6eaaee..43c9db68 100644 --- a/client/ios_handler/in_process_handler_test.cc +++ b/client/ios_handler/in_process_handler_test.cc @@ -110,35 +110,35 @@ TEST_F(InProcessHandlerTest, TestPendingFileLimit) { // Only process other app files. CreateFiles(0, 20); - handler().ProcessIntermediateDumps({}, {}); + handler().ProcessIntermediateDumps({}, nullptr); VerifyRemainingFileCount(0, 0); ClearFiles(); // Only process our app files. CreateFiles(20, 20); - handler().ProcessIntermediateDumps({}, {}); + handler().ProcessIntermediateDumps({}, nullptr); VerifyRemainingFileCount(0, 20); ClearFiles(); // Process all of our files and 10 remaining. CreateFiles(10, 30); - handler().ProcessIntermediateDumps({}, {}); + handler().ProcessIntermediateDumps({}, nullptr); VerifyRemainingFileCount(0, 20); ClearFiles(); // Process 20 our files, leaving 10 remaining, and all other files remaining. CreateFiles(30, 10); - handler().ProcessIntermediateDumps({}, {}); + handler().ProcessIntermediateDumps({}, nullptr); VerifyRemainingFileCount(10, 10); ClearFiles(); CreateFiles(0, 0); - handler().ProcessIntermediateDumps({}, {}); + handler().ProcessIntermediateDumps({}, nullptr); VerifyRemainingFileCount(0, 0); ClearFiles(); CreateFiles(10, 0); - handler().ProcessIntermediateDumps({}, {}); + handler().ProcessIntermediateDumps({}, nullptr); VerifyRemainingFileCount(0, 0); ClearFiles(); } diff --git a/test/ios/crash_type_xctest.mm b/test/ios/crash_type_xctest.mm index 646c9dfd..8d000950 100644 --- a/test/ios/crash_type_xctest.mm +++ b/test/ios/crash_type_xctest.mm @@ -104,6 +104,12 @@ bool IsMacOSVersion143OrGreaterAndiOS16OrLess() { - (void)setUp { app_ = [[XCUIApplication alloc] init]; + if ([self.name isEqualToString:@"-[CPTestTestCase testExtensionStreams]"]) { + app_.launchArguments = @[ @"--test-extension-streams" ]; + } else if ([self.name isEqualToString: + @"-[CPTestTestCase testCrashWithExtraMemory]"]) { + app_.launchArguments = @[ @"--test-extra_memory" ]; + } [app_ launch]; rootObject_ = [EDOClientService rootObjectWithPort:12345]; [rootObject_ clearPendingReports]; @@ -397,12 +403,47 @@ bool IsMacOSVersion143OrGreaterAndiOS16OrLess() { XCTAssertFalse(reader.Pop(ringBufferEntry)); } +- (void)testCrashWithExtraMemory { +#if TARGET_OS_SIMULATOR + // This test will fail on older ((data), size); + return true; + } +}; + +static constexpr char kExpectedStreamData[] = "Injected extension stream!"; + +class TestUserStreamDataSource : public crashpad::UserStreamDataSource { + public: + TestUserStreamDataSource() {} + + TestUserStreamDataSource(const TestUserStreamDataSource&) = delete; + TestUserStreamDataSource& operator=(const TestUserStreamDataSource&) = delete; + + std::unique_ptr + ProduceStreamData(crashpad::ProcessSnapshot* process_snapshot) override; +}; + +std::unique_ptr +TestUserStreamDataSource::ProduceStreamData( + crashpad::ProcessSnapshot* process_snapshot) { + return std::make_unique( + 0xCAFEBABE, kExpectedStreamData, sizeof(kExpectedStreamData)); +} + constexpr crashpad::Annotation::Type kRingBufferType = crashpad::Annotation::UserDefinedType(42); @@ -138,6 +169,8 @@ UIWindow* GetAnyWindow() { @implementation CPTestApplicationDelegate { crashpad::CrashpadClient client_; crashpad::ScopedFileHandle raw_logging_file_; + crashpad::SimpleAddressRangeBag extra_ranges_; + std::unique_ptr extra_memory_string_; } @synthesize window = _window; @@ -173,7 +206,19 @@ UIWindow* GetAnyWindow() { annotations, crashpad::CrashpadClient:: ProcessPendingReportsObservationCallback())) { - client_.ProcessIntermediateDumps(); + crashpad::UserStreamDataSources user_stream_data_sources; + if ([arguments containsObject:@"--test-extension-streams"]) { + user_stream_data_sources.push_back( + std::make_unique()); + } + client_.ProcessIntermediateDumps({}, &user_stream_data_sources); + } + + if ([arguments containsObject:@"--test-extra_memory"]) { + crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges( + &extra_ranges_); + extra_memory_string_ = std::make_unique("hello world"); + extra_ranges_.Insert((void*)extra_memory_string_->c_str(), 11); } self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; @@ -284,6 +329,43 @@ UIWindow* GetAnyWindow() { return [dict passByValue]; } +- (NSDictionary*)getExtraMemory { + auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending(); + if (!process_snapshot) + return @{}; + + NSDictionary* dict = [@{} mutableCopy]; + + for (auto memory : process_snapshot->ExtraMemory()) { + ReadToString delegate; + if (memory->Size() > 0 && memory->Read(&delegate) && + !delegate.result.empty()) { + NSString* key = [@(memory->Address()) stringValue]; + NSString* value = @(delegate.result.c_str()); + if (value.length) { + [dict setValue:value forKey:key]; + } + } + } + return [dict passByValue]; +} + +- (BOOL)hasExtensionStream { + auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending(); + if (!process_snapshot) + return NO; + + auto streams = process_snapshot->CustomMinidumpStreams(); + for (const auto& stream : streams) { + if (stream->stream_type() == 0xCAFEBABE) { + return memcmp(kExpectedStreamData, + stream->data().data(), + sizeof(kExpectedStreamData)) == 0; + } + } + return NO; +} + - (NSDictionary*)getProcessAnnotations { auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending(); if (!process_snapshot) diff --git a/test/ios/host/cptest_shared_object.h b/test/ios/host/cptest_shared_object.h index 21011ce4..985bd46a 100644 --- a/test/ios/host/cptest_shared_object.h +++ b/test/ios/host/cptest_shared_object.h @@ -43,11 +43,19 @@ - (bool)pendingReportExceptionInfo:(NSNumber**)exception_info; // 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. +// "vector" an array named "objects", and an array named "ringbuffers", +// representing the combination of all modules AnnotationsSimpleMap, +// AnnotationsVector and AnnotationObjects (String and RingBuffer type) +// respectively. - (NSDictionary*)getAnnotations; +// Return an NSDictionary with a dictionary representing all key value pairs of +// ExtraMemory MemorySnapshots where the data can be converted to an NSString. +- (NSDictionary*)getExtraMemory; + +// Returns YES if a minidump contains the expected custom stream data. +- (BOOL)hasExtensionStream; + // Return an NSDictionary representing the ProcessSnapshotMinidump // AnnotationsSimpleMap. - (NSDictionary*)getProcessAnnotations;