crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump.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

317 lines
10 KiB
C++

// Copyright 2020 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 <sys/stat.h>
#include "base/logging.h"
#include "snapshot/ios/intermediate_dump_reader_util.h"
#include "util/ios/ios_intermediate_dump_data.h"
#include "util/ios/ios_intermediate_dump_list.h"
#include "util/ios/ios_intermediate_dump_map.h"
namespace {
void MachTimeValueToTimeval(const time_value& mach, timeval* tv) {
tv->tv_sec = mach.seconds;
tv->tv_usec = mach.microseconds;
}
} // namespace
namespace crashpad {
namespace internal {
using Key = internal::IntermediateDumpKey;
bool ProcessSnapshotIOSIntermediateDump::InitializeWithFilePath(
const base::FilePath& dump_path,
const std::map<std::string, std::string>& annotations) {
IOSIntermediateDumpFilePath dump_interface;
if (!dump_interface.Initialize(dump_path))
return false;
return InitializeWithFileInterface(dump_interface, annotations);
}
bool ProcessSnapshotIOSIntermediateDump::InitializeWithFileInterface(
const IOSIntermediateDumpInterface& dump_interface,
const std::map<std::string, std::string>& annotations) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
annotations_simple_map_ = annotations;
client_id_.InitializeToZero();
IOSIntermediateDumpReaderInitializeResult result =
reader_.Initialize(dump_interface);
if (result == IOSIntermediateDumpReaderInitializeResult::kFailure) {
return false;
} else if (result == IOSIntermediateDumpReaderInitializeResult::kIncomplete) {
annotations_simple_map_["crashpad_intermediate_dump_incomplete"] = "yes";
}
const IOSIntermediateDumpMap* root_map = reader_.RootMap();
if (root_map->empty())
return false;
uint8_t version;
if (!GetDataValueFromMap(root_map, Key::kVersion, &version) || version != 1) {
LOG(ERROR) << "Root map version mismatch";
return false;
}
const internal::IOSIntermediateDumpMap* process_info =
GetMapFromMap(root_map, Key::kProcessInfo);
if (!process_info) {
LOG(ERROR) << "Process snapshot missing required process info map.";
return false;
}
GetDataValueFromMap(process_info, Key::kPID, &p_pid_);
GetDataValueFromMap(process_info, Key::kParentPID, &e_ppid_);
GetDataValueFromMap(process_info, Key::kStartTime, &p_starttime_);
const IOSIntermediateDumpMap* basic_info =
process_info->GetAsMap(Key::kTaskBasicInfo);
if (basic_info) {
GetDataValueFromMap(basic_info, Key::kUserTime, &basic_info_user_time_);
GetDataValueFromMap(basic_info, Key::kSystemTime, &basic_info_system_time_);
}
const IOSIntermediateDumpMap* thread_times =
process_info->GetAsMap(Key::kTaskThreadTimes);
if (thread_times) {
GetDataValueFromMap(thread_times, Key::kUserTime, &thread_times_user_time_);
GetDataValueFromMap(
thread_times, Key::kSystemTime, &thread_times_system_time_);
}
GetDataValueFromMap(process_info, Key::kSnapshotTime, &snapshot_time_);
const IOSIntermediateDumpList* simple_map_dump =
process_info->GetAsList(IntermediateDumpKey::kAnnotationsSimpleMap);
if (simple_map_dump) {
for (auto& annotation : *simple_map_dump) {
const IOSIntermediateDumpData* name_dump =
annotation->GetAsData(IntermediateDumpKey::kAnnotationName);
const IOSIntermediateDumpData* value_dump =
annotation->GetAsData(IntermediateDumpKey::kAnnotationValue);
if (name_dump && value_dump) {
annotations_simple_map_.insert(
make_pair(name_dump->GetString(), value_dump->GetString()));
}
}
}
const IOSIntermediateDumpMap* system_info =
GetMapFromMap(root_map, Key::kSystemInfo);
if (!system_info) {
LOG(ERROR) << "Process snapshot missing required system info map.";
return false;
}
system_.Initialize(system_info);
annotations_simple_map_["crashpad_uptime_ns"] =
std::to_string(system_.CrashpadUptime());
// Threads
const IOSIntermediateDumpList* thread_list =
GetListFromMap(root_map, Key::kThreads);
if (thread_list) {
for (const auto& value : *thread_list) {
auto thread =
std::make_unique<internal::ThreadSnapshotIOSIntermediateDump>();
if (thread->Initialize(value.get())) {
threads_.push_back(std::move(thread));
}
}
}
const IOSIntermediateDumpList* module_list =
GetListFromMap(root_map, Key::kModules);
if (module_list) {
for (const auto& value : *module_list) {
auto module =
std::make_unique<internal::ModuleSnapshotIOSIntermediateDump>();
if (module->Initialize(value.get())) {
modules_.push_back(std::move(module));
}
}
}
// Exceptions
const IOSIntermediateDumpMap* signal_exception =
root_map->GetAsMap(Key::kSignalException);
const IOSIntermediateDumpMap* mach_exception =
root_map->GetAsMap(Key::kMachException);
const IOSIntermediateDumpMap* ns_exception =
root_map->GetAsMap(Key::kNSException);
if (signal_exception) {
exception_.reset(new internal::ExceptionSnapshotIOSIntermediateDump());
if (!exception_->InitializeFromSignal(signal_exception)) {
LOG(ERROR) << "Process snapshot could not initialize signal exception.";
return false;
}
} else if (mach_exception) {
exception_.reset(new internal::ExceptionSnapshotIOSIntermediateDump());
if (!exception_->InitializeFromMachException(
mach_exception, GetListFromMap(root_map, Key::kThreads))) {
LOG(ERROR) << "Process snapshot could not initialize Mach exception.";
return false;
}
} else if (ns_exception) {
exception_.reset(new internal::ExceptionSnapshotIOSIntermediateDump());
if (!exception_->InitializeFromNSException(
ns_exception, GetListFromMap(root_map, Key::kThreads))) {
LOG(ERROR) << "Process snapshot could not initialize NSException.";
return false;
}
}
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
void ProcessSnapshotIOSIntermediateDump::SetClientID(const UUID& client_id) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
client_id_ = client_id;
}
void ProcessSnapshotIOSIntermediateDump::SetReportID(const UUID& report_id) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
report_id_ = report_id;
}
pid_t ProcessSnapshotIOSIntermediateDump::ProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return p_pid_;
}
pid_t ProcessSnapshotIOSIntermediateDump::ParentProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return e_ppid_;
}
void ProcessSnapshotIOSIntermediateDump::SnapshotTime(
timeval* snapshot_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
*snapshot_time = snapshot_time_;
}
void ProcessSnapshotIOSIntermediateDump::ProcessStartTime(
timeval* start_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
*start_time = p_starttime_;
}
void ProcessSnapshotIOSIntermediateDump::ProcessCPUTimes(
timeval* user_time,
timeval* system_time) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
// Calculate user and system time the same way the kernel does for
// getrusage(). See 10.15.0 xnu-6153.11.26/bsd/kern/kern_resource.c calcru().
timerclear(user_time);
timerclear(system_time);
MachTimeValueToTimeval(basic_info_user_time_, user_time);
MachTimeValueToTimeval(basic_info_system_time_, system_time);
timeval thread_user_time;
MachTimeValueToTimeval(thread_times_user_time_, &thread_user_time);
timeval thread_system_time;
MachTimeValueToTimeval(thread_times_system_time_, &thread_system_time);
timeradd(user_time, &thread_user_time, user_time);
timeradd(system_time, &thread_system_time, system_time);
}
void ProcessSnapshotIOSIntermediateDump::ReportID(UUID* report_id) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
*report_id = report_id_;
}
void ProcessSnapshotIOSIntermediateDump::ClientID(UUID* client_id) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
*client_id = client_id_;
}
const std::map<std::string, std::string>&
ProcessSnapshotIOSIntermediateDump::AnnotationsSimpleMap() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return annotations_simple_map_;
}
const SystemSnapshot* ProcessSnapshotIOSIntermediateDump::System() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return &system_;
}
std::vector<const ThreadSnapshot*> ProcessSnapshotIOSIntermediateDump::Threads()
const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
std::vector<const ThreadSnapshot*> threads;
for (const auto& thread : threads_) {
threads.push_back(thread.get());
}
return threads;
}
std::vector<const ModuleSnapshot*> ProcessSnapshotIOSIntermediateDump::Modules()
const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
std::vector<const ModuleSnapshot*> modules;
for (const auto& module : modules_) {
modules.push_back(module.get());
}
return modules;
}
std::vector<UnloadedModuleSnapshot>
ProcessSnapshotIOSIntermediateDump::UnloadedModules() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return std::vector<UnloadedModuleSnapshot>();
}
const ExceptionSnapshot* ProcessSnapshotIOSIntermediateDump::Exception() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return exception_.get();
}
std::vector<const MemoryMapRegionSnapshot*>
ProcessSnapshotIOSIntermediateDump::MemoryMap() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return std::vector<const MemoryMapRegionSnapshot*>();
}
std::vector<HandleSnapshot> ProcessSnapshotIOSIntermediateDump::Handles()
const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return std::vector<HandleSnapshot>();
}
std::vector<const MemorySnapshot*>
ProcessSnapshotIOSIntermediateDump::ExtraMemory() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return std::vector<const MemorySnapshot*>();
}
const ProcessMemory* ProcessSnapshotIOSIntermediateDump::Memory() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return nullptr;
}
} // namespace internal
} // namespace crashpad