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:
Justin Cohen 2021-05-26 15:12:33 -04:00 committed by Crashpad LUCI CQ
parent e58fde685b
commit 7f6f917aac
24 changed files with 2300 additions and 1519 deletions

View File

@ -25,13 +25,11 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
deps = [
"client:client_test",
"minidump:minidump_test",
"snapshot:snapshot_test",
"test:googlemock_main",
"test:test_test",
"util:util_test",
]
if (!crashpad_is_ios) {
deps += [ "snapshot:snapshot_test" ]
}
if (!crashpad_is_ios && !crashpad_is_fuchsia) {
deps += [ "handler:handler_test" ]
}
@ -150,9 +148,6 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
"snapshot:snapshot_test",
"test:googletest_main",
]
if (crashpad_is_ios) {
deps -= [ "snapshot:snapshot_test" ]
}
}
test("crashpad_test_test") {

View File

@ -22,7 +22,6 @@
#include "base/logging.h"
#include "base/mac/mach_logging.h"
#include "base/mac/scoped_mach_port.h"
#include "snapshot/ios/process_snapshot_ios.h"
#include "util/ios/exception_processor.h"
#include "util/ios/ios_system_data_collector.h"
#include "util/mach/exc_server_variants.h"
@ -168,16 +167,6 @@ class CrashHandler : public Thread, public UniversalMachExcServer::Interface {
ConstThreadState old_state,
mach_msg_type_number_t old_state_count) {
// TODO(justincohen): This is incomplete.
ProcessSnapshotIOS process_snapshot;
process_snapshot.Initialize(system_data_);
process_snapshot.SetExceptionFromMachException(behavior,
thread,
exception,
code,
code_count,
flavor,
old_state,
old_state_count);
}
// The signal handler installed at OS-level.
@ -190,9 +179,6 @@ class CrashHandler : public Thread, public UniversalMachExcServer::Interface {
siginfo_t* siginfo,
ucontext_t* context) {
// TODO(justincohen): This is incomplete.
ProcessSnapshotIOS process_snapshot;
process_snapshot.Initialize(system_data_);
process_snapshot.SetExceptionFromSignal(siginfo, context);
// Always call system handler.
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, &old_action_);

View File

@ -107,18 +107,20 @@ crashpad_static_library("snapshot") {
if (crashpad_is_ios) {
sources += [
"ios/exception_snapshot_ios.cc",
"ios/exception_snapshot_ios.h",
"ios/memory_snapshot_ios.cc",
"ios/memory_snapshot_ios.h",
"ios/module_snapshot_ios.cc",
"ios/module_snapshot_ios.h",
"ios/process_snapshot_ios.cc",
"ios/process_snapshot_ios.h",
"ios/system_snapshot_ios.cc",
"ios/system_snapshot_ios.h",
"ios/thread_snapshot_ios.cc",
"ios/thread_snapshot_ios.h",
"ios/exception_snapshot_ios_intermediate_dump.cc",
"ios/exception_snapshot_ios_intermediate_dump.h",
"ios/intermediate_dump_reader_util.cc",
"ios/intermediate_dump_reader_util.h",
"ios/memory_snapshot_ios_intermediate_dump.cc",
"ios/memory_snapshot_ios_intermediate_dump.h",
"ios/module_snapshot_ios_intermediate_dump.cc",
"ios/module_snapshot_ios_intermediate_dump.h",
"ios/process_snapshot_ios_intermediate_dump.cc",
"ios/process_snapshot_ios_intermediate_dump.h",
"ios/system_snapshot_ios_intermediate_dump.cc",
"ios/system_snapshot_ios_intermediate_dump.h",
"ios/thread_snapshot_ios_intermediate_dump.cc",
"ios/thread_snapshot_ios_intermediate_dump.h",
"mac/cpu_context_mac.cc",
"mac/cpu_context_mac.h",
]
@ -358,6 +360,13 @@ source_set("snapshot_test") {
]
}
if (crashpad_is_ios) {
sources += [
"ios/process_snapshot_ios_intermediate_dump_test.cc",
"mac/cpu_context_mac_test.cc",
]
}
if (crashpad_is_linux || crashpad_is_android) {
sources += [
"linux/debug_rendezvous_test.cc",
@ -433,6 +442,10 @@ source_set("snapshot_test") {
deps += [ "../client" ]
}
if (crashpad_is_ios) {
deps += [ "../minidump" ]
}
data_deps = [
":crashpad_snapshot_test_module",
":crashpad_snapshot_test_module_large",

View File

@ -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
// its not). code[1] may carry the exception address for other exception
// types too, but its 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 its 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

View 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
// its not). code[1] may carry the exception address for other exception
// types too, but its 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 its 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

View File

@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_SNAPSHOT_IOS_EXCEPTION_SNAPSHOT_IOS_H_
#define CRASHPAD_SNAPSHOT_IOS_EXCEPTION_SNAPSHOT_IOS_H_
#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_EXCEPTION_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_EXCEPTION_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
#include <mach/mach.h>
#include <stdint.h>
@ -24,6 +24,7 @@
#include "build/build_config.h"
#include "snapshot/cpu_context.h"
#include "snapshot/exception_snapshot.h"
#include "util/ios/ios_intermediate_dump_map.h"
#include "util/mach/mach_extensions.h"
#include "util/misc/initialization_state_dcheck.h"
@ -33,32 +34,46 @@ namespace internal {
//! \brief An ExceptionSnapshot of an exception sustained by a running (or
//! crashed) process on an iOS system.
class ExceptionSnapshotIOS final : public ExceptionSnapshot {
class ExceptionSnapshotIOSIntermediateDump final : public ExceptionSnapshot {
public:
ExceptionSnapshotIOS();
~ExceptionSnapshotIOS() override;
ExceptionSnapshotIOSIntermediateDump();
~ExceptionSnapshotIOSIntermediateDump() override;
//! \brief Initializes the object from a signal.
//! \brief Initialize the snapshot as a signal exception.
//!
//! \param[in] exception_data The intermediate dump map used to initialize
//! this object.
//!
//! \return `true` if the snapshot could be created, `false` otherwise with
//! an appropriate message logged.
void InitializeFromSignal(const siginfo_t* siginfo,
const ucontext_t* context);
bool InitializeFromSignal(const IOSIntermediateDumpMap* exception_data);
//! \brief Initialize the object from a Mach exception for the current task.
//! \brief Initialize the object as a Mach exception from an intermediate
//! dump.
//!
//! \param[in] exception_data The intermediate dump map used to initialize
//! this object.
//! \param[in] thread_list The intermediate dump map containing list of
//! threads.
//!
//! \return `true` if the snapshot could be created, `false` otherwise with
//! an appropriate message logged.
void InitializeFromMachException(exception_behavior_t behavior,
thread_t exception_thread,
exception_type_t exception,
const mach_exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t flavor,
ConstThreadState state,
mach_msg_type_number_t state_count);
bool InitializeFromMachException(const IOSIntermediateDumpMap* exception_data,
const IOSIntermediateDumpList* thread_list);
//! \brief Initialize the object as an NSException from an intermediate dump.
//!
//! \param[in] exception_data The intermediate dump map used to initialize
//! this object.
//! \param[in] thread_list The intermediate dump map containing list of
//! threads.
//!
//! \return `true` if the snapshot could be created, `false` otherwise with
//! an appropriate message logged.
bool InitializeFromNSException(const IOSIntermediateDumpMap* exception_data,
const IOSIntermediateDumpList* thread_list);
// ExceptionSnapshot:
const CPUContext* Context() const override;
uint64_t ThreadID() const override;
uint32_t Exception() const override;
@ -68,6 +83,11 @@ class ExceptionSnapshotIOS final : public ExceptionSnapshot {
virtual std::vector<const MemorySnapshot*> ExtraMemory() const override;
private:
void LoadContextFromUncaughtNSExceptionFrames(
const IOSIntermediateDumpData* data,
const IOSIntermediateDumpMap* other_thread);
void LoadContextFromThread(const IOSIntermediateDumpMap* exception_data,
const IOSIntermediateDumpMap* other_thread);
#if defined(ARCH_CPU_X86_64)
CPUContextX86_64 context_x86_64_;
#elif defined(ARCH_CPU_ARM64)
@ -83,10 +103,10 @@ class ExceptionSnapshotIOS final : public ExceptionSnapshot {
uint32_t exception_info_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ExceptionSnapshotIOS);
DISALLOW_COPY_AND_ASSIGN(ExceptionSnapshotIOSIntermediateDump);
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_IOS_EXCEPTION_SNAPSHOT_IOS_H_
#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_EXCEPTION_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_

View 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

View 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_

View File

@ -12,52 +12,50 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/ios/memory_snapshot_ios.h"
#include "snapshot/ios/memory_snapshot_ios_intermediate_dump.h"
namespace crashpad {
namespace internal {
void MemorySnapshotIOS::Initialize(vm_address_t address, vm_size_t size) {
void MemorySnapshotIOSIntermediateDump::Initialize(vm_address_t address,
vm_address_t data,
vm_size_t size) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
address_ = address;
data_ = data;
size_ = base::checked_cast<size_t>(size);
// TODO(justincohen): This is temporary, as MemorySnapshotIOS will likely be
// able to point directly to the deserialized data dump rather than copying
// data around.
buffer_ = std::unique_ptr<uint8_t[]>(new uint8_t[size_]);
memcpy(buffer_.get(), reinterpret_cast<void*>(address_), size_);
INITIALIZATION_STATE_SET_VALID(initialized_);
}
uint64_t MemorySnapshotIOS::Address() const {
uint64_t MemorySnapshotIOSIntermediateDump::Address() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return address_;
}
size_t MemorySnapshotIOS::Size() const {
size_t MemorySnapshotIOSIntermediateDump::Size() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return size_;
}
bool MemorySnapshotIOS::Read(Delegate* delegate) const {
bool MemorySnapshotIOSIntermediateDump::Read(Delegate* delegate) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
if (size_ == 0) {
return delegate->MemorySnapshotDelegateRead(nullptr, size_);
}
return delegate->MemorySnapshotDelegateRead(buffer_.get(), size_);
return delegate->MemorySnapshotDelegateRead(reinterpret_cast<void*>(data_),
size_);
}
const MemorySnapshot* MemorySnapshotIOS::MergeWithOtherSnapshot(
const MemorySnapshot* MemorySnapshotIOSIntermediateDump::MergeWithOtherSnapshot(
const MemorySnapshot* other) const {
CheckedRange<uint64_t, size_t> merged(0, 0);
if (!LoggingDetermineMergedRange(this, other, &merged))
return nullptr;
auto result = std::make_unique<MemorySnapshotIOS>();
result->Initialize(merged.base(), merged.size());
auto result = std::make_unique<MemorySnapshotIOSIntermediateDump>();
result->Initialize(merged.base(), data_, merged.size());
return result.release();
}

View File

@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_SNAPSHOT_IOS_MEMORY_SNAPSHOT_IOS_H_
#define CRASHPAD_SNAPSHOT_IOS_MEMORY_SNAPSHOT_IOS_H_
#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MEMORY_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MEMORY_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
#include "base/macros.h"
#include "snapshot/memory_snapshot.h"
@ -24,16 +24,16 @@ namespace crashpad {
namespace internal {
//! \brief A MemorySnapshot of a memory region.
class MemorySnapshotIOS final : public MemorySnapshot {
class MemorySnapshotIOSIntermediateDump final : public MemorySnapshot {
public:
MemorySnapshotIOS() = default;
~MemorySnapshotIOS() = default;
MemorySnapshotIOSIntermediateDump() = default;
~MemorySnapshotIOSIntermediateDump() = default;
//! \brief Initializes the object.
//!
//! \param[in] address The base address of the memory region to snapshot.
//! \param[in] size The size of the memory region to snapshot.
void Initialize(vm_address_t address, vm_size_t size);
void Initialize(vm_address_t address, vm_address_t data, vm_size_t size);
// MemorySnapshot:
uint64_t Address() const override;
@ -48,16 +48,15 @@ class MemorySnapshotIOS final : public MemorySnapshot {
const T* self,
const MemorySnapshot* other);
// TODO(justincohen): This is temporary until deserialization is worked out.
std::unique_ptr<uint8_t[]> buffer_;
vm_address_t address_;
vm_address_t data_;
vm_size_t size_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(MemorySnapshotIOS);
DISALLOW_COPY_AND_ASSIGN(MemorySnapshotIOSIntermediateDump);
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_IOS_MEMORY_SNAPSHOT_IOS_H_
#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MEMORY_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_

View File

@ -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 doesnt 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

View 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,
&timestamp_,
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

View File

@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_SNAPSHOT_IOS_MODULE_SNAPSHOT_IOS_H_
#define CRASHPAD_SNAPSHOT_IOS_MODULE_SNAPSHOT_IOS_H_
#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MODULE_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MODULE_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
#include <mach-o/dyld_images.h>
#include <stdint.h>
@ -26,6 +26,7 @@
#include "base/macros.h"
#include "snapshot/crashpad_info_client_options.h"
#include "snapshot/module_snapshot.h"
#include "util/ios/ios_intermediate_dump_map.h"
#include "util/misc/initialization_state_dcheck.h"
namespace crashpad {
@ -33,37 +34,18 @@ namespace internal {
//! \brief A ModuleSnapshot of a code module (binary image) loaded into a
//! running (or crashed) process on an iOS system.
class ModuleSnapshotIOS final : public ModuleSnapshot {
class ModuleSnapshotIOSIntermediateDump final : public ModuleSnapshot {
public:
ModuleSnapshotIOS();
~ModuleSnapshotIOS() override;
ModuleSnapshotIOSIntermediateDump();
~ModuleSnapshotIOSIntermediateDump() override;
// TODO(justincohen): This function is temporary, and will be broken into two
// parts. One to do an in-process dump of all the relevant information, and
// two to initialize the snapshot after the in-process dump is loaded.
//! \brief Initializes the object.
//! \brief Initialize the snapshot
//!
//! \param[in] image The mach-o image to be loaded.
//! \param[in] exception_data The intermediate dump map used to initialize
//! this object.
//!
//! \return `true` if the snapshot could be created.
bool Initialize(const dyld_image_info* image);
// TODO(justincohen): This function is temporary, and will be broken into two
// parts. One to do an in-process dump of all the relevant information, and
// two to initialize the snapshot after the in-process dump is loaded.
//! \brief Initializes the object specifically for the dyld module.
//!
//! \param[in] images The structure containing the necessary dyld information.
//!
//! \return `true` if the snapshot could be created.
bool InitializeDyld(const dyld_all_image_infos* images);
//! \brief Returns options from the modules CrashpadInfo structure.
//!
//! \param[out] options Options set in the modules CrashpadInfo structure.
void GetCrashpadOptions(CrashpadInfoClientOptions* options);
static const dyld_all_image_infos* DyldAllImageInfo();
bool Initialize(const IOSIntermediateDumpMap* image_data);
// ModuleSnapshot:
std::string Name() const override;
@ -89,9 +71,6 @@ class ModuleSnapshotIOS final : public ModuleSnapshot {
std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;
private:
// Gather the the module information based off of a mach_header_64 |address_|.
bool FinishInitialization();
std::string name_;
uint64_t address_;
uint64_t size_;
@ -100,12 +79,16 @@ class ModuleSnapshotIOS final : public ModuleSnapshot {
uint64_t source_version_;
uint32_t filetype_;
UUID uuid_;
std::vector<std::string> annotations_vector_;
std::map<std::string, std::string> annotations_simple_map_;
std::vector<AnnotationSnapshot> annotation_objects_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotIOS);
DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotIOSIntermediateDump);
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_IOS_MODULE_SNAPSHOT_IOS_H_
#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MODULE_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_

View File

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

View 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

View File

@ -12,54 +12,42 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_
#define CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_
#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_PROCESS_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_PROCESS_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
#include <sys/sysctl.h>
#include <vector>
#include "snapshot/ios/exception_snapshot_ios.h"
#include "snapshot/ios/module_snapshot_ios.h"
#include "snapshot/ios/system_snapshot_ios.h"
#include "snapshot/ios/thread_snapshot_ios.h"
#include "base/files/file_path.h"
#include "snapshot/ios/exception_snapshot_ios_intermediate_dump.h"
#include "snapshot/ios/module_snapshot_ios_intermediate_dump.h"
#include "snapshot/ios/process_snapshot_ios_intermediate_dump.h"
#include "snapshot/ios/system_snapshot_ios_intermediate_dump.h"
#include "snapshot/ios/thread_snapshot_ios_intermediate_dump.h"
#include "snapshot/process_snapshot.h"
#include "snapshot/thread_snapshot.h"
#include "snapshot/unloaded_module_snapshot.h"
#include "util/ios/ios_intermediate_dump_reader.h"
namespace crashpad {
namespace internal {
//! \brief A ProcessSnapshot of a running (or crashed) process running on a
//! iphoneOS system.
class ProcessSnapshotIOS final : public ProcessSnapshot {
class ProcessSnapshotIOSIntermediateDump final : public ProcessSnapshot {
public:
ProcessSnapshotIOS();
~ProcessSnapshotIOS() override;
ProcessSnapshotIOSIntermediateDump() = default;
//! \brief Initializes the object.
//!
//! \param[in] system_data A class containing various system data points.
//! \param[in] dump_path A class containing various system data points.
//!
//! \return `true` if the snapshot could be created, `false` otherwise with
//! an appropriate message logged.
bool Initialize(const internal::IOSSystemDataCollector& system_data);
bool Initialize(const base::FilePath& dump_path,
const std::map<std::string, std::string>& annotations);
//! \brief Initialize exception information from a signal.
void SetExceptionFromSignal(const siginfo_t* siginfo,
const ucontext_t* context);
//! \brief Initialize exception information from a Mach exception.
void SetExceptionFromMachException(exception_behavior_t behavior,
thread_t exception_thread,
exception_type_t exception,
const mach_exception_data_type_t* code,
mach_msg_type_number_t code_count,
thread_state_flavor_t flavor,
ConstThreadState old_state,
mach_msg_type_number_t old_state_count);
//! \brief Sets the value to be returned by ClientID().
//!
//! On iOS, the client ID is under the control of the snapshot producer,
//! which may call this method to set the client ID. If this is not done,
//! ClientID() will return an identifier consisting entirely of zeroes.
@ -93,30 +81,33 @@ class ProcessSnapshotIOS final : public ProcessSnapshot {
const ProcessMemory* Memory() const override;
private:
// Initializes modules_ on behalf of Initialize().
void InitializeModules();
// Initializes threads_ on behalf of Initialize().
void InitializeThreads();
kinfo_proc kern_proc_info_;
// Retain the reader for the lifetime of the ProcessSnapshot so large chunks
// of data do not need to be copied around (such as MemorySnapshot
// intermediate dumps).
IOSIntermediateDumpReader reader_;
pid_t p_pid_;
pid_t e_ppid_;
timeval p_starttime_;
time_value_t basic_info_user_time_;
time_value_t basic_info_system_time_;
time_value_t thread_times_user_time_;
time_value_t thread_times_system_time_;
internal::SystemSnapshotIOS system_;
std::vector<std::unique_ptr<internal::ThreadSnapshotIOS>> threads_;
std::vector<std::unique_ptr<internal::ModuleSnapshotIOS>> modules_;
std::unique_ptr<internal::ExceptionSnapshotIOS> exception_;
internal::SystemSnapshotIOSIntermediateDump system_;
std::vector<std::unique_ptr<internal::ThreadSnapshotIOSIntermediateDump>>
threads_;
std::vector<std::unique_ptr<internal::ModuleSnapshotIOSIntermediateDump>>
modules_;
std::unique_ptr<internal::ExceptionSnapshotIOSIntermediateDump> exception_;
UUID report_id_;
UUID client_id_;
std::map<std::string, std::string> annotations_simple_map_;
timeval snapshot_time_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotIOS);
DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotIOSIntermediateDump);
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_IOS_PROCESS_SNAPSHOT_IOS_H_
#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_PROCESS_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_

View 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, &current_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

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/ios/system_snapshot_ios.h"
#include "snapshot/ios/system_snapshot_ios_intermediate_dump.h"
#include <mach/mach.h>
#include <stddef.h>
@ -27,7 +27,9 @@
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "snapshot/cpu_context.h"
#include "snapshot/ios/intermediate_dump_reader_util.h"
#include "snapshot/posix/timezone.h"
#include "util/ios/ios_intermediate_dump_data.h"
#include "util/mac/mac_util.h"
#include "util/numeric/in_range_cast.h"
@ -35,7 +37,9 @@ namespace crashpad {
namespace internal {
SystemSnapshotIOS::SystemSnapshotIOS()
using Key = IntermediateDumpKey;
SystemSnapshotIOSIntermediateDump::SystemSnapshotIOSIntermediateDump()
: SystemSnapshot(),
os_version_build_(),
machine_description_(),
@ -55,51 +59,67 @@ SystemSnapshotIOS::SystemSnapshotIOS()
daylight_name_(),
initialized_() {}
SystemSnapshotIOS::~SystemSnapshotIOS() {}
SystemSnapshotIOSIntermediateDump::~SystemSnapshotIOSIntermediateDump() {}
void SystemSnapshotIOS::Initialize(const IOSSystemDataCollector& system_data) {
void SystemSnapshotIOSIntermediateDump::Initialize(
const IOSIntermediateDumpMap* system_data) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
system_data.OSVersion(
&os_version_major_, &os_version_minor_, &os_version_bugfix_);
os_version_build_ = system_data.Build();
machine_description_ = system_data.MachineDescription();
cpu_count_ = system_data.ProcessorCount();
cpu_vendor_ = system_data.CPUVendor();
if (system_data.HasDaylightSavingTime()) {
dst_status_ = system_data.IsDaylightSavingTime()
GetDataStringFromMap(system_data, Key::kOSVersionBuild, &os_version_build_);
GetDataStringFromMap(
system_data, Key::kMachineDescription, &machine_description_);
GetDataStringFromMap(system_data, Key::kCpuVendor, &cpu_vendor_);
GetDataStringFromMap(system_data, Key::kStandardName, &standard_name_);
GetDataStringFromMap(system_data, Key::kDaylightName, &daylight_name_);
GetDataValueFromMap(system_data, Key::kOSVersionMajor, &os_version_major_);
GetDataValueFromMap(system_data, Key::kOSVersionMinor, &os_version_minor_);
GetDataValueFromMap(system_data, Key::kOSVersionBugfix, &os_version_bugfix_);
GetDataValueFromMap(system_data, Key::kCpuCount, &cpu_count_);
GetDataValueFromMap(
system_data, Key::kStandardOffsetSeconds, &standard_offset_seconds_);
GetDataValueFromMap(
system_data, Key::kDaylightOffsetSeconds, &daylight_offset_seconds_);
bool has_daylight_saving_time;
GetDataValueFromMap(
system_data, Key::kHasDaylightSavingTime, &has_daylight_saving_time);
bool is_daylight_saving_time;
GetDataValueFromMap(
system_data, Key::kIsDaylightSavingTime, &is_daylight_saving_time);
if (has_daylight_saving_time) {
dst_status_ = is_daylight_saving_time
? SystemSnapshot::kObservingDaylightSavingTime
: SystemSnapshot::kObservingStandardTime;
} else {
dst_status_ = SystemSnapshot::kDoesNotObserveDaylightSavingTime;
}
standard_offset_seconds_ = system_data.StandardOffsetSeconds();
daylight_offset_seconds_ = system_data.DaylightOffsetSeconds();
standard_name_ = system_data.StandardName();
daylight_name_ = system_data.DaylightName();
// Currently unused by minidump.
vm_size_t page_size;
host_page_size(mach_host_self(), &page_size);
mach_msg_type_number_t host_size =
sizeof(vm_statistics_data_t) / sizeof(integer_t);
vm_statistics_data_t vm_stat;
kern_return_t kr = host_statistics(mach_host_self(),
HOST_VM_INFO,
reinterpret_cast<host_info_t>(&vm_stat),
&host_size);
if (kr != KERN_SUCCESS) {
MACH_LOG(WARNING, kr) << "host_statistics";
if (GetDataValueFromMap(system_data, Key::kPageSize, &page_size)) {
const IOSIntermediateDumpMap* vm_stat =
GetMapFromMap(system_data, Key::kVMStat);
if (vm_stat) {
GetDataValueFromMap(vm_stat, Key::kActive, &active_);
active_ *= page_size;
GetDataValueFromMap(vm_stat, Key::kInactive, &inactive_);
inactive_ *= page_size;
GetDataValueFromMap(vm_stat, Key::kWired, &wired_);
wired_ *= page_size;
GetDataValueFromMap(vm_stat, Key::kFree, &free_);
free_ *= page_size;
}
}
active_ = vm_stat.active_count * page_size;
inactive_ = vm_stat.inactive_count * page_size;
wired_ = vm_stat.wire_count * page_size;
free_ = vm_stat.free_count * page_size;
INITIALIZATION_STATE_SET_VALID(initialized_);
}
CPUArchitecture SystemSnapshotIOS::GetCPUArchitecture() const {
CPUArchitecture SystemSnapshotIOSIntermediateDump::GetCPUArchitecture() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_64)
return kCPUArchitectureX86_64;
@ -108,24 +128,24 @@ CPUArchitecture SystemSnapshotIOS::GetCPUArchitecture() const {
#endif
}
uint32_t SystemSnapshotIOS::CPURevision() const {
uint32_t SystemSnapshotIOSIntermediateDump::CPURevision() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
// TODO(justincohen): sysctlbyname machdep.cpu.* returns -1 on iOS/ARM64, but
// consider recording this for X86_64 only.
return 0;
}
uint8_t SystemSnapshotIOS::CPUCount() const {
uint8_t SystemSnapshotIOSIntermediateDump::CPUCount() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return cpu_count_;
}
std::string SystemSnapshotIOS::CPUVendor() const {
std::string SystemSnapshotIOSIntermediateDump::CPUVendor() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return cpu_vendor_;
}
void SystemSnapshotIOS::CPUFrequency(uint64_t* current_hz,
void SystemSnapshotIOSIntermediateDump::CPUFrequency(uint64_t* current_hz,
uint64_t* max_hz) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
// TODO(justincohen): sysctlbyname hw.cpufrequency returns -1 on iOS/ARM64,
@ -134,47 +154,48 @@ void SystemSnapshotIOS::CPUFrequency(uint64_t* current_hz,
*max_hz = 0;
}
uint32_t SystemSnapshotIOS::CPUX86Signature() const {
uint32_t SystemSnapshotIOSIntermediateDump::CPUX86Signature() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
// TODO(justincohen): Consider recording this for X86_64 only.
return 0;
}
uint64_t SystemSnapshotIOS::CPUX86Features() const {
uint64_t SystemSnapshotIOSIntermediateDump::CPUX86Features() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
// TODO(justincohen): Consider recording this for X86_64 only.
return 0;
}
uint64_t SystemSnapshotIOS::CPUX86ExtendedFeatures() const {
uint64_t SystemSnapshotIOSIntermediateDump::CPUX86ExtendedFeatures() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
// TODO(justincohen): Consider recording this for X86_64 only.
return 0;
}
uint32_t SystemSnapshotIOS::CPUX86Leaf7Features() const {
uint32_t SystemSnapshotIOSIntermediateDump::CPUX86Leaf7Features() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
// TODO(justincohen): Consider recording this for X86_64 only.
return 0;
}
bool SystemSnapshotIOS::CPUX86SupportsDAZ() const {
bool SystemSnapshotIOSIntermediateDump::CPUX86SupportsDAZ() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
// TODO(justincohen): Consider recording this for X86_64 only.
return false;
}
SystemSnapshot::OperatingSystem SystemSnapshotIOS::GetOperatingSystem() const {
SystemSnapshot::OperatingSystem
SystemSnapshotIOSIntermediateDump::GetOperatingSystem() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return kOperatingSystemIOS;
}
bool SystemSnapshotIOS::OSServer() const {
bool SystemSnapshotIOSIntermediateDump::OSServer() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return false;
}
void SystemSnapshotIOS::OSVersion(int* major,
void SystemSnapshotIOSIntermediateDump::OSVersion(int* major,
int* minor,
int* bugfix,
std::string* build) const {
@ -185,7 +206,7 @@ void SystemSnapshotIOS::OSVersion(int* major,
build->assign(os_version_build_);
}
std::string SystemSnapshotIOS::OSVersionFull() const {
std::string SystemSnapshotIOSIntermediateDump::OSVersionFull() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return base::StringPrintf("%d.%d.%d %s",
os_version_major_,
@ -194,19 +215,20 @@ std::string SystemSnapshotIOS::OSVersionFull() const {
os_version_build_.c_str());
}
std::string SystemSnapshotIOS::MachineDescription() const {
std::string SystemSnapshotIOSIntermediateDump::MachineDescription() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return machine_description_;
}
bool SystemSnapshotIOS::NXEnabled() const {
bool SystemSnapshotIOSIntermediateDump::NXEnabled() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
// TODO(justincohen): Consider using kern.nx when available (pre-iOS 13,
// pre-OS X 10.15). Otherwise the bit is always enabled.
return true;
}
void SystemSnapshotIOS::TimeZone(DaylightSavingTimeStatus* dst_status,
void SystemSnapshotIOSIntermediateDump::TimeZone(
DaylightSavingTimeStatus* dst_status,
int* standard_offset_seconds,
int* daylight_offset_seconds,
std::string* standard_name,

View File

@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_SNAPSHOT_IOS_SYSTEM_SNAPSHOT_IOS_H_
#define CRASHPAD_SNAPSHOT_IOS_SYSTEM_SNAPSHOT_IOS_H_
#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_SYSTEM_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_SYSTEM_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
#include <stdint.h>
@ -21,6 +21,7 @@
#include "base/macros.h"
#include "snapshot/system_snapshot.h"
#include "util/ios/ios_intermediate_dump_map.h"
#include "util/ios/ios_system_data_collector.h"
#include "util/misc/initialization_state_dcheck.h"
@ -29,15 +30,17 @@ namespace crashpad {
namespace internal {
//! \brief A SystemSnapshot of the running system, when the system runs iOS.
class SystemSnapshotIOS final : public SystemSnapshot {
class SystemSnapshotIOSIntermediateDump final : public SystemSnapshot {
public:
SystemSnapshotIOS();
~SystemSnapshotIOS() override;
SystemSnapshotIOSIntermediateDump();
~SystemSnapshotIOSIntermediateDump() override;
//! \brief Initializes the object.
//!
//! \param[in] system_data A class containing various system data points.
void Initialize(const IOSSystemDataCollector& system_data);
//! \param[in] system_data An intermediate dump map containing various system
//! data points.
//! \return `true` if the snapshot could be created.
void Initialize(const IOSIntermediateDumpMap* system_data);
// SystemSnapshot:
@ -72,10 +75,10 @@ class SystemSnapshotIOS final : public SystemSnapshot {
int os_version_major_;
int os_version_minor_;
int os_version_bugfix_;
uint64_t active_;
uint64_t inactive_;
uint64_t wired_;
uint64_t free_;
uint32_t active_;
uint32_t inactive_;
uint32_t wired_;
uint32_t free_;
int cpu_count_;
std::string cpu_vendor_;
DaylightSavingTimeStatus dst_status_;
@ -85,10 +88,10 @@ class SystemSnapshotIOS final : public SystemSnapshot {
std::string daylight_name_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(SystemSnapshotIOS);
DISALLOW_COPY_AND_ASSIGN(SystemSnapshotIOSIntermediateDump);
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_IOS_SYSTEM_SNAPSHOT_IOS_H_
#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_SYSTEM_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_

View File

@ -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 threads
//! 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 systems 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 theres 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, its 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
//! threads 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 threads stack.
//! \param[out] stack_region_size The size of the memory region used as the
//! threads stack.
//!
//! \return The base address (lowest address) of the memory region used as the
//! threads 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(),
&region_base,
&region_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) {
// Theres nothing mapped at the stack pointers address. Something may have
// trashed the stack pointer. Note that this shouldnt 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 isnt readable, the stack pointer probably points to the
// guard region. Dont include it as part of the stack, and dont 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, &region_base, &region_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 threads 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 doesnt collect map entries belonging to another threads
// 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,
&region_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 threads 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

View 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

View File

@ -12,36 +12,33 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_SNAPSHOT_IOS_THREAD_SNAPSHOT_IOS_H_
#define CRASHPAD_SNAPSHOT_IOS_THREAD_SNAPSHOT_IOS_H_
#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_
#include "base/macros.h"
#include "build/build_config.h"
#include "snapshot/cpu_context.h"
#include "snapshot/ios/memory_snapshot_ios.h"
#include "snapshot/ios/memory_snapshot_ios_intermediate_dump.h"
#include "snapshot/thread_snapshot.h"
#include "util/ios/ios_intermediate_dump_map.h"
#include "util/misc/initialization_state_dcheck.h"
namespace crashpad {
namespace internal {
//! \brief A ThreadSnapshot of a thread on an iOS system.
class ThreadSnapshotIOS final : public ThreadSnapshot {
class ThreadSnapshotIOSIntermediateDump final : public ThreadSnapshot {
public:
ThreadSnapshotIOS();
~ThreadSnapshotIOS() override;
ThreadSnapshotIOSIntermediateDump();
~ThreadSnapshotIOSIntermediateDump() override;
//! \brief Initializes the object.
//!
//! \brief thread The Mach thread used to initialize this object.
bool Initialize(thread_t thread);
//! \brief Returns an array of thread_t threads.
//! \brief thread_data The intermediate dump map used to initialize this
//! object.
//!
//! \param[out] count The number of threads returned.
//!
//! \return An array of of size \a count threads.
static thread_act_array_t GetThreads(mach_msg_type_number_t* count);
//! \return `true` if the snapshot could be created.
bool Initialize(const IOSIntermediateDumpMap* thread_data);
// ThreadSnapshot:
const CPUContext* Context() const override;
@ -61,17 +58,20 @@ class ThreadSnapshotIOS final : public ThreadSnapshot {
#error Port.
#endif // ARCH_CPU_X86_64
CPUContext context_;
MemorySnapshotIOS stack_;
std::vector<uint8_t> exception_stack_memory_;
MemorySnapshotIOSIntermediateDump stack_;
uint64_t thread_id_;
uint64_t thread_specific_data_address_;
int suspend_count_;
int priority_;
std::vector<std::unique_ptr<internal::MemorySnapshotIOSIntermediateDump>>
extra_memory_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotIOS);
DISALLOW_COPY_AND_ASSIGN(ThreadSnapshotIOSIntermediateDump);
};
} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_IOS_THREAD_SNAPSHOT_IOS_H_
#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_

View File

@ -110,4 +110,24 @@ void Metrics::HandlerCrashed(uint32_t exception_code) {
"Crashpad.HandlerCrash.ExceptionCode." METRICS_OS_NAME, exception_code);
}
#if defined(OS_IOS)
// static
void Metrics::MissingIntermediateDumpKey(
const internal::IntermediateDumpKey& key) {
UMA_HISTOGRAM_ENUMERATION("Crashpad.IntermediateDump.Reader.MissingKey",
key,
internal::IntermediateDumpKey::kMaxValue);
}
// static
void Metrics::InvalidIntermediateDumpKeySize(
const internal::IntermediateDumpKey& key) {
UMA_HISTOGRAM_ENUMERATION("Crashpad.IntermediateDump.Reader.InvalidKeySize",
key,
internal::IntermediateDumpKey::kMaxValue);
}
#endif
// static
} // namespace crashpad

View File

@ -18,8 +18,13 @@
#include <inttypes.h>
#include "base/macros.h"
#include "build/build_config.h"
#include "util/file/file_io.h"
#if defined(OS_IOS)
#include "util/ios/ios_intermediate_dump_format.h"
#endif
namespace crashpad {
//! \brief Container class to hold shared UMA metrics integration points.
@ -195,6 +200,16 @@ class Metrics {
//! This is currently only reported on Windows.
static void HandlerCrashed(uint32_t exception_code);
#if defined(OS_IOS) || DOXYGEN
//! \brief Records a missing key from an intermediate dump.
static void MissingIntermediateDumpKey(
const internal::IntermediateDumpKey& key);
//! \brief Records a key with an invalid key size from an intermediate dump.
static void InvalidIntermediateDumpKeySize(
const internal::IntermediateDumpKey& key);
#endif
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Metrics);
};