From 7f6f917aac8ae572641ecd2a013b02dea75f1af9 Mon Sep 17 00:00:00 2001 From: Justin Cohen Date: Wed, 26 May 2021 15:12:33 -0400 Subject: [PATCH] ios: Migrate ios/snapshot to reading intermediate dumps. Change-Id: Ib7715e642fa685a5f607239d07dcb68868cacb09 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2883523 Commit-Queue: Justin Cohen Reviewed-by: Mark Mentovai --- BUILD.gn | 7 +- client/crashpad_client_ios.cc | 14 - snapshot/BUILD.gn | 37 +- snapshot/ios/exception_snapshot_ios.cc | 276 -------- ...xception_snapshot_ios_intermediate_dump.cc | 391 +++++++++++ ...xception_snapshot_ios_intermediate_dump.h} | 60 +- snapshot/ios/intermediate_dump_reader_util.cc | 93 +++ snapshot/ios/intermediate_dump_reader_util.h | 116 ++++ ... memory_snapshot_ios_intermediate_dump.cc} | 28 +- ...> memory_snapshot_ios_intermediate_dump.h} | 19 +- snapshot/ios/module_snapshot_ios.cc | 238 ------- .../module_snapshot_ios_intermediate_dump.cc | 266 ++++++++ ...> module_snapshot_ios_intermediate_dump.h} | 49 +- snapshot/ios/process_snapshot_ios.cc | 288 -------- .../process_snapshot_ios_intermediate_dump.cc | 273 ++++++++ ... process_snapshot_ios_intermediate_dump.h} | 71 +- ...ess_snapshot_ios_intermediate_dump_test.cc | 640 ++++++++++++++++++ ... system_snapshot_ios_intermediate_dump.cc} | 134 ++-- ...> system_snapshot_ios_intermediate_dump.h} | 29 +- snapshot/ios/thread_snapshot_ios.cc | 481 ------------- .../thread_snapshot_ios_intermediate_dump.cc | 240 +++++++ ...> thread_snapshot_ios_intermediate_dump.h} | 34 +- util/misc/metrics.cc | 20 + util/misc/metrics.h | 15 + 24 files changed, 2300 insertions(+), 1519 deletions(-) delete mode 100644 snapshot/ios/exception_snapshot_ios.cc create mode 100644 snapshot/ios/exception_snapshot_ios_intermediate_dump.cc rename snapshot/ios/{exception_snapshot_ios.h => exception_snapshot_ios_intermediate_dump.h} (50%) create mode 100644 snapshot/ios/intermediate_dump_reader_util.cc create mode 100644 snapshot/ios/intermediate_dump_reader_util.h rename snapshot/ios/{memory_snapshot_ios.cc => memory_snapshot_ios_intermediate_dump.cc} (62%) rename snapshot/ios/{memory_snapshot_ios.h => memory_snapshot_ios_intermediate_dump.h} (72%) delete mode 100644 snapshot/ios/module_snapshot_ios.cc create mode 100644 snapshot/ios/module_snapshot_ios_intermediate_dump.cc rename snapshot/ios/{module_snapshot_ios.h => module_snapshot_ios_intermediate_dump.h} (61%) delete mode 100644 snapshot/ios/process_snapshot_ios.cc create mode 100644 snapshot/ios/process_snapshot_ios_intermediate_dump.cc rename snapshot/ios/{process_snapshot_ios.h => process_snapshot_ios_intermediate_dump.h} (61%) create mode 100644 snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc rename snapshot/ios/{system_snapshot_ios.cc => system_snapshot_ios_intermediate_dump.cc} (54%) rename snapshot/ios/{system_snapshot_ios.h => system_snapshot_ios_intermediate_dump.h} (75%) delete mode 100644 snapshot/ios/thread_snapshot_ios.cc create mode 100644 snapshot/ios/thread_snapshot_ios_intermediate_dump.cc rename snapshot/ios/{thread_snapshot_ios.h => thread_snapshot_ios_intermediate_dump.h} (63%) diff --git a/BUILD.gn b/BUILD.gn index ff4a0dd5..eeba1805 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -25,13 +25,11 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { deps = [ "client:client_test", "minidump:minidump_test", + "snapshot:snapshot_test", "test:googlemock_main", "test:test_test", "util:util_test", ] - if (!crashpad_is_ios) { - deps += [ "snapshot:snapshot_test" ] - } if (!crashpad_is_ios && !crashpad_is_fuchsia) { deps += [ "handler:handler_test" ] } @@ -150,9 +148,6 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { "snapshot:snapshot_test", "test:googletest_main", ] - if (crashpad_is_ios) { - deps -= [ "snapshot:snapshot_test" ] - } } test("crashpad_test_test") { diff --git a/client/crashpad_client_ios.cc b/client/crashpad_client_ios.cc index d25f757c..69e90d8c 100644 --- a/client/crashpad_client_ios.cc +++ b/client/crashpad_client_ios.cc @@ -22,7 +22,6 @@ #include "base/logging.h" #include "base/mac/mach_logging.h" #include "base/mac/scoped_mach_port.h" -#include "snapshot/ios/process_snapshot_ios.h" #include "util/ios/exception_processor.h" #include "util/ios/ios_system_data_collector.h" #include "util/mach/exc_server_variants.h" @@ -168,16 +167,6 @@ class CrashHandler : public Thread, public UniversalMachExcServer::Interface { ConstThreadState old_state, mach_msg_type_number_t old_state_count) { // TODO(justincohen): This is incomplete. - ProcessSnapshotIOS process_snapshot; - process_snapshot.Initialize(system_data_); - process_snapshot.SetExceptionFromMachException(behavior, - thread, - exception, - code, - code_count, - flavor, - old_state, - old_state_count); } // The signal handler installed at OS-level. @@ -190,9 +179,6 @@ class CrashHandler : public Thread, public UniversalMachExcServer::Interface { siginfo_t* siginfo, ucontext_t* context) { // TODO(justincohen): This is incomplete. - ProcessSnapshotIOS process_snapshot; - process_snapshot.Initialize(system_data_); - process_snapshot.SetExceptionFromSignal(siginfo, context); // Always call system handler. Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, &old_action_); diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index b9ee9593..70e4a310 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -107,18 +107,20 @@ crashpad_static_library("snapshot") { if (crashpad_is_ios) { sources += [ - "ios/exception_snapshot_ios.cc", - "ios/exception_snapshot_ios.h", - "ios/memory_snapshot_ios.cc", - "ios/memory_snapshot_ios.h", - "ios/module_snapshot_ios.cc", - "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", + "ios/exception_snapshot_ios_intermediate_dump.cc", + "ios/exception_snapshot_ios_intermediate_dump.h", + "ios/intermediate_dump_reader_util.cc", + "ios/intermediate_dump_reader_util.h", + "ios/memory_snapshot_ios_intermediate_dump.cc", + "ios/memory_snapshot_ios_intermediate_dump.h", + "ios/module_snapshot_ios_intermediate_dump.cc", + "ios/module_snapshot_ios_intermediate_dump.h", + "ios/process_snapshot_ios_intermediate_dump.cc", + "ios/process_snapshot_ios_intermediate_dump.h", + "ios/system_snapshot_ios_intermediate_dump.cc", + "ios/system_snapshot_ios_intermediate_dump.h", + "ios/thread_snapshot_ios_intermediate_dump.cc", + "ios/thread_snapshot_ios_intermediate_dump.h", "mac/cpu_context_mac.cc", "mac/cpu_context_mac.h", ] @@ -358,6 +360,13 @@ source_set("snapshot_test") { ] } + if (crashpad_is_ios) { + sources += [ + "ios/process_snapshot_ios_intermediate_dump_test.cc", + "mac/cpu_context_mac_test.cc", + ] + } + if (crashpad_is_linux || crashpad_is_android) { sources += [ "linux/debug_rendezvous_test.cc", @@ -433,6 +442,10 @@ source_set("snapshot_test") { deps += [ "../client" ] } + if (crashpad_is_ios) { + deps += [ "../minidump" ] + } + data_deps = [ ":crashpad_snapshot_test_module", ":crashpad_snapshot_test_module_large", diff --git a/snapshot/ios/exception_snapshot_ios.cc b/snapshot/ios/exception_snapshot_ios.cc deleted file mode 100644 index 5d6adc84..00000000 --- a/snapshot/ios/exception_snapshot_ios.cc +++ /dev/null @@ -1,276 +0,0 @@ -// 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/exception_snapshot_ios.h" - -#include "base/logging.h" -#include "base/mac/mach_logging.h" -#include "base/strings/stringprintf.h" -#include "snapshot/cpu_context.h" -#include "snapshot/mac/cpu_context_mac.h" -#include "util/misc/from_pointer_cast.h" - -namespace crashpad { - -namespace internal { - -ExceptionSnapshotIOS::ExceptionSnapshotIOS() - : ExceptionSnapshot(), - context_(), - codes_(), - thread_id_(0), - exception_address_(0), - exception_(0), - exception_info_(0), - initialized_() {} - -ExceptionSnapshotIOS::~ExceptionSnapshotIOS() {} - -void ExceptionSnapshotIOS::InitializeFromSignal(const siginfo_t* siginfo, - const ucontext_t* context) { - INITIALIZATION_STATE_SET_INITIALIZING(initialized_); - - mcontext_t mcontext = context->uc_mcontext; -#if defined(ARCH_CPU_X86_64) - context_.architecture = kCPUArchitectureX86_64; - context_.x86_64 = &context_x86_64_; - x86_debug_state64_t empty_debug_state = {}; - InitializeCPUContextX86_64(&context_x86_64_, - THREAD_STATE_NONE, - nullptr, - 0, - &mcontext->__ss, - &mcontext->__fs, - &empty_debug_state); -#elif defined(ARCH_CPU_ARM64) - context_.architecture = kCPUArchitectureARM64; - context_.arm64 = &context_arm64_; - arm_debug_state64_t empty_debug_state = {}; - InitializeCPUContextARM64(&context_arm64_, - THREAD_STATE_NONE, - nullptr, - 0, - &mcontext->__ss, - &mcontext->__ns, - &empty_debug_state); -#else -#error Port to your CPU architecture -#endif - - // Thread ID. - thread_identifier_info identifier_info; - mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; - kern_return_t kr = - thread_info(mach_thread_self(), - THREAD_IDENTIFIER_INFO, - reinterpret_cast(&identifier_info), - &count); - if (kr != KERN_SUCCESS) { - MACH_LOG(ERROR, kr) << "thread_identifier_info"; - } else { - thread_id_ = identifier_info.thread_id; - } - - exception_ = siginfo->si_signo; - exception_info_ = siginfo->si_code; - - // TODO(justincohen): Investigate recording more codes_. - - exception_address_ = FromPointerCast(siginfo->si_addr); - - // TODO(justincohen): Record the source of the exception (signal, mach, etc). - - INITIALIZATION_STATE_SET_VALID(initialized_); -} - -void ExceptionSnapshotIOS::InitializeFromMachException( - exception_behavior_t behavior, - thread_t exception_thread, - exception_type_t exception, - const mach_exception_data_type_t* code, - mach_msg_type_number_t code_count, - thread_state_flavor_t flavor, - ConstThreadState state, - mach_msg_type_number_t state_count) { - INITIALIZATION_STATE_SET_INITIALIZING(initialized_); - - codes_.push_back(exception); - // TODO: rationalize with the macOS implementation. - for (mach_msg_type_number_t code_index = 0; code_index < code_count; - ++code_index) { - codes_.push_back(code[code_index]); - } - exception_ = exception; - exception_info_ = code[0]; - - // For serialization, float_state and, on x86, debug_state, will be identical - // between here and the thread_snapshot version for thread_id. That means - // this block getting float_state and debug_state can be skipped when doing - // proper serialization. -#if defined(ARCH_CPU_X86_64) - x86_thread_state64_t thread_state; - x86_float_state64_t float_state; - x86_debug_state64_t debug_state; - mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT; - mach_msg_type_number_t float_state_count = x86_FLOAT_STATE64_COUNT; - mach_msg_type_number_t debug_state_count = x86_DEBUG_STATE64_COUNT; - const thread_state_flavor_t kThreadStateFlavor = x86_THREAD_STATE64; - const thread_state_flavor_t kFloatStateFlavor = x86_FLOAT_STATE64; - const thread_state_flavor_t kDebugStateFlavor = x86_DEBUG_STATE64; -#elif defined(ARCH_CPU_ARM64) - arm_thread_state64_t thread_state; - arm_neon_state64_t float_state; - arm_debug_state64_t debug_state; - mach_msg_type_number_t float_state_count = ARM_NEON_STATE64_COUNT; - mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT; - mach_msg_type_number_t debug_state_count = ARM_DEBUG_STATE64_COUNT; - const thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64; - const thread_state_flavor_t kFloatStateFlavor = ARM_NEON_STATE64; - const thread_state_flavor_t kDebugStateFlavor = ARM_DEBUG_STATE64; -#endif - - kern_return_t kr = - thread_get_state(exception_thread, - kThreadStateFlavor, - reinterpret_cast(&thread_state), - &thread_state_count); - if (kr != KERN_SUCCESS) { - MACH_LOG(ERROR, kr) << "thread_get_state(" << kThreadStateFlavor << ")"; - } - - kr = thread_get_state(exception_thread, - kFloatStateFlavor, - reinterpret_cast(&float_state), - &float_state_count); - if (kr != KERN_SUCCESS) { - MACH_LOG(ERROR, kr) << "thread_get_state(" << kFloatStateFlavor << ")"; - } - - kr = thread_get_state(exception_thread, - kDebugStateFlavor, - reinterpret_cast(&debug_state), - &debug_state_count); - if (kr != KERN_SUCCESS) { - MACH_LOG(ERROR, kr) << "thread_get_state(" << kDebugStateFlavor << ")"; - } - -#if defined(ARCH_CPU_X86_64) - context_.architecture = kCPUArchitectureX86_64; - context_.x86_64 = &context_x86_64_; - InitializeCPUContextX86_64(&context_x86_64_, - flavor, - state, - state_count, - &thread_state, - &float_state, - &debug_state); -#elif defined(ARCH_CPU_ARM64) - context_.architecture = kCPUArchitectureARM64; - context_.arm64 = &context_arm64_; - InitializeCPUContextARM64(&context_arm64_, - flavor, - state, - state_count, - &thread_state, - &float_state, - &debug_state); -#else -#error Port to your CPU architecture -#endif - - // Thread ID. - thread_identifier_info identifier_info; - mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; - kr = thread_info(mach_thread_self(), - THREAD_IDENTIFIER_INFO, - reinterpret_cast(&identifier_info), - &count); - if (kr != KERN_SUCCESS) { - MACH_LOG(ERROR, kr) << "thread_identifier_info"; - } else { - thread_id_ = identifier_info.thread_id; - } - - // Normally, for EXC_BAD_ACCESS exceptions, the exception address is present - // in code[1]. It may or may not be the instruction pointer address (usually - // it’s not). code[1] may carry the exception address for other exception - // types too, but it’s not guaranteed. But for all other exception types, the - // instruction pointer will be the exception address, and in fact will be - // equal to codes[1] when it’s carrying the exception address. In those cases, - // just use the instruction pointer directly. - bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS; - -#if defined(ARCH_CPU_X86_64) - // For x86 and x86_64 EXC_BAD_ACCESS exceptions, some code[0] values - // indicate that code[1] does not (or may not) carry the exception address: - // EXC_I386_GPFLT (10.9.5 xnu-2422.115.4/osfmk/i386/trap.c user_trap() for - // T_GENERAL_PROTECTION) and the oddball (VM_PROT_READ | VM_PROT_EXECUTE) - // which collides with EXC_I386_BOUNDFLT (10.9.5 - // xnu-2422.115.4/osfmk/i386/fpu.c fpextovrflt()). Other EXC_BAD_ACCESS - // exceptions come through 10.9.5 xnu-2422.115.4/osfmk/i386/trap.c - // user_page_fault_continue() and do contain the exception address in - // code[1]. - if (exception_ == EXC_BAD_ACCESS && - (exception_info_ == EXC_I386_GPFLT || - exception_info_ == (VM_PROT_READ | VM_PROT_EXECUTE))) { - code_1_is_exception_address = false; - } -#endif - - if (code_1_is_exception_address) { - exception_address_ = code[1]; - } else { - exception_address_ = context_.InstructionPointer(); - } - - INITIALIZATION_STATE_SET_VALID(initialized_); -} - -const CPUContext* ExceptionSnapshotIOS::Context() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return &context_; -} - -uint64_t ExceptionSnapshotIOS::ThreadID() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return thread_id_; -} - -uint32_t ExceptionSnapshotIOS::Exception() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return exception_; -} - -uint32_t ExceptionSnapshotIOS::ExceptionInfo() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return exception_info_; -} - -uint64_t ExceptionSnapshotIOS::ExceptionAddress() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return exception_address_; -} - -const std::vector& ExceptionSnapshotIOS::Codes() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return codes_; -} - -std::vector ExceptionSnapshotIOS::ExtraMemory() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return std::vector(); -} - -} // namespace internal -} // namespace crashpad diff --git a/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc b/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc new file mode 100644 index 00000000..a46880f1 --- /dev/null +++ b/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc @@ -0,0 +1,391 @@ +// 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/exception_snapshot_ios_intermediate_dump.h" + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/strings/stringprintf.h" +#include "snapshot/cpu_context.h" +#include "snapshot/ios/intermediate_dump_reader_util.h" +#include "snapshot/mac/cpu_context_mac.h" +#include "util/ios/ios_intermediate_dump_data.h" +#include "util/ios/ios_intermediate_dump_list.h" +#include "util/ios/ios_intermediate_dump_writer.h" +#include "util/misc/from_pointer_cast.h" + +namespace crashpad { + +namespace internal { + +size_t ThreadStateLengthForFlavor(thread_state_flavor_t flavor) { +#if defined(ARCH_CPU_X86_64) + switch (flavor) { + case x86_THREAD_STATE: + return sizeof(x86_thread_state_t); + case x86_FLOAT_STATE: + return sizeof(x86_float_state_t); + case x86_DEBUG_STATE: + return sizeof(x86_debug_state_t); + case x86_THREAD_STATE64: + return sizeof(x86_thread_state64_t); + case x86_FLOAT_STATE64: + return sizeof(x86_float_state64_t); + case x86_DEBUG_STATE64: + return sizeof(x86_debug_state64_t); + default: + return 0; + } +#elif defined(ARCH_CPU_ARM64) + switch (flavor) { + case ARM_UNIFIED_THREAD_STATE: + return sizeof(arm_unified_thread_state_t); + case ARM_THREAD_STATE64: + return sizeof(arm_thread_state64_t); + case ARM_NEON_STATE64: + return sizeof(arm_neon_state64_t); + case ARM_DEBUG_STATE64: + return sizeof(arm_debug_state64_t); + default: + return 0; + } +#endif +} + +using Key = IntermediateDumpKey; + +ExceptionSnapshotIOSIntermediateDump::ExceptionSnapshotIOSIntermediateDump() + : ExceptionSnapshot(), + context_(), + codes_(), + thread_id_(0), + exception_address_(0), + exception_(0), + exception_info_(0), + initialized_() { +#if defined(ARCH_CPU_X86_64) + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_x86_64_; +#elif defined(ARCH_CPU_ARM64) + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_arm64_; +#else +#error Port to your CPU architecture +#endif +} + +ExceptionSnapshotIOSIntermediateDump::~ExceptionSnapshotIOSIntermediateDump() {} + +bool ExceptionSnapshotIOSIntermediateDump::InitializeFromSignal( + const IOSIntermediateDumpMap* exception_data) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + DCHECK(exception_data); + + if (!GetDataValueFromMap(exception_data, Key::kThreadID, &thread_id_)) { + LOG(ERROR) << "Exceptions require a thread id."; + return false; + } + +#if defined(ARCH_CPU_X86_64) + typedef x86_thread_state64_t thread_state_type; + typedef x86_float_state64_t float_state_type; +#elif defined(ARCH_CPU_ARM64) + typedef arm_thread_state64_t thread_state_type; + typedef arm_neon_state64_t float_state_type; +#endif + + thread_state_type thread_state; + float_state_type float_state; + if (GetDataValueFromMap(exception_data, Key::kThreadState, &thread_state) && + GetDataValueFromMap(exception_data, Key::kFloatState, &float_state)) { +#if defined(ARCH_CPU_X86_64) + x86_debug_state64_t empty_debug_state = {}; + InitializeCPUContextX86_64(&context_x86_64_, + THREAD_STATE_NONE, + nullptr, + 0, + &thread_state, + &float_state, + &empty_debug_state); +#elif defined(ARCH_CPU_ARM64) + arm_debug_state64_t empty_debug_state = {}; + InitializeCPUContextARM64(&context_arm64_, + THREAD_STATE_NONE, + nullptr, + 0, + &thread_state, + &float_state, + &empty_debug_state); +#else +#error Port to your CPU architecture +#endif + } + + GetDataValueFromMap(exception_data, Key::kSignalNumber, &exception_); + GetDataValueFromMap(exception_data, Key::kSignalCode, &exception_info_); + GetDataValueFromMap(exception_data, Key::kSignalAddress, &exception_address_); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ExceptionSnapshotIOSIntermediateDump::InitializeFromMachException( + const IOSIntermediateDumpMap* exception_data, + const IOSIntermediateDumpList* thread_list) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + DCHECK(exception_data); + + if (!GetDataValueFromMap(exception_data, Key::kThreadID, &thread_id_)) { + LOG(ERROR) << "Exceptions require a thread id."; + return false; + } + + exception_type_t exception; + if (GetDataValueFromMap(exception_data, Key::kException, &exception)) { + codes_.push_back(exception); + exception_ = exception; + } + + mach_msg_type_number_t code_count; + GetDataValueFromMap(exception_data, Key::kCodeCount, &code_count); + + const IOSIntermediateDumpData* code_dump = + GetDataFromMap(exception_data, Key::kCode); + if (code_dump) { + const std::vector& bytes = code_dump->bytes(); + const mach_exception_data_type_t* code = + reinterpret_cast(bytes.data()); + if (!code || + bytes.size() != sizeof(mach_exception_data_type_t) * code_count) { + LOG(ERROR) << "Invalid mach exception code."; + } else { + // TODO: rationalize with the macOS implementation. + for (mach_msg_type_number_t code_index = 0; code_index < code_count; + ++code_index) { + codes_.push_back(code[code_index]); + } + exception_info_ = code[0]; + exception_address_ = code[1]; + } + } + + if (thread_list) { + for (const auto& other_thread : *thread_list) { + uint64_t other_thread_id; + if (GetDataValueFromMap( + other_thread.get(), Key::kThreadID, &other_thread_id)) { + if (thread_id_ == other_thread_id) { + LoadContextFromThread(exception_data, other_thread.get()); + break; + } + } + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ExceptionSnapshotIOSIntermediateDump::InitializeFromNSException( + const IOSIntermediateDumpMap* exception_data, + const IOSIntermediateDumpList* thread_list) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + DCHECK(exception_data); + + exception_ = EXC_SOFTWARE; + exception_info_ = 0xDEADC0DE; /* uncaught NSException */ + + if (!GetDataValueFromMap(exception_data, Key::kThreadID, &thread_id_)) { + LOG(ERROR) << "Exceptions require a thread id."; + return false; + } + + if (thread_list) { + for (const auto& other_thread : *thread_list) { + uint64_t other_thread_id; + if (GetDataValueFromMap( + other_thread.get(), Key::kThreadID, &other_thread_id)) { + if (thread_id_ == other_thread_id) { + const IOSIntermediateDumpData* uncaught_exceptions = + other_thread->GetAsData(Key::kThreadUncaughtNSExceptionFrames); + if (uncaught_exceptions) { + LoadContextFromUncaughtNSExceptionFrames(uncaught_exceptions, + other_thread.get()); + } else { + LoadContextFromThread(exception_data, other_thread.get()); + } + break; + } + } + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ExceptionSnapshotIOSIntermediateDump::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +uint64_t ExceptionSnapshotIOSIntermediateDump::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +uint32_t ExceptionSnapshotIOSIntermediateDump::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_; +} + +uint32_t ExceptionSnapshotIOSIntermediateDump::ExceptionInfo() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_info_; +} + +uint64_t ExceptionSnapshotIOSIntermediateDump::ExceptionAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_address_; +} + +const std::vector& ExceptionSnapshotIOSIntermediateDump::Codes() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return codes_; +} + +std::vector +ExceptionSnapshotIOSIntermediateDump::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +void ExceptionSnapshotIOSIntermediateDump::LoadContextFromThread( + const IOSIntermediateDumpMap* exception_data, + const IOSIntermediateDumpMap* other_thread) { +#if defined(ARCH_CPU_X86_64) + typedef x86_thread_state64_t thread_state_type; + typedef x86_float_state64_t float_state_type; + typedef x86_debug_state64_t debug_state_type; +#elif defined(ARCH_CPU_ARM64) + typedef arm_thread_state64_t thread_state_type; + typedef arm_neon_state64_t float_state_type; + typedef arm_debug_state64_t debug_state_type; +#endif + + thread_state_type thread_state; + float_state_type float_state; + debug_state_type debug_state; + + mach_msg_type_number_t state_count = 0; + thread_state_flavor_t flavor = THREAD_STATE_NONE; + if (GetDataValueFromMap(exception_data, Key::kStateCount, &state_count) && + GetDataValueFromMap(exception_data, Key::kFlavor, &flavor) && + GetDataValueFromMap(other_thread, Key::kThreadState, &thread_state) && + GetDataValueFromMap(other_thread, Key::kFloatState, &float_state) && + GetDataValueFromMap(other_thread, Key::kDebugState, &debug_state)) { + size_t expected_length = ThreadStateLengthForFlavor(flavor); + const IOSIntermediateDumpData* state_dump = + GetDataFromMap(exception_data, Key::kState); + if (state_dump) { + const std::vector& bytes = state_dump->bytes(); + size_t actual_length = bytes.size(); + if (expected_length == actual_length) { + const ConstThreadState state = + reinterpret_cast(bytes.data()); +#if defined(ARCH_CPU_X86_64) + InitializeCPUContextX86_64(&context_x86_64_, + flavor, + state, + state_count, + &thread_state, + &float_state, + &debug_state); +#elif defined(ARCH_CPU_ARM64) + InitializeCPUContextARM64(&context_arm64_, + flavor, + state, + state_count, + &thread_state, + &float_state, + &debug_state); +#else +#error Port to your CPU architecture +#endif + } + } + } + + // Normally, for EXC_BAD_ACCESS exceptions, the exception address is present + // in code[1]. It may or may not be the instruction pointer address (usually + // it’s not). code[1] may carry the exception address for other exception + // types too, but it’s not guaranteed. But for all other exception types, the + // instruction pointer will be the exception address, and in fact will be + // equal to codes[1] when it’s carrying the exception address. In those cases, + // just use the instruction pointer directly. + bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS; + +#if defined(ARCH_CPU_X86_64) + // For x86 and x86_64 EXC_BAD_ACCESS exceptions, some code[0] values + // indicate that code[1] does not (or may not) carry the exception address: + // EXC_I386_GPFLT (10.9.5 xnu-2422.115.4/osfmk/i386/trap.c user_trap() for + // T_GENERAL_PROTECTION) and the oddball (VM_PROT_READ | VM_PROT_EXECUTE) + // which collides with EXC_I386_BOUNDFLT (10.9.5 + // xnu-2422.115.4/osfmk/i386/fpu.c fpextovrflt()). Other EXC_BAD_ACCESS + // exceptions come through 10.9.5 xnu-2422.115.4/osfmk/i386/trap.c + // user_page_fault_continue() and do contain the exception address in + // code[1]. + if (exception_ == EXC_BAD_ACCESS && + (exception_info_ == EXC_I386_GPFLT || + exception_info_ == (VM_PROT_READ | VM_PROT_EXECUTE))) { + code_1_is_exception_address = false; + } +#endif + + if (!code_1_is_exception_address) { + exception_address_ = context_.InstructionPointer(); + } +} + +void ExceptionSnapshotIOSIntermediateDump:: + LoadContextFromUncaughtNSExceptionFrames( + const IOSIntermediateDumpData* frames_dump, + const IOSIntermediateDumpMap* other_thread) { + const std::vector& bytes = frames_dump->bytes(); + const uint64_t* frames = reinterpret_cast(bytes.data()); + size_t num_frames = bytes.size() / sizeof(uint64_t); + if (num_frames < 2) { + return; + } + +#if defined(ARCH_CPU_X86_64) + context_x86_64_ = {}; + context_x86_64_.rip = frames[0]; // instruction pointer + context_x86_64_.rsp = frames[1]; +#elif defined(ARCH_CPU_ARM64) + context_arm64_ = {}; + context_arm64_.sp = 0; + context_arm64_.pc = frames[0]; + context_arm64_.regs[30] = frames[1]; // link register + context_arm64_.regs[29] = sizeof(uintptr_t); // function pointers +#else +#error Port to your CPU architecture +#endif + + exception_address_ = frames[0]; +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/ios/exception_snapshot_ios.h b/snapshot/ios/exception_snapshot_ios_intermediate_dump.h similarity index 50% rename from snapshot/ios/exception_snapshot_ios.h rename to snapshot/ios/exception_snapshot_ios_intermediate_dump.h index 5c1337db..88d5ea6c 100644 --- a/snapshot/ios/exception_snapshot_ios.h +++ b/snapshot/ios/exception_snapshot_ios_intermediate_dump.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CRASHPAD_SNAPSHOT_IOS_EXCEPTION_SNAPSHOT_IOS_H_ -#define CRASHPAD_SNAPSHOT_IOS_EXCEPTION_SNAPSHOT_IOS_H_ +#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_EXCEPTION_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ +#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_EXCEPTION_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ #include #include @@ -24,6 +24,7 @@ #include "build/build_config.h" #include "snapshot/cpu_context.h" #include "snapshot/exception_snapshot.h" +#include "util/ios/ios_intermediate_dump_map.h" #include "util/mach/mach_extensions.h" #include "util/misc/initialization_state_dcheck.h" @@ -33,32 +34,46 @@ namespace internal { //! \brief An ExceptionSnapshot of an exception sustained by a running (or //! crashed) process on an iOS system. -class ExceptionSnapshotIOS final : public ExceptionSnapshot { +class ExceptionSnapshotIOSIntermediateDump final : public ExceptionSnapshot { public: - ExceptionSnapshotIOS(); - ~ExceptionSnapshotIOS() override; + ExceptionSnapshotIOSIntermediateDump(); + ~ExceptionSnapshotIOSIntermediateDump() override; - //! \brief Initializes the object from a signal. + //! \brief Initialize the snapshot as a signal exception. + //! + //! \param[in] exception_data The intermediate dump map used to initialize + //! this object. //! //! \return `true` if the snapshot could be created, `false` otherwise with //! an appropriate message logged. - void InitializeFromSignal(const siginfo_t* siginfo, - const ucontext_t* context); + bool InitializeFromSignal(const IOSIntermediateDumpMap* exception_data); - //! \brief Initialize the object from a Mach exception for the current task. + //! \brief Initialize the object as a Mach exception from an intermediate + //! dump. + //! + //! \param[in] exception_data The intermediate dump map used to initialize + //! this object. + //! \param[in] thread_list The intermediate dump map containing list of + //! threads. //! //! \return `true` if the snapshot could be created, `false` otherwise with //! an appropriate message logged. - void InitializeFromMachException(exception_behavior_t behavior, - thread_t exception_thread, - exception_type_t exception, - const mach_exception_data_type_t* code, - mach_msg_type_number_t code_count, - thread_state_flavor_t flavor, - ConstThreadState state, - mach_msg_type_number_t state_count); + bool InitializeFromMachException(const IOSIntermediateDumpMap* exception_data, + const IOSIntermediateDumpList* thread_list); + + //! \brief Initialize the object as an NSException from an intermediate dump. + //! + //! \param[in] exception_data The intermediate dump map used to initialize + //! this object. + //! \param[in] thread_list The intermediate dump map containing list of + //! threads. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool InitializeFromNSException(const IOSIntermediateDumpMap* exception_data, + const IOSIntermediateDumpList* thread_list); + // ExceptionSnapshot: - const CPUContext* Context() const override; uint64_t ThreadID() const override; uint32_t Exception() const override; @@ -68,6 +83,11 @@ class ExceptionSnapshotIOS final : public ExceptionSnapshot { virtual std::vector ExtraMemory() const override; private: + void LoadContextFromUncaughtNSExceptionFrames( + const IOSIntermediateDumpData* data, + const IOSIntermediateDumpMap* other_thread); + void LoadContextFromThread(const IOSIntermediateDumpMap* exception_data, + const IOSIntermediateDumpMap* other_thread); #if defined(ARCH_CPU_X86_64) CPUContextX86_64 context_x86_64_; #elif defined(ARCH_CPU_ARM64) @@ -83,10 +103,10 @@ class ExceptionSnapshotIOS final : public ExceptionSnapshot { uint32_t exception_info_; InitializationStateDcheck initialized_; - DISALLOW_COPY_AND_ASSIGN(ExceptionSnapshotIOS); + DISALLOW_COPY_AND_ASSIGN(ExceptionSnapshotIOSIntermediateDump); }; } // namespace internal } // namespace crashpad -#endif // CRASHPAD_SNAPSHOT_IOS_EXCEPTION_SNAPSHOT_IOS_H_ +#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_EXCEPTION_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ diff --git a/snapshot/ios/intermediate_dump_reader_util.cc b/snapshot/ios/intermediate_dump_reader_util.cc new file mode 100644 index 00000000..74c0842c --- /dev/null +++ b/snapshot/ios/intermediate_dump_reader_util.cc @@ -0,0 +1,93 @@ +// Copyright 2021 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/intermediate_dump_reader_util.h" + +#include "util/ios/ios_intermediate_dump_data.h" +#include "util/ios/ios_intermediate_dump_map.h" +#include "util/misc/metrics.h" + +namespace crashpad { +namespace internal { + +std::ostream& operator<<(std::ostream& os, const IntermediateDumpKey& t) { + switch (t) { +#define X(Name, Value) \ + case IntermediateDumpKey::Name: \ + os << #Name; \ + break; + INTERMEDIATE_DUMP_KEYS(X) +#undef X + } + return os; +} + +const IOSIntermediateDumpData* GetDataFromMap( + const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key, + LogMissingDataValueFromMap logging) { + const IOSIntermediateDumpData* data = map->GetAsData(key); + if (!data) { + if (logging != LogMissingDataValueFromMap::kDontLogIfMissing) { + LOG(ERROR) << "Missing expected data for key " << key; + Metrics::MissingIntermediateDumpKey(key); + } + return nullptr; + } + return data; +} + +const IOSIntermediateDumpMap* GetMapFromMap(const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key) { + const IOSIntermediateDumpMap* map_dump = map->GetAsMap(key); + if (!map_dump) { + LOG(ERROR) << "Missing expected map for key " << key; + Metrics::MissingIntermediateDumpKey(key); + return nullptr; + } + return map_dump; +} + +const IOSIntermediateDumpList* GetListFromMap(const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key) { + const IOSIntermediateDumpList* list = map->GetAsList(key); + if (!list) { + LOG(ERROR) << "Missing expected list for key " << key; + Metrics::MissingIntermediateDumpKey(key); + return nullptr; + } + return list; +} + +bool GetDataStringFromMap(const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key, + std::string* value) { + const IOSIntermediateDumpData* data = GetDataFromMap(map, key); + if (!data) { + LOG(ERROR) << "Missing expected string for key " << key; + Metrics::MissingIntermediateDumpKey(key); + return false; + } + + *value = data->GetString(); + return true; +} + +void GetDataValueFromMapErrorInternal(const IntermediateDumpKey& key) { + LOG(ERROR) << "Invalid key size: " << key; + Metrics::InvalidIntermediateDumpKeySize(key); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/ios/intermediate_dump_reader_util.h b/snapshot/ios/intermediate_dump_reader_util.h new file mode 100644 index 00000000..4d33d965 --- /dev/null +++ b/snapshot/ios/intermediate_dump_reader_util.h @@ -0,0 +1,116 @@ +// Copyright 2021 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 + +#include "base/logging.h" +#include "util/ios/ios_intermediate_dump_data.h" +#include "util/ios/ios_intermediate_dump_map.h" + +#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_READER_UTILS_H_ +#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_READER_UTILS_H_ + +namespace crashpad { + +namespace internal { + +//! \brief Overload the ostream output operator to make logged keys readable. +std::ostream& operator<<(std::ostream& os, const IntermediateDumpKey& t); + +//! \brief Determine if GetDataFromMap will log and report missing keys. +enum class LogMissingDataValueFromMap : bool { + //! \brief Do not log an error and report to UMA if a key is missing. + kDontLogIfMissing, + + //! \brief Log an error and report to UMA if a key is missing. + kLogIfMissing, +}; + +//! \brief Call GetAsData with error and UMA logging. +//! +//! \param[in] map The map to load from. +//! \param[in] key The key to load from \a map. +//! \param[in] logging This call will log missing keys unless \a logging is +//! LogDataValueFromMap::kDontLogIfMissing +//! +//! \return The IOSIntermediateDumpData pointer or a nullptr; +const IOSIntermediateDumpData* GetDataFromMap( + const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key, + LogMissingDataValueFromMap logging = + LogMissingDataValueFromMap::kLogIfMissing); + +//! \brief Call GetAsMap with error and UMA logging. +//! +//! \param[in] map The map to load from. +//! \param[in] key The key to load from \a map. +//! +//! \return The IOSIntermediateDumpMap pointer or a nullptr; +const IOSIntermediateDumpMap* GetMapFromMap(const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key); + +//! \brief Call GetAsList with error and UMA logging. +//! +//! \param[in] map The map to load from. +//! \param[in] key The key to load from \a map. +//! +//! \return The IOSIntermediateDumpList pointer or a nullptr; +const IOSIntermediateDumpList* GetListFromMap(const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key); + +//! \brief Call GetAsList with error and UMA logging. +//! +//! \param[in] map The map to load from. +//! \param[in] key The key to load from \a map. +//! +//! \return Returns `true` if the string could be loaded, otherwise returns +//! `false` and logs an error. +bool GetDataStringFromMap(const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key, + std::string* value); + +//! \brief Log key size error and record error with UMA. +void GetDataValueFromMapErrorInternal(const IntermediateDumpKey& key); + +//! \brief Call GetAsData and GetValue with error and UMA logging. +//! +//! \param[in] map The map to load from. +//! \param[in] key The key to load from \a map. +//! \param[out] value The data to populate. +//! \param[in] logging This call will log missing keys unless \a logging is +//! LogDataValueFromMap::kDontLogIfMissing. This call will always log +//! keys with an invalid size. +//! +//! \return On success, returns `true`, otherwise returns `false`. +template +bool GetDataValueFromMap(const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key, + T* value, + LogMissingDataValueFromMap logging = + LogMissingDataValueFromMap::kLogIfMissing) { + const IOSIntermediateDumpData* data = GetDataFromMap(map, key, logging); + if (!data) { + return false; + } + if (!data->GetValue(value)) { + GetDataValueFromMapErrorInternal(key); + return false; + } + return true; +} + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_READER_UTILS_H_ diff --git a/snapshot/ios/memory_snapshot_ios.cc b/snapshot/ios/memory_snapshot_ios_intermediate_dump.cc similarity index 62% rename from snapshot/ios/memory_snapshot_ios.cc rename to snapshot/ios/memory_snapshot_ios_intermediate_dump.cc index e760465d..8e521d90 100644 --- a/snapshot/ios/memory_snapshot_ios.cc +++ b/snapshot/ios/memory_snapshot_ios_intermediate_dump.cc @@ -12,52 +12,50 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "snapshot/ios/memory_snapshot_ios.h" +#include "snapshot/ios/memory_snapshot_ios_intermediate_dump.h" namespace crashpad { namespace internal { -void MemorySnapshotIOS::Initialize(vm_address_t address, vm_size_t size) { +void MemorySnapshotIOSIntermediateDump::Initialize(vm_address_t address, + vm_address_t data, + vm_size_t size) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); address_ = address; + data_ = data; size_ = base::checked_cast(size); - - // TODO(justincohen): This is temporary, as MemorySnapshotIOS will likely be - // able to point directly to the deserialized data dump rather than copying - // data around. - buffer_ = std::unique_ptr(new uint8_t[size_]); - memcpy(buffer_.get(), reinterpret_cast(address_), size_); INITIALIZATION_STATE_SET_VALID(initialized_); } -uint64_t MemorySnapshotIOS::Address() const { +uint64_t MemorySnapshotIOSIntermediateDump::Address() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return address_; } -size_t MemorySnapshotIOS::Size() const { +size_t MemorySnapshotIOSIntermediateDump::Size() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return size_; } -bool MemorySnapshotIOS::Read(Delegate* delegate) const { +bool MemorySnapshotIOSIntermediateDump::Read(Delegate* delegate) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); if (size_ == 0) { return delegate->MemorySnapshotDelegateRead(nullptr, size_); } - return delegate->MemorySnapshotDelegateRead(buffer_.get(), size_); + return delegate->MemorySnapshotDelegateRead(reinterpret_cast(data_), + size_); } -const MemorySnapshot* MemorySnapshotIOS::MergeWithOtherSnapshot( +const MemorySnapshot* MemorySnapshotIOSIntermediateDump::MergeWithOtherSnapshot( const MemorySnapshot* other) const { CheckedRange merged(0, 0); if (!LoggingDetermineMergedRange(this, other, &merged)) return nullptr; - auto result = std::make_unique(); - result->Initialize(merged.base(), merged.size()); + auto result = std::make_unique(); + result->Initialize(merged.base(), data_, merged.size()); return result.release(); } diff --git a/snapshot/ios/memory_snapshot_ios.h b/snapshot/ios/memory_snapshot_ios_intermediate_dump.h similarity index 72% rename from snapshot/ios/memory_snapshot_ios.h rename to snapshot/ios/memory_snapshot_ios_intermediate_dump.h index be991056..895cbd0d 100644 --- a/snapshot/ios/memory_snapshot_ios.h +++ b/snapshot/ios/memory_snapshot_ios_intermediate_dump.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CRASHPAD_SNAPSHOT_IOS_MEMORY_SNAPSHOT_IOS_H_ -#define CRASHPAD_SNAPSHOT_IOS_MEMORY_SNAPSHOT_IOS_H_ +#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MEMORY_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ +#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MEMORY_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ #include "base/macros.h" #include "snapshot/memory_snapshot.h" @@ -24,16 +24,16 @@ namespace crashpad { namespace internal { //! \brief A MemorySnapshot of a memory region. -class MemorySnapshotIOS final : public MemorySnapshot { +class MemorySnapshotIOSIntermediateDump final : public MemorySnapshot { public: - MemorySnapshotIOS() = default; - ~MemorySnapshotIOS() = default; + MemorySnapshotIOSIntermediateDump() = default; + ~MemorySnapshotIOSIntermediateDump() = default; //! \brief Initializes the object. //! //! \param[in] address The base address of the memory region to snapshot. //! \param[in] size The size of the memory region to snapshot. - void Initialize(vm_address_t address, vm_size_t size); + void Initialize(vm_address_t address, vm_address_t data, vm_size_t size); // MemorySnapshot: uint64_t Address() const override; @@ -48,16 +48,15 @@ class MemorySnapshotIOS final : public MemorySnapshot { const T* self, const MemorySnapshot* other); - // TODO(justincohen): This is temporary until deserialization is worked out. - std::unique_ptr buffer_; vm_address_t address_; + vm_address_t data_; vm_size_t size_; InitializationStateDcheck initialized_; - DISALLOW_COPY_AND_ASSIGN(MemorySnapshotIOS); + DISALLOW_COPY_AND_ASSIGN(MemorySnapshotIOSIntermediateDump); }; } // namespace internal } // namespace crashpad -#endif // CRASHPAD_SNAPSHOT_IOS_MEMORY_SNAPSHOT_IOS_H_ +#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MEMORY_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ diff --git a/snapshot/ios/module_snapshot_ios.cc b/snapshot/ios/module_snapshot_ios.cc deleted file mode 100644 index 8b251eb9..00000000 --- a/snapshot/ios/module_snapshot_ios.cc +++ /dev/null @@ -1,238 +0,0 @@ -// 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/module_snapshot_ios.h" - -#include -#include - -#include "base/files/file_path.h" -#include "base/mac/mach_logging.h" -#include "util/misc/from_pointer_cast.h" -#include "util/misc/uuid.h" - -namespace crashpad { -namespace internal { - -ModuleSnapshotIOS::ModuleSnapshotIOS() - : ModuleSnapshot(), - name_(), - address_(0), - size_(0), - timestamp_(0), - dylib_version_(0), - source_version_(0), - filetype_(0), - initialized_() {} - -ModuleSnapshotIOS::~ModuleSnapshotIOS() {} - -// static. -const dyld_all_image_infos* ModuleSnapshotIOS::DyldAllImageInfo() { - task_dyld_info_data_t dyld_info; - mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; - - kern_return_t kr = task_info(mach_task_self(), - TASK_DYLD_INFO, - reinterpret_cast(&dyld_info), - &count); - if (kr != KERN_SUCCESS) { - MACH_LOG(WARNING, kr) << "task_info"; - return 0; - } - - return reinterpret_cast(dyld_info.all_image_info_addr); -} - -bool ModuleSnapshotIOS::InitializeDyld(const dyld_all_image_infos* images) { - INITIALIZATION_STATE_SET_INITIALIZING(initialized_); - - name_ = images->dyldPath; - address_ = FromPointerCast(images->dyldImageLoadAddress); - return FinishInitialization(); -} - -bool ModuleSnapshotIOS::Initialize(const dyld_image_info* image) { - INITIALIZATION_STATE_SET_INITIALIZING(initialized_); - - name_ = image->imageFilePath; - address_ = FromPointerCast(image->imageLoadAddress); - timestamp_ = image->imageFileModDate; - return FinishInitialization(); -} - -bool ModuleSnapshotIOS::FinishInitialization() { -#ifndef ARCH_CPU_64_BITS -#error Only 64-bit Mach-O is supported -#endif - DCHECK(address_); - const mach_header_64* header = - reinterpret_cast(address_); - const load_command* command = - reinterpret_cast(header + 1); - // Make sure that the basic load command structure doesn’t overflow the - // space allotted for load commands, as well as iterating through ncmds. - for (uint32_t cmd_index = 0, cumulative_cmd_size = 0; - cmd_index <= header->ncmds && cumulative_cmd_size < header->sizeofcmds; - ++cmd_index, cumulative_cmd_size += command->cmdsize) { - if (command->cmd == LC_SEGMENT_64) { - const segment_command_64* segment = - reinterpret_cast(command); - if (strcmp(segment->segname, SEG_TEXT) == 0) { - size_ = segment->vmsize; - } - } else if (command->cmd == LC_ID_DYLIB) { - const dylib_command* dylib = - reinterpret_cast(command); - dylib_version_ = dylib->dylib.current_version; - } else if (command->cmd == LC_SOURCE_VERSION) { - const source_version_command* source_version = - reinterpret_cast(command); - source_version_ = source_version->version; - } else if (command->cmd == LC_UUID) { - const uuid_command* uuid = reinterpret_cast(command); - uuid_.InitializeFromBytes(uuid->uuid); - } - - command = reinterpret_cast( - reinterpret_cast(command) + command->cmdsize); - - // TODO(justincohen): Warn-able things: - // - Bad Mach-O magic (and give up trying to process the module) - // - Unrecognized Mach-O type - // - No SEG_TEXT - // - More than one SEG_TEXT - // - More than one LC_ID_DYLIB, LC_SOURCE_VERSION, or LC_UUID - // - No LC_ID_DYLIB in a dylib file - // - LC_ID_DYLIB in a non-dylib file - // And more optional: - // - Missing LC_UUID (although it leaves us with a big "?") - // - Missing LC_SOURCE_VERSION. - } - - filetype_ = header->filetype; - - INITIALIZATION_STATE_SET_VALID(initialized_); - return true; -} - -std::string ModuleSnapshotIOS::Name() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return name_; -} - -uint64_t ModuleSnapshotIOS::Address() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return address_; -} - -uint64_t ModuleSnapshotIOS::Size() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return size_; -} - -time_t ModuleSnapshotIOS::Timestamp() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return timestamp_; -} - -void ModuleSnapshotIOS::FileVersion(uint16_t* version_0, - uint16_t* version_1, - uint16_t* version_2, - uint16_t* version_3) const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - if (filetype_ == MH_DYLIB) { - *version_0 = (dylib_version_ & 0xffff0000) >> 16; - *version_1 = (dylib_version_ & 0x0000ff00) >> 8; - *version_2 = (dylib_version_ & 0x000000ff); - *version_3 = 0; - } else { - *version_0 = 0; - *version_1 = 0; - *version_2 = 0; - *version_3 = 0; - } -} - -void ModuleSnapshotIOS::SourceVersion(uint16_t* version_0, - uint16_t* version_1, - uint16_t* version_2, - uint16_t* version_3) const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - *version_0 = (source_version_ & 0xffff000000000000u) >> 48; - *version_1 = (source_version_ & 0x0000ffff00000000u) >> 32; - *version_2 = (source_version_ & 0x00000000ffff0000u) >> 16; - *version_3 = source_version_ & 0x000000000000ffffu; -} - -ModuleSnapshot::ModuleType ModuleSnapshotIOS::GetModuleType() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - switch (filetype_) { - case MH_EXECUTE: - return kModuleTypeExecutable; - case MH_DYLIB: - return kModuleTypeSharedLibrary; - case MH_DYLINKER: - return kModuleTypeDynamicLoader; - case MH_BUNDLE: - return kModuleTypeLoadableModule; - default: - return kModuleTypeUnknown; - } -} - -void ModuleSnapshotIOS::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - *uuid = uuid_; - *age = 0; -} - -std::string ModuleSnapshotIOS::DebugFileName() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return base::FilePath(Name()).BaseName().value(); -} - -std::vector ModuleSnapshotIOS::BuildID() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return std::vector(); -} - -std::vector ModuleSnapshotIOS::AnnotationsVector() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return std::vector(); -} - -std::map ModuleSnapshotIOS::AnnotationsSimpleMap() - const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return std::map(); -} - -std::vector ModuleSnapshotIOS::AnnotationObjects() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return std::vector(); -} - -std::set> ModuleSnapshotIOS::ExtraMemoryRanges() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return std::set>(); -} - -std::vector -ModuleSnapshotIOS::CustomMinidumpStreams() const { - return std::vector(); -} - -} // namespace internal -} // namespace crashpad diff --git a/snapshot/ios/module_snapshot_ios_intermediate_dump.cc b/snapshot/ios/module_snapshot_ios_intermediate_dump.cc new file mode 100644 index 00000000..cdef7697 --- /dev/null +++ b/snapshot/ios/module_snapshot_ios_intermediate_dump.cc @@ -0,0 +1,266 @@ +// 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/module_snapshot_ios_intermediate_dump.h" + +#include +#include + +#include "base/files/file_path.h" +#include "base/mac/mach_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/misc/from_pointer_cast.h" +#include "util/misc/uuid.h" + +namespace crashpad { +namespace internal { + +using Key = IntermediateDumpKey; + +ModuleSnapshotIOSIntermediateDump::ModuleSnapshotIOSIntermediateDump() + : ModuleSnapshot(), + name_(), + address_(0), + size_(0), + timestamp_(0), + dylib_version_(0), + source_version_(0), + filetype_(0), + initialized_() {} + +ModuleSnapshotIOSIntermediateDump::~ModuleSnapshotIOSIntermediateDump() {} + +bool ModuleSnapshotIOSIntermediateDump::Initialize( + const IOSIntermediateDumpMap* image_data) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + GetDataStringFromMap(image_data, Key::kName, &name_); + GetDataValueFromMap(image_data, Key::kAddress, &address_); + GetDataValueFromMap(image_data, Key::kSize, &size_); + GetDataValueFromMap(image_data, Key::kSourceVersion, &source_version_); + GetDataValueFromMap(image_data, Key::kFileType, &filetype_); + + // These keys are often missing. + GetDataValueFromMap(image_data, + Key::kTimestamp, + ×tamp_, + LogMissingDataValueFromMap::kDontLogIfMissing); + GetDataValueFromMap(image_data, + Key::kDylibCurrentVersion, + &dylib_version_, + LogMissingDataValueFromMap::kDontLogIfMissing); + + const IOSIntermediateDumpData* uuid_dump = + GetDataFromMap(image_data, IntermediateDumpKey::kUUID); + if (uuid_dump) { + const std::vector& bytes = uuid_dump->bytes(); + if (!bytes.data() || bytes.size() != 16) { + LOG(ERROR) << "Invalid module uuid."; + } else { + uuid_.InitializeFromBytes(bytes.data()); + } + } + + const IOSIntermediateDumpList* annotation_list = + image_data->GetAsList(IntermediateDumpKey::kAnnotationObjects); + if (annotation_list) { + for (auto& annotation : *annotation_list) { + std::string name; + if (!GetDataStringFromMap( + annotation.get(), Key::kAnnotationName, &name) || + name.empty() || name.length() > 64) { // Annotation::kNameMaxLength + LOG(ERROR) << "Invalid annotation name length."; + continue; + } + + uint16_t type; + const IOSIntermediateDumpData* type_dump = + annotation->GetAsData(IntermediateDumpKey::kAnnotationType); + const IOSIntermediateDumpData* value_dump = + annotation->GetAsData(IntermediateDumpKey::kAnnotationValue); + if (type_dump && value_dump && type_dump->GetValue(&type)) { + const std::vector& bytes = value_dump->bytes(); + uint64_t length = bytes.size(); + if (!bytes.data() || length > 20480) { // Annotation::kValueMaxSize + LOG(ERROR) << "Invalid annotation value length."; + continue; + } + annotation_objects_.push_back(AnnotationSnapshot(name, type, bytes)); + } + } + } + + const IOSIntermediateDumpList* simple_map_dump = + image_data->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* crash_info_dump = + image_data->GetAsMap(IntermediateDumpKey::kAnnotationsCrashInfo); + if (crash_info_dump) { + const IOSIntermediateDumpData* message1_dump = crash_info_dump->GetAsData( + IntermediateDumpKey::kAnnotationsCrashInfoMessage1); + if (message1_dump) { + std::string message1 = message1_dump->GetString(); + if (!message1.empty()) + annotations_vector_.push_back(message1); + } + const IOSIntermediateDumpData* message2_dump = crash_info_dump->GetAsData( + IntermediateDumpKey::kAnnotationsCrashInfoMessage2); + if (message2_dump) { + std::string message2 = message2_dump->GetString(); + if (!message2.empty()) + annotations_vector_.push_back(message2); + } + } + + const IOSIntermediateDumpData* dyld_error_dump = + image_data->GetAsData(IntermediateDumpKey::kAnnotationsDyldErrorString); + if (dyld_error_dump) { + std::string dyld_error_string = dyld_error_dump->GetString(); + if (!dyld_error_string.empty()) + annotations_vector_.push_back(dyld_error_string); + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +std::string ModuleSnapshotIOSIntermediateDump::Name() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return name_; +} + +uint64_t ModuleSnapshotIOSIntermediateDump::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return address_; +} + +uint64_t ModuleSnapshotIOSIntermediateDump::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return size_; +} + +time_t ModuleSnapshotIOSIntermediateDump::Timestamp() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return timestamp_; +} + +void ModuleSnapshotIOSIntermediateDump::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (filetype_ == MH_DYLIB) { + *version_0 = (dylib_version_ & 0xffff0000) >> 16; + *version_1 = (dylib_version_ & 0x0000ff00) >> 8; + *version_2 = (dylib_version_ & 0x000000ff); + *version_3 = 0; + } else { + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; + } +} + +void ModuleSnapshotIOSIntermediateDump::SourceVersion( + uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *version_0 = (source_version_ & 0xffff000000000000u) >> 48; + *version_1 = (source_version_ & 0x0000ffff00000000u) >> 32; + *version_2 = (source_version_ & 0x00000000ffff0000u) >> 16; + *version_3 = source_version_ & 0x000000000000ffffu; +} + +ModuleSnapshot::ModuleType ModuleSnapshotIOSIntermediateDump::GetModuleType() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + switch (filetype_) { + case MH_EXECUTE: + return kModuleTypeExecutable; + case MH_DYLIB: + return kModuleTypeSharedLibrary; + case MH_DYLINKER: + return kModuleTypeDynamicLoader; + case MH_BUNDLE: + return kModuleTypeLoadableModule; + default: + return kModuleTypeUnknown; + } +} + +void ModuleSnapshotIOSIntermediateDump::UUIDAndAge(crashpad::UUID* uuid, + uint32_t* age) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *uuid = uuid_; + *age = 0; +} + +std::string ModuleSnapshotIOSIntermediateDump::DebugFileName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return base::FilePath(Name()).BaseName().value(); +} + +std::vector ModuleSnapshotIOSIntermediateDump::BuildID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +std::vector ModuleSnapshotIOSIntermediateDump::AnnotationsVector() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_vector_; +} + +std::map +ModuleSnapshotIOSIntermediateDump::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_simple_map_; +} + +std::vector +ModuleSnapshotIOSIntermediateDump::AnnotationObjects() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotation_objects_; +} + +std::set> +ModuleSnapshotIOSIntermediateDump::ExtraMemoryRanges() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::set>(); +} + +std::vector +ModuleSnapshotIOSIntermediateDump::CustomMinidumpStreams() const { + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/ios/module_snapshot_ios.h b/snapshot/ios/module_snapshot_ios_intermediate_dump.h similarity index 61% rename from snapshot/ios/module_snapshot_ios.h rename to snapshot/ios/module_snapshot_ios_intermediate_dump.h index 505c08f8..cd85c44f 100644 --- a/snapshot/ios/module_snapshot_ios.h +++ b/snapshot/ios/module_snapshot_ios_intermediate_dump.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CRASHPAD_SNAPSHOT_IOS_MODULE_SNAPSHOT_IOS_H_ -#define CRASHPAD_SNAPSHOT_IOS_MODULE_SNAPSHOT_IOS_H_ +#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MODULE_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ +#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MODULE_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ #include #include @@ -26,6 +26,7 @@ #include "base/macros.h" #include "snapshot/crashpad_info_client_options.h" #include "snapshot/module_snapshot.h" +#include "util/ios/ios_intermediate_dump_map.h" #include "util/misc/initialization_state_dcheck.h" namespace crashpad { @@ -33,37 +34,18 @@ namespace internal { //! \brief A ModuleSnapshot of a code module (binary image) loaded into a //! running (or crashed) process on an iOS system. -class ModuleSnapshotIOS final : public ModuleSnapshot { +class ModuleSnapshotIOSIntermediateDump final : public ModuleSnapshot { public: - ModuleSnapshotIOS(); - ~ModuleSnapshotIOS() override; + ModuleSnapshotIOSIntermediateDump(); + ~ModuleSnapshotIOSIntermediateDump() override; - // TODO(justincohen): This function is temporary, and will be broken into two - // parts. One to do an in-process dump of all the relevant information, and - // two to initialize the snapshot after the in-process dump is loaded. - //! \brief Initializes the object. + //! \brief Initialize the snapshot //! - //! \param[in] image The mach-o image to be loaded. + //! \param[in] exception_data The intermediate dump map used to initialize + //! this object. //! //! \return `true` if the snapshot could be created. - bool Initialize(const dyld_image_info* image); - - // TODO(justincohen): This function is temporary, and will be broken into two - // parts. One to do an in-process dump of all the relevant information, and - // two to initialize the snapshot after the in-process dump is loaded. - //! \brief Initializes the object specifically for the dyld module. - //! - //! \param[in] images The structure containing the necessary dyld information. - //! - //! \return `true` if the snapshot could be created. - bool InitializeDyld(const dyld_all_image_infos* images); - - //! \brief Returns options from the module’s CrashpadInfo structure. - //! - //! \param[out] options Options set in the module’s CrashpadInfo structure. - void GetCrashpadOptions(CrashpadInfoClientOptions* options); - - static const dyld_all_image_infos* DyldAllImageInfo(); + bool Initialize(const IOSIntermediateDumpMap* image_data); // ModuleSnapshot: std::string Name() const override; @@ -89,9 +71,6 @@ class ModuleSnapshotIOS final : public ModuleSnapshot { std::vector CustomMinidumpStreams() const override; private: - // Gather the the module information based off of a mach_header_64 |address_|. - bool FinishInitialization(); - std::string name_; uint64_t address_; uint64_t size_; @@ -100,12 +79,16 @@ class ModuleSnapshotIOS final : public ModuleSnapshot { uint64_t source_version_; uint32_t filetype_; UUID uuid_; + std::vector annotations_vector_; + std::map annotations_simple_map_; + std::vector annotation_objects_; + InitializationStateDcheck initialized_; - DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotIOS); + DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotIOSIntermediateDump); }; } // namespace internal } // namespace crashpad -#endif // CRASHPAD_SNAPSHOT_IOS_MODULE_SNAPSHOT_IOS_H_ +#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MODULE_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ diff --git a/snapshot/ios/process_snapshot_ios.cc b/snapshot/ios/process_snapshot_ios.cc deleted file mode 100644 index 9917164d..00000000 --- a/snapshot/ios/process_snapshot_ios.cc +++ /dev/null @@ -1,288 +0,0 @@ -// 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/process_snapshot_ios.h" - -#include -#include - -#include "base/cxx17_backports.h" -#include "base/logging.h" -#include "base/mac/mach_logging.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_(), - exception_(), - report_id_(), - client_id_(), - annotations_simple_map_(), - snapshot_time_(), - initialized_() {} - -ProcessSnapshotIOS::~ProcessSnapshotIOS() {} - -bool ProcessSnapshotIOS::Initialize( - const internal::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(); - - INITIALIZATION_STATE_SET_VALID(initialized_); - return true; -} - -void ProcessSnapshotIOS::SetExceptionFromSignal(const siginfo_t* siginfo, - const ucontext_t* context) { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - DCHECK(!exception_.get()); - - exception_.reset(new internal::ExceptionSnapshotIOS()); - exception_->InitializeFromSignal(siginfo, context); -} - -void ProcessSnapshotIOS::SetExceptionFromMachException( - exception_behavior_t behavior, - thread_t exception_thread, - exception_type_t exception, - const mach_exception_data_type_t* code, - mach_msg_type_number_t code_count, - thread_state_flavor_t flavor, - ConstThreadState old_state, - mach_msg_type_number_t old_state_count) { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - DCHECK(!exception_.get()); - - exception_.reset(new internal::ExceptionSnapshotIOS()); - exception_->InitializeFromMachException(behavior, - exception_thread, - exception, - code, - code_count, - flavor, - old_state, - old_state_count); -} - -pid_t ProcessSnapshotIOS::ProcessID() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return kern_proc_info_.kp_proc.p_pid; -} - -pid_t ProcessSnapshotIOS::ParentProcessID() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return kern_proc_info_.kp_eproc.e_ppid; -} - -void ProcessSnapshotIOS::SnapshotTime(timeval* snapshot_time) const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - *snapshot_time = snapshot_time_; -} - -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 { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - *report_id = report_id_; -} - -void ProcessSnapshotIOS::ClientID(UUID* client_id) const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - *client_id = client_id_; -} - -const std::map& -ProcessSnapshotIOS::AnnotationsSimpleMap() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return annotations_simple_map_; -} - -const SystemSnapshot* ProcessSnapshotIOS::System() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return &system_; -} - -std::vector ProcessSnapshotIOS::Threads() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - std::vector threads; - for (const auto& thread : threads_) { - threads.push_back(thread.get()); - } - return threads; -} - -std::vector ProcessSnapshotIOS::Modules() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - std::vector modules; - for (const auto& module : modules_) { - modules.push_back(module.get()); - } - return modules; -} - -std::vector ProcessSnapshotIOS::UnloadedModules() - const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return std::vector(); -} - -const ExceptionSnapshot* ProcessSnapshotIOS::Exception() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return exception_.get(); -} - -std::vector ProcessSnapshotIOS::MemoryMap() - const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return std::vector(); -} - -std::vector ProcessSnapshotIOS::Handles() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return std::vector(); -} - -std::vector ProcessSnapshotIOS::ExtraMemory() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return std::vector(); -} - -const ProcessMemory* ProcessSnapshotIOS::Memory() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return nullptr; -} - -void ProcessSnapshotIOS::InitializeThreads() { - mach_msg_type_number_t thread_count = 0; - const thread_act_array_t threads = - internal::ThreadSnapshotIOS::GetThreads(&thread_count); - for (uint32_t thread_index = 0; thread_index < thread_count; ++thread_index) { - thread_t thread = threads[thread_index]; - auto thread_snapshot = std::make_unique(); - if (thread_snapshot->Initialize(thread)) { - threads_.push_back(std::move(thread_snapshot)); - } - mach_port_deallocate(mach_task_self(), thread); - } - // TODO(justincohen): This dealloc above and below needs to move with the - // call to task_threads inside internal::ThreadSnapshotIOS::GetThreads. - vm_deallocate(mach_task_self(), - reinterpret_cast(threads), - sizeof(thread_t) * thread_count); -} - -void ProcessSnapshotIOS::InitializeModules() { - const dyld_all_image_infos* image_infos = - internal::ModuleSnapshotIOS::DyldAllImageInfo(); - - uint32_t image_count = image_infos->infoArrayCount; - const dyld_image_info* image_array = image_infos->infoArray; - for (uint32_t image_index = 0; image_index < image_count; ++image_index) { - const dyld_image_info* image = &image_array[image_index]; - auto module = std::make_unique(); - if (module->Initialize(image)) { - modules_.push_back(std::move(module)); - } - } - auto module = std::make_unique(); - if (module->InitializeDyld(image_infos)) { - modules_.push_back(std::move(module)); - } -} - -} // namespace crashpad diff --git a/snapshot/ios/process_snapshot_ios_intermediate_dump.cc b/snapshot/ios/process_snapshot_ios_intermediate_dump.cc new file mode 100644 index 00000000..7f022002 --- /dev/null +++ b/snapshot/ios/process_snapshot_ios_intermediate_dump.cc @@ -0,0 +1,273 @@ +// 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/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::Initialize( + const base::FilePath& dump_path, + const std::map& annotations) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + annotations_simple_map_ = annotations; + + if (!reader_.Initialize(dump_path)) { + return false; + } + + 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(basic_info, Key::kUserTime, &thread_times_user_time_); + GetDataValueFromMap( + basic_info, Key::kSystemTime, &thread_times_system_time_); + } + + GetDataValueFromMap(process_info, Key::kSnapshotTime, &snapshot_time_); + + 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; +} + +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 diff --git a/snapshot/ios/process_snapshot_ios.h b/snapshot/ios/process_snapshot_ios_intermediate_dump.h similarity index 61% rename from snapshot/ios/process_snapshot_ios.h rename to snapshot/ios/process_snapshot_ios_intermediate_dump.h index 63014475..70a4d7da 100644 --- a/snapshot/ios/process_snapshot_ios.h +++ b/snapshot/ios/process_snapshot_ios_intermediate_dump.h @@ -12,54 +12,42 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_ -#define CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_ +#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_PROCESS_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ +#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_PROCESS_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ #include #include -#include "snapshot/ios/exception_snapshot_ios.h" -#include "snapshot/ios/module_snapshot_ios.h" -#include "snapshot/ios/system_snapshot_ios.h" -#include "snapshot/ios/thread_snapshot_ios.h" +#include "base/files/file_path.h" +#include "snapshot/ios/exception_snapshot_ios_intermediate_dump.h" +#include "snapshot/ios/module_snapshot_ios_intermediate_dump.h" +#include "snapshot/ios/process_snapshot_ios_intermediate_dump.h" +#include "snapshot/ios/system_snapshot_ios_intermediate_dump.h" +#include "snapshot/ios/thread_snapshot_ios_intermediate_dump.h" #include "snapshot/process_snapshot.h" #include "snapshot/thread_snapshot.h" #include "snapshot/unloaded_module_snapshot.h" +#include "util/ios/ios_intermediate_dump_reader.h" namespace crashpad { +namespace internal { //! \brief A ProcessSnapshot of a running (or crashed) process running on a //! iphoneOS system. -class ProcessSnapshotIOS final : public ProcessSnapshot { +class ProcessSnapshotIOSIntermediateDump final : public ProcessSnapshot { public: - ProcessSnapshotIOS(); - ~ProcessSnapshotIOS() override; + ProcessSnapshotIOSIntermediateDump() = default; //! \brief Initializes the object. //! - //! \param[in] system_data A class containing various system data points. + //! \param[in] dump_path A class containing various system data points. //! //! \return `true` if the snapshot could be created, `false` otherwise with //! an appropriate message logged. - bool Initialize(const internal::IOSSystemDataCollector& system_data); + bool Initialize(const base::FilePath& dump_path, + const std::map& annotations); - //! \brief Initialize exception information from a signal. - void SetExceptionFromSignal(const siginfo_t* siginfo, - const ucontext_t* context); - - //! \brief Initialize exception information from a Mach exception. - void SetExceptionFromMachException(exception_behavior_t behavior, - thread_t exception_thread, - exception_type_t exception, - const mach_exception_data_type_t* code, - mach_msg_type_number_t code_count, - thread_state_flavor_t flavor, - ConstThreadState old_state, - mach_msg_type_number_t old_state_count); - - //! \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. @@ -93,30 +81,33 @@ class ProcessSnapshotIOS final : public ProcessSnapshot { const ProcessMemory* Memory() const override; private: - // Initializes modules_ on behalf of Initialize(). - void InitializeModules(); - - // Initializes threads_ on behalf of Initialize(). - void InitializeThreads(); - - kinfo_proc kern_proc_info_; + // Retain the reader for the lifetime of the ProcessSnapshot so large chunks + // of data do not need to be copied around (such as MemorySnapshot + // intermediate dumps). + IOSIntermediateDumpReader reader_; + pid_t p_pid_; + pid_t e_ppid_; + timeval p_starttime_; 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_; - std::unique_ptr exception_; + internal::SystemSnapshotIOSIntermediateDump system_; + std::vector> + threads_; + std::vector> + modules_; + std::unique_ptr exception_; UUID report_id_; UUID client_id_; std::map annotations_simple_map_; timeval snapshot_time_; InitializationStateDcheck initialized_; - DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotIOS); + DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotIOSIntermediateDump); }; +} // namespace internal } // namespace crashpad -#endif // CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_ +#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_PROCESS_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ diff --git a/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc b/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc new file mode 100644 index 00000000..1dff5882 --- /dev/null +++ b/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc @@ -0,0 +1,640 @@ +// Copyright 2021 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/process_snapshot_ios_intermediate_dump.h" + +#include + +#include "base/cxx17_backports.h" +#include "base/files/scoped_file.h" +#include "base/posix/eintr_wrapper.h" +#include "client/annotation.h" +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "test/errors.h" +#include "test/scoped_temp_dir.h" +#include "util/file/file_io.h" +#include "util/file/filesystem.h" +#include "util/file/string_file.h" +#include "util/misc/uuid.h" + +namespace crashpad { +namespace test { +namespace { + +using Key = internal::IntermediateDumpKey; +using internal::IOSIntermediateDumpWriter; +using internal::ProcessSnapshotIOSIntermediateDump; + +class ReadToString : public crashpad::MemorySnapshot::Delegate { + public: + std::string result; + + bool MemorySnapshotDelegateRead(void* data, size_t size) override { + result = std::string(reinterpret_cast(data), size); + return true; + } +}; + +class ProcessSnapshotIOSIntermediateDumpTest : public testing::Test { + protected: + // testing::Test: + + void SetUp() override { + path_ = temp_dir_.path().Append("dump_file"); + writer_ = std::make_unique(); + EXPECT_TRUE(writer_->Open(path_)); + ASSERT_TRUE(IsRegularFile(path_)); + } + + void TearDown() override { + writer_.reset(); + EXPECT_FALSE(IsRegularFile(path_)); + } + + const auto& path() const { return path_; } + const auto& annotations() const { return annotations_; } + auto writer() const { return writer_.get(); } + + bool DumpSnapshot(const ProcessSnapshotIOSIntermediateDump& snapshot) { + MinidumpFileWriter minidump; + minidump.InitializeFromSnapshot(&snapshot); + StringFile string_file; + return minidump.WriteEverything(&string_file); + } + + void WriteProcessInfo(IOSIntermediateDumpWriter* writer) { + IOSIntermediateDumpWriter::ScopedMap map(writer, Key::kProcessInfo); + pid_t pid = 2; + pid_t parent = 1; + EXPECT_TRUE(writer->AddProperty(Key::kPID, &pid)); + EXPECT_TRUE(writer->AddProperty(Key::kParentPID, &parent)); + timeval start_time = {12, 0}; + EXPECT_TRUE(writer->AddProperty(Key::kStartTime, &start_time)); + + time_value_t user_time = {20, 0}; + time_value_t system_time = {30, 0}; + { + IOSIntermediateDumpWriter::ScopedMap taskInfo(writer, + Key::kTaskBasicInfo); + EXPECT_TRUE(writer->AddProperty(Key::kUserTime, &user_time)); + EXPECT_TRUE(writer->AddProperty(Key::kSystemTime, &system_time)); + } + { + IOSIntermediateDumpWriter::ScopedMap taskThreadTimesMap( + writer, Key::kTaskThreadTimes); + writer->AddProperty(Key::kUserTime, &user_time); + writer->AddProperty(Key::kSystemTime, &system_time); + } + + timeval snapshot_time = {42, 0}; + writer->AddProperty(Key::kSnapshotTime, &snapshot_time); + } + + void WriteSystemInfo(IOSIntermediateDumpWriter* writer) { + IOSIntermediateDumpWriter::ScopedMap map(writer, Key::kSystemInfo); + std::string machine_description = "Gibson"; + EXPECT_TRUE(writer->AddProperty(Key::kMachineDescription, + machine_description.c_str(), + machine_description.length())); + int os_version_major = 1995; + int os_version_minor = 9; + int os_version_bugfix = 15; + EXPECT_TRUE(writer->AddProperty(Key::kOSVersionMajor, &os_version_major)); + EXPECT_TRUE(writer->AddProperty(Key::kOSVersionMinor, &os_version_minor)); + EXPECT_TRUE(writer->AddProperty(Key::kOSVersionBugfix, &os_version_bugfix)); + std::string os_version_build = "Da Vinci"; + writer->AddProperty(Key::kOSVersionBuild, + os_version_build.c_str(), + os_version_build.length()); + + int cpu_count = 1; + EXPECT_TRUE(writer->AddProperty(Key::kCpuCount, &cpu_count)); + std::string cpu_vendor = "RISC"; + EXPECT_TRUE(writer->AddProperty( + Key::kCpuVendor, cpu_vendor.c_str(), cpu_vendor.length())); + + bool has_daylight_saving_time = true; + EXPECT_TRUE(writer->AddProperty(Key::kHasDaylightSavingTime, + &has_daylight_saving_time)); + bool is_daylight_saving_time = true; + EXPECT_TRUE(writer->AddProperty(Key::kIsDaylightSavingTime, + &is_daylight_saving_time)); + int standard_offset_seconds = 7200; + EXPECT_TRUE(writer->AddProperty(Key::kStandardOffsetSeconds, + &standard_offset_seconds)); + int daylight_offset_seconds = 3600; + EXPECT_TRUE(writer->AddProperty(Key::kDaylightOffsetSeconds, + &daylight_offset_seconds)); + std::string standard_name = "Standard"; + EXPECT_TRUE(writer->AddProperty( + Key::kStandardName, standard_name.c_str(), standard_name.length())); + std::string daylight_name = "Daylight"; + EXPECT_TRUE(writer->AddProperty( + Key::kDaylightName, daylight_name.c_str(), daylight_name.length())); + + vm_size_t page_size = getpagesize(); + EXPECT_TRUE(writer->AddProperty(Key::kPageSize, &page_size)); + { + natural_t count = 0; + IOSIntermediateDumpWriter::ScopedMap vmStatMap(writer, Key::kVMStat); + EXPECT_TRUE(writer->AddProperty(Key::kActive, &count)); + EXPECT_TRUE(writer->AddProperty(Key::kInactive, &count)); + EXPECT_TRUE(writer->AddProperty(Key::kWired, &count)); + EXPECT_TRUE(writer->AddProperty(Key::kFree, &count)); + } + } + + void WriteAnnotations(IOSIntermediateDumpWriter* writer) { + constexpr char annotation_name[] = "annotation_name"; + constexpr char annotation_value[] = "annotation_value"; + { + IOSIntermediateDumpWriter::ScopedArray annotationObjectArray( + writer, Key::kAnnotationObjects); + { + IOSIntermediateDumpWriter::ScopedArrayMap annotationMap(writer); + EXPECT_TRUE(writer->AddPropertyBytes( + Key::kAnnotationName, annotation_name, strlen(annotation_name))); + EXPECT_TRUE(writer->AddPropertyBytes( + Key::kAnnotationValue, annotation_value, strlen(annotation_value))); + Annotation::Type type = Annotation::Type::kString; + EXPECT_TRUE(writer->AddProperty(Key::kAnnotationType, &type)); + } + } + { + IOSIntermediateDumpWriter::ScopedArray annotationsSimpleArray( + writer, Key::kAnnotationsSimpleMap); + { + IOSIntermediateDumpWriter::ScopedArrayMap annotationMap(writer); + EXPECT_TRUE(writer->AddPropertyBytes( + Key::kAnnotationName, annotation_name, strlen(annotation_name))); + EXPECT_TRUE(writer->AddPropertyBytes( + Key::kAnnotationValue, annotation_value, strlen(annotation_value))); + } + } + + IOSIntermediateDumpWriter::ScopedMap annotationMap( + writer, Key::kAnnotationsCrashInfo); + { + EXPECT_TRUE(writer->AddPropertyBytes(Key::kAnnotationsCrashInfoMessage1, + annotation_value, + strlen(annotation_value))); + EXPECT_TRUE(writer->AddPropertyBytes(Key::kAnnotationsCrashInfoMessage2, + annotation_value, + strlen(annotation_value))); + } + } + + void WriteModules(IOSIntermediateDumpWriter* writer) { + IOSIntermediateDumpWriter::ScopedArray moduleArray(writer, Key::kModules); + for (uint32_t image_index = 0; image_index < 2; ++image_index) { + IOSIntermediateDumpWriter::ScopedArrayMap modules(writer); + + constexpr char image_file[] = "/path/to/module"; + EXPECT_TRUE( + writer->AddProperty(Key::kName, image_file, strlen(image_file))); + + uint64_t address = 0; + uint64_t vmsize = 1; + uintptr_t imageFileModDate = 2; + uint32_t current_version = 3; + uint32_t filetype = MH_DYLIB; + uint64_t source_version = 5; + static constexpr uint8_t uuid[16] = {0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0a, + 0x0b, + 0x0c, + 0x0d, + 0x0e, + 0x0f}; + EXPECT_TRUE(writer->AddProperty(Key::kAddress, &address)); + EXPECT_TRUE(writer->AddProperty(Key::kSize, &vmsize)); + EXPECT_TRUE(writer->AddProperty(Key::kTimestamp, &imageFileModDate)); + EXPECT_TRUE( + writer->AddProperty(Key::kDylibCurrentVersion, ¤t_version)); + EXPECT_TRUE(writer->AddProperty(Key::kSourceVersion, &source_version)); + EXPECT_TRUE(writer->AddProperty(Key::kUUID, &uuid)); + EXPECT_TRUE(writer->AddProperty(Key::kFileType, &filetype)); + WriteAnnotations(writer); + } + } + + void ExpectModules(const std::vector& modules) { + for (auto module : modules) { + EXPECT_EQ(module->GetModuleType(), + ModuleSnapshot::kModuleTypeSharedLibrary); + EXPECT_STREQ(module->Name().c_str(), "/path/to/module"); + EXPECT_STREQ(module->DebugFileName().c_str(), "module"); + UUID uuid; + uint32_t age; + module->UUIDAndAge(&uuid, &age); + EXPECT_EQ(uuid.ToString(), "00010203-0405-0607-0809-0a0b0c0d0e0f"); + + for (auto annotation : module->AnnotationsVector()) { + EXPECT_STREQ(annotation.c_str(), "annotation_value"); + } + + for (const auto& it : module->AnnotationsSimpleMap()) { + EXPECT_STREQ(it.first.c_str(), "annotation_name"); + EXPECT_STREQ(it.second.c_str(), "annotation_value"); + } + + for (auto annotation_object : module->AnnotationObjects()) { + EXPECT_STREQ(annotation_object.name.c_str(), "annotation_name"); + EXPECT_EQ(annotation_object.type, (short)Annotation::Type::kString); + EXPECT_STREQ(std::string(reinterpret_cast( + annotation_object.value.data()), + annotation_object.value.size()) + .c_str(), + "annotation_value"); + } + } + } + + void WriteMachException(IOSIntermediateDumpWriter* writer) { + IOSIntermediateDumpWriter::ScopedMap machExceptionMap(writer, + Key::kMachException); + exception_type_t exception = 5; + mach_exception_data_type_t code[] = {4, 3}; + mach_msg_type_number_t code_count = 2; + +#if defined(ARCH_CPU_X86_64) + thread_state_flavor_t flavor = x86_THREAD_STATE; + mach_msg_type_number_t state_count = x86_THREAD_STATE_COUNT; + x86_thread_state_t state = {}; + state.tsh.flavor = x86_THREAD_STATE64; + state.tsh.count = x86_THREAD_STATE64_COUNT; + state.uts.ts64.__rip = 0xdeadbeef; + size_t state_length = sizeof(x86_thread_state_t); +#elif defined(ARCH_CPU_ARM64) + thread_state_flavor_t flavor = ARM_UNIFIED_THREAD_STATE; + mach_msg_type_number_t state_count = ARM_UNIFIED_THREAD_STATE_COUNT; + arm_unified_thread_state_t state = {}; + state.ash.flavor = ARM_THREAD_STATE64; + state.ash.count = ARM_THREAD_STATE64_COUNT; + state.ts_64.__pc = 0xdeadbeef; + size_t state_length = sizeof(arm_unified_thread_state_t); +#endif + EXPECT_TRUE(writer->AddProperty(Key::kException, &exception)); + EXPECT_TRUE(writer->AddProperty(Key::kCode, code, code_count)); + EXPECT_TRUE(writer->AddProperty(Key::kCodeCount, &code_count)); + EXPECT_TRUE(writer->AddProperty(Key::kFlavor, &flavor)); + EXPECT_TRUE(writer->AddPropertyBytes( + Key::kState, reinterpret_cast(&state), state_length)); + EXPECT_TRUE(writer->AddProperty(Key::kStateCount, &state_count)); + uint64_t thread_id = 1; + EXPECT_TRUE(writer->AddProperty(Key::kThreadID, &thread_id)); + } + + void WriteThreads(IOSIntermediateDumpWriter* writer) { + vm_address_t stack_region_address = 0; + IOSIntermediateDumpWriter::ScopedArray threadArray(writer, Key::kThreads); + for (uint64_t thread_id = 1; thread_id < 3; thread_id++) { + IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer); + EXPECT_TRUE(writer->AddProperty(Key::kThreadID, &thread_id)); + + integer_t suspend_count = 666; + integer_t importance = 5; + uint64_t thread_handle = thread_id; + EXPECT_TRUE(writer->AddProperty(Key::kSuspendCount, &suspend_count)); + EXPECT_TRUE(writer->AddProperty(Key::kPriority, &importance)); + EXPECT_TRUE(writer->AddProperty(Key::kThreadDataAddress, &thread_handle)); + +#if defined(ARCH_CPU_X86_64) + x86_thread_state64_t thread_state = {}; + thread_state.__rip = 0xdeadbeef; + x86_float_state64_t float_state = {}; + x86_debug_state64_t debug_state = {}; +#elif defined(ARCH_CPU_ARM64) + arm_thread_state64_t thread_state = {}; + thread_state.__pc = 0xdeadbeef; + arm_neon_state64_t float_state = {}; + arm_debug_state64_t debug_state = {}; +#endif + EXPECT_TRUE(writer->AddProperty(Key::kThreadState, &thread_state)); + EXPECT_TRUE(writer->AddProperty(Key::kFloatState, &float_state)); + EXPECT_TRUE(writer->AddProperty(Key::kDebugState, &debug_state)); + + // Non-overlapping stack_region_address. + stack_region_address += 10; + EXPECT_TRUE( + writer->AddProperty(Key::kStackRegionAddress, &stack_region_address)); + constexpr char memory_region[] = "stack_data"; + EXPECT_TRUE( + writer->AddPropertyBytes(Key::kStackRegionData, memory_region, 10)); + { + IOSIntermediateDumpWriter::ScopedArray memoryRegions( + writer, Key::kThreadContextMemoryRegions); + { + IOSIntermediateDumpWriter::ScopedArrayMap memoryRegion(writer); + const vm_address_t memory_region_address = 0; + EXPECT_TRUE(writer->AddProperty( + Key::kThreadContextMemoryRegionAddress, &memory_region_address)); + constexpr char memory_region[] = "string"; + EXPECT_TRUE(writer->AddPropertyBytes( + Key::kThreadContextMemoryRegionData, memory_region, 6)); + } + } + } + } + + void ExpectMachException(const ExceptionSnapshot& exception) { + EXPECT_EQ(exception.ThreadID(), 1u); + EXPECT_EQ(exception.Exception(), 5u); + EXPECT_TRUE(exception.Context()->Is64Bit()); + EXPECT_EQ(exception.Context()->InstructionPointer(), 0xdeadbeef); + EXPECT_EQ(exception.ExceptionInfo(), 4u); + EXPECT_EQ(exception.ExceptionAddress(), 0xdeadbeef); + EXPECT_EQ(exception.Codes()[0], 5u); + EXPECT_EQ(exception.Codes()[1], 4u); + EXPECT_EQ(exception.Codes()[2], 3u); + } + + void ExpectThreads(const std::vector& threads) { + uint64_t thread_id = 1; + for (auto thread : threads) { + EXPECT_EQ(thread->ThreadID(), thread_id); + EXPECT_EQ(thread->SuspendCount(), 666); + EXPECT_EQ(thread->Priority(), 5); + EXPECT_EQ(thread->ThreadSpecificDataAddress(), thread_id++); + ReadToString delegate; + for (auto memory : thread->ExtraMemory()) { + memory->Read(&delegate); + EXPECT_STREQ(delegate.result.c_str(), "string"); + } + + thread->Stack()->Read(&delegate); + EXPECT_STREQ(delegate.result.c_str(), "stack_data"); + + EXPECT_TRUE(thread->Context()->Is64Bit()); + EXPECT_EQ(thread->Context()->InstructionPointer(), 0xdeadbeef); + } + } + + void ExpectSystem(const SystemSnapshot& system) { + EXPECT_EQ(system.CPUCount(), 1u); + EXPECT_STREQ(system.CPUVendor().c_str(), "RISC"); + int major; + int minor; + int bugfix; + std::string build; + system.OSVersion(&major, &minor, &bugfix, &build); + EXPECT_EQ(major, 1995); + EXPECT_EQ(minor, 9); + EXPECT_EQ(bugfix, 15); + EXPECT_STREQ(build.c_str(), "Da Vinci"); + EXPECT_STREQ(system.OSVersionFull().c_str(), "1995.9.15 Da Vinci"); + EXPECT_STREQ(system.MachineDescription().c_str(), "Gibson"); + + SystemSnapshot::DaylightSavingTimeStatus dst_status; + int standard_offset_seconds; + int daylight_offset_seconds; + std::string standard_name; + std::string daylight_name; + + system.TimeZone(&dst_status, + &standard_offset_seconds, + &daylight_offset_seconds, + &standard_name, + &daylight_name); + EXPECT_EQ(standard_offset_seconds, 7200); + EXPECT_EQ(daylight_offset_seconds, 3600); + EXPECT_STREQ(standard_name.c_str(), "Standard"); + EXPECT_STREQ(daylight_name.c_str(), "Daylight"); + } + + void ExpectSnapshot(const ProcessSnapshot& snapshot) { + EXPECT_EQ(snapshot.ProcessID(), 2); + EXPECT_EQ(snapshot.ParentProcessID(), 1); + + timeval snapshot_time; + snapshot.SnapshotTime(&snapshot_time); + EXPECT_EQ(snapshot_time.tv_sec, 42); + EXPECT_EQ(snapshot_time.tv_usec, 0); + + timeval start_time; + snapshot.ProcessStartTime(&start_time); + EXPECT_EQ(start_time.tv_sec, 12); + EXPECT_EQ(start_time.tv_usec, 0); + + timeval user_time, system_time; + snapshot.ProcessCPUTimes(&user_time, &system_time); + EXPECT_EQ(user_time.tv_sec, 40); + EXPECT_EQ(user_time.tv_usec, 0); + EXPECT_EQ(system_time.tv_sec, 60); + EXPECT_EQ(system_time.tv_usec, 0); + + ExpectSystem(*snapshot.System()); + ExpectThreads(snapshot.Threads()); + ExpectModules(snapshot.Modules()); + ExpectMachException(*snapshot.Exception()); + } + + private: + std::unique_ptr writer_; + ScopedTempDir temp_dir_; + base::FilePath path_; + std::map annotations_; +}; + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, InitializeNoFile) { + const base::FilePath file; + ProcessSnapshotIOSIntermediateDump process_snapshot; + EXPECT_FALSE(process_snapshot.Initialize(file, annotations())); + EXPECT_TRUE(LoggingRemoveFile(path())); + EXPECT_FALSE(IsRegularFile(path())); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, InitializeEmpty) { + ProcessSnapshotIOSIntermediateDump process_snapshot; + EXPECT_FALSE(process_snapshot.Initialize(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, InitializeMinimumDump) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + { IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kSystemInfo); } + { IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kProcessInfo); } + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); + EXPECT_TRUE(DumpSnapshot(process_snapshot)); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, MissingSystemDump) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + { IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kProcessInfo); } + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_FALSE(process_snapshot.Initialize(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, MissingProcessDump) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + { IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kSystemInfo); } + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_FALSE(process_snapshot.Initialize(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptySignalDump) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + WriteSystemInfo(writer()); + WriteProcessInfo(writer()); + { + IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kSignalException); + uint64_t thread_id = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id)); + } + { + IOSIntermediateDumpWriter::ScopedArray threadArray(writer(), + Key::kThreads); + IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer()); + uint64_t thread_id = 1; + writer()->AddProperty(Key::kThreadID, &thread_id); + } + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); + EXPECT_TRUE(DumpSnapshot(process_snapshot)); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptyMachDump) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + WriteSystemInfo(writer()); + WriteProcessInfo(writer()); + { + IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kMachException); + uint64_t thread_id = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id)); + } + { + IOSIntermediateDumpWriter::ScopedArray threadArray(writer(), + Key::kThreads); + IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer()); + uint64_t thread_id = 1; + writer()->AddProperty(Key::kThreadID, &thread_id); + } + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); + EXPECT_TRUE(DumpSnapshot(process_snapshot)); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptyExceptionDump) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + WriteSystemInfo(writer()); + WriteProcessInfo(writer()); + { + IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kNSException); + uint64_t thread_id = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id)); + } + { + IOSIntermediateDumpWriter::ScopedArray threadArray(writer(), + Key::kThreads); + IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer()); + uint64_t thread_id = 1; + writer()->AddProperty(Key::kThreadID, &thread_id); + } + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); + EXPECT_TRUE(DumpSnapshot(process_snapshot)); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptyUncaughtNSExceptionDump) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + WriteSystemInfo(writer()); + WriteProcessInfo(writer()); + { + IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kNSException); + uint64_t thread_id = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id)); + } + { + IOSIntermediateDumpWriter::ScopedArray threadArray(writer(), + Key::kThreads); + IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer()); + uint64_t thread_id = 1; + writer()->AddProperty(Key::kThreadID, &thread_id); + const uint64_t frames[] = {0, 0}; + const size_t num_frames = 2; + writer()->AddProperty( + Key::kThreadUncaughtNSExceptionFrames, frames, num_frames); + } + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); + EXPECT_TRUE(DumpSnapshot(process_snapshot)); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, FullReport) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + WriteSystemInfo(writer()); + WriteProcessInfo(writer()); + WriteThreads(writer()); + WriteModules(writer()); + WriteMachException(writer()); + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); + EXPECT_TRUE(DumpSnapshot(process_snapshot)); + ExpectSnapshot(process_snapshot); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/snapshot/ios/system_snapshot_ios.cc b/snapshot/ios/system_snapshot_ios_intermediate_dump.cc similarity index 54% rename from snapshot/ios/system_snapshot_ios.cc rename to snapshot/ios/system_snapshot_ios_intermediate_dump.cc index 397b1f84..d10e9c0a 100644 --- a/snapshot/ios/system_snapshot_ios.cc +++ b/snapshot/ios/system_snapshot_ios_intermediate_dump.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "snapshot/ios/system_snapshot_ios.h" +#include "snapshot/ios/system_snapshot_ios_intermediate_dump.h" #include #include @@ -27,7 +27,9 @@ #include "base/strings/stringprintf.h" #include "build/build_config.h" #include "snapshot/cpu_context.h" +#include "snapshot/ios/intermediate_dump_reader_util.h" #include "snapshot/posix/timezone.h" +#include "util/ios/ios_intermediate_dump_data.h" #include "util/mac/mac_util.h" #include "util/numeric/in_range_cast.h" @@ -35,7 +37,9 @@ namespace crashpad { namespace internal { -SystemSnapshotIOS::SystemSnapshotIOS() +using Key = IntermediateDumpKey; + +SystemSnapshotIOSIntermediateDump::SystemSnapshotIOSIntermediateDump() : SystemSnapshot(), os_version_build_(), machine_description_(), @@ -55,51 +59,67 @@ SystemSnapshotIOS::SystemSnapshotIOS() daylight_name_(), initialized_() {} -SystemSnapshotIOS::~SystemSnapshotIOS() {} +SystemSnapshotIOSIntermediateDump::~SystemSnapshotIOSIntermediateDump() {} -void SystemSnapshotIOS::Initialize(const IOSSystemDataCollector& system_data) { +void SystemSnapshotIOSIntermediateDump::Initialize( + const IOSIntermediateDumpMap* system_data) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); - system_data.OSVersion( - &os_version_major_, &os_version_minor_, &os_version_bugfix_); - os_version_build_ = system_data.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() + GetDataStringFromMap(system_data, Key::kOSVersionBuild, &os_version_build_); + GetDataStringFromMap( + system_data, Key::kMachineDescription, &machine_description_); + GetDataStringFromMap(system_data, Key::kCpuVendor, &cpu_vendor_); + GetDataStringFromMap(system_data, Key::kStandardName, &standard_name_); + GetDataStringFromMap(system_data, Key::kDaylightName, &daylight_name_); + + GetDataValueFromMap(system_data, Key::kOSVersionMajor, &os_version_major_); + GetDataValueFromMap(system_data, Key::kOSVersionMinor, &os_version_minor_); + GetDataValueFromMap(system_data, Key::kOSVersionBugfix, &os_version_bugfix_); + GetDataValueFromMap(system_data, Key::kCpuCount, &cpu_count_); + + GetDataValueFromMap( + system_data, Key::kStandardOffsetSeconds, &standard_offset_seconds_); + GetDataValueFromMap( + system_data, Key::kDaylightOffsetSeconds, &daylight_offset_seconds_); + + bool has_daylight_saving_time; + GetDataValueFromMap( + system_data, Key::kHasDaylightSavingTime, &has_daylight_saving_time); + bool is_daylight_saving_time; + GetDataValueFromMap( + system_data, Key::kIsDaylightSavingTime, &is_daylight_saving_time); + + if (has_daylight_saving_time) { + dst_status_ = is_daylight_saving_time ? 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"; + if (GetDataValueFromMap(system_data, Key::kPageSize, &page_size)) { + const IOSIntermediateDumpMap* vm_stat = + GetMapFromMap(system_data, Key::kVMStat); + if (vm_stat) { + GetDataValueFromMap(vm_stat, Key::kActive, &active_); + active_ *= page_size; + + GetDataValueFromMap(vm_stat, Key::kInactive, &inactive_); + inactive_ *= page_size; + + GetDataValueFromMap(vm_stat, Key::kWired, &wired_); + wired_ *= page_size; + + GetDataValueFromMap(vm_stat, Key::kFree, &free_); + free_ *= page_size; + } } - 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 { +CPUArchitecture SystemSnapshotIOSIntermediateDump::GetCPUArchitecture() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); #if defined(ARCH_CPU_X86_64) return kCPUArchitectureX86_64; @@ -108,25 +128,25 @@ CPUArchitecture SystemSnapshotIOS::GetCPUArchitecture() const { #endif } -uint32_t SystemSnapshotIOS::CPURevision() const { +uint32_t SystemSnapshotIOSIntermediateDump::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 { +uint8_t SystemSnapshotIOSIntermediateDump::CPUCount() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return cpu_count_; } -std::string SystemSnapshotIOS::CPUVendor() const { +std::string SystemSnapshotIOSIntermediateDump::CPUVendor() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return cpu_vendor_; } -void SystemSnapshotIOS::CPUFrequency(uint64_t* current_hz, - uint64_t* max_hz) const { +void SystemSnapshotIOSIntermediateDump::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. @@ -134,50 +154,51 @@ void SystemSnapshotIOS::CPUFrequency(uint64_t* current_hz, *max_hz = 0; } -uint32_t SystemSnapshotIOS::CPUX86Signature() const { +uint32_t SystemSnapshotIOSIntermediateDump::CPUX86Signature() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); // TODO(justincohen): Consider recording this for X86_64 only. return 0; } -uint64_t SystemSnapshotIOS::CPUX86Features() const { +uint64_t SystemSnapshotIOSIntermediateDump::CPUX86Features() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); // TODO(justincohen): Consider recording this for X86_64 only. return 0; } -uint64_t SystemSnapshotIOS::CPUX86ExtendedFeatures() const { +uint64_t SystemSnapshotIOSIntermediateDump::CPUX86ExtendedFeatures() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); // TODO(justincohen): Consider recording this for X86_64 only. return 0; } -uint32_t SystemSnapshotIOS::CPUX86Leaf7Features() const { +uint32_t SystemSnapshotIOSIntermediateDump::CPUX86Leaf7Features() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); // TODO(justincohen): Consider recording this for X86_64 only. return 0; } -bool SystemSnapshotIOS::CPUX86SupportsDAZ() const { +bool SystemSnapshotIOSIntermediateDump::CPUX86SupportsDAZ() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); // TODO(justincohen): Consider recording this for X86_64 only. return false; } -SystemSnapshot::OperatingSystem SystemSnapshotIOS::GetOperatingSystem() const { +SystemSnapshot::OperatingSystem +SystemSnapshotIOSIntermediateDump::GetOperatingSystem() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return kOperatingSystemIOS; } -bool SystemSnapshotIOS::OSServer() const { +bool SystemSnapshotIOSIntermediateDump::OSServer() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return false; } -void SystemSnapshotIOS::OSVersion(int* major, - int* minor, - int* bugfix, - std::string* build) const { +void SystemSnapshotIOSIntermediateDump::OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); *major = os_version_major_; *minor = os_version_minor_; @@ -185,7 +206,7 @@ void SystemSnapshotIOS::OSVersion(int* major, build->assign(os_version_build_); } -std::string SystemSnapshotIOS::OSVersionFull() const { +std::string SystemSnapshotIOSIntermediateDump::OSVersionFull() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return base::StringPrintf("%d.%d.%d %s", os_version_major_, @@ -194,23 +215,24 @@ std::string SystemSnapshotIOS::OSVersionFull() const { os_version_build_.c_str()); } -std::string SystemSnapshotIOS::MachineDescription() const { +std::string SystemSnapshotIOSIntermediateDump::MachineDescription() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return machine_description_; } -bool SystemSnapshotIOS::NXEnabled() const { +bool SystemSnapshotIOSIntermediateDump::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 { +void SystemSnapshotIOSIntermediateDump::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_; diff --git a/snapshot/ios/system_snapshot_ios.h b/snapshot/ios/system_snapshot_ios_intermediate_dump.h similarity index 75% rename from snapshot/ios/system_snapshot_ios.h rename to snapshot/ios/system_snapshot_ios_intermediate_dump.h index a38de4ef..72eaa64b 100644 --- a/snapshot/ios/system_snapshot_ios.h +++ b/snapshot/ios/system_snapshot_ios_intermediate_dump.h @@ -12,8 +12,8 @@ // 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_ +#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_SYSTEM_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ +#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_SYSTEM_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ #include @@ -21,6 +21,7 @@ #include "base/macros.h" #include "snapshot/system_snapshot.h" +#include "util/ios/ios_intermediate_dump_map.h" #include "util/ios/ios_system_data_collector.h" #include "util/misc/initialization_state_dcheck.h" @@ -29,15 +30,17 @@ namespace crashpad { namespace internal { //! \brief A SystemSnapshot of the running system, when the system runs iOS. -class SystemSnapshotIOS final : public SystemSnapshot { +class SystemSnapshotIOSIntermediateDump final : public SystemSnapshot { public: - SystemSnapshotIOS(); - ~SystemSnapshotIOS() override; + SystemSnapshotIOSIntermediateDump(); + ~SystemSnapshotIOSIntermediateDump() override; //! \brief Initializes the object. //! - //! \param[in] system_data A class containing various system data points. - void Initialize(const IOSSystemDataCollector& system_data); + //! \param[in] system_data An intermediate dump map containing various system + //! data points. + //! \return `true` if the snapshot could be created. + void Initialize(const IOSIntermediateDumpMap* system_data); // SystemSnapshot: @@ -72,10 +75,10 @@ class SystemSnapshotIOS final : public SystemSnapshot { int os_version_major_; int os_version_minor_; int os_version_bugfix_; - uint64_t active_; - uint64_t inactive_; - uint64_t wired_; - uint64_t free_; + uint32_t active_; + uint32_t inactive_; + uint32_t wired_; + uint32_t free_; int cpu_count_; std::string cpu_vendor_; DaylightSavingTimeStatus dst_status_; @@ -85,10 +88,10 @@ class SystemSnapshotIOS final : public SystemSnapshot { std::string daylight_name_; InitializationStateDcheck initialized_; - DISALLOW_COPY_AND_ASSIGN(SystemSnapshotIOS); + DISALLOW_COPY_AND_ASSIGN(SystemSnapshotIOSIntermediateDump); }; } // namespace internal } // namespace crashpad -#endif // CRASHPAD_SNAPSHOT_IOS_SYSTEM_SNAPSHOT_IOS_H_ +#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_SYSTEM_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ diff --git a/snapshot/ios/thread_snapshot_ios.cc b/snapshot/ios/thread_snapshot_ios.cc deleted file mode 100644 index ffd5a452..00000000 --- a/snapshot/ios/thread_snapshot_ios.cc +++ /dev/null @@ -1,481 +0,0 @@ -// 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/thread_snapshot_ios.h" - -#include "base/mac/mach_logging.h" -#include "snapshot/mac/cpu_context_mac.h" - -namespace { - -#if defined(ARCH_CPU_X86_64) -const thread_state_flavor_t kThreadStateFlavor = x86_THREAD_STATE64; -const thread_state_flavor_t kFloatStateFlavor = x86_FLOAT_STATE64; -const thread_state_flavor_t kDebugStateFlavor = x86_DEBUG_STATE64; -#elif defined(ARCH_CPU_ARM64) -const thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64; -const thread_state_flavor_t kFloatStateFlavor = ARM_NEON_STATE64; -const thread_state_flavor_t kDebugStateFlavor = ARM_DEBUG_STATE64; -#endif - -kern_return_t MachVMRegionRecurseDeepest(task_t task, - vm_address_t* address, - vm_size_t* size, - natural_t* depth, - vm_prot_t* protection, - unsigned int* user_tag) { - vm_region_submap_short_info_64 submap_info; - mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; - while (true) { - kern_return_t kr = vm_region_recurse_64( - task, - address, - size, - depth, - reinterpret_cast(&submap_info), - &count); - if (kr != KERN_SUCCESS) { - return kr; - } - - if (!submap_info.is_submap) { - *protection = submap_info.protection; - *user_tag = submap_info.user_tag; - return KERN_SUCCESS; - } - - ++*depth; - } -} - -//! \brief Adjusts the region for the red zone, if the ABI requires one. -//! -//! This method performs red zone calculation for CalculateStackRegion(). Its -//! parameters are local variables used within that method, and may be -//! modified as needed. -//! -//! Where a red zone is required, the region of memory captured for a thread’s -//! stack will be extended to include the red zone below the stack pointer, -//! provided that such memory is mapped, readable, and has the correct user -//! tag value. If these conditions cannot be met fully, as much of the red -//! zone will be captured as is possible while meeting these conditions. -//! -//! \param[in,out] start_address The base address of the region to begin -//! capturing stack memory from. On entry, \a start_address is the stack -//! pointer. On return, \a start_address may be decreased to encompass a -//! red zone. -//! \param[in,out] region_base The base address of the region that contains -//! stack memory. This is distinct from \a start_address in that \a -//! region_base will be page-aligned. On entry, \a region_base is the -//! base address of a region that contains \a start_address. On return, -//! if \a start_address is decremented and is outside of the region -//! originally described by \a region_base, \a region_base will also be -//! decremented appropriately. -//! \param[in,out] region_size The size of the region that contains stack -//! memory. This region begins at \a region_base. On return, if \a -//! region_base is decremented, \a region_size will be incremented -//! appropriately. -//! \param[in] user_tag The Mach VM system’s user tag for the region described -//! by the initial values of \a region_base and \a region_size. The red -//! zone will only be allowed to extend out of the region described by -//! these initial values if the user tag is appropriate for stack memory -//! and the expanded region has the same user tag value. -void LocateRedZone(vm_address_t* const start_address, - vm_address_t* const region_base, - vm_address_t* const region_size, - const unsigned int user_tag) { - // x86_64 has a red zone. See AMD64 ABI 0.99.8, - // https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-r252.pdf#page=19, - // section 3.2.2, “The Stack Frame”. - // So does ARM64, - // https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html - // section "Red Zone". - constexpr vm_size_t kRedZoneSize = 128; - vm_address_t red_zone_base = - *start_address >= kRedZoneSize ? *start_address - kRedZoneSize : 0; - bool red_zone_ok = false; - if (red_zone_base >= *region_base) { - // The red zone is within the region already discovered. - red_zone_ok = true; - } else if (red_zone_base < *region_base && user_tag == VM_MEMORY_STACK) { - // Probe to see if there’s a region immediately below the one already - // discovered. - vm_address_t red_zone_region_base = red_zone_base; - vm_size_t red_zone_region_size; - natural_t red_zone_depth = 0; - vm_prot_t red_zone_protection; - unsigned int red_zone_user_tag; - kern_return_t kr = MachVMRegionRecurseDeepest(mach_task_self(), - &red_zone_region_base, - &red_zone_region_size, - &red_zone_depth, - &red_zone_protection, - &red_zone_user_tag); - if (kr != KERN_SUCCESS) { - MACH_LOG(INFO, kr) << "vm_region_recurse"; - *start_address = *region_base; - } else if (red_zone_region_base + red_zone_region_size == *region_base && - (red_zone_protection & VM_PROT_READ) != 0 && - red_zone_user_tag == user_tag) { - // The region containing the red zone is immediately below the region - // already found, it’s readable (not the guard region), and it has the - // same user tag as the region already found, so merge them. - red_zone_ok = true; - *region_base -= red_zone_region_size; - *region_size += red_zone_region_size; - } - } - - if (red_zone_ok) { - // Begin capturing from the base of the red zone (but not the entire - // region that encompasses the red zone). - *start_address = red_zone_base; - } else { - // The red zone would go lower into another region in memory, but no - // region was found. Memory can only be captured to an address as low as - // the base address of the region already found. - *start_address = *region_base; - } -} - -//! \brief Calculates the base address and size of the region used as a -//! thread’s stack. -//! -//! The region returned by this method may be formed by merging multiple -//! adjacent regions in a process’ memory map if appropriate. The base address -//! of the returned region may be lower than the \a stack_pointer passed in -//! when the ABI mandates a red zone below the stack pointer. -//! -//! \param[in] stack_pointer The stack pointer, referring to the top (lowest -//! address) of a thread’s stack. -//! \param[out] stack_region_size The size of the memory region used as the -//! thread’s stack. -//! -//! \return The base address (lowest address) of the memory region used as the -//! thread’s stack. -vm_address_t CalculateStackRegion(vm_address_t stack_pointer, - vm_size_t* stack_region_size) { - // For pthreads, it may be possible to compute the stack region based on the - // internal _pthread::stackaddr and _pthread::stacksize. The _pthread struct - // for a thread can be located at TSD slot 0, or the known offsets of - // stackaddr and stacksize from the TSD area could be used. - vm_address_t region_base = stack_pointer; - vm_size_t region_size; - natural_t depth = 0; - vm_prot_t protection; - unsigned int user_tag; - kern_return_t kr = MachVMRegionRecurseDeepest(mach_task_self(), - ®ion_base, - ®ion_size, - &depth, - &protection, - &user_tag); - if (kr != KERN_SUCCESS) { - MACH_LOG(INFO, kr) << "mach_vm_region_recurse"; - *stack_region_size = 0; - return 0; - } - - if (region_base > stack_pointer) { - // There’s nothing mapped at the stack pointer’s address. Something may have - // trashed the stack pointer. Note that this shouldn’t happen for a normal - // stack guard region violation because the guard region is mapped but has - // VM_PROT_NONE protection. - *stack_region_size = 0; - return 0; - } - - vm_address_t start_address = stack_pointer; - - if ((protection & VM_PROT_READ) == 0) { - // If the region isn’t readable, the stack pointer probably points to the - // guard region. Don’t include it as part of the stack, and don’t include - // anything at any lower memory address. The code below may still possibly - // find the real stack region at a memory address higher than this region. - start_address = region_base + region_size; - } else { - // If the ABI requires a red zone, adjust the region to include it if - // possible. - LocateRedZone(&start_address, ®ion_base, ®ion_size, user_tag); - - // Regardless of whether the ABI requires a red zone, capture up to - // kExtraCaptureSize additional bytes of stack, but only if present in the - // region that was already found. - constexpr vm_size_t kExtraCaptureSize = 128; - start_address = std::max(start_address >= kExtraCaptureSize - ? start_address - kExtraCaptureSize - : start_address, - region_base); - - // Align start_address to a 16-byte boundary, which can help readers by - // ensuring that data is aligned properly. This could page-align instead, - // but that might be wasteful. - constexpr vm_size_t kDesiredAlignment = 16; - start_address &= ~(kDesiredAlignment - 1); - DCHECK_GE(start_address, region_base); - } - - region_size -= (start_address - region_base); - region_base = start_address; - - vm_size_t total_region_size = region_size; - - // The stack region may have gotten split up into multiple abutting regions. - // Try to coalesce them. This frequently happens for the main thread’s stack - // when setrlimit(RLIMIT_STACK, …) is called. It may also happen if a region - // is split up due to an mprotect() or vm_protect() call. - // - // Stack regions created by the kernel and the pthreads library will be marked - // with the VM_MEMORY_STACK user tag. Scanning for multiple adjacent regions - // with the same tag should find an entire stack region. Checking that the - // protection on individual regions is not VM_PROT_NONE should guarantee that - // this algorithm doesn’t collect map entries belonging to another thread’s - // stack: well-behaved stacks (such as those created by the kernel and the - // pthreads library) have VM_PROT_NONE guard regions at their low-address - // ends. - // - // Other stack regions may not be so well-behaved and thus if user_tag is not - // VM_MEMORY_STACK, the single region that was found is used as-is without - // trying to merge it with other adjacent regions. - if (user_tag == VM_MEMORY_STACK) { - vm_address_t try_address = region_base; - vm_address_t original_try_address; - - while (try_address += region_size, - original_try_address = try_address, - (kr = MachVMRegionRecurseDeepest(mach_task_self(), - &try_address, - ®ion_size, - &depth, - &protection, - &user_tag) == KERN_SUCCESS) && - try_address == original_try_address && - (protection & VM_PROT_READ) != 0 && - user_tag == VM_MEMORY_STACK) { - total_region_size += region_size; - } - - if (kr != KERN_SUCCESS && kr != KERN_INVALID_ADDRESS) { - // Tolerate KERN_INVALID_ADDRESS because it will be returned when there - // are no more regions in the map at or above the specified |try_address|. - MACH_LOG(INFO, kr) << "vm_region_recurse"; - } - } - - *stack_region_size = total_region_size; - return region_base; -} - -} // namespace - -namespace crashpad { -namespace internal { - -ThreadSnapshotIOS::ThreadSnapshotIOS() - : ThreadSnapshot(), - context_(), - stack_(), - thread_id_(0), - thread_specific_data_address_(0), - suspend_count_(0), - priority_(0), - initialized_() {} - -ThreadSnapshotIOS::~ThreadSnapshotIOS() {} - -// static -thread_act_array_t ThreadSnapshotIOS::GetThreads( - mach_msg_type_number_t* count) { - thread_act_array_t threads; - kern_return_t kr = task_threads(mach_task_self(), &threads, count); - if (kr != KERN_SUCCESS) { - MACH_LOG(WARNING, kr) << "task_threads"; - } - return threads; -} - -bool ThreadSnapshotIOS::Initialize(thread_t thread) { - INITIALIZATION_STATE_SET_INITIALIZING(initialized_); - - // TODO(justincohen): Move the following thread_get_state, thread_get_info, - // thread_policy_get and CalculateStackRegion to the serialize-on-read - // section. - thread_basic_info basic_info; - thread_precedence_policy precedence; - vm_size_t stack_region_size; - vm_address_t stack_region_address; -#if defined(ARCH_CPU_X86_64) - x86_thread_state64_t thread_state; - x86_float_state64_t float_state; - x86_debug_state64_t debug_state; - mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT; - mach_msg_type_number_t float_state_count = x86_FLOAT_STATE64_COUNT; - mach_msg_type_number_t debug_state_count = x86_DEBUG_STATE64_COUNT; -#elif defined(ARCH_CPU_ARM64) - arm_thread_state64_t thread_state; - arm_neon_state64_t float_state; - arm_debug_state64_t debug_state; - mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT; - mach_msg_type_number_t float_state_count = ARM_NEON_STATE64_COUNT; - mach_msg_type_number_t debug_state_count = ARM_DEBUG_STATE64_COUNT; -#endif - - kern_return_t kr = - thread_get_state(thread, - kThreadStateFlavor, - reinterpret_cast(&thread_state), - &thread_state_count); - if (kr != KERN_SUCCESS) { - MACH_LOG(ERROR, kr) << "thread_get_state(" << kThreadStateFlavor << ")"; - } - - kr = thread_get_state(thread, - kFloatStateFlavor, - reinterpret_cast(&float_state), - &float_state_count); - if (kr != KERN_SUCCESS) { - MACH_LOG(ERROR, kr) << "thread_get_state(" << kFloatStateFlavor << ")"; - } - - kr = thread_get_state(thread, - kDebugStateFlavor, - reinterpret_cast(&debug_state), - &debug_state_count); - if (kr != KERN_SUCCESS) { - MACH_LOG(ERROR, kr) << "thread_get_state(" << kDebugStateFlavor << ")"; - } - - mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; - kr = thread_info(thread, - THREAD_BASIC_INFO, - reinterpret_cast(&basic_info), - &count); - if (kr != KERN_SUCCESS) { - MACH_LOG(WARNING, kr) << "thread_info(THREAD_BASIC_INFO)"; - } - - thread_identifier_info identifier_info; - count = THREAD_IDENTIFIER_INFO_COUNT; - kr = thread_info(thread, - THREAD_IDENTIFIER_INFO, - reinterpret_cast(&identifier_info), - &count); - if (kr != KERN_SUCCESS) { - MACH_LOG(WARNING, kr) << "thread_info(THREAD_IDENTIFIER_INFO)"; - } - - count = THREAD_PRECEDENCE_POLICY_COUNT; - boolean_t get_default = FALSE; - kr = thread_policy_get(thread, - THREAD_PRECEDENCE_POLICY, - reinterpret_cast(&precedence), - &count, - &get_default); - if (kr != KERN_SUCCESS) { - MACH_LOG(ERROR, kr) << "thread_policy_get"; - } - -#if defined(ARCH_CPU_X86_64) - vm_address_t stack_pointer = thread_state.__rsp; -#elif defined(ARCH_CPU_ARM64) - vm_address_t stack_pointer = arm_thread_state64_get_sp(thread_state); -#endif - stack_region_address = - CalculateStackRegion(stack_pointer, &stack_region_size); - - // TODO(justincohen): Assume the following will fill in snapshot data from - // a deserialized object. - thread_id_ = identifier_info.thread_id; - suspend_count_ = basic_info.suspend_count; - priority_ = precedence.importance; - - // thread_identifier_info::thread_handle contains the base of the - // thread-specific data area, which on x86 and x86_64 is the thread’s base - // address of the %gs segment. 10.9.2 xnu-2422.90.20/osfmk/kern/thread.c - // thread_info_internal() gets the value from - // machine_thread::cthread_self, which is the same value used to set the - // %gs base in xnu-2422.90.20/osfmk/i386/pcb_native.c - // act_machine_switch_pcb(). - // - // On ARM64 10.15.0 xnu-6153.11.26/osfmk/kern/thread.c, it sets - // thread_identifier_info_t::thread_handle to - // thread->machine.cthread_self, which is set to tsd_base in - // osfmk/arm64/pcb.c. - thread_specific_data_address_ = identifier_info.thread_handle; - stack_.Initialize(stack_region_address, stack_region_size); - -#if defined(ARCH_CPU_X86_64) - context_.architecture = kCPUArchitectureX86_64; - context_.x86_64 = &context_x86_64_; - InitializeCPUContextX86_64(&context_x86_64_, - THREAD_STATE_NONE, - nullptr, - 0, - &thread_state, - &float_state, - &debug_state); -#elif defined(ARCH_CPU_ARM64) - context_.architecture = kCPUArchitectureARM64; - context_.arm64 = &context_arm64_; - InitializeCPUContextARM64(&context_arm64_, - THREAD_STATE_NONE, - nullptr, - 0, - &thread_state, - &float_state, - &debug_state); -#else -#error Port to your CPU architecture -#endif - - INITIALIZATION_STATE_SET_VALID(initialized_); - return true; -} - -const CPUContext* ThreadSnapshotIOS::Context() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return &context_; -} - -const MemorySnapshot* ThreadSnapshotIOS::Stack() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return &stack_; -} - -uint64_t ThreadSnapshotIOS::ThreadID() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return thread_id_; -} - -int ThreadSnapshotIOS::SuspendCount() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return suspend_count_; -} - -int ThreadSnapshotIOS::Priority() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return priority_; -} - -uint64_t ThreadSnapshotIOS::ThreadSpecificDataAddress() const { - INITIALIZATION_STATE_DCHECK_VALID(initialized_); - return thread_specific_data_address_; -} - -std::vector ThreadSnapshotIOS::ExtraMemory() const { - return std::vector(); -} - -} // namespace internal -} // namespace crashpad diff --git a/snapshot/ios/thread_snapshot_ios_intermediate_dump.cc b/snapshot/ios/thread_snapshot_ios_intermediate_dump.cc new file mode 100644 index 00000000..198b41a9 --- /dev/null +++ b/snapshot/ios/thread_snapshot_ios_intermediate_dump.cc @@ -0,0 +1,240 @@ +// 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/thread_snapshot_ios_intermediate_dump.h" + +#include "base/mac/mach_logging.h" +#include "snapshot/ios/intermediate_dump_reader_util.h" +#include "snapshot/mac/cpu_context_mac.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" + +#include + +namespace { + +std::vector GenerateStackMemoryFromFrames(const uint64_t* frames, + const size_t frame_count) { + std::vector stack_memory; + if (frame_count < 2) { + return stack_memory; + } + size_t pointer_size = sizeof(uintptr_t); + size_t frame_record_size = 2 * pointer_size; + size_t stack_size = frame_record_size * (frame_count - 1) + pointer_size; + stack_memory.resize(stack_size); + uintptr_t sp = stack_size - pointer_size; + uintptr_t fp = 0; + uintptr_t lr = 0; + for (size_t current_frame = frame_count - 1; current_frame > 0; + --current_frame) { + memcpy(&stack_memory[0] + sp, &lr, sizeof(lr)); + sp -= pointer_size; + memcpy(&stack_memory[0] + sp, &fp, sizeof(fp)); + fp = sp; + sp -= pointer_size; + lr = frames[current_frame]; + } + + if (sp != 0) { + LOG(ERROR) << "kExpectedFinalSp should be 0, is " << sp; + } + if (fp != sizeof(uintptr_t)) { + LOG(ERROR) << "kExpectedFinalFp should be sizeof(uintptr_t), is " << fp; + } + if (lr != frames[1]) { + LOG(ERROR) << "lr should be " << frames[1] << ", is " << lr; + } + return stack_memory; +} + +} // namespace +namespace crashpad { +namespace internal { + +using Key = IntermediateDumpKey; + +ThreadSnapshotIOSIntermediateDump::ThreadSnapshotIOSIntermediateDump() + : ThreadSnapshot(), + context_(), + stack_(), + thread_id_(0), + thread_specific_data_address_(0), + suspend_count_(0), + priority_(0), + initialized_() { +#if defined(ARCH_CPU_X86_64) + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_x86_64_; +#elif defined(ARCH_CPU_ARM64) + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_arm64_; +#endif +} + +ThreadSnapshotIOSIntermediateDump::~ThreadSnapshotIOSIntermediateDump() {} + +bool ThreadSnapshotIOSIntermediateDump::Initialize( + const IOSIntermediateDumpMap* thread_data) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + GetDataValueFromMap(thread_data, Key::kSuspendCount, &suspend_count_); + GetDataValueFromMap(thread_data, Key::kPriority, &priority_); + GetDataValueFromMap(thread_data, Key::kThreadID, &thread_id_); + GetDataValueFromMap( + thread_data, Key::kThreadDataAddress, &thread_specific_data_address_); + +#if defined(ARCH_CPU_X86_64) + typedef x86_thread_state64_t thread_state_type; + typedef x86_float_state64_t float_state_type; + typedef x86_debug_state64_t debug_state_type; +#elif defined(ARCH_CPU_ARM64) + typedef arm_thread_state64_t thread_state_type; + typedef arm_neon_state64_t float_state_type; + typedef arm_debug_state64_t debug_state_type; +#endif + + thread_state_type thread_state; + float_state_type float_state; + debug_state_type debug_state; + + const IOSIntermediateDumpData* nsexception_frames = + thread_data->GetAsData(Key::kThreadUncaughtNSExceptionFrames); + const IOSIntermediateDumpData* thread_stack_data_dump = + thread_data->GetAsData(Key::kStackRegionData); + if (nsexception_frames && thread_stack_data_dump) { + LOG(ERROR) << "Unexpected thread with kStackRegionData and " + << "kThreadUncaughtNSExceptionFrames, using kStackRegionData"; + } + if (thread_stack_data_dump) { + vm_address_t stack_region_address; + GetDataValueFromMap( + thread_data, Key::kStackRegionAddress, &stack_region_address); + + const std::vector& bytes = thread_stack_data_dump->bytes(); + const vm_address_t stack_region_data = + reinterpret_cast(bytes.data()); + vm_size_t stack_region_size = bytes.size(); + stack_.Initialize( + stack_region_address, stack_region_data, stack_region_size); + } else if (nsexception_frames) { + const std::vector& bytes = nsexception_frames->bytes(); + const uint64_t* frames = reinterpret_cast(bytes.data()); + size_t frame_count = bytes.size() / sizeof(uint64_t); + exception_stack_memory_ = + GenerateStackMemoryFromFrames(frames, frame_count); + stack_.Initialize( + 0, + reinterpret_cast(&exception_stack_memory_[0]), + exception_stack_memory_.size()); + } else { + stack_.Initialize(0, 0, 0); + } + + if (GetDataValueFromMap(thread_data, Key::kThreadState, &thread_state) && + GetDataValueFromMap(thread_data, Key::kFloatState, &float_state) && + GetDataValueFromMap(thread_data, Key::kDebugState, &debug_state)) { +#if defined(ARCH_CPU_X86_64) + InitializeCPUContextX86_64(&context_x86_64_, + THREAD_STATE_NONE, + nullptr, + 0, + &thread_state, + &float_state, + &debug_state); +#elif defined(ARCH_CPU_ARM64) + InitializeCPUContextARM64(&context_arm64_, + THREAD_STATE_NONE, + nullptr, + 0, + &thread_state, + &float_state, + &debug_state); + +#else +#error Port to your CPU architecture +#endif + } + const IOSIntermediateDumpList* thread_context_memory_regions = + GetListFromMap(thread_data, Key::kThreadContextMemoryRegions); + if (thread_context_memory_regions) { + for (auto& region : *thread_context_memory_regions) { + vm_address_t address; + const IOSIntermediateDumpData* region_data = + region->GetAsData(Key::kThreadContextMemoryRegionData); + if (!region_data) + continue; + if (GetDataValueFromMap( + region.get(), Key::kThreadContextMemoryRegionAddress, &address)) { + const std::vector& bytes = region_data->bytes(); + vm_size_t data_size = bytes.size(); + if (data_size == 0) + continue; + + const vm_address_t data = + reinterpret_cast(bytes.data()); + + auto memory = + std::make_unique(); + memory->Initialize(address, data, data_size); + extra_memory_.push_back(std::move(memory)); + } + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} +const CPUContext* ThreadSnapshotIOSIntermediateDump::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +const MemorySnapshot* ThreadSnapshotIOSIntermediateDump::Stack() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &stack_; +} + +uint64_t ThreadSnapshotIOSIntermediateDump::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +int ThreadSnapshotIOSIntermediateDump::SuspendCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return suspend_count_; +} + +int ThreadSnapshotIOSIntermediateDump::Priority() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return priority_; +} + +uint64_t ThreadSnapshotIOSIntermediateDump::ThreadSpecificDataAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_specific_data_address_; +} + +std::vector +ThreadSnapshotIOSIntermediateDump::ExtraMemory() const { + std::vector extra_memory; + for (const auto& memory : extra_memory_) { + extra_memory.push_back(memory.get()); + } + return extra_memory; +} + +} // namespace internal +} // namespace crashpad diff --git a/snapshot/ios/thread_snapshot_ios.h b/snapshot/ios/thread_snapshot_ios_intermediate_dump.h similarity index 63% rename from snapshot/ios/thread_snapshot_ios.h rename to snapshot/ios/thread_snapshot_ios_intermediate_dump.h index 978a8186..d986c493 100644 --- a/snapshot/ios/thread_snapshot_ios.h +++ b/snapshot/ios/thread_snapshot_ios_intermediate_dump.h @@ -12,36 +12,33 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CRASHPAD_SNAPSHOT_IOS_THREAD_SNAPSHOT_IOS_H_ -#define CRASHPAD_SNAPSHOT_IOS_THREAD_SNAPSHOT_IOS_H_ +#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ +#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ #include "base/macros.h" #include "build/build_config.h" #include "snapshot/cpu_context.h" -#include "snapshot/ios/memory_snapshot_ios.h" +#include "snapshot/ios/memory_snapshot_ios_intermediate_dump.h" #include "snapshot/thread_snapshot.h" +#include "util/ios/ios_intermediate_dump_map.h" #include "util/misc/initialization_state_dcheck.h" namespace crashpad { namespace internal { //! \brief A ThreadSnapshot of a thread on an iOS system. -class ThreadSnapshotIOS final : public ThreadSnapshot { +class ThreadSnapshotIOSIntermediateDump final : public ThreadSnapshot { public: - ThreadSnapshotIOS(); - ~ThreadSnapshotIOS() override; + ThreadSnapshotIOSIntermediateDump(); + ~ThreadSnapshotIOSIntermediateDump() override; //! \brief Initializes the object. //! - //! \brief thread The Mach thread used to initialize this object. - bool Initialize(thread_t thread); - - //! \brief Returns an array of thread_t threads. + //! \brief thread_data The intermediate dump map used to initialize this + //! object. //! - //! \param[out] count The number of threads returned. - //! - //! \return An array of of size \a count threads. - static thread_act_array_t GetThreads(mach_msg_type_number_t* count); + //! \return `true` if the snapshot could be created. + bool Initialize(const IOSIntermediateDumpMap* thread_data); // ThreadSnapshot: const CPUContext* Context() const override; @@ -61,17 +58,20 @@ class ThreadSnapshotIOS final : public ThreadSnapshot { #error Port. #endif // ARCH_CPU_X86_64 CPUContext context_; - MemorySnapshotIOS stack_; + std::vector exception_stack_memory_; + MemorySnapshotIOSIntermediateDump stack_; uint64_t thread_id_; uint64_t thread_specific_data_address_; int suspend_count_; int priority_; + std::vector> + extra_memory_; InitializationStateDcheck initialized_; - DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotIOS); + DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotIOSIntermediateDump); }; } // namespace internal } // namespace crashpad -#endif // CRASHPAD_SNAPSHOT_IOS_THREAD_SNAPSHOT_IOS_H_ +#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ diff --git a/util/misc/metrics.cc b/util/misc/metrics.cc index 4b87f107..d45fad65 100644 --- a/util/misc/metrics.cc +++ b/util/misc/metrics.cc @@ -110,4 +110,24 @@ void Metrics::HandlerCrashed(uint32_t exception_code) { "Crashpad.HandlerCrash.ExceptionCode." METRICS_OS_NAME, exception_code); } +#if defined(OS_IOS) +// static +void Metrics::MissingIntermediateDumpKey( + const internal::IntermediateDumpKey& key) { + UMA_HISTOGRAM_ENUMERATION("Crashpad.IntermediateDump.Reader.MissingKey", + key, + internal::IntermediateDumpKey::kMaxValue); +} + +// static +void Metrics::InvalidIntermediateDumpKeySize( + const internal::IntermediateDumpKey& key) { + UMA_HISTOGRAM_ENUMERATION("Crashpad.IntermediateDump.Reader.InvalidKeySize", + key, + internal::IntermediateDumpKey::kMaxValue); +} +#endif + +// static + } // namespace crashpad diff --git a/util/misc/metrics.h b/util/misc/metrics.h index d9763812..292f23a0 100644 --- a/util/misc/metrics.h +++ b/util/misc/metrics.h @@ -18,8 +18,13 @@ #include #include "base/macros.h" +#include "build/build_config.h" #include "util/file/file_io.h" +#if defined(OS_IOS) +#include "util/ios/ios_intermediate_dump_format.h" +#endif + namespace crashpad { //! \brief Container class to hold shared UMA metrics integration points. @@ -195,6 +200,16 @@ class Metrics { //! This is currently only reported on Windows. static void HandlerCrashed(uint32_t exception_code); +#if defined(OS_IOS) || DOXYGEN + //! \brief Records a missing key from an intermediate dump. + static void MissingIntermediateDumpKey( + const internal::IntermediateDumpKey& key); + + //! \brief Records a key with an invalid key size from an intermediate dump. + static void InvalidIntermediateDumpKeySize( + const internal::IntermediateDumpKey& key); +#endif + private: DISALLOW_IMPLICIT_CONSTRUCTORS(Metrics); };