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 +
@@ -74,111 +74,116
74 74 elsif (ARGV.length==2) and (ARGV[1]=='all')
75 75 stop_grader(:all)
76 76 puts "A global stop file ('stop.all') created."
77 77 puts "You should remove it manually later."
78 78 else
79 79 (1..ARGV.length-1).each do |i|
80 80 stop_grader(ARGV[i])
81 81 end
82 82 puts "stop file(s) created"
83 83 end
84 84 exit(0)
85 85 end
86 86
87 87 if check_stopfile
88 88 puts "Stop file exists. Terminated."
89 89 clear_stopfile
90 90 exit(0)
91 91 end
92 92
93 93 grader_mode = 'queue'
94 94 if ARGV.length >= 1
95 95 GRADER_ENV = ARGV[0]
96 96 if ARGV.length >=2
97 97 grader_mode = ARGV[1]
98 98 end
99 99 else
100 100 GRADER_ENV = 'exam'
101 101 end
102 102
103 103 puts "environment: #{GRADER_ENV}"
104 104 require File.join(File.dirname(__FILE__),'config/environment')
105 105
106 106 # add grader_mode to config
107 107 # this is needed because method log needs it. TODO: clean this up
108 108 class << config
109 109 attr_accessor :grader_mode
110 110 end
111 111 config.grader_mode = grader_mode
112 112
113 113 # reading rails environment
114 114 log 'Reading rails environment'
115 115
116 116 RAILS_ENV = config.rails_env
117 117 require RAILS_ROOT + '/config/environment'
118 118
119 119 # register grader process
120 120 if config.report_grader
121 121 grader_proc = GraderProcess.register(config.grader_hostname,
122 122 Process.pid,
123 123 grader_mode)
124 124 else
125 125 grader_proc = nil
126 126 end
127 127
128 128 #set loggin environment
129 129 ENV['GRADER_LOGGING'] = log_file_name
130 130
131 131 #
132 132 # MAIN LOOP
133 133 #
134 134
135 135 case grader_mode
136 136 when "queue", "test_request"
137 137 log "Grader: #{grader_mode}"
138 138 if grader_mode=="queue"
139 139 engine = Grader::Engine.new
140 140 else
141 141 engine = Grader::Engine.new(Grader::TestRequestRoomMaker.new,
142 142 Grader::TestRequestReporter.new)
143 143 end
144 144
145 145 runner = Grader::Runner.new(engine, grader_proc)
146 146 while true
147 147
148 148 if check_stopfile # created by calling grader stop
149 149 clear_stopfile
150 150 log "stopped (with stop file)"
151 151 break
152 152 end
153 153
154 154 if grader_mode=="queue"
155 155 task = runner.grade_oldest_task
156 156 else
157 157 task = runner.grade_oldest_test_request
158 158 end
159 159 if task==nil
160 160 sleep(1)
161 161 end
162 162 end
163 163
164 164 when "prob"
165 165 engine = Grader::Engine.new
166 166 runner = Grader::Runner.new(engine, grader_proc)
167 167
168 168 grader_proc.report_active if grader_proc!=nil
169 169
170 - prob = Problem.find_by_name(ARGV[2])
171 - if prob==nil
172 - puts "cannot find problem: #{ARGV[2]}"
173 - else
174 - runner.grade_problem(prob)
170 + ARGV.shift
171 + ARGV.shift
172 +
173 + ARGV.each do |prob_name|
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 180 end
176 181
177 182 else
178 183 display_manual
179 184 exit(0)
180 185 end
181 186
182 187 # report inactive
183 188 grader_proc.report_inactive if grader_proc!=nil
184 189
@@ -1,128 +1,128
1 1 /*
2 2 * A Simple Testing Sandbox
3 3 *
4 4 * (c) 2001--2004 Martin Mares <mj@ucw.cz>
5 5 */
6 6
7 7 #define _LARGEFILE64_SOURCE
8 8 //#define _GNU_SOURCE
9 9
10 10 #include <errno.h>
11 11 #include <stdio.h>
12 12 #include <fcntl.h>
13 13 #include <stdlib.h>
14 14 #include <string.h>
15 15 #include <stdarg.h>
16 16 #include <unistd.h>
17 17 #include <getopt.h>
18 18 #include <time.h>
19 19 #include <sys/wait.h>
20 20 #include <sys/user.h>
21 21 #include <sys/time.h>
22 22 #include <sys/ptrace.h>
23 23 #include <sys/signal.h>
24 24 #include <sys/sysinfo.h>
25 25 #include <sys/syscall.h>
26 26 #include <sys/resource.h>
27 27
28 28 #define NONRET __attribute__((noreturn))
29 29 #define UNUSED __attribute__((unused))
30 30
31 31 static int filter_syscalls; /* 0=off, 1=liberal, 2=totalitarian */
32 - static int timeout;
32 + static double timeout;
33 33 static int pass_environ;
34 34 static int use_wall_clock;
35 35 static int file_access;
36 36 static int verbose;
37 37 static int memory_limit;
38 38 static int allow_times;
39 39 static char *redir_stdin, *redir_stdout;
40 40 static char *set_cwd;
41 41
42 42 static pid_t box_pid;
43 43 static int is_ptraced;
44 44 static volatile int timer_tick;
45 45 static time_t start_time;
46 46 static int ticks_per_sec;
47 47 static int page_size;
48 48
49 49 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0
50 50 /* glibc 2.1 or newer -> has lseek64 */
51 51 #define long_seek(f,o,w) lseek64(f,o,w)
52 52 #else
53 53 /* Touching clandestine places in glibc */
54 54 extern loff_t llseek(int fd, loff_t pos, int whence);
55 55 #define long_seek(f,o,w) llseek(f,o,w)
56 56 #endif
57 57
58 58 int max_mem_used = 0;
59 59
60 60 void print_running_stat(double wall_time,
61 61 double user_time,
62 62 double system_time,
63 63 int mem_usage)
64 64 {
65 65 fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n",
66 66 wall_time, user_time, system_time, mem_usage);
67 67 }
68 68
69 69 static void NONRET
70 70 box_exit(void)
71 71 {
72 72 if (box_pid > 0) {
73 73 if (is_ptraced)
74 74 ptrace(PTRACE_KILL, box_pid);
75 75 kill(-box_pid, SIGKILL);
76 76 kill(box_pid, SIGKILL);
77 77 }
78 78
79 79 struct timeval total;
80 80 int wall;
81 81 struct rusage rus;
82 82 int stat;
83 83 pid_t p;
84 84
85 85 // wait so that we can get information
86 86 p = wait4(box_pid, &stat, WUNTRACED, &rus);
87 87 if (p < 0) {
88 88 fprintf(stderr,"wait4: error\n");
89 89 print_running_stat(0,0,0,max_mem_used);
90 90 } else if (p != box_pid) {
91 91 fprintf(stderr,"wait4: unknown pid %d exited!\n", p);
92 92 print_running_stat(0,0,0,max_mem_used);
93 93 } else {
94 94 if (!WIFEXITED(stat))
95 95 fprintf(stderr,"wait4: unknown status\n");
96 96 struct timeval total;
97 97 int wall;
98 98 wall = time(NULL) - start_time;
99 99 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
100 100
101 101 print_running_stat((double)wall,
102 102 (double) rus.ru_utime.tv_sec +
103 103 ((double) rus.ru_utime.tv_usec/1000000.0),
104 104 (double) rus.ru_stime.tv_sec +
105 105 ((double) rus.ru_stime.tv_usec/1000000.0),
106 106 max_mem_used);
107 107 }
108 108 exit(1);
109 109 }
110 110
111 111 static void NONRET __attribute__((format(printf,1,2)))
112 112 die(char *msg, ...)
113 113 {
114 114 va_list args;
115 115 va_start(args, msg);
116 116 vfprintf(stderr, msg, args);
117 117 fputc('\n', stderr);
118 118 box_exit();
119 119 }
120 120
121 121 static void __attribute__((format(printf,1,2)))
122 122 log(char *msg, ...)
123 123 {
124 124 va_list args;
125 125 va_start(args, msg);
126 126 if (verbose)
127 127 {
128 128 vfprintf(stderr, msg, args);
@@ -234,444 +234,447
234 234 case __NR_getegid:
235 235 case __NR_dup2:
236 236 case __NR_ftruncate:
237 237 case __NR_fstat:
238 238 case __NR_personality:
239 239 case __NR__llseek:
240 240 case __NR_readv:
241 241 case __NR_writev:
242 242 case __NR_getresuid:
243 243 #ifdef __NR_pread64
244 244 case __NR_pread64:
245 245 case __NR_pwrite64:
246 246 #else
247 247 case __NR_pread:
248 248 case __NR_pwrite:
249 249 #endif
250 250 case __NR_ftruncate64:
251 251 case __NR_fstat64:
252 252 case __NR_fcntl:
253 253 case __NR_fcntl64:
254 254 case __NR_mmap:
255 255 case __NR_munmap:
256 256 case __NR_ioctl:
257 257 case __NR_uname:
258 258 case 252:
259 259 case 243:
260 260 return 1;
261 261 // case __NR_time:
262 262 case __NR_alarm:
263 263 // case __NR_pause:
264 264 case __NR_signal:
265 265 case __NR_fchmod:
266 266 case __NR_sigaction:
267 267 case __NR_sgetmask:
268 268 case __NR_ssetmask:
269 269 case __NR_sigsuspend:
270 270 case __NR_sigpending:
271 271 case __NR_getrlimit:
272 272 case __NR_getrusage:
273 273 case __NR_gettimeofday:
274 274 case __NR_select:
275 275 case __NR_readdir:
276 276 case __NR_setitimer:
277 277 case __NR_getitimer:
278 278 case __NR_sigreturn:
279 279 case __NR_mprotect:
280 280 case __NR_sigprocmask:
281 281 case __NR_getdents:
282 282 case __NR_getdents64:
283 283 case __NR__newselect:
284 284 case __NR_fdatasync:
285 285 case __NR_mremap:
286 286 case __NR_poll:
287 287 case __NR_getcwd:
288 288 case __NR_nanosleep:
289 289 case __NR_rt_sigreturn:
290 290 case __NR_rt_sigaction:
291 291 case __NR_rt_sigprocmask:
292 292 case __NR_rt_sigpending:
293 293 case __NR_rt_sigtimedwait:
294 294 case __NR_rt_sigqueueinfo:
295 295 case __NR_rt_sigsuspend:
296 296 case __NR_mmap2:
297 297 case __NR__sysctl:
298 298 return (filter_syscalls == 1);
299 299 case __NR_times:
300 300 return allow_times;
301 301 case __NR_kill:
302 302 if (u->regs.ebx == box_pid)
303 303 die("Commited suicide by signal %d.", (int)u->regs.ecx);
304 304 return 0;
305 305 default:
306 306 return 0;
307 307 }
308 308 }
309 309
310 310 static void
311 311 signal_alarm(int unused UNUSED)
312 312 {
313 313 /* Time limit checks are synchronous, so we only schedule them there. */
314 314 timer_tick = 1;
315 315
316 316 //NOTE: do not use alarm, changed to setitimer for precision
317 317 // alarm(1);
318 318 }
319 319
320 320 static void
321 321 signal_int(int unused UNUSED)
322 322 {
323 323 /* Interrupts are fatal, so no synchronization requirements. */
324 324 die("Interrupted.");
325 325 }
326 326
327 327 static void
328 328 check_timeout(void)
329 329 {
330 - int sec;
330 + double sec;
331 331
332 332 if (use_wall_clock)
333 - sec = time(NULL) - start_time;
333 + sec = (double)(time(NULL) - start_time);
334 334 else
335 335 {
336 336 char buf[4096], *x;
337 337 int c, utime, stime;
338 338 static int proc_status_fd;
339 339 if (!proc_status_fd)
340 340 {
341 341 sprintf(buf, "/proc/%d/stat", (int) box_pid);
342 342 proc_status_fd = open(buf, O_RDONLY);
343 343 if (proc_status_fd < 0)
344 344 die("open(%s): %m", buf);
345 345 }
346 346 lseek(proc_status_fd, 0, SEEK_SET);
347 347 if ((c = read(proc_status_fd, buf, sizeof(buf)-1)) < 0)
348 348 die("read on /proc/$pid/stat: %m");
349 349 if (c >= (int) sizeof(buf) - 1)
350 350 die("/proc/$pid/stat too long");
351 351 buf[c] = 0;
352 352 x = buf;
353 353 while (*x && *x != ' ')
354 354 x++;
355 355 while (*x == ' ')
356 356 x++;
357 357 if (*x++ != '(')
358 358 die("proc syntax error 1");
359 359 while (*x && (*x != ')' || x[1] != ' '))
360 360 x++;
361 361 while (*x == ')' || *x == ' ')
362 362 x++;
363 363 if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2)
364 364 die("proc syntax error 2");
365 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 368 if (verbose > 1)
369 369 fprintf(stderr, "[timecheck: %d seconds]\n", sec);
370 370 if (sec > timeout) {
371 - die("Time limit exceeded.");
371 + die("Time limit exceeded.",sec,timeout);
372 372 }
373 373 }
374 374
375 375 static void
376 376 check_memory_usage()
377 377 {
378 378 char proc_fname[100];
379 379 sprintf(proc_fname,"/proc/%d/statm",box_pid);
380 380 //printf("proc fname: %s\n",proc_fname);
381 381 FILE *fp = fopen(proc_fname,"r");
382 382 if(fp!=NULL) {
383 383 char line[1000];
384 384 fgets(line,999,fp);
385 385 //printf("%s\n",line);
386 386 int m;
387 387
388 388 if(sscanf(line,"%d",&m)==1) {
389 389 m = (m*page_size+1023)/1024;
390 390 if(m>max_mem_used)
391 391 max_mem_used = m;
392 392 }
393 393
394 394 fclose(fp);
395 395 }
396 396 }
397 397
398 398 static void
399 399 boxkeeper(void)
400 400 {
401 401 int syscall_count = 0;
402 402 struct sigaction sa;
403 403
404 404 is_ptraced = 1;
405 405 bzero(&sa, sizeof(sa));
406 406 sa.sa_handler = signal_int;
407 407 sigaction(SIGINT, &sa, NULL);
408 408 start_time = time(NULL);
409 409 ticks_per_sec = sysconf(_SC_CLK_TCK);
410 410 page_size = getpagesize();
411 411 if (ticks_per_sec <= 0)
412 412 die("Invalid ticks_per_sec!");
413 413
414 414 check_memory_usage();
415 415
416 416 sa.sa_handler = signal_alarm;
417 417 sigaction(SIGALRM, &sa, NULL);
418 418 //alarm(1);
419 419
420 420 struct itimerval val;
421 421 val.it_interval.tv_sec = 0;
422 422 val.it_interval.tv_usec = 50000;
423 423 val.it_value.tv_sec = 0;
424 424 val.it_value.tv_usec = 50000;
425 425 setitimer(ITIMER_REAL,&val,NULL);
426 426
427 427 /*
428 428 --- add alarm handler no matter what..
429 429 if (timeout)
430 430 {
431 431 sa.sa_handler = signal_alarm;
432 432 sigaction(SIGALRM, &sa, NULL);
433 433 alarm(1);
434 434 }
435 435 */
436 436
437 437 for(;;)
438 438 {
439 439 struct rusage rus;
440 440 int stat;
441 441 pid_t p;
442 442
443 443 if (timer_tick)
444 444 {
445 445 check_timeout();
446 446 check_memory_usage();
447 447 timer_tick = 0;
448 448 }
449 449 p = wait4(box_pid, &stat, WUNTRACED, &rus);
450 450
451 451 if (p < 0)
452 452 {
453 453 if (errno == EINTR)
454 454 continue;
455 455 die("wait4: %m");
456 456 }
457 457 if (p != box_pid)
458 458 die("wait4: unknown pid %d exited!", p);
459 459 if (WIFEXITED(stat))
460 460 {
461 461 struct timeval total;
462 462 int wall;
463 463 wall = time(NULL) - start_time;
464 464 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
465 465
466 466 box_pid = 0;
467 467 if (WEXITSTATUS(stat))
468 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 473 fprintf(stderr,"Time limit exceeded.\n");
471 474 else
472 475 // report OK and statistics
473 476 fprintf(stderr,"OK\n");
474 477
475 478 print_running_stat((double) wall,
476 479 (double) rus.ru_utime.tv_sec +
477 480 ((double) rus.ru_utime.tv_usec/1000000.0),
478 481 (double) rus.ru_stime.tv_sec +
479 482 ((double) rus.ru_stime.tv_usec/1000000.0),
480 483 max_mem_used);
481 484 /*
482 485 (%.4lf sec real (%d), %d sec wall, %d syscalls, %d kb)\n",
483 486 (double) total.tv_sec + ((double)total.tv_usec / 1000000.0),
484 487 (int) total.tv_usec,
485 488 wall,
486 489 syscall_count,
487 490 max_mem_used);
488 491 */
489 492 exit(0);
490 493 }
491 494 if (WIFSIGNALED(stat))
492 495 {
493 496 box_pid = 0;
494 497 fprintf(stderr,"Caught fatal signal %d.\n", WTERMSIG(stat));
495 498
496 499 struct timeval total;
497 500 int wall;
498 501 wall = time(NULL) - start_time;
499 502 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
500 503 print_running_stat((double) wall,
501 504 (double) rus.ru_utime.tv_sec +
502 505 ((double) rus.ru_utime.tv_usec/1000000.0),
503 506 (double) rus.ru_stime.tv_sec +
504 507 ((double) rus.ru_stime.tv_usec/1000000.0),
505 508 max_mem_used);
506 509 exit(0);
507 510 }
508 511 if (WIFSTOPPED(stat))
509 512 {
510 513 int sig = WSTOPSIG(stat);
511 514 if (sig == SIGTRAP)
512 515 {
513 516 struct user u;
514 517 static int stop_count = -1;
515 518 if (ptrace(PTRACE_GETREGS, box_pid, NULL, &u) < 0)
516 519 die("ptrace(PTRACE_GETREGS): %m");
517 520 stop_count++;
518 521 if (!stop_count) /* Traceme request */
519 522 log(">> Traceme request caught\n");
520 523 else if (stop_count & 1) /* Syscall entry */
521 524 {
522 525 log(">> Syscall %3ld (%08lx,%08lx,%08lx) ", u.regs.orig_eax, u.regs.ebx, u.regs.ecx, u.regs.edx);
523 526 syscall_count++;
524 527 if (!valid_syscall(&u))
525 528 {
526 529 /*
527 530 * Unfortunately, PTRACE_KILL kills _after_ the syscall completes,
528 531 * so we have to change it to something harmless (e.g., an undefined
529 532 * syscall) and make the program continue.
530 533 */
531 534 unsigned int sys = u.regs.orig_eax;
532 535 u.regs.orig_eax = 0xffffffff;
533 536 if (ptrace(PTRACE_SETREGS, box_pid, NULL, &u) < 0)
534 537 die("ptrace(PTRACE_SETREGS): %m");
535 538 die("Forbidden syscall %d.", sys);
536 539 }
537 540 }
538 541 else /* Syscall return */
539 542 log("= %ld\n", u.regs.eax);
540 543 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
541 544 }
542 545 else if (sig != SIGSTOP && sig != SIGXCPU && sig != SIGXFSZ)
543 546 {
544 547 log(">> Signal %d\n", sig);
545 548 ptrace(PTRACE_SYSCALL, box_pid, 0, sig);
546 549 }
547 550 else
548 551 die("Received signal %d.", sig);
549 552 }
550 553 else
551 554 die("wait4: unknown status %x, giving up!", stat);
552 555 }
553 556 }
554 557
555 558 static void
556 559 box_inside(int argc, char **argv)
557 560 {
558 561 struct rlimit rl;
559 562 char *args[argc+1];
560 563 char *env[1] = { NULL };
561 564
562 565 memcpy(args, argv, argc * sizeof(char *));
563 566 args[argc] = NULL;
564 567 if (set_cwd && chdir(set_cwd))
565 568 die("chdir: %m");
566 569 if (redir_stdin)
567 570 {
568 571 close(0);
569 572 if (open(redir_stdin, O_RDONLY) != 0)
570 573 die("open(\"%s\"): %m", redir_stdin);
571 574 }
572 575 if (redir_stdout)
573 576 {
574 577 close(1);
575 578 if (open(redir_stdout, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1)
576 579 die("open(\"%s\"): %m", redir_stdout);
577 580 }
578 581 dup2(1, 2);
579 582 setpgrp();
580 583 if (memory_limit)
581 584 {
582 585 rl.rlim_cur = rl.rlim_max = memory_limit * 1024;
583 586 if (setrlimit(RLIMIT_AS, &rl) < 0)
584 587 die("setrlimit: %m");
585 588 }
586 589 rl.rlim_cur = rl.rlim_max = 64;
587 590 if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
588 591 die("setrlimit: %m");
589 592 if (filter_syscalls && ptrace(PTRACE_TRACEME) < 0)
590 593 die("ptrace(PTRACE_TRACEME): %m");
591 594 execve(args[0], args, (pass_environ ? environ : env));
592 595 die("execve(\"%s\"): %m", args[0]);
593 596 }
594 597
595 598 static void
596 599 usage(void)
597 600 {
598 601 fprintf(stderr, "Invalid arguments!\n");
599 602 printf("\
600 603 Usage: box [<options>] -- <command> <arguments>\n\
601 604 \n\
602 605 Options:\n\
603 606 -a <level>\tSet file access level (0=none, 1=cwd, 2=/etc,/lib,..., 3=whole fs, 9=no checks; needs -f)\n\
604 607 -c <dir>\tChange directory to <dir> first\n\
605 608 -e\t\tPass full environment of parent process\n\
606 609 -f\t\tFilter system calls (-ff=very restricted)\n\
607 610 -i <file>\tRedirect stdin from <file>\n\
608 611 -m <size>\tLimit address space to <size> KB\n\
609 612 -o <file>\tRedirect stdout to <file>\n\
610 613 -t <time>\tStop after <time> seconds\n\
611 614 -T\t\tAllow syscalls for measuring run time\n\
612 615 -v\t\tBe verbose\n\
613 616 -w\t\tMeasure wall clock time instead of run time\n\
614 617 ");
615 618 exit(1);
616 619 }
617 620
618 621 int
619 622 main(int argc, char **argv)
620 623 {
621 624 int c;
622 625 uid_t uid;
623 626
624 627 while ((c = getopt(argc, argv, "a:c:efi:m:o:t:Tvw")) >= 0)
625 628 switch (c)
626 629 {
627 630 case 'a':
628 631 file_access = atol(optarg);
629 632 break;
630 633 case 'c':
631 634 set_cwd = optarg;
632 635 break;
633 636 case 'e':
634 637 pass_environ = 1;
635 638 break;
636 639 case 'f':
637 640 filter_syscalls++;
638 641 break;
639 642 case 'i':
640 643 redir_stdin = optarg;
641 644 break;
642 645 case 'm':
643 646 memory_limit = atol(optarg);
644 647 break;
645 648 case 'o':
646 649 redir_stdout = optarg;
647 650 break;
648 651 case 't':
649 - timeout = atol(optarg);
652 + timeout = atof(optarg);
650 653 break;
651 654 case 'T':
652 655 allow_times++;
653 656 break;
654 657 case 'v':
655 658 verbose++;
656 659 break;
657 660 case 'w':
658 661 use_wall_clock = 1;
659 662 break;
660 663 default:
661 664 usage();
662 665 }
663 666 if (optind >= argc)
664 667 usage();
665 668
666 669 uid = geteuid();
667 670 if (setreuid(uid, uid) < 0)
668 671 die("setreuid: %m");
669 672 box_pid = fork();
670 673 if (box_pid < 0)
671 674 die("fork: %m");
672 675 if (!box_pid)
673 676 box_inside(argc-optind, argv+optind);
674 677 else
675 678 boxkeeper();
676 679 die("Internal error: fell over edge of the world");
677 680 }
@@ -1,20 +1,24
1 1 problem do
2 2 num_tests 2
3 3 full_score 20
4 4 time_limit_each 1
5 5 mem_limit_each 16
6 6 score_each 10
7 7
8 + test 1 do
9 + time_limit 0.5
10 + end
11 +
8 12 run 1 do
9 13 tests 1
10 14 end
11 15
12 16 test 2 do
13 - time_limit 2
17 + time_limit 0.6
14 18 end
15 19
16 20 run 2 do
17 21 tests 2
18 22 end
19 23
20 24 end
@@ -1,151 +1,151
1 1 require File.join(File.dirname(__FILE__),'spec_helper')
2 2 require File.join(File.dirname(__FILE__),'engine_spec_helper')
3 3
4 4 describe "A grader engine, when grading submissions" do
5 5
6 6 include GraderEngineHelperMethods
7 7
8 8 before(:each) do
9 9 @config = Grader::Configuration.get_instance
10 10
11 11 # this test is from Pong
12 12 @problem_test_normal = stub(Problem,
13 13 :id => 1, :name => 'test_normal',
14 14 :full_score => 135)
15 15 @user_user1 = stub(User,
16 16 :id => 1, :login => 'user1')
17 17
18 18 @engine = Grader::Engine.new
19 19 init_sandbox
20 20 end
21 21
22 22 it "should grade normal submission" do
23 23 grader_should(:grade => "test1_correct.c",
24 24 :on => @problem_test_normal,
25 25 :and_report => {
26 26 :score => 135,
27 27 :comment => /^PASSED/})
28 28 end
29 29
30 30
31 31 it "should produce error message when submission cannot compile" do
32 32 grader_should(:grade => "test1_compile_error.c",
33 33 :on => @problem_test_normal,
34 34 :and_report => {
35 35 :score => 0,
36 36 :comment => 'FAILED: compilation error',
37 37 :compiler_message => /[Ee]rror/})
38 38 end
39 39
40 40 it "should produce timeout error when submission runs forever" do
41 41 @problem_test_timeout = stub(Problem,
42 42 :id => 1, :name => 'test_timeout',
43 43 :full_score => 10)
44 44 grader_should(:grade => "test2_timeout.c",
45 45 :on => @problem_test_timeout,
46 46 :and_report => {
47 47 :score => 0,
48 48 :comment => 'FAILED: TT'})
49 49 end
50 50
51 - it "should produce timeout error correctly when submission runs slower than expected in less than a second" do
52 - @problem_test_timeout = stub(Problem,
51 + it "should produce timeout error correctly with fractional running time and fractional time limits" do
52 + @problem_test_timeout = stub(Problem,
53 53 :id => 1, :name => 'test_timeout',
54 54 :full_score => 20)
55 - grader_should(:grade => "test2_1-5sec.c",
55 + grader_should(:grade => "test2_05sec.c",
56 56 :on => @problem_test_timeout,
57 57 :and_report => {
58 58 :score => 10,
59 59 :comment => 'FAILED: TP'})
60 60 end
61 61
62 62 it "should produce runtime error when submission uses too much static memory" do
63 63 @problem_test_memory = stub(Problem,
64 64 :id => 1, :name => 'test_memory',
65 65 :full_score => 20)
66 66 grader_should(:grade => "add_too_much_memory_static.c",
67 67 :on => @problem_test_memory,
68 68 :and_report => {
69 69 :score => 10,
70 70 :comment => /FAILED: [^P]P/})
71 71 end
72 72
73 73 it "should not allow submission to allocate too much dynamic memory" do
74 74 @problem_test_memory = stub(Problem,
75 75 :id => 1, :name => 'test_memory',
76 76 :full_score => 20)
77 77 grader_should(:grade => "add_too_much_memory_dynamic.c",
78 78 :on => @problem_test_memory,
79 79 :and_report => {
80 80 :score => 10,
81 81 :comment => /FAILED: [^P]P/})
82 82 end
83 83
84 84 it "should score test runs correctly when submission fails in some test case" do
85 85 grader_should(:grade => "add_fail_test_case_1.c",
86 86 :on => @problem_test_normal,
87 87 :and_report => {
88 88 :score => 105,
89 89 :comment => /^FAILED:/})
90 90 end
91 91
92 92 it "should fail submission with non-zero exit status" do
93 93 grader_should(:grade => "add_nonzero_exit_status.c",
94 94 :on => @problem_test_normal,
95 95 :and_report => {
96 96 :score => 0,
97 97 :comment => /^FAILED:/})
98 98 end
99 99
100 100 it "should not allow malicious submission to see PROBLEM_HOME" do
101 101 problem_test_yesno = stub(Problem,
102 102 :id => 1, :name => 'test_yesno',
103 103 :full_score => 10)
104 104 grader_should(:grade => "yesno_access_problem_home.c",
105 105 :on => problem_test_yesno,
106 106 :and_report => {
107 107 :score => 0,
108 108 :comment => /^FAILED:/})
109 109 end
110 110
111 111 it "should not allow malicious submission to open files" do
112 112 problem_test_yesno = stub(Problem,
113 113 :id => 1, :name => 'test_yesno',
114 114 :full_score => 10)
115 115 grader_should(:grade => "yesno_open_file.c",
116 116 :on => problem_test_yesno,
117 117 :and_report => {
118 118 :score => 0,
119 119 :comment => /^FAILED:/})
120 120 end
121 121
122 122 def grader_should(args)
123 123 @user1 = stub(User,
124 124 :id => 1, :login => 'user1')
125 125 submission =
126 126 create_submission_from_file(1, @user1, args[:on], args[:grade])
127 127 submission.should_receive(:graded_at=)
128 128
129 129 expected_score = args[:and_report][:score]
130 130 expected_comment = args[:and_report][:comment]
131 131 if args[:and_report][:compiler_message]!=nil
132 132 expected_compiler_message = args[:and_report][:compiler_message]
133 133 else
134 134 expected_compiler_message = ''
135 135 end
136 136
137 137 submission.should_receive(:points=).with(expected_score)
138 138 submission.should_receive(:grader_comment=).with(expected_comment)
139 139 submission.should_receive(:compiler_message=).with(expected_compiler_message)
140 140 submission.should_receive(:save)
141 141
142 142 @engine.grade(submission)
143 143 end
144 144
145 145 protected
146 146
147 147 def create_normal_submission_mock_from_file(source_fname)
148 148 create_submission_from_file(1, @user_user1, @problem_test_normal, source_fname)
149 149 end
150 150
151 151 end
deleted file
You need to be logged in to leave comments. Login now