crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc
Justin Cohen 22c386d1ac ios: Allow missing exception thread id from thread list.
It's expected that iOS intermediate dumps can be written with
missing information, but it's better to try and report as much as
possible rather than drop the incomplete minidump.

Bug: b/284959148
Change-Id: I04110b576a4ee552814234d559c9ba85db0382f0
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/4582167
Commit-Queue: Justin Cohen <justincohen@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
2024-01-22 19:57:50 +00:00

797 lines
31 KiB
C++

// Copyright 2021 The Crashpad Authors
//
// 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.
#include "snapshot/ios/process_snapshot_ios_intermediate_dump.h"
#include <mach-o/loader.h>
#include <algorithm>
#include "base/files/scoped_file.h"
#include "base/posix/eintr_wrapper.h"
#include "build/build_config.h"
#include "client/annotation.h"
#include "gtest/gtest.h"
#include "minidump/minidump_file_writer.h"
#include "test/errors.h"
#include "test/scoped_temp_dir.h"
#include "test/test_paths.h"
#include "util/file/file_io.h"
#include "util/file/filesystem.h"
#include "util/file/string_file.h"
#include "util/misc/uuid.h"
namespace crashpad {
namespace test {
namespace {
using Key = internal::IntermediateDumpKey;
using internal::IOSIntermediateDumpWriter;
using internal::ProcessSnapshotIOSIntermediateDump;
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 ProcessSnapshotIOSIntermediateDumpTest : public testing::Test {
protected:
ProcessSnapshotIOSIntermediateDumpTest()
: long_annotation_name_(Annotation::kNameMaxLength, 'a'),
long_annotation_value_(Annotation::kValueMaxSize, 'b') {}
void SetUp() override {
path_ = temp_dir_.path().Append("dump_file");
writer_ = std::make_unique<internal::IOSIntermediateDumpWriter>();
EXPECT_TRUE(writer_->Open(path_));
ASSERT_TRUE(IsRegularFile(path_));
}
void TearDown() override {
CloseWriter();
writer_.reset();
EXPECT_FALSE(IsRegularFile(path_));
}
const auto& path() const { return path_; }
const auto& annotations() const { return annotations_; }
auto writer() const { return writer_.get(); }
bool DumpSnapshot(const ProcessSnapshotIOSIntermediateDump& snapshot) {
MinidumpFileWriter minidump;
minidump.InitializeFromSnapshot(&snapshot);
StringFile string_file;
return minidump.WriteEverything(&string_file);
}
void WriteProcessInfo(IOSIntermediateDumpWriter* writer) {
IOSIntermediateDumpWriter::ScopedMap map(writer, Key::kProcessInfo);
pid_t pid = 2;
pid_t parent = 1;
EXPECT_TRUE(writer->AddProperty(Key::kPID, &pid));
EXPECT_TRUE(writer->AddProperty(Key::kParentPID, &parent));
timeval start_time = {12, 0};
EXPECT_TRUE(writer->AddProperty(Key::kStartTime, &start_time));
time_value_t user_time = {20, 0};
time_value_t system_time = {30, 0};
{
IOSIntermediateDumpWriter::ScopedMap taskInfo(writer,
Key::kTaskBasicInfo);
EXPECT_TRUE(writer->AddProperty(Key::kUserTime, &user_time));
EXPECT_TRUE(writer->AddProperty(Key::kSystemTime, &system_time));
}
{
IOSIntermediateDumpWriter::ScopedMap taskThreadTimesMap(
writer, Key::kTaskThreadTimes);
writer->AddProperty(Key::kUserTime, &user_time);
writer->AddProperty(Key::kSystemTime, &system_time);
}
timeval snapshot_time = {42, 0};
writer->AddProperty(Key::kSnapshotTime, &snapshot_time);
}
void WriteSystemInfo(IOSIntermediateDumpWriter* writer) {
IOSIntermediateDumpWriter::ScopedMap map(writer, Key::kSystemInfo);
std::string machine_description = "Gibson";
EXPECT_TRUE(writer->AddProperty(Key::kMachineDescription,
machine_description.c_str(),
machine_description.length()));
int os_version_major = 1995;
int os_version_minor = 9;
int os_version_bugfix = 15;
EXPECT_TRUE(writer->AddProperty(Key::kOSVersionMajor, &os_version_major));
EXPECT_TRUE(writer->AddProperty(Key::kOSVersionMinor, &os_version_minor));
EXPECT_TRUE(writer->AddProperty(Key::kOSVersionBugfix, &os_version_bugfix));
std::string os_version_build = "Da Vinci";
writer->AddProperty(Key::kOSVersionBuild,
os_version_build.c_str(),
os_version_build.length());
int cpu_count = 1;
EXPECT_TRUE(writer->AddProperty(Key::kCpuCount, &cpu_count));
std::string cpu_vendor = "RISC";
EXPECT_TRUE(writer->AddProperty(
Key::kCpuVendor, cpu_vendor.c_str(), cpu_vendor.length()));
bool has_daylight_saving_time = true;
EXPECT_TRUE(writer->AddProperty(Key::kHasDaylightSavingTime,
&has_daylight_saving_time));
bool is_daylight_saving_time = true;
EXPECT_TRUE(writer->AddProperty(Key::kIsDaylightSavingTime,
&is_daylight_saving_time));
int standard_offset_seconds = 7200;
EXPECT_TRUE(writer->AddProperty(Key::kStandardOffsetSeconds,
&standard_offset_seconds));
int daylight_offset_seconds = 3600;
EXPECT_TRUE(writer->AddProperty(Key::kDaylightOffsetSeconds,
&daylight_offset_seconds));
std::string standard_name = "Standard";
EXPECT_TRUE(writer->AddProperty(
Key::kStandardName, standard_name.c_str(), standard_name.length()));
std::string daylight_name = "Daylight";
EXPECT_TRUE(writer->AddProperty(
Key::kDaylightName, daylight_name.c_str(), daylight_name.length()));
vm_size_t page_size = getpagesize();
EXPECT_TRUE(writer->AddProperty(Key::kPageSize, &page_size));
{
natural_t count = 0;
IOSIntermediateDumpWriter::ScopedMap vmStatMap(writer, Key::kVMStat);
EXPECT_TRUE(writer->AddProperty(Key::kActive, &count));
EXPECT_TRUE(writer->AddProperty(Key::kInactive, &count));
EXPECT_TRUE(writer->AddProperty(Key::kWired, &count));
EXPECT_TRUE(writer->AddProperty(Key::kFree, &count));
}
uint64_t crashpad_report_time_nanos = 1234567890;
EXPECT_TRUE(
writer->AddProperty(Key::kCrashpadUptime, &crashpad_report_time_nanos));
}
void WriteAnnotations(IOSIntermediateDumpWriter* writer,
bool use_long_annotations) {
constexpr char short_annotation_name[] = "annotation_name";
constexpr char short_annotation_value[] = "annotation_value";
const char* const annotation_name = use_long_annotations
? long_annotation_name_.c_str()
: short_annotation_name;
const char* const annotation_value = use_long_annotations
? long_annotation_value_.c_str()
: short_annotation_value;
{
IOSIntermediateDumpWriter::ScopedArray annotationObjectArray(
writer, Key::kAnnotationObjects);
{
IOSIntermediateDumpWriter::ScopedArrayMap annotationMap(writer);
EXPECT_TRUE(writer->AddPropertyBytes(
Key::kAnnotationName, annotation_name, strlen(annotation_name)));
EXPECT_TRUE(writer->AddPropertyBytes(
Key::kAnnotationValue, annotation_value, strlen(annotation_value)));
Annotation::Type type = Annotation::Type::kString;
EXPECT_TRUE(writer->AddProperty(Key::kAnnotationType, &type));
}
}
{
IOSIntermediateDumpWriter::ScopedArray annotationsSimpleArray(
writer, Key::kAnnotationsSimpleMap);
{
IOSIntermediateDumpWriter::ScopedArrayMap annotationMap(writer);
EXPECT_TRUE(writer->AddPropertyBytes(
Key::kAnnotationName, annotation_name, strlen(annotation_name)));
EXPECT_TRUE(writer->AddPropertyBytes(
Key::kAnnotationValue, annotation_value, strlen(annotation_value)));
}
}
IOSIntermediateDumpWriter::ScopedMap annotationMap(
writer, Key::kAnnotationsCrashInfo);
{
EXPECT_TRUE(writer->AddPropertyBytes(Key::kAnnotationsCrashInfoMessage1,
annotation_value,
strlen(annotation_value)));
EXPECT_TRUE(writer->AddPropertyBytes(Key::kAnnotationsCrashInfoMessage2,
annotation_value,
strlen(annotation_value)));
}
}
void WriteModules(IOSIntermediateDumpWriter* writer,
bool has_module_path,
bool use_long_annotations) {
IOSIntermediateDumpWriter::ScopedArray moduleArray(writer, Key::kModules);
for (uint32_t image_index = 0; image_index < 2; ++image_index) {
IOSIntermediateDumpWriter::ScopedArrayMap modules(writer);
if (has_module_path) {
constexpr char image_file[] = "/path/to/module";
EXPECT_TRUE(
writer->AddProperty(Key::kName, image_file, strlen(image_file)));
}
uint64_t address = 0;
uint64_t vmsize = 1;
uintptr_t imageFileModDate = 2;
uint32_t current_version = 3;
uint32_t filetype = MH_DYLIB;
uint64_t source_version = 5;
static constexpr uint8_t uuid[16] = {0x00,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x09,
0x0a,
0x0b,
0x0c,
0x0d,
0x0e,
0x0f};
EXPECT_TRUE(writer->AddProperty(Key::kAddress, &address));
EXPECT_TRUE(writer->AddProperty(Key::kSize, &vmsize));
EXPECT_TRUE(writer->AddProperty(Key::kTimestamp, &imageFileModDate));
EXPECT_TRUE(
writer->AddProperty(Key::kDylibCurrentVersion, &current_version));
EXPECT_TRUE(writer->AddProperty(Key::kSourceVersion, &source_version));
EXPECT_TRUE(writer->AddProperty(Key::kUUID, &uuid));
EXPECT_TRUE(writer->AddProperty(Key::kFileType, &filetype));
WriteAnnotations(writer, use_long_annotations);
}
}
void ExpectModules(const std::vector<const ModuleSnapshot*>& modules,
bool expect_module_path,
bool expect_long_annotations) {
for (auto module : modules) {
EXPECT_EQ(module->GetModuleType(),
ModuleSnapshot::kModuleTypeSharedLibrary);
if (expect_module_path) {
EXPECT_STREQ(module->Name().c_str(), "/path/to/module");
EXPECT_STREQ(module->DebugFileName().c_str(), "module");
}
UUID uuid;
uint32_t age;
module->UUIDAndAge(&uuid, &age);
EXPECT_EQ(uuid.ToString(), "00010203-0405-0607-0809-0a0b0c0d0e0f");
for (auto annotation : module->AnnotationsVector()) {
if (expect_long_annotations) {
EXPECT_EQ(annotation, long_annotation_value_);
} else {
EXPECT_STREQ(annotation.c_str(), "annotation_value");
}
}
for (const auto& it : module->AnnotationsSimpleMap()) {
if (expect_long_annotations) {
EXPECT_EQ(it.first, long_annotation_name_);
EXPECT_EQ(it.second, long_annotation_value_);
} else {
EXPECT_STREQ(it.first.c_str(), "annotation_name");
EXPECT_STREQ(it.second.c_str(), "annotation_value");
}
}
for (auto annotation_object : module->AnnotationObjects()) {
EXPECT_EQ(annotation_object.type, (short)Annotation::Type::kString);
if (expect_long_annotations) {
EXPECT_EQ(annotation_object.name, long_annotation_name_);
EXPECT_EQ(std::string(reinterpret_cast<const char*>(
annotation_object.value.data()),
annotation_object.value.size()),
long_annotation_value_);
} else {
EXPECT_STREQ(annotation_object.name.c_str(), "annotation_name");
EXPECT_STREQ(std::string(reinterpret_cast<const char*>(
annotation_object.value.data()),
annotation_object.value.size())
.c_str(),
"annotation_value");
}
}
}
}
void WriteMachException(IOSIntermediateDumpWriter* writer,
bool short_context = false) {
IOSIntermediateDumpWriter::ScopedMap machExceptionMap(writer,
Key::kMachException);
exception_type_t exception = 5;
mach_exception_data_type_t code[] = {4, 3};
mach_msg_type_number_t code_count = 2;
#if defined(ARCH_CPU_X86_64)
thread_state_flavor_t flavor = x86_THREAD_STATE;
x86_thread_state_t state = {};
state.tsh.flavor = x86_THREAD_STATE64;
state.tsh.count = x86_THREAD_STATE64_COUNT;
state.uts.ts64.__rip = 0xdeadbeef;
size_t state_length = sizeof(x86_thread_state_t);
#elif defined(ARCH_CPU_ARM64)
thread_state_flavor_t flavor = ARM_UNIFIED_THREAD_STATE;
arm_unified_thread_state_t state = {};
state.ash.flavor = ARM_THREAD_STATE64;
state.ash.count = ARM_THREAD_STATE64_COUNT;
state.ts_64.__pc = 0xdeadbeef;
size_t state_length = sizeof(arm_unified_thread_state_t);
#endif
EXPECT_TRUE(writer->AddProperty(Key::kException, &exception));
EXPECT_TRUE(writer->AddProperty(Key::kCodes, code, code_count));
EXPECT_TRUE(writer->AddProperty(Key::kFlavor, &flavor));
if (short_context) {
state_length -= 10;
}
EXPECT_TRUE(writer->AddPropertyBytes(
Key::kState, reinterpret_cast<const void*>(&state), state_length));
uint64_t thread_id = 1;
EXPECT_TRUE(writer->AddProperty(Key::kThreadID, &thread_id));
}
void WriteThreads(IOSIntermediateDumpWriter* writer) {
vm_address_t stack_region_address = 0;
IOSIntermediateDumpWriter::ScopedArray threadArray(writer, Key::kThreads);
for (uint64_t thread_id = 1; thread_id < 3; thread_id++) {
IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer);
EXPECT_TRUE(writer->AddProperty(Key::kThreadID, &thread_id));
integer_t suspend_count = 666;
integer_t importance = 5;
uint64_t thread_handle = thread_id;
EXPECT_TRUE(writer->AddProperty(Key::kSuspendCount, &suspend_count));
EXPECT_TRUE(writer->AddProperty(Key::kPriority, &importance));
EXPECT_TRUE(writer->AddProperty(Key::kThreadDataAddress, &thread_handle));
#if defined(ARCH_CPU_X86_64)
x86_thread_state64_t thread_state = {};
thread_state.__rip = 0xdeadbeef;
x86_float_state64_t float_state = {};
x86_debug_state64_t debug_state = {};
#elif defined(ARCH_CPU_ARM64)
arm_thread_state64_t thread_state = {};
thread_state.__pc = 0xdeadbeef;
arm_neon_state64_t float_state = {};
arm_debug_state64_t debug_state = {};
#endif
EXPECT_TRUE(writer->AddProperty(Key::kThreadState, &thread_state));
EXPECT_TRUE(writer->AddProperty(Key::kFloatState, &float_state));
EXPECT_TRUE(writer->AddProperty(Key::kDebugState, &debug_state));
// Non-overlapping stack_region_address.
stack_region_address += 10;
EXPECT_TRUE(
writer->AddProperty(Key::kStackRegionAddress, &stack_region_address));
EXPECT_TRUE(
writer->AddPropertyBytes(Key::kStackRegionData, "stack_data", 10));
{
IOSIntermediateDumpWriter::ScopedArray memoryRegions(
writer, Key::kThreadContextMemoryRegions);
{
IOSIntermediateDumpWriter::ScopedArrayMap memoryRegion(writer);
const vm_address_t memory_region_address = 0;
EXPECT_TRUE(writer->AddProperty(
Key::kThreadContextMemoryRegionAddress, &memory_region_address));
EXPECT_TRUE(writer->AddPropertyBytes(
Key::kThreadContextMemoryRegionData, "string", 6));
}
}
EXPECT_TRUE(writer->AddPropertyBytes(Key::kThreadName, "ariadne", 7));
}
}
void ExpectMachException(const ExceptionSnapshot& exception) {
EXPECT_EQ(exception.ThreadID(), 1u);
EXPECT_EQ(exception.Exception(), 5u);
EXPECT_TRUE(exception.Context()->Is64Bit());
EXPECT_EQ(exception.Context()->InstructionPointer(), 0xdeadbeef);
EXPECT_EQ(exception.ExceptionInfo(), 4u);
EXPECT_EQ(exception.ExceptionAddress(), 0xdeadbeef);
EXPECT_EQ(exception.Codes()[0], 5u);
EXPECT_EQ(exception.Codes()[1], 4u);
EXPECT_EQ(exception.Codes()[2], 3u);
}
void ExpectThreads(const std::vector<const ThreadSnapshot*>& threads) {
uint64_t thread_id = 1;
for (auto thread : threads) {
EXPECT_EQ(thread->ThreadID(), thread_id);
EXPECT_EQ(thread->ThreadName(), "ariadne");
EXPECT_EQ(thread->SuspendCount(), 666);
EXPECT_EQ(thread->Priority(), 5);
EXPECT_EQ(thread->ThreadSpecificDataAddress(), thread_id++);
ReadToString delegate;
for (auto memory : thread->ExtraMemory()) {
memory->Read(&delegate);
EXPECT_STREQ(delegate.result.c_str(), "string");
}
thread->Stack()->Read(&delegate);
EXPECT_STREQ(delegate.result.c_str(), "stack_data");
EXPECT_TRUE(thread->Context()->Is64Bit());
EXPECT_EQ(thread->Context()->InstructionPointer(), 0xdeadbeef);
}
}
void ExpectSystem(const SystemSnapshot& system) {
EXPECT_EQ(system.CPUCount(), 1u);
EXPECT_STREQ(system.CPUVendor().c_str(), "RISC");
int major;
int minor;
int bugfix;
std::string build;
system.OSVersion(&major, &minor, &bugfix, &build);
EXPECT_EQ(major, 1995);
EXPECT_EQ(minor, 9);
EXPECT_EQ(bugfix, 15);
EXPECT_STREQ(build.c_str(), "Da Vinci");
EXPECT_STREQ(system.OSVersionFull().c_str(), "1995.9.15 Da Vinci");
EXPECT_STREQ(system.MachineDescription().c_str(), "Gibson");
SystemSnapshot::DaylightSavingTimeStatus dst_status;
int standard_offset_seconds;
int daylight_offset_seconds;
std::string standard_name;
std::string daylight_name;
system.TimeZone(&dst_status,
&standard_offset_seconds,
&daylight_offset_seconds,
&standard_name,
&daylight_name);
EXPECT_EQ(standard_offset_seconds, 7200);
EXPECT_EQ(daylight_offset_seconds, 3600);
EXPECT_STREQ(standard_name.c_str(), "Standard");
EXPECT_STREQ(daylight_name.c_str(), "Daylight");
}
void ExpectSnapshot(const ProcessSnapshot& snapshot,
bool expect_module_path,
bool expect_long_annotations) {
EXPECT_EQ(snapshot.ProcessID(), 2);
EXPECT_EQ(snapshot.ParentProcessID(), 1);
timeval snapshot_time;
snapshot.SnapshotTime(&snapshot_time);
EXPECT_EQ(snapshot_time.tv_sec, 42);
EXPECT_EQ(snapshot_time.tv_usec, 0);
timeval start_time;
snapshot.ProcessStartTime(&start_time);
EXPECT_EQ(start_time.tv_sec, 12);
EXPECT_EQ(start_time.tv_usec, 0);
timeval user_time, system_time;
snapshot.ProcessCPUTimes(&user_time, &system_time);
EXPECT_EQ(user_time.tv_sec, 40);
EXPECT_EQ(user_time.tv_usec, 0);
EXPECT_EQ(system_time.tv_sec, 60);
EXPECT_EQ(system_time.tv_usec, 0);
ExpectSystem(*snapshot.System());
ExpectThreads(snapshot.Threads());
ExpectModules(
snapshot.Modules(), expect_module_path, expect_long_annotations);
ExpectMachException(*snapshot.Exception());
auto map = snapshot.AnnotationsSimpleMap();
EXPECT_EQ(map["crashpad_uptime_ns"], "1234567890");
}
void CloseWriter() { EXPECT_TRUE(writer_->Close()); }
private:
std::unique_ptr<internal::IOSIntermediateDumpWriter> writer_;
ScopedTempDir temp_dir_;
base::FilePath path_;
std::map<std::string, std::string> annotations_;
const std::string long_annotation_name_;
const std::string long_annotation_value_;
};
TEST_F(ProcessSnapshotIOSIntermediateDumpTest, InitializeNoFile) {
const base::FilePath file;
ProcessSnapshotIOSIntermediateDump process_snapshot;
EXPECT_FALSE(process_snapshot.InitializeWithFilePath(file, annotations()));
EXPECT_TRUE(LoggingRemoveFile(path()));
EXPECT_FALSE(IsRegularFile(path()));
}
TEST_F(ProcessSnapshotIOSIntermediateDumpTest, InitializeEmpty) {
ProcessSnapshotIOSIntermediateDump process_snapshot;
EXPECT_FALSE(process_snapshot.InitializeWithFilePath(path(), annotations()));
EXPECT_FALSE(IsRegularFile(path()));
}
TEST_F(ProcessSnapshotIOSIntermediateDumpTest, InitializeMinimumDump) {
{
IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());
uint8_t version = 1;
EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));
{ IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kSystemInfo); }
{ IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kProcessInfo); }
}
CloseWriter();
ProcessSnapshotIOSIntermediateDump process_snapshot;
ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));
EXPECT_FALSE(IsRegularFile(path()));
EXPECT_TRUE(DumpSnapshot(process_snapshot));
}
TEST_F(ProcessSnapshotIOSIntermediateDumpTest, MissingSystemDump) {
{
IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());
uint8_t version = 1;
EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));
{ IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kProcessInfo); }
}
CloseWriter();
ProcessSnapshotIOSIntermediateDump process_snapshot;
ASSERT_FALSE(process_snapshot.InitializeWithFilePath(path(), annotations()));
EXPECT_FALSE(IsRegularFile(path()));
}
TEST_F(ProcessSnapshotIOSIntermediateDumpTest, MissingProcessDump) {
{
IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());
uint8_t version = 1;
EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));
{ IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kSystemInfo); }
}
CloseWriter();
ProcessSnapshotIOSIntermediateDump process_snapshot;
ASSERT_FALSE(process_snapshot.InitializeWithFilePath(path(), annotations()));
EXPECT_FALSE(IsRegularFile(path()));
}
TEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptySignalDump) {
{
IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());
uint8_t version = 1;
EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));
WriteSystemInfo(writer());
WriteProcessInfo(writer());
{
IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kSignalException);
uint64_t thread_id = 1;
EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id));
}
{
IOSIntermediateDumpWriter::ScopedArray threadArray(writer(),
Key::kThreads);
IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer());
uint64_t thread_id = 1;
writer()->AddProperty(Key::kThreadID, &thread_id);
}
}
CloseWriter();
ProcessSnapshotIOSIntermediateDump process_snapshot;
ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));
EXPECT_FALSE(IsRegularFile(path()));
EXPECT_TRUE(DumpSnapshot(process_snapshot));
}
TEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptyMachDump) {
{
IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());
uint8_t version = 1;
EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));
WriteSystemInfo(writer());
WriteProcessInfo(writer());
{
IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kMachException);
uint64_t thread_id = 1;
EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id));
}
{
IOSIntermediateDumpWriter::ScopedArray threadArray(writer(),
Key::kThreads);
IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer());
uint64_t thread_id = 1;
writer()->AddProperty(Key::kThreadID, &thread_id);
}
}
CloseWriter();
ProcessSnapshotIOSIntermediateDump process_snapshot;
ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));
EXPECT_FALSE(IsRegularFile(path()));
EXPECT_TRUE(DumpSnapshot(process_snapshot));
}
TEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptyExceptionDump) {
{
IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());
uint8_t version = 1;
EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));
WriteSystemInfo(writer());
WriteProcessInfo(writer());
{
IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kNSException);
uint64_t thread_id = 1;
EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id));
}
{
IOSIntermediateDumpWriter::ScopedArray threadArray(writer(),
Key::kThreads);
IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer());
uint64_t thread_id = 1;
writer()->AddProperty(Key::kThreadID, &thread_id);
}
}
CloseWriter();
ProcessSnapshotIOSIntermediateDump process_snapshot;
ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));
EXPECT_FALSE(IsRegularFile(path()));
EXPECT_TRUE(DumpSnapshot(process_snapshot));
}
TEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptyUncaughtNSExceptionDump) {
{
IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());
uint8_t version = 1;
EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));
WriteSystemInfo(writer());
WriteProcessInfo(writer());
{
IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kNSException);
uint64_t thread_id = 1;
EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id));
}
{
IOSIntermediateDumpWriter::ScopedArray threadArray(writer(),
Key::kThreads);
IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer());
uint64_t thread_id = 1;
writer()->AddProperty(Key::kThreadID, &thread_id);
const uint64_t frames[] = {0, 0};
const size_t num_frames = 2;
writer()->AddProperty(
Key::kThreadUncaughtNSExceptionFrames, frames, num_frames);
}
}
CloseWriter();
ProcessSnapshotIOSIntermediateDump process_snapshot;
ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));
EXPECT_FALSE(IsRegularFile(path()));
EXPECT_TRUE(DumpSnapshot(process_snapshot));
}
TEST_F(ProcessSnapshotIOSIntermediateDumpTest, ShortContext) {
{
IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());
uint8_t version = 1;
EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));
WriteSystemInfo(writer());
WriteProcessInfo(writer());
WriteThreads(writer());
WriteModules(
writer(), /*has_module_path=*/false, /*use_long_annotations=*/false);
WriteMachException(writer(), true /* short_context=true*/);
}
CloseWriter();
ProcessSnapshotIOSIntermediateDump process_snapshot;
ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));
EXPECT_FALSE(IsRegularFile(path()));
EXPECT_TRUE(DumpSnapshot(process_snapshot));
ExpectSnapshot(process_snapshot,
/*expect_module_path=*/false,
/*expect_long_annotations=*/false);
}
TEST_F(ProcessSnapshotIOSIntermediateDumpTest, LongAnnotations) {
{
IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());
uint8_t version = 1;
EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));
WriteSystemInfo(writer());
WriteProcessInfo(writer());
WriteThreads(writer());
WriteModules(
writer(), /*has_module_path=*/false, /*use_long_annotations=*/true);
WriteMachException(writer());
}
CloseWriter();
ProcessSnapshotIOSIntermediateDump process_snapshot;
ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));
EXPECT_FALSE(IsRegularFile(path()));
EXPECT_TRUE(DumpSnapshot(process_snapshot));
ExpectSnapshot(process_snapshot,
/*expect_module_path=*/false,
/*expect_long_annotations=*/true);
}
TEST_F(ProcessSnapshotIOSIntermediateDumpTest, FullReport) {
{
IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());
uint8_t version = 1;
EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));
WriteSystemInfo(writer());
WriteProcessInfo(writer());
WriteThreads(writer());
WriteModules(
writer(), /*has_module_path=*/true, /*use_long_annotations=*/false);
WriteMachException(writer());
}
CloseWriter();
ProcessSnapshotIOSIntermediateDump process_snapshot;
ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));
EXPECT_FALSE(IsRegularFile(path()));
EXPECT_TRUE(DumpSnapshot(process_snapshot));
ExpectSnapshot(process_snapshot,
/*expect_module_path=*/true,
/*expect_long_annotations=*/false);
}
TEST_F(ProcessSnapshotIOSIntermediateDumpTest, FuzzTestCases) {
base::FilePath fuzz_path = TestPaths::TestDataRoot().Append(FILE_PATH_LITERAL(
"snapshot/ios/testdata/crash-1fa088dda0adb41459d063078a0f384a0bb8eefa"));
crashpad::internal::ProcessSnapshotIOSIntermediateDump process_snapshot;
EXPECT_TRUE(process_snapshot.InitializeWithFilePath(fuzz_path, {}));
EXPECT_TRUE(LoggingRemoveFile(path()));
auto map = process_snapshot.AnnotationsSimpleMap();
ASSERT_TRUE(map.find("crashpad_intermediate_dump_incomplete") != map.end());
EXPECT_EQ(map["crashpad_intermediate_dump_incomplete"], "yes");
fuzz_path = TestPaths::TestDataRoot().Append(
FILE_PATH_LITERAL("snapshot/ios/testdata/crash-5726011582644224"));
crashpad::internal::ProcessSnapshotIOSIntermediateDump process_snapshot2;
EXPECT_TRUE(process_snapshot2.InitializeWithFilePath(fuzz_path, {}));
map = process_snapshot2.AnnotationsSimpleMap();
ASSERT_TRUE(map.find("crashpad_intermediate_dump_incomplete") != map.end());
EXPECT_EQ(map["crashpad_intermediate_dump_incomplete"], "yes");
fuzz_path = TestPaths::TestDataRoot().Append(
FILE_PATH_LITERAL("snapshot/ios/testdata/crash-6605504629637120"));
crashpad::internal::ProcessSnapshotIOSIntermediateDump process_snapshot3;
EXPECT_FALSE(process_snapshot3.InitializeWithFilePath(fuzz_path, {}));
fuzz_path = TestPaths::TestDataRoot().Append(
FILE_PATH_LITERAL("snapshot/ios/testdata/crash-c44acfcbccd8c7a8"));
crashpad::internal::ProcessSnapshotIOSIntermediateDump process_snapshot4;
EXPECT_TRUE(process_snapshot4.InitializeWithFilePath(fuzz_path, {}));
}
TEST_F(ProcessSnapshotIOSIntermediateDumpTest, WriteNoThreads) {
{
IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());
uint8_t version = 1;
EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));
WriteSystemInfo(writer());
WriteProcessInfo(writer());
WriteMachException(writer());
}
CloseWriter();
ProcessSnapshotIOSIntermediateDump process_snapshot;
ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));
EXPECT_FALSE(IsRegularFile(path()));
EXPECT_TRUE(DumpSnapshot(process_snapshot));
}
} // namespace
} // namespace test
} // namespace crashpad