Add exc_client_variants (UniversalExceptionRaise()) and its test.

This also puts kMachExceptionCodes into mach_extensions.h, because a
form of MACH_EXCEPTION_CODES that’s the right type
(exception_behavior_t) has now shown its use in more than one file.

TEST=util_test ExcClientVariants.UniversalExceptionRaise
R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/567283002
This commit is contained in:
Mark Mentovai 2014-09-16 09:48:01 -04:00
parent b6a0183cce
commit 1e7cdb30a0
9 changed files with 623 additions and 29 deletions

View File

@ -32,6 +32,7 @@
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "util/file/fd_io.h"
#include "util/mach/mach_extensions.h"
#include "util/stdlib/pointer_container.h"
#include "util/test/errors.h"
#include "util/test/mac/dyld.h"
@ -153,7 +154,7 @@ TEST(ProcessReader, SelfOneThread) {
EXPECT_EQ(PthreadToThreadID(pthread_self()), threads[0].id);
base::mac::ScopedMachSendRight thread_self(mach_thread_self());
mach_port_t thread_self = MachThreadSelf();
EXPECT_EQ(thread_self, threads[0].port);
EXPECT_EQ(0, threads[0].suspend_count);
@ -418,7 +419,7 @@ TEST(ProcessReader, SelfSeveralThreads) {
// When testing in-process, verify that when this thread shows up in the
// vector, it has the expected thread port, and that this thread port only
// shows up once.
base::mac::ScopedMachSendRight thread_self(mach_thread_self());
mach_port_t thread_self = MachThreadSelf();
bool found_thread_self = false;
for (const ProcessReader::Thread& thread : threads) {
if (thread.port == thread_self) {

View File

@ -0,0 +1,130 @@
// Copyright 2014 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 "util/mach/exc_client_variants.h"
#include <vector>
#include "base/logging.h"
#include "util/mach/exc.h"
#include "util/mach/mach_exc.h"
#include "util/mach/mach_extensions.h"
namespace crashpad {
kern_return_t UniversalExceptionRaise(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,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count) {
// This function is similar to 10.9.4 xnu-2422.110.17/osfmk/kern/exception.c
// exception_deliver() as far as the delivery logic is concerned. Unlike
// exception_deliver(), this function does not get or set thread states for
// behavior values that require this, as that is left to the caller to do if
// needed.
std::vector<exception_data_type_t> small_code_vector;
exception_data_t small_code = NULL;
if ((behavior & MACH_EXCEPTION_CODES) == 0 && code_count) {
small_code_vector.reserve(code_count);
for (size_t code_index = 0; code_index < code_count; ++code_index) {
small_code_vector[code_index] = code[code_index];
}
small_code = &small_code_vector[0];
}
// The *exception_raise*() family has bad declarations. Their code and
// old_state arguments arent pointers to const data, although they should be.
// The generated stubs in excUser.c and mach_excUser.c make it clear that the
// data is never modified, and these parameters could be declared with const
// appropriately. The uses of const_cast below are thus safe.
switch (behavior) {
case EXCEPTION_DEFAULT:
return exception_raise(
exception_port, thread, task, exception, small_code, code_count);
case EXCEPTION_STATE:
return exception_raise_state(exception_port,
exception,
small_code,
code_count,
flavor,
const_cast<thread_state_t>(old_state),
old_state_count,
new_state,
new_state_count);
case EXCEPTION_STATE_IDENTITY:
return exception_raise_state_identity(
exception_port,
thread,
task,
exception,
small_code,
code_count,
flavor,
const_cast<thread_state_t>(old_state),
old_state_count,
new_state,
new_state_count);
case EXCEPTION_DEFAULT | kMachExceptionCodes:
return mach_exception_raise(exception_port,
thread,
task,
exception,
const_cast<mach_exception_data_type_t*>(code),
code_count);
case EXCEPTION_STATE | kMachExceptionCodes:
return mach_exception_raise_state(
exception_port,
exception,
const_cast<mach_exception_data_type_t*>(code),
code_count,
flavor,
const_cast<thread_state_t>(old_state),
old_state_count,
new_state,
new_state_count);
case EXCEPTION_STATE_IDENTITY | kMachExceptionCodes:
return mach_exception_raise_state_identity(
exception_port,
thread,
task,
exception,
const_cast<mach_exception_data_type_t*>(code),
code_count,
flavor,
const_cast<thread_state_t>(old_state),
old_state_count,
new_state,
new_state_count);
default:
NOTREACHED();
return KERN_INVALID_ARGUMENT;
}
}
} // namespace crashpad

View File

@ -0,0 +1,83 @@
// Copyright 2014 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.
#ifndef CRASHPAD_UTIL_MACH_EXC_CLIENT_VARIANTS_H_
#define CRASHPAD_UTIL_MACH_EXC_CLIENT_VARIANTS_H_
#include <mach/mach.h>
namespace crashpad {
//! \brief Calls the appropriate `*exception_raise*()` function for the
//! specified \a behavior.
//!
//! The function called will be `exception_raise()` for `EXCEPTION_DEFAULT`,
//! `exception_raise_state()` for `EXCEPTION_STATE`, or
//! `exception_raise_state_identity()` for `EXCEPTION_STATE_IDENTITY`. If
//! `MACH_EXCEPTION_CODES` is also set, the function called will instead be
//! `mach_exception_raise()`, `mach_exception_raise_state()` or
//! `mach_exception_raise_state_identity()`, respectively.
//!
//! This function does not fetch the existing thread state for \a behavior
//! values that require a thread state. The caller must provide the existing
//! thread state in the \a flavor, \a old_state, and \a old_state_count
//! parameters for \a behavior values that require a thread state. Thread states
//! may be obtained by calling `thread_get_state()` if needed. Similarly, this
//! function does not do anything with the new thread state returned for these
//! \a behavior values. Callers that wish to make use of the new thread state
//! may do so by using the returned \a flavor, \a new_state, and \a
//! new_state_count values. Thread states may be set by calling
//! `thread_set_state()` if needed.
//!
//! \a thread and \a task are only used when \a behavior indicates that the
//! exception message will carry identity information, when it has the value
//! value `EXCEPTION_DEFAULT` or `EXCEPTION_STATE_IDENTITY`, possibly with
//! `MACH_EXCEPTION_CODES` also set. In other cases, these parameters are unused
//! and may be set to `MACH_PORT_NULL`.
//!
//! \a flavor, \a old_state, \a old_state_count, \a new_state, and \a
//! new_state_count are only used when \a behavior indicates that the exception
//! message will carry thread state information, when it has the value
//! `EXCEPTION_STATE` or `EXCEPTION_STATE_IDENTITY`, possibly with
//! `MACH_EXCEPTION_CODES` also set. In other cases, these parameters are unused
//! and may be set to `0` (\a old_state_count) or `NULL` (the remaining
//! parameters).
//!
//! \param[in] behavior The exception behavior, which dictates which function
//! will be called. It is an error to call this function with an invalid
//! value for \a behavior.
//! \param[in] code If \behavior indicates a behavior without
//! `MACH_EXCEPTION_CODES`, the elements of \a code will be truncated in
//! order to be passed to the appropriate exception handler.
//!
//! All other parameters are treated equivalently to their treatment by the
//! `*exception_raise*()` family of functions.
//!
//! \return The return value of the function called.
kern_return_t UniversalExceptionRaise(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,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count);
} // namespace crashpad
#endif // CRASHPAD_UTIL_MACH_EXC_CLIENT_VARIANTS_H_

View File

@ -0,0 +1,288 @@
// Copyright 2014 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 "util/mach/exc_client_variants.h"
#include <mach/mach.h>
#include <pthread.h>
#include <string.h>
#include "base/basictypes.h"
#include "base/strings/stringprintf.h"
#include "gtest/gtest.h"
#include "util/mach/exc_server_variants.h"
#include "util/mach/mach_extensions.h"
#include "util/test/mac/mach_errors.h"
#include "util/test/mac/mach_multiprocess.h"
namespace {
using namespace crashpad;
using namespace crashpad::test;
class TestExcClientVariants : public UniversalMachExcServer,
public MachMultiprocess {
public:
TestExcClientVariants(exception_behavior_t behavior, bool all_fields)
: UniversalMachExcServer(),
MachMultiprocess(),
behavior_(behavior),
all_fields_(all_fields),
handled_(false) {
++exception_;
++exception_code_;
++exception_subcode_;
}
// UniversalMachExcServer:
virtual 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,
const natural_t* old_state,
mach_msg_type_number_t old_state_count,
thread_state_t new_state,
mach_msg_type_number_t* new_state_count,
bool* destroy_complex_request) override {
*destroy_complex_request = true;
EXPECT_FALSE(handled_);
handled_ = true;
EXPECT_EQ(behavior_, behavior);
EXPECT_EQ(LocalPort(), exception_port);
if (HasIdentity()) {
EXPECT_NE(kMachPortNull, thread);
EXPECT_EQ(ChildTask(), task);
} else {
EXPECT_EQ(kMachPortNull, thread);
EXPECT_EQ(kMachPortNull, task);
}
mach_exception_code_t expect_code = exception_code_;
mach_exception_subcode_t expect_subcode = exception_subcode_;
if ((behavior & MACH_EXCEPTION_CODES) == 0) {
expect_code = static_cast<exception_data_type_t>(expect_code);
expect_subcode = static_cast<exception_data_type_t>(expect_subcode);
}
EXPECT_EQ(exception_, exception);
EXPECT_EQ(2u, code_count);
EXPECT_EQ(expect_code, code[0]);
EXPECT_EQ(expect_subcode, code[1]);
if (HasState()) {
EXPECT_EQ(exception_ + 10, *flavor);
EXPECT_EQ(MACHINE_THREAD_STATE_COUNT, old_state_count);
EXPECT_NE(static_cast<const natural_t*>(NULL), old_state);
EXPECT_EQ(static_cast<mach_msg_type_number_t>(THREAD_STATE_MAX),
*new_state_count);
EXPECT_NE(static_cast<natural_t*>(NULL), new_state);
for (size_t index = 0; index < old_state_count; ++index) {
EXPECT_EQ(index, old_state[index]);
}
// Use a flavor known to be different from the incoming flavor, for a test
// of the “out” side of the inout flavor parameter.
*flavor = exception_ + 20;
*new_state_count = MACHINE_THREAD_STATE_COUNT;
// Send a new state back to the client.
for (size_t index = 0; index < *new_state_count; ++index) {
EXPECT_EQ(0u, new_state[index]);
new_state[index] = MACHINE_THREAD_STATE_COUNT - index;
}
} else {
EXPECT_EQ(THREAD_STATE_NONE, *flavor);
EXPECT_EQ(0u, old_state_count);
EXPECT_EQ(NULL, old_state);
EXPECT_EQ(0u, *new_state_count);
EXPECT_EQ(NULL, new_state);
}
return KERN_SUCCESS;
}
private:
// MachMultiprocess:
virtual void MachMultiprocessParent() override {
kern_return_t kr = MachMessageServer::Run(this,
LocalPort(),
MACH_MSG_OPTION_NONE,
MachMessageServer::kOneShot,
MachMessageServer::kBlocking,
0);
EXPECT_EQ(KERN_SUCCESS, kr)
<< MachErrorMessage(kr, "MachMessageServer::Run");
EXPECT_TRUE(handled_);
}
virtual void MachMultiprocessChild() override {
const exception_type_t exception = exception_;
const mach_exception_data_type_t code[] = {
exception_code_,
exception_subcode_
};
thread_t thread = MACH_PORT_NULL;
task_t task = MACH_PORT_NULL;
if (all_fields_ || HasIdentity()) {
thread = MachThreadSelf();
task = mach_task_self();
}
thread_state_flavor_t flavor;
thread_state_flavor_t* flavor_p = NULL;
natural_t old_state[MACHINE_THREAD_STATE_COUNT];
thread_state_t old_state_p = NULL;
mach_msg_type_number_t old_state_count = 0;
natural_t new_state[THREAD_STATE_MAX];
thread_state_t new_state_p = NULL;
mach_msg_type_number_t new_state_count;
mach_msg_type_number_t* new_state_count_p = NULL;
if (all_fields_ || HasState()) {
// Pick a different flavor each time based on the value of exception_.
// These arent real flavors, its just for testing.
flavor = exception_ + 10;
flavor_p = &flavor;
for (size_t index = 0; index < arraysize(old_state); ++index) {
old_state[index] = index;
}
old_state_p = reinterpret_cast<thread_state_t>(&old_state);
old_state_count = arraysize(old_state);
// new_state and new_state_count are out parameters that the server should
// never see or use, so set them to bogus values. The call to the server
// should overwrite these.
memset(new_state, 0xa5, sizeof(new_state));
new_state_p = reinterpret_cast<thread_state_t>(&new_state);
new_state_count = 0x5a;
new_state_count_p = &new_state_count;
}
EXPECT_EQ(KERN_SUCCESS, UniversalExceptionRaise(behavior_,
RemotePort(),
thread,
task,
exception,
code,
arraysize(code),
flavor_p,
old_state_p,
old_state_count,
new_state_p,
new_state_count_p));
if (HasState()) {
// Verify the out parameters.
EXPECT_EQ(exception_ + 20, flavor);
EXPECT_EQ(MACHINE_THREAD_STATE_COUNT, new_state_count);
for (size_t index = 0; index < new_state_count; ++index) {
EXPECT_EQ(MACHINE_THREAD_STATE_COUNT - index, new_state[index]);
}
}
}
exception_behavior_t BasicBehavior() const {
return behavior_ & ~MACH_EXCEPTION_CODES;
}
bool HasIdentity() const {
exception_behavior_t basic_behavior = BasicBehavior();
return basic_behavior == EXCEPTION_DEFAULT ||
basic_behavior == EXCEPTION_STATE_IDENTITY;
}
bool HasState() const {
exception_behavior_t basic_behavior = BasicBehavior();
return basic_behavior == EXCEPTION_STATE ||
basic_behavior == EXCEPTION_STATE_IDENTITY;
}
// The behavior to test.
exception_behavior_t behavior_;
// If false, only fields required for the current value of behavior_ are set
// in a call to UniversalExceptionRaise(). The thread and task fields are only
// set for identity-carrying behaviors, and the flavor and state fields are
// only set for state-carrying behaviors. If true, all fields are set
// regardless of the behavior. Testing in both ways verifies that
// UniversalExceptionRaise() can tolerate the null arguments documented as
// usable when the behavior allows it, and that it ignores these arguments
// even when set when the behavior does not make use of them.
bool all_fields_;
// true if an exception message was handled.
bool handled_;
// These fields will increment for each instantiation of the test class.
static exception_type_t exception_;
static mach_exception_code_t exception_code_;
static mach_exception_subcode_t exception_subcode_;
DISALLOW_COPY_AND_ASSIGN(TestExcClientVariants);
};
exception_type_t TestExcClientVariants::exception_ = 0;
// exception_code_ and exception_subcode_ are always large enough to require
// 64 bits, so that when the 32-bit-only variants not using MACH_EXCEPITON_CODES
// are tested, the code and subcode fields can be checked for proper truncation.
mach_exception_code_t TestExcClientVariants::exception_code_ = 0x100000000;
mach_exception_subcode_t TestExcClientVariants::exception_subcode_ =
0xffffffff00000000;
TEST(ExcClientVariants, UniversalExceptionRaise) {
const exception_behavior_t kBehaviors[] = {
EXCEPTION_DEFAULT,
EXCEPTION_STATE,
EXCEPTION_STATE_IDENTITY,
kMachExceptionCodes | EXCEPTION_DEFAULT,
kMachExceptionCodes | EXCEPTION_STATE,
kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,
};
for (size_t index = 0; index < arraysize(kBehaviors); ++index) {
exception_behavior_t behavior = kBehaviors[index];
SCOPED_TRACE(base::StringPrintf("index %zu, behavior %d", index, behavior));
{
SCOPED_TRACE("all_fields = false");
TestExcClientVariants test_exc_client_variants(behavior, false);
test_exc_client_variants.Run();
}
{
SCOPED_TRACE("all_fields = true");
TestExcClientVariants test_exc_client_variants(behavior, true);
test_exc_client_variants.Run();
}
}
}
} // namespace

View File

@ -44,12 +44,12 @@ const exception_type_t kExceptionType = EXC_BAD_ACCESS;
// Test using an exception code with the high bit set to ensure that it gets
// promoted to the wider mach_exception_data_type_t type as a signed quantity.
const exception_data_type_t kExceptionCodes[] = {
const exception_data_type_t kTestExceptonCodes[] = {
KERN_PROTECTION_FAILURE,
static_cast<exception_data_type_t>(0xfedcba98),
};
const exception_data_type_t kMachExceptionCodes[] = {
const exception_data_type_t kTestMachExceptionCodes[] = {
KERN_PROTECTION_FAILURE,
static_cast<exception_data_type_t>(0xfedcba9876543210),
};
@ -97,8 +97,8 @@ struct __attribute__((packed, aligned(4))) ExceptionRaiseRequest {
NDR = NDR_record;
exception = kExceptionType;
codeCnt = 2;
code[0] = kExceptionCodes[0];
code[1] = kExceptionCodes[1];
code[0] = kTestExceptonCodes[0];
code[1] = kTestExceptonCodes[1];
}
};
@ -127,8 +127,7 @@ struct __attribute__((packed, aligned(4))) ExceptionRaiseReply {
case EXCEPTION_DEFAULT:
EXPECT_EQ(2501, Head.msgh_id);
break;
case static_cast<exception_behavior_t>(EXCEPTION_DEFAULT |
MACH_EXCEPTION_CODES):
case EXCEPTION_DEFAULT | kMachExceptionCodes:
EXPECT_EQ(2505, Head.msgh_id);
break;
default:
@ -162,8 +161,8 @@ struct __attribute__((packed, aligned(4))) ExceptionRaiseStateRequest {
NDR = NDR_record;
exception = kExceptionType;
codeCnt = 2;
code[0] = kExceptionCodes[0];
code[1] = kExceptionCodes[1];
code[0] = kTestExceptonCodes[0];
code[1] = kTestExceptonCodes[1];
flavor = kThreadStateFlavor;
old_stateCnt = kThreadStateFlavorCount;
@ -205,12 +204,10 @@ struct __attribute__((packed, aligned(4))) ExceptionRaiseStateReply {
case EXCEPTION_STATE_IDENTITY:
EXPECT_EQ(2503, Head.msgh_id);
break;
case static_cast<exception_behavior_t>(EXCEPTION_STATE |
MACH_EXCEPTION_CODES):
case EXCEPTION_STATE | kMachExceptionCodes:
EXPECT_EQ(2506, Head.msgh_id);
break;
case static_cast<exception_behavior_t>(EXCEPTION_STATE_IDENTITY |
MACH_EXCEPTION_CODES):
case EXCEPTION_STATE_IDENTITY | kMachExceptionCodes:
EXPECT_EQ(2507, Head.msgh_id);
break;
default:
@ -253,8 +250,8 @@ struct __attribute__((packed, aligned(4))) ExceptionRaiseStateIdentityRequest {
NDR = NDR_record;
exception = kExceptionType;
codeCnt = 2;
code[0] = kExceptionCodes[0];
code[1] = kExceptionCodes[1];
code[0] = kTestExceptonCodes[0];
code[1] = kTestExceptonCodes[1];
flavor = kThreadStateFlavor;
old_stateCnt = kThreadStateFlavorCount;
@ -293,8 +290,8 @@ struct __attribute__((packed, aligned(4))) MachExceptionRaiseRequest {
NDR = NDR_record;
exception = kExceptionType;
codeCnt = 2;
code[0] = kMachExceptionCodes[0];
code[1] = kMachExceptionCodes[1];
code[0] = kTestMachExceptionCodes[0];
code[1] = kTestMachExceptionCodes[1];
}
};
@ -323,8 +320,8 @@ struct __attribute__((packed, aligned(4))) MachExceptionRaiseStateRequest {
NDR = NDR_record;
exception = kExceptionType;
codeCnt = 2;
code[0] = kMachExceptionCodes[0];
code[1] = kMachExceptionCodes[1];
code[0] = kTestMachExceptionCodes[0];
code[1] = kTestMachExceptionCodes[1];
flavor = kThreadStateFlavor;
old_stateCnt = kThreadStateFlavorCount;
@ -367,8 +364,8 @@ struct __attribute__((packed,
NDR = NDR_record;
exception = kExceptionType;
codeCnt = 2;
code[0] = kMachExceptionCodes[0];
code[1] = kMachExceptionCodes[1];
code[0] = kTestMachExceptionCodes[0];
code[1] = kTestMachExceptionCodes[1];
flavor = kThreadStateFlavor;
old_stateCnt = kThreadStateFlavorCount;
@ -564,7 +561,8 @@ TEST(ExcServerVariants, MockExceptionRaise) {
kExceptionThreadPort,
kExceptionTaskPort,
kExceptionType,
AreExceptionCodes(kExceptionCodes[0], kExceptionCodes[1]),
AreExceptionCodes(
kTestExceptonCodes[0], kTestExceptonCodes[1]),
Pointee(Eq(THREAD_STATE_NONE)),
IsThreadStateCount(0u),
IsThreadStateCount(0u)))
@ -603,7 +601,8 @@ TEST(ExcServerVariants, MockExceptionRaiseState) {
MACH_PORT_NULL,
MACH_PORT_NULL,
kExceptionType,
AreExceptionCodes(kExceptionCodes[0], kExceptionCodes[1]),
AreExceptionCodes(
kTestExceptonCodes[0], kTestExceptonCodes[1]),
Pointee(Eq(kThreadStateFlavor)),
IsThreadStateCount(kThreadStateFlavorCount),
IsThreadStateCount(arraysize(reply.new_state))))
@ -645,7 +644,8 @@ TEST(ExcServerVariants, MockExceptionRaiseStateIdentity) {
kExceptionThreadPort,
kExceptionTaskPort,
kExceptionType,
AreExceptionCodes(kExceptionCodes[0], kExceptionCodes[1]),
AreExceptionCodes(
kTestExceptonCodes[0], kTestExceptonCodes[1]),
Pointee(Eq(kThreadStateFlavor)),
IsThreadStateCount(kThreadStateFlavorCount),
IsThreadStateCount(arraysize(reply.new_state))))
@ -686,7 +686,8 @@ TEST(ExcServerVariants, MockMachExceptionRaise) {
kExceptionThreadPort,
kExceptionTaskPort,
kExceptionType,
AreExceptionCodes(kMachExceptionCodes[0], kMachExceptionCodes[1]),
AreExceptionCodes(
kTestMachExceptionCodes[0], kTestMachExceptionCodes[1]),
Pointee(Eq(THREAD_STATE_NONE)),
IsThreadStateCount(0u),
IsThreadStateCount(0u)))
@ -727,7 +728,8 @@ TEST(ExcServerVariants, MockMachExceptionRaiseState) {
MACH_PORT_NULL,
MACH_PORT_NULL,
kExceptionType,
AreExceptionCodes(kMachExceptionCodes[0], kMachExceptionCodes[1]),
AreExceptionCodes(
kTestMachExceptionCodes[0], kTestMachExceptionCodes[1]),
Pointee(Eq(kThreadStateFlavor)),
IsThreadStateCount(kThreadStateFlavorCount),
IsThreadStateCount(arraysize(reply.new_state))))
@ -771,7 +773,8 @@ TEST(ExcServerVariants, MockMachExceptionRaiseStateIdentity) {
kExceptionThreadPort,
kExceptionTaskPort,
kExceptionType,
AreExceptionCodes(kMachExceptionCodes[0], kMachExceptionCodes[1]),
AreExceptionCodes(
kTestMachExceptionCodes[0], kTestMachExceptionCodes[1]),
Pointee(Eq(kThreadStateFlavor)),
IsThreadStateCount(kThreadStateFlavorCount),
IsThreadStateCount(arraysize(reply.new_state))))
@ -924,7 +927,7 @@ class TestExcServerVariants : public UniversalMachExcServer,
EXPECT_NE(static_cast<const natural_t*>(NULL), old_state);
EXPECT_EQ(static_cast<mach_msg_type_number_t>(THREAD_STATE_MAX),
*new_state_count);
EXPECT_NE(static_cast<const natural_t*>(NULL), new_state);
EXPECT_NE(static_cast<natural_t*>(NULL), new_state);
} else {
EXPECT_EQ(THREAD_STATE_NONE, *flavor);
EXPECT_EQ(0u, old_state_count);

View File

@ -0,0 +1,27 @@
// Copyright 2014 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 "util/mach/mach_extensions.h"
#include <pthread.h>
namespace crashpad {
mach_port_t MachThreadSelf() {
// The pthreads library keeps its own copy of the mach_port_t. Using it does
// not increment its reference count.
return pthread_mach_thread_np(pthread_self());
}
} // namespace crashpad

View File

@ -28,6 +28,14 @@ namespace crashpad {
//! assertions.
const mach_port_t kMachPortNull = MACH_PORT_NULL;
//! \brief `MACH_EXCEPTION_CODES` with the correct type for a Mach exception
//! behavior, `exception_behavior_t`.
//!
//! Signedness problems can occur when ORing `MACH_EXCEPTION_CODES` as a signed
//! integer, because a signed integer overflow results. This constant can be
//! used instead of `MACH_EXCEPTION_CODES` in such cases.
const exception_behavior_t kMachExceptionCodes = MACH_EXCEPTION_CODES;
// Because exception_mask_t is an int and has one bit for each defined
// exception_type_t, its reasonable to assume that there cannot be any
// officially-defined exception_type_t values higher than 31.
@ -39,6 +47,26 @@ const mach_port_t kMachPortNull = MACH_PORT_NULL;
//! \brief An exception type to use for simulated exceptions.
const exception_type_t kMachExceptionSimulated = 'CPsx';
//! \brief Like `mach_thread_self()`, but without the obligation to release the
//! send right.
//!
//! `mach_thread_self()` returns a send right to the current thread port,
//! incrementing its reference count. This burdens the caller with maintaining
//! this send right, and calling `mach_port_deallocate()` when it is no longer
//! needed. This is burdensome, and is at odds with the normal operation of
//! `mach_task_self()`, which does not increment the task ports reference count
//! whose result must not be deallocated.
//!
//! Callers can use this function in preference to `mach_thread_self()`. This
//! function returns an extant reference to the current threads port without
//! incrementing its reference count.
//!
//! \return The value of `mach_thread_self()` without incrementing its reference
//! count. The returned port must not be deallocated by
//! `mach_port_deallocate()`. The returned value is valid as long as the
//! thread continues to exist as a `pthread_t`.
mach_port_t MachThreadSelf();
} // namespace crashpad
#endif // CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_

View File

@ -0,0 +1,29 @@
// Copyright 2014 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 "util/mach/mach_extensions.h"
#include "base/mac/scoped_mach_port.h"
#include "gtest/gtest.h"
namespace {
using namespace crashpad;
TEST(MachExtensions, MachThreadSelf) {
base::mac::ScopedMachSendRight thread_self(mach_thread_self());
EXPECT_EQ(thread_self, MachThreadSelf());
}
} // namespace

View File

@ -61,8 +61,11 @@
'mac/process_types/traits.h',
'mach/bootstrap.cc',
'mach/bootstrap.h',
'mach/exc_client_variants.cc',
'mach/exc_client_variants.h',
'mach/exc_server_variants.cc',
'mach/exc_server_variants.h',
'mach/mach_extensions.cc',
'mach/mach_extensions.h',
'mach/mach_message_server.cc',
'mach/mach_message_server.h',
@ -192,7 +195,9 @@
'mac/process_types_test.cc',
'mac/service_management_test.mm',
'mach/bootstrap_test.cc',
'mach/exc_client_variants_test.cc',
'mach/exc_server_variants_test.cc',
'mach/mach_extensions_test.cc',
'mach/mach_message_server_test.cc',
'mach/task_memory_test.cc',
'misc/initialization_state_dcheck_test.cc',