mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
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 <justincohen@chromium.org> Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
e58fde685b
commit
7f6f917aac
7
BUILD.gn
7
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") {
|
||||
|
@ -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_);
|
||||
|
@ -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",
|
||||
|
@ -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<thread_info_t>(&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<uintptr_t>(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_t>(&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<thread_state_t>(&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<thread_state_t>(&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<thread_info_t>(&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<uint64_t>& ExceptionSnapshotIOS::Codes() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return codes_;
|
||||
}
|
||||
|
||||
std::vector<const MemorySnapshot*> ExceptionSnapshotIOS::ExtraMemory() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<const MemorySnapshot*>();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
391
snapshot/ios/exception_snapshot_ios_intermediate_dump.cc
Normal file
391
snapshot/ios/exception_snapshot_ios_intermediate_dump.cc
Normal file
@ -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<uint8_t>& bytes = code_dump->bytes();
|
||||
const mach_exception_data_type_t* code =
|
||||
reinterpret_cast<const mach_exception_data_type_t*>(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<uint64_t>& ExceptionSnapshotIOSIntermediateDump::Codes()
|
||||
const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return codes_;
|
||||
}
|
||||
|
||||
std::vector<const MemorySnapshot*>
|
||||
ExceptionSnapshotIOSIntermediateDump::ExtraMemory() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<const MemorySnapshot*>();
|
||||
}
|
||||
|
||||
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<uint8_t>& bytes = state_dump->bytes();
|
||||
size_t actual_length = bytes.size();
|
||||
if (expected_length == actual_length) {
|
||||
const ConstThreadState state =
|
||||
reinterpret_cast<const ConstThreadState>(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<uint8_t>& bytes = frames_dump->bytes();
|
||||
const uint64_t* frames = reinterpret_cast<const uint64_t*>(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
|
@ -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 <mach/mach.h>
|
||||
#include <stdint.h>
|
||||
@ -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<const MemorySnapshot*> 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_
|
93
snapshot/ios/intermediate_dump_reader_util.cc
Normal file
93
snapshot/ios/intermediate_dump_reader_util.cc
Normal file
@ -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
|
116
snapshot/ios/intermediate_dump_reader_util.h
Normal file
116
snapshot/ios/intermediate_dump_reader_util.h
Normal file
@ -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 <ostream>
|
||||
|
||||
#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 <typename T>
|
||||
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_
|
@ -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_t>(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<uint8_t[]>(new uint8_t[size_]);
|
||||
memcpy(buffer_.get(), reinterpret_cast<void*>(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<void*>(data_),
|
||||
size_);
|
||||
}
|
||||
|
||||
const MemorySnapshot* MemorySnapshotIOS::MergeWithOtherSnapshot(
|
||||
const MemorySnapshot* MemorySnapshotIOSIntermediateDump::MergeWithOtherSnapshot(
|
||||
const MemorySnapshot* other) const {
|
||||
CheckedRange<uint64_t, size_t> merged(0, 0);
|
||||
if (!LoggingDetermineMergedRange(this, other, &merged))
|
||||
return nullptr;
|
||||
|
||||
auto result = std::make_unique<MemorySnapshotIOS>();
|
||||
result->Initialize(merged.base(), merged.size());
|
||||
auto result = std::make_unique<MemorySnapshotIOSIntermediateDump>();
|
||||
result->Initialize(merged.base(), data_, merged.size());
|
||||
return result.release();
|
||||
}
|
||||
|
@ -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<uint8_t[]> 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_
|
@ -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 <mach-o/loader.h>
|
||||
#include <mach/mach.h>
|
||||
|
||||
#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<task_info_t>(&dyld_info),
|
||||
&count);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
MACH_LOG(WARNING, kr) << "task_info";
|
||||
return 0;
|
||||
}
|
||||
|
||||
return reinterpret_cast<dyld_all_image_infos*>(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<uint64_t>(images->dyldImageLoadAddress);
|
||||
return FinishInitialization();
|
||||
}
|
||||
|
||||
bool ModuleSnapshotIOS::Initialize(const dyld_image_info* image) {
|
||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||
|
||||
name_ = image->imageFilePath;
|
||||
address_ = FromPointerCast<uint64_t>(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<const mach_header_64*>(address_);
|
||||
const load_command* command =
|
||||
reinterpret_cast<const load_command*>(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<const segment_command_64*>(command);
|
||||
if (strcmp(segment->segname, SEG_TEXT) == 0) {
|
||||
size_ = segment->vmsize;
|
||||
}
|
||||
} else if (command->cmd == LC_ID_DYLIB) {
|
||||
const dylib_command* dylib =
|
||||
reinterpret_cast<const dylib_command*>(command);
|
||||
dylib_version_ = dylib->dylib.current_version;
|
||||
} else if (command->cmd == LC_SOURCE_VERSION) {
|
||||
const source_version_command* source_version =
|
||||
reinterpret_cast<const source_version_command*>(command);
|
||||
source_version_ = source_version->version;
|
||||
} else if (command->cmd == LC_UUID) {
|
||||
const uuid_command* uuid = reinterpret_cast<const uuid_command*>(command);
|
||||
uuid_.InitializeFromBytes(uuid->uuid);
|
||||
}
|
||||
|
||||
command = reinterpret_cast<const load_command*>(
|
||||
reinterpret_cast<const uint8_t*>(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<uint8_t> ModuleSnapshotIOS::BuildID() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<uint8_t>();
|
||||
}
|
||||
|
||||
std::vector<std::string> ModuleSnapshotIOS::AnnotationsVector() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> ModuleSnapshotIOS::AnnotationsSimpleMap()
|
||||
const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::map<std::string, std::string>();
|
||||
}
|
||||
|
||||
std::vector<AnnotationSnapshot> ModuleSnapshotIOS::AnnotationObjects() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<AnnotationSnapshot>();
|
||||
}
|
||||
|
||||
std::set<CheckedRange<uint64_t>> ModuleSnapshotIOS::ExtraMemoryRanges() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::set<CheckedRange<uint64_t>>();
|
||||
}
|
||||
|
||||
std::vector<const UserMinidumpStream*>
|
||||
ModuleSnapshotIOS::CustomMinidumpStreams() const {
|
||||
return std::vector<const UserMinidumpStream*>();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
266
snapshot/ios/module_snapshot_ios_intermediate_dump.cc
Normal file
266
snapshot/ios/module_snapshot_ios_intermediate_dump.cc
Normal file
@ -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 <mach-o/loader.h>
|
||||
#include <mach/mach.h>
|
||||
|
||||
#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<uint8_t>& 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<uint16_t>(&type)) {
|
||||
const std::vector<uint8_t>& 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<uint8_t> ModuleSnapshotIOSIntermediateDump::BuildID() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<uint8_t>();
|
||||
}
|
||||
|
||||
std::vector<std::string> ModuleSnapshotIOSIntermediateDump::AnnotationsVector()
|
||||
const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return annotations_vector_;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string>
|
||||
ModuleSnapshotIOSIntermediateDump::AnnotationsSimpleMap() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return annotations_simple_map_;
|
||||
}
|
||||
|
||||
std::vector<AnnotationSnapshot>
|
||||
ModuleSnapshotIOSIntermediateDump::AnnotationObjects() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return annotation_objects_;
|
||||
}
|
||||
|
||||
std::set<CheckedRange<uint64_t>>
|
||||
ModuleSnapshotIOSIntermediateDump::ExtraMemoryRanges() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::set<CheckedRange<uint64_t>>();
|
||||
}
|
||||
|
||||
std::vector<const UserMinidumpStream*>
|
||||
ModuleSnapshotIOSIntermediateDump::CustomMinidumpStreams() const {
|
||||
return std::vector<const UserMinidumpStream*>();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
@ -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 <mach-o/dyld_images.h>
|
||||
#include <stdint.h>
|
||||
@ -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<const UserMinidumpStream*> 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<std::string> annotations_vector_;
|
||||
std::map<std::string, std::string> annotations_simple_map_;
|
||||
std::vector<AnnotationSnapshot> 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_
|
@ -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 <mach-o/loader.h>
|
||||
#include <mach/mach.h>
|
||||
|
||||
#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_info_t>(&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_info_t>(&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<std::string, std::string>&
|
||||
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<const ThreadSnapshot*> ProcessSnapshotIOS::Threads() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
std::vector<const ThreadSnapshot*> threads;
|
||||
for (const auto& thread : threads_) {
|
||||
threads.push_back(thread.get());
|
||||
}
|
||||
return threads;
|
||||
}
|
||||
|
||||
std::vector<const ModuleSnapshot*> ProcessSnapshotIOS::Modules() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
std::vector<const ModuleSnapshot*> modules;
|
||||
for (const auto& module : modules_) {
|
||||
modules.push_back(module.get());
|
||||
}
|
||||
return modules;
|
||||
}
|
||||
|
||||
std::vector<UnloadedModuleSnapshot> ProcessSnapshotIOS::UnloadedModules()
|
||||
const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<UnloadedModuleSnapshot>();
|
||||
}
|
||||
|
||||
const ExceptionSnapshot* ProcessSnapshotIOS::Exception() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return exception_.get();
|
||||
}
|
||||
|
||||
std::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotIOS::MemoryMap()
|
||||
const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<const MemoryMapRegionSnapshot*>();
|
||||
}
|
||||
|
||||
std::vector<HandleSnapshot> ProcessSnapshotIOS::Handles() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<HandleSnapshot>();
|
||||
}
|
||||
|
||||
std::vector<const MemorySnapshot*> ProcessSnapshotIOS::ExtraMemory() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<const MemorySnapshot*>();
|
||||
}
|
||||
|
||||
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<internal::ThreadSnapshotIOS>();
|
||||
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<vm_address_t>(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<internal::ModuleSnapshotIOS>();
|
||||
if (module->Initialize(image)) {
|
||||
modules_.push_back(std::move(module));
|
||||
}
|
||||
}
|
||||
auto module = std::make_unique<internal::ModuleSnapshotIOS>();
|
||||
if (module->InitializeDyld(image_infos)) {
|
||||
modules_.push_back(std::move(module));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
273
snapshot/ios/process_snapshot_ios_intermediate_dump.cc
Normal file
273
snapshot/ios/process_snapshot_ios_intermediate_dump.cc
Normal file
@ -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 <sys/stat.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "snapshot/ios/intermediate_dump_reader_util.h"
|
||||
#include "util/ios/ios_intermediate_dump_data.h"
|
||||
#include "util/ios/ios_intermediate_dump_list.h"
|
||||
#include "util/ios/ios_intermediate_dump_map.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void MachTimeValueToTimeval(const time_value& mach, timeval* tv) {
|
||||
tv->tv_sec = mach.seconds;
|
||||
tv->tv_usec = mach.microseconds;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
using Key = internal::IntermediateDumpKey;
|
||||
|
||||
bool ProcessSnapshotIOSIntermediateDump::Initialize(
|
||||
const base::FilePath& dump_path,
|
||||
const std::map<std::string, std::string>& 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<internal::ThreadSnapshotIOSIntermediateDump>();
|
||||
if (thread->Initialize(value.get())) {
|
||||
threads_.push_back(std::move(thread));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const IOSIntermediateDumpList* module_list =
|
||||
GetListFromMap(root_map, Key::kModules);
|
||||
if (module_list) {
|
||||
for (const auto& value : *module_list) {
|
||||
auto module =
|
||||
std::make_unique<internal::ModuleSnapshotIOSIntermediateDump>();
|
||||
if (module->Initialize(value.get())) {
|
||||
modules_.push_back(std::move(module));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exceptions
|
||||
const IOSIntermediateDumpMap* signal_exception =
|
||||
root_map->GetAsMap(Key::kSignalException);
|
||||
const IOSIntermediateDumpMap* mach_exception =
|
||||
root_map->GetAsMap(Key::kMachException);
|
||||
const IOSIntermediateDumpMap* ns_exception =
|
||||
root_map->GetAsMap(Key::kNSException);
|
||||
if (signal_exception) {
|
||||
exception_.reset(new internal::ExceptionSnapshotIOSIntermediateDump());
|
||||
if (!exception_->InitializeFromSignal(signal_exception)) {
|
||||
LOG(ERROR) << "Process snapshot could not initialize signal exception.";
|
||||
return false;
|
||||
}
|
||||
} else if (mach_exception) {
|
||||
exception_.reset(new internal::ExceptionSnapshotIOSIntermediateDump());
|
||||
if (!exception_->InitializeFromMachException(
|
||||
mach_exception, GetListFromMap(root_map, Key::kThreads))) {
|
||||
LOG(ERROR) << "Process snapshot could not initialize Mach exception.";
|
||||
return false;
|
||||
}
|
||||
} else if (ns_exception) {
|
||||
exception_.reset(new internal::ExceptionSnapshotIOSIntermediateDump());
|
||||
if (!exception_->InitializeFromNSException(
|
||||
ns_exception, GetListFromMap(root_map, Key::kThreads))) {
|
||||
LOG(ERROR) << "Process snapshot could not initialize NSException.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||
return true;
|
||||
}
|
||||
|
||||
pid_t ProcessSnapshotIOSIntermediateDump::ProcessID() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return p_pid_;
|
||||
}
|
||||
|
||||
pid_t ProcessSnapshotIOSIntermediateDump::ParentProcessID() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return e_ppid_;
|
||||
}
|
||||
|
||||
void ProcessSnapshotIOSIntermediateDump::SnapshotTime(
|
||||
timeval* snapshot_time) const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
*snapshot_time = snapshot_time_;
|
||||
}
|
||||
|
||||
void ProcessSnapshotIOSIntermediateDump::ProcessStartTime(
|
||||
timeval* start_time) const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
*start_time = p_starttime_;
|
||||
}
|
||||
|
||||
void ProcessSnapshotIOSIntermediateDump::ProcessCPUTimes(
|
||||
timeval* user_time,
|
||||
timeval* system_time) const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
|
||||
// Calculate user and system time the same way the kernel does for
|
||||
// getrusage(). See 10.15.0 xnu-6153.11.26/bsd/kern/kern_resource.c calcru().
|
||||
timerclear(user_time);
|
||||
timerclear(system_time);
|
||||
|
||||
MachTimeValueToTimeval(basic_info_user_time_, user_time);
|
||||
MachTimeValueToTimeval(basic_info_system_time_, system_time);
|
||||
|
||||
timeval thread_user_time;
|
||||
MachTimeValueToTimeval(thread_times_user_time_, &thread_user_time);
|
||||
timeval thread_system_time;
|
||||
MachTimeValueToTimeval(thread_times_system_time_, &thread_system_time);
|
||||
|
||||
timeradd(user_time, &thread_user_time, user_time);
|
||||
timeradd(system_time, &thread_system_time, system_time);
|
||||
}
|
||||
|
||||
void ProcessSnapshotIOSIntermediateDump::ReportID(UUID* report_id) const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
*report_id = report_id_;
|
||||
}
|
||||
|
||||
void ProcessSnapshotIOSIntermediateDump::ClientID(UUID* client_id) const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
*client_id = client_id_;
|
||||
}
|
||||
|
||||
const std::map<std::string, std::string>&
|
||||
ProcessSnapshotIOSIntermediateDump::AnnotationsSimpleMap() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return annotations_simple_map_;
|
||||
}
|
||||
|
||||
const SystemSnapshot* ProcessSnapshotIOSIntermediateDump::System() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return &system_;
|
||||
}
|
||||
|
||||
std::vector<const ThreadSnapshot*> ProcessSnapshotIOSIntermediateDump::Threads()
|
||||
const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
std::vector<const ThreadSnapshot*> threads;
|
||||
for (const auto& thread : threads_) {
|
||||
threads.push_back(thread.get());
|
||||
}
|
||||
return threads;
|
||||
}
|
||||
|
||||
std::vector<const ModuleSnapshot*> ProcessSnapshotIOSIntermediateDump::Modules()
|
||||
const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
std::vector<const ModuleSnapshot*> modules;
|
||||
for (const auto& module : modules_) {
|
||||
modules.push_back(module.get());
|
||||
}
|
||||
return modules;
|
||||
}
|
||||
|
||||
std::vector<UnloadedModuleSnapshot>
|
||||
ProcessSnapshotIOSIntermediateDump::UnloadedModules() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<UnloadedModuleSnapshot>();
|
||||
}
|
||||
|
||||
const ExceptionSnapshot* ProcessSnapshotIOSIntermediateDump::Exception() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return exception_.get();
|
||||
}
|
||||
|
||||
std::vector<const MemoryMapRegionSnapshot*>
|
||||
ProcessSnapshotIOSIntermediateDump::MemoryMap() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<const MemoryMapRegionSnapshot*>();
|
||||
}
|
||||
|
||||
std::vector<HandleSnapshot> ProcessSnapshotIOSIntermediateDump::Handles()
|
||||
const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<HandleSnapshot>();
|
||||
}
|
||||
|
||||
std::vector<const MemorySnapshot*>
|
||||
ProcessSnapshotIOSIntermediateDump::ExtraMemory() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return std::vector<const MemorySnapshot*>();
|
||||
}
|
||||
|
||||
const ProcessMemory* ProcessSnapshotIOSIntermediateDump::Memory() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
@ -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 <sys/sysctl.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#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<std::string, std::string>& 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<std::unique_ptr<internal::ThreadSnapshotIOS>> threads_;
|
||||
std::vector<std::unique_ptr<internal::ModuleSnapshotIOS>> modules_;
|
||||
std::unique_ptr<internal::ExceptionSnapshotIOS> exception_;
|
||||
internal::SystemSnapshotIOSIntermediateDump system_;
|
||||
std::vector<std::unique_ptr<internal::ThreadSnapshotIOSIntermediateDump>>
|
||||
threads_;
|
||||
std::vector<std::unique_ptr<internal::ModuleSnapshotIOSIntermediateDump>>
|
||||
modules_;
|
||||
std::unique_ptr<internal::ExceptionSnapshotIOSIntermediateDump> exception_;
|
||||
UUID report_id_;
|
||||
UUID client_id_;
|
||||
std::map<std::string, std::string> 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_
|
640
snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc
Normal file
640
snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc
Normal file
@ -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 <mach-o/loader.h>
|
||||
|
||||
#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<const char*>(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<internal::IOSIntermediateDumpWriter>();
|
||||
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<const ModuleSnapshot*>& 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<const char*>(
|
||||
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<const void*>(&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<const ThreadSnapshot*>& 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<internal::IOSIntermediateDumpWriter> writer_;
|
||||
ScopedTempDir temp_dir_;
|
||||
base::FilePath path_;
|
||||
std::map<std::string, std::string> 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
|
@ -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 <mach/mach.h>
|
||||
#include <stddef.h>
|
||||
@ -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<host_info_t>(&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_;
|
@ -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 <stdint.h>
|
||||
|
||||
@ -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_
|
@ -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<vm_region_recurse_info_t>(&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_t>(&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<thread_state_t>(&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<thread_state_t>(&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<thread_info_t>(&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<thread_info_t>(&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<thread_policy_t>(&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<const MemorySnapshot*> ThreadSnapshotIOS::ExtraMemory() const {
|
||||
return std::vector<const MemorySnapshot*>();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
240
snapshot/ios/thread_snapshot_ios_intermediate_dump.cc
Normal file
240
snapshot/ios/thread_snapshot_ios_intermediate_dump.cc
Normal file
@ -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 <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<uint8_t> GenerateStackMemoryFromFrames(const uint64_t* frames,
|
||||
const size_t frame_count) {
|
||||
std::vector<uint8_t> 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<uint8_t>& bytes = thread_stack_data_dump->bytes();
|
||||
const vm_address_t stack_region_data =
|
||||
reinterpret_cast<const vm_address_t>(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<uint8_t>& bytes = nsexception_frames->bytes();
|
||||
const uint64_t* frames = reinterpret_cast<const uint64_t*>(bytes.data());
|
||||
size_t frame_count = bytes.size() / sizeof(uint64_t);
|
||||
exception_stack_memory_ =
|
||||
GenerateStackMemoryFromFrames(frames, frame_count);
|
||||
stack_.Initialize(
|
||||
0,
|
||||
reinterpret_cast<vm_address_t>(&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<uint8_t>& bytes = region_data->bytes();
|
||||
vm_size_t data_size = bytes.size();
|
||||
if (data_size == 0)
|
||||
continue;
|
||||
|
||||
const vm_address_t data =
|
||||
reinterpret_cast<const vm_address_t>(bytes.data());
|
||||
|
||||
auto memory =
|
||||
std::make_unique<internal::MemorySnapshotIOSIntermediateDump>();
|
||||
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<const MemorySnapshot*>
|
||||
ThreadSnapshotIOSIntermediateDump::ExtraMemory() const {
|
||||
std::vector<const MemorySnapshot*> extra_memory;
|
||||
for (const auto& memory : extra_memory_) {
|
||||
extra_memory.push_back(memory.get());
|
||||
}
|
||||
return extra_memory;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
@ -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<uint8_t> exception_stack_memory_;
|
||||
MemorySnapshotIOSIntermediateDump stack_;
|
||||
uint64_t thread_id_;
|
||||
uint64_t thread_specific_data_address_;
|
||||
int suspend_count_;
|
||||
int priority_;
|
||||
std::vector<std::unique_ptr<internal::MemorySnapshotIOSIntermediateDump>>
|
||||
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_
|
@ -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
|
||||
|
@ -18,8 +18,13 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
#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);
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user