mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
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:
parent
b6a0183cce
commit
1e7cdb30a0
@ -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) {
|
||||
|
130
util/mach/exc_client_variants.cc
Normal file
130
util/mach/exc_client_variants.cc
Normal 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 aren’t 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
|
83
util/mach/exc_client_variants.h
Normal file
83
util/mach/exc_client_variants.h
Normal 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_
|
288
util/mach/exc_client_variants_test.cc
Normal file
288
util/mach/exc_client_variants_test.cc
Normal 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 aren’t real flavors, it’s 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
|
@ -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);
|
||||
|
27
util/mach/mach_extensions.cc
Normal file
27
util/mach/mach_extensions.cc
Normal 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
|
@ -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, it’s 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 port’s 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 thread’s 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_
|
||||
|
29
util/mach/mach_extensions_test.cc
Normal file
29
util/mach/mach_extensions_test.cc
Normal 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
|
@ -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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user