Skip to content

Instantly share code, notes, and snippets.

@TheBurnDoc
Created October 20, 2013 12:39
Show Gist options
  • Save TheBurnDoc/7069048 to your computer and use it in GitHub Desktop.
Save TheBurnDoc/7069048 to your computer and use it in GitHub Desktop.
A method of using Posix fork to 'catch' and report seg faults in Google Test. To take advantage of it, use the ForkedTest class as your base test case class and remember to call its SetUp and TearDown in the derived classes' SetUp and TearDown.
#include <iostream>
#include <gtest/gtest.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <resource.h>
#include "ForkedTest.h"
using namespace ::std;
using namespace ::testing;
ForkedTest::ForkedTest()
: m_pid(0)
, m_read_fd(m_pipe_fd[0])
, m_write_fd(m_pipe_fd[1])
{
if (pipe2(m_pipe_fd, O_NONBLOCK) == -1)
{
cout << "ForkedTest::ForkedTest(): pipe2() failed with error: " << strerror(errno) << std::endl;
exit(EXIT_FAILURE);
}
}
ForkedTest::~ForkedTest()
{
close(m_read_fd);
close(m_write_fd);
}
void ForkedTest::SetUp()
{
m_pid = fork();
if (m_pid == -1)
{
cout << "ForkedTest::SetUp(): fork() failed with error: " << strerror(errno) << endl;
_exit(EXIT_FAILURE);
}
// Child only code
else if (m_pid == 0)
{
// Disable core dumps on new process
struct rlimit rl;
rl.rlim_cur = rl.rlim_max = 0;
if (setrlimit(RLIMIT_CORE, &rl) == -1)
cout << "ForkedTest::SetUp(): setrlimit() failed with error: " << strerror(errno) << endl;
}
// Parent only code
else if (m_pid > 0)
{
pid_t wpid;
int exit_status;
bool do_suicide = false;
ssize_t read_size;
do
{
wpid = waitpid(m_pid, &exit_status, WNOHANG);
if (wpid == -1)
{
cout << "ForkedTest::SetUp(): waitpid() failed with error: " << strerror(errno) << endl;
_exit(EXIT_FAILURE);
}
else if (wpid && WIFSIGNALED(exit_status))
{
bool seg_fault_detected = static_cast<bool>(WTERMSIG(exit_status) == SIGSEGV);
ASSERT_FALSE(seg_fault_detected);
}
else
{
// Child's TearDown() has told us to die
read_size = read(m_read_fd, static_cast<void*>(&do_suicide), sizeof(bool));
if (read_size != -1 && read_size == sizeof(bool))
if (do_suicide) _exit(EXIT_SUCCESS);
}
}
while (wpid == 0);
}
}
void ForkedTest::TearDown()
{
// Child only code
if (m_pid == 0)
{
bool parent_die = true;
ssize_t write_size = write(m_write_fd, static_cast<void*>(&parent_die), sizeof(bool));
if (write_size == -1)
{
cout << "ForkedTest::TearDown(): write() failed with error: " << strerror(errno) << endl;
_exit(EXIT_FAILURE);
}
}
}
#ifndef CORESAFETEST_H_
#define CORESAFETEST_H_
#include <gtest/gtest.h>
#include <fcntl.h>
class ForkedTest : public ::testing::Test
{
protected:
ForkedTest();
virtual ~ForkedTest();
virtual void SetUp();
virtual void TearDown();
private:
pid_t m_pid;
int m_pipe_fd[2];
const int& m_read_fd;
const int& m_write_fd;
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment