blob: 609ea654cd0596989aaf3a0980866760868d9eb3 [file] [log] [blame]
// 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