crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc
Justin Cohen 30b2f4ba38 ios: Add crashpad_uptime_ns crash key to iOS reports.
This CL introduces a new crash key 'crashpad_uptime_ns' that records the
number of nanoseconds between when Crashpad was initialized and when a
snapshot is generated.

Crashpad minidumps record the MDRawMiscInfo process_create_time using a
sysctl(KERN_PROC).kp_proc.p_starttime. This time is used to display the
'uptime' of a process.  However, iOS 15 and later has a feature that
'prewarms' the app to reduce the amount of time the user waits before
the app is usable. This mean crashes that may happen immediately on
startup would appear to happen minutes or hours after process creation
time.

While initial implementations of prewarming would include some parts of
main, since iOS16 prewarming is complete before main, and therefore
before Crashpad is typically initialized.

Bug: crashpad:472
Change-Id: Iff960e37ae40121bd5927d319a2767d1cafce846
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/5171091
Reviewed-by: Ben Hamilton <benhamilton@google.com>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Justin Cohen <justincohen@chromium.org>
2024-01-11 16:42:54 +00:00

781 lines
30 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, {}));
}
} // namespace
} // namespace test
} // namespace crashpad