mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 22:26:06 +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 = [
|
deps = [
|
||||||
"client:client_test",
|
"client:client_test",
|
||||||
"minidump:minidump_test",
|
"minidump:minidump_test",
|
||||||
|
"snapshot:snapshot_test",
|
||||||
"test:googlemock_main",
|
"test:googlemock_main",
|
||||||
"test:test_test",
|
"test:test_test",
|
||||||
"util:util_test",
|
"util:util_test",
|
||||||
]
|
]
|
||||||
if (!crashpad_is_ios) {
|
|
||||||
deps += [ "snapshot:snapshot_test" ]
|
|
||||||
}
|
|
||||||
if (!crashpad_is_ios && !crashpad_is_fuchsia) {
|
if (!crashpad_is_ios && !crashpad_is_fuchsia) {
|
||||||
deps += [ "handler:handler_test" ]
|
deps += [ "handler:handler_test" ]
|
||||||
}
|
}
|
||||||
@ -150,9 +148,6 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
|
|||||||
"snapshot:snapshot_test",
|
"snapshot:snapshot_test",
|
||||||
"test:googletest_main",
|
"test:googletest_main",
|
||||||
]
|
]
|
||||||
if (crashpad_is_ios) {
|
|
||||||
deps -= [ "snapshot:snapshot_test" ]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("crashpad_test_test") {
|
test("crashpad_test_test") {
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/mac/mach_logging.h"
|
#include "base/mac/mach_logging.h"
|
||||||
#include "base/mac/scoped_mach_port.h"
|
#include "base/mac/scoped_mach_port.h"
|
||||||
#include "snapshot/ios/process_snapshot_ios.h"
|
|
||||||
#include "util/ios/exception_processor.h"
|
#include "util/ios/exception_processor.h"
|
||||||
#include "util/ios/ios_system_data_collector.h"
|
#include "util/ios/ios_system_data_collector.h"
|
||||||
#include "util/mach/exc_server_variants.h"
|
#include "util/mach/exc_server_variants.h"
|
||||||
@ -168,16 +167,6 @@ class CrashHandler : public Thread, public UniversalMachExcServer::Interface {
|
|||||||
ConstThreadState old_state,
|
ConstThreadState old_state,
|
||||||
mach_msg_type_number_t old_state_count) {
|
mach_msg_type_number_t old_state_count) {
|
||||||
// TODO(justincohen): This is incomplete.
|
// 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.
|
// The signal handler installed at OS-level.
|
||||||
@ -190,9 +179,6 @@ class CrashHandler : public Thread, public UniversalMachExcServer::Interface {
|
|||||||
siginfo_t* siginfo,
|
siginfo_t* siginfo,
|
||||||
ucontext_t* context) {
|
ucontext_t* context) {
|
||||||
// TODO(justincohen): This is incomplete.
|
// TODO(justincohen): This is incomplete.
|
||||||
ProcessSnapshotIOS process_snapshot;
|
|
||||||
process_snapshot.Initialize(system_data_);
|
|
||||||
process_snapshot.SetExceptionFromSignal(siginfo, context);
|
|
||||||
|
|
||||||
// Always call system handler.
|
// Always call system handler.
|
||||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, &old_action_);
|
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, &old_action_);
|
||||||
|
@ -107,18 +107,20 @@ crashpad_static_library("snapshot") {
|
|||||||
|
|
||||||
if (crashpad_is_ios) {
|
if (crashpad_is_ios) {
|
||||||
sources += [
|
sources += [
|
||||||
"ios/exception_snapshot_ios.cc",
|
"ios/exception_snapshot_ios_intermediate_dump.cc",
|
||||||
"ios/exception_snapshot_ios.h",
|
"ios/exception_snapshot_ios_intermediate_dump.h",
|
||||||
"ios/memory_snapshot_ios.cc",
|
"ios/intermediate_dump_reader_util.cc",
|
||||||
"ios/memory_snapshot_ios.h",
|
"ios/intermediate_dump_reader_util.h",
|
||||||
"ios/module_snapshot_ios.cc",
|
"ios/memory_snapshot_ios_intermediate_dump.cc",
|
||||||
"ios/module_snapshot_ios.h",
|
"ios/memory_snapshot_ios_intermediate_dump.h",
|
||||||
"ios/process_snapshot_ios.cc",
|
"ios/module_snapshot_ios_intermediate_dump.cc",
|
||||||
"ios/process_snapshot_ios.h",
|
"ios/module_snapshot_ios_intermediate_dump.h",
|
||||||
"ios/system_snapshot_ios.cc",
|
"ios/process_snapshot_ios_intermediate_dump.cc",
|
||||||
"ios/system_snapshot_ios.h",
|
"ios/process_snapshot_ios_intermediate_dump.h",
|
||||||
"ios/thread_snapshot_ios.cc",
|
"ios/system_snapshot_ios_intermediate_dump.cc",
|
||||||
"ios/thread_snapshot_ios.h",
|
"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.cc",
|
||||||
"mac/cpu_context_mac.h",
|
"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) {
|
if (crashpad_is_linux || crashpad_is_android) {
|
||||||
sources += [
|
sources += [
|
||||||
"linux/debug_rendezvous_test.cc",
|
"linux/debug_rendezvous_test.cc",
|
||||||
@ -433,6 +442,10 @@ source_set("snapshot_test") {
|
|||||||
deps += [ "../client" ]
|
deps += [ "../client" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (crashpad_is_ios) {
|
||||||
|
deps += [ "../minidump" ]
|
||||||
|
}
|
||||||
|
|
||||||
data_deps = [
|
data_deps = [
|
||||||
":crashpad_snapshot_test_module",
|
":crashpad_snapshot_test_module",
|
||||||
":crashpad_snapshot_test_module_large",
|
":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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#ifndef CRASHPAD_SNAPSHOT_IOS_EXCEPTION_SNAPSHOT_IOS_H_
|
#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_EXCEPTION_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
|
||||||
#define CRASHPAD_SNAPSHOT_IOS_EXCEPTION_SNAPSHOT_IOS_H_
|
#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_EXCEPTION_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
|
||||||
|
|
||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@ -24,6 +24,7 @@
|
|||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
#include "snapshot/cpu_context.h"
|
#include "snapshot/cpu_context.h"
|
||||||
#include "snapshot/exception_snapshot.h"
|
#include "snapshot/exception_snapshot.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_map.h"
|
||||||
#include "util/mach/mach_extensions.h"
|
#include "util/mach/mach_extensions.h"
|
||||||
#include "util/misc/initialization_state_dcheck.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
|
//! \brief An ExceptionSnapshot of an exception sustained by a running (or
|
||||||
//! crashed) process on an iOS system.
|
//! crashed) process on an iOS system.
|
||||||
class ExceptionSnapshotIOS final : public ExceptionSnapshot {
|
class ExceptionSnapshotIOSIntermediateDump final : public ExceptionSnapshot {
|
||||||
public:
|
public:
|
||||||
ExceptionSnapshotIOS();
|
ExceptionSnapshotIOSIntermediateDump();
|
||||||
~ExceptionSnapshotIOS() override;
|
~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
|
//! \return `true` if the snapshot could be created, `false` otherwise with
|
||||||
//! an appropriate message logged.
|
//! an appropriate message logged.
|
||||||
void InitializeFromSignal(const siginfo_t* siginfo,
|
bool InitializeFromSignal(const IOSIntermediateDumpMap* exception_data);
|
||||||
const ucontext_t* context);
|
|
||||||
|
|
||||||
//! \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
|
//! \return `true` if the snapshot could be created, `false` otherwise with
|
||||||
//! an appropriate message logged.
|
//! an appropriate message logged.
|
||||||
void InitializeFromMachException(exception_behavior_t behavior,
|
bool InitializeFromMachException(const IOSIntermediateDumpMap* exception_data,
|
||||||
thread_t exception_thread,
|
const IOSIntermediateDumpList* thread_list);
|
||||||
exception_type_t exception,
|
|
||||||
const mach_exception_data_type_t* code,
|
//! \brief Initialize the object as an NSException from an intermediate dump.
|
||||||
mach_msg_type_number_t code_count,
|
//!
|
||||||
thread_state_flavor_t flavor,
|
//! \param[in] exception_data The intermediate dump map used to initialize
|
||||||
ConstThreadState state,
|
//! this object.
|
||||||
mach_msg_type_number_t state_count);
|
//! \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:
|
// ExceptionSnapshot:
|
||||||
|
|
||||||
const CPUContext* Context() const override;
|
const CPUContext* Context() const override;
|
||||||
uint64_t ThreadID() const override;
|
uint64_t ThreadID() const override;
|
||||||
uint32_t Exception() const override;
|
uint32_t Exception() const override;
|
||||||
@ -68,6 +83,11 @@ class ExceptionSnapshotIOS final : public ExceptionSnapshot {
|
|||||||
virtual std::vector<const MemorySnapshot*> ExtraMemory() const override;
|
virtual std::vector<const MemorySnapshot*> ExtraMemory() const override;
|
||||||
|
|
||||||
private:
|
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)
|
#if defined(ARCH_CPU_X86_64)
|
||||||
CPUContextX86_64 context_x86_64_;
|
CPUContextX86_64 context_x86_64_;
|
||||||
#elif defined(ARCH_CPU_ARM64)
|
#elif defined(ARCH_CPU_ARM64)
|
||||||
@ -83,10 +103,10 @@ class ExceptionSnapshotIOS final : public ExceptionSnapshot {
|
|||||||
uint32_t exception_info_;
|
uint32_t exception_info_;
|
||||||
InitializationStateDcheck initialized_;
|
InitializationStateDcheck initialized_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ExceptionSnapshotIOS);
|
DISALLOW_COPY_AND_ASSIGN(ExceptionSnapshotIOSIntermediateDump);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace crashpad
|
} // 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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include "snapshot/ios/memory_snapshot_ios.h"
|
#include "snapshot/ios/memory_snapshot_ios_intermediate_dump.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace internal {
|
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_);
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||||
address_ = address;
|
address_ = address;
|
||||||
|
data_ = data;
|
||||||
size_ = base::checked_cast<size_t>(size);
|
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_);
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t MemorySnapshotIOS::Address() const {
|
uint64_t MemorySnapshotIOSIntermediateDump::Address() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
return address_;
|
return address_;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t MemorySnapshotIOS::Size() const {
|
size_t MemorySnapshotIOSIntermediateDump::Size() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
return size_;
|
return size_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MemorySnapshotIOS::Read(Delegate* delegate) const {
|
bool MemorySnapshotIOSIntermediateDump::Read(Delegate* delegate) const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
|
||||||
if (size_ == 0) {
|
if (size_ == 0) {
|
||||||
return delegate->MemorySnapshotDelegateRead(nullptr, size_);
|
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 {
|
const MemorySnapshot* other) const {
|
||||||
CheckedRange<uint64_t, size_t> merged(0, 0);
|
CheckedRange<uint64_t, size_t> merged(0, 0);
|
||||||
if (!LoggingDetermineMergedRange(this, other, &merged))
|
if (!LoggingDetermineMergedRange(this, other, &merged))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
auto result = std::make_unique<MemorySnapshotIOS>();
|
auto result = std::make_unique<MemorySnapshotIOSIntermediateDump>();
|
||||||
result->Initialize(merged.base(), merged.size());
|
result->Initialize(merged.base(), data_, merged.size());
|
||||||
return result.release();
|
return result.release();
|
||||||
}
|
}
|
||||||
|
|
@ -12,8 +12,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#ifndef CRASHPAD_SNAPSHOT_IOS_MEMORY_SNAPSHOT_IOS_H_
|
#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MEMORY_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
|
||||||
#define CRASHPAD_SNAPSHOT_IOS_MEMORY_SNAPSHOT_IOS_H_
|
#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MEMORY_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
|
||||||
|
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
#include "snapshot/memory_snapshot.h"
|
#include "snapshot/memory_snapshot.h"
|
||||||
@ -24,16 +24,16 @@ namespace crashpad {
|
|||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
//! \brief A MemorySnapshot of a memory region.
|
//! \brief A MemorySnapshot of a memory region.
|
||||||
class MemorySnapshotIOS final : public MemorySnapshot {
|
class MemorySnapshotIOSIntermediateDump final : public MemorySnapshot {
|
||||||
public:
|
public:
|
||||||
MemorySnapshotIOS() = default;
|
MemorySnapshotIOSIntermediateDump() = default;
|
||||||
~MemorySnapshotIOS() = default;
|
~MemorySnapshotIOSIntermediateDump() = default;
|
||||||
|
|
||||||
//! \brief Initializes the object.
|
//! \brief Initializes the object.
|
||||||
//!
|
//!
|
||||||
//! \param[in] address The base address of the memory region to snapshot.
|
//! \param[in] address The base address of the memory region to snapshot.
|
||||||
//! \param[in] size The size 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:
|
// MemorySnapshot:
|
||||||
uint64_t Address() const override;
|
uint64_t Address() const override;
|
||||||
@ -48,16 +48,15 @@ class MemorySnapshotIOS final : public MemorySnapshot {
|
|||||||
const T* self,
|
const T* self,
|
||||||
const MemorySnapshot* other);
|
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 address_;
|
||||||
|
vm_address_t data_;
|
||||||
vm_size_t size_;
|
vm_size_t size_;
|
||||||
InitializationStateDcheck initialized_;
|
InitializationStateDcheck initialized_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(MemorySnapshotIOS);
|
DISALLOW_COPY_AND_ASSIGN(MemorySnapshotIOSIntermediateDump);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace crashpad
|
} // 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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#ifndef CRASHPAD_SNAPSHOT_IOS_MODULE_SNAPSHOT_IOS_H_
|
#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MODULE_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
|
||||||
#define CRASHPAD_SNAPSHOT_IOS_MODULE_SNAPSHOT_IOS_H_
|
#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MODULE_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
|
||||||
|
|
||||||
#include <mach-o/dyld_images.h>
|
#include <mach-o/dyld_images.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@ -26,6 +26,7 @@
|
|||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
#include "snapshot/crashpad_info_client_options.h"
|
#include "snapshot/crashpad_info_client_options.h"
|
||||||
#include "snapshot/module_snapshot.h"
|
#include "snapshot/module_snapshot.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_map.h"
|
||||||
#include "util/misc/initialization_state_dcheck.h"
|
#include "util/misc/initialization_state_dcheck.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
@ -33,37 +34,18 @@ namespace internal {
|
|||||||
|
|
||||||
//! \brief A ModuleSnapshot of a code module (binary image) loaded into a
|
//! \brief A ModuleSnapshot of a code module (binary image) loaded into a
|
||||||
//! running (or crashed) process on an iOS system.
|
//! running (or crashed) process on an iOS system.
|
||||||
class ModuleSnapshotIOS final : public ModuleSnapshot {
|
class ModuleSnapshotIOSIntermediateDump final : public ModuleSnapshot {
|
||||||
public:
|
public:
|
||||||
ModuleSnapshotIOS();
|
ModuleSnapshotIOSIntermediateDump();
|
||||||
~ModuleSnapshotIOS() override;
|
~ModuleSnapshotIOSIntermediateDump() override;
|
||||||
|
|
||||||
// TODO(justincohen): This function is temporary, and will be broken into two
|
//! \brief Initialize the snapshot
|
||||||
// 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.
|
|
||||||
//!
|
//!
|
||||||
//! \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.
|
//! \return `true` if the snapshot could be created.
|
||||||
bool Initialize(const dyld_image_info* image);
|
bool Initialize(const IOSIntermediateDumpMap* image_data);
|
||||||
|
|
||||||
// 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();
|
|
||||||
|
|
||||||
// ModuleSnapshot:
|
// ModuleSnapshot:
|
||||||
std::string Name() const override;
|
std::string Name() const override;
|
||||||
@ -89,9 +71,6 @@ class ModuleSnapshotIOS final : public ModuleSnapshot {
|
|||||||
std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;
|
std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Gather the the module information based off of a mach_header_64 |address_|.
|
|
||||||
bool FinishInitialization();
|
|
||||||
|
|
||||||
std::string name_;
|
std::string name_;
|
||||||
uint64_t address_;
|
uint64_t address_;
|
||||||
uint64_t size_;
|
uint64_t size_;
|
||||||
@ -100,12 +79,16 @@ class ModuleSnapshotIOS final : public ModuleSnapshot {
|
|||||||
uint64_t source_version_;
|
uint64_t source_version_;
|
||||||
uint32_t filetype_;
|
uint32_t filetype_;
|
||||||
UUID uuid_;
|
UUID uuid_;
|
||||||
|
std::vector<std::string> annotations_vector_;
|
||||||
|
std::map<std::string, std::string> annotations_simple_map_;
|
||||||
|
std::vector<AnnotationSnapshot> annotation_objects_;
|
||||||
|
|
||||||
InitializationStateDcheck initialized_;
|
InitializationStateDcheck initialized_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotIOS);
|
DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotIOSIntermediateDump);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace crashpad
|
} // 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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#ifndef CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_
|
#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_PROCESS_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
|
||||||
#define CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_
|
#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_PROCESS_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
|
||||||
|
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "snapshot/ios/exception_snapshot_ios.h"
|
#include "base/files/file_path.h"
|
||||||
#include "snapshot/ios/module_snapshot_ios.h"
|
#include "snapshot/ios/exception_snapshot_ios_intermediate_dump.h"
|
||||||
#include "snapshot/ios/system_snapshot_ios.h"
|
#include "snapshot/ios/module_snapshot_ios_intermediate_dump.h"
|
||||||
#include "snapshot/ios/thread_snapshot_ios.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/process_snapshot.h"
|
||||||
#include "snapshot/thread_snapshot.h"
|
#include "snapshot/thread_snapshot.h"
|
||||||
#include "snapshot/unloaded_module_snapshot.h"
|
#include "snapshot/unloaded_module_snapshot.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_reader.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
//! \brief A ProcessSnapshot of a running (or crashed) process running on a
|
//! \brief A ProcessSnapshot of a running (or crashed) process running on a
|
||||||
//! iphoneOS system.
|
//! iphoneOS system.
|
||||||
class ProcessSnapshotIOS final : public ProcessSnapshot {
|
class ProcessSnapshotIOSIntermediateDump final : public ProcessSnapshot {
|
||||||
public:
|
public:
|
||||||
ProcessSnapshotIOS();
|
ProcessSnapshotIOSIntermediateDump() = default;
|
||||||
~ProcessSnapshotIOS() override;
|
|
||||||
|
|
||||||
//! \brief Initializes the object.
|
//! \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
|
//! \return `true` if the snapshot could be created, `false` otherwise with
|
||||||
//! an appropriate message logged.
|
//! 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,
|
//! 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,
|
//! which may call this method to set the client ID. If this is not done,
|
||||||
//! ClientID() will return an identifier consisting entirely of zeroes.
|
//! ClientID() will return an identifier consisting entirely of zeroes.
|
||||||
@ -93,30 +81,33 @@ class ProcessSnapshotIOS final : public ProcessSnapshot {
|
|||||||
const ProcessMemory* Memory() const override;
|
const ProcessMemory* Memory() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Initializes modules_ on behalf of Initialize().
|
// Retain the reader for the lifetime of the ProcessSnapshot so large chunks
|
||||||
void InitializeModules();
|
// of data do not need to be copied around (such as MemorySnapshot
|
||||||
|
// intermediate dumps).
|
||||||
// Initializes threads_ on behalf of Initialize().
|
IOSIntermediateDumpReader reader_;
|
||||||
void InitializeThreads();
|
pid_t p_pid_;
|
||||||
|
pid_t e_ppid_;
|
||||||
kinfo_proc kern_proc_info_;
|
timeval p_starttime_;
|
||||||
time_value_t basic_info_user_time_;
|
time_value_t basic_info_user_time_;
|
||||||
time_value_t basic_info_system_time_;
|
time_value_t basic_info_system_time_;
|
||||||
time_value_t thread_times_user_time_;
|
time_value_t thread_times_user_time_;
|
||||||
time_value_t thread_times_system_time_;
|
time_value_t thread_times_system_time_;
|
||||||
internal::SystemSnapshotIOS system_;
|
internal::SystemSnapshotIOSIntermediateDump system_;
|
||||||
std::vector<std::unique_ptr<internal::ThreadSnapshotIOS>> threads_;
|
std::vector<std::unique_ptr<internal::ThreadSnapshotIOSIntermediateDump>>
|
||||||
std::vector<std::unique_ptr<internal::ModuleSnapshotIOS>> modules_;
|
threads_;
|
||||||
std::unique_ptr<internal::ExceptionSnapshotIOS> exception_;
|
std::vector<std::unique_ptr<internal::ModuleSnapshotIOSIntermediateDump>>
|
||||||
|
modules_;
|
||||||
|
std::unique_ptr<internal::ExceptionSnapshotIOSIntermediateDump> exception_;
|
||||||
UUID report_id_;
|
UUID report_id_;
|
||||||
UUID client_id_;
|
UUID client_id_;
|
||||||
std::map<std::string, std::string> annotations_simple_map_;
|
std::map<std::string, std::string> annotations_simple_map_;
|
||||||
timeval snapshot_time_;
|
timeval snapshot_time_;
|
||||||
InitializationStateDcheck initialized_;
|
InitializationStateDcheck initialized_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotIOS);
|
DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotIOSIntermediateDump);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
} // namespace crashpad
|
} // 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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// 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 <mach/mach.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
@ -27,7 +27,9 @@
|
|||||||
#include "base/strings/stringprintf.h"
|
#include "base/strings/stringprintf.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
#include "snapshot/cpu_context.h"
|
#include "snapshot/cpu_context.h"
|
||||||
|
#include "snapshot/ios/intermediate_dump_reader_util.h"
|
||||||
#include "snapshot/posix/timezone.h"
|
#include "snapshot/posix/timezone.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_data.h"
|
||||||
#include "util/mac/mac_util.h"
|
#include "util/mac/mac_util.h"
|
||||||
#include "util/numeric/in_range_cast.h"
|
#include "util/numeric/in_range_cast.h"
|
||||||
|
|
||||||
@ -35,7 +37,9 @@ namespace crashpad {
|
|||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
SystemSnapshotIOS::SystemSnapshotIOS()
|
using Key = IntermediateDumpKey;
|
||||||
|
|
||||||
|
SystemSnapshotIOSIntermediateDump::SystemSnapshotIOSIntermediateDump()
|
||||||
: SystemSnapshot(),
|
: SystemSnapshot(),
|
||||||
os_version_build_(),
|
os_version_build_(),
|
||||||
machine_description_(),
|
machine_description_(),
|
||||||
@ -55,51 +59,67 @@ SystemSnapshotIOS::SystemSnapshotIOS()
|
|||||||
daylight_name_(),
|
daylight_name_(),
|
||||||
initialized_() {}
|
initialized_() {}
|
||||||
|
|
||||||
SystemSnapshotIOS::~SystemSnapshotIOS() {}
|
SystemSnapshotIOSIntermediateDump::~SystemSnapshotIOSIntermediateDump() {}
|
||||||
|
|
||||||
void SystemSnapshotIOS::Initialize(const IOSSystemDataCollector& system_data) {
|
void SystemSnapshotIOSIntermediateDump::Initialize(
|
||||||
|
const IOSIntermediateDumpMap* system_data) {
|
||||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||||
|
|
||||||
system_data.OSVersion(
|
GetDataStringFromMap(system_data, Key::kOSVersionBuild, &os_version_build_);
|
||||||
&os_version_major_, &os_version_minor_, &os_version_bugfix_);
|
GetDataStringFromMap(
|
||||||
os_version_build_ = system_data.Build();
|
system_data, Key::kMachineDescription, &machine_description_);
|
||||||
machine_description_ = system_data.MachineDescription();
|
GetDataStringFromMap(system_data, Key::kCpuVendor, &cpu_vendor_);
|
||||||
cpu_count_ = system_data.ProcessorCount();
|
GetDataStringFromMap(system_data, Key::kStandardName, &standard_name_);
|
||||||
cpu_vendor_ = system_data.CPUVendor();
|
GetDataStringFromMap(system_data, Key::kDaylightName, &daylight_name_);
|
||||||
if (system_data.HasDaylightSavingTime()) {
|
|
||||||
dst_status_ = system_data.IsDaylightSavingTime()
|
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::kObservingDaylightSavingTime
|
||||||
: SystemSnapshot::kObservingStandardTime;
|
: SystemSnapshot::kObservingStandardTime;
|
||||||
} else {
|
} else {
|
||||||
dst_status_ = SystemSnapshot::kDoesNotObserveDaylightSavingTime;
|
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;
|
vm_size_t page_size;
|
||||||
host_page_size(mach_host_self(), &page_size);
|
if (GetDataValueFromMap(system_data, Key::kPageSize, &page_size)) {
|
||||||
mach_msg_type_number_t host_size =
|
const IOSIntermediateDumpMap* vm_stat =
|
||||||
sizeof(vm_statistics_data_t) / sizeof(integer_t);
|
GetMapFromMap(system_data, Key::kVMStat);
|
||||||
vm_statistics_data_t vm_stat;
|
if (vm_stat) {
|
||||||
kern_return_t kr = host_statistics(mach_host_self(),
|
GetDataValueFromMap(vm_stat, Key::kActive, &active_);
|
||||||
HOST_VM_INFO,
|
active_ *= page_size;
|
||||||
reinterpret_cast<host_info_t>(&vm_stat),
|
|
||||||
&host_size);
|
GetDataValueFromMap(vm_stat, Key::kInactive, &inactive_);
|
||||||
if (kr != KERN_SUCCESS) {
|
inactive_ *= page_size;
|
||||||
MACH_LOG(WARNING, kr) << "host_statistics";
|
|
||||||
|
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_);
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||||
}
|
}
|
||||||
|
|
||||||
CPUArchitecture SystemSnapshotIOS::GetCPUArchitecture() const {
|
CPUArchitecture SystemSnapshotIOSIntermediateDump::GetCPUArchitecture() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
#if defined(ARCH_CPU_X86_64)
|
#if defined(ARCH_CPU_X86_64)
|
||||||
return kCPUArchitectureX86_64;
|
return kCPUArchitectureX86_64;
|
||||||
@ -108,24 +128,24 @@ CPUArchitecture SystemSnapshotIOS::GetCPUArchitecture() const {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t SystemSnapshotIOS::CPURevision() const {
|
uint32_t SystemSnapshotIOSIntermediateDump::CPURevision() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
// TODO(justincohen): sysctlbyname machdep.cpu.* returns -1 on iOS/ARM64, but
|
// TODO(justincohen): sysctlbyname machdep.cpu.* returns -1 on iOS/ARM64, but
|
||||||
// consider recording this for X86_64 only.
|
// consider recording this for X86_64 only.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t SystemSnapshotIOS::CPUCount() const {
|
uint8_t SystemSnapshotIOSIntermediateDump::CPUCount() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
return cpu_count_;
|
return cpu_count_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SystemSnapshotIOS::CPUVendor() const {
|
std::string SystemSnapshotIOSIntermediateDump::CPUVendor() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
return cpu_vendor_;
|
return cpu_vendor_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemSnapshotIOS::CPUFrequency(uint64_t* current_hz,
|
void SystemSnapshotIOSIntermediateDump::CPUFrequency(uint64_t* current_hz,
|
||||||
uint64_t* max_hz) const {
|
uint64_t* max_hz) const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
// TODO(justincohen): sysctlbyname hw.cpufrequency returns -1 on iOS/ARM64,
|
// TODO(justincohen): sysctlbyname hw.cpufrequency returns -1 on iOS/ARM64,
|
||||||
@ -134,47 +154,48 @@ void SystemSnapshotIOS::CPUFrequency(uint64_t* current_hz,
|
|||||||
*max_hz = 0;
|
*max_hz = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t SystemSnapshotIOS::CPUX86Signature() const {
|
uint32_t SystemSnapshotIOSIntermediateDump::CPUX86Signature() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
// TODO(justincohen): Consider recording this for X86_64 only.
|
// TODO(justincohen): Consider recording this for X86_64 only.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t SystemSnapshotIOS::CPUX86Features() const {
|
uint64_t SystemSnapshotIOSIntermediateDump::CPUX86Features() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
// TODO(justincohen): Consider recording this for X86_64 only.
|
// TODO(justincohen): Consider recording this for X86_64 only.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t SystemSnapshotIOS::CPUX86ExtendedFeatures() const {
|
uint64_t SystemSnapshotIOSIntermediateDump::CPUX86ExtendedFeatures() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
// TODO(justincohen): Consider recording this for X86_64 only.
|
// TODO(justincohen): Consider recording this for X86_64 only.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t SystemSnapshotIOS::CPUX86Leaf7Features() const {
|
uint32_t SystemSnapshotIOSIntermediateDump::CPUX86Leaf7Features() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
// TODO(justincohen): Consider recording this for X86_64 only.
|
// TODO(justincohen): Consider recording this for X86_64 only.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemSnapshotIOS::CPUX86SupportsDAZ() const {
|
bool SystemSnapshotIOSIntermediateDump::CPUX86SupportsDAZ() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
// TODO(justincohen): Consider recording this for X86_64 only.
|
// TODO(justincohen): Consider recording this for X86_64 only.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemSnapshot::OperatingSystem SystemSnapshotIOS::GetOperatingSystem() const {
|
SystemSnapshot::OperatingSystem
|
||||||
|
SystemSnapshotIOSIntermediateDump::GetOperatingSystem() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
return kOperatingSystemIOS;
|
return kOperatingSystemIOS;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemSnapshotIOS::OSServer() const {
|
bool SystemSnapshotIOSIntermediateDump::OSServer() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemSnapshotIOS::OSVersion(int* major,
|
void SystemSnapshotIOSIntermediateDump::OSVersion(int* major,
|
||||||
int* minor,
|
int* minor,
|
||||||
int* bugfix,
|
int* bugfix,
|
||||||
std::string* build) const {
|
std::string* build) const {
|
||||||
@ -185,7 +206,7 @@ void SystemSnapshotIOS::OSVersion(int* major,
|
|||||||
build->assign(os_version_build_);
|
build->assign(os_version_build_);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SystemSnapshotIOS::OSVersionFull() const {
|
std::string SystemSnapshotIOSIntermediateDump::OSVersionFull() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
return base::StringPrintf("%d.%d.%d %s",
|
return base::StringPrintf("%d.%d.%d %s",
|
||||||
os_version_major_,
|
os_version_major_,
|
||||||
@ -194,19 +215,20 @@ std::string SystemSnapshotIOS::OSVersionFull() const {
|
|||||||
os_version_build_.c_str());
|
os_version_build_.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SystemSnapshotIOS::MachineDescription() const {
|
std::string SystemSnapshotIOSIntermediateDump::MachineDescription() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
return machine_description_;
|
return machine_description_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemSnapshotIOS::NXEnabled() const {
|
bool SystemSnapshotIOSIntermediateDump::NXEnabled() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
// TODO(justincohen): Consider using kern.nx when available (pre-iOS 13,
|
// TODO(justincohen): Consider using kern.nx when available (pre-iOS 13,
|
||||||
// pre-OS X 10.15). Otherwise the bit is always enabled.
|
// pre-OS X 10.15). Otherwise the bit is always enabled.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemSnapshotIOS::TimeZone(DaylightSavingTimeStatus* dst_status,
|
void SystemSnapshotIOSIntermediateDump::TimeZone(
|
||||||
|
DaylightSavingTimeStatus* dst_status,
|
||||||
int* standard_offset_seconds,
|
int* standard_offset_seconds,
|
||||||
int* daylight_offset_seconds,
|
int* daylight_offset_seconds,
|
||||||
std::string* standard_name,
|
std::string* standard_name,
|
@ -12,8 +12,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#ifndef CRASHPAD_SNAPSHOT_IOS_SYSTEM_SNAPSHOT_IOS_H_
|
#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_SYSTEM_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
|
||||||
#define CRASHPAD_SNAPSHOT_IOS_SYSTEM_SNAPSHOT_IOS_H_
|
#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_SYSTEM_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
#include "snapshot/system_snapshot.h"
|
#include "snapshot/system_snapshot.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_map.h"
|
||||||
#include "util/ios/ios_system_data_collector.h"
|
#include "util/ios/ios_system_data_collector.h"
|
||||||
#include "util/misc/initialization_state_dcheck.h"
|
#include "util/misc/initialization_state_dcheck.h"
|
||||||
|
|
||||||
@ -29,15 +30,17 @@ namespace crashpad {
|
|||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
//! \brief A SystemSnapshot of the running system, when the system runs iOS.
|
//! \brief A SystemSnapshot of the running system, when the system runs iOS.
|
||||||
class SystemSnapshotIOS final : public SystemSnapshot {
|
class SystemSnapshotIOSIntermediateDump final : public SystemSnapshot {
|
||||||
public:
|
public:
|
||||||
SystemSnapshotIOS();
|
SystemSnapshotIOSIntermediateDump();
|
||||||
~SystemSnapshotIOS() override;
|
~SystemSnapshotIOSIntermediateDump() override;
|
||||||
|
|
||||||
//! \brief Initializes the object.
|
//! \brief Initializes the object.
|
||||||
//!
|
//!
|
||||||
//! \param[in] system_data A class containing various system data points.
|
//! \param[in] system_data An intermediate dump map containing various system
|
||||||
void Initialize(const IOSSystemDataCollector& system_data);
|
//! data points.
|
||||||
|
//! \return `true` if the snapshot could be created.
|
||||||
|
void Initialize(const IOSIntermediateDumpMap* system_data);
|
||||||
|
|
||||||
// SystemSnapshot:
|
// SystemSnapshot:
|
||||||
|
|
||||||
@ -72,10 +75,10 @@ class SystemSnapshotIOS final : public SystemSnapshot {
|
|||||||
int os_version_major_;
|
int os_version_major_;
|
||||||
int os_version_minor_;
|
int os_version_minor_;
|
||||||
int os_version_bugfix_;
|
int os_version_bugfix_;
|
||||||
uint64_t active_;
|
uint32_t active_;
|
||||||
uint64_t inactive_;
|
uint32_t inactive_;
|
||||||
uint64_t wired_;
|
uint32_t wired_;
|
||||||
uint64_t free_;
|
uint32_t free_;
|
||||||
int cpu_count_;
|
int cpu_count_;
|
||||||
std::string cpu_vendor_;
|
std::string cpu_vendor_;
|
||||||
DaylightSavingTimeStatus dst_status_;
|
DaylightSavingTimeStatus dst_status_;
|
||||||
@ -85,10 +88,10 @@ class SystemSnapshotIOS final : public SystemSnapshot {
|
|||||||
std::string daylight_name_;
|
std::string daylight_name_;
|
||||||
InitializationStateDcheck initialized_;
|
InitializationStateDcheck initialized_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(SystemSnapshotIOS);
|
DISALLOW_COPY_AND_ASSIGN(SystemSnapshotIOSIntermediateDump);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace crashpad
|
} // 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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#ifndef CRASHPAD_SNAPSHOT_IOS_THREAD_SNAPSHOT_IOS_H_
|
#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
|
||||||
#define CRASHPAD_SNAPSHOT_IOS_THREAD_SNAPSHOT_IOS_H_
|
#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
|
||||||
|
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
#include "snapshot/cpu_context.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 "snapshot/thread_snapshot.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_map.h"
|
||||||
#include "util/misc/initialization_state_dcheck.h"
|
#include "util/misc/initialization_state_dcheck.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
//! \brief A ThreadSnapshot of a thread on an iOS system.
|
//! \brief A ThreadSnapshot of a thread on an iOS system.
|
||||||
class ThreadSnapshotIOS final : public ThreadSnapshot {
|
class ThreadSnapshotIOSIntermediateDump final : public ThreadSnapshot {
|
||||||
public:
|
public:
|
||||||
ThreadSnapshotIOS();
|
ThreadSnapshotIOSIntermediateDump();
|
||||||
~ThreadSnapshotIOS() override;
|
~ThreadSnapshotIOSIntermediateDump() override;
|
||||||
|
|
||||||
//! \brief Initializes the object.
|
//! \brief Initializes the object.
|
||||||
//!
|
//!
|
||||||
//! \brief thread The Mach thread used to initialize this object.
|
//! \brief thread_data The intermediate dump map used to initialize this
|
||||||
bool Initialize(thread_t thread);
|
//! object.
|
||||||
|
|
||||||
//! \brief Returns an array of thread_t threads.
|
|
||||||
//!
|
//!
|
||||||
//! \param[out] count The number of threads returned.
|
//! \return `true` if the snapshot could be created.
|
||||||
//!
|
bool Initialize(const IOSIntermediateDumpMap* thread_data);
|
||||||
//! \return An array of of size \a count threads.
|
|
||||||
static thread_act_array_t GetThreads(mach_msg_type_number_t* count);
|
|
||||||
|
|
||||||
// ThreadSnapshot:
|
// ThreadSnapshot:
|
||||||
const CPUContext* Context() const override;
|
const CPUContext* Context() const override;
|
||||||
@ -61,17 +58,20 @@ class ThreadSnapshotIOS final : public ThreadSnapshot {
|
|||||||
#error Port.
|
#error Port.
|
||||||
#endif // ARCH_CPU_X86_64
|
#endif // ARCH_CPU_X86_64
|
||||||
CPUContext context_;
|
CPUContext context_;
|
||||||
MemorySnapshotIOS stack_;
|
std::vector<uint8_t> exception_stack_memory_;
|
||||||
|
MemorySnapshotIOSIntermediateDump stack_;
|
||||||
uint64_t thread_id_;
|
uint64_t thread_id_;
|
||||||
uint64_t thread_specific_data_address_;
|
uint64_t thread_specific_data_address_;
|
||||||
int suspend_count_;
|
int suspend_count_;
|
||||||
int priority_;
|
int priority_;
|
||||||
|
std::vector<std::unique_ptr<internal::MemorySnapshotIOSIntermediateDump>>
|
||||||
|
extra_memory_;
|
||||||
InitializationStateDcheck initialized_;
|
InitializationStateDcheck initialized_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotIOS);
|
DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotIOSIntermediateDump);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace crashpad
|
} // 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);
|
"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
|
} // namespace crashpad
|
||||||
|
@ -18,8 +18,13 @@
|
|||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
#include "util/file/file_io.h"
|
#include "util/file/file_io.h"
|
||||||
|
|
||||||
|
#if defined(OS_IOS)
|
||||||
|
#include "util/ios/ios_intermediate_dump_format.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
//! \brief Container class to hold shared UMA metrics integration points.
|
//! \brief Container class to hold shared UMA metrics integration points.
|
||||||
@ -195,6 +200,16 @@ class Metrics {
|
|||||||
//! This is currently only reported on Windows.
|
//! This is currently only reported on Windows.
|
||||||
static void HandlerCrashed(uint32_t exception_code);
|
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:
|
private:
|
||||||
DISALLOW_IMPLICIT_CONSTRUCTORS(Metrics);
|
DISALLOW_IMPLICIT_CONSTRUCTORS(Metrics);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user