From d9c1ca1216fc278b671c7b5edaebf3a5d80609f9 Mon Sep 17 00:00:00 2001 From: Justin Cohen Date: Wed, 25 Mar 2020 16:12:22 -0400 Subject: [PATCH] [ios] Bring up first draft system snapshot and iOS data collector. Gather most of the necessary information for the system snapshot. Note that: - The 'capture' portion of this CL will be moved out of the snapshot interface and into a separate in-process dump to disk location. - All of the pointer dereferences need to be wrapped in vm_read. - The read-fast-and-dump logic in thread_snapshot may end up in a different file completely, but until we pick a serialization/deserialization method, keep it as-is. Bug: crashpad:31 Change-Id: Iac82491fdb4a823163f02149f52a1e18e26fa9de Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2090173 Commit-Queue: Justin Cohen Reviewed-by: Mark Mentovai --- client/BUILD.gn | 10 +- client/crashpad_client_ios.cc | 6 +- minidump/minidump_extensions.h | 3 +- minidump/minidump_system_info_writer.cc | 3 + snapshot/BUILD.gn | 2 + snapshot/ios/process_snapshot_ios.cc | 79 ++++++++- snapshot/ios/process_snapshot_ios.h | 27 ++- snapshot/ios/system_snapshot_ios.cc | 224 ++++++++++++++++++++++++ snapshot/ios/system_snapshot_ios.h | 94 ++++++++++ snapshot/system_snapshot.h | 3 + util/BUILD.gn | 14 +- util/ios/ios_system_data_collector.h | 81 +++++++++ util/ios/ios_system_data_collector.mm | 202 +++++++++++++++++++++ 13 files changed, 734 insertions(+), 14 deletions(-) create mode 100644 snapshot/ios/system_snapshot_ios.cc create mode 100644 snapshot/ios/system_snapshot_ios.h create mode 100644 util/ios/ios_system_data_collector.h create mode 100644 util/ios/ios_system_data_collector.mm diff --git a/client/BUILD.gn b/client/BUILD.gn index 476ceb2c..dfdb3351 100644 --- a/client/BUILD.gn +++ b/client/BUILD.gn @@ -44,7 +44,10 @@ static_library("client") { } if (crashpad_is_ios) { - sources += [ "crashpad_client_ios.cc" ] + sources += [ + "crash_report_database_mac.mm", + "crashpad_client_ios.cc", + ] } if (crashpad_is_linux || crashpad_is_android) { @@ -95,7 +98,10 @@ static_library("client") { # TODO(justincohen): Temporary dependency to bring up the iOS client. if (crashpad_is_ios) { - deps += [ "../snapshot" ] + deps += [ + "../minidump", + "../snapshot", + ] } if (crashpad_is_linux || crashpad_is_android) { diff --git a/client/crashpad_client_ios.cc b/client/crashpad_client_ios.cc index 8e3fafaa..9f63a916 100644 --- a/client/crashpad_client_ios.cc +++ b/client/crashpad_client_ios.cc @@ -20,6 +20,7 @@ #include "base/strings/stringprintf.h" #include "client/client_argv_handling.h" #include "snapshot/ios/process_snapshot_ios.h" +#include "util/ios/ios_system_data_collector.h" #include "util/posix/signals.h" namespace crashpad { @@ -43,7 +44,7 @@ class SignalHandler { void HandleCrash(int signo, siginfo_t* siginfo, void* context) { // TODO(justincohen): This is incomplete. ProcessSnapshotIOS process_snapshot; - process_snapshot.Initialize(); + process_snapshot.Initialize(system_data); } private: @@ -68,6 +69,9 @@ class SignalHandler { Signals::OldActions old_actions_ = {}; + // Collect some system data before the signal handler is triggered. + IOSSystemDataCollector system_data; + DISALLOW_COPY_AND_ASSIGN(SignalHandler); }; diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h index 9332f96e..97276d52 100644 --- a/minidump/minidump_extensions.h +++ b/minidump/minidump_extensions.h @@ -242,7 +242,7 @@ enum MinidumpOS : uint32_t { kMinidumpOSMacOSX = 0x8101, //! \brief iOS, Darwin for mobile devices. - kMinidumpOSiOS = 0x8102, + kMinidumpOSIOS = 0x8102, //! \brief Linux, not including Android. kMinidumpOSLinux = 0x8201, @@ -264,7 +264,6 @@ enum MinidumpOS : uint32_t { kMinidumpOSUnknown = 0xffffffff, }; - //! \brief A list of ::RVA pointers. struct ALIGNAS(4) PACKED MinidumpRVAList { //! \brief The number of children present in the #children array. diff --git a/minidump/minidump_system_info_writer.cc b/minidump/minidump_system_info_writer.cc index 06aeecda..7b4b49dc 100644 --- a/minidump/minidump_system_info_writer.cc +++ b/minidump/minidump_system_info_writer.cc @@ -176,6 +176,9 @@ void MinidumpSystemInfoWriter::InitializeFromSnapshot( case SystemSnapshot::kOperatingSystemFuchsia: operating_system = kMinidumpOSFuchsia; break; + case SystemSnapshot::kOperatingSystemIOS: + operating_system = kMinidumpOSIOS; + break; default: NOTREACHED(); operating_system = kMinidumpOSUnknown; diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 0a118d0f..8f67a00e 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -119,6 +119,8 @@ static_library("snapshot") { "ios/module_snapshot_ios.h", "ios/process_snapshot_ios.cc", "ios/process_snapshot_ios.h", + "ios/system_snapshot_ios.cc", + "ios/system_snapshot_ios.h", "ios/thread_snapshot_ios.cc", "ios/thread_snapshot_ios.h", "mac/cpu_context_mac.cc", diff --git a/snapshot/ios/process_snapshot_ios.cc b/snapshot/ios/process_snapshot_ios.cc index 27f9de4d..8decea70 100644 --- a/snapshot/ios/process_snapshot_ios.cc +++ b/snapshot/ios/process_snapshot_ios.cc @@ -17,15 +17,29 @@ #include #include -#include - #include "base/logging.h" #include "base/mac/mach_logging.h" +#include "base/stl_util.h" + +namespace { + +void MachTimeValueToTimeval(const time_value& mach, timeval* tv) { + tv->tv_sec = mach.seconds; + tv->tv_usec = mach.microseconds; +} + +} // namespace namespace crashpad { ProcessSnapshotIOS::ProcessSnapshotIOS() : ProcessSnapshot(), + kern_proc_info_(), + basic_info_user_time_(), + basic_info_system_time_(), + thread_times_user_time_(), + thread_times_system_time_(), + system_(), threads_(), modules_(), report_id_(), @@ -36,14 +50,50 @@ ProcessSnapshotIOS::ProcessSnapshotIOS() ProcessSnapshotIOS::~ProcessSnapshotIOS() {} -bool ProcessSnapshotIOS::Initialize() { +bool ProcessSnapshotIOS::Initialize(const IOSSystemDataCollector& system_data) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + // Used by pid, parent pid and snapshot time. + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()}; + size_t len = sizeof(kern_proc_info_); + if (sysctl(mib, base::size(mib), &kern_proc_info_, &len, nullptr, 0)) { + PLOG(ERROR) << "sysctl"; + return false; + } + + // Used by user time and system time. + task_basic_info_64 task_basic_info; + mach_msg_type_number_t task_basic_info_count = TASK_BASIC_INFO_64_COUNT; + kern_return_t kr = task_info(mach_task_self(), + TASK_BASIC_INFO_64, + reinterpret_cast(&task_basic_info), + &task_basic_info_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "task_info TASK_BASIC_INFO_64"; + return false; + } + + task_thread_times_info_data_t task_thread_times; + mach_msg_type_number_t task_thread_times_count = TASK_THREAD_TIMES_INFO_COUNT; + kr = task_info(mach_task_self(), + TASK_THREAD_TIMES_INFO, + reinterpret_cast(&task_thread_times), + &task_thread_times_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "task_info TASK_THREAD_TIMES"; + } + + basic_info_user_time_ = task_basic_info.user_time; + basic_info_system_time_ = task_basic_info.system_time; + thread_times_user_time_ = task_thread_times.user_time; + thread_times_system_time_ = task_thread_times.system_time; + if (gettimeofday(&snapshot_time_, nullptr) != 0) { PLOG(ERROR) << "gettimeofday"; return false; } + system_.Initialize(system_data); InitializeThreads(); InitializeModules(); @@ -53,12 +103,12 @@ bool ProcessSnapshotIOS::Initialize() { pid_t ProcessSnapshotIOS::ProcessID() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return getpid(); + return kern_proc_info_.kp_proc.p_pid; } pid_t ProcessSnapshotIOS::ParentProcessID() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return 0; + return kern_proc_info_.kp_eproc.e_ppid; } void ProcessSnapshotIOS::SnapshotTime(timeval* snapshot_time) const { @@ -68,11 +118,28 @@ void ProcessSnapshotIOS::SnapshotTime(timeval* snapshot_time) const { void ProcessSnapshotIOS::ProcessStartTime(timeval* start_time) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *start_time = kern_proc_info_.kp_proc.p_starttime; } void ProcessSnapshotIOS::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 ProcessSnapshotIOS::ReportID(UUID* report_id) const { @@ -93,7 +160,7 @@ ProcessSnapshotIOS::AnnotationsSimpleMap() const { const SystemSnapshot* ProcessSnapshotIOS::System() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return nullptr; + return &system_; } std::vector ProcessSnapshotIOS::Threads() const { diff --git a/snapshot/ios/process_snapshot_ios.h b/snapshot/ios/process_snapshot_ios.h index 19d43b2e..28b67613 100644 --- a/snapshot/ios/process_snapshot_ios.h +++ b/snapshot/ios/process_snapshot_ios.h @@ -15,9 +15,12 @@ #ifndef CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_ #define CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_ +#include + #include #include "snapshot/ios/module_snapshot_ios.h" +#include "snapshot/ios/system_snapshot_ios.h" #include "snapshot/ios/thread_snapshot_ios.h" #include "snapshot/process_snapshot.h" #include "snapshot/thread_snapshot.h" @@ -34,9 +37,25 @@ class ProcessSnapshotIOS final : public ProcessSnapshot { //! \brief Initializes the object. //! + //! \param[in] system_data A class containing various system data points. + //! //! \return `true` if the snapshot could be created, `false` otherwise with //! an appropriate message logged. - bool Initialize(); + bool Initialize(const IOSSystemDataCollector& system_data); + + //! \brief Sets the value to be returned by ClientID(). + //! + //! On iOS, the client ID is under the control of the snapshot producer, + //! which may call this method to set the client ID. If this is not done, + //! ClientID() will return an identifier consisting entirely of zeroes. + void SetClientID(const UUID& client_id) { client_id_ = client_id; } + + //! \brief Sets the value to be returned by ReportID(). + //! + //! On iOS, the crash report ID is under the control of the snapshot + //! producer, which may call this method to set the report ID. If this is not + //! done, ReportID() will return an identifier consisting entirely of zeroes. + void SetReportID(const UUID& report_id) { report_id_ = report_id; } // ProcessSnapshot: pid_t ProcessID() const override; @@ -65,6 +84,12 @@ class ProcessSnapshotIOS final : public ProcessSnapshot { // Initializes threads_ on behalf of Initialize(). void InitializeThreads(); + kinfo_proc kern_proc_info_; + time_value_t basic_info_user_time_; + time_value_t basic_info_system_time_; + time_value_t thread_times_user_time_; + time_value_t thread_times_system_time_; + internal::SystemSnapshotIOS system_; std::vector> threads_; std::vector> modules_; UUID report_id_; diff --git a/snapshot/ios/system_snapshot_ios.cc b/snapshot/ios/system_snapshot_ios.cc new file mode 100644 index 00000000..57c686e8 --- /dev/null +++ b/snapshot/ios/system_snapshot_ios.cc @@ -0,0 +1,224 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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/system_snapshot_ios.h" + +#include +#include +#include +#include +#include + +#include + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/posix/timezone.h" +#include "util/mac/mac_util.h" +#include "util/numeric/in_range_cast.h" + +namespace crashpad { + +namespace internal { + +SystemSnapshotIOS::SystemSnapshotIOS() + : SystemSnapshot(), + os_version_build_(), + machine_description_(), + os_version_major_(0), + os_version_minor_(0), + os_version_bugfix_(0), + active_(0), + inactive_(0), + wired_(0), + free_(0), + cpu_count_(0), + cpu_vendor_(), + dst_status_(), + standard_offset_seconds_(0), + daylight_offset_seconds_(0), + standard_name_(), + daylight_name_(), + initialized_() {} + +SystemSnapshotIOS::~SystemSnapshotIOS() {} + +void SystemSnapshotIOS::Initialize(const IOSSystemDataCollector& system_data) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + system_data.OSVersion(&os_version_major_, + &os_version_minor_, + &os_version_bugfix_, + &os_version_build_); + machine_description_ = system_data.MachineDescription(); + cpu_count_ = system_data.ProcessorCount(); + cpu_vendor_ = system_data.CPUVendor(); + if (system_data.HasDaylightSavingTime()) { + dst_status_ = system_data.IsDaylightSavingTime() + ? SystemSnapshot::kObservingDaylightSavingTime + : SystemSnapshot::kObservingStandardTime; + } else { + dst_status_ = SystemSnapshot::kDoesNotObserveDaylightSavingTime; + } + standard_offset_seconds_ = system_data.StandardOffsetSeconds(); + daylight_offset_seconds_ = system_data.DaylightOffsetSeconds(); + standard_name_ = system_data.StandardName(); + daylight_name_ = system_data.DaylightName(); + + // Currently unused by minidump. + vm_size_t page_size; + host_page_size(mach_host_self(), &page_size); + mach_msg_type_number_t host_size = + sizeof(vm_statistics_data_t) / sizeof(integer_t); + vm_statistics_data_t vm_stat; + kern_return_t kr = host_statistics(mach_host_self(), + HOST_VM_INFO, + reinterpret_cast(&vm_stat), + &host_size); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "host_statistics"; + } + active_ = vm_stat.active_count * page_size; + inactive_ = vm_stat.inactive_count * page_size; + wired_ = vm_stat.wire_count * page_size; + free_ = vm_stat.free_count * page_size; + + INITIALIZATION_STATE_SET_VALID(initialized_); +} + +CPUArchitecture SystemSnapshotIOS::GetCPUArchitecture() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return kCPUArchitectureX86_64; +#elif defined(ARCH_CPU_ARM64) + return kCPUArchitectureARM64; +#endif +} + +uint32_t SystemSnapshotIOS::CPURevision() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): sysctlbyname machdep.cpu.* returns -1 on iOS/ARM64, but + // consider recording this for X86_64 only. + return 0; +} + +uint8_t SystemSnapshotIOS::CPUCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return cpu_count_; +} + +std::string SystemSnapshotIOS::CPUVendor() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return cpu_vendor_; +} + +void SystemSnapshotIOS::CPUFrequency(uint64_t* current_hz, + uint64_t* max_hz) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): sysctlbyname hw.cpufrequency returns -1 on iOS/ARM64, + // but consider recording this for X86_64 only. + *current_hz = 0; + *max_hz = 0; +} + +uint32_t SystemSnapshotIOS::CPUX86Signature() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): Consider recording this for X86_64 only. + return 0; +} + +uint64_t SystemSnapshotIOS::CPUX86Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): Consider recording this for X86_64 only. + return 0; +} + +uint64_t SystemSnapshotIOS::CPUX86ExtendedFeatures() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): Consider recording this for X86_64 only. + return 0; +} + +uint32_t SystemSnapshotIOS::CPUX86Leaf7Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): Consider recording this for X86_64 only. + return 0; +} + +bool SystemSnapshotIOS::CPUX86SupportsDAZ() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): Consider recording this for X86_64 only. + return false; +} + +SystemSnapshot::OperatingSystem SystemSnapshotIOS::GetOperatingSystem() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kOperatingSystemIOS; +} + +bool SystemSnapshotIOS::OSServer() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return false; +} + +void SystemSnapshotIOS::OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *major = os_version_major_; + *minor = os_version_minor_; + *bugfix = os_version_bugfix_; + build->assign(os_version_build_); +} + +std::string SystemSnapshotIOS::OSVersionFull() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return base::StringPrintf("%d.%d.%d %s", + os_version_major_, + os_version_minor_, + os_version_bugfix_, + os_version_build_.c_str()); +} + +std::string SystemSnapshotIOS::MachineDescription() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return machine_description_; +} + +bool SystemSnapshotIOS::NXEnabled() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): Consider using kern.nx when available (pre-iOS 13, + // pre-OS X 10.15). Otherwise the bit is always enabled. + return true; +} + +void SystemSnapshotIOS::TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *dst_status = dst_status_; + *standard_offset_seconds = standard_offset_seconds_; + *daylight_offset_seconds = daylight_offset_seconds_; + standard_name->assign(standard_name_); + daylight_name->assign(daylight_name_); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/ios/system_snapshot_ios.h b/snapshot/ios/system_snapshot_ios.h new file mode 100644 index 00000000..a38de4ef --- /dev/null +++ b/snapshot/ios/system_snapshot_ios.h @@ -0,0 +1,94 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_IOS_SYSTEM_SNAPSHOT_IOS_H_ +#define CRASHPAD_SNAPSHOT_IOS_SYSTEM_SNAPSHOT_IOS_H_ + +#include + +#include + +#include "base/macros.h" +#include "snapshot/system_snapshot.h" +#include "util/ios/ios_system_data_collector.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +namespace internal { + +//! \brief A SystemSnapshot of the running system, when the system runs iOS. +class SystemSnapshotIOS final : public SystemSnapshot { + public: + SystemSnapshotIOS(); + ~SystemSnapshotIOS() override; + + //! \brief Initializes the object. + //! + //! \param[in] system_data A class containing various system data points. + void Initialize(const IOSSystemDataCollector& system_data); + + // SystemSnapshot: + + CPUArchitecture GetCPUArchitecture() const override; + uint32_t CPURevision() const override; + uint8_t CPUCount() const override; + std::string CPUVendor() const override; + void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override; + uint32_t CPUX86Signature() const override; + uint64_t CPUX86Features() const override; + uint64_t CPUX86ExtendedFeatures() const override; + uint32_t CPUX86Leaf7Features() const override; + bool CPUX86SupportsDAZ() const override; + OperatingSystem GetOperatingSystem() const override; + bool OSServer() const override; + void OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const override; + std::string OSVersionFull() const override; + bool NXEnabled() const override; + std::string MachineDescription() const override; + void TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const override; + + private: + std::string os_version_build_; + std::string machine_description_; + int os_version_major_; + int os_version_minor_; + int os_version_bugfix_; + uint64_t active_; + uint64_t inactive_; + uint64_t wired_; + uint64_t free_; + int cpu_count_; + std::string cpu_vendor_; + DaylightSavingTimeStatus dst_status_; + int standard_offset_seconds_; + int daylight_offset_seconds_; + std::string standard_name_; + std::string daylight_name_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(SystemSnapshotIOS); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_IOS_SYSTEM_SNAPSHOT_IOS_H_ diff --git a/snapshot/system_snapshot.h b/snapshot/system_snapshot.h index a363c0c8..d78cc235 100644 --- a/snapshot/system_snapshot.h +++ b/snapshot/system_snapshot.h @@ -50,6 +50,9 @@ class SystemSnapshot { //! \brief Fuchsia. kOperatingSystemFuchsia, + + //! \brief iOS. + kOperatingSystemIOS, }; //! \brief A system’s daylight saving time status. diff --git a/util/BUILD.gn b/util/BUILD.gn index bd0d2427..ab0fe359 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -17,6 +17,9 @@ import("net/tls.gni") if (crashpad_is_in_chromium) { import("//build/config/sanitizers/sanitizers.gni") + + # Prevent Chromium source assignment filters from being inherited. + set_sources_assignment_filter([]) } if (crashpad_is_mac) { @@ -241,6 +244,15 @@ static_library("util") { } } + if (crashpad_is_ios) { + sources += [ + "ios/ios_system_data_collector.h", + "ios/ios_system_data_collector.mm", + "mac/xattr.cc", + "mac/xattr.h", + ] + } + if (crashpad_is_mac) { sources += [ "mac/checked_mach_address_range.h", @@ -313,7 +325,6 @@ static_library("util") { } if (crashpad_is_linux || crashpad_is_android) { - set_sources_assignment_filter([]) sources += [ "linux/address_types.h", "linux/auxiliary_vector.cc", @@ -661,7 +672,6 @@ source_set("util_test") { } if (crashpad_is_linux || crashpad_is_android) { - set_sources_assignment_filter([]) sources += [ "linux/auxiliary_vector_test.cc", "linux/memory_map_test.cc", diff --git a/util/ios/ios_system_data_collector.h b/util/ios/ios_system_data_collector.h new file mode 100644 index 00000000..45837c66 --- /dev/null +++ b/util/ios/ios_system_data_collector.h @@ -0,0 +1,81 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_IOS_IOS_SYSTEM_DATA_COLLECTOR_H_ +#define CRASHPAD_UTIL_IOS_IOS_SYSTEM_DATA_COLLECTOR_H_ + +#import + +#include + +namespace crashpad { + +//! \brief Used to collect system level data before a crash occurs. +class IOSSystemDataCollector { + public: + IOSSystemDataCollector(); + ~IOSSystemDataCollector(); + + void OSVersion(int* major, int* minor, int* bugfix, std::string* build) const; + std::string MachineDescription() const { return machine_description_; } + int ProcessorCount() const { return processor_count_; } + std::string CPUVendor() const { return cpu_vendor_; } + bool HasDaylightSavingTime() const { return has_next_daylight_saving_time_; } + bool IsDaylightSavingTime() const { return is_daylight_saving_time_; } + int StandardOffsetSeconds() const { return standard_offset_seconds_; } + int DaylightOffsetSeconds() const { return daylight_offset_seconds_; } + std::string StandardName() const { return standard_name_; } + std::string DaylightName() const { return daylight_name_; } + + // Currently unused by minidump. + int Orientation() const { return orientation_; } + + private: + // Notification handlers. + void InstallHandlers(); + static void SystemTimeZoneDidChangeNotificationHandler( + CFNotificationCenterRef center, + void* observer, + CFStringRef name, + const void* object, + CFDictionaryRef userInfo); + void SystemTimeZoneDidChangeNotification(); + + static void OrientationDidChangeNotificationHandler( + CFNotificationCenterRef center, + void* observer, + CFStringRef name, + const void* object, + CFDictionaryRef userInfo); + void OrientationDidChangeNotification(); + + int major_version_; + int minor_version_; + int patch_version_; + std::string build_; + std::string machine_description_; + int orientation_; + int processor_count_; + std::string cpu_vendor_; + bool has_next_daylight_saving_time_; + bool is_daylight_saving_time_; + int standard_offset_seconds_; + int daylight_offset_seconds_; + std::string standard_name_; + std::string daylight_name_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_IOS_IOS_SYSTEM_DATA_COLLECTOR_H_ diff --git a/util/ios/ios_system_data_collector.mm b/util/ios/ios_system_data_collector.mm new file mode 100644 index 00000000..ca6e91b8 --- /dev/null +++ b/util/ios/ios_system_data_collector.mm @@ -0,0 +1,202 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 "util/ios/ios_system_data_collector.h" + +#include +#include + +#import +#include +#import + +#include "base/mac/mach_logging.h" +#include "base/numerics/safe_conversions.h" +#include "build/build_config.h" + +namespace { + +std::string ReadStringSysctlByName(const char* name) { + size_t buf_len; + if (sysctlbyname(name, nullptr, &buf_len, nullptr, 0) != 0) { + PLOG(WARNING) << "sysctlbyname (size) " << name; + return std::string(); + } + + if (buf_len == 0) { + return std::string(); + } + + std::string value(buf_len - 1, '\0'); + if (sysctlbyname(name, &value[0], &buf_len, nullptr, 0) != 0) { + PLOG(WARNING) << "sysctlbyname " << name; + return std::string(); + } + + return value; +} + +} // namespace + +namespace crashpad { + +IOSSystemDataCollector::IOSSystemDataCollector() + : major_version_(0), + minor_version_(0), + patch_version_(0), + build_(), + machine_description_(), + orientation_(0), + processor_count_(0), + cpu_vendor_(), + has_next_daylight_saving_time_(false), + is_daylight_saving_time_(false), + standard_offset_seconds_(0), + daylight_offset_seconds_(0), + standard_name_(), + daylight_name_() { + NSOperatingSystemVersion version = + [[NSProcessInfo processInfo] operatingSystemVersion]; + major_version_ = base::saturated_cast(version.majorVersion); + minor_version_ = base::saturated_cast(version.minorVersion); + patch_version_ = base::saturated_cast(version.patchVersion); + processor_count_ = + base::saturated_cast([[NSProcessInfo processInfo] processorCount]); + build_ = ReadStringSysctlByName("kern.osversion"); + +#if defined(ARCH_CPU_X86_64) + cpu_vendor_ = ReadStringSysctlByName("machdep.cpu.vendor"); +#endif + +#if TARGET_OS_SIMULATOR + // TODO(justincohen): Consider adding board and model information to + // |machine_description| as well (similar to MacModelAndBoard in + // util/mac/mac_util.cc). + switch (UI_USER_INTERFACE_IDIOM()) { + case UIUserInterfaceIdiomPhone: + machine_description_ = "iOS Simulator (iPhone)"; + break; + case UIUserInterfaceIdiomPad: + machine_description_ = "iOS Simulator (iPad)"; + break; + default: + machine_description_ = "iOS Simulator (Unknown)"; + break; + } +#elif TARGET_OS_IPHONE + utsname uts; + if (uname(&uts) == 0) { + machine_description_ = uts.machine; + } +#else +#error "Unexpected target type OS." +#endif + + InstallHandlers(); +} + +IOSSystemDataCollector::~IOSSystemDataCollector() { + CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetLocalCenter(), + this); +} + +void IOSSystemDataCollector::OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const { + *major = major_version_; + *minor = minor_version_; + *bugfix = patch_version_; + build->assign(build_); +} + +void IOSSystemDataCollector::InstallHandlers() { + // Timezone. + CFNotificationCenterAddObserver( + CFNotificationCenterGetLocalCenter(), + this, + IOSSystemDataCollector::SystemTimeZoneDidChangeNotificationHandler, + reinterpret_cast(NSSystemTimeZoneDidChangeNotification), + nullptr, + CFNotificationSuspensionBehaviorDeliverImmediately); + SystemTimeZoneDidChangeNotification(); + + // Orientation. + CFNotificationCenterAddObserver( + CFNotificationCenterGetLocalCenter(), + this, + IOSSystemDataCollector::OrientationDidChangeNotificationHandler, + reinterpret_cast(UIDeviceOrientationDidChangeNotification), + nullptr, + CFNotificationSuspensionBehaviorDeliverImmediately); + OrientationDidChangeNotification(); +} + +// static +void IOSSystemDataCollector::SystemTimeZoneDidChangeNotificationHandler( + CFNotificationCenterRef center, + void* observer, + CFStringRef name, + const void* object, + CFDictionaryRef userInfo) { + static_cast(observer) + ->SystemTimeZoneDidChangeNotification(); +} + +void IOSSystemDataCollector::SystemTimeZoneDidChangeNotification() { + NSTimeZone* time_zone = NSTimeZone.localTimeZone; + NSDate* transition = + [time_zone nextDaylightSavingTimeTransitionAfterDate:[NSDate date]]; + if (transition == nil) { + has_next_daylight_saving_time_ = false; + daylight_name_ = [[time_zone abbreviation] UTF8String]; + standard_name_ = daylight_name_; + } else if (time_zone.isDaylightSavingTime) { + has_next_daylight_saving_time_ = true; + is_daylight_saving_time_ = true; + daylight_offset_seconds_ = + base::saturated_cast([time_zone secondsFromGMT]); + standard_offset_seconds_ = + base::saturated_cast([time_zone secondsFromGMTForDate:transition]); + daylight_name_ = [[time_zone abbreviation] UTF8String]; + standard_name_ = [[time_zone abbreviationForDate:transition] UTF8String]; + } else { + has_next_daylight_saving_time_ = true; + is_daylight_saving_time_ = false; + standard_name_ = [[time_zone abbreviation] UTF8String]; + daylight_name_ = [[time_zone abbreviationForDate:transition] UTF8String]; + standard_offset_seconds_ = + base::saturated_cast([time_zone secondsFromGMT]); + daylight_offset_seconds_ = + base::saturated_cast([time_zone secondsFromGMTForDate:transition]); + } +} + +// static +void IOSSystemDataCollector::OrientationDidChangeNotificationHandler( + CFNotificationCenterRef center, + void* observer, + CFStringRef name, + const void* object, + CFDictionaryRef userInfo) { + static_cast(observer) + ->OrientationDidChangeNotification(); +} + +void IOSSystemDataCollector::OrientationDidChangeNotification() { + orientation_ = + base::saturated_cast([[UIDevice currentDevice] orientation]); +} + +} // namespace crashpad