// 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 #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& 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& 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); // Threads const IOSIntermediateDumpList* thread_list = GetListFromMap(root_map, Key::kThreads); if (thread_list) { for (const auto& value : *thread_list) { auto thread = std::make_unique(); 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(); 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& 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 ProcessSnapshotIOSIntermediateDump::Threads() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); std::vector threads; for (const auto& thread : threads_) { threads.push_back(thread.get()); } return threads; } std::vector ProcessSnapshotIOSIntermediateDump::Modules() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); std::vector modules; for (const auto& module : modules_) { modules.push_back(module.get()); } return modules; } std::vector ProcessSnapshotIOSIntermediateDump::UnloadedModules() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return std::vector(); } const ExceptionSnapshot* ProcessSnapshotIOSIntermediateDump::Exception() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return exception_.get(); } std::vector ProcessSnapshotIOSIntermediateDump::MemoryMap() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return std::vector(); } std::vector ProcessSnapshotIOSIntermediateDump::Handles() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return std::vector(); } std::vector ProcessSnapshotIOSIntermediateDump::ExtraMemory() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return std::vector(); } const ProcessMemory* ProcessSnapshotIOSIntermediateDump::Memory() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return nullptr; } } // namespace internal } // namespace crashpad