| // 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 |