I’ve been trying to implement an online judge, but I’m stuck..
I use fork to create a child process and then from the child process I use the system command to start the program I’m supposed to test.
This is the program I use to test the grader. It’s in an infinite loop, but I just comment that line to make it work.
#include <stdio.h>
int main () {
int a, b;
scanf ("%d %d", &a, &b);
while (1);
printf ("%d\n", a + b);
return 0;
}
And the grader I coded so far is
struct timeval start, end;
struct rlimit rl, default_memory, default_time;
getrlimit (RLIMIT_AS, &default_memory);
getrlimit (RLIMIT_CPU, &default_time);
pid_t pid = fork ();
gettimeofday (&start, NULL);
if (pid < 0) printf ("Error\n");
else if (pid == 0) { // child process
if (memory_limit > 0) {
rl.rlim_cur = rl.rlim_max = memory_limit;
setrlimit (RLIMIT_AS, &rl);
}
if (time_limit > 0) {
rl.rlim_cur = rl.rlim_max = time_limit / 1000 + 1;
setrlimit (RLIMIT_CPU, &rl);
}
ptrace (PTRACE_TRACEME, pid, NULL, NULL);
system ("./sum < in.txt > out.txt");
setrlimit (RLIMIT_CPU, &default_time);
setrlimit (RLIMIT_AS, &default_memory);
}
else { // parent process
int *status;
// Is this ok?
// Because here http://linux.die.net/man/2/ptrace it says that
// PTRACE_GETSIGINFO is used to get information about the signal that
// caused the stop. And I'm interested If the program was killed because of
// rlimit (exceeding time or memory limit) or if it was something else.
ptrace (PTRACE_GETSIGINFO, pid, NULL, NULL);
if (wait (status) == -1) return 1; // waiting for the child process to finish
if (WIFSIGNALED (status)) {
int exit_signal = WTERMSIG (status);
printf ("Terminated with signal: %d.\n", exit_signal);
switch (exit_signal) {
case 0:
printf ("OK\n");
break;
case SIGSEGV: // RLIMIT_AS -- SIGSEGV is sent, when the memory limit is exceeded
printf ("Memory Limit Exceeded\n");
break;
case SIGXCPU: // RLIMIT_CPU -- when soft limit is reached SIGXCPU is sent
case SIGKILL: // when hard limit is reached SIGKILL is sent
printf ("Time Limit Exceeded\n");
break;
default: // I didn't handle all the signals, so I made everything else a run time error
printf ("Run Time Error\n");
break;
}
}
ptrace (PTRACE_KILL, pid, NULL, NULL); // Kill the process If it is not killed
gettimeofday (&end, NULL); // This is working fine
double time = (end.tv_sec - start.tv_sec) + 1e-6 * (end.tv_usec - start.tv_usec);
printf ("%lf\n", time);
}
You don’t need to use ptrace, you can send signals to the child with
kill(2)and get the exit status viawait(2), if it exited because of a signal use WTERMSIG.The problem is probably that you’re using system(), so the grader actually runs in a third process, not in the child.
N.B personally I wouldn’t use system() with file redirections like that, I’d set up pipes then use exec() in the child, so the grader actually runs in the child. Or I’d just use my pstreams library.