Description:
[grader] added memory usage status report, better running time report git-svn-id: http://theory.cpe.ku.ac.th/grader/judge/trunk/scripts@165 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

r42:9669362454bd - - 6 files changed: 747 inserted, 573 deleted

This diff has been collapsed as it changes many lines, (663 lines changed) Show them Hide them
@@ -0,0 +1,663
1 + /*
2 + * A Simple Testing Sandbox
3 + *
4 + * (c) 2001--2004 Martin Mares <mj@ucw.cz>
5 + */
6 +
7 + #define _LARGEFILE64_SOURCE
8 + //#define _GNU_SOURCE
9 +
10 + #include <errno.h>
11 + #include <stdio.h>
12 + #include <fcntl.h>
13 + #include <stdlib.h>
14 + #include <string.h>
15 + #include <stdarg.h>
16 + #include <unistd.h>
17 + #include <getopt.h>
18 + #include <time.h>
19 + #include <sys/wait.h>
20 + #include <sys/user.h>
21 + #include <sys/time.h>
22 + #include <sys/ptrace.h>
23 + #include <sys/signal.h>
24 + #include <sys/sysinfo.h>
25 + #include <sys/syscall.h>
26 + #include <sys/resource.h>
27 +
28 + #define NONRET __attribute__((noreturn))
29 + #define UNUSED __attribute__((unused))
30 +
31 + static int filter_syscalls; /* 0=off, 1=liberal, 2=totalitarian */
32 + static int timeout;
33 + static int pass_environ;
34 + static int use_wall_clock;
35 + static int file_access;
36 + static int verbose;
37 + static int memory_limit;
38 + static int allow_times;
39 + static char *redir_stdin, *redir_stdout;
40 + static char *set_cwd;
41 +
42 + static pid_t box_pid;
43 + static int is_ptraced;
44 + static volatile int timer_tick;
45 + static time_t start_time;
46 + static int ticks_per_sec;
47 +
48 + #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0
49 + /* glibc 2.1 or newer -> has lseek64 */
50 + #define long_seek(f,o,w) lseek64(f,o,w)
51 + #else
52 + /* Touching clandestine places in glibc */
53 + extern loff_t llseek(int fd, loff_t pos, int whence);
54 + #define long_seek(f,o,w) llseek(f,o,w)
55 + #endif
56 +
57 + static void NONRET
58 + box_exit(void)
59 + {
60 + if (box_pid > 0)
61 + {
62 + if (is_ptraced)
63 + ptrace(PTRACE_KILL, box_pid);
64 + kill(-box_pid, SIGKILL);
65 + kill(box_pid, SIGKILL);
66 + }
67 + exit(1);
68 + }
69 +
70 + static void
71 + box_kill(void)
72 + {
73 + if (box_pid > 0)
74 + {
75 + if (is_ptraced)
76 + ptrace(PTRACE_KILL, box_pid);
77 + kill(-box_pid, SIGKILL);
78 + kill(box_pid, SIGKILL);
79 + }
80 + }
81 +
82 + static void NONRET __attribute__((format(printf,1,2)))
83 + die(char *msg, ...)
84 + {
85 + va_list args;
86 + va_start(args, msg);
87 + vfprintf(stderr, msg, args);
88 + fputc('\n', stderr);
89 + box_exit();
90 + }
91 +
92 + static void __attribute__((format(printf,1,2)))
93 + die_report(char *msg, ...)
94 + {
95 + va_list args;
96 + va_start(args, msg);
97 + vfprintf(stderr, msg, args);
98 + fputc('\n', stderr);
99 + box_kill();
100 + }
101 +
102 + static void __attribute__((format(printf,1,2)))
103 + log(char *msg, ...)
104 + {
105 + va_list args;
106 + va_start(args, msg);
107 + if (verbose)
108 + {
109 + vfprintf(stderr, msg, args);
110 + fflush(stderr);
111 + }
112 + va_end(args);
113 + }
114 +
115 + static void
116 + valid_filename(unsigned long addr)
117 + {
118 + char namebuf[4096], *p, *end;
119 + static int mem_fd;
120 +
121 + if (!file_access)
122 + die("File access forbidden.");
123 + if (file_access >= 9)
124 + return;
125 +
126 + if (!mem_fd)
127 + {
128 + sprintf(namebuf, "/proc/%d/mem", (int) box_pid);
129 + mem_fd = open(namebuf, O_RDONLY);
130 + if (mem_fd < 0)
131 + die("open(%s): %m", namebuf);
132 + }
133 + p = end = namebuf;
134 + do
135 + {
136 + if (p >= end)
137 + {
138 + int remains = PAGE_SIZE - (addr & (PAGE_SIZE-1));
139 + int l = namebuf + sizeof(namebuf) - end;
140 + if (l > remains)
141 + l = remains;
142 + if (!l)
143 + die("Access to file with name too long.");
144 + if (long_seek(mem_fd, addr, SEEK_SET) < 0)
145 + die("long_seek(mem): %m");
146 + remains = read(mem_fd, end, l);
147 + if (remains < 0)
148 + die("read(mem): %m");
149 + if (!remains)
150 + die("Access to file with name out of memory.");
151 + end += l;
152 + addr += l;
153 + }
154 + }
155 + while (*p++);
156 +
157 + log("[%s] ", namebuf);
158 + if (file_access >= 3)
159 + return;
160 + if (!strchr(namebuf, '/') && strcmp(namebuf, ".."))
161 + return;
162 + if (file_access >= 2)
163 + {
164 + if ((!strncmp(namebuf, "/etc/", 5) ||
165 + !strncmp(namebuf, "/lib/", 5) ||
166 + !strncmp(namebuf, "/usr/lib/", 9))
167 + && !strstr(namebuf, ".."))
168 + return;
169 + if (!strcmp(namebuf, "/dev/null") ||
170 + !strcmp(namebuf, "/dev/zero") ||
171 + !strcmp(namebuf, "/proc/meminfo") ||
172 + !strcmp(namebuf, "/proc/self/stat") ||
173 + !strncmp(namebuf, "/usr/share/zoneinfo/", 20))
174 + return;
175 + }
176 + die("Forbidden access to file `%s'.", namebuf);
177 + }
178 +
179 + static int
180 + valid_syscall(struct user *u)
181 + {
182 + switch (u->regs.orig_eax)
183 + {
184 + case __NR_execve:
185 + {
186 + static int exec_counter;
187 + return !exec_counter++;
188 + }
189 + case __NR_open:
190 + case __NR_creat:
191 + case __NR_unlink:
192 + case __NR_oldstat:
193 + case __NR_access:
194 + case __NR_oldlstat:
195 + case __NR_truncate:
196 + case __NR_stat:
197 + case __NR_lstat:
198 + case __NR_truncate64:
199 + case __NR_stat64:
200 + case __NR_lstat64:
201 + valid_filename(u->regs.ebx);
202 + return 1;
203 + case __NR_exit:
204 + case __NR_read:
205 + case __NR_write:
206 + case __NR_close:
207 + case __NR_lseek:
208 + case __NR_getpid:
209 + case __NR_getuid:
210 + case __NR_oldfstat:
211 + case __NR_dup:
212 + case __NR_brk:
213 + case __NR_getgid:
214 + case __NR_geteuid:
215 + case __NR_getegid:
216 + case __NR_dup2:
217 + case __NR_ftruncate:
218 + case __NR_fstat:
219 + case __NR_personality:
220 + case __NR__llseek:
221 + case __NR_readv:
222 + case __NR_writev:
223 + case __NR_getresuid:
224 + #ifdef __NR_pread64
225 + case __NR_pread64:
226 + case __NR_pwrite64:
227 + #else
228 + case __NR_pread:
229 + case __NR_pwrite:
230 + #endif
231 + case __NR_ftruncate64:
232 + case __NR_fstat64:
233 + case __NR_fcntl:
234 + case __NR_fcntl64:
235 + case __NR_mmap:
236 + case __NR_munmap:
237 + case __NR_ioctl:
238 + case __NR_uname:
239 + case 252:
240 + case 243:
241 + return 1;
242 + // case __NR_time:
243 + case __NR_alarm:
244 + // case __NR_pause:
245 + case __NR_signal:
246 + case __NR_fchmod:
247 + case __NR_sigaction:
248 + case __NR_sgetmask:
249 + case __NR_ssetmask:
250 + case __NR_sigsuspend:
251 + case __NR_sigpending:
252 + case __NR_getrlimit:
253 + case __NR_getrusage:
254 + case __NR_gettimeofday:
255 + case __NR_select:
256 + case __NR_readdir:
257 + case __NR_setitimer:
258 + case __NR_getitimer:
259 + case __NR_sigreturn:
260 + case __NR_mprotect:
261 + case __NR_sigprocmask:
262 + case __NR_getdents:
263 + case __NR_getdents64:
264 + case __NR__newselect:
265 + case __NR_fdatasync:
266 + case __NR_mremap:
267 + case __NR_poll:
268 + case __NR_getcwd:
269 + case __NR_nanosleep:
270 + case __NR_rt_sigreturn:
271 + case __NR_rt_sigaction:
272 + case __NR_rt_sigprocmask:
273 + case __NR_rt_sigpending:
274 + case __NR_rt_sigtimedwait:
275 + case __NR_rt_sigqueueinfo:
276 + case __NR_rt_sigsuspend:
277 + case __NR_mmap2:
278 + case __NR__sysctl:
279 + return (filter_syscalls == 1);
280 + case __NR_times:
281 + return allow_times;
282 + case __NR_kill:
283 + if (u->regs.ebx == box_pid)
284 + die("Commited suicide by signal %d.", (int)u->regs.ecx);
285 + return 0;
286 + default:
287 + return 0;
288 + }
289 + }
290 +
291 + static void
292 + signal_alarm(int unused UNUSED)
293 + {
294 + /* Time limit checks are synchronous, so we only schedule them there. */
295 + timer_tick = 1;
296 +
297 + //NOTE: do not use alarm, changed to setitimer for precision
298 + // alarm(1);
299 + }
300 +
301 + static void
302 + signal_int(int unused UNUSED)
303 + {
304 + /* Interrupts are fatal, so no synchronization requirements. */
305 + die("Interrupted.");
306 + }
307 +
308 + static void
309 + check_timeout(void)
310 + {
311 + int sec;
312 +
313 + if (use_wall_clock)
314 + sec = time(NULL) - start_time;
315 + else
316 + {
317 + char buf[4096], *x;
318 + int c, utime, stime;
319 + static int proc_status_fd;
320 + if (!proc_status_fd)
321 + {
322 + sprintf(buf, "/proc/%d/stat", (int) box_pid);
323 + proc_status_fd = open(buf, O_RDONLY);
324 + if (proc_status_fd < 0)
325 + die("open(%s): %m", buf);
326 + }
327 + lseek(proc_status_fd, 0, SEEK_SET);
328 + if ((c = read(proc_status_fd, buf, sizeof(buf)-1)) < 0)
329 + die("read on /proc/$pid/stat: %m");
330 + if (c >= (int) sizeof(buf) - 1)
331 + die("/proc/$pid/stat too long");
332 + buf[c] = 0;
333 + x = buf;
334 + while (*x && *x != ' ')
335 + x++;
336 + while (*x == ' ')
337 + x++;
338 + if (*x++ != '(')
339 + die("proc syntax error 1");
340 + while (*x && (*x != ')' || x[1] != ' '))
341 + x++;
342 + while (*x == ')' || *x == ' ')
343 + x++;
344 + if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2)
345 + die("proc syntax error 2");
346 + //printf("%s - %d\n",x,ticks_per_sec);
347 + sec = (utime + stime + ticks_per_sec-1)/ticks_per_sec;
348 + }
349 + if (verbose > 1)
350 + fprintf(stderr, "[timecheck: %d seconds]\n", sec);
351 + if (sec > timeout) {
352 + die_report("Time limit exceeded.");
353 + }
354 + }
355 +
356 + int max_mem_used = 0;
357 +
358 + static void
359 + check_memory_usage()
360 + {
361 + char proc_fname[100];
362 + sprintf(proc_fname,"/proc/%d/statm",box_pid);
363 + //printf("proc fname: %s\n",proc_fname);
364 + FILE *fp = fopen(proc_fname,"r");
365 + if(fp!=NULL) {
366 + char line[1000];
367 + fgets(line,999,fp);
368 + //printf("%s\n",line);
369 + int m;
370 +
371 + if(sscanf(line,"%d",&m)==1)
372 + if(m>max_mem_used)
373 + max_mem_used = m;
374 +
375 + fclose(fp);
376 + }
377 + }
378 +
379 + static void
380 + boxkeeper(void)
381 + {
382 + int syscall_count = 0;
383 + struct sigaction sa;
384 +
385 + is_ptraced = 1;
386 + bzero(&sa, sizeof(sa));
387 + sa.sa_handler = signal_int;
388 + sigaction(SIGINT, &sa, NULL);
389 + start_time = time(NULL);
390 + ticks_per_sec = sysconf(_SC_CLK_TCK);
391 + if (ticks_per_sec <= 0)
392 + die("Invalid ticks_per_sec!");
393 +
394 + check_memory_usage();
395 +
396 + sa.sa_handler = signal_alarm;
397 + sigaction(SIGALRM, &sa, NULL);
398 + //alarm(1);
399 +
400 + struct itimerval val;
401 + val.it_interval.tv_sec = 0;
402 + val.it_interval.tv_usec = 50000;
403 + val.it_value.tv_sec = 0;
404 + val.it_value.tv_usec = 50000;
405 + setitimer(ITIMER_REAL,&val,NULL);
406 +
407 + /*
408 + --- add alarm handler no matter what..
409 + if (timeout)
410 + {
411 + sa.sa_handler = signal_alarm;
412 + sigaction(SIGALRM, &sa, NULL);
413 + alarm(1);
414 + }
415 + */
416 +
417 + for(;;)
418 + {
419 + struct rusage rus;
420 + int stat;
421 + pid_t p;
422 +
423 + if (timer_tick)
424 + {
425 + check_timeout();
426 + timer_tick = 0;
427 + }
428 + p = wait4(box_pid, &stat, WUNTRACED, &rus);
429 +
430 + if(!WIFEXITED(stat)) {
431 + // printf("CHECKING\n");
432 + check_memory_usage();
433 + }
434 +
435 + if (p < 0)
436 + {
437 + if (errno == EINTR)
438 + continue;
439 + die("wait4: %m");
440 + }
441 + if (p != box_pid)
442 + die("wait4: unknown pid %d exited!", p);
443 + if (WIFEXITED(stat))
444 + {
445 + struct timeval total;
446 + int wall;
447 + wall = time(NULL) - start_time;
448 + timeradd(&rus.ru_utime, &rus.ru_stime, &total);
449 +
450 + box_pid = 0;
451 + if (WEXITSTATUS(stat))
452 + fprintf(stderr,"Exited with error status %d.", WEXITSTATUS(stat));
453 + else if ((use_wall_clock ? wall : total.tv_sec) > timeout)
454 + fprintf(stderr,"Time limit exceeded.");
455 + else
456 + // report OK and statistics
457 + fprintf(stderr,"OK\n");
458 +
459 + fprintf(stderr,"%dr%.4lfu%.4lfs%dm\n",
460 + wall,
461 + (double) rus.ru_utime.tv_sec +
462 + ((double) rus.ru_utime.tv_usec/1000000.0),
463 + (double) rus.ru_stime.tv_sec +
464 + ((double) rus.ru_stime.tv_usec/1000000.0),
465 + max_mem_used);
466 + /*
467 + (%.4lf sec real (%d), %d sec wall, %d syscalls, %d kb)\n",
468 + (double) total.tv_sec + ((double)total.tv_usec / 1000000.0),
469 + (int) total.tv_usec,
470 + wall,
471 + syscall_count,
472 + max_mem_used);
473 + */
474 + exit(0);
475 + }
476 + if (WIFSIGNALED(stat))
477 + {
478 + box_pid = 0;
479 + fprintf(stderr,"Caught fatal signal %d.", WTERMSIG(stat));
480 +
481 + struct timeval total;
482 + int wall;
483 + wall = time(NULL) - start_time;
484 + timeradd(&rus.ru_utime, &rus.ru_stime, &total);
485 + fprintf(stderr,"%dr%.4lfu%.4lfs%dm\n",
486 + wall,
487 + (double) rus.ru_utime.tv_sec +
488 + ((double) rus.ru_utime.tv_usec/1000000.0),
489 + (double) rus.ru_stime.tv_sec +
490 + ((double) rus.ru_stime.tv_usec/1000000.0),
491 + max_mem_used);
492 + exit(0);
493 + }
494 + if (WIFSTOPPED(stat))
495 + {
496 + int sig = WSTOPSIG(stat);
497 + if (sig == SIGTRAP)
498 + {
499 + struct user u;
500 + static int stop_count = -1;
501 + if (ptrace(PTRACE_GETREGS, box_pid, NULL, &u) < 0)
502 + die("ptrace(PTRACE_GETREGS): %m");
503 + stop_count++;
504 + if (!stop_count) /* Traceme request */
505 + log(">> Traceme request caught\n");
506 + else if (stop_count & 1) /* Syscall entry */
507 + {
508 + log(">> Syscall %3ld (%08lx,%08lx,%08lx) ", u.regs.orig_eax, u.regs.ebx, u.regs.ecx, u.regs.edx);
509 + syscall_count++;
510 + if (!valid_syscall(&u))
511 + {
512 + /*
513 + * Unfortunately, PTRACE_KILL kills _after_ the syscall completes,
514 + * so we have to change it to something harmless (e.g., an undefined
515 + * syscall) and make the program continue.
516 + */
517 + unsigned int sys = u.regs.orig_eax;
518 + u.regs.orig_eax = 0xffffffff;
519 + if (ptrace(PTRACE_SETREGS, box_pid, NULL, &u) < 0)
520 + die("ptrace(PTRACE_SETREGS): %m");
521 + die("Forbidden syscall %d.", sys);
522 + }
523 + }
524 + else /* Syscall return */
525 + log("= %ld\n", u.regs.eax);
526 + ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
527 + }
528 + else if (sig != SIGSTOP && sig != SIGXCPU && sig != SIGXFSZ)
529 + {
530 + log(">> Signal %d\n", sig);
531 + ptrace(PTRACE_SYSCALL, box_pid, 0, sig);
532 + }
533 + else
534 + die("Received signal %d.", sig);
535 + }
536 + else
537 + die("wait4: unknown status %x, giving up!", stat);
538 + }
539 + }
540 +
541 + static void
542 + box_inside(int argc, char **argv)
543 + {
544 + struct rlimit rl;
545 + char *args[argc+1];
546 + char *env[1] = { NULL };
547 +
548 + memcpy(args, argv, argc * sizeof(char *));
549 + args[argc] = NULL;
550 + if (set_cwd && chdir(set_cwd))
551 + die("chdir: %m");
552 + if (redir_stdin)
553 + {
554 + close(0);
555 + if (open(redir_stdin, O_RDONLY) != 0)
556 + die("open(\"%s\"): %m", redir_stdin);
557 + }
558 + if (redir_stdout)
559 + {
560 + close(1);
561 + if (open(redir_stdout, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1)
562 + die("open(\"%s\"): %m", redir_stdout);
563 + }
564 + dup2(1, 2);
565 + setpgrp();
566 + if (memory_limit)
567 + {
568 + rl.rlim_cur = rl.rlim_max = memory_limit * 1024;
569 + if (setrlimit(RLIMIT_AS, &rl) < 0)
570 + die("setrlimit: %m");
571 + }
572 + rl.rlim_cur = rl.rlim_max = 64;
573 + if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
574 + die("setrlimit: %m");
575 + if (filter_syscalls && ptrace(PTRACE_TRACEME) < 0)
576 + die("ptrace(PTRACE_TRACEME): %m");
577 + execve(args[0], args, (pass_environ ? environ : env));
578 + die("execve(\"%s\"): %m", args[0]);
579 + }
580 +
581 + static void
582 + usage(void)
583 + {
584 + fprintf(stderr, "Invalid arguments!\n");
585 + printf("\
586 + Usage: box [<options>] -- <command> <arguments>\n\
587 + \n\
588 + Options:\n\
589 + -a <level>\tSet file access level (0=none, 1=cwd, 2=/etc,/lib,..., 3=whole fs, 9=no checks; needs -f)\n\
590 + -c <dir>\tChange directory to <dir> first\n\
591 + -e\t\tPass full environment of parent process\n\
592 + -f\t\tFilter system calls (-ff=very restricted)\n\
593 + -i <file>\tRedirect stdin from <file>\n\
594 + -m <size>\tLimit address space to <size> KB\n\
595 + -o <file>\tRedirect stdout to <file>\n\
596 + -t <time>\tStop after <time> seconds\n\
597 + -T\t\tAllow syscalls for measuring run time\n\
598 + -v\t\tBe verbose\n\
599 + -w\t\tMeasure wall clock time instead of run time\n\
600 + ");
601 + exit(1);
602 + }
603 +
604 + int
605 + main(int argc, char **argv)
606 + {
607 + int c;
608 + uid_t uid;
609 +
610 + while ((c = getopt(argc, argv, "a:c:efi:m:o:t:Tvw")) >= 0)
611 + switch (c)
612 + {
613 + case 'a':
614 + file_access = atol(optarg);
615 + break;
616 + case 'c':
617 + set_cwd = optarg;
618 + break;
619 + case 'e':
620 + pass_environ = 1;
621 + break;
622 + case 'f':
623 + filter_syscalls++;
624 + break;
625 + case 'i':
626 + redir_stdin = optarg;
627 + break;
628 + case 'm':
629 + memory_limit = atol(optarg);
630 + break;
631 + case 'o':
632 + redir_stdout = optarg;
633 + break;
634 + case 't':
635 + timeout = atol(optarg);
636 + break;
637 + case 'T':
638 + allow_times++;
639 + break;
640 + case 'v':
641 + verbose++;
642 + break;
643 + case 'w':
644 + use_wall_clock = 1;
645 + break;
646 + default:
647 + usage();
648 + }
649 + if (optind >= argc)
650 + usage();
651 +
652 + uid = geteuid();
653 + if (setreuid(uid, uid) < 0)
654 + die("setreuid: %m");
655 + box_pid = fork();
656 + if (box_pid < 0)
657 + die("fork: %m");
658 + if (!box_pid)
659 + box_inside(argc-optind, argv+optind);
660 + else
661 + boxkeeper();
662 + die("Internal error: fell over edge of the world");
663 + }
@@ -0,0 +1,29
1 + /*
2 + LANG: C
3 + */
4 + #include <stdio.h>
5 + #include <stdlib.h>
6 +
7 + int main()
8 + {
9 + int a,b,d;
10 + int i,j;
11 + char *c = malloc(100000);
12 +
13 + scanf("%d %d",&a,&b);
14 + d = a+b;
15 + // printf("%d\n",a+b);
16 + for(j=0; j<1; j++)
17 + for(i=0; i<100000000; i++) {
18 + b+=a;
19 + a++;
20 + }
21 + if((c!=NULL) || (b<100))
22 + b++;
23 + if(b==100)
24 + printf("hello");
25 + else
26 + printf("%d\n",d);
27 + return 0;
28 + }
29 +
@@ -75,132 +75,141
75 end
75 end
76
76
77 def link_input_file(test_request,problem_home)
77 def link_input_file(test_request,problem_home)
78 cmd = "ln -s #{test_request.input_file_name} #{problem_home}/test_cases/1/input-1.txt"
78 cmd = "ln -s #{test_request.input_file_name} #{problem_home}/test_cases/1/input-1.txt"
79 system_and_raise_when_fail(cmd,"Test Request: cannot link input file")
79 system_and_raise_when_fail(cmd,"Test Request: cannot link input file")
80 end
80 end
81
81
82 def remove_data_files(problem_home)
82 def remove_data_files(problem_home)
83 if File.exists?("#{problem_home}/test_cases/1/input-1.txt")
83 if File.exists?("#{problem_home}/test_cases/1/input-1.txt")
84 cmd = "rm #{problem_home}/test_cases/1/*"
84 cmd = "rm #{problem_home}/test_cases/1/*"
85 system_and_raise_when_fail(cmd,"Test Request: cannot remove data files")
85 system_and_raise_when_fail(cmd,"Test Request: cannot remove data files")
86 end
86 end
87 end
87 end
88
88
89 def system_and_raise_when_fail(cmd,msg)
89 def system_and_raise_when_fail(cmd,msg)
90 if !system(cmd)
90 if !system(cmd)
91 raise msg
91 raise msg
92 end
92 end
93 end
93 end
94
94
95 end
95 end
96
96
97 class TestRequestReporter
97 class TestRequestReporter
98 def initialize
98 def initialize
99 @config = Grader::Configuration.get_instance
99 @config = Grader::Configuration.get_instance
100 end
100 end
101
101
102 def report(test_request,test_result_dir)
102 def report(test_request,test_result_dir)
103 save_result(test_request,read_result(test_result_dir))
103 save_result(test_request,read_result(test_result_dir))
104 end
104 end
105
105
106 def report_error(test_request, msg)
106 def report_error(test_request, msg)
107 save_result(test_request, {:running_stat => {:msg => "#{msg}"}})
107 save_result(test_request, {:running_stat => {:msg => "#{msg}"}})
108 end
108 end
109
109
110 protected
110 protected
111 def read_result(test_result_dir)
111 def read_result(test_result_dir)
112 # TODO:
112 # TODO:
113 cmp_msg_fname = "#{test_result_dir}/compiler_message"
113 cmp_msg_fname = "#{test_result_dir}/compiler_message"
114 cmp_file = File.open(cmp_msg_fname)
114 cmp_file = File.open(cmp_msg_fname)
115 cmp_msg = cmp_file.read
115 cmp_msg = cmp_file.read
116 cmp_file.close
116 cmp_file.close
117
117
118 result_file_name = "#{test_result_dir}/1/result"
118 result_file_name = "#{test_result_dir}/1/result"
119
119
120 if File.exists?(result_file_name)
120 if File.exists?(result_file_name)
121 output_file_name = "#{test_result_dir}/1/output.txt"
121 output_file_name = "#{test_result_dir}/1/output.txt"
122 results = File.open("#{test_result_dir}/1/result").readlines
122 results = File.open("#{test_result_dir}/1/result").readlines
123 - stat = format_running_stat(results)
123 + stat = extract_running_stat(results)
124
124
125 return {
125 return {
126 :output_file_name => output_file_name,
126 :output_file_name => output_file_name,
127 :running_stat => stat,
127 :running_stat => stat,
128 :comment => "",
128 :comment => "",
129 :cmp_msg => cmp_msg}
129 :cmp_msg => cmp_msg}
130 else
130 else
131 return {
131 return {
132 :running_stat => nil,
132 :running_stat => nil,
133 :comment => "Compilation error",
133 :comment => "Compilation error",
134 :cmp_msg => cmp_msg}
134 :cmp_msg => cmp_msg}
135 end
135 end
136 end
136 end
137
137
138 - def format_running_stat(results)
138 + def extract_running_stat(results)
139 - running_time_line = results[-1]
139 + running_stat_line = results[-1]
140
140
141 # extract exit status line
141 # extract exit status line
142 run_stat = ""
142 run_stat = ""
143 if !(/[Cc]orrect/.match(results[0]))
143 if !(/[Cc]orrect/.match(results[0]))
144 run_stat = results[0].chomp
144 run_stat = results[0].chomp
145 else
145 else
146 run_stat = 'Program exited normally'
146 run_stat = 'Program exited normally'
147 end
147 end
148
148
149 # extract running time
149 # extract running time
150 - if res = /r(.*)u(.*)s/.match(running_time_line)
150 + if res = /r(.*)u(.*)s/.match(running_stat_line)
151 seconds = (res[1].to_f + res[2].to_f)
151 seconds = (res[1].to_f + res[2].to_f)
152 time_stat = "Time used: #{seconds} sec."
152 time_stat = "Time used: #{seconds} sec."
153 else
153 else
154 seconds = nil
154 seconds = nil
155 time_stat = "Time used: n/a sec."
155 time_stat = "Time used: n/a sec."
156 end
156 end
157 +
158 + # extract memory usage
159 + if res = /s(.*)m/.match(running_stat_line)
160 + memory_used = res[1].to_i
161 + else
162 + memory_used = -1
163 + end
164 +
157 return {
165 return {
158 :msg => "#{run_stat}\n#{time_stat}",
166 :msg => "#{run_stat}\n#{time_stat}",
159 :running_time => seconds,
167 :running_time => seconds,
160 - :exit_status => run_stat
168 + :exit_status => run_stat,
169 + :memory_usage => memory_used
161 }
170 }
162 end
171 end
163
172
164 def save_result(test_request,result)
173 def save_result(test_request,result)
165 if result[:output_file_name]!=nil
174 if result[:output_file_name]!=nil
166 test_request.output_file_name = link_output_file(test_request,
175 test_request.output_file_name = link_output_file(test_request,
167 result[:output_file_name])
176 result[:output_file_name])
168 end
177 end
169 test_request.graded_at = Time.now
178 test_request.graded_at = Time.now
170 test_request.compiler_message = (result[:cmp_msg] or '')
179 test_request.compiler_message = (result[:cmp_msg] or '')
171 test_request.grader_comment = (result[:comment] or '')
180 test_request.grader_comment = (result[:comment] or '')
172 if result[:running_stat]!=nil
181 if result[:running_stat]!=nil
173 test_request.running_stat = (result[:running_stat][:msg] or '')
182 test_request.running_stat = (result[:running_stat][:msg] or '')
174 test_request.running_time = (result[:running_stat][:running_time] or nil)
183 test_request.running_time = (result[:running_stat][:running_time] or nil)
175 - test_request.exit_status = (result[:running_stat][:exit_status])
184 + test_request.exit_status = result[:running_stat][:exit_status]
176 - test_request.memory_usage = nil # should be added later
185 + test_request.memory_usage = result[:running_stat][:memory_usage]
177 else
186 else
178 test_request.running_stat = ''
187 test_request.running_stat = ''
179 end
188 end
180 test_request.save
189 test_request.save
181 end
190 end
182
191
183 protected
192 protected
184 def link_output_file(test_request, fname)
193 def link_output_file(test_request, fname)
185 target_file_name = random_output_file_name(test_request.user,
194 target_file_name = random_output_file_name(test_request.user,
186 test_request.problem)
195 test_request.problem)
187 FileUtils.mkdir_p(File.dirname(target_file_name))
196 FileUtils.mkdir_p(File.dirname(target_file_name))
188 cmd = "ln -s #{fname} #{target_file_name}"
197 cmd = "ln -s #{fname} #{target_file_name}"
189 if !system(cmd)
198 if !system(cmd)
190 raise "TestRequestReporter: cannot move output file"
199 raise "TestRequestReporter: cannot move output file"
191 end
200 end
192 return target_file_name
201 return target_file_name
193 end
202 end
194
203
195 def random_output_file_name(user,problem)
204 def random_output_file_name(user,problem)
196 problem_name = TestRequest.name_of(problem)
205 problem_name = TestRequest.name_of(problem)
197 begin
206 begin
198 tmpname = "#{@config.test_request_output_base_dir}" +
207 tmpname = "#{@config.test_request_output_base_dir}" +
199 "/#{user.login}/#{problem_name}/#{rand(10000)}"
208 "/#{user.login}/#{problem_name}/#{rand(10000)}"
200 end while File.exists?(tmpname)
209 end while File.exists?(tmpname)
201 tmpname
210 tmpname
202 end
211 end
203
212
204 end
213 end
205
214
206 end
215 end
@@ -1,149 +1,155
1 #!/usr/bin/ruby
1 #!/usr/bin/ruby
2
2
3 def log(str='')
3 def log(str='')
4 if ENV['TALKATIVE']!=nil
4 if ENV['TALKATIVE']!=nil
5 puts str
5 puts str
6 end
6 end
7 if ENV['GRADER_LOGGING']!=nil
7 if ENV['GRADER_LOGGING']!=nil
8 log_fname = ENV['GRADER_LOGGING']
8 log_fname = ENV['GRADER_LOGGING']
9 fp = File.open(log_fname,"a")
9 fp = File.open(log_fname,"a")
10 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
10 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
11 fp.close
11 fp.close
12 end
12 end
13 end
13 end
14
14
15 def extract_time(t)
15 def extract_time(t)
16 + # puts "TIME: #{t}"
16 if (result=/^(.*)r(.*)u(.*)s/.match(t))
17 if (result=/^(.*)r(.*)u(.*)s/.match(t))
17 {:real => result[1], :user => result[2], :sys => result[3]}
18 {:real => result[1], :user => result[2], :sys => result[3]}
18 else
19 else
19 - raise "Error reading running time"
20 + #{:real => 0, :user => 0, :sys => 0}
21 + #puts "ERROR READING RUNNING TIME: #{t}"
22 + raise "Error reading running time: #{t}"
20 end
23 end
21 end
24 end
22
25
23 def compile_box(source,bin)
26 def compile_box(source,bin)
24 - system("gcc #{source} -o #{bin}")
27 + system("g++ #{source} -o #{bin}")
25 end
28 end
26
29
27 if ARGV.length < 2 || ARGV.length > 3
30 if ARGV.length < 2 || ARGV.length > 3
28 puts "Usage: run <language> <test-num> [<program-name>]"
31 puts "Usage: run <language> <test-num> [<program-name>]"
29 exit(127)
32 exit(127)
30 end
33 end
31
34
32 language = ARGV[0]
35 language = ARGV[0]
33 test_num = ARGV[1].to_i
36 test_num = ARGV[1].to_i
34 if ARGV.length > 2
37 if ARGV.length > 2
35 program_name = ARGV[2]
38 program_name = ARGV[2]
36 else
39 else
37 program_name = "a.out"
40 program_name = "a.out"
38 end
41 end
39
42
40 problem_home = ENV['PROBLEM_HOME']
43 problem_home = ENV['PROBLEM_HOME']
41 require "#{problem_home}/script/test_dsl.rb"
44 require "#{problem_home}/script/test_dsl.rb"
42 load "#{problem_home}/test_cases/all_tests.cfg"
45 load "#{problem_home}/test_cases/all_tests.cfg"
43 problem = Problem.get_instance
46 problem = Problem.get_instance
44
47
45 if problem.well_formed? == false
48 if problem.well_formed? == false
46 log "The problem specification is not well formed."
49 log "The problem specification is not well formed."
47 exit(127)
50 exit(127)
48 end
51 end
49
52
50 # Check if the test number is okay.
53 # Check if the test number is okay.
51 if test_num <= 0 || test_num > problem.num_tests
54 if test_num <= 0 || test_num > problem.num_tests
52 log "You have specified a wrong test number."
55 log "You have specified a wrong test number."
53 exit(127)
56 exit(127)
54 end
57 end
55
58
56 #####################################
59 #####################################
57 # Set the relavant file names here. #
60 # Set the relavant file names here. #
58 #####################################
61 #####################################
59
62
60 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
63 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
61
64
62 #####################################
65 #####################################
63
66
64 time_limit = problem.get_time_limit test_num
67 time_limit = problem.get_time_limit test_num
65 mem_limit = problem.get_mem_limit(test_num) * 1024
68 mem_limit = problem.get_mem_limit(test_num) * 1024
66
69
67 # Copy the input file.
70 # Copy the input file.
68 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
71 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
69
72
70 - time_output_format = "%Er%Uu%Ss"
71 -
72 # check if box is there, if not, compile it!
73 # check if box is there, if not, compile it!
73 if !File.exists?("#{problem_home}/script/box")
74 if !File.exists?("#{problem_home}/script/box")
74 log "WARNING: Compiling box: to increase efficiency, it should be compile manually"
75 log "WARNING: Compiling box: to increase efficiency, it should be compile manually"
75 - compile_box("#{problem_home}/script/box.c",
76 + compile_box("#{problem_home}/script/box.cc",
76 "#{problem_home}/script/box")
77 "#{problem_home}/script/box")
77 end
78 end
78
79
79 # Hide PROBLEM_HOME
80 # Hide PROBLEM_HOME
80 ENV['PROBLEM_HOME'] = nil
81 ENV['PROBLEM_HOME'] = nil
81
82
82 # Run the program.
83 # Run the program.
83 - run_command = "/usr/bin/time -f \"#{time_output_format}\" 2>run_result #{problem_home}/script/box -a 2 -f -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name}"
84 + #run_command = "/usr/bin/time -f \"#{time_output_format}\" 2>run_result #{problem_home}/script/box_new -a 2 -f -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name}"
85 + run_command = "#{problem_home}/script/box -a 2 -f -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name} 2>run_result"
84 log "Running test #{test_num}..."
86 log "Running test #{test_num}..."
85 log run_command
87 log run_command
86 log
88 log
87 system(run_command)
89 system(run_command)
88
90
89 # Restore PROBLEM_HOME
91 # Restore PROBLEM_HOME
90 ENV['PROBLEM_HOME'] = problem_home
92 ENV['PROBLEM_HOME'] = problem_home
91
93
92 # Create the result file.
94 # Create the result file.
93 result_file = File.new("result", "w")
95 result_file = File.new("result", "w")
94 comment_file = File.new("comment", "w")
96 comment_file = File.new("comment", "w")
95
97
96 # Check if the program actually produced any output.
98 # Check if the program actually produced any output.
97 run_result_file = File.new("run_result", "r")
99 run_result_file = File.new("run_result", "r")
98 run_result = run_result_file.readlines
100 run_result = run_result_file.readlines
99 run_result_file.close
101 run_result_file.close
100 - time_elapsed = run_result[run_result.length-1]
102 +
101 - running_time = extract_time(time_elapsed)
103 + run_stat = run_result[run_result.length-1]
104 + running_time = extract_time(run_stat)
102
105
103 report = lambda{ |status, points, comment|
106 report = lambda{ |status, points, comment|
104 result_file.write status.strip
107 result_file.write status.strip
105 result_file.write "\n"
108 result_file.write "\n"
106 result_file.write points.to_s.strip
109 result_file.write points.to_s.strip
107 result_file.write "\n"
110 result_file.write "\n"
108 - result_file.write time_elapsed.strip
111 + result_file.write run_stat.strip
109 result_file.write "\n"
112 result_file.write "\n"
110 result_file.close
113 result_file.close
111 `rm run_result`
114 `rm run_result`
112 # `rm output.txt` --- keep the output
115 # `rm output.txt` --- keep the output
113
116
114 comment_file.write comment
117 comment_file.write comment
118 +
119 + # added for debuggin --- jittat
115 comment_file.write "--run-result--\n"
120 comment_file.write "--run-result--\n"
116 run_result.each do |l|
121 run_result.each do |l|
117 comment_file.write l
122 comment_file.write l
118 end
123 end
124 +
119 comment_file.close
125 comment_file.close
120
126
121 log "Done!"
127 log "Done!"
122 exit(0)
128 exit(0)
123 }
129 }
124
130
125 if run_result[0][0,2] != "OK"
131 if run_result[0][0,2] != "OK"
126 log "There was a runtime error."
132 log "There was a runtime error."
127 report.call(run_result[0], 0, "No comment.\n")
133 report.call(run_result[0], 0, "No comment.\n")
128 end
134 end
129
135
130 if running_time[:user].to_f + running_time[:sys].to_f > time_limit
136 if running_time[:user].to_f + running_time[:sys].to_f > time_limit
131 log "Time limit exceeded."
137 log "Time limit exceeded."
132 report.call("Time limit exceeded", 0, "No comment.\n")
138 report.call("Time limit exceeded", 0, "No comment.\n")
133 end
139 end
134
140
135 # Run 'check' to evaluate the output.
141 # Run 'check' to evaluate the output.
136 #puts "There was no runtime error. Proceed to checking the output."
142 #puts "There was no runtime error. Proceed to checking the output."
137 check_command = "#{problem_home}/script/check #{language} #{test_num}"
143 check_command = "#{problem_home}/script/check #{language} #{test_num}"
138 log "Checking the output..."
144 log "Checking the output..."
139 log check_command
145 log check_command
140 if not system(check_command)
146 if not system(check_command)
141 log "Problem with check script"
147 log "Problem with check script"
142 report.call("Incorrect",0,"Check script error.\n")
148 report.call("Incorrect",0,"Check script error.\n")
143 exit(127)
149 exit(127)
144 end
150 end
145
151
146 check_file = File.new("check_result", "r")
152 check_file = File.new("check_result", "r")
147 check_file_lines = check_file.readlines
153 check_file_lines = check_file.readlines
148
154
149 report.call(check_file_lines[0], check_file_lines[1], "No comment.\n")
155 report.call(check_file_lines[0], check_file_lines[1], "No comment.\n")
@@ -206,87 +206,111
206 :with => 'in1.txt',
206 :with => 'in1.txt',
207 :and_report => {
207 :and_report => {
208 :graded_at= => nil,
208 :graded_at= => nil,
209 :compiler_message= => '',
209 :compiler_message= => '',
210 :grader_comment= => '',
210 :grader_comment= => '',
211 :running_stat= => nil,
211 :running_stat= => nil,
212 :output_file_name= => nil,
212 :output_file_name= => nil,
213 :running_time= => nil,
213 :running_time= => nil,
214 :exit_status= => nil,
214 :exit_status= => nil,
215 :memory_usage= => nil,
215 :memory_usage= => nil,
216 :save => nil})
216 :save => nil})
217 File.exists?(@config.user_result_dir + "/test_request/test_normal/test_cases/1/input-1.txt").should be_false
217 File.exists?(@config.user_result_dir + "/test_request/test_normal/test_cases/1/input-1.txt").should be_false
218 end
218 end
219
219
220 it "should compile test request with error and report compilation error" do
220 it "should compile test request with error and report compilation error" do
221 problem = stub(Problem,
221 problem = stub(Problem,
222 :id => 1, :name => 'test_normal')
222 :id => 1, :name => 'test_normal')
223 grader_should(:grade => 'test1_compile_error.c',
223 grader_should(:grade => 'test1_compile_error.c',
224 :on => problem,
224 :on => problem,
225 :with => 'in1.txt',
225 :with => 'in1.txt',
226 :and_report => {
226 :and_report => {
227 :graded_at= => nil,
227 :graded_at= => nil,
228 :compiler_message= => /.+/,
228 :compiler_message= => /.+/,
229 :grader_comment= => /[Cc]ompil.*error/,
229 :grader_comment= => /[Cc]ompil.*error/,
230 :running_stat= => '',
230 :running_stat= => '',
231 :save => nil})
231 :save => nil})
232 end
232 end
233
233
234 it "should report exit status" do
234 it "should report exit status" do
235 problem = stub(Problem,
235 problem = stub(Problem,
236 :id => 1, :name => 'test_normal')
236 :id => 1, :name => 'test_normal')
237 grader_should(:grade => 'add_nonzero_exit_status.c',
237 grader_should(:grade => 'add_nonzero_exit_status.c',
238 :on => problem,
238 :on => problem,
239 :with => 'in1.txt',
239 :with => 'in1.txt',
240 :and_report => {
240 :and_report => {
241 :graded_at= => nil,
241 :graded_at= => nil,
242 :compiler_message= => '',
242 :compiler_message= => '',
243 :grader_comment= => '',
243 :grader_comment= => '',
244 :running_stat= => /[Ee]xit.*status.*10.*0\.0 sec/m,
244 :running_stat= => /[Ee]xit.*status.*10.*0\.0 sec/m,
245 :output_file_name= => lambda { |fname|
245 :output_file_name= => lambda { |fname|
246 File.exists?(fname).should be_true
246 File.exists?(fname).should be_true
247 },
247 },
248 :running_time= => nil,
248 :running_time= => nil,
249 :exit_status= => /10/,
249 :exit_status= => /10/,
250 :memory_usage= => nil,
250 :memory_usage= => nil,
251 :save => nil})
251 :save => nil})
252 end
252 end
253
253
254 + it "should produce running statistics for normal submission" do
255 + problem = stub(Problem,
256 + :id => 1, :name => 'test_normal')
257 + grader_should(:grade => 'test_run_stat.c',
258 + :on => problem,
259 + :with => 'in1.txt',
260 + :and_report => {
261 + :graded_at= => nil,
262 + :compiler_message= => '',
263 + :grader_comment= => '',
264 + :running_stat= => nil,
265 + :output_file_name= => lambda { |fname|
266 + File.exists?(fname).should be_true
267 + },
268 + :running_time= => lambda { |t|
269 + (t>=0.14) and (t<=0.16)
270 + },
271 + :exit_status= => nil,
272 + :memory_usage= => lambda { |s|
273 + (s>=500) and (s<=1000)
274 + },
275 + :save => nil})
276 + end
277 +
254 protected
278 protected
255 def grader_should(args)
279 def grader_should(args)
256 @user1 = stub(User,
280 @user1 = stub(User,
257 :id => 1, :login => 'user1')
281 :id => 1, :login => 'user1')
258
282
259 problem = args[:on]
283 problem = args[:on]
260 input_file = @config.test_request_input_base_dir + "/" + args[:with]
284 input_file = @config.test_request_input_base_dir + "/" + args[:with]
261
285
262 submission =
286 submission =
263 create_submission_from_file(1, @user1, args[:on], args[:grade])
287 create_submission_from_file(1, @user1, args[:on], args[:grade])
264
288
265 test_request = stub(TestRequest,
289 test_request = stub(TestRequest,
266 :id => 1,
290 :id => 1,
267 :user => @user1,
291 :user => @user1,
268 :problem => problem,
292 :problem => problem,
269 :submission => submission,
293 :submission => submission,
270 :input_file_name => input_file,
294 :input_file_name => input_file,
271 :language => submission.language,
295 :language => submission.language,
272 :problem_name => problem.name)
296 :problem_name => problem.name)
273
297
274 expectations = args[:and_report]
298 expectations = args[:and_report]
275
299
276 expectations.each do |key,val|
300 expectations.each do |key,val|
277 if val==nil
301 if val==nil
278 test_request.should_receive(key)
302 test_request.should_receive(key)
279 elsif val.class == Proc
303 elsif val.class == Proc
280 test_request.should_receive(key) { |fname|
304 test_request.should_receive(key) { |fname|
281 val.call(fname)
305 val.call(fname)
282 }
306 }
283 else
307 else
284 test_request.should_receive(key).with(val)
308 test_request.should_receive(key).with(val)
285 end
309 end
286 end
310 end
287
311
288 @engine.grade(test_request)
312 @engine.grade(test_request)
289 end
313 end
290
314
291 end
315 end
292
316
deleted file
This diff has been collapsed as it changes many lines, (557 lines changed) Show them Hide them
You need to be logged in to leave comments. Login now