Description:
[grader] box.cc now reports running status when die git-svn-id: http://theory.cpe.ku.ac.th/grader/judge/trunk/scripts@172 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

r46:31f3935d33aa - - 2 files changed: 61 inserted, 47 deleted

@@ -9,141 +9,159
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 32 static int 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
48 48 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0
49 49 /* glibc 2.1 or newer -> has lseek64 */
50 50 #define long_seek(f,o,w) lseek64(f,o,w)
51 51 #else
52 52 /* Touching clandestine places in glibc */
53 53 extern loff_t llseek(int fd, loff_t pos, int whence);
54 54 #define long_seek(f,o,w) llseek(f,o,w)
55 55 #endif
56 56
57 + int max_mem_used = 0;
58 +
59 + void print_running_stat(double wall_time,
60 + double user_time,
61 + double system_time,
62 + int mem_usage)
63 + {
64 + fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n",
65 + wall_time, user_time, system_time, mem_usage);
66 + }
67 +
57 68 static void NONRET
58 69 box_exit(void)
59 70 {
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 - }
71 + if (box_pid > 0) {
72 + if (is_ptraced)
73 + ptrace(PTRACE_KILL, box_pid);
74 + kill(-box_pid, SIGKILL);
75 + kill(box_pid, SIGKILL);
76 + }
69 77
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 - }
78 + struct timeval total;
79 + int wall;
80 + struct rusage rus;
81 + int stat;
82 + pid_t p;
83 +
84 + // wait so that we can get information
85 + p = wait4(box_pid, &stat, WUNTRACED, &rus);
86 + if (p < 0) {
87 + fprintf(stderr,"wait4: error\n");
88 + print_running_stat(0,0,0,max_mem_used);
89 + } else if (p != box_pid) {
90 + fprintf(stderr,"wait4: unknown pid %d exited!\n", p);
91 + print_running_stat(0,0,0,max_mem_used);
92 + } else {
93 + if (!WIFEXITED(stat))
94 + fprintf(stderr,"wait4: unknown status\n");
95 + struct timeval total;
96 + int wall;
97 + wall = time(NULL) - start_time;
98 + timeradd(&rus.ru_utime, &rus.ru_stime, &total);
99 +
100 + print_running_stat((double)wall,
101 + (double) rus.ru_utime.tv_sec +
102 + ((double) rus.ru_utime.tv_usec/1000000.0),
103 + (double) rus.ru_stime.tv_sec +
104 + ((double) rus.ru_stime.tv_usec/1000000.0),
105 + max_mem_used);
106 + }
107 + exit(1);
80 108 }
81 109
82 110 static void NONRET __attribute__((format(printf,1,2)))
83 111 die(char *msg, ...)
84 112 {
85 113 va_list args;
86 114 va_start(args, msg);
87 115 vfprintf(stderr, msg, args);
88 116 fputc('\n', stderr);
89 117 box_exit();
90 118 }
91 119
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 120 static void __attribute__((format(printf,1,2)))
103 121 log(char *msg, ...)
104 122 {
105 123 va_list args;
106 124 va_start(args, msg);
107 125 if (verbose)
108 126 {
109 127 vfprintf(stderr, msg, args);
110 128 fflush(stderr);
111 129 }
112 130 va_end(args);
113 131 }
114 132
115 133 static void
116 134 valid_filename(unsigned long addr)
117 135 {
118 136 char namebuf[4096], *p, *end;
119 137 static int mem_fd;
120 138
121 139 if (!file_access)
122 140 die("File access forbidden.");
123 141 if (file_access >= 9)
124 142 return;
125 143
126 144 if (!mem_fd)
127 145 {
128 146 sprintf(namebuf, "/proc/%d/mem", (int) box_pid);
129 147 mem_fd = open(namebuf, O_RDONLY);
130 148 if (mem_fd < 0)
131 149 die("open(%s): %m", namebuf);
132 150 }
133 151 p = end = namebuf;
134 152 do
135 153 {
136 154 if (p >= end)
137 155 {
138 156 int remains = PAGE_SIZE - (addr & (PAGE_SIZE-1));
139 157 int l = namebuf + sizeof(namebuf) - end;
140 158 if (l > remains)
141 159 l = remains;
142 160 if (!l)
143 161 die("Access to file with name too long.");
144 162 if (long_seek(mem_fd, addr, SEEK_SET) < 0)
145 163 die("long_seek(mem): %m");
146 164 remains = read(mem_fd, end, l);
147 165 if (remains < 0)
148 166 die("read(mem): %m");
149 167 if (!remains)
@@ -304,102 +322,100
304 322 /* Interrupts are fatal, so no synchronization requirements. */
305 323 die("Interrupted.");
306 324 }
307 325
308 326 static void
309 327 check_timeout(void)
310 328 {
311 329 int sec;
312 330
313 331 if (use_wall_clock)
314 332 sec = time(NULL) - start_time;
315 333 else
316 334 {
317 335 char buf[4096], *x;
318 336 int c, utime, stime;
319 337 static int proc_status_fd;
320 338 if (!proc_status_fd)
321 339 {
322 340 sprintf(buf, "/proc/%d/stat", (int) box_pid);
323 341 proc_status_fd = open(buf, O_RDONLY);
324 342 if (proc_status_fd < 0)
325 343 die("open(%s): %m", buf);
326 344 }
327 345 lseek(proc_status_fd, 0, SEEK_SET);
328 346 if ((c = read(proc_status_fd, buf, sizeof(buf)-1)) < 0)
329 347 die("read on /proc/$pid/stat: %m");
330 348 if (c >= (int) sizeof(buf) - 1)
331 349 die("/proc/$pid/stat too long");
332 350 buf[c] = 0;
333 351 x = buf;
334 352 while (*x && *x != ' ')
335 353 x++;
336 354 while (*x == ' ')
337 355 x++;
338 356 if (*x++ != '(')
339 357 die("proc syntax error 1");
340 358 while (*x && (*x != ')' || x[1] != ' '))
341 359 x++;
342 360 while (*x == ')' || *x == ' ')
343 361 x++;
344 362 if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2)
345 363 die("proc syntax error 2");
346 364 //printf("%s - %d\n",x,ticks_per_sec);
347 365 sec = (utime + stime + ticks_per_sec-1)/ticks_per_sec;
348 366 }
349 367 if (verbose > 1)
350 368 fprintf(stderr, "[timecheck: %d seconds]\n", sec);
351 369 if (sec > timeout) {
352 - die_report("Time limit exceeded.");
370 + die("Time limit exceeded.");
353 371 }
354 372 }
355 373
356 - int max_mem_used = 0;
357 -
358 374 static void
359 375 check_memory_usage()
360 376 {
361 377 char proc_fname[100];
362 378 sprintf(proc_fname,"/proc/%d/statm",box_pid);
363 379 //printf("proc fname: %s\n",proc_fname);
364 380 FILE *fp = fopen(proc_fname,"r");
365 381 if(fp!=NULL) {
366 382 char line[1000];
367 383 fgets(line,999,fp);
368 384 //printf("%s\n",line);
369 385 int m;
370 386
371 387 if(sscanf(line,"%d",&m)==1)
372 388 if(m>max_mem_used)
373 389 max_mem_used = m;
374 390
375 391 fclose(fp);
376 392 }
377 393 }
378 394
379 395 static void
380 396 boxkeeper(void)
381 397 {
382 398 int syscall_count = 0;
383 399 struct sigaction sa;
384 400
385 401 is_ptraced = 1;
386 402 bzero(&sa, sizeof(sa));
387 403 sa.sa_handler = signal_int;
388 404 sigaction(SIGINT, &sa, NULL);
389 405 start_time = time(NULL);
390 406 ticks_per_sec = sysconf(_SC_CLK_TCK);
391 407 if (ticks_per_sec <= 0)
392 408 die("Invalid ticks_per_sec!");
393 409
394 410 check_memory_usage();
395 411
396 412 sa.sa_handler = signal_alarm;
397 413 sigaction(SIGALRM, &sa, NULL);
398 414 //alarm(1);
399 415
400 416 struct itimerval val;
401 417 val.it_interval.tv_sec = 0;
402 418 val.it_interval.tv_usec = 50000;
403 419 val.it_value.tv_sec = 0;
404 420 val.it_value.tv_usec = 50000;
405 421 setitimer(ITIMER_REAL,&val,NULL);
@@ -407,129 +423,127
407 423 /*
408 424 --- add alarm handler no matter what..
409 425 if (timeout)
410 426 {
411 427 sa.sa_handler = signal_alarm;
412 428 sigaction(SIGALRM, &sa, NULL);
413 429 alarm(1);
414 430 }
415 431 */
416 432
417 433 for(;;)
418 434 {
419 435 struct rusage rus;
420 436 int stat;
421 437 pid_t p;
422 438
423 439 if (timer_tick)
424 440 {
425 441 check_timeout();
426 442 check_memory_usage();
427 443 timer_tick = 0;
428 444 }
429 445 p = wait4(box_pid, &stat, WUNTRACED, &rus);
430 446
431 447 if (p < 0)
432 448 {
433 449 if (errno == EINTR)
434 450 continue;
435 451 die("wait4: %m");
436 452 }
437 453 if (p != box_pid)
438 454 die("wait4: unknown pid %d exited!", p);
439 455 if (WIFEXITED(stat))
440 456 {
441 457 struct timeval total;
442 458 int wall;
443 459 wall = time(NULL) - start_time;
444 460 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
445 461
446 462 box_pid = 0;
447 463 if (WEXITSTATUS(stat))
448 464 fprintf(stderr,"Exited with error status %d.\n", WEXITSTATUS(stat));
449 465 else if ((use_wall_clock ? wall : total.tv_sec) > timeout)
450 466 fprintf(stderr,"Time limit exceeded.\n");
451 467 else
452 468 // report OK and statistics
453 469 fprintf(stderr,"OK\n");
454 470
455 - fprintf(stderr,"%dr%.4lfu%.4lfs%dm\n",
456 - wall,
457 - (double) rus.ru_utime.tv_sec +
458 - ((double) rus.ru_utime.tv_usec/1000000.0),
459 - (double) rus.ru_stime.tv_sec +
460 - ((double) rus.ru_stime.tv_usec/1000000.0),
461 - max_mem_used);
471 + print_running_stat((double) wall,
472 + (double) rus.ru_utime.tv_sec +
473 + ((double) rus.ru_utime.tv_usec/1000000.0),
474 + (double) rus.ru_stime.tv_sec +
475 + ((double) rus.ru_stime.tv_usec/1000000.0),
476 + max_mem_used);
462 477 /*
463 478 (%.4lf sec real (%d), %d sec wall, %d syscalls, %d kb)\n",
464 479 (double) total.tv_sec + ((double)total.tv_usec / 1000000.0),
465 480 (int) total.tv_usec,
466 481 wall,
467 482 syscall_count,
468 483 max_mem_used);
469 484 */
470 485 exit(0);
471 486 }
472 487 if (WIFSIGNALED(stat))
473 488 {
474 489 box_pid = 0;
475 490 fprintf(stderr,"Caught fatal signal %d.\n", WTERMSIG(stat));
476 491
477 492 struct timeval total;
478 493 int wall;
479 494 wall = time(NULL) - start_time;
480 495 timeradd(&rus.ru_utime, &rus.ru_stime, &total);
481 - fprintf(stderr,"%dr%.4lfu%.4lfs%dm\n",
482 - wall,
483 - (double) rus.ru_utime.tv_sec +
484 - ((double) rus.ru_utime.tv_usec/1000000.0),
485 - (double) rus.ru_stime.tv_sec +
486 - ((double) rus.ru_stime.tv_usec/1000000.0),
487 - max_mem_used);
496 + print_running_stat((double) wall,
497 + (double) rus.ru_utime.tv_sec +
498 + ((double) rus.ru_utime.tv_usec/1000000.0),
499 + (double) rus.ru_stime.tv_sec +
500 + ((double) rus.ru_stime.tv_usec/1000000.0),
501 + max_mem_used);
488 502 exit(0);
489 503 }
490 504 if (WIFSTOPPED(stat))
491 505 {
492 506 int sig = WSTOPSIG(stat);
493 507 if (sig == SIGTRAP)
494 508 {
495 509 struct user u;
496 510 static int stop_count = -1;
497 511 if (ptrace(PTRACE_GETREGS, box_pid, NULL, &u) < 0)
498 512 die("ptrace(PTRACE_GETREGS): %m");
499 513 stop_count++;
500 514 if (!stop_count) /* Traceme request */
501 515 log(">> Traceme request caught\n");
502 516 else if (stop_count & 1) /* Syscall entry */
503 517 {
504 518 log(">> Syscall %3ld (%08lx,%08lx,%08lx) ", u.regs.orig_eax, u.regs.ebx, u.regs.ecx, u.regs.edx);
505 519 syscall_count++;
506 520 if (!valid_syscall(&u))
507 521 {
508 522 /*
509 523 * Unfortunately, PTRACE_KILL kills _after_ the syscall completes,
510 524 * so we have to change it to something harmless (e.g., an undefined
511 525 * syscall) and make the program continue.
512 526 */
513 527 unsigned int sys = u.regs.orig_eax;
514 528 u.regs.orig_eax = 0xffffffff;
515 529 if (ptrace(PTRACE_SETREGS, box_pid, NULL, &u) < 0)
516 530 die("ptrace(PTRACE_SETREGS): %m");
517 531 die("Forbidden syscall %d.", sys);
518 532 }
519 533 }
520 534 else /* Syscall return */
521 535 log("= %ld\n", u.regs.eax);
522 536 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
523 537 }
524 538 else if (sig != SIGSTOP && sig != SIGXCPU && sig != SIGXFSZ)
525 539 {
526 540 log(">> Signal %d\n", sig);
527 541 ptrace(PTRACE_SYSCALL, box_pid, 0, sig);
528 542 }
529 543 else
530 544 die("Received signal %d.", sig);
531 545 }
532 546 else
533 547 die("wait4: unknown status %x, giving up!", stat);
534 548 }
535 549 }
@@ -196,97 +196,97
196 196 :exit_status= => nil,
197 197 :memory_usage= => nil,
198 198 :save => nil})
199 199 end
200 200
201 201 it "should clean up problem directory after running test request" do
202 202 problem = stub(Problem,
203 203 :id => 1, :name => 'test_normal')
204 204 grader_should(:grade => 'test1_correct.c',
205 205 :on => problem,
206 206 :with => 'in1.txt',
207 207 :and_report => {
208 208 :graded_at= => nil,
209 209 :compiler_message= => '',
210 210 :grader_comment= => '',
211 211 :running_stat= => nil,
212 212 :output_file_name= => nil,
213 213 :running_time= => nil,
214 214 :exit_status= => nil,
215 215 :memory_usage= => nil,
216 216 :save => nil})
217 217 File.exists?(@config.user_result_dir + "/test_request/test_normal/test_cases/1/input-1.txt").should be_false
218 218 end
219 219
220 220 it "should compile test request with error and report compilation error" do
221 221 problem = stub(Problem,
222 222 :id => 1, :name => 'test_normal')
223 223 grader_should(:grade => 'test1_compile_error.c',
224 224 :on => problem,
225 225 :with => 'in1.txt',
226 226 :and_report => {
227 227 :graded_at= => nil,
228 228 :compiler_message= => /.+/,
229 229 :grader_comment= => /[Cc]ompil.*error/,
230 230 :running_stat= => '',
231 231 :save => nil})
232 232 end
233 233
234 234 it "should report exit status" do
235 235 problem = stub(Problem,
236 236 :id => 1, :name => 'test_normal')
237 237 grader_should(:grade => 'add_nonzero_exit_status.c',
238 238 :on => problem,
239 239 :with => 'in1.txt',
240 240 :and_report => {
241 241 :graded_at= => nil,
242 242 :compiler_message= => '',
243 243 :grader_comment= => '',
244 - :running_stat= => /[Ee]xit.*status.*10.*0\.0 sec/m,
244 + :running_stat= => /[Ee]xit.*status.*10.*0\.0\d* 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 254 it "should produce running statistics for normal submission" do
255 255 problem = stub(Problem,
256 256 :id => 1, :name => 'test_normal')
257 257 grader_should(:grade => 'test_run_stat.c',
258 258 :on => problem,
259 259 :with => 'in1.txt',
260 260 :and_report => {
261 261 :graded_at= => nil,
262 262 :compiler_message= => '',
263 263 :grader_comment= => '',
264 264 :running_stat= => nil,
265 265 :output_file_name= => lambda { |fname|
266 266 File.exists?(fname).should be_true
267 267 },
268 268 :running_time= => lambda { |t|
269 269 (t>=0.14) and (t<=0.16)
270 270 },
271 271 :exit_status= => nil,
272 272 :memory_usage= => lambda { |s|
273 273 (s>=500) and (s<=1000)
274 274 },
275 275 :save => nil})
276 276 end
277 277
278 278 protected
279 279 def grader_should(args)
280 280 @user1 = stub(User,
281 281 :id => 1, :login => 'user1')
282 282
283 283 problem = args[:on]
284 284 input_file = @config.test_request_input_base_dir + "/" + args[:with]
285 285
286 286 submission =
287 287 create_submission_from_file(1, @user1, args[:on], args[:grade])
288 288
289 289 test_request = stub(TestRequest,
290 290 :id => 1,
291 291 :user => @user1,
292 292 :problem => problem,
You need to be logged in to leave comments. Login now