Description:
[grader] added fractional timelimit git-svn-id: http://theory.cpe.ku.ac.th/grader/judge/trunk/scripts@191 6386c4cd-e34a-4fa8-8920-d93eb39b512e
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r51:3618759afb50 - - 6 files changed: 66 inserted, 57 deleted

@@ -0,0 +1,38
1 + #include <stdio.h>
2 + #include <stdlib.h>
3 + #include <unistd.h>
4 + #include <sys/time.h>
5 + #include <time.h>
6 + #include <sys/resource.h>
7 +
8 + // run it for 1.5 s
9 +
10 + int main()
11 + {
12 + int a,b;
13 +
14 + int c=0;
15 +
16 + scanf("%d %d",&a,&b);
17 + printf("%d\n",a+b);
18 +
19 + struct rusage ru;
20 +
21 + while(1) {
22 + c++;
23 + b+=c;
24 + while(c<100000) {
25 + c++;
26 + b+=c;
27 + }
28 + getrusage(RUSAGE_SELF,&ru);
29 + double rtime = ru.ru_utime.tv_sec + ru.ru_stime.tv_sec;
30 + rtime += (double)ru.ru_utime.tv_usec / 1000000.0;
31 + rtime += (double)ru.ru_stime.tv_usec / 1000000.0;
32 + if(rtime > 0.5)
33 + break;
34 + }
35 + printf("%d\n",b);
36 + exit(0);
37 + }
38 +
@@ -122,63 +122,68
122 Process.pid,
122 Process.pid,
123 grader_mode)
123 grader_mode)
124 else
124 else
125 grader_proc = nil
125 grader_proc = nil
126 end
126 end
127
127
128 #set loggin environment
128 #set loggin environment
129 ENV['GRADER_LOGGING'] = log_file_name
129 ENV['GRADER_LOGGING'] = log_file_name
130
130
131 #
131 #
132 # MAIN LOOP
132 # MAIN LOOP
133 #
133 #
134
134
135 case grader_mode
135 case grader_mode
136 when "queue", "test_request"
136 when "queue", "test_request"
137 log "Grader: #{grader_mode}"
137 log "Grader: #{grader_mode}"
138 if grader_mode=="queue"
138 if grader_mode=="queue"
139 engine = Grader::Engine.new
139 engine = Grader::Engine.new
140 else
140 else
141 engine = Grader::Engine.new(Grader::TestRequestRoomMaker.new,
141 engine = Grader::Engine.new(Grader::TestRequestRoomMaker.new,
142 Grader::TestRequestReporter.new)
142 Grader::TestRequestReporter.new)
143 end
143 end
144
144
145 runner = Grader::Runner.new(engine, grader_proc)
145 runner = Grader::Runner.new(engine, grader_proc)
146 while true
146 while true
147
147
148 if check_stopfile # created by calling grader stop
148 if check_stopfile # created by calling grader stop
149 clear_stopfile
149 clear_stopfile
150 log "stopped (with stop file)"
150 log "stopped (with stop file)"
151 break
151 break
152 end
152 end
153
153
154 if grader_mode=="queue"
154 if grader_mode=="queue"
155 task = runner.grade_oldest_task
155 task = runner.grade_oldest_task
156 else
156 else
157 task = runner.grade_oldest_test_request
157 task = runner.grade_oldest_test_request
158 end
158 end
159 if task==nil
159 if task==nil
160 sleep(1)
160 sleep(1)
161 end
161 end
162 end
162 end
163
163
164 when "prob"
164 when "prob"
165 engine = Grader::Engine.new
165 engine = Grader::Engine.new
166 runner = Grader::Runner.new(engine, grader_proc)
166 runner = Grader::Runner.new(engine, grader_proc)
167
167
168 grader_proc.report_active if grader_proc!=nil
168 grader_proc.report_active if grader_proc!=nil
169
169
170 - prob = Problem.find_by_name(ARGV[2])
170 + ARGV.shift
171 - if prob==nil
171 + ARGV.shift
172 - puts "cannot find problem: #{ARGV[2]}"
172 +
173 - else
173 + ARGV.each do |prob_name|
174 - runner.grade_problem(prob)
174 + prob = Problem.find_by_name(prob_name)
175 + if prob==nil
176 + puts "cannot find problem: #{prob_name}"
177 + else
178 + runner.grade_problem(prob)
179 + end
175 end
180 end
176
181
177 else
182 else
178 display_manual
183 display_manual
179 exit(0)
184 exit(0)
180 end
185 end
181
186
182 # report inactive
187 # report inactive
183 grader_proc.report_inactive if grader_proc!=nil
188 grader_proc.report_inactive if grader_proc!=nil
184
189
@@ -1,80 +1,80
1 /*
1 /*
2 * A Simple Testing Sandbox
2 * A Simple Testing Sandbox
3 *
3 *
4 * (c) 2001--2004 Martin Mares <mj@ucw.cz>
4 * (c) 2001--2004 Martin Mares <mj@ucw.cz>
5 */
5 */
6
6
7 #define _LARGEFILE64_SOURCE
7 #define _LARGEFILE64_SOURCE
8 //#define _GNU_SOURCE
8 //#define _GNU_SOURCE
9
9
10 #include <errno.h>
10 #include <errno.h>
11 #include <stdio.h>
11 #include <stdio.h>
12 #include <fcntl.h>
12 #include <fcntl.h>
13 #include <stdlib.h>
13 #include <stdlib.h>
14 #include <string.h>
14 #include <string.h>
15 #include <stdarg.h>
15 #include <stdarg.h>
16 #include <unistd.h>
16 #include <unistd.h>
17 #include <getopt.h>
17 #include <getopt.h>
18 #include <time.h>
18 #include <time.h>
19 #include <sys/wait.h>
19 #include <sys/wait.h>
20 #include <sys/user.h>
20 #include <sys/user.h>
21 #include <sys/time.h>
21 #include <sys/time.h>
22 #include <sys/ptrace.h>
22 #include <sys/ptrace.h>
23 #include <sys/signal.h>
23 #include <sys/signal.h>
24 #include <sys/sysinfo.h>
24 #include <sys/sysinfo.h>
25 #include <sys/syscall.h>
25 #include <sys/syscall.h>
26 #include <sys/resource.h>
26 #include <sys/resource.h>
27
27
28 #define NONRET __attribute__((noreturn))
28 #define NONRET __attribute__((noreturn))
29 #define UNUSED __attribute__((unused))
29 #define UNUSED __attribute__((unused))
30
30
31 static int filter_syscalls; /* 0=off, 1=liberal, 2=totalitarian */
31 static int filter_syscalls; /* 0=off, 1=liberal, 2=totalitarian */
32 - static int timeout;
32 + static double timeout;
33 static int pass_environ;
33 static int pass_environ;
34 static int use_wall_clock;
34 static int use_wall_clock;
35 static int file_access;
35 static int file_access;
36 static int verbose;
36 static int verbose;
37 static int memory_limit;
37 static int memory_limit;
38 static int allow_times;
38 static int allow_times;
39 static char *redir_stdin, *redir_stdout;
39 static char *redir_stdin, *redir_stdout;
40 static char *set_cwd;
40 static char *set_cwd;
41
41
42 static pid_t box_pid;
42 static pid_t box_pid;
43 static int is_ptraced;
43 static int is_ptraced;
44 static volatile int timer_tick;
44 static volatile int timer_tick;
45 static time_t start_time;
45 static time_t start_time;
46 static int ticks_per_sec;
46 static int ticks_per_sec;
47 static int page_size;
47 static int page_size;
48
48
49 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0
49 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0
50 /* glibc 2.1 or newer -> has lseek64 */
50 /* glibc 2.1 or newer -> has lseek64 */
51 #define long_seek(f,o,w) lseek64(f,o,w)
51 #define long_seek(f,o,w) lseek64(f,o,w)
52 #else
52 #else
53 /* Touching clandestine places in glibc */
53 /* Touching clandestine places in glibc */
54 extern loff_t llseek(int fd, loff_t pos, int whence);
54 extern loff_t llseek(int fd, loff_t pos, int whence);
55 #define long_seek(f,o,w) llseek(f,o,w)
55 #define long_seek(f,o,w) llseek(f,o,w)
56 #endif
56 #endif
57
57
58 int max_mem_used = 0;
58 int max_mem_used = 0;
59
59
60 void print_running_stat(double wall_time,
60 void print_running_stat(double wall_time,
61 double user_time,
61 double user_time,
62 double system_time,
62 double system_time,
63 int mem_usage)
63 int mem_usage)
64 {
64 {
65 fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n",
65 fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n",
66 wall_time, user_time, system_time, mem_usage);
66 wall_time, user_time, system_time, mem_usage);
67 }
67 }
68
68
69 static void NONRET
69 static void NONRET
70 box_exit(void)
70 box_exit(void)
71 {
71 {
72 if (box_pid > 0) {
72 if (box_pid > 0) {
73 if (is_ptraced)
73 if (is_ptraced)
74 ptrace(PTRACE_KILL, box_pid);
74 ptrace(PTRACE_KILL, box_pid);
75 kill(-box_pid, SIGKILL);
75 kill(-box_pid, SIGKILL);
76 kill(box_pid, SIGKILL);
76 kill(box_pid, SIGKILL);
77 }
77 }
78
78
79 struct timeval total;
79 struct timeval total;
80 int wall;
80 int wall;
@@ -282,138 +282,138
282 case __NR_getdents64:
282 case __NR_getdents64:
283 case __NR__newselect:
283 case __NR__newselect:
284 case __NR_fdatasync:
284 case __NR_fdatasync:
285 case __NR_mremap:
285 case __NR_mremap:
286 case __NR_poll:
286 case __NR_poll:
287 case __NR_getcwd:
287 case __NR_getcwd:
288 case __NR_nanosleep:
288 case __NR_nanosleep:
289 case __NR_rt_sigreturn:
289 case __NR_rt_sigreturn:
290 case __NR_rt_sigaction:
290 case __NR_rt_sigaction:
291 case __NR_rt_sigprocmask:
291 case __NR_rt_sigprocmask:
292 case __NR_rt_sigpending:
292 case __NR_rt_sigpending:
293 case __NR_rt_sigtimedwait:
293 case __NR_rt_sigtimedwait:
294 case __NR_rt_sigqueueinfo:
294 case __NR_rt_sigqueueinfo:
295 case __NR_rt_sigsuspend:
295 case __NR_rt_sigsuspend:
296 case __NR_mmap2:
296 case __NR_mmap2:
297 case __NR__sysctl:
297 case __NR__sysctl:
298 return (filter_syscalls == 1);
298 return (filter_syscalls == 1);
299 case __NR_times:
299 case __NR_times:
300 return allow_times;
300 return allow_times;
301 case __NR_kill:
301 case __NR_kill:
302 if (u->regs.ebx == box_pid)
302 if (u->regs.ebx == box_pid)
303 die("Commited suicide by signal %d.", (int)u->regs.ecx);
303 die("Commited suicide by signal %d.", (int)u->regs.ecx);
304 return 0;
304 return 0;
305 default:
305 default:
306 return 0;
306 return 0;
307 }
307 }
308 }
308 }
309
309
310 static void
310 static void
311 signal_alarm(int unused UNUSED)
311 signal_alarm(int unused UNUSED)
312 {
312 {
313 /* Time limit checks are synchronous, so we only schedule them there. */
313 /* Time limit checks are synchronous, so we only schedule them there. */
314 timer_tick = 1;
314 timer_tick = 1;
315
315
316 //NOTE: do not use alarm, changed to setitimer for precision
316 //NOTE: do not use alarm, changed to setitimer for precision
317 // alarm(1);
317 // alarm(1);
318 }
318 }
319
319
320 static void
320 static void
321 signal_int(int unused UNUSED)
321 signal_int(int unused UNUSED)
322 {
322 {
323 /* Interrupts are fatal, so no synchronization requirements. */
323 /* Interrupts are fatal, so no synchronization requirements. */
324 die("Interrupted.");
324 die("Interrupted.");
325 }
325 }
326
326
327 static void
327 static void
328 check_timeout(void)
328 check_timeout(void)
329 {
329 {
330 - int sec;
330 + double sec;
331
331
332 if (use_wall_clock)
332 if (use_wall_clock)
333 - sec = time(NULL) - start_time;
333 + sec = (double)(time(NULL) - start_time);
334 else
334 else
335 {
335 {
336 char buf[4096], *x;
336 char buf[4096], *x;
337 int c, utime, stime;
337 int c, utime, stime;
338 static int proc_status_fd;
338 static int proc_status_fd;
339 if (!proc_status_fd)
339 if (!proc_status_fd)
340 {
340 {
341 sprintf(buf, "/proc/%d/stat", (int) box_pid);
341 sprintf(buf, "/proc/%d/stat", (int) box_pid);
342 proc_status_fd = open(buf, O_RDONLY);
342 proc_status_fd = open(buf, O_RDONLY);
343 if (proc_status_fd < 0)
343 if (proc_status_fd < 0)
344 die("open(%s): %m", buf);
344 die("open(%s): %m", buf);
345 }
345 }
346 lseek(proc_status_fd, 0, SEEK_SET);
346 lseek(proc_status_fd, 0, SEEK_SET);
347 if ((c = read(proc_status_fd, buf, sizeof(buf)-1)) < 0)
347 if ((c = read(proc_status_fd, buf, sizeof(buf)-1)) < 0)
348 die("read on /proc/$pid/stat: %m");
348 die("read on /proc/$pid/stat: %m");
349 if (c >= (int) sizeof(buf) - 1)
349 if (c >= (int) sizeof(buf) - 1)
350 die("/proc/$pid/stat too long");
350 die("/proc/$pid/stat too long");
351 buf[c] = 0;
351 buf[c] = 0;
352 x = buf;
352 x = buf;
353 while (*x && *x != ' ')
353 while (*x && *x != ' ')
354 x++;
354 x++;
355 while (*x == ' ')
355 while (*x == ' ')
356 x++;
356 x++;
357 if (*x++ != '(')
357 if (*x++ != '(')
358 die("proc syntax error 1");
358 die("proc syntax error 1");
359 while (*x && (*x != ')' || x[1] != ' '))
359 while (*x && (*x != ')' || x[1] != ' '))
360 x++;
360 x++;
361 while (*x == ')' || *x == ' ')
361 while (*x == ')' || *x == ' ')
362 x++;
362 x++;
363 if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2)
363 if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2)
364 die("proc syntax error 2");
364 die("proc syntax error 2");
365 //printf("%s - %d\n",x,ticks_per_sec);
365 //printf("%s - %d\n",x,ticks_per_sec);
366 - sec = (utime + stime + ticks_per_sec-1)/ticks_per_sec;
366 + sec = ((double)(utime + stime))/(double)ticks_per_sec;
367 }
367 }
368 if (verbose > 1)
368 if (verbose > 1)
369 fprintf(stderr, "[timecheck: %d seconds]\n", sec);
369 fprintf(stderr, "[timecheck: %d seconds]\n", sec);
370 if (sec > timeout) {
370 if (sec > timeout) {
371 - die("Time limit exceeded.");
371 + die("Time limit exceeded.",sec,timeout);
372 }
372 }
373 }
373 }
374
374
375 static void
375 static void
376 check_memory_usage()
376 check_memory_usage()
377 {
377 {
378 char proc_fname[100];
378 char proc_fname[100];
379 sprintf(proc_fname,"/proc/%d/statm",box_pid);
379 sprintf(proc_fname,"/proc/%d/statm",box_pid);
380 //printf("proc fname: %s\n",proc_fname);
380 //printf("proc fname: %s\n",proc_fname);
381 FILE *fp = fopen(proc_fname,"r");
381 FILE *fp = fopen(proc_fname,"r");
382 if(fp!=NULL) {
382 if(fp!=NULL) {
383 char line[1000];
383 char line[1000];
384 fgets(line,999,fp);
384 fgets(line,999,fp);
385 //printf("%s\n",line);
385 //printf("%s\n",line);
386 int m;
386 int m;
387
387
388 if(sscanf(line,"%d",&m)==1) {
388 if(sscanf(line,"%d",&m)==1) {
389 m = (m*page_size+1023)/1024;
389 m = (m*page_size+1023)/1024;
390 if(m>max_mem_used)
390 if(m>max_mem_used)
391 max_mem_used = m;
391 max_mem_used = m;
392 }
392 }
393
393
394 fclose(fp);
394 fclose(fp);
395 }
395 }
396 }
396 }
397
397
398 static void
398 static void
399 boxkeeper(void)
399 boxkeeper(void)
400 {
400 {
401 int syscall_count = 0;
401 int syscall_count = 0;
402 struct sigaction sa;
402 struct sigaction sa;
403
403
404 is_ptraced = 1;
404 is_ptraced = 1;
405 bzero(&sa, sizeof(sa));
405 bzero(&sa, sizeof(sa));
406 sa.sa_handler = signal_int;
406 sa.sa_handler = signal_int;
407 sigaction(SIGINT, &sa, NULL);
407 sigaction(SIGINT, &sa, NULL);
408 start_time = time(NULL);
408 start_time = time(NULL);
409 ticks_per_sec = sysconf(_SC_CLK_TCK);
409 ticks_per_sec = sysconf(_SC_CLK_TCK);
410 page_size = getpagesize();
410 page_size = getpagesize();
411 if (ticks_per_sec <= 0)
411 if (ticks_per_sec <= 0)
412 die("Invalid ticks_per_sec!");
412 die("Invalid ticks_per_sec!");
413
413
414 check_memory_usage();
414 check_memory_usage();
415
415
416 sa.sa_handler = signal_alarm;
416 sa.sa_handler = signal_alarm;
417 sigaction(SIGALRM, &sa, NULL);
417 sigaction(SIGALRM, &sa, NULL);
418 //alarm(1);
418 //alarm(1);
419
419
@@ -421,97 +421,100
421 val.it_interval.tv_sec = 0;
421 val.it_interval.tv_sec = 0;
422 val.it_interval.tv_usec = 50000;
422 val.it_interval.tv_usec = 50000;
423 val.it_value.tv_sec = 0;
423 val.it_value.tv_sec = 0;
424 val.it_value.tv_usec = 50000;
424 val.it_value.tv_usec = 50000;
425 setitimer(ITIMER_REAL,&val,NULL);
425 setitimer(ITIMER_REAL,&val,NULL);
426
426
427 /*
427 /*
428 --- add alarm handler no matter what..
428 --- add alarm handler no matter what..
429 if (timeout)
429 if (timeout)
430 {
430 {
431 sa.sa_handler = signal_alarm;
431 sa.sa_handler = signal_alarm;
432 sigaction(SIGALRM, &sa, NULL);
432 sigaction(SIGALRM, &sa, NULL);
433 alarm(1);
433 alarm(1);
434 }
434 }
435 */
435 */
436
436
437 for(;;)
437 for(;;)
438 {
438 {
439 struct rusage rus;
439 struct rusage rus;
440 int stat;
440 int stat;
441 pid_t p;
441 pid_t p;
442
442
443 if (timer_tick)
443 if (timer_tick)
444 {
444 {
445 check_timeout();
445 check_timeout();
446 check_memory_usage();
446 check_memory_usage();
447 timer_tick = 0;
447 timer_tick = 0;
448 }
448 }
449 p = wait4(box_pid, &stat, WUNTRACED, &rus);
449 p = wait4(box_pid, &stat, WUNTRACED, &rus);
450
450
451 if (p < 0)
451 if (p < 0)
452 {
452 {
453 if (errno == EINTR)
453 if (errno == EINTR)
454 continue;
454 continue;
455 die("wait4: %m");
455 die("wait4: %m");
456 }
456 }
457 if (p != box_pid)
457 if (p != box_pid)
458 die("wait4: unknown pid %d exited!", p);
458 die("wait4: unknown pid %d exited!", p);
459 if (WIFEXITED(stat))
459 if (WIFEXITED(stat))
460 {
460 {
461 struct timeval total;
461 struct timeval total;
462 int wall;
462 int wall;
463 wall = time(NULL) - start_time;
463 wall = time(NULL) - start_time;
464 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
464 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
465
465
466 box_pid = 0;
466 box_pid = 0;
467 if (WEXITSTATUS(stat))
467 if (WEXITSTATUS(stat))
468 fprintf(stderr,"Exited with error status %d.\n", WEXITSTATUS(stat));
468 fprintf(stderr,"Exited with error status %d.\n", WEXITSTATUS(stat));
469 - else if ((use_wall_clock ? wall : total.tv_sec) > timeout)
469 + else if ((use_wall_clock ?
470 + wall :
471 + (double) total.tv_sec +
472 + ((double) total.tv_usec/1000000.0)) > timeout)
470 fprintf(stderr,"Time limit exceeded.\n");
473 fprintf(stderr,"Time limit exceeded.\n");
471 else
474 else
472 // report OK and statistics
475 // report OK and statistics
473 fprintf(stderr,"OK\n");
476 fprintf(stderr,"OK\n");
474
477
475 print_running_stat((double) wall,
478 print_running_stat((double) wall,
476 (double) rus.ru_utime.tv_sec +
479 (double) rus.ru_utime.tv_sec +
477 ((double) rus.ru_utime.tv_usec/1000000.0),
480 ((double) rus.ru_utime.tv_usec/1000000.0),
478 (double) rus.ru_stime.tv_sec +
481 (double) rus.ru_stime.tv_sec +
479 ((double) rus.ru_stime.tv_usec/1000000.0),
482 ((double) rus.ru_stime.tv_usec/1000000.0),
480 max_mem_used);
483 max_mem_used);
481 /*
484 /*
482 (%.4lf sec real (%d), %d sec wall, %d syscalls, %d kb)\n",
485 (%.4lf sec real (%d), %d sec wall, %d syscalls, %d kb)\n",
483 (double) total.tv_sec + ((double)total.tv_usec / 1000000.0),
486 (double) total.tv_sec + ((double)total.tv_usec / 1000000.0),
484 (int) total.tv_usec,
487 (int) total.tv_usec,
485 wall,
488 wall,
486 syscall_count,
489 syscall_count,
487 max_mem_used);
490 max_mem_used);
488 */
491 */
489 exit(0);
492 exit(0);
490 }
493 }
491 if (WIFSIGNALED(stat))
494 if (WIFSIGNALED(stat))
492 {
495 {
493 box_pid = 0;
496 box_pid = 0;
494 fprintf(stderr,"Caught fatal signal %d.\n", WTERMSIG(stat));
497 fprintf(stderr,"Caught fatal signal %d.\n", WTERMSIG(stat));
495
498
496 struct timeval total;
499 struct timeval total;
497 int wall;
500 int wall;
498 wall = time(NULL) - start_time;
501 wall = time(NULL) - start_time;
499 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
502 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
500 print_running_stat((double) wall,
503 print_running_stat((double) wall,
501 (double) rus.ru_utime.tv_sec +
504 (double) rus.ru_utime.tv_sec +
502 ((double) rus.ru_utime.tv_usec/1000000.0),
505 ((double) rus.ru_utime.tv_usec/1000000.0),
503 (double) rus.ru_stime.tv_sec +
506 (double) rus.ru_stime.tv_sec +
504 ((double) rus.ru_stime.tv_usec/1000000.0),
507 ((double) rus.ru_stime.tv_usec/1000000.0),
505 max_mem_used);
508 max_mem_used);
506 exit(0);
509 exit(0);
507 }
510 }
508 if (WIFSTOPPED(stat))
511 if (WIFSTOPPED(stat))
509 {
512 {
510 int sig = WSTOPSIG(stat);
513 int sig = WSTOPSIG(stat);
511 if (sig == SIGTRAP)
514 if (sig == SIGTRAP)
512 {
515 {
513 struct user u;
516 struct user u;
514 static int stop_count = -1;
517 static int stop_count = -1;
515 if (ptrace(PTRACE_GETREGS, box_pid, NULL, &u) < 0)
518 if (ptrace(PTRACE_GETREGS, box_pid, NULL, &u) < 0)
516 die("ptrace(PTRACE_GETREGS): %m");
519 die("ptrace(PTRACE_GETREGS): %m");
517 stop_count++;
520 stop_count++;
@@ -601,77 +604,77
601 \n\
604 \n\
602 Options:\n\
605 Options:\n\
603 -a <level>\tSet file access level (0=none, 1=cwd, 2=/etc,/lib,..., 3=whole fs, 9=no checks; needs -f)\n\
606 -a <level>\tSet file access level (0=none, 1=cwd, 2=/etc,/lib,..., 3=whole fs, 9=no checks; needs -f)\n\
604 -c <dir>\tChange directory to <dir> first\n\
607 -c <dir>\tChange directory to <dir> first\n\
605 -e\t\tPass full environment of parent process\n\
608 -e\t\tPass full environment of parent process\n\
606 -f\t\tFilter system calls (-ff=very restricted)\n\
609 -f\t\tFilter system calls (-ff=very restricted)\n\
607 -i <file>\tRedirect stdin from <file>\n\
610 -i <file>\tRedirect stdin from <file>\n\
608 -m <size>\tLimit address space to <size> KB\n\
611 -m <size>\tLimit address space to <size> KB\n\
609 -o <file>\tRedirect stdout to <file>\n\
612 -o <file>\tRedirect stdout to <file>\n\
610 -t <time>\tStop after <time> seconds\n\
613 -t <time>\tStop after <time> seconds\n\
611 -T\t\tAllow syscalls for measuring run time\n\
614 -T\t\tAllow syscalls for measuring run time\n\
612 -v\t\tBe verbose\n\
615 -v\t\tBe verbose\n\
613 -w\t\tMeasure wall clock time instead of run time\n\
616 -w\t\tMeasure wall clock time instead of run time\n\
614 ");
617 ");
615 exit(1);
618 exit(1);
616 }
619 }
617
620
618 int
621 int
619 main(int argc, char **argv)
622 main(int argc, char **argv)
620 {
623 {
621 int c;
624 int c;
622 uid_t uid;
625 uid_t uid;
623
626
624 while ((c = getopt(argc, argv, "a:c:efi:m:o:t:Tvw")) >= 0)
627 while ((c = getopt(argc, argv, "a:c:efi:m:o:t:Tvw")) >= 0)
625 switch (c)
628 switch (c)
626 {
629 {
627 case 'a':
630 case 'a':
628 file_access = atol(optarg);
631 file_access = atol(optarg);
629 break;
632 break;
630 case 'c':
633 case 'c':
631 set_cwd = optarg;
634 set_cwd = optarg;
632 break;
635 break;
633 case 'e':
636 case 'e':
634 pass_environ = 1;
637 pass_environ = 1;
635 break;
638 break;
636 case 'f':
639 case 'f':
637 filter_syscalls++;
640 filter_syscalls++;
638 break;
641 break;
639 case 'i':
642 case 'i':
640 redir_stdin = optarg;
643 redir_stdin = optarg;
641 break;
644 break;
642 case 'm':
645 case 'm':
643 memory_limit = atol(optarg);
646 memory_limit = atol(optarg);
644 break;
647 break;
645 case 'o':
648 case 'o':
646 redir_stdout = optarg;
649 redir_stdout = optarg;
647 break;
650 break;
648 case 't':
651 case 't':
649 - timeout = atol(optarg);
652 + timeout = atof(optarg);
650 break;
653 break;
651 case 'T':
654 case 'T':
652 allow_times++;
655 allow_times++;
653 break;
656 break;
654 case 'v':
657 case 'v':
655 verbose++;
658 verbose++;
656 break;
659 break;
657 case 'w':
660 case 'w':
658 use_wall_clock = 1;
661 use_wall_clock = 1;
659 break;
662 break;
660 default:
663 default:
661 usage();
664 usage();
662 }
665 }
663 if (optind >= argc)
666 if (optind >= argc)
664 usage();
667 usage();
665
668
666 uid = geteuid();
669 uid = geteuid();
667 if (setreuid(uid, uid) < 0)
670 if (setreuid(uid, uid) < 0)
668 die("setreuid: %m");
671 die("setreuid: %m");
669 box_pid = fork();
672 box_pid = fork();
670 if (box_pid < 0)
673 if (box_pid < 0)
671 die("fork: %m");
674 die("fork: %m");
672 if (!box_pid)
675 if (!box_pid)
673 box_inside(argc-optind, argv+optind);
676 box_inside(argc-optind, argv+optind);
674 else
677 else
675 boxkeeper();
678 boxkeeper();
676 die("Internal error: fell over edge of the world");
679 die("Internal error: fell over edge of the world");
677 }
680 }
@@ -1,20 +1,24
1 problem do
1 problem do
2 num_tests 2
2 num_tests 2
3 full_score 20
3 full_score 20
4 time_limit_each 1
4 time_limit_each 1
5 mem_limit_each 16
5 mem_limit_each 16
6 score_each 10
6 score_each 10
7
7
8 + test 1 do
9 + time_limit 0.5
10 + end
11 +
8 run 1 do
12 run 1 do
9 tests 1
13 tests 1
10 end
14 end
11
15
12 test 2 do
16 test 2 do
13 - time_limit 2
17 + time_limit 0.6
14 end
18 end
15
19
16 run 2 do
20 run 2 do
17 tests 2
21 tests 2
18 end
22 end
19
23
20 end
24 end
@@ -3,101 +3,101
3
3
4 describe "A grader engine, when grading submissions" do
4 describe "A grader engine, when grading submissions" do
5
5
6 include GraderEngineHelperMethods
6 include GraderEngineHelperMethods
7
7
8 before(:each) do
8 before(:each) do
9 @config = Grader::Configuration.get_instance
9 @config = Grader::Configuration.get_instance
10
10
11 # this test is from Pong
11 # this test is from Pong
12 @problem_test_normal = stub(Problem,
12 @problem_test_normal = stub(Problem,
13 :id => 1, :name => 'test_normal',
13 :id => 1, :name => 'test_normal',
14 :full_score => 135)
14 :full_score => 135)
15 @user_user1 = stub(User,
15 @user_user1 = stub(User,
16 :id => 1, :login => 'user1')
16 :id => 1, :login => 'user1')
17
17
18 @engine = Grader::Engine.new
18 @engine = Grader::Engine.new
19 init_sandbox
19 init_sandbox
20 end
20 end
21
21
22 it "should grade normal submission" do
22 it "should grade normal submission" do
23 grader_should(:grade => "test1_correct.c",
23 grader_should(:grade => "test1_correct.c",
24 :on => @problem_test_normal,
24 :on => @problem_test_normal,
25 :and_report => {
25 :and_report => {
26 :score => 135,
26 :score => 135,
27 :comment => /^PASSED/})
27 :comment => /^PASSED/})
28 end
28 end
29
29
30
30
31 it "should produce error message when submission cannot compile" do
31 it "should produce error message when submission cannot compile" do
32 grader_should(:grade => "test1_compile_error.c",
32 grader_should(:grade => "test1_compile_error.c",
33 :on => @problem_test_normal,
33 :on => @problem_test_normal,
34 :and_report => {
34 :and_report => {
35 :score => 0,
35 :score => 0,
36 :comment => 'FAILED: compilation error',
36 :comment => 'FAILED: compilation error',
37 :compiler_message => /[Ee]rror/})
37 :compiler_message => /[Ee]rror/})
38 end
38 end
39
39
40 it "should produce timeout error when submission runs forever" do
40 it "should produce timeout error when submission runs forever" do
41 @problem_test_timeout = stub(Problem,
41 @problem_test_timeout = stub(Problem,
42 :id => 1, :name => 'test_timeout',
42 :id => 1, :name => 'test_timeout',
43 :full_score => 10)
43 :full_score => 10)
44 grader_should(:grade => "test2_timeout.c",
44 grader_should(:grade => "test2_timeout.c",
45 :on => @problem_test_timeout,
45 :on => @problem_test_timeout,
46 :and_report => {
46 :and_report => {
47 :score => 0,
47 :score => 0,
48 :comment => 'FAILED: TT'})
48 :comment => 'FAILED: TT'})
49 end
49 end
50
50
51 - it "should produce timeout error correctly when submission runs slower than expected in less than a second" do
51 + it "should produce timeout error correctly with fractional running time and fractional time limits" do
52 - @problem_test_timeout = stub(Problem,
52 + @problem_test_timeout = stub(Problem,
53 :id => 1, :name => 'test_timeout',
53 :id => 1, :name => 'test_timeout',
54 :full_score => 20)
54 :full_score => 20)
55 - grader_should(:grade => "test2_1-5sec.c",
55 + grader_should(:grade => "test2_05sec.c",
56 :on => @problem_test_timeout,
56 :on => @problem_test_timeout,
57 :and_report => {
57 :and_report => {
58 :score => 10,
58 :score => 10,
59 :comment => 'FAILED: TP'})
59 :comment => 'FAILED: TP'})
60 end
60 end
61
61
62 it "should produce runtime error when submission uses too much static memory" do
62 it "should produce runtime error when submission uses too much static memory" do
63 @problem_test_memory = stub(Problem,
63 @problem_test_memory = stub(Problem,
64 :id => 1, :name => 'test_memory',
64 :id => 1, :name => 'test_memory',
65 :full_score => 20)
65 :full_score => 20)
66 grader_should(:grade => "add_too_much_memory_static.c",
66 grader_should(:grade => "add_too_much_memory_static.c",
67 :on => @problem_test_memory,
67 :on => @problem_test_memory,
68 :and_report => {
68 :and_report => {
69 :score => 10,
69 :score => 10,
70 :comment => /FAILED: [^P]P/})
70 :comment => /FAILED: [^P]P/})
71 end
71 end
72
72
73 it "should not allow submission to allocate too much dynamic memory" do
73 it "should not allow submission to allocate too much dynamic memory" do
74 @problem_test_memory = stub(Problem,
74 @problem_test_memory = stub(Problem,
75 :id => 1, :name => 'test_memory',
75 :id => 1, :name => 'test_memory',
76 :full_score => 20)
76 :full_score => 20)
77 grader_should(:grade => "add_too_much_memory_dynamic.c",
77 grader_should(:grade => "add_too_much_memory_dynamic.c",
78 :on => @problem_test_memory,
78 :on => @problem_test_memory,
79 :and_report => {
79 :and_report => {
80 :score => 10,
80 :score => 10,
81 :comment => /FAILED: [^P]P/})
81 :comment => /FAILED: [^P]P/})
82 end
82 end
83
83
84 it "should score test runs correctly when submission fails in some test case" do
84 it "should score test runs correctly when submission fails in some test case" do
85 grader_should(:grade => "add_fail_test_case_1.c",
85 grader_should(:grade => "add_fail_test_case_1.c",
86 :on => @problem_test_normal,
86 :on => @problem_test_normal,
87 :and_report => {
87 :and_report => {
88 :score => 105,
88 :score => 105,
89 :comment => /^FAILED:/})
89 :comment => /^FAILED:/})
90 end
90 end
91
91
92 it "should fail submission with non-zero exit status" do
92 it "should fail submission with non-zero exit status" do
93 grader_should(:grade => "add_nonzero_exit_status.c",
93 grader_should(:grade => "add_nonzero_exit_status.c",
94 :on => @problem_test_normal,
94 :on => @problem_test_normal,
95 :and_report => {
95 :and_report => {
96 :score => 0,
96 :score => 0,
97 :comment => /^FAILED:/})
97 :comment => /^FAILED:/})
98 end
98 end
99
99
100 it "should not allow malicious submission to see PROBLEM_HOME" do
100 it "should not allow malicious submission to see PROBLEM_HOME" do
101 problem_test_yesno = stub(Problem,
101 problem_test_yesno = stub(Problem,
102 :id => 1, :name => 'test_yesno',
102 :id => 1, :name => 'test_yesno',
103 :full_score => 10)
103 :full_score => 10)
deleted file
You need to be logged in to leave comments. Login now