mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
Fix ExceptionPorts.TaskAndThreadExceptionPorts under better compiler
optimization. Newer versions of clang (in this case, trunk r231191) can see through the pointless division by zero and optimize it away. This caused the test to hang in release mode. A 50ms timeout is added to each test to transform the hang into a failure. The test was split into 12 tests to provide better feedback and control. To fix the bug, the division by zero is replaced by __builtin_trap(). TEST=crashpad_util_test ExceptionPorts.* R=rsesek@chromium.org Review URL: https://codereview.chromium.org/993613004
This commit is contained in:
parent
ab6cdb6bc1
commit
c4a8b32495
@ -107,6 +107,12 @@ void TestGetExceptionPorts(const ExceptionPorts& exception_ports,
|
||||
class TestExceptionPorts : public MachMultiprocess,
|
||||
public UniversalMachExcServer::Interface {
|
||||
public:
|
||||
// Which entities to set exception ports for.
|
||||
enum SetOn {
|
||||
kSetOnTaskOnly = 0,
|
||||
kSetOnTaskAndThreads,
|
||||
};
|
||||
|
||||
// Where to call ExceptionPorts::SetExceptionPort() from.
|
||||
enum SetType {
|
||||
// Call it from the child process on itself.
|
||||
@ -116,12 +122,6 @@ class TestExceptionPorts : public MachMultiprocess,
|
||||
kSetOutOfProcess,
|
||||
};
|
||||
|
||||
// Which entities to set exception ports for.
|
||||
enum SetOn {
|
||||
kSetOnTaskOnly = 0,
|
||||
kSetOnTaskAndThreads,
|
||||
};
|
||||
|
||||
// Which thread in the child process is expected to crash.
|
||||
enum WhoCrashes {
|
||||
kNobodyCrashes = 0,
|
||||
@ -129,16 +129,16 @@ class TestExceptionPorts : public MachMultiprocess,
|
||||
kOtherThreadCrashes,
|
||||
};
|
||||
|
||||
TestExceptionPorts(SetType set_type, SetOn set_on, WhoCrashes who_crashes)
|
||||
TestExceptionPorts(SetOn set_on, SetType set_type, WhoCrashes who_crashes)
|
||||
: MachMultiprocess(),
|
||||
UniversalMachExcServer::Interface(),
|
||||
set_type_(set_type),
|
||||
set_on_(set_on),
|
||||
set_type_(set_type),
|
||||
who_crashes_(who_crashes),
|
||||
handled_(false) {}
|
||||
|
||||
SetType set_type() const { return set_type_; }
|
||||
SetOn set_on() const { return set_on_; }
|
||||
SetType set_type() const { return set_type_; }
|
||||
WhoCrashes who_crashes() const { return who_crashes_; }
|
||||
|
||||
// UniversalMachExcServer::Interface:
|
||||
@ -191,10 +191,8 @@ class TestExceptionPorts : public MachMultiprocess,
|
||||
int signal;
|
||||
ExcCrashRecoverOriginalException(code[0], nullptr, &signal);
|
||||
|
||||
// The child crashed with a division by zero, which shows up as SIGFPE.
|
||||
// This was chosen because it’s unlikely to be generated by testing or
|
||||
// assertion failures.
|
||||
EXPECT_EQ(SIGFPE, signal);
|
||||
// The child crashed with __builtin_trap(), which shows up as SIGILL.
|
||||
EXPECT_EQ(SIGILL, signal);
|
||||
|
||||
SetExpectedChildTermination(kTerminationSignal, signal);
|
||||
}
|
||||
@ -326,10 +324,9 @@ class TestExceptionPorts : public MachMultiprocess,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Crashes by performing a division by zero. The assignment is present to
|
||||
// avoid optimizing zero_ out entirely by making it appear that its value
|
||||
// might change.
|
||||
static void Crash() { zero_ = 1 / zero_; }
|
||||
static void Crash() {
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
// The parent object.
|
||||
TestExceptionPorts* test_exception_ports_; // weak
|
||||
@ -345,11 +342,6 @@ class TestExceptionPorts : public MachMultiprocess,
|
||||
// child can test its exception ports and possibly crash, as appropriate.
|
||||
Semaphore crash_semaphore_;
|
||||
|
||||
// Always zero. Crash() divides by this in order to trigger a crash. This is
|
||||
// structured as a static volatile int to ward off aggressive compiler
|
||||
// optimizations.
|
||||
static volatile int zero_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Child);
|
||||
};
|
||||
|
||||
@ -447,13 +439,14 @@ class TestExceptionPorts : public MachMultiprocess,
|
||||
if (who_crashes_ != kNobodyCrashes) {
|
||||
UniversalMachExcServer universal_mach_exc_server(this);
|
||||
|
||||
const mach_msg_timeout_t kTimeoutMs = 50;
|
||||
kern_return_t kr =
|
||||
MachMessageServer::Run(&universal_mach_exc_server,
|
||||
local_port,
|
||||
MACH_MSG_OPTION_NONE,
|
||||
MachMessageServer::kOneShot,
|
||||
MachMessageServer::kReceiveLargeError,
|
||||
kMachMessageTimeoutWaitIndefinitely);
|
||||
kTimeoutMs);
|
||||
EXPECT_EQ(KERN_SUCCESS, kr)
|
||||
<< MachErrorMessage(kr, "MachMessageServer::Run");
|
||||
|
||||
@ -471,8 +464,8 @@ class TestExceptionPorts : public MachMultiprocess,
|
||||
child.Run();
|
||||
}
|
||||
|
||||
SetType set_type_;
|
||||
SetOn set_on_;
|
||||
SetType set_type_;
|
||||
WhoCrashes who_crashes_;
|
||||
|
||||
// true if an exception message was handled.
|
||||
@ -481,66 +474,103 @@ class TestExceptionPorts : public MachMultiprocess,
|
||||
DISALLOW_COPY_AND_ASSIGN(TestExceptionPorts);
|
||||
};
|
||||
|
||||
volatile int TestExceptionPorts::Child::zero_ = 0;
|
||||
|
||||
TEST(ExceptionPorts, TaskAndThreadExceptionPorts) {
|
||||
struct Testcase {
|
||||
TestExceptionPorts::SetType set_type;
|
||||
TestExceptionPorts::SetOn set_on;
|
||||
TestExceptionPorts::WhoCrashes who_crashes;
|
||||
};
|
||||
const Testcase kTestcases[] = {
|
||||
{TestExceptionPorts::kSetInProcess,
|
||||
TestExceptionPorts::kSetOnTaskOnly,
|
||||
TestExceptionPorts::kNobodyCrashes},
|
||||
{TestExceptionPorts::kSetInProcess,
|
||||
TestExceptionPorts::kSetOnTaskOnly,
|
||||
TestExceptionPorts::kMainThreadCrashes},
|
||||
{TestExceptionPorts::kSetInProcess,
|
||||
TestExceptionPorts::kSetOnTaskOnly,
|
||||
TestExceptionPorts::kOtherThreadCrashes},
|
||||
{TestExceptionPorts::kSetInProcess,
|
||||
TestExceptionPorts::kSetOnTaskAndThreads,
|
||||
TestExceptionPorts::kNobodyCrashes},
|
||||
{TestExceptionPorts::kSetInProcess,
|
||||
TestExceptionPorts::kSetOnTaskAndThreads,
|
||||
TestExceptionPorts::kMainThreadCrashes},
|
||||
{TestExceptionPorts::kSetInProcess,
|
||||
TestExceptionPorts::kSetOnTaskAndThreads,
|
||||
TestExceptionPorts::kOtherThreadCrashes},
|
||||
{TestExceptionPorts::kSetOutOfProcess,
|
||||
TestExceptionPorts::kSetOnTaskOnly,
|
||||
TestExceptionPorts::kNobodyCrashes},
|
||||
{TestExceptionPorts::kSetOutOfProcess,
|
||||
TestExceptionPorts::kSetOnTaskOnly,
|
||||
TestExceptionPorts::kMainThreadCrashes},
|
||||
{TestExceptionPorts::kSetOutOfProcess,
|
||||
TestExceptionPorts::kSetOnTaskOnly,
|
||||
TestExceptionPorts::kOtherThreadCrashes},
|
||||
{TestExceptionPorts::kSetOutOfProcess,
|
||||
TestExceptionPorts::kSetOnTaskAndThreads,
|
||||
TestExceptionPorts::kNobodyCrashes},
|
||||
{TestExceptionPorts::kSetOutOfProcess,
|
||||
TestExceptionPorts::kSetOnTaskAndThreads,
|
||||
TestExceptionPorts::kMainThreadCrashes},
|
||||
{TestExceptionPorts::kSetOutOfProcess,
|
||||
TestExceptionPorts::kSetOnTaskAndThreads,
|
||||
TestExceptionPorts::kOtherThreadCrashes},
|
||||
};
|
||||
|
||||
for (size_t index = 0; index < arraysize(kTestcases); ++index) {
|
||||
const Testcase& testcase = kTestcases[index];
|
||||
SCOPED_TRACE(
|
||||
base::StringPrintf("index %zu, set_type %d, set_on %d, who_crashes %d",
|
||||
index,
|
||||
testcase.set_type,
|
||||
testcase.set_on,
|
||||
testcase.who_crashes));
|
||||
|
||||
TEST(ExceptionPorts, TaskExceptionPorts_SetInProcess_NoCrash) {
|
||||
TestExceptionPorts test_exception_ports(
|
||||
testcase.set_type, testcase.set_on, testcase.who_crashes);
|
||||
TestExceptionPorts::kSetOnTaskOnly,
|
||||
TestExceptionPorts::kSetInProcess,
|
||||
TestExceptionPorts::kNobodyCrashes);
|
||||
test_exception_ports.Run();
|
||||
}
|
||||
|
||||
TEST(ExceptionPorts, TaskExceptionPorts_SetInProcess_MainThreadCrash) {
|
||||
TestExceptionPorts test_exception_ports(
|
||||
TestExceptionPorts::kSetOnTaskOnly,
|
||||
TestExceptionPorts::kSetInProcess,
|
||||
TestExceptionPorts::kMainThreadCrashes);
|
||||
test_exception_ports.Run();
|
||||
}
|
||||
|
||||
TEST(ExceptionPorts, TaskExceptionPorts_SetInProcess_OtherThreadCrash) {
|
||||
TestExceptionPorts test_exception_ports(
|
||||
TestExceptionPorts::kSetOnTaskOnly,
|
||||
TestExceptionPorts::kSetInProcess,
|
||||
TestExceptionPorts::kOtherThreadCrashes);
|
||||
test_exception_ports.Run();
|
||||
}
|
||||
|
||||
TEST(ExceptionPorts, TaskAndThreadExceptionPorts_SetInProcess_NoCrash) {
|
||||
TestExceptionPorts test_exception_ports(
|
||||
TestExceptionPorts::kSetOnTaskAndThreads,
|
||||
TestExceptionPorts::kSetInProcess,
|
||||
TestExceptionPorts::kNobodyCrashes);
|
||||
test_exception_ports.Run();
|
||||
}
|
||||
|
||||
TEST(ExceptionPorts, TaskAndThreadExceptionPorts_SetInProcess_MainThreadCrash) {
|
||||
TestExceptionPorts test_exception_ports(
|
||||
TestExceptionPorts::kSetOnTaskAndThreads,
|
||||
TestExceptionPorts::kSetInProcess,
|
||||
TestExceptionPorts::kMainThreadCrashes);
|
||||
test_exception_ports.Run();
|
||||
}
|
||||
|
||||
TEST(ExceptionPorts,
|
||||
TaskAndThreadExceptionPorts_SetInProcess_OtherThreadCrash) {
|
||||
TestExceptionPorts test_exception_ports(
|
||||
TestExceptionPorts::kSetOnTaskAndThreads,
|
||||
TestExceptionPorts::kSetInProcess,
|
||||
TestExceptionPorts::kOtherThreadCrashes);
|
||||
test_exception_ports.Run();
|
||||
}
|
||||
|
||||
TEST(ExceptionPorts, TaskExceptionPorts_SetOutOfProcess_NoCrash) {
|
||||
TestExceptionPorts test_exception_ports(
|
||||
TestExceptionPorts::kSetOnTaskOnly,
|
||||
TestExceptionPorts::kSetOutOfProcess,
|
||||
TestExceptionPorts::kNobodyCrashes);
|
||||
test_exception_ports.Run();
|
||||
}
|
||||
|
||||
TEST(ExceptionPorts, TaskExceptionPorts_SetOutOfProcess_MainThreadCrash) {
|
||||
TestExceptionPorts test_exception_ports(
|
||||
TestExceptionPorts::kSetOnTaskOnly,
|
||||
TestExceptionPorts::kSetOutOfProcess,
|
||||
TestExceptionPorts::kMainThreadCrashes);
|
||||
test_exception_ports.Run();
|
||||
}
|
||||
|
||||
TEST(ExceptionPorts, TaskExceptionPorts_SetOutOfProcess_OtherThreadCrash) {
|
||||
TestExceptionPorts test_exception_ports(
|
||||
TestExceptionPorts::kSetOnTaskOnly,
|
||||
TestExceptionPorts::kSetOutOfProcess,
|
||||
TestExceptionPorts::kOtherThreadCrashes);
|
||||
test_exception_ports.Run();
|
||||
}
|
||||
|
||||
TEST(ExceptionPorts, TaskAndThreadExceptionPorts_SetOutOfProcess_NoCrash) {
|
||||
TestExceptionPorts test_exception_ports(
|
||||
TestExceptionPorts::kSetOnTaskAndThreads,
|
||||
TestExceptionPorts::kSetOutOfProcess,
|
||||
TestExceptionPorts::kNobodyCrashes);
|
||||
test_exception_ports.Run();
|
||||
}
|
||||
|
||||
TEST(ExceptionPorts,
|
||||
TaskAndThreadExceptionPorts_SetOutOfProcess_MainThreadCrash) {
|
||||
TestExceptionPorts test_exception_ports(
|
||||
TestExceptionPorts::kSetOnTaskAndThreads,
|
||||
TestExceptionPorts::kSetOutOfProcess,
|
||||
TestExceptionPorts::kMainThreadCrashes);
|
||||
test_exception_ports.Run();
|
||||
}
|
||||
|
||||
TEST(ExceptionPorts,
|
||||
TaskAndThreadExceptionPorts_SetOutOfProcess_OtherThreadCrash) {
|
||||
TestExceptionPorts test_exception_ports(
|
||||
TestExceptionPorts::kSetOnTaskAndThreads,
|
||||
TestExceptionPorts::kSetOutOfProcess,
|
||||
TestExceptionPorts::kOtherThreadCrashes);
|
||||
test_exception_ports.Run();
|
||||
}
|
||||
|
||||
TEST(ExceptionPorts, HostExceptionPorts) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user