mac: Report richer exception codes via metrics
Previously, only the top-level exception code was reported via the Crashpad.ExceptionCode.Mac histogram. Making this histogram work (https://crbug.com/678720) has revealed that Chrome is triggering EXC_RESOURCE exceptions at a rate in excess of 4x that of ordinary crashes. These exceptions were not previously visible because they are not uploaded unless the system treats them as fatal, which it does not normally do absent an explicit request. In order to learn more about the problem, this change augments the data reported via the Crashpad.ExceptionCode.Mac histogram to report (at least) second-level exception data. This means that we will no longer see just EXC_RESOURCE, but potentially more useful information such as EXC_RESOURCE / RESOURCE_TYPE_IO / FLAVOR_IO_PHYSICAL_WRITES. This also applies to other exception types, so that the majority of crashes currently falling into the EXC_CRASH bucket will now have additional information decoded and will be reported as, for example, EXC_BAD_ACCESS / KERN_INVALID_ADDRESS, EXC_BAD_INSTRUCTION / EXC_I386_INVOP, and EXC_CRASH / SIGABRT. Because the old mechanism was only live (in an “it works” sense) for several days, and the new mechanism does not overlap with histogram values used by the old one, there’s no need to invent a new histogram name. BUG=chromium:684051 Change-Id: Ia0a372b4127f7b3b2e7dbbaac9304cce3b5aadfe Reviewed-on: https://chromium-review.googlesource.com/430933 Reviewed-by: Scott Graham <scottmg@chromium.org>
This commit is contained in:
Родитель
f9b3a18f3f
Коммит
e7630628e9
|
@ -66,7 +66,7 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
|
|||
const mach_msg_trailer_t* trailer,
|
||||
bool* destroy_complex_request) {
|
||||
Metrics::ExceptionEncountered();
|
||||
Metrics::ExceptionCode(exception);
|
||||
Metrics::ExceptionCode(ExceptionCodeForMetrics(exception, code[0]));
|
||||
*destroy_complex_request = true;
|
||||
|
||||
// The expected behavior is EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
|
||||
|
|
|
@ -66,20 +66,7 @@ bool ExceptionSnapshotMac::Initialize(ProcessReader* process_reader,
|
|||
exception_ = ExcCrashRecoverOriginalException(
|
||||
exception_code_0, &exception_code_0, nullptr);
|
||||
|
||||
if (exception_ == EXC_CRASH ||
|
||||
exception_ == EXC_RESOURCE ||
|
||||
exception_ == EXC_GUARD) {
|
||||
// EXC_CRASH should never be wrapped in another EXC_CRASH.
|
||||
//
|
||||
// EXC_RESOURCE and EXC_GUARD are software exceptions that are never
|
||||
// wrapped in EXC_CRASH. The only time EXC_CRASH is generated is for
|
||||
// processes exiting due to an unhandled core-generating signal or being
|
||||
// killed by SIGKILL for code-signing reasons. Neither of these applies to
|
||||
// EXC_RESOURCE or EXC_GUARD. See 10.10 xnu-2782.1.97/bsd/kern/kern_exit.c
|
||||
// proc_prepareexit(). Receiving these exception types wrapped in
|
||||
// EXC_CRASH would lose information because their code[0] uses all 64 bits
|
||||
// (see below) and the code[0] recovered from EXC_CRASH only contains 20
|
||||
// significant bits.
|
||||
if (!ExcCrashCouldContainException(exception_)) {
|
||||
LOG(WARNING) << base::StringPrintf(
|
||||
"exception %s invalid in EXC_CRASH",
|
||||
ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric)
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#include "base/logging.h"
|
||||
#include "base/mac/mach_logging.h"
|
||||
#include "util/mac/mac_util.h"
|
||||
#include "util/mach/mach_extensions.h"
|
||||
#include "util/numeric/in_range_cast.h"
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
|
||||
|
||||
|
@ -128,6 +130,114 @@ exception_type_t ExcCrashRecoverOriginalException(
|
|||
return (code_0 >> 20) & 0xf;
|
||||
}
|
||||
|
||||
bool ExcCrashCouldContainException(exception_type_t exception) {
|
||||
// EXC_CRASH should never be wrapped in another EXC_CRASH.
|
||||
//
|
||||
// EXC_RESOURCE and EXC_GUARD are software exceptions that are never wrapped
|
||||
// in EXC_CRASH. The only time EXC_CRASH is generated is for processes exiting
|
||||
// due to an unhandled core-generating signal or being killed by SIGKILL for
|
||||
// code-signing reasons. Neither of these apply to EXC_RESOURCE or EXC_GUARD.
|
||||
// See 10.10 xnu-2782.1.97/bsd/kern/kern_exit.c proc_prepareexit(). Receiving
|
||||
// these exception types wrapped in EXC_CRASH would lose information because
|
||||
// their code[0] uses all 64 bits (see ExceptionSnapshotMac::Initialize()) and
|
||||
// the code[0] recovered from EXC_CRASH only contains 20 significant bits.
|
||||
//
|
||||
// EXC_CORPSE_NOTIFY may be generated from EXC_CRASH, but the opposite should
|
||||
// never occur.
|
||||
//
|
||||
// kMachExceptionSimulated is a non-fatal Crashpad-specific pseudo-exception
|
||||
// that never exists as an exception within the kernel and should thus never
|
||||
// be wrapped in EXC_CRASH.
|
||||
return exception != EXC_CRASH &&
|
||||
exception != EXC_RESOURCE &&
|
||||
exception != EXC_GUARD &&
|
||||
exception != EXC_CORPSE_NOTIFY &&
|
||||
exception != kMachExceptionSimulated;
|
||||
}
|
||||
|
||||
int32_t ExceptionCodeForMetrics(exception_type_t exception,
|
||||
mach_exception_code_t code_0) {
|
||||
if (exception == kMachExceptionSimulated) {
|
||||
return exception;
|
||||
}
|
||||
|
||||
int signal = 0;
|
||||
if (exception == EXC_CRASH) {
|
||||
const exception_type_t original_exception =
|
||||
ExcCrashRecoverOriginalException(code_0, &code_0, &signal);
|
||||
if (!ExcCrashCouldContainException(original_exception)) {
|
||||
LOG(WARNING) << "EXC_CRASH should not contain exception "
|
||||
<< original_exception;
|
||||
return InRangeCast<uint16_t>(original_exception, 0xffff) << 16;
|
||||
}
|
||||
exception = original_exception;
|
||||
}
|
||||
|
||||
uint16_t metrics_exception = InRangeCast<uint16_t>(exception, 0xffff);
|
||||
|
||||
uint16_t metrics_code_0;
|
||||
switch (exception) {
|
||||
case EXC_RESOURCE:
|
||||
metrics_code_0 = (EXC_RESOURCE_DECODE_RESOURCE_TYPE(code_0) << 8) |
|
||||
EXC_RESOURCE_DECODE_FLAVOR(code_0);
|
||||
break;
|
||||
|
||||
case EXC_GUARD: {
|
||||
// This will be GUARD_TYPE_MACH_PORT (1) from <mach/port.h> or
|
||||
// GUARD_TYPE_FD (2) from 10.12.2 xnu-3789.31.2/bsd/sys/guarded.h
|
||||
const uint8_t guard_type = (code_0) >> 61;
|
||||
|
||||
// These exceptions come through 10.12.2
|
||||
// xnu-3789.31.2/osfmk/ipc/mach_port.c mach_port_guard_exception() or
|
||||
// xnu-3789.31.2/bsd/kern/kern_guarded.c fp_guard_exception(). In each
|
||||
// case, bits 32-60 of code_0 encode the guard type-specific “flavor”. For
|
||||
// Mach port guards, these flavor codes come from the
|
||||
// mach_port_guard_exception_codes enum in <mach/port.h>. For file
|
||||
// descriptor guards, they come from the guard_exception_codes enum in
|
||||
// xnu-3789.31.2/bsd/sys/guarded.h. Both of these enums define shifted-bit
|
||||
// values (1 << 0, 1 << 1, 1 << 2, etc.) In actual usage as determined by
|
||||
// callers to these functions, these “flavor” codes are never ORed with
|
||||
// one another. For the purposes of encoding these codes for metrics,
|
||||
// convert the flavor codes to their corresponding bit shift values.
|
||||
const uint32_t guard_flavor = (code_0 >> 32) & 0x1fffffff;
|
||||
uint8_t metrics_guard_flavor = 0xff;
|
||||
for (int bit = 0; bit < 29; ++bit) {
|
||||
const uint32_t test_mask = 1 << bit;
|
||||
if (guard_flavor & test_mask) {
|
||||
// Make sure that no other bits are set.
|
||||
DCHECK_EQ(guard_flavor, test_mask);
|
||||
|
||||
metrics_guard_flavor = bit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
metrics_code_0 = (guard_type << 8) | metrics_guard_flavor;
|
||||
break;
|
||||
}
|
||||
|
||||
case EXC_CORPSE_NOTIFY:
|
||||
// code_0 may be a pointer. See 10.12.2 xnu-3789.31.2/osfmk/kern/task.c
|
||||
// task_deliver_crash_notification(). Just encode 0 for metrics purposes.
|
||||
metrics_code_0 = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
metrics_code_0 = InRangeCast<uint16_t>(code_0, 0xffff);
|
||||
if (exception == 0 && metrics_code_0 == 0 && signal != 0) {
|
||||
// This exception came from a signal that did not originate as another
|
||||
// Mach exception. Encode the signal number, using EXC_CRASH as the
|
||||
// top-level exception type. This is safe because EXC_CRASH will not
|
||||
// otherwise appear as metrics_exception.
|
||||
metrics_exception = EXC_CRASH;
|
||||
metrics_code_0 = signal;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return (metrics_exception << 16) | metrics_code_0;
|
||||
}
|
||||
|
||||
bool IsExceptionNonfatalResource(exception_type_t exception,
|
||||
mach_exception_code_t code_0,
|
||||
pid_t pid) {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#ifndef CRASHPAD_UTIL_MACH_EXCEPTION_TYPES_H_
|
||||
#define CRASHPAD_UTIL_MACH_EXCEPTION_TYPES_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <mach/mach.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
@ -54,6 +55,55 @@ exception_type_t ExcCrashRecoverOriginalException(
|
|||
mach_exception_code_t* original_code_0,
|
||||
int* signal);
|
||||
|
||||
//! \brief Determines whether a given exception type could plausibly be carried
|
||||
//! within an `EXC_CRASH` exception.
|
||||
//!
|
||||
//! \param[in] exception The exception type to test.
|
||||
//!
|
||||
//! \return `true` if an `EXC_CRASH` exception could plausibly carry \a
|
||||
//! exception.
|
||||
//!
|
||||
//! An `EXC_CRASH` exception can wrap exceptions that originate as hardware
|
||||
//! faults, as well as exceptions that originate from certain software sources
|
||||
//! such as POSIX signals. It cannot wrap another `EXC_CRASH` exception, nor can
|
||||
//! it wrap `EXC_RESOURCE`, `EXC_GUARD`, or `EXC_CORPSE_NOTIFY` exceptions. It
|
||||
//! also cannot wrap Crashpad-specific #kMachExceptionSimulated exceptions.
|
||||
bool ExcCrashCouldContainException(exception_type_t exception);
|
||||
|
||||
//! \brief Returns the exception code to report via a configured metrics system.
|
||||
//!
|
||||
//! \param[in] exception The exception type as received by a Mach exception
|
||||
//! handler.
|
||||
//! \param[in] code_0 The first exception code (`code[0]`) as received by a
|
||||
//! Mach exception handler.
|
||||
//!
|
||||
//! \return An exception code that maps useful information from \a exception and
|
||||
//! \a code_0 to the more limited data type available for metrics reporting.
|
||||
//!
|
||||
//! For classic Mach exceptions (including hardware faults reported as Mach
|
||||
//! exceptions), the mapping is `(exception << 16) | code_0`.
|
||||
//!
|
||||
//! For `EXC_CRASH` exceptions that originate as Mach exceptions described
|
||||
//! above, the mapping above is used, with the original exception’s values. For
|
||||
//! `EXC_CRASH` exceptions that originate as POSIX signals without an underlying
|
||||
//! Mach exception, the mapping is `(EXC_CRASH << 16) | code_0`.
|
||||
//!
|
||||
//! `EXC_RESOURCE` and `EXC_GUARD` exceptions both contain exception-specific
|
||||
//! “type” values and type-specific “flavor” values. In these cases, the mapping
|
||||
//! is `(exception << 16) | (type << 8) | flavor`. For `EXC_GUARD`, the “flavor”
|
||||
//! value is rewritten to be more space-efficient by replacing the
|
||||
//! kernel-supplied bitmask having exactly one bit set with the index of the set
|
||||
//! bit.
|
||||
//!
|
||||
//! `EXC_CORPSE_NOTIFY` exceptions are reported as classic Mach exceptions with
|
||||
//! the \a code_0 field set to `0`.
|
||||
//!
|
||||
//! If \a exception is #kMachExceptionSimulated, that value is returned as-is.
|
||||
//!
|
||||
//! Overflow conditions in any field are handled via saturation.
|
||||
int32_t ExceptionCodeForMetrics(exception_type_t exception,
|
||||
mach_exception_code_t code_0);
|
||||
|
||||
//! \brief Determines whether an exception is a non-fatal `EXC_RESOURCE`.
|
||||
//!
|
||||
//! \param[in] exception The exception type as received by a Mach exception
|
||||
|
|
|
@ -41,9 +41,21 @@ TEST(ExceptionTypes, ExcCrashRecoverOriginalException) {
|
|||
{0xb100001, EXC_BAD_ACCESS, KERN_INVALID_ADDRESS, SIGSEGV},
|
||||
{0xb100002, EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE, SIGSEGV},
|
||||
{0xa100002, EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE, SIGBUS},
|
||||
{0x4200001, EXC_BAD_INSTRUCTION, 1, SIGILL},
|
||||
{0x8300001, EXC_ARITHMETIC, 1, SIGFPE},
|
||||
{0x5600002, EXC_BREAKPOINT, 2, SIGTRAP},
|
||||
{0xa100005, EXC_BAD_ACCESS, VM_PROT_READ | VM_PROT_EXECUTE, SIGBUS},
|
||||
{0xa10000d, EXC_BAD_ACCESS, EXC_I386_GPFLT, SIGBUS},
|
||||
{0x9100032, EXC_BAD_ACCESS, KERN_CODESIGN_ERROR, SIGKILL},
|
||||
{0x4200001, EXC_BAD_INSTRUCTION, EXC_I386_INVOP, SIGILL},
|
||||
{0x420000b, EXC_BAD_INSTRUCTION, EXC_I386_SEGNPFLT, SIGILL},
|
||||
{0x420000c, EXC_BAD_INSTRUCTION, EXC_I386_STKFLT, SIGILL},
|
||||
{0x8300001, EXC_ARITHMETIC, EXC_I386_DIV, SIGFPE},
|
||||
{0x8300002, EXC_ARITHMETIC, EXC_I386_INTO, SIGFPE},
|
||||
{0x8300005, EXC_ARITHMETIC, EXC_I386_EXTERR, SIGFPE},
|
||||
{0x8300008, EXC_ARITHMETIC, EXC_I386_SSEEXTERR, SIGFPE},
|
||||
{0x5500007, EXC_SOFTWARE, EXC_I386_BOUND, SIGTRAP},
|
||||
{0x5600001, EXC_BREAKPOINT, EXC_I386_SGL, SIGTRAP},
|
||||
{0x5600002, EXC_BREAKPOINT, EXC_I386_BPT, SIGTRAP},
|
||||
{0x0700080, EXC_SYSCALL, 128, 0},
|
||||
{0x0706000, EXC_SYSCALL, 0x6000, 0},
|
||||
{0x3000000, 0, 0, SIGQUIT},
|
||||
{0x6000000, 0, 0, SIGABRT},
|
||||
{0xc000000, 0, 0, SIGSYS},
|
||||
|
@ -86,40 +98,173 @@ TEST(ExceptionTypes, ExcCrashRecoverOriginalException) {
|
|||
EXPECT_EQ(test_data.signal, signal);
|
||||
}
|
||||
|
||||
// These macros come from the #ifdef KERNEL section of <kern/exc_resource.h>:
|
||||
// 10.10 xnu-2782.1.97/osfmk/kern/exc_resource.h.
|
||||
#define EXC_RESOURCE_ENCODE_TYPE(code, type) \
|
||||
((code) |= ((static_cast<uint64_t>(type) & 0x7ull) << 61))
|
||||
#define EXC_RESOURCE_ENCODE_FLAVOR(code, flavor) \
|
||||
((code) |= ((static_cast<uint64_t>(flavor) & 0x7ull) << 58))
|
||||
TEST(ExceptionTypes, ExcCrashCouldContainException) {
|
||||
// This seems wrong, but it happens when EXC_CRASH carries an exception not
|
||||
// originally caused by a hardware fault, such as SIGABRT.
|
||||
EXPECT_TRUE(ExcCrashCouldContainException(0));
|
||||
|
||||
EXPECT_TRUE(ExcCrashCouldContainException(EXC_BAD_ACCESS));
|
||||
EXPECT_TRUE(ExcCrashCouldContainException(EXC_BAD_INSTRUCTION));
|
||||
EXPECT_TRUE(ExcCrashCouldContainException(EXC_ARITHMETIC));
|
||||
EXPECT_TRUE(ExcCrashCouldContainException(EXC_EMULATION));
|
||||
EXPECT_TRUE(ExcCrashCouldContainException(EXC_SOFTWARE));
|
||||
EXPECT_TRUE(ExcCrashCouldContainException(EXC_BREAKPOINT));
|
||||
EXPECT_TRUE(ExcCrashCouldContainException(EXC_SYSCALL));
|
||||
EXPECT_TRUE(ExcCrashCouldContainException(EXC_MACH_SYSCALL));
|
||||
EXPECT_TRUE(ExcCrashCouldContainException(EXC_RPC_ALERT));
|
||||
EXPECT_FALSE(ExcCrashCouldContainException(EXC_CRASH));
|
||||
EXPECT_FALSE(ExcCrashCouldContainException(EXC_RESOURCE));
|
||||
EXPECT_FALSE(ExcCrashCouldContainException(EXC_GUARD));
|
||||
EXPECT_FALSE(ExcCrashCouldContainException(EXC_CORPSE_NOTIFY));
|
||||
EXPECT_FALSE(ExcCrashCouldContainException(kMachExceptionSimulated));
|
||||
}
|
||||
|
||||
// This macro is adapted from those in the #ifdef KERNEL section of
|
||||
// <kern/exc_resource.h>: 10.12.2 xnu-3789.31.2/osfmk/kern/exc_resource.h.
|
||||
#define EXC_RESOURCE_ENCODE_TYPE_FLAVOR(type, flavor) \
|
||||
(static_cast<mach_exception_code_t>( \
|
||||
(((static_cast<uint64_t>(type) & 0x7ull) << 61) | \
|
||||
(static_cast<uint64_t>(flavor) & 0x7ull) << 58)))
|
||||
|
||||
TEST(ExceptionTypes, ExceptionCodeForMetrics) {
|
||||
struct TestData {
|
||||
exception_type_t exception;
|
||||
mach_exception_code_t code_0;
|
||||
int32_t metrics_code;
|
||||
};
|
||||
const TestData kTestData[] = {
|
||||
#define ENCODE_EXC(type, code_0) \
|
||||
{ (type), (code_0), ((type) << 16) | (code_0) }
|
||||
ENCODE_EXC(EXC_BAD_ACCESS, KERN_INVALID_ADDRESS),
|
||||
ENCODE_EXC(EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE),
|
||||
ENCODE_EXC(EXC_BAD_ACCESS, VM_PROT_READ | VM_PROT_EXECUTE),
|
||||
ENCODE_EXC(EXC_BAD_ACCESS, EXC_I386_GPFLT),
|
||||
ENCODE_EXC(EXC_BAD_ACCESS, KERN_CODESIGN_ERROR),
|
||||
ENCODE_EXC(EXC_BAD_INSTRUCTION, EXC_I386_INVOP),
|
||||
ENCODE_EXC(EXC_BAD_INSTRUCTION, EXC_I386_SEGNPFLT),
|
||||
ENCODE_EXC(EXC_BAD_INSTRUCTION, EXC_I386_STKFLT),
|
||||
ENCODE_EXC(EXC_ARITHMETIC, EXC_I386_DIV),
|
||||
ENCODE_EXC(EXC_ARITHMETIC, EXC_I386_INTO),
|
||||
ENCODE_EXC(EXC_ARITHMETIC, EXC_I386_EXTERR),
|
||||
ENCODE_EXC(EXC_ARITHMETIC, EXC_I386_SSEEXTERR),
|
||||
ENCODE_EXC(EXC_SOFTWARE, EXC_I386_BOUND),
|
||||
ENCODE_EXC(EXC_BREAKPOINT, EXC_I386_SGL),
|
||||
ENCODE_EXC(EXC_BREAKPOINT, EXC_I386_BPT),
|
||||
ENCODE_EXC(EXC_SYSCALL, 128),
|
||||
ENCODE_EXC(EXC_SYSCALL, 0x6000),
|
||||
#undef ENCODE_EXC
|
||||
|
||||
#define ENCODE_EXC_CRASH(type, code_0) \
|
||||
{ \
|
||||
EXC_CRASH, (((type) & 0xf) << 20) | ((code_0) & 0xfffff), \
|
||||
((type) << 16) | (code_0) \
|
||||
}
|
||||
ENCODE_EXC_CRASH(EXC_BAD_ACCESS, KERN_INVALID_ADDRESS),
|
||||
ENCODE_EXC_CRASH(EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE),
|
||||
ENCODE_EXC_CRASH(EXC_BAD_ACCESS, VM_PROT_READ | VM_PROT_EXECUTE),
|
||||
ENCODE_EXC_CRASH(EXC_BAD_ACCESS, EXC_I386_GPFLT),
|
||||
ENCODE_EXC_CRASH(EXC_BAD_ACCESS, KERN_CODESIGN_ERROR),
|
||||
ENCODE_EXC_CRASH(EXC_BAD_INSTRUCTION, EXC_I386_INVOP),
|
||||
ENCODE_EXC_CRASH(EXC_BAD_INSTRUCTION, EXC_I386_SEGNPFLT),
|
||||
ENCODE_EXC_CRASH(EXC_BAD_INSTRUCTION, EXC_I386_STKFLT),
|
||||
ENCODE_EXC_CRASH(EXC_ARITHMETIC, EXC_I386_DIV),
|
||||
ENCODE_EXC_CRASH(EXC_ARITHMETIC, EXC_I386_INTO),
|
||||
ENCODE_EXC_CRASH(EXC_ARITHMETIC, EXC_I386_EXTERR),
|
||||
ENCODE_EXC_CRASH(EXC_ARITHMETIC, EXC_I386_SSEEXTERR),
|
||||
ENCODE_EXC_CRASH(EXC_SOFTWARE, EXC_I386_BOUND),
|
||||
ENCODE_EXC_CRASH(EXC_BREAKPOINT, EXC_I386_SGL),
|
||||
ENCODE_EXC_CRASH(EXC_BREAKPOINT, EXC_I386_BPT),
|
||||
ENCODE_EXC_CRASH(EXC_SYSCALL, 128),
|
||||
ENCODE_EXC_CRASH(EXC_SYSCALL, 0x6000),
|
||||
#undef ENCODE_EXC_CRASH
|
||||
|
||||
#define ENCODE_EXC_CRASH_SIGNAL(signal) \
|
||||
{ EXC_CRASH, (((signal) & 0xff) << 24), (EXC_CRASH << 16) | (signal) }
|
||||
ENCODE_EXC_CRASH_SIGNAL(SIGQUIT),
|
||||
ENCODE_EXC_CRASH_SIGNAL(SIGABRT),
|
||||
ENCODE_EXC_CRASH_SIGNAL(SIGSYS),
|
||||
#undef ENCODE_EXC_CRASH_SIGNAL
|
||||
|
||||
#define ENCODE_EXC_RESOURCE(type, flavor) \
|
||||
{ \
|
||||
EXC_RESOURCE, EXC_RESOURCE_ENCODE_TYPE_FLAVOR((type), (flavor)), \
|
||||
(EXC_RESOURCE << 16) | ((type) << 8) | (flavor) \
|
||||
}
|
||||
ENCODE_EXC_RESOURCE(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR),
|
||||
ENCODE_EXC_RESOURCE(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR_FATAL),
|
||||
ENCODE_EXC_RESOURCE(RESOURCE_TYPE_WAKEUPS, FLAVOR_WAKEUPS_MONITOR),
|
||||
ENCODE_EXC_RESOURCE(RESOURCE_TYPE_MEMORY, FLAVOR_HIGH_WATERMARK),
|
||||
ENCODE_EXC_RESOURCE(RESOURCE_TYPE_IO, FLAVOR_IO_PHYSICAL_WRITES),
|
||||
ENCODE_EXC_RESOURCE(RESOURCE_TYPE_IO, FLAVOR_IO_LOGICAL_WRITES),
|
||||
#undef ENCODE_EXC_RESOURCE
|
||||
|
||||
#define ENCODE_EXC_GUARD(type, flavor) \
|
||||
{ \
|
||||
EXC_GUARD, \
|
||||
static_cast<mach_exception_code_t>(static_cast<uint64_t>((type) & 0x7) \
|
||||
<< 61) | \
|
||||
(static_cast<uint64_t>((1 << (flavor)) & 0x1ffffffff) << 32), \
|
||||
(EXC_GUARD << 16) | ((type) << 8) | (flavor) \
|
||||
}
|
||||
ENCODE_EXC_GUARD(GUARD_TYPE_MACH_PORT, 0), // kGUARD_EXC_DESTROY
|
||||
ENCODE_EXC_GUARD(GUARD_TYPE_MACH_PORT, 1), // kGUARD_EXC_MOD_REFS
|
||||
ENCODE_EXC_GUARD(GUARD_TYPE_MACH_PORT, 2), // kGUARD_EXC_SET_CONTEXT
|
||||
ENCODE_EXC_GUARD(GUARD_TYPE_MACH_PORT, 3), // kGUARD_EXC_UNGUARDED
|
||||
ENCODE_EXC_GUARD(GUARD_TYPE_MACH_PORT, 4), // kGUARD_EXC_INCORRECT_GUARD
|
||||
|
||||
// 2 is GUARD_TYPE_FD from 10.12.2 xnu-3789.31.2/bsd/sys/guarded.h.
|
||||
ENCODE_EXC_GUARD(2, 0), // kGUARD_EXC_CLOSE
|
||||
ENCODE_EXC_GUARD(2, 1), // kGUARD_EXC_DUP
|
||||
ENCODE_EXC_GUARD(2, 2), // kGUARD_EXC_NOCLOEXEC
|
||||
ENCODE_EXC_GUARD(2, 3), // kGUARD_EXC_SOCKET_IPC
|
||||
ENCODE_EXC_GUARD(2, 4), // kGUARD_EXC_FILEPORT
|
||||
ENCODE_EXC_GUARD(2, 5), // kGUARD_EXC_MISMATCH
|
||||
ENCODE_EXC_GUARD(2, 6), // kGUARD_EXC_WRITE
|
||||
#undef ENCODE_EXC_GUARD
|
||||
|
||||
// Test that overflow saturates.
|
||||
{0x00010000, 0x00001000, static_cast<int32_t>(0xffff1000)},
|
||||
{0x00001000, 0x00010000, 0x1000ffff},
|
||||
{0x00010000, 0x00010000, static_cast<int32_t>(0xffffffff)},
|
||||
};
|
||||
|
||||
for (size_t index = 0; index < arraysize(kTestData); ++index) {
|
||||
const TestData& test_data = kTestData[index];
|
||||
SCOPED_TRACE(base::StringPrintf("index %zu, exception 0x%x, code_0 0x%llx",
|
||||
index,
|
||||
test_data.exception,
|
||||
test_data.code_0));
|
||||
|
||||
int32_t metrics_code =
|
||||
ExceptionCodeForMetrics(test_data.exception, test_data.code_0);
|
||||
|
||||
EXPECT_EQ(test_data.metrics_code, metrics_code);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ExceptionTypes, IsExceptionNonfatalResource) {
|
||||
const pid_t pid = getpid();
|
||||
|
||||
mach_exception_code_t code = 0;
|
||||
EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_CPU);
|
||||
EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_CPU_MONITOR);
|
||||
mach_exception_code_t code =
|
||||
EXC_RESOURCE_ENCODE_TYPE_FLAVOR(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR);
|
||||
EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));
|
||||
|
||||
if (MacOSXMinorVersion() >= 10) {
|
||||
// FLAVOR_CPU_MONITOR_FATAL was introduced in OS X 10.10.
|
||||
code = 0;
|
||||
EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_CPU);
|
||||
EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_CPU_MONITOR_FATAL);
|
||||
code = EXC_RESOURCE_ENCODE_TYPE_FLAVOR(RESOURCE_TYPE_CPU,
|
||||
FLAVOR_CPU_MONITOR_FATAL);
|
||||
EXPECT_FALSE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));
|
||||
}
|
||||
|
||||
// This assumes that WAKEMON_MAKE_FATAL is not set for this process. The
|
||||
// default is for WAKEMON_MAKE_FATAL to not be set, there’s no public API to
|
||||
// enable it, and nothing in this process should have enabled it.
|
||||
code = 0;
|
||||
EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_WAKEUPS);
|
||||
EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_WAKEUPS_MONITOR);
|
||||
code = EXC_RESOURCE_ENCODE_TYPE_FLAVOR(RESOURCE_TYPE_WAKEUPS,
|
||||
FLAVOR_WAKEUPS_MONITOR);
|
||||
EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));
|
||||
|
||||
code = 0;
|
||||
EXC_RESOURCE_ENCODE_TYPE(code, RESOURCE_TYPE_MEMORY);
|
||||
EXC_RESOURCE_ENCODE_FLAVOR(code, FLAVOR_HIGH_WATERMARK);
|
||||
code = EXC_RESOURCE_ENCODE_TYPE_FLAVOR(RESOURCE_TYPE_MEMORY,
|
||||
FLAVOR_HIGH_WATERMARK);
|
||||
EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));
|
||||
|
||||
// Non-EXC_RESOURCE exceptions should never be considered nonfatal resource
|
||||
|
|
Загрузка…
Ссылка в новой задаче