mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 22:26:06 +00:00
[ios] Bring up first draft Mach exception server.
Add Mach exception server and fill out exceptions snapshot. Note that: - The 'capture' portion of this CL will be moved out of the snapshot interface and into a separate in-process dump to disk location. - All of the pointer dereferences need to be wrapped in vm_read. - The read-fast-and-dump logic in exception_snapshot will end up in a different file completely, but until we pick a serialization/deserialization method, keep it as-is. Bug: crashpad:31 Change-Id: I44203aa44036a341d6b4517fde7ab0cb9d7e94d7 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2160122 Commit-Queue: Justin Cohen <justincohen@chromium.org> Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
6d8b196150
commit
17a515d33d
@ -431,18 +431,19 @@ class CrashpadClient {
|
||||
//!
|
||||
//! This method is only defined on iOS.
|
||||
//!
|
||||
//! \return `true` on success, `false` on failure with a message logged.
|
||||
//!
|
||||
//! TODO(justincohen): This method will need to take database, metrics_dir,
|
||||
//! url and annotations eventually.
|
||||
bool StartCrashpadInProcessHandler();
|
||||
void StartCrashpadInProcessHandler();
|
||||
|
||||
// TODO(justincohen): This method is purely for bringing up iOS interfaces.
|
||||
//! \brief Requests that the handler capture a dump even though there hasn't
|
||||
//! been a crash.
|
||||
//!
|
||||
//! A handler must have already been installed before calling this method.
|
||||
static void DumpWithoutCrash();
|
||||
//!
|
||||
//! \param[in] context A NativeCPUContext, generally captured by
|
||||
//! CaptureContext() or similar.
|
||||
static void DumpWithoutCrash(NativeCPUContext* context);
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX) || DOXYGEN
|
||||
|
@ -16,65 +16,192 @@
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <ios>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "client/client_argv_handling.h"
|
||||
#include "base/mac/mach_logging.h"
|
||||
#include "base/mac/scoped_mach_port.h"
|
||||
#include "base/stl_util.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"
|
||||
#include "util/mach/exception_ports.h"
|
||||
#include "util/mach/mach_extensions.h"
|
||||
#include "util/mach/mach_message.h"
|
||||
#include "util/mach/mach_message_server.h"
|
||||
#include "util/misc/initialization_state_dcheck.h"
|
||||
#include "util/posix/signals.h"
|
||||
#include "util/thread/thread.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
// A base class for Crashpad signal handler implementations.
|
||||
class SignalHandler {
|
||||
// A base class for signal handler and Mach exception server.
|
||||
class CrashHandler : public Thread, public UniversalMachExcServer::Interface {
|
||||
public:
|
||||
// Returns the currently installed signal hander.
|
||||
static SignalHandler* Get() {
|
||||
static SignalHandler* instance = new SignalHandler();
|
||||
static CrashHandler* Get() {
|
||||
static CrashHandler* instance = new CrashHandler();
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool Install(const std::set<int>* unhandled_signals) {
|
||||
return Signals::InstallCrashHandlers(
|
||||
HandleSignal, 0, &old_actions_, unhandled_signals);
|
||||
void Initialize() {
|
||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||
InstallMachExceptionHandler();
|
||||
CHECK(Signals::InstallHandler(SIGABRT, CatchSignal, 0, &old_action_));
|
||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||
}
|
||||
|
||||
void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
|
||||
// TODO(justincohen): This is incomplete.
|
||||
ProcessSnapshotIOS process_snapshot;
|
||||
process_snapshot.Initialize(system_data);
|
||||
process_snapshot.SetException(siginfo,
|
||||
reinterpret_cast<ucontext_t*>(context));
|
||||
void DumpWithoutCrash(NativeCPUContext* context) {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
mach_exception_data_type_t code[2] = {};
|
||||
static constexpr int kSimulatedException = -1;
|
||||
HandleMachException(MACH_EXCEPTION_CODES,
|
||||
mach_thread_self(),
|
||||
kSimulatedException,
|
||||
code,
|
||||
base::size(code),
|
||||
MACHINE_THREAD_STATE,
|
||||
reinterpret_cast<ConstThreadState>(context),
|
||||
MACHINE_THREAD_STATE_COUNT);
|
||||
}
|
||||
|
||||
private:
|
||||
SignalHandler() = default;
|
||||
CrashHandler() = default;
|
||||
|
||||
// The base implementation for all signal handlers, suitable for calling
|
||||
// directly to simulate signal delivery.
|
||||
void HandleCrashAndReraiseSignal(int signo,
|
||||
siginfo_t* siginfo,
|
||||
void* context) {
|
||||
HandleCrash(signo, siginfo, context);
|
||||
// Always call system handler.
|
||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(
|
||||
siginfo, old_actions_.ActionForSignal(signo));
|
||||
void InstallMachExceptionHandler() {
|
||||
exception_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
||||
CHECK(exception_port_.is_valid());
|
||||
|
||||
kern_return_t kr = mach_port_insert_right(mach_task_self(),
|
||||
exception_port_.get(),
|
||||
exception_port_.get(),
|
||||
MACH_MSG_TYPE_MAKE_SEND);
|
||||
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_right";
|
||||
|
||||
// TODO: Use SwapExceptionPort instead and put back EXC_MASK_BREAKPOINT.
|
||||
const exception_mask_t mask =
|
||||
ExcMaskAll() &
|
||||
~(EXC_MASK_EMULATION | EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT |
|
||||
EXC_MASK_RPC_ALERT | EXC_MASK_GUARD);
|
||||
ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL);
|
||||
exception_ports.GetExceptionPorts(mask, &original_handlers_);
|
||||
exception_ports.SetExceptionPort(
|
||||
mask,
|
||||
exception_port_.get(),
|
||||
EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
|
||||
MACHINE_THREAD_STATE);
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
// Thread:
|
||||
|
||||
void ThreadMain() override {
|
||||
UniversalMachExcServer universal_mach_exc_server(this);
|
||||
while (true) {
|
||||
mach_msg_return_t mr =
|
||||
MachMessageServer::Run(&universal_mach_exc_server,
|
||||
exception_port_.get(),
|
||||
MACH_MSG_OPTION_NONE,
|
||||
MachMessageServer::kPersistent,
|
||||
MachMessageServer::kReceiveLargeIgnore,
|
||||
kMachMessageTimeoutWaitIndefinitely);
|
||||
MACH_CHECK(mr == MACH_SEND_INVALID_DEST, mr) << "MachMessageServer::Run";
|
||||
}
|
||||
}
|
||||
|
||||
// UniversalMachExcServer::Interface:
|
||||
|
||||
kern_return_t CatchMachException(exception_behavior_t behavior,
|
||||
exception_handler_t exception_port,
|
||||
thread_t thread,
|
||||
task_t task,
|
||||
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,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t* new_state_count,
|
||||
const mach_msg_trailer_t* trailer,
|
||||
bool* destroy_complex_request) override {
|
||||
*destroy_complex_request = true;
|
||||
|
||||
// TODO(justincohen): Forward exceptions to original_handlers_ with
|
||||
// UniversalExceptionRaise.
|
||||
|
||||
// iOS shouldn't have any child processes, but just in case, those will
|
||||
// inherit the task exception ports, and this process isn’t prepared to
|
||||
// handle them
|
||||
if (task != mach_task_self()) {
|
||||
LOG(WARNING) << "task 0x" << std::hex << task << " != 0x"
|
||||
<< mach_task_self();
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
|
||||
HandleMachException(behavior,
|
||||
thread,
|
||||
exception,
|
||||
code,
|
||||
code_count,
|
||||
*flavor,
|
||||
old_state,
|
||||
old_state_count);
|
||||
|
||||
// Respond with KERN_FAILURE so the system will continue to handle this
|
||||
// exception as a crash.
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
|
||||
void HandleMachException(exception_behavior_t behavior,
|
||||
thread_t 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) {
|
||||
// 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.
|
||||
static void HandleSignal(int signo, siginfo_t* siginfo, void* context) {
|
||||
Get()->HandleCrashAndReraiseSignal(signo, siginfo, context);
|
||||
static void CatchSignal(int signo, siginfo_t* siginfo, void* context) {
|
||||
Get()->HandleAndReraiseSignal(
|
||||
signo, siginfo, reinterpret_cast<ucontext_t*>(context));
|
||||
}
|
||||
|
||||
Signals::OldActions old_actions_ = {};
|
||||
void HandleAndReraiseSignal(int signo,
|
||||
siginfo_t* siginfo,
|
||||
ucontext_t* context) {
|
||||
// TODO(justincohen): This is incomplete.
|
||||
ProcessSnapshotIOS process_snapshot;
|
||||
process_snapshot.Initialize(system_data_);
|
||||
process_snapshot.SetExceptionFromSignal(siginfo, context);
|
||||
|
||||
// Collect some system data before the signal handler is triggered.
|
||||
IOSSystemDataCollector system_data;
|
||||
// Always call system handler.
|
||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, &old_action_);
|
||||
}
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SignalHandler);
|
||||
base::mac::ScopedMachReceiveRight exception_port_;
|
||||
ExceptionPorts::ExceptionHandlerVector original_handlers_;
|
||||
struct sigaction old_action_ = {};
|
||||
IOSSystemDataCollector system_data_;
|
||||
InitializationStateDcheck initialized_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashHandler);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@ -83,16 +210,19 @@ CrashpadClient::CrashpadClient() {}
|
||||
|
||||
CrashpadClient::~CrashpadClient() {}
|
||||
|
||||
bool CrashpadClient::StartCrashpadInProcessHandler() {
|
||||
void CrashpadClient::StartCrashpadInProcessHandler() {
|
||||
InstallObjcExceptionPreprocessor();
|
||||
return SignalHandler::Get()->Install(nullptr);
|
||||
|
||||
CrashHandler* crash_handler = CrashHandler::Get();
|
||||
DCHECK(crash_handler);
|
||||
crash_handler->Initialize();
|
||||
}
|
||||
|
||||
// static
|
||||
void CrashpadClient::DumpWithoutCrash() {
|
||||
DCHECK(SignalHandler::Get());
|
||||
siginfo_t siginfo = {};
|
||||
SignalHandler::Get()->HandleCrash(siginfo.si_signo, &siginfo, nullptr);
|
||||
void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
|
||||
CrashHandler* crash_handler = CrashHandler::Get();
|
||||
DCHECK(crash_handler);
|
||||
crash_handler->DumpWithoutCrash(context);
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -27,11 +27,24 @@ namespace {
|
||||
|
||||
using CrashpadIOSClient = PlatformTest;
|
||||
|
||||
// TODO(justincohen): This is a placeholder.
|
||||
TEST_F(CrashpadIOSClient, DumpWithoutCrash) {
|
||||
CrashpadClient client;
|
||||
client.StartCrashpadInProcessHandler();
|
||||
client.DumpWithoutCrash();
|
||||
|
||||
NativeCPUContext context;
|
||||
#if defined(ARCH_CPU_X86_64)
|
||||
CaptureContext(&context);
|
||||
#elif defined(ARCH_CPU_ARM64)
|
||||
// TODO(justincohen): Implement CaptureContext for ARM64.
|
||||
mach_msg_type_number_t thread_state_count = MACHINE_THREAD_STATE_COUNT;
|
||||
kern_return_t kr =
|
||||
thread_get_state(mach_thread_self(),
|
||||
MACHINE_THREAD_STATE,
|
||||
reinterpret_cast<thread_state_t>(&context),
|
||||
&thread_state_count);
|
||||
ASSERT_EQ(kr, KERN_SUCCESS);
|
||||
#endif
|
||||
client.DumpWithoutCrash(&context);
|
||||
}
|
||||
|
||||
// This test is covered by a similar XCUITest, but for development purposes
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "util/misc/from_pointer_cast.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace internal {
|
||||
|
||||
ExceptionSnapshotIOS::ExceptionSnapshotIOS()
|
||||
@ -30,24 +31,21 @@ ExceptionSnapshotIOS::ExceptionSnapshotIOS()
|
||||
codes_(),
|
||||
thread_id_(0),
|
||||
exception_address_(0),
|
||||
signal_number_(0),
|
||||
signal_code_(0),
|
||||
exception_(0),
|
||||
exception_info_(0),
|
||||
initialized_() {}
|
||||
|
||||
ExceptionSnapshotIOS::~ExceptionSnapshotIOS() {}
|
||||
|
||||
bool ExceptionSnapshotIOS::Initialize(const siginfo_t* siginfo,
|
||||
const ucontext_t* context) {
|
||||
void ExceptionSnapshotIOS::InitializeFromSignal(const siginfo_t* siginfo,
|
||||
const ucontext_t* context) {
|
||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||
|
||||
if (!context)
|
||||
return false;
|
||||
|
||||
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;
|
||||
x86_debug_state64_t empty_debug_state = {};
|
||||
InitializeCPUContextX86_64(&context_x86_64_,
|
||||
THREAD_STATE_NONE,
|
||||
nullptr,
|
||||
@ -58,7 +56,12 @@ bool ExceptionSnapshotIOS::Initialize(const siginfo_t* siginfo,
|
||||
#elif defined(ARCH_CPU_ARM64)
|
||||
context_.architecture = kCPUArchitectureARM64;
|
||||
context_.arm64 = &context_arm64_;
|
||||
InitializeCPUContextARM64(&context_arm64_, &mcontext->__ss, &mcontext->__ns);
|
||||
InitializeCPUContextARM64(&context_arm64_,
|
||||
THREAD_STATE_NONE,
|
||||
nullptr,
|
||||
0,
|
||||
&mcontext->__ss,
|
||||
&mcontext->__ns);
|
||||
#endif
|
||||
|
||||
// Thread ID.
|
||||
@ -75,12 +78,151 @@ bool ExceptionSnapshotIOS::Initialize(const siginfo_t* siginfo,
|
||||
thread_id_ = identifier_info.thread_id;
|
||||
}
|
||||
|
||||
signal_number_ = siginfo->si_signo;
|
||||
signal_code_ = siginfo->si_code;
|
||||
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;
|
||||
mach_msg_type_number_t float_state_count = ARM_NEON_STATE64_COUNT;
|
||||
mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT;
|
||||
const thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64;
|
||||
const thread_state_flavor_t kFloatStateFlavor = ARM_NEON_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 << ")";
|
||||
}
|
||||
|
||||
#if defined(ARCH_CPU_X86_64)
|
||||
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 << ")";
|
||||
}
|
||||
#endif
|
||||
|
||||
#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);
|
||||
#endif
|
||||
|
||||
// Thread ID.
|
||||
thread_identifier_info identifier_info;
|
||||
mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
|
||||
kr = thread_info(mach_thread_self(),
|
||||
THREAD_IDENTIFIER_INFO,
|
||||
reinterpret_cast<thread_info_t>(&identifier_info),
|
||||
&count);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
MACH_LOG(ERROR, kr) << "thread_identifier_info";
|
||||
} else {
|
||||
thread_id_ = identifier_info.thread_id;
|
||||
}
|
||||
|
||||
// Normally, for EXC_BAD_ACCESS exceptions, the exception address is present
|
||||
// in code[1]. It may or may not be the instruction pointer address (usually
|
||||
// it’s not). code[1] may carry the exception address for other exception
|
||||
// types too, but it’s not guaranteed. But for all other exception types, the
|
||||
// instruction pointer will be the exception address, and in fact will be
|
||||
// equal to codes[1] when it’s carrying the exception address. In those cases,
|
||||
// just use the instruction pointer directly.
|
||||
bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS;
|
||||
|
||||
#if defined(ARCH_CPU_X86_64)
|
||||
// For x86 and x86_64 EXC_BAD_ACCESS exceptions, some code[0] values
|
||||
// indicate that code[1] does not (or may not) carry the exception address:
|
||||
// EXC_I386_GPFLT (10.9.5 xnu-2422.115.4/osfmk/i386/trap.c user_trap() for
|
||||
// T_GENERAL_PROTECTION) and the oddball (VM_PROT_READ | VM_PROT_EXECUTE)
|
||||
// which collides with EXC_I386_BOUNDFLT (10.9.5
|
||||
// xnu-2422.115.4/osfmk/i386/fpu.c fpextovrflt()). Other EXC_BAD_ACCESS
|
||||
// exceptions come through 10.9.5 xnu-2422.115.4/osfmk/i386/trap.c
|
||||
// user_page_fault_continue() and do contain the exception address in
|
||||
// code[1].
|
||||
if (exception_ == EXC_BAD_ACCESS &&
|
||||
(exception_info_ == EXC_I386_GPFLT ||
|
||||
exception_info_ == (VM_PROT_READ | VM_PROT_EXECUTE))) {
|
||||
code_1_is_exception_address = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (code_1_is_exception_address) {
|
||||
exception_address_ = code[1];
|
||||
} else {
|
||||
exception_address_ = context_.InstructionPointer();
|
||||
}
|
||||
|
||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||
return true;
|
||||
}
|
||||
|
||||
const CPUContext* ExceptionSnapshotIOS::Context() const {
|
||||
@ -95,12 +237,12 @@ uint64_t ExceptionSnapshotIOS::ThreadID() const {
|
||||
|
||||
uint32_t ExceptionSnapshotIOS::Exception() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return signal_number_;
|
||||
return exception_;
|
||||
}
|
||||
|
||||
uint32_t ExceptionSnapshotIOS::ExceptionInfo() const {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
return signal_code_;
|
||||
return exception_info_;
|
||||
}
|
||||
|
||||
uint64_t ExceptionSnapshotIOS::ExceptionAddress() const {
|
||||
|
@ -38,12 +38,25 @@ class ExceptionSnapshotIOS final : public ExceptionSnapshot {
|
||||
ExceptionSnapshotIOS();
|
||||
~ExceptionSnapshotIOS() override;
|
||||
|
||||
//! \brief Initializes the object.
|
||||
//! \brief Initializes the object from a signal.
|
||||
//!
|
||||
//! \return `true` if the snapshot could be created, `false` otherwise with
|
||||
//! an appropriate message logged.
|
||||
bool Initialize(const siginfo_t* siginfo, const ucontext_t* context);
|
||||
void InitializeFromSignal(const siginfo_t* siginfo,
|
||||
const ucontext_t* context);
|
||||
|
||||
//! \brief Initialize the object from a Mach exception for the current task.
|
||||
//!
|
||||
//! \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);
|
||||
// ExceptionSnapshot:
|
||||
|
||||
const CPUContext* Context() const override;
|
||||
@ -66,8 +79,8 @@ class ExceptionSnapshotIOS final : public ExceptionSnapshot {
|
||||
std::vector<uint64_t> codes_;
|
||||
uint64_t thread_id_;
|
||||
uintptr_t exception_address_;
|
||||
int signal_number_;
|
||||
int signal_code_;
|
||||
uint32_t exception_;
|
||||
uint32_t exception_info_;
|
||||
InitializationStateDcheck initialized_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ExceptionSnapshotIOS);
|
||||
|
@ -102,13 +102,36 @@ bool ProcessSnapshotIOS::Initialize(const IOSSystemDataCollector& system_data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProcessSnapshotIOS::SetException(const siginfo_t* siginfo,
|
||||
const ucontext_t* context) {
|
||||
void ProcessSnapshotIOS::SetExceptionFromSignal(const siginfo_t* siginfo,
|
||||
const ucontext_t* context) {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
DCHECK(!exception_.get());
|
||||
|
||||
exception_.reset(new internal::ExceptionSnapshotIOS());
|
||||
if (!exception_->Initialize(siginfo, context)) {
|
||||
exception_.reset();
|
||||
}
|
||||
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 {
|
||||
|
@ -44,7 +44,20 @@ class ProcessSnapshotIOS final : public ProcessSnapshot {
|
||||
//! an appropriate message logged.
|
||||
bool Initialize(const IOSSystemDataCollector& system_data);
|
||||
|
||||
void SetException(const siginfo_t* siginfo, const ucontext_t* context);
|
||||
//! \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,
|
||||
|
@ -427,7 +427,12 @@ bool ThreadSnapshotIOS::Initialize(thread_t thread) {
|
||||
#elif defined(ARCH_CPU_ARM64)
|
||||
context_.architecture = kCPUArchitectureARM64;
|
||||
context_.arm64 = &context_arm64_;
|
||||
InitializeCPUContextARM64(&context_arm64_, &thread_state, &float_state);
|
||||
InitializeCPUContextARM64(&context_arm64_,
|
||||
THREAD_STATE_NONE,
|
||||
nullptr,
|
||||
0,
|
||||
&thread_state,
|
||||
&float_state);
|
||||
#endif
|
||||
|
||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||
|
@ -33,7 +33,7 @@ class ThreadSnapshotIOS final : public ThreadSnapshot {
|
||||
|
||||
//! \brief Initializes the object.
|
||||
//!
|
||||
//! \brief thread The mach thread used to initialize this object.
|
||||
//! \brief thread The Mach thread used to initialize this object.
|
||||
bool Initialize(thread_t thread);
|
||||
|
||||
//! \brief Returns an array of thread_t threads.
|
||||
|
@ -438,11 +438,11 @@ void InitializeCPUContextX86_64(CPUContextX86_64* context,
|
||||
|
||||
#elif defined(ARCH_CPU_ARM_FAMILY)
|
||||
|
||||
namespace internal {
|
||||
namespace {
|
||||
|
||||
void InitializeCPUContextARM64(CPUContextARM64* context,
|
||||
const arm_thread_state64_t* arm_thread_state64,
|
||||
const arm_neon_state64_t* arm_neon_state64) {
|
||||
void InitializeCPUContextARM64Thread(
|
||||
CPUContextARM64* context,
|
||||
const arm_thread_state64_t* arm_thread_state64) {
|
||||
// The structures of context->regs and arm_thread_state64->__x are laid out
|
||||
// identically for this copy, even though the members are organized
|
||||
// differently. Because of this difference, there can't be a static assert
|
||||
@ -452,7 +452,10 @@ void InitializeCPUContextARM64(CPUContextARM64* context,
|
||||
context->pc = arm_thread_state64->__pc;
|
||||
context->spsr =
|
||||
static_cast<decltype(context->spsr)>(arm_thread_state64->__cpsr);
|
||||
}
|
||||
|
||||
void InitializeCPUContextARM64Neon(CPUContextARM64* context,
|
||||
const arm_neon_state64_t* arm_neon_state64) {
|
||||
static_assert(sizeof(context->fpsimd) == sizeof(arm_neon_state64->__v),
|
||||
"fpsimd context size mismatch");
|
||||
memcpy(context->fpsimd, arm_neon_state64->__v, sizeof(arm_neon_state64->__v));
|
||||
@ -460,6 +463,107 @@ void InitializeCPUContextARM64(CPUContextARM64* context,
|
||||
context->fpcr = arm_neon_state64->__fpcr;
|
||||
}
|
||||
|
||||
thread_state_flavor_t InitializeCPUContextARM64Flavor(
|
||||
CPUContextARM64* context,
|
||||
thread_state_flavor_t flavor,
|
||||
ConstThreadState state,
|
||||
mach_msg_type_number_t state_count) {
|
||||
mach_msg_type_number_t expected_state_count;
|
||||
switch (flavor) {
|
||||
case ARM_THREAD_STATE:
|
||||
expected_state_count = ARM_THREAD_STATE_COUNT;
|
||||
break;
|
||||
case ARM_THREAD_STATE64:
|
||||
expected_state_count = ARM_THREAD_STATE64_COUNT;
|
||||
break;
|
||||
case ARM_NEON_STATE64:
|
||||
expected_state_count = ARM_NEON_STATE64_COUNT;
|
||||
break;
|
||||
case THREAD_STATE_NONE: {
|
||||
// This may happen without error when called without exception-style
|
||||
// flavor data, or even from an exception handler when the exception
|
||||
// behavior is EXCEPTION_DEFAULT.
|
||||
return flavor;
|
||||
}
|
||||
default:
|
||||
LOG(WARNING) << "unhandled flavor " << flavor;
|
||||
return THREAD_STATE_NONE;
|
||||
}
|
||||
|
||||
if (state_count < expected_state_count) {
|
||||
LOG(WARNING) << "expected state_count " << expected_state_count
|
||||
<< " for flavor " << flavor << ", observed " << state_count;
|
||||
return THREAD_STATE_NONE;
|
||||
}
|
||||
|
||||
switch (flavor) {
|
||||
case ARM_THREAD_STATE: {
|
||||
const arm_unified_thread_state_t* arm_thread_state =
|
||||
reinterpret_cast<const arm_unified_thread_state_t*>(state);
|
||||
if (arm_thread_state->ash.flavor != ARM_THREAD_STATE64) {
|
||||
LOG(WARNING) << "expected flavor ARM_THREAD_STATE64, observed "
|
||||
<< arm_thread_state->ash.flavor;
|
||||
return THREAD_STATE_NONE;
|
||||
}
|
||||
return InitializeCPUContextARM64Flavor(
|
||||
context,
|
||||
arm_thread_state->ash.flavor,
|
||||
reinterpret_cast<ConstThreadState>(&arm_thread_state->ts_64),
|
||||
arm_thread_state->ash.count);
|
||||
}
|
||||
|
||||
case ARM_THREAD_STATE64: {
|
||||
const arm_thread_state64_t* arm_thread_state =
|
||||
reinterpret_cast<const arm_thread_state64_t*>(state);
|
||||
InitializeCPUContextARM64Thread(context, arm_thread_state);
|
||||
return ARM_THREAD_STATE64;
|
||||
}
|
||||
|
||||
case ARM_NEON_STATE64: {
|
||||
const arm_neon_state64_t* arm_neon_state =
|
||||
reinterpret_cast<const arm_neon_state64_t*>(state);
|
||||
InitializeCPUContextARM64Neon(context, arm_neon_state);
|
||||
return ARM_NEON_STATE64;
|
||||
}
|
||||
|
||||
case THREAD_STATE_NONE: {
|
||||
// This may happen without error when called without exception-style
|
||||
// flavor data, or even from an exception handler when the exception
|
||||
// behavior is EXCEPTION_DEFAULT.
|
||||
return flavor;
|
||||
}
|
||||
|
||||
default: {
|
||||
NOTREACHED();
|
||||
return THREAD_STATE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace internal {
|
||||
|
||||
void InitializeCPUContextARM64(CPUContextARM64* context,
|
||||
thread_state_flavor_t flavor,
|
||||
ConstThreadState state,
|
||||
mach_msg_type_number_t state_count,
|
||||
const arm_thread_state64_t* arm_thread_state64,
|
||||
const arm_neon_state64_t* arm_neon_state64) {
|
||||
thread_state_flavor_t set_flavor = THREAD_STATE_NONE;
|
||||
if (flavor != THREAD_STATE_NONE) {
|
||||
set_flavor =
|
||||
InitializeCPUContextARM64Flavor(context, flavor, state, state_count);
|
||||
}
|
||||
|
||||
if (set_flavor != ARM_THREAD_STATE64) {
|
||||
InitializeCPUContextARM64Thread(context, arm_thread_state64);
|
||||
}
|
||||
if (set_flavor != ARM_NEON_STATE64) {
|
||||
InitializeCPUContextARM64Neon(context, arm_neon_state64);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
#endif
|
||||
|
@ -110,13 +110,40 @@ void InitializeCPUContextX86_64(CPUContextX86_64* context,
|
||||
|
||||
#elif defined(ARCH_CPU_ARM_FAMILY) || DOXYGEN
|
||||
//! \brief Initializes a CPUContextARM64 structure from native context
|
||||
//! structures.
|
||||
//! structures on iOS.
|
||||
//!
|
||||
//! \a flavor, \a state, and \a state_count may be supplied by exception
|
||||
//! handlers in order for the \a context parameter to be initialized by the
|
||||
//! thread state received by the exception handler to the extent possible. In
|
||||
//! that case, whatever thread state specified by these three parameters will
|
||||
//! supersede \a arm_thread_state64 or \a arm_neon_state64. If thread state in
|
||||
//! this format is not available, \a flavor may be set to `THREAD_STATE_NONE`,
|
||||
//! and all of \a arm_thread_state64 abd \a arm_neon_state64 will be honored.
|
||||
//!
|
||||
//! If \a flavor, \a state, and \a state_count are provided but do not contain
|
||||
//! valid values, a message will be logged and their values will be ignored as
|
||||
//! though \a flavor were specified as `THREAD_STATE_NONE`.
|
||||
//!
|
||||
//! \param[out] context The CPUContextARM64 structure to initialize.
|
||||
//! \param[in] flavor The native thread state flavor of \a state. This may be
|
||||
//! `ARM_THREAD_STATE64`, `ARM_THREAD_STATE` or `ARM_NEON_STATE64`. It may
|
||||
//! also be `THREAD_STATE_NONE` if \a state is not supplied (and is
|
||||
//! `nullptr`).
|
||||
//! \param[in] state The native thread state, which may be a casted pointer to
|
||||
//! `arm_thread_state64_t`, `arm_unified_thread_state` or
|
||||
//! `arm_neon_state64_t`. This parameter may be `nullptr` to not supply this
|
||||
//! data, in which case \a flavor must be `THREAD_STATE_NONE`. If a
|
||||
//! “universal” structure is used, it must carry 64-bit state data of the
|
||||
//! correct type.
|
||||
//! \param[in] state_count The number of `int`-sized units in \a state. This
|
||||
//! may be 0 if \a state is `nullptr`.
|
||||
//! \param[in] arm_thread_state64 The state of the thread’s integer registers.
|
||||
//! \param[in] arm_neon_state64 The state of the thread’s floating-point
|
||||
//! registers.
|
||||
void InitializeCPUContextARM64(CPUContextARM64* context,
|
||||
thread_state_flavor_t flavor,
|
||||
ConstThreadState state,
|
||||
mach_msg_type_number_t state_count,
|
||||
const arm_thread_state64_t* arm_thread_state64,
|
||||
const arm_neon_state64_t* arm_neon_state64);
|
||||
#endif
|
||||
|
@ -142,8 +142,13 @@ bool ExceptionSnapshotMac::Initialize(ProcessReaderMac* process_reader,
|
||||
|
||||
thread_id_ = thread->id;
|
||||
|
||||
// Normally, the exception address is present in code[1] for EXC_BAD_ACCESS
|
||||
// exceptions, but not for other types of exceptions.
|
||||
// Normally, for EXC_BAD_ACCESS exceptions, the exception address is present
|
||||
// in code[1]. It may or may not be the instruction pointer address (usually
|
||||
// it’s not). code[1] may carry the exception address for other exception
|
||||
// types too, but it’s not guaranteed. But for all other exception types, the
|
||||
// instruction pointer will be the exception address, and in fact will be
|
||||
// equal to codes[1] when it’s carrying the exception address. In those cases,
|
||||
// just use the instruction pointer directly.
|
||||
bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS;
|
||||
|
||||
#if defined(ARCH_CPU_X86_FAMILY)
|
||||
|
@ -35,8 +35,6 @@
|
||||
action:@selector(throwUIGestureEnvironmentException)];
|
||||
[button addGestureRecognizer:tapGesture];
|
||||
[button setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
[button.widthAnchor constraintEqualToConstant:16.0].active = YES;
|
||||
[button.heightAnchor constraintEqualToConstant:16.0].active = YES;
|
||||
|
||||
[buttonStack addArrangedSubview:button];
|
||||
|
||||
|
@ -305,6 +305,7 @@ static_library("util") {
|
||||
"mach/mach_message.h",
|
||||
"mach/mach_message_server.cc",
|
||||
"mach/mach_message_server.h",
|
||||
"misc/capture_context_mac.S",
|
||||
"misc/clock_mac.cc",
|
||||
"misc/paths_mac.cc",
|
||||
"synchronization/semaphore_mac.cc",
|
||||
@ -337,7 +338,6 @@ static_library("util") {
|
||||
"mach/symbolic_constants_mach.h",
|
||||
"mach/task_for_pid.cc",
|
||||
"mach/task_for_pid.h",
|
||||
"misc/capture_context_mac.S",
|
||||
"net/http_transport_mac.mm",
|
||||
"posix/process_info_mac.cc",
|
||||
"process/process_memory_mac.cc",
|
||||
|
@ -17,9 +17,7 @@
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_IOS)
|
||||
#include <sys/ucontext.h>
|
||||
#elif defined(OS_MACOSX)
|
||||
#if defined(OS_MACOSX)
|
||||
#include <mach/mach.h>
|
||||
#elif defined(OS_WIN)
|
||||
#include <windows.h>
|
||||
@ -31,11 +29,11 @@
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
#if defined(OS_IOS)
|
||||
using NativeCPUContext = ucontext_t;
|
||||
#elif defined(OS_MACOSX)
|
||||
#if defined(OS_MACOSX)
|
||||
#if defined(ARCH_CPU_X86_FAMILY)
|
||||
using NativeCPUContext = x86_thread_state;
|
||||
#elif defined(ARCH_CPU_ARM64)
|
||||
using NativeCPUContext = arm_unified_thread_state;
|
||||
#endif
|
||||
#elif defined(OS_WIN)
|
||||
using NativeCPUContext = CONTEXT;
|
||||
|
Loading…
x
Reference in New Issue
Block a user