libgrading
A simple library for grading C and C++ assignments.
 All Classes Namespaces Files Functions Enumerations Enumerator
posix.cpp
Go to the documentation of this file.
1 
23 #include <libgrading.h>
24 
25 #include <cassert>
26 #include <functional>
27 
28 #include <sys/mman.h>
29 #include <err.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <signal.h>
33 #include <unistd.h>
34 
35 using namespace grading;
36 using std::unique_ptr;
37 
38 
40 static std::string currentTestName;
41 
42 
43 CheckResult::~CheckResult()
44 {
45  if (reportError_)
46  {
47  std::cerr << "Check failed";
48 
49  if (not currentTestName.empty())
50  std::cerr << " in test '" << currentTestName << "'";
51 
52  std::cerr << "\n";
53 
54  if (expected_.empty())
55  std::cerr << " " << actual_ << "\n";
56 
57  else
58  std::cerr
59  << " expected `" << expected_
60  << "`, got `" << actual_ << "`\n"
61  ;
62 
63  std::cerr
64  << " " << message_.str()
65  << "\n\n"
66  ;
67 
68  exit(static_cast<int>(TestResult::Fail));
69  }
70 }
71 
72 
73 static TestResult ProcessChildStatus(int status)
74 {
75  if (WIFEXITED(status))
76  return static_cast<TestResult>(WEXITSTATUS(status));
77 
78  if (WIFSIGNALED(status))
79  {
80  if (WTERMSIG(status) == SIGSEGV)
81  return TestResult::Segfault;
82 
83  else
85  }
86 
87  assert(false && "unhandled child exit mode");
88 }
89 
90 
91 
95 class PosixSharedMemory : public SharedMemory
96 {
97  public:
98  PosixSharedMemory(int fd, size_t len, void *rawPtr)
99  : shmfd(fd), length(len), ptr(rawPtr)
100  {
101  }
102 
103  ~PosixSharedMemory()
104  {
105  munmap(ptr, length);
106  close(shmfd);
107  }
108 
109  virtual void *rawPointer() const override { return ptr; }
110 
111  private:
112  int shmfd;
113  size_t length;
114  void *ptr;
115 };
116 
117 
118 unique_ptr<SharedMemory> grading::MapSharedData(size_t len)
119 {
120  int shmfd = shm_open("shm.tmp", O_RDWR);
121  void *map = mmap(0, len, PROT_READ | PROT_WRITE,
122  MAP_ANON | MAP_SHARED, shmfd, 0);
123 
124  return unique_ptr<SharedMemory>(new PosixSharedMemory(shmfd, len, map));
125 }
126 
127 
128 TestResult grading::RunTest(std::function<TestResult ()> test, std::string name,
129  time_t timeout, std::ostream& errorStream)
130 {
131  std::cout.flush();
132  std::cerr.flush();
133  std::clog.flush();
134 
135  pid_t child = fork();
136 
137  if (child == 0)
138  {
139  currentTestName = name;
140 
141  // Redirect cerr in the child process to the designated stream.
142  std::cerr.rdbuf(errorStream.rdbuf());
143 
144  TestResult result = test();
145  exit(static_cast<int>(result));
146  }
147  else
148  {
149  int status;
150  int options = (timeout ? WNOHANG : 0);
151 
152  time_t start = time(nullptr);
153 
154  while (true)
155  {
156  pid_t result = waitpid(child, &status, options);
157 
158  // Success: the child process has returned.
159  if (result == child)
160  break;
161 
162  // Error in waitpid()?
163  if (result < 0)
164  {
165  assert(errno == EINTR);
166  continue;
167  }
168 
169  // Child process isn't finished yet.
170  const time_t now = time(nullptr);
171  if ((now - start) > timeout)
172  {
173  kill(child, SIGKILL);
174  waitpid(child, &status, 0);
175  return TestResult::Timeout;
176  }
177 
178  usleep(100);
179  }
180 
181  return ProcessChildStatus(status);
182  }
183 }
the test took too long to run
A library for grading C- and C++-based assignments.
TestResult
The result of running one test within a separate process.
Definition: libgrading.h:113
std::unique_ptr< SharedMemory > MapSharedData(size_t size)
Map data into the address space that can be shared with other processes.
Definition: posix.cpp:118
the test caused a segmentation fault
A representation of a shared memory object.
Definition: libgrading.h:133
TestResult RunTest(std::function< TestResult()> test, std::string name="<unnamed test>", time_t timeout=0, std::ostream &errorStream=std::cerr)
Run a test closure in a separate process, capturing segmentation faults and other errors that lead to...
Definition: posix.cpp:128
the test terminated for another reason
Container for all libgrading names.
Definition: libgrading.h:34