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 <justincohen@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Justin Cohen 2025-03-17 18:35:30 -04:00 committed by Crashpad LUCI CQ
parent 3eba16f1ca
commit cfe842c074
6 changed files with 144 additions and 12 deletions

View File

@ -538,7 +538,7 @@ class CrashpadClient {
//! added to the minidump.
static void ProcessIntermediateDumps(
const std::map<std::string, std::string>& 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

View File

@ -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();
}

View File

@ -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 (<iOS17 simulators) when running on macOS 14.3
// or newer due to a bug in Simulator. crbug.com/328282286
if (crashpad::IsMacOSVersion143OrGreaterAndiOS16OrLess()) {
return;
}
#endif
[rootObject_ crashKillAbort];
[self verifyCrashReportException:EXC_SOFT_SIGNAL];
NSDictionary* dict = [rootObject_ getExtraMemory];
BOOL found = NO;
for (NSString* key in dict) {
if ([dict[key] isEqualToString:@"hello world"]) {
found = YES;
break;
}
}
XCTAssertTrue(found);
}
- (void)testExtensionStreams {
#if TARGET_OS_SIMULATOR
// This test will fail on older (<iOS17 simulators) when running on macOS 14.3
// or newer due to a bug in Simulator. crbug.com/328282286
if (crashpad::IsMacOSVersion143OrGreaterAndiOS16OrLess()) {
return;
}
#endif
[rootObject_ crashKillAbort];
[self verifyCrashReportException:EXC_SOFT_SIGNAL];
XCTAssertTrue([rootObject_ hasExtensionStream]);
}
- (void)testDumpWithoutCrash {
[rootObject_ generateDumpWithoutCrash:10 threads:3];
// The app should not crash
XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);
XCTAssertEqual([rootObject_ pendingReportCount], 30);
}

View File

@ -44,6 +44,7 @@ static_library("app_host_sources") {
":app_shared_sources",
"../../../build:apple_enable_arc",
"../../../client",
"../../../minidump:test_support",
"../../../snapshot",
"../../../test",
"../../../third_party/edo",

View File

@ -41,6 +41,7 @@
#include "client/ring_buffer_annotation.h"
#include "client/simple_string_dictionary.h"
#include "client/simulate_crash.h"
#include "minidump/test/minidump_user_extension_stream_util.h"
#include "snapshot/minidump/process_snapshot_minidump.h"
#include "test/file.h"
#import "test/ios/host/cptest_crash_view_controller.h"
@ -55,6 +56,36 @@ using Report = crashpad::CrashReportDatabase::Report;
namespace {
class ReadToString : public crashpad::MemorySnapshot::Delegate {
public:
std::string result;
bool MemorySnapshotDelegateRead(void* data, size_t size) override {
result = std::string(reinterpret_cast<const char*>(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<crashpad::MinidumpUserExtensionStreamDataSource>
ProduceStreamData(crashpad::ProcessSnapshot* process_snapshot) override;
};
std::unique_ptr<crashpad::MinidumpUserExtensionStreamDataSource>
TestUserStreamDataSource::ProduceStreamData(
crashpad::ProcessSnapshot* process_snapshot) {
return std::make_unique<crashpad::test::BufferExtensionStreamDataSource>(
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<std::string> 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<TestUserStreamDataSource>());
}
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<std::string>("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)

View File

@ -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;