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 +
@@ -111,78 +111,87
111 111 def read_result(test_result_dir)
112 112 # TODO:
113 113 cmp_msg_fname = "#{test_result_dir}/compiler_message"
114 114 cmp_file = File.open(cmp_msg_fname)
115 115 cmp_msg = cmp_file.read
116 116 cmp_file.close
117 117
118 118 result_file_name = "#{test_result_dir}/1/result"
119 119
120 120 if File.exists?(result_file_name)
121 121 output_file_name = "#{test_result_dir}/1/output.txt"
122 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 125 return {
126 126 :output_file_name => output_file_name,
127 127 :running_stat => stat,
128 128 :comment => "",
129 129 :cmp_msg => cmp_msg}
130 130 else
131 131 return {
132 132 :running_stat => nil,
133 133 :comment => "Compilation error",
134 134 :cmp_msg => cmp_msg}
135 135 end
136 136 end
137 137
138 - def format_running_stat(results)
139 - running_time_line = results[-1]
138 + def extract_running_stat(results)
139 + running_stat_line = results[-1]
140 140
141 141 # extract exit status line
142 142 run_stat = ""
143 143 if !(/[Cc]orrect/.match(results[0]))
144 144 run_stat = results[0].chomp
145 145 else
146 146 run_stat = 'Program exited normally'
147 147 end
148 148
149 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 151 seconds = (res[1].to_f + res[2].to_f)
152 152 time_stat = "Time used: #{seconds} sec."
153 153 else
154 154 seconds = nil
155 155 time_stat = "Time used: n/a sec."
156 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 165 return {
158 166 :msg => "#{run_stat}\n#{time_stat}",
159 167 :running_time => seconds,
160 - :exit_status => run_stat
168 + :exit_status => run_stat,
169 + :memory_usage => memory_used
161 170 }
162 171 end
163 172
164 173 def save_result(test_request,result)
165 174 if result[:output_file_name]!=nil
166 175 test_request.output_file_name = link_output_file(test_request,
167 176 result[:output_file_name])
168 177 end
169 178 test_request.graded_at = Time.now
170 179 test_request.compiler_message = (result[:cmp_msg] or '')
171 180 test_request.grader_comment = (result[:comment] or '')
172 181 if result[:running_stat]!=nil
173 182 test_request.running_stat = (result[:running_stat][:msg] or '')
174 183 test_request.running_time = (result[:running_stat][:running_time] or nil)
175 - test_request.exit_status = (result[:running_stat][:exit_status])
176 - test_request.memory_usage = nil # should be added later
184 + test_request.exit_status = result[:running_stat][:exit_status]
185 + test_request.memory_usage = result[:running_stat][:memory_usage]
177 186 else
178 187 test_request.running_stat = ''
179 188 end
180 189 test_request.save
181 190 end
182 191
183 192 protected
184 193 def link_output_file(test_request, fname)
185 194 target_file_name = random_output_file_name(test_request.user,
186 195 test_request.problem)
187 196 FileUtils.mkdir_p(File.dirname(target_file_name))
188 197 cmd = "ln -s #{fname} #{target_file_name}"
@@ -4,33 +4,36
4 4 if ENV['TALKATIVE']!=nil
5 5 puts str
6 6 end
7 7 if ENV['GRADER_LOGGING']!=nil
8 8 log_fname = ENV['GRADER_LOGGING']
9 9 fp = File.open(log_fname,"a")
10 10 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
11 11 fp.close
12 12 end
13 13 end
14 14
15 15 def extract_time(t)
16 + # puts "TIME: #{t}"
16 17 if (result=/^(.*)r(.*)u(.*)s/.match(t))
17 18 {:real => result[1], :user => result[2], :sys => result[3]}
18 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 23 end
21 24 end
22 25
23 26 def compile_box(source,bin)
24 - system("gcc #{source} -o #{bin}")
27 + system("g++ #{source} -o #{bin}")
25 28 end
26 29
27 30 if ARGV.length < 2 || ARGV.length > 3
28 31 puts "Usage: run <language> <test-num> [<program-name>]"
29 32 exit(127)
30 33 end
31 34
32 35 language = ARGV[0]
33 36 test_num = ARGV[1].to_i
34 37 if ARGV.length > 2
35 38 program_name = ARGV[2]
36 39 else
@@ -58,73 +61,76
58 61 #####################################
59 62
60 63 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
61 64
62 65 #####################################
63 66
64 67 time_limit = problem.get_time_limit test_num
65 68 mem_limit = problem.get_mem_limit(test_num) * 1024
66 69
67 70 # Copy the input file.
68 71 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
69 72
70 - time_output_format = "%Er%Uu%Ss"
71 -
72 73 # check if box is there, if not, compile it!
73 74 if !File.exists?("#{problem_home}/script/box")
74 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 77 "#{problem_home}/script/box")
77 78 end
78 79
79 80 # Hide PROBLEM_HOME
80 81 ENV['PROBLEM_HOME'] = nil
81 82
82 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 86 log "Running test #{test_num}..."
85 87 log run_command
86 88 log
87 89 system(run_command)
88 90
89 91 # Restore PROBLEM_HOME
90 92 ENV['PROBLEM_HOME'] = problem_home
91 93
92 94 # Create the result file.
93 95 result_file = File.new("result", "w")
94 96 comment_file = File.new("comment", "w")
95 97
96 98 # Check if the program actually produced any output.
97 99 run_result_file = File.new("run_result", "r")
98 100 run_result = run_result_file.readlines
99 101 run_result_file.close
100 - time_elapsed = run_result[run_result.length-1]
101 - running_time = extract_time(time_elapsed)
102 +
103 + run_stat = run_result[run_result.length-1]
104 + running_time = extract_time(run_stat)
102 105
103 106 report = lambda{ |status, points, comment|
104 107 result_file.write status.strip
105 108 result_file.write "\n"
106 109 result_file.write points.to_s.strip
107 110 result_file.write "\n"
108 - result_file.write time_elapsed.strip
111 + result_file.write run_stat.strip
109 112 result_file.write "\n"
110 113 result_file.close
111 114 `rm run_result`
112 115 # `rm output.txt` --- keep the output
113 116
114 117 comment_file.write comment
118 +
119 + # added for debuggin --- jittat
115 120 comment_file.write "--run-result--\n"
116 121 run_result.each do |l|
117 122 comment_file.write l
118 123 end
124 +
119 125 comment_file.close
120 126
121 127 log "Done!"
122 128 exit(0)
123 129 }
124 130
125 131 if run_result[0][0,2] != "OK"
126 132 log "There was a runtime error."
127 133 report.call(run_result[0], 0, "No comment.\n")
128 134 end
129 135
130 136 if running_time[:user].to_f + running_time[:sys].to_f > time_limit
@@ -242,24 +242,48
242 242 :compiler_message= => '',
243 243 :grader_comment= => '',
244 244 :running_stat= => /[Ee]xit.*status.*10.*0\.0 sec/m,
245 245 :output_file_name= => lambda { |fname|
246 246 File.exists?(fname).should be_true
247 247 },
248 248 :running_time= => nil,
249 249 :exit_status= => /10/,
250 250 :memory_usage= => nil,
251 251 :save => nil})
252 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 278 protected
255 279 def grader_should(args)
256 280 @user1 = stub(User,
257 281 :id => 1, :login => 'user1')
258 282
259 283 problem = args[:on]
260 284 input_file = @config.test_request_input_base_dir + "/" + args[:with]
261 285
262 286 submission =
263 287 create_submission_from_file(1, @user1, args[:on], args[:grade])
264 288
265 289 test_request = stub(TestRequest,
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