[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:
Justin Cohen 2020-04-25 17:16:10 -04:00 committed by Commit Bot
parent 6d8b196150
commit 17a515d33d
15 changed files with 558 additions and 86 deletions

View File

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

View File

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

View File

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

View File

@ -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,
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
// 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_);
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 {

View File

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

View File

@ -102,13 +102,36 @@ bool ProcessSnapshotIOS::Initialize(const IOSSystemDataCollector& system_data) {
return true;
}
void ProcessSnapshotIOS::SetException(const siginfo_t* siginfo,
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 {

View File

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

View File

@ -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_);

View File

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

View File

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

View File

@ -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 threads integer registers.
//! \param[in] arm_neon_state64 The state of the threads 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

View File

@ -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
// 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_FAMILY)

View File

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

View File

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

View File

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