ios: Enable extra_memory_ranges on iOS.

Port set_extra_memory_ranges support to iOS. Because iOS crashes
are processed in two parts, store the memory data itself (not just
the range) in the intermediate dump, and on post-crash processing,
move the memory to ProcessSnapshot ExtraMemory.

Change-Id: I759d32bec5b451417b078dee4f836db6f6778fb1
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/6341431
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
Commit-Queue: Justin Cohen <justincohen@chromium.org>
This commit is contained in:
Justin Cohen 2025-03-10 22:43:14 -04:00 committed by Crashpad LUCI CQ
parent e09f5ee484
commit 342bd4df0e
10 changed files with 141 additions and 3 deletions

View File

@ -81,16 +81,25 @@ struct CrashpadInfo {
//! this method is called, or they may be added, removed, or modified in \a
//! address_range_bag after this method is called.
//!
//! TODO(scottmg) This is currently only supported on Windows.
//! TODO(scottmg) This is currently only supported on Windows and iOS.
//!
//! \param[in] address_range_bag A bag of address ranges. The CrashpadInfo
//! object does not take ownership of the SimpleAddressRangeBag object.
//! It is the callers responsibility to ensure that this pointer remains
//! valid while it is in effect for a CrashpadInfo object.
//!
//! \sa extra_memory_ranges()
void set_extra_memory_ranges(SimpleAddressRangeBag* address_range_bag) {
extra_memory_ranges_ = address_range_bag;
}
//! \return The simple extra memory ranges SimpleAddressRangeBag object.
//!
//! \sa set_extra_memory_ranges()
SimpleAddressRangeBag* extra_memory_ranges() const {
return extra_memory_ranges_;
}
//! \brief Sets the simple annotations dictionary.
//!
//! Simple annotations set on a CrashpadInfo structure are interpreted by

View File

@ -1144,6 +1144,7 @@ void InProcessIntermediateDumpHandler::WriteDataSegmentAnnotations(
crashpad_info->version() == 1) {
WriteCrashpadAnnotationsList(writer, crashpad_info.get());
WriteCrashpadSimpleAnnotationsDictionary(writer, crashpad_info.get());
WriteCrashpadExtraMemoryRanges(writer, crashpad_info.get());
}
} else if (strcmp(section_vm_read_ptr->sectname, "__crash_info") == 0) {
ScopedVMRead<crashreporter_annotations_t> crash_info;
@ -1257,5 +1258,35 @@ void InProcessIntermediateDumpHandler::WriteCrashpadAnnotationsList(
}
}
void InProcessIntermediateDumpHandler::WriteCrashpadExtraMemoryRanges(
IOSIntermediateDumpWriter* writer,
CrashpadInfo* crashpad_info) {
if (!crashpad_info->extra_memory_ranges()) {
return;
}
ScopedVMRead<SimpleAddressRangeBag> extra_memory_ranges;
if (!extra_memory_ranges.Read(crashpad_info->extra_memory_ranges())) {
CRASHPAD_RAW_LOG("Unable to read extra memory ranges object");
return;
}
IOSIntermediateDumpWriter::ScopedArray module_extra_memory_regions_array(
writer, IntermediateDumpKey::kModuleExtraMemoryRegions);
SimpleAddressRangeBag::Iterator iterator(*(extra_memory_ranges.get()));
while (const SimpleAddressRangeBag::Entry* entry = iterator.Next()) {
const uint64_t& address = entry->base;
const uint64_t& size = entry->size;
IOSIntermediateDumpWriter::ScopedArrayMap memory_region_map(writer);
WriteProperty(
writer, IntermediateDumpKey::kModuleExtraMemoryRegionAddress, &address);
WritePropertyBytes(writer,
IntermediateDumpKey::kModuleExtraMemoryRegionData,
reinterpret_cast<const void*>(address),
size);
}
}
} // namespace internal
} // namespace crashpad

View File

@ -152,6 +152,10 @@ class InProcessIntermediateDumpHandler final {
//! \brief Write Crashpad annotations list.
static void WriteCrashpadAnnotationsList(IOSIntermediateDumpWriter* writer,
CrashpadInfo* crashpad_info);
//! \brief Write Crashpad extra memory data.
static void WriteCrashpadExtraMemoryRanges(IOSIntermediateDumpWriter* writer,
CrashpadInfo* crashpad_info);
};
} // namespace internal

View File

@ -38,6 +38,16 @@ namespace {
using internal::InProcessIntermediateDumpHandler;
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;
}
};
class InProcessIntermediateDumpHandlerTest : public testing::Test {
protected:
// testing::Test:
@ -230,6 +240,37 @@ TEST_F(InProcessIntermediateDumpHandlerTest, TestAnnotations) {
}
}
TEST_F(InProcessIntermediateDumpHandlerTest, TestExtraMemoryRanges) {
#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 (IsMacOSVersion143OrGreaterAndiOS16OrLess()) {
// For TearDown.
ASSERT_TRUE(LoggingRemoveFile(path()));
return;
}
#endif
constexpr char kSomeExtraMemoryString[] = "extra memory range";
crashpad::SimpleAddressRangeBag* ios_extra_ranges =
new crashpad::SimpleAddressRangeBag();
crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(
ios_extra_ranges);
ios_extra_ranges->Insert((void*)kSomeExtraMemoryString, 18);
WriteReportAndCloseWriter();
crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(nullptr);
internal::ProcessSnapshotIOSIntermediateDump process_snapshot;
ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {}));
ASSERT_EQ(process_snapshot.ExtraMemory().size(), 1LU);
auto memory = process_snapshot.ExtraMemory()[0];
EXPECT_EQ(memory->Address(),
reinterpret_cast<uint64_t>(kSomeExtraMemoryString));
EXPECT_EQ(memory->Size(), 18LU);
ReadToString delegate;
ASSERT_TRUE(memory->Read(&delegate));
EXPECT_EQ(delegate.result, kSomeExtraMemoryString);
}
TEST_F(InProcessIntermediateDumpHandlerTest, TestThreads) {
const ScopedSetThreadName scoped_set_thread_name("TestThreads");

View File

@ -127,6 +127,33 @@ bool ModuleSnapshotIOSIntermediateDump::Initialize(
}
}
const IOSIntermediateDumpList* extra_memory_regions_array =
image_data->GetAsList(IntermediateDumpKey::kModuleExtraMemoryRegions);
if (extra_memory_regions_array) {
for (auto& region : *extra_memory_regions_array) {
vm_address_t address;
const IOSIntermediateDumpData* region_data =
region->GetAsData(Key::kModuleExtraMemoryRegionData);
if (!region_data)
continue;
if (GetDataValueFromMap(
region.get(), Key::kModuleExtraMemoryRegionAddress, &address)) {
const std::vector<uint8_t>& bytes = region_data->bytes();
vm_size_t data_size = bytes.size();
if (data_size == 0)
continue;
const vm_address_t data =
reinterpret_cast<const vm_address_t>(bytes.data());
auto memory =
std::make_unique<internal::MemorySnapshotIOSIntermediateDump>();
memory->Initialize(address, data, data_size);
extra_memory_.push_back(std::move(memory));
}
}
}
const IOSIntermediateDumpMap* crash_info_dump =
image_data->GetAsMap(IntermediateDumpKey::kAnnotationsCrashInfo);
if (crash_info_dump) {
@ -266,6 +293,15 @@ ModuleSnapshotIOSIntermediateDump::ExtraMemoryRanges() const {
return std::set<CheckedRange<uint64_t>>();
}
std::vector<const MemorySnapshot*>
ModuleSnapshotIOSIntermediateDump::ExtraMemory() const {
std::vector<const MemorySnapshot*> extra_memory;
for (const auto& memory : extra_memory_) {
extra_memory.push_back(memory.get());
}
return extra_memory;
}
std::vector<const UserMinidumpStream*>
ModuleSnapshotIOSIntermediateDump::CustomMinidumpStreams() const {
return std::vector<const UserMinidumpStream*>();

View File

@ -24,6 +24,7 @@
#include <vector>
#include "snapshot/crashpad_info_client_options.h"
#include "snapshot/ios/memory_snapshot_ios_intermediate_dump.h"
#include "snapshot/module_snapshot.h"
#include "util/ios/ios_intermediate_dump_map.h"
#include "util/misc/initialization_state_dcheck.h"
@ -75,6 +76,9 @@ class ModuleSnapshotIOSIntermediateDump final : public ModuleSnapshot {
std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;
std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;
// Used by ProcessSnapshot
std::vector<const MemorySnapshot*> ExtraMemory() const;
private:
std::string name_;
uint64_t address_;
@ -87,7 +91,8 @@ class ModuleSnapshotIOSIntermediateDump final : public ModuleSnapshot {
std::vector<std::string> annotations_vector_;
std::map<std::string, std::string> annotations_simple_map_;
std::vector<AnnotationSnapshot> annotation_objects_;
std::vector<std::unique_ptr<internal::MemorySnapshotIOSIntermediateDump>>
extra_memory_;
InitializationStateDcheck initialized_;
};

View File

@ -304,7 +304,13 @@ std::vector<HandleSnapshot> ProcessSnapshotIOSIntermediateDump::Handles()
std::vector<const MemorySnapshot*>
ProcessSnapshotIOSIntermediateDump::ExtraMemory() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return std::vector<const MemorySnapshot*>();
std::vector<const MemorySnapshot*> extra_memory;
for (const auto& module : modules_) {
for (const auto& memory : module->ExtraMemory()) {
extra_memory.push_back(memory);
}
}
return extra_memory;
}
const ProcessMemory* ProcessSnapshotIOSIntermediateDump::Memory() const {

View File

@ -121,6 +121,8 @@ class ProcessSnapshotIOSIntermediateDump final : public ProcessSnapshot {
UUID client_id_;
std::map<std::string, std::string> annotations_simple_map_;
timeval snapshot_time_;
std::vector<std::unique_ptr<internal::MemorySnapshotIOSIntermediateDump>>
extra_memory_;
InitializationStateDcheck initialized_;
};

View File

@ -242,6 +242,7 @@ uint64_t ThreadSnapshotIOSIntermediateDump::ThreadSpecificDataAddress() const {
std::vector<const MemorySnapshot*>
ThreadSnapshotIOSIntermediateDump::ExtraMemory() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
std::vector<const MemorySnapshot*> extra_memory;
for (const auto& memory : extra_memory_) {
extra_memory.push_back(memory.get());

View File

@ -56,6 +56,9 @@ namespace internal {
TD(kAnnotationsCrashInfoMessage1, 3016) \
TD(kAnnotationsCrashInfoMessage2, 3017) \
TD(kAnnotationsDyldErrorString, 3018) \
TD(kModuleExtraMemoryRegions, 3019) \
TD(kModuleExtraMemoryRegionAddress, 3020) \
TD(kModuleExtraMemoryRegionData, 3021) \
TD(kProcessInfo, 4000) \
TD(kParentPID, 4001) \
TD(kPID, 4002) \