1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
#include "caffe2/utils/signal_handler.h"
#if defined(C10_SUPPORTS_FATAL_SIGNAL_HANDLERS)
#include <gtest/gtest.h>
#include <pthread.h>
#include <unistd.h>
#include <functional>
#include <iostream>
#include <array>
#include "caffe2/core/common.h"
namespace {
void* dummy_thread(void*) {
while (1) {
}
return nullptr;
}
bool forkAndPipe(
std::string& stderrBuffer,
std::function<void(void)> callback) {
std::array<int, 2> stderrPipe;
if (pipe(stderrPipe.data()) != 0) {
perror("STDERR pipe");
return false;
}
pid_t child = fork();
if (child == 0) {
// Replace this process' stderr so we can read it.
if (dup2(stderrPipe[1], STDERR_FILENO) < 0) {
close(stderrPipe[0]);
close(stderrPipe[1]);
perror("dup2 STDERR");
exit(5);
}
// This is for the parent to work with.
close(stderrPipe[0]);
close(stderrPipe[1]);
callback();
exit(7);
} else if (child > 0) {
const int bufferSize = 128;
std::array<char, bufferSize> buffer;
// We want to close the writing end of the pipe right away so our
// read actually gets an EOF.
close(stderrPipe[1]);
// wait for child to finish crashing.
int statloc;
if (wait(&statloc) < 0) {
close(stderrPipe[0]);
perror("wait");
return false;
}
ssize_t bytesRead;
while ((bytesRead = read(stderrPipe[0], buffer.data(), bufferSize)) > 0) {
const std::string tmp(buffer.data(), bytesRead);
std::cout << tmp;
stderrBuffer += tmp;
}
// The child should have exited due to signal.
if (!WIFSIGNALED(statloc)) {
fprintf(stderr, "Child didn't exit because it received a signal\n");
if (WIFEXITED(statloc)) {
fprintf(stderr, "Exited with code: %d\n", WEXITSTATUS(statloc) & 0xff);
}
return false;
}
if (bytesRead < 0) {
perror("read");
return false;
}
close(stderrPipe[0]);
return true;
} else {
perror("fork");
return false;
}
}
} // namespace
#define _TEST_FATAL_SIGNAL(signum, name, threadCount, print, expected) \
do { \
std::string stderrBuffer; \
ASSERT_TRUE(forkAndPipe(stderrBuffer, [=]() { \
caffe2::setPrintStackTracesOnFatalSignal(print); \
pthread_t pt; \
for (int i = 0; i < threadCount; i++) { \
if (pthread_create(&pt, nullptr, ::dummy_thread, nullptr)) { \
perror("pthread_create"); \
} \
} \
raise(signum); \
})); \
int keyPhraseCount = 0; \
std::string keyPhrase = \
std::string(name) + "(" + c10::to_string(signum) + ")"; \
size_t loc = 0; \
while ((loc = stderrBuffer.find(keyPhrase, loc)) != std::string::npos) { \
keyPhraseCount += 1; \
loc += 1; \
} \
EXPECT_GE(keyPhraseCount, expected); \
} while (0)
#define TEST_FATAL_SIGNAL(signum, name, threadCount) \
_TEST_FATAL_SIGNAL(signum, name, threadCount, true, threadCount + 1)
#define TEST_FATAL_SIGNAL_NO_PRINT(signum, name, threadCount) \
_TEST_FATAL_SIGNAL(signum, name, threadCount, false, 0)
TEST(fatalSignalTest, SIGABRT8) {
TEST_FATAL_SIGNAL(SIGABRT, "SIGABRT", 8);
}
TEST(fatalSignalTest, SIGINT8) {
TEST_FATAL_SIGNAL(SIGINT, "SIGINT", 8);
}
TEST(fatalSignalTest, SIGILL8) {
TEST_FATAL_SIGNAL(SIGILL, "SIGILL", 8);
}
TEST(fatalSignalTest, SIGFPE8) {
TEST_FATAL_SIGNAL(SIGFPE, "SIGFPE", 8);
}
TEST(fatalSignalTest, SIGBUS8) {
TEST_FATAL_SIGNAL(SIGBUS, "SIGBUS", 8);
}
TEST(fatalSignalTest, SIGSEGV8) {
TEST_FATAL_SIGNAL(SIGSEGV, "SIGSEGV", 8);
}
// Test that if we don't enable printing stack traces then we don't get any.
TEST(fatalSignalTest, SIGABRT8_NOPRINT) {
TEST_FATAL_SIGNAL_NO_PRINT(SIGABRT, "SIGABRT", 8);
}
#endif // defined(C10_SUPPORTS_FATAL_SIGNAL_HANDLERS)
|