Description:
merge from algo-bm
Commit status:
[Not Reviewed]
References:
merge java
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r173:aa6e7ff3942c - - 9 files changed: 124 inserted, 34 deleted

@@ -25,53 +25,64
25
25
26 def log_file_name
26 def log_file_name
27 if !File.exists?(config.log_dir)
27 if !File.exists?(config.log_dir)
28 raise "Log directory does not exist: #{config.log_dir}"
28 raise "Log directory does not exist: #{config.log_dir}"
29 end
29 end
30 config.log_dir +
30 config.log_dir +
31 "/#{GRADER_ENV}_#{config.grader_mode}.#{Process.pid}"
31 "/#{GRADER_ENV}_#{config.grader_mode}.#{Process.pid}"
32 end
32 end
33
33
34 def log(str)
34 def log(str)
35 if config.talkative
35 if config.talkative
36 puts str
36 puts str
37 end
37 end
38 if config.logging
38 if config.logging
39 fp = File.open(log_file_name,"a")
39 fp = File.open(log_file_name,"a")
40 fp.puts("GRADER: #{Time.new.strftime("%H:%M")} #{str}")
40 fp.puts("GRADER: #{Time.new.strftime("%H:%M")} #{str}")
41 fp.close
41 fp.close
42 end
42 end
43 end
43 end
44
44
45 def display_manual
45 def display_manual
46 puts <<USAGE
46 puts <<USAGE
47 Grader.
47 Grader.
48 using: (1) grader
48 using: (1) grader
49 - (2) grader environment [mode]
49 + (2) grader environment [mode] [options]
50 (3) grader stop [all|pids-list]
50 (3) grader stop [all|pids-list]
51 (4) grader --help
51 (4) grader --help
52 (1) call grader with environment = 'exam', mode = 'queue'
52 (1) call grader with environment = 'exam', mode = 'queue'
53 (2) possible modes are: 'queue', 'test_request', 'prob', 'sub', 'contest', and 'autonew'
53 (2) possible modes are: 'queue', 'test_request', 'prob', 'sub', 'contest', and 'autonew'
54 + queue: repeatedly check the task queue and grade any available tasks
55 +
56 + prob: re-grade every user latest submission of the specific problem.
57 + the problem name must be specified by the next argument.
58 +
59 + additional options:
60 +
61 + --all-sub re-grade every submissions instead of just the latest submission of each user.
62 + sub: re-grader the specified submission.
63 + The submission ID to be re-graded must be specified by the next argument.
64 +
54 (3) create stop-file to stop running grader in queue mode
65 (3) create stop-file to stop running grader in queue mode
55 (4) You are here.
66 (4) You are here.
56 USAGE
67 USAGE
57 end
68 end
58
69
59 def process_options_and_stop_file
70 def process_options_and_stop_file
60 # The list of options are:
71 # The list of options are:
61 # - stop [all|process ids]
72 # - stop [all|process ids]
62 # -
73 # -
63
74
64 # Process 'help' option
75 # Process 'help' option
65 if (ARGV.length==1) and (/help/.match(ARGV[0]))
76 if (ARGV.length==1) and (/help/.match(ARGV[0]))
66 display_manual
77 display_manual
67 exit(0)
78 exit(0)
68 end
79 end
69
80
70 # Process 'stop' option.
81 # Process 'stop' option.
71 if (ARGV.length >= 1) and (ARGV[0]=='stop')
82 if (ARGV.length >= 1) and (ARGV[0]=='stop')
72 if ARGV.length==1
83 if ARGV.length==1
73 puts "you should specify pid-list or 'all'"
84 puts "you should specify pid-list or 'all'"
74 display_manual
85 display_manual
75 elsif (ARGV.length==2) and (ARGV[1]=='all')
86 elsif (ARGV.length==2) and (ARGV[1]=='all')
76 stop_grader(:all)
87 stop_grader(:all)
77 puts "A global stop file ('stop.all') created."
88 puts "A global stop file ('stop.all') created."
@@ -84,62 +95,66
84 end
95 end
85 exit(0)
96 exit(0)
86 end
97 end
87
98
88 # Check stop file.
99 # Check stop file.
89 if check_stopfile
100 if check_stopfile
90 puts "Stop file exists. Terminated."
101 puts "Stop file exists. Terminated."
91 clear_stopfile
102 clear_stopfile
92 exit(0)
103 exit(0)
93 end
104 end
94
105
95 #default options
106 #default options
96 options = {
107 options = {
97 :mode => 'queue',
108 :mode => 'queue',
98 :environment => 'exam',
109 :environment => 'exam',
99 :dry_run => false,
110 :dry_run => false,
100 }
111 }
101
112
102 # Process mode and environment option
113 # Process mode and environment option
103 if ARGV.length >= 1
114 if ARGV.length >= 1
104 options[:environment] = ARGV.shift
115 options[:environment] = ARGV.shift
105 if ARGV.length >=1
116 if ARGV.length >=1
106 options[:mode] = ARGV.shift
117 options[:mode] = ARGV.shift
107 end
118 end
119 + else
120 + puts 'no argument specified, using default mode and environment.'
108 end
121 end
109
122
110 options[:dry_run] = (ARGV.delete('--dry') != nil)
123 options[:dry_run] = (ARGV.delete('--dry') != nil)
111 if options[:dry_run] and (not ['prob','contest','autonew'].include? options[:mode])
124 if options[:dry_run] and (not ['prob','contest','autonew'].include? options[:mode])
112 puts "Dry run currently works only for 'prob' or 'contest' modes."
125 puts "Dry run currently works only for 'prob' or 'contest' modes."
113 exit(0)
126 exit(0)
114 end
127 end
115
128
116 options[:report] = (ARGV.delete('--report') != nil)
129 options[:report] = (ARGV.delete('--report') != nil)
117 if options[:report] and (not ['prob','contest','autonew'].include? options[:mode])
130 if options[:report] and (not ['prob','contest','autonew'].include? options[:mode])
118 puts "Report currently works only for 'prob' or 'contest' modes."
131 puts "Report currently works only for 'prob' or 'contest' modes."
119 exit(0)
132 exit(0)
120 end
133 end
121
134
135 + options[:all_sub] = (ARGV.delete('--all-sub') != nil)
136 +
122 return options
137 return options
123 end
138 end
124
139
125 class ResultCollector
140 class ResultCollector
126 def initialize
141 def initialize
127 @results = {}
142 @results = {}
128 @problems = {}
143 @problems = {}
129 @users = {}
144 @users = {}
130 end
145 end
131
146
132 def after_save_hook(submission, grading_result)
147 def after_save_hook(submission, grading_result)
133 end
148 end
134
149
135 def save(submission, grading_result)
150 def save(submission, grading_result)
136 user = submission.user
151 user = submission.user
137 problem = submission.problem
152 problem = submission.problem
138 if not @problems.has_key? problem.id
153 if not @problems.has_key? problem.id
139 @problems[problem.id] = problem
154 @problems[problem.id] = problem
140 end
155 end
141 if not @users.has_key? user.id
156 if not @users.has_key? user.id
142 @users[user.id] = user
157 @users[user.id] = user
143 end
158 end
144 @results[[user.id, problem.id]] = grading_result
159 @results[[user.id, problem.id]] = grading_result
145
160
@@ -274,49 +289,49
274
289
275 def grader_grade_problems(grader_proc, options)
290 def grader_grade_problems(grader_proc, options)
276 if options[:report]
291 if options[:report]
277 result_collector = ResultCollector.new
292 result_collector = ResultCollector.new
278 else
293 else
279 result_collector = nil
294 result_collector = nil
280 end
295 end
281
296
282 if options[:dry_run]
297 if options[:dry_run]
283 puts "Running in dry mode"
298 puts "Running in dry mode"
284 end
299 end
285
300
286 prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run],
301 prob_reporter = Grader::SubmissionReporter.new(:dry_run => options[:dry_run],
287 :result_collector => result_collector)
302 :result_collector => result_collector)
288 engine = Grader::Engine.new(:reporter => prob_reporter)
303 engine = Grader::Engine.new(:reporter => prob_reporter)
289 runner = Grader::Runner.new(engine, grader_proc)
304 runner = Grader::Runner.new(engine, grader_proc)
290
305
291 grader_proc.report_active if grader_proc!=nil
306 grader_proc.report_active if grader_proc!=nil
292
307
293 ARGV.each do |prob_name|
308 ARGV.each do |prob_name|
294 prob = Problem.find_by_name(prob_name)
309 prob = Problem.find_by_name(prob_name)
295 if prob==nil
310 if prob==nil
296 puts "cannot find problem: #{prob_name}"
311 puts "cannot find problem: #{prob_name}"
297 else
312 else
298 - runner.grade_problem(prob)
313 + runner.grade_problem(prob,options)
299 end
314 end
300 end
315 end
301
316
302 if options[:report]
317 if options[:report]
303 result_collector.print_report_by_user
318 result_collector.print_report_by_user
304 end
319 end
305 end
320 end
306
321
307 def grader_grade_contests(grader_proc, options)
322 def grader_grade_contests(grader_proc, options)
308 # always use dry run when grading during contest
323 # always use dry run when grading during contest
309 dry_run = options[:dry_run] = true
324 dry_run = options[:dry_run] = true
310
325
311 contest_name = ARGV.shift
326 contest_name = ARGV.shift
312
327
313 contest = Contest.find_by_name(contest_name)
328 contest = Contest.find_by_name(contest_name)
314 if contest==nil
329 if contest==nil
315 puts "cannot find contest: #{contest_name}"
330 puts "cannot find contest: #{contest_name}"
316 exit(0)
331 exit(0)
317 end
332 end
318
333
319 if options[:report]
334 if options[:report]
320 result_collector = ResultCollector.new
335 result_collector = ResultCollector.new
321 else
336 else
322 result_collector = nil
337 result_collector = nil
@@ -357,48 +372,49
357 puts "Grading #{sub_id}"
372 puts "Grading #{sub_id}"
358 begin
373 begin
359 submission = Submission.find(sub_id.to_i)
374 submission = Submission.find(sub_id.to_i)
360 rescue ActiveRecord::RecordNotFound
375 rescue ActiveRecord::RecordNotFound
361 puts "Submission #{sub_id} not found"
376 puts "Submission #{sub_id} not found"
362 submission = nil
377 submission = nil
363 end
378 end
364
379
365 if submission!=nil
380 if submission!=nil
366 runner.grade_submission(submission)
381 runner.grade_submission(submission)
367 end
382 end
368 end
383 end
369 end
384 end
370
385
371 #########################################
386 #########################################
372 # main program
387 # main program
373 #########################################
388 #########################################
374
389
375 options = process_options_and_stop_file
390 options = process_options_and_stop_file
376 GRADER_ENV = options[:environment]
391 GRADER_ENV = options[:environment]
377 grader_mode = options[:mode]
392 grader_mode = options[:mode]
378 dry_run = options[:dry_run]
393 dry_run = options[:dry_run]
379
394
380 puts "environment: #{GRADER_ENV}"
395 puts "environment: #{GRADER_ENV}"
396 + puts "grader mode: #{grader_mode}"
381 require File.join(File.dirname(__FILE__),'config/environment')
397 require File.join(File.dirname(__FILE__),'config/environment')
382
398
383 # add grader_mode to config
399 # add grader_mode to config
384 # this is needed because method log needs it. TODO: clean this up
400 # this is needed because method log needs it. TODO: clean this up
385 class << config
401 class << config
386 attr_accessor :grader_mode
402 attr_accessor :grader_mode
387 end
403 end
388 config.grader_mode = grader_mode
404 config.grader_mode = grader_mode
389
405
390 # reading rails environment
406 # reading rails environment
391 log 'Reading rails environment'
407 log 'Reading rails environment'
392
408
393 RAILS_ENV = config.rails_env
409 RAILS_ENV = config.rails_env
394 require RAILS_ROOT + '/config/environment'
410 require RAILS_ROOT + '/config/environment'
395
411
396 # register grader process
412 # register grader process
397 if config.report_grader
413 if config.report_grader
398 grader_proc = GraderProcess.register(config.grader_hostname,
414 grader_proc = GraderProcess.register(config.grader_hostname,
399 Process.pid,
415 Process.pid,
400 grader_mode)
416 grader_mode)
401 else
417 else
402 grader_proc = nil
418 grader_proc = nil
403 end
419 end
404
420
@@ -4,58 +4,63
4
4
5 module Grader
5 module Grader
6
6
7 class Runner
7 class Runner
8
8
9 def initialize(engine, grader_process=nil)
9 def initialize(engine, grader_process=nil)
10 @engine = engine
10 @engine = engine
11 @grader_process = grader_process
11 @grader_process = grader_process
12 end
12 end
13
13
14 def grade_oldest_task
14 def grade_oldest_task
15 task = Task.get_inqueue_and_change_status(Task::STATUS_GRADING)
15 task = Task.get_inqueue_and_change_status(Task::STATUS_GRADING)
16 if task!=nil
16 if task!=nil
17 @grader_process.report_active(task) if @grader_process!=nil
17 @grader_process.report_active(task) if @grader_process!=nil
18
18
19 submission = Submission.find(task.submission_id)
19 submission = Submission.find(task.submission_id)
20 @engine.grade(submission)
20 @engine.grade(submission)
21 task.status_complete!
21 task.status_complete!
22 @grader_process.report_inactive(task) if @grader_process!=nil
22 @grader_process.report_inactive(task) if @grader_process!=nil
23 end
23 end
24 return task
24 return task
25 end
25 end
26
26
27 def grade_problem(problem, options={})
27 def grade_problem(problem, options={})
28 - users = User.find(:all)
28 + User.find_each do |u|
29 - users.each do |u|
30 puts "user: #{u.login}"
29 puts "user: #{u.login}"
31 if options[:user_conditions]!=nil
30 if options[:user_conditions]!=nil
32 con_proc = options[:user_conditions]
31 con_proc = options[:user_conditions]
33 next if not con_proc.call(u)
32 next if not con_proc.call(u)
34 end
33 end
35 - last_sub = Submission.find_last_by_user_and_problem(u.id,problem.id)
34 + if options[:all_sub]
36 - if last_sub!=nil
35 + Submission.where(user_id: u.id,problem_id: problem.id).find_each do |sub|
37 - @engine.grade(last_sub)
36 + @engine.grade(sub)
37 + end
38 + else
39 + last_sub = Submission.find_last_by_user_and_problem(u.id,problem.id)
40 + if last_sub!=nil
41 + @engine.grade(last_sub)
42 + end
38 end
43 end
39 end
44 end
40 end
45 end
41
46
42 def grade_submission(submission)
47 def grade_submission(submission)
43 puts "Submission: #{submission.id} by #{submission.user.full_name}"
48 puts "Submission: #{submission.id} by #{submission.user.full_name}"
44 @engine.grade(submission)
49 @engine.grade(submission)
45 end
50 end
46
51
47 def grade_oldest_test_request
52 def grade_oldest_test_request
48 test_request = TestRequest.get_inqueue_and_change_status(Task::STATUS_GRADING)
53 test_request = TestRequest.get_inqueue_and_change_status(Task::STATUS_GRADING)
49 if test_request!=nil
54 if test_request!=nil
50 @grader_process.report_active(test_request) if @grader_process!=nil
55 @grader_process.report_active(test_request) if @grader_process!=nil
51
56
52 @engine.grade(test_request)
57 @engine.grade(test_request)
53 test_request.status_complete!
58 test_request.status_complete!
54 @grader_process.report_inactive(test_request) if @grader_process!=nil
59 @grader_process.report_inactive(test_request) if @grader_process!=nil
55 end
60 end
56 return test_request
61 return test_request
57 end
62 end
58
63
59 end
64 end
60
65
61 end
66 end
@@ -44,91 +44,109
44 if @result_collector
44 if @result_collector
45 @result_collector.save(sub,
45 @result_collector.save(sub,
46 result)
46 result)
47 end
47 end
48 save_result(sub,result)
48 save_result(sub,result)
49 end
49 end
50
50
51 def report_error(sub,msg)
51 def report_error(sub,msg)
52 save_result(sub,{:points => 0,
52 save_result(sub,{:points => 0,
53 :comment => "Grading error: #{msg}" })
53 :comment => "Grading error: #{msg}" })
54 end
54 end
55
55
56 protected
56 protected
57 def read_result(test_result_dir)
57 def read_result(test_result_dir)
58 cmp_msg_fname = "#{test_result_dir}/compiler_message"
58 cmp_msg_fname = "#{test_result_dir}/compiler_message"
59 if FileTest.exist?(cmp_msg_fname)
59 if FileTest.exist?(cmp_msg_fname)
60 cmp_file = File.open(cmp_msg_fname)
60 cmp_file = File.open(cmp_msg_fname)
61 cmp_msg = cmp_file.read
61 cmp_msg = cmp_file.read
62 cmp_file.close
62 cmp_file.close
63 else
63 else
64 cmp_msg = ""
64 cmp_msg = ""
65 end
65 end
66
66
67 result_fname = "#{test_result_dir}/result"
67 result_fname = "#{test_result_dir}/result"
68 - comment_fname = "#{test_result_dir}/comment"
68 + comment_fname = "#{test_result_dir}/comment"
69 + runstat_fname = "#{test_result_dir}/run_stat"
69 if FileTest.exist?(result_fname)
70 if FileTest.exist?(result_fname)
70 comment = ""
71 comment = ""
71 begin
72 begin
72 result_file = File.open(result_fname)
73 result_file = File.open(result_fname)
73 result = result_file.readline.to_i
74 result = result_file.readline.to_i
74 result_file.close
75 result_file.close
75 rescue
76 rescue
76 result = 0
77 result = 0
77 comment = "error reading result file."
78 comment = "error reading result file."
78 end
79 end
79
80
80 begin
81 begin
81 comment_file = File.open(comment_fname)
82 comment_file = File.open(comment_fname)
82 comment += comment_file.readline.chomp
83 comment += comment_file.readline.chomp
83 comment_file.close
84 comment_file.close
84 rescue
85 rescue
85 comment += ""
86 comment += ""
86 end
87 end
87
88
88 - return {:points => result,
89 + begin
89 - :comment => comment,
90 + runstat_file = File.open(runstat_fname)
90 - :cmp_msg => cmp_msg}
91 + max_runtime = runstat_file.readline.to_f
92 + peak_memory = runstat_file.readline.to_i
93 + rescue
94 + max_runtime = -1
95 + peak_memory = -1
96 + end
97 +
98 +
99 + return {points: result,
100 + comment: comment,
101 + cmp_msg: cmp_msg,
102 + max_runtime: max_runtime,
103 + peak_memory: peak_memory
104 + }
91 else
105 else
92 if FileTest.exist?("#{test_result_dir}/a.out")
106 if FileTest.exist?("#{test_result_dir}/a.out")
93 return {:points => 0,
107 return {:points => 0,
94 :comment => 'error during grading',
108 :comment => 'error during grading',
95 :cmp_msg => cmp_msg}
109 :cmp_msg => cmp_msg}
96 else
110 else
97 return {:points => 0,
111 return {:points => 0,
98 :comment => 'compilation error',
112 :comment => 'compilation error',
99 :cmp_msg => cmp_msg}
113 :cmp_msg => cmp_msg}
100 end
114 end
101 end
115 end
102 end
116 end
103
117
104 def save_result(submission,result)
118 def save_result(submission,result)
105 problem = submission.problem
119 problem = submission.problem
106 submission.graded_at = Time.now.gmtime
120 submission.graded_at = Time.now.gmtime
107 points = result[:points]
121 points = result[:points]
108 submission.points = points
122 submission.points = points
109 comment = @config.report_comment(result[:comment])
123 comment = @config.report_comment(result[:comment])
110
124
125 + submission.peak_memory = result[:peak_memory]
126 + submission.max_runtime = result[:max_runtime]
127 + submission.effective_code_length =submission.source.length
128 +
111 #
129 #
112 # TODO: FIX THIS MESSAGE
130 # TODO: FIX THIS MESSAGE
113 #
131 #
114 if problem == nil
132 if problem == nil
115 submission.grader_comment = 'PASSED: ' + comment + '(problem is nil)'
133 submission.grader_comment = 'PASSED: ' + comment + '(problem is nil)'
116 elsif points == problem.full_score
134 elsif points == problem.full_score
117 #submission.grader_comment = 'PASSED: ' + comment
135 #submission.grader_comment = 'PASSED: ' + comment
118 submission.grader_comment = comment
136 submission.grader_comment = comment
119 elsif result[:comment].chomp =~ /^[\[\]P]+$/
137 elsif result[:comment].chomp =~ /^[\[\]P]+$/
120 submission.grader_comment = 'PASSED: ' + comment + '(inconsistent score)'
138 submission.grader_comment = 'PASSED: ' + comment + '(inconsistent score)'
121 else
139 else
122 #submission.grader_comment = 'FAILED: ' + comment
140 #submission.grader_comment = 'FAILED: ' + comment
123 submission.grader_comment = comment
141 submission.grader_comment = comment
124 end
142 end
125 submission.compiler_message = result[:cmp_msg] or ''
143 submission.compiler_message = result[:cmp_msg] or ''
126
144
127 if not @dry_run
145 if not @dry_run
128 submission.save
146 submission.save
129 end
147 end
130 end
148 end
131
149
132 end
150 end
133
151
134 end
152 end
@@ -177,49 +177,49
177 end
177 end
178 end
178 end
179
179
180 def extract_running_stat(results)
180 def extract_running_stat(results)
181 running_stat_line = results[-1]
181 running_stat_line = results[-1]
182
182
183 # extract exit status line
183 # extract exit status line
184 run_stat = ""
184 run_stat = ""
185 if !(/[Cc]orrect/.match(results[0]))
185 if !(/[Cc]orrect/.match(results[0]))
186 run_stat = results[0].chomp
186 run_stat = results[0].chomp
187 else
187 else
188 run_stat = 'Program exited normally'
188 run_stat = 'Program exited normally'
189 end
189 end
190
190
191 # extract running time
191 # extract running time
192 if res = /r(.*)u(.*)s/.match(running_stat_line)
192 if res = /r(.*)u(.*)s/.match(running_stat_line)
193 seconds = (res[1].to_f + res[2].to_f)
193 seconds = (res[1].to_f + res[2].to_f)
194 time_stat = "Time used: #{seconds} sec."
194 time_stat = "Time used: #{seconds} sec."
195 else
195 else
196 seconds = nil
196 seconds = nil
197 time_stat = "Time used: n/a sec."
197 time_stat = "Time used: n/a sec."
198 end
198 end
199
199
200 # extract memory usage
200 # extract memory usage
201 - if res = /s(.*)m/.match(running_stat_line)
201 + if res = /s(.*)kbytes/.match(running_stat_line)
202 memory_used = res[1].to_i
202 memory_used = res[1].to_i
203 else
203 else
204 memory_used = -1
204 memory_used = -1
205 end
205 end
206
206
207 return {
207 return {
208 :msg => "#{run_stat}\n#{time_stat}",
208 :msg => "#{run_stat}\n#{time_stat}",
209 :running_time => seconds,
209 :running_time => seconds,
210 :exit_status => run_stat,
210 :exit_status => run_stat,
211 :memory_usage => memory_used
211 :memory_usage => memory_used
212 }
212 }
213 end
213 end
214
214
215 def save_result(test_request,result)
215 def save_result(test_request,result)
216 if result[:output_file_name]!=nil
216 if result[:output_file_name]!=nil
217 test_request.output_file_name = link_output_file(test_request,
217 test_request.output_file_name = link_output_file(test_request,
218 result[:output_file_name])
218 result[:output_file_name])
219 end
219 end
220 test_request.graded_at = Time.now
220 test_request.graded_at = Time.now
221 test_request.compiler_message = (result[:cmp_msg] or '')
221 test_request.compiler_message = (result[:cmp_msg] or '')
222 test_request.grader_comment = (result[:comment] or '')
222 test_request.grader_comment = (result[:comment] or '')
223 if result[:running_stat]!=nil
223 if result[:running_stat]!=nil
224 test_request.running_stat = (result[:running_stat][:msg] or '')
224 test_request.running_stat = (result[:running_stat][:msg] or '')
225 test_request.running_time = (result[:running_stat][:running_time] or nil)
225 test_request.running_time = (result[:running_stat][:running_time] or nil)
@@ -139,98 +139,98
139 fclose(metafile);
139 fclose(metafile);
140 }
140 }
141
141
142 static void __attribute__((format(printf,1,2)))
142 static void __attribute__((format(printf,1,2)))
143 meta_printf(const char *fmt, ...)
143 meta_printf(const char *fmt, ...)
144 {
144 {
145 if (!metafile)
145 if (!metafile)
146 return;
146 return;
147
147
148 va_list args;
148 va_list args;
149 va_start(args, fmt);
149 va_start(args, fmt);
150 vfprintf(metafile, fmt, args);
150 vfprintf(metafile, fmt, args);
151 va_end(args);
151 va_end(args);
152 }
152 }
153
153
154
154
155 static void print_running_stat(double wall_time,
155 static void print_running_stat(double wall_time,
156 double user_time,
156 double user_time,
157 double system_time,
157 double system_time,
158 int mem_usage)
158 int mem_usage)
159 {
159 {
160 //total is user
160 //total is user
161 //wall is wall
161 //wall is wall
162 //
162 //
163 - fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n",
163 + fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dkbytes\n",
164 wall_time, user_time, system_time, mem_usage);
164 wall_time, user_time, system_time, mem_usage);
165 }
165 }
166
166
167 static void
167 static void
168 final_stats(struct rusage *rus)
168 final_stats(struct rusage *rus)
169 {
169 {
170 struct timeval total, now, wall;
170 struct timeval total, now, wall;
171 timeradd(&rus->ru_utime, &rus->ru_stime, &total);
171 timeradd(&rus->ru_utime, &rus->ru_stime, &total);
172 total_ms = total.tv_sec*1000 + total.tv_usec/1000;
172 total_ms = total.tv_sec*1000 + total.tv_usec/1000;
173 gettimeofday(&now, NULL);
173 gettimeofday(&now, NULL);
174 timersub(&now, &start_time, &wall);
174 timersub(&now, &start_time, &wall);
175 wall_ms = wall.tv_sec*1000 + wall.tv_usec/1000;
175 wall_ms = wall.tv_sec*1000 + wall.tv_usec/1000;
176 sys_ms = rus->ru_stime.tv_sec * 1000 + rus->ru_stime.tv_usec / 1000;
176 sys_ms = rus->ru_stime.tv_sec * 1000 + rus->ru_stime.tv_usec / 1000;
177
177
178 meta_printf("time:%d.%03d\n", total_ms/1000, total_ms%1000);
178 meta_printf("time:%d.%03d\n", total_ms/1000, total_ms%1000);
179 meta_printf("time-wall:%d.%03d\n", wall_ms/1000, wall_ms%1000);
179 meta_printf("time-wall:%d.%03d\n", wall_ms/1000, wall_ms%1000);
180 meta_printf("mem:%llu\n", (unsigned long long) mem_peak_kb * 1024);
180 meta_printf("mem:%llu\n", (unsigned long long) mem_peak_kb * 1024);
181 }
181 }
182
182
183 /*** Messages and exits ***/
183 /*** Messages and exits ***/
184
184
185 static void NONRET
185 static void NONRET
186 box_exit(int rc)
186 box_exit(int rc)
187 {
187 {
188 if (box_pid > 0)
188 if (box_pid > 0)
189 {
189 {
190 sample_mem_peak();
190 sample_mem_peak();
191 if (is_ptraced)
191 if (is_ptraced)
192 ptrace(PTRACE_KILL, box_pid);
192 ptrace(PTRACE_KILL, box_pid);
193 kill(-box_pid, SIGKILL);
193 kill(-box_pid, SIGKILL);
194 kill(box_pid, SIGKILL);
194 kill(box_pid, SIGKILL);
195 meta_printf("killed:1\n");
195 meta_printf("killed:1\n");
196
196
197 struct rusage rus;
197 struct rusage rus;
198 int p, stat;
198 int p, stat;
199 do
199 do
200 p = wait4(box_pid, &stat, 0, &rus);
200 p = wait4(box_pid, &stat, 0, &rus);
201 while (p < 0 && errno == EINTR);
201 while (p < 0 && errno == EINTR);
202 if (p < 0)
202 if (p < 0)
203 fprintf(stderr, "UGH: Lost track of the process (%m)\n");
203 fprintf(stderr, "UGH: Lost track of the process (%m)\n");
204 else {
204 else {
205 final_stats(&rus);
205 final_stats(&rus);
206 }
206 }
207 }
207 }
208 print_running_stat(
208 print_running_stat(
209 (double)wall_ms/1000,
209 (double)wall_ms/1000,
210 (double)total_ms/1000,
210 (double)total_ms/1000,
211 (double)sys_ms/1000,
211 (double)sys_ms/1000,
212 - (mem_peak_kb + 1023) / 1024);
212 + mem_peak_kb);
213 meta_close();
213 meta_close();
214 exit(rc);
214 exit(rc);
215 }
215 }
216
216
217 static void
217 static void
218 flush_line(void)
218 flush_line(void)
219 {
219 {
220 if (partial_line)
220 if (partial_line)
221 fputc('\n', stderr);
221 fputc('\n', stderr);
222 partial_line = 0;
222 partial_line = 0;
223 }
223 }
224
224
225 /* Report an error of the sandbox itself */
225 /* Report an error of the sandbox itself */
226 static void NONRET __attribute__((format(printf,1,2)))
226 static void NONRET __attribute__((format(printf,1,2)))
227 die(char *msg, ...)
227 die(char *msg, ...)
228 {
228 {
229 va_list args;
229 va_list args;
230 va_start(args, msg);
230 va_start(args, msg);
231 flush_line();
231 flush_line();
232 char buf[1024];
232 char buf[1024];
233 vsnprintf(buf, sizeof(buf), msg, args);
233 vsnprintf(buf, sizeof(buf), msg, args);
234 meta_printf("status:XX\nmessage:%s\n", buf);
234 meta_printf("status:XX\nmessage:%s\n", buf);
235 fputs(buf, stderr);
235 fputs(buf, stderr);
236 fputc('\n', stderr);
236 fputc('\n', stderr);
@@ -1435,53 +1435,48
1435 die("wait4: unknown pid %d exited!", p);
1435 die("wait4: unknown pid %d exited!", p);
1436 if (WIFEXITED(stat))
1436 if (WIFEXITED(stat))
1437 {
1437 {
1438 box_pid = 0;
1438 box_pid = 0;
1439 final_stats(&rus);
1439 final_stats(&rus);
1440 if (WEXITSTATUS(stat))
1440 if (WEXITSTATUS(stat))
1441 {
1441 {
1442 if (syscall_count)
1442 if (syscall_count)
1443 {
1443 {
1444 meta_printf("exitcode:%d\n", WEXITSTATUS(stat));
1444 meta_printf("exitcode:%d\n", WEXITSTATUS(stat));
1445 err("RE: Exited with error status %d", WEXITSTATUS(stat));
1445 err("RE: Exited with error status %d", WEXITSTATUS(stat));
1446 }
1446 }
1447 else
1447 else
1448 {
1448 {
1449 // Internal error happened inside the child process and it has been already reported.
1449 // Internal error happened inside the child process and it has been already reported.
1450 box_exit(2);
1450 box_exit(2);
1451 }
1451 }
1452 }
1452 }
1453 if (timeout && total_ms > timeout)
1453 if (timeout && total_ms > timeout)
1454 err("TO: Time limit exceeded");
1454 err("TO: Time limit exceeded");
1455 if (wall_timeout && wall_ms > wall_timeout)
1455 if (wall_timeout && wall_ms > wall_timeout)
1456 err("TO: Time limit exceeded (wall clock)");
1456 err("TO: Time limit exceeded (wall clock)");
1457 flush_line();
1457 flush_line();
1458 fprintf(stderr,"OK\n");
1458 fprintf(stderr,"OK\n");
1459 - print_running_stat(
1460 - (double)wall_ms/1000,
1461 - (double)total_ms/1000,
1462 - (double)sys_ms/1000,
1463 - (mem_peak_kb + 1023) / 1024);
1464 box_exit(0);
1459 box_exit(0);
1465 }
1460 }
1466 if (WIFSIGNALED(stat))
1461 if (WIFSIGNALED(stat))
1467 {
1462 {
1468 box_pid = 0;
1463 box_pid = 0;
1469 meta_printf("exitsig:%d\n", WTERMSIG(stat));
1464 meta_printf("exitsig:%d\n", WTERMSIG(stat));
1470 final_stats(&rus);
1465 final_stats(&rus);
1471 err("SG: Caught fatal signal %d%s", WTERMSIG(stat), (syscall_count ? "" : " during startup"));
1466 err("SG: Caught fatal signal %d%s", WTERMSIG(stat), (syscall_count ? "" : " during startup"));
1472 }
1467 }
1473 if (WIFSTOPPED(stat))
1468 if (WIFSTOPPED(stat))
1474 {
1469 {
1475 int sig = WSTOPSIG(stat);
1470 int sig = WSTOPSIG(stat);
1476 if (sig == SIGTRAP)
1471 if (sig == SIGTRAP)
1477 {
1472 {
1478 if (verbose > 2)
1473 if (verbose > 2)
1479 msg("[ptrace status %08x] ", stat);
1474 msg("[ptrace status %08x] ", stat);
1480 static int stop_count;
1475 static int stop_count;
1481 if (!stop_count++) /* Traceme request */
1476 if (!stop_count++) /* Traceme request */
1482 msg(">> Traceme request caught\n");
1477 msg(">> Traceme request caught\n");
1483 else
1478 else
1484 err("SG: Breakpoint");
1479 err("SG: Breakpoint");
1485 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1480 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1486 }
1481 }
1487 else if (sig == (SIGTRAP | 0x80))
1482 else if (sig == (SIGTRAP | 0x80))
@@ -9,54 +9,56
9 # Supported compilers:
9 # Supported compilers:
10 # gcc, g++, and fpc.
10 # gcc, g++, and fpc.
11 #
11 #
12 ##############################
12 ##############################
13
13
14 def talk(str='')
14 def talk(str='')
15 if ENV['TALKATIVE']!=nil
15 if ENV['TALKATIVE']!=nil
16 puts str
16 puts str
17 end
17 end
18 if ENV['GRADER_LOGGING']!=nil
18 if ENV['GRADER_LOGGING']!=nil
19 log_fname = ENV['GRADER_LOGGING']
19 log_fname = ENV['GRADER_LOGGING']
20 fp = File.open(log_fname,"a")
20 fp = File.open(log_fname,"a")
21 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
21 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
22 fp.close
22 fp.close
23 end
23 end
24 end
24 end
25
25
26 C_COMPILER = "/usr/bin/gcc"
26 C_COMPILER = "/usr/bin/gcc"
27 CPLUSPLUS_COMPILER = "/usr/bin/g++"
27 CPLUSPLUS_COMPILER = "/usr/bin/g++"
28 PASCAL_COMPILER = "/usr/bin/fpc"
28 PASCAL_COMPILER = "/usr/bin/fpc"
29 JAVA_COMPILER = "/usr/bin/javac"
29 JAVA_COMPILER = "/usr/bin/javac"
30 RUBY_INTERPRETER = "/usr/bin/ruby"
30 RUBY_INTERPRETER = "/usr/bin/ruby"
31 PYTHON_INTERPRETER = "/usr/bin/python"
31 PYTHON_INTERPRETER = "/usr/bin/python"
32 PYTHON_CHECKER = "/usr/bin/pyflakes"
32 PYTHON_CHECKER = "/usr/bin/pyflakes"
33 + PHP_INTERPRETER = "/usr/bin/php"
33
34
34 C_OPTIONS = "-O2 -s -static -std=c99 -DCONTEST -lm -Wall"
35 C_OPTIONS = "-O2 -s -static -std=c99 -DCONTEST -lm -Wall"
35 - CPLUSPLUS_OPTIONS = "-O2 -s -static -DCONTEST -lm -Wall"
36 + CPLUSPLUS_OPTIONS = "-O2 -s -std=c++11 -static -DCONTEST -lm -Wall"
36 PASCAL_OPTIONS = "-O1 -XS -dCONTEST"
37 PASCAL_OPTIONS = "-O1 -XS -dCONTEST"
37 JAVA_OPTIONS = ""
38 JAVA_OPTIONS = ""
38 PYTHON_OPTIONS = ""
39 PYTHON_OPTIONS = ""
40 + PHP_OPTIONS = "-l"
39
41
40 # Check for the correct number of arguments. Otherwise, print usage.
42 # Check for the correct number of arguments. Otherwise, print usage.
41 if ARGV.length == 0 or ARGV.length > 4
43 if ARGV.length == 0 or ARGV.length > 4
42 puts "Usage: compile <language> [<source-file>] [<output-file>] [<message-file>]"
44 puts "Usage: compile <language> [<source-file>] [<output-file>] [<message-file>]"
43 puts
45 puts
44 puts "<source-file> is defaulted to \"source\"."
46 puts "<source-file> is defaulted to \"source\"."
45 puts "<output-file> is defaulted to \"a.out\"."
47 puts "<output-file> is defaulted to \"a.out\"."
46 puts "<message-file> is defaulted to \"compiler_message\"."
48 puts "<message-file> is defaulted to \"compiler_message\"."
47 puts
49 puts
48 exit(127)
50 exit(127)
49 end
51 end
50
52
51 PARAMS = {
53 PARAMS = {
52 :source_file => [1,'source'],
54 :source_file => [1,'source'],
53 :output_file => [2,'a.out'],
55 :output_file => [2,'a.out'],
54 :message_file => [3,'compiler_message']
56 :message_file => [3,'compiler_message']
55 }
57 }
56
58
57 params = {}
59 params = {}
58 params[:prog_lang] = ARGV[0]
60 params[:prog_lang] = ARGV[0]
59 PARAMS.each_key do |param_name|
61 PARAMS.each_key do |param_name|
60 index, default = PARAMS[param_name]
62 index, default = PARAMS[param_name]
61 if ARGV.length > index
63 if ARGV.length > index
62 params[param_name] = ARGV[index]
64 params[param_name] = ARGV[index]
@@ -87,79 +89,99
87 params[:prog_lang] = 'c++'
89 params[:prog_lang] = 'c++'
88 end
90 end
89
91
90 # Compile.
92 # Compile.
91 case params[:prog_lang]
93 case params[:prog_lang]
92
94
93 when "c"
95 when "c"
94 command = "#{C_COMPILER} #{params[:source_file]} -o #{params[:output_file]} #{C_OPTIONS} 2> #{params[:message_file]}"
96 command = "#{C_COMPILER} #{params[:source_file]} -o #{params[:output_file]} #{C_OPTIONS} 2> #{params[:message_file]}"
95 system(command)
97 system(command)
96
98
97 when "c++"
99 when "c++"
98 command = "#{CPLUSPLUS_COMPILER} #{params[:source_file]} -o #{params[:output_file]} #{CPLUSPLUS_OPTIONS} 2> #{params[:message_file]}"
100 command = "#{CPLUSPLUS_COMPILER} #{params[:source_file]} -o #{params[:output_file]} #{CPLUSPLUS_OPTIONS} 2> #{params[:message_file]}"
99 system(command)
101 system(command)
100
102
101 when "pas"
103 when "pas"
102 command = "#{PASCAL_COMPILER} #{params[:source_file]} -ooutpas #{PASCAL_OPTIONS} > #{params[:message_file]}"
104 command = "#{PASCAL_COMPILER} #{params[:source_file]} -ooutpas #{PASCAL_OPTIONS} > #{params[:message_file]}"
103 system(command)
105 system(command)
104 FileUtils.mv("output", params[:output_file])
106 FileUtils.mv("output", params[:output_file])
105
107
106 when "java"
108 when "java"
107 #rename the file to the public class name
109 #rename the file to the public class name
108
110
109 #get the class name
111 #get the class name
110 classname = 'DUMMY'
112 classname = 'DUMMY'
113 + source = Array.new
111 File.foreach(params[:source_file]) do |line|
114 File.foreach(params[:source_file]) do |line|
112 md = /\s*public\s*class\s*(\w*)/.match(line)
115 md = /\s*public\s*class\s*(\w*)/.match(line)
113 classname=md[1] if md
116 classname=md[1] if md
117 + source << line unless line =~ /\s*package\s*\w+\s*\;/
114 end
118 end
115 - system("cp #{params[:source_file]} #{classname}.java")
119 + File.open("#{classname}.java","w") do |file|
120 + source.each do |s|
121 + file.puts s
122 + end
123 + end
124 + #system("cp #{params[:source_file]} #{classname}.java")
116 command = "#{JAVA_COMPILER} #{classname}.java 2> #{params[:message_file]}"
125 command = "#{JAVA_COMPILER} #{classname}.java 2> #{params[:message_file]}"
117 system(command)
126 system(command)
118 if File.exists?(classname + ".class")
127 if File.exists?(classname + ".class")
119 File.open(params[:output_file],"w") {|file| file.write("#{classname}")}
128 File.open(params[:output_file],"w") {|file| file.write("#{classname}")}
120 end
129 end
121 if classname == 'DUMMY'
130 if classname == 'DUMMY'
122 File.open(params[:message_file],"w") {|file| file.write("Cannot find any public class in the source code\n")}
131 File.open(params[:message_file],"w") {|file| file.write("Cannot find any public class in the source code\n")}
123 end
132 end
124
133
125 when "ruby"
134 when "ruby"
126 command = "#{RUBY_INTERPRETER} -c #{params[:source_file]} 2> #{params[:message_file]}"
135 command = "#{RUBY_INTERPRETER} -c #{params[:source_file]} 2> #{params[:message_file]}"
127 if system(command)
136 if system(command)
128 File.open(params[:output_file],"w") do |out_file|
137 File.open(params[:output_file],"w") do |out_file|
129 out_file.puts "#!#{RUBY_INTERPRETER}"
138 out_file.puts "#!#{RUBY_INTERPRETER}"
130 File.open(params[:source_file],"r").each do |line|
139 File.open(params[:source_file],"r").each do |line|
131 out_file.print line
140 out_file.print line
132 end
141 end
133 end
142 end
134 File.chmod(0755, params[:output_file])
143 File.chmod(0755, params[:output_file])
135 end
144 end
136
145
137 when "python"
146 when "python"
138 command = "#{PYTHON_CHECKER} #{params[:source_file]} > #{params[:message_file]}"
147 command = "#{PYTHON_CHECKER} #{params[:source_file]} > #{params[:message_file]}"
139 if system(command)
148 if system(command)
140 #compile to python bytecode
149 #compile to python bytecode
141 command = "#{PYTHON_INTERPRETER} -m py_compile #{params[:source_file]}"
150 command = "#{PYTHON_INTERPRETER} -m py_compile #{params[:source_file]}"
142 puts "compile: #{command}"
151 puts "compile: #{command}"
143 system(command)
152 system(command)
144 puts "pwd: " + Dir.pwd
153 puts "pwd: " + Dir.pwd
145 Dir.new('.').each {|file| puts file}
154 Dir.new('.').each {|file| puts file}
146 File.open(params[:output_file],"w") do |out_file|
155 File.open(params[:output_file],"w") do |out_file|
147 out_file.puts "#!#{PYTHON_INTERPRETER} #{params[:source_file]}c"
156 out_file.puts "#!#{PYTHON_INTERPRETER} #{params[:source_file]}c"
148 end
157 end
149 File.chmod(0755, params[:output_file])
158 File.chmod(0755, params[:output_file])
159 + FileUtils.cp("#{params[:source_file]}c",params[:output_file])
150 end
160 end
151 -
161 +
162 + when "php"
163 + command = "#{PHP_INTERPRETER} #{PHP_OPTIONS} #{params[:source_file]} 2> #{params[:message_file]}"
164 + if system(command)
165 + File.open(params[:output_file],"w") do |out_file|
166 + out_file.puts "#!#{PHP_INTERPRETER}"
167 + File.open(params[:source_file],"r").each do |line|
168 + out_file.print line
169 + end
170 + end
171 + File.chmod(0755, params[:output_file])
172 + end
173 +
152 else
174 else
153 talk("ERROR: Invalid language specified!")
175 talk("ERROR: Invalid language specified!")
154 open(params[:message_file],"w") do |f|
176 open(params[:message_file],"w") do |f|
155 f.puts "ERROR: Invalid language specified!"
177 f.puts "ERROR: Invalid language specified!"
156 end
178 end
157 exit(127)
179 exit(127)
158 end
180 end
159
181
160 # Report success or failure.
182 # Report success or failure.
161 if FileTest.exists? params[:output_file]
183 if FileTest.exists? params[:output_file]
162 talk "Compilation was successful!"
184 talk "Compilation was successful!"
163 else
185 else
164 talk "ERROR: Something was wrong during the compilation!"
186 talk "ERROR: Something was wrong during the compilation!"
165 end
187 end
@@ -10,99 +10,124
10 puts str
10 puts str
11 end
11 end
12 if ENV['GRADER_LOGGING']!=nil
12 if ENV['GRADER_LOGGING']!=nil
13 log_fname = ENV['GRADER_LOGGING']
13 log_fname = ENV['GRADER_LOGGING']
14 fp = File.open(log_fname,"a")
14 fp = File.open(log_fname,"a")
15 fp.puts("grade: #{Time.new.strftime("%H:%M")} #{str}")
15 fp.puts("grade: #{Time.new.strftime("%H:%M")} #{str}")
16 fp.close
16 fp.close
17 end
17 end
18 end
18 end
19
19
20 def char_comment(comment)
20 def char_comment(comment)
21 if comment =~ /[Ii]ncorrect/
21 if comment =~ /[Ii]ncorrect/
22 INCORRECT_MARK
22 INCORRECT_MARK
23 elsif comment =~ /[Cc]orrect/
23 elsif comment =~ /[Cc]orrect/
24 CORRECT_MARK
24 CORRECT_MARK
25 elsif comment =~ /[Tt]ime/
25 elsif comment =~ /[Tt]ime/
26 TIMEOUT_MARK
26 TIMEOUT_MARK
27 elsif res = /^[Cc]omment:(.*)$/.match(comment)
27 elsif res = /^[Cc]omment:(.*)$/.match(comment)
28 res[1]
28 res[1]
29 else
29 else
30 RUN_ERROR_MARK # these are run time errors
30 RUN_ERROR_MARK # these are run time errors
31 end
31 end
32 end
32 end
33
33
34 + def extract_time(t)
35 + #puts "TIME: #{t}"
36 + if (result=/^(.*)r(.*)u(.*)s(.*)kbytes/.match(t))
37 + {:real => result[1], :user => result[2], :sys => result[3], :mem => result[4]}
38 + else
39 + #{:real => 0, :user => 0, :sys => 0}
40 + #puts "ERROR READING RUNNING TIME: #{t}"
41 + raise "Error reading running time: #{t}"
42 + end
43 + end
44 +
34 problem_home = ENV['PROBLEM_HOME']
45 problem_home = ENV['PROBLEM_HOME']
35 require "#{problem_home}/script/test_dsl.rb"
46 require "#{problem_home}/script/test_dsl.rb"
36 load "#{problem_home}/test_cases/all_tests.cfg"
47 load "#{problem_home}/test_cases/all_tests.cfg"
37 problem = Problem.get_instance
48 problem = Problem.get_instance
38
49
39 if problem.well_formed? == false
50 if problem.well_formed? == false
40 log "The problem specification is not well formed."
51 log "The problem specification is not well formed."
41 exit(127)
52 exit(127)
42 end
53 end
43
54
44 all_score = 0
55 all_score = 0
45 all_comment = ''
56 all_comment = ''
57 + peak_memory = -1
58 + max_runtime = -1
46 (1..(problem.runs.length-1)).each do |k|
59 (1..(problem.runs.length-1)).each do |k|
47 log "grade run #{k}"
60 log "grade run #{k}"
48 run = problem.runs[k]
61 run = problem.runs[k]
49 run_score = nil
62 run_score = nil
50 run_comment = ''
63 run_comment = ''
51 run_comment_short = ''
64 run_comment_short = ''
52 run.tests.each do |test_num|
65 run.tests.each do |test_num|
53 result_file_name = "#{test_num}/result"
66 result_file_name = "#{test_num}/result"
54 if not File.exists?(result_file_name)
67 if not File.exists?(result_file_name)
55 run_comment += "result file for test #{test_num} not found\n"
68 run_comment += "result file for test #{test_num} not found\n"
56 run_comment_short += RUN_ERROR_MARK
69 run_comment_short += RUN_ERROR_MARK
57 log "Cannot find the file #{test_num}/result!"
70 log "Cannot find the file #{test_num}/result!"
58 else
71 else
59 result_file = File.new(result_file_name, "r")
72 result_file = File.new(result_file_name, "r")
60 result_file_lines = result_file.readlines
73 result_file_lines = result_file.readlines
61 - if result_file_lines.length>=2
74 + if result_file_lines.length>=3
62 current_run_score = result_file_lines[1].to_i
75 current_run_score = result_file_lines[1].to_i
63 run_comment += result_file_lines[0]
76 run_comment += result_file_lines[0]
64 run_comment_short += char_comment(result_file_lines[0].chomp)
77 run_comment_short += char_comment(result_file_lines[0].chomp)
78 +
79 + #update max runtime & memory
80 + run_stat = extract_time result_file_lines[2]
81 + peak_memory = [peak_memory,run_stat[:mem].to_i].max
82 + max_runtime = [max_runtime,run_stat[:user].to_f + run_stat[:sys].to_f].max
65 else
83 else
66 current_run_score = 0
84 current_run_score = 0
67 run_comment += "result file for test #{test_num} error\n"
85 run_comment += "result file for test #{test_num} error\n"
68 run_comment_short += RUN_ERROR_MARK
86 run_comment_short += RUN_ERROR_MARK
69 log "Error in #{test_num}/result!"
87 log "Error in #{test_num}/result!"
70 end
88 end
71
89
72 # the score of this run should be the minimum of the score for
90 # the score of this run should be the minimum of the score for
73 # each test case
91 # each test case
74 if (run_score==nil) or (run_score>current_run_score)
92 if (run_score==nil) or (run_score>current_run_score)
75 run_score = current_run_score
93 run_score = current_run_score
76 end
94 end
77 result_file.close
95 result_file.close
78 end
96 end
79 end
97 end
80
98
81 run_result_file = File.new("result-#{k}", "w")
99 run_result_file = File.new("result-#{k}", "w")
82 run_result_file.write run_score
100 run_result_file.write run_score
83 run_result_file.write "\n"
101 run_result_file.write "\n"
84 run_result_file.close
102 run_result_file.close
85
103
86 run_comment_file = File.new("comment-#{k}", "w")
104 run_comment_file = File.new("comment-#{k}", "w")
87 run_comment_file.write "#{run_comment}\n"
105 run_comment_file.write "#{run_comment}\n"
88 run_comment_file.close
106 run_comment_file.close
89
107
90 all_score = all_score + run_score
108 all_score = all_score + run_score
91
109
92 # append comment for test run with many test cases
110 # append comment for test run with many test cases
93 if run.tests.length > 1
111 if run.tests.length > 1
94 run_comment_short = '[' + run_comment_short + ']'
112 run_comment_short = '[' + run_comment_short + ']'
95 end
113 end
96 all_comment += run_comment_short
114 all_comment += run_comment_short
97 end
115 end
98
116
99 result_file = File.new("result", "w")
117 result_file = File.new("result", "w")
100 result_file.write all_score
118 result_file.write all_score
101 result_file.write "\n"
119 result_file.write "\n"
102 result_file.close
120 result_file.close
103
121
104 comment_file = File.new("comment", "w")
122 comment_file = File.new("comment", "w")
105 comment_file.write "#{all_comment}\n"
123 comment_file.write "#{all_comment}\n"
106 comment_file.close
124 comment_file.close
107
125
108 - log "score = #{all_score} comment = #{all_comment}"
126 +
127 + File.open("run_stat","w") do |file|
128 + file.puts max_runtime
129 + file.puts peak_memory
130 + end
131 +
132 + log "score = #{all_score}\ncomment = #{all_comment}"
133 + log "max_runtime = #{max_runtime}\npeak_memory = #{peak_memory}"
@@ -32,54 +32,55
32 log msg
32 log msg
33 raise msg
33 raise msg
34 end
34 end
35 end
35 end
36
36
37 def clear_and_create_empty_dir(dir)
37 def clear_and_create_empty_dir(dir)
38 FileUtils.rm_rf(dir, :secure => true)
38 FileUtils.rm_rf(dir, :secure => true)
39 call_and_log("Cannot make directory #{dir}.") { FileUtils.mkdir(dir) }
39 call_and_log("Cannot make directory #{dir}.") { FileUtils.mkdir(dir) }
40 end
40 end
41
41
42 # ARGV[0] --- language
42 # ARGV[0] --- language
43 # ARGV[1] --- program source file
43 # ARGV[1] --- program source file
44 # ARGV[2] --- test result directory
44 # ARGV[2] --- test result directory
45 # ARGV[3] --- sandbox directory
45 # ARGV[3] --- sandbox directory
46
46
47 if ARGV.length < 2 || ARGV.length > 4
47 if ARGV.length < 2 || ARGV.length > 4
48 puts "Usage: judge <language> <program-source> [<test-result-directory>] [<sandbox-directory>]"
48 puts "Usage: judge <language> <program-source> [<test-result-directory>] [<sandbox-directory>]"
49 puts " <sandbox-directory> is defaulted to ./sandbox"
49 puts " <sandbox-directory> is defaulted to ./sandbox"
50 puts " <test-result-directory> is defaulted to ./test-result"
50 puts " <test-result-directory> is defaulted to ./test-result"
51 puts "WARNING: The judge script will forcefully create the (implicitly and explicitly) specified directories and remove anything inside it."
51 puts "WARNING: The judge script will forcefully create the (implicitly and explicitly) specified directories and remove anything inside it."
52 exit(127)
52 exit(127)
53 end
53 end
54
54
55 language = ARGV[0]
55 language = ARGV[0]
56 - if language != "c" && language != "c++" && language != "pas" && language != "java" && language != "ruby" && language != "python"
56 + if language != "c" && language != "c++" && language != "pas" && language != "java" && language != "ruby" && language != "python" && language != "php"
57 log "You specified a language that is not supported: #{language}."
57 log "You specified a language that is not supported: #{language}."
58 exit(127)
58 exit(127)
59 end
59 end
60
60
61 source_file = ARGV[1]
61 source_file = ARGV[1]
62 + ENV['SOURCE_NAME'] = source_file
62 if File.exist?(source_file) == false
63 if File.exist?(source_file) == false
63 log "The source file does not exist."
64 log "The source file does not exist."
64 exit(127)
65 exit(127)
65 end
66 end
66
67
67 log "Making test result and sandbox directories..."
68 log "Making test result and sandbox directories..."
68
69
69 current_dir = FileUtils.pwd
70 current_dir = FileUtils.pwd
70 current_dir.strip!
71 current_dir.strip!
71
72
72 if ARGV.length >= 3
73 if ARGV.length >= 3
73 test_result_dir = ARGV[2]
74 test_result_dir = ARGV[2]
74 else
75 else
75 test_result_dir = "#{current_dir}/test-result"
76 test_result_dir = "#{current_dir}/test-result"
76 end
77 end
77
78
78 log "Test result directory: #{test_result_dir}"
79 log "Test result directory: #{test_result_dir}"
79 clear_and_create_empty_dir(test_result_dir)
80 clear_and_create_empty_dir(test_result_dir)
80
81
81 if ARGV.length >= 4
82 if ARGV.length >= 4
82 sandbox_dir = ARGV[3]
83 sandbox_dir = ARGV[3]
83 else
84 else
84 sandbox_dir = "#{current_dir}/sandbox"
85 sandbox_dir = "#{current_dir}/sandbox"
85 end
86 end
@@ -119,49 +120,49
119 require "#{problem_home}/script/test_dsl.rb"
120 require "#{problem_home}/script/test_dsl.rb"
120 load "#{problem_home}/test_cases/all_tests.cfg"
121 load "#{problem_home}/test_cases/all_tests.cfg"
121 problem = Problem.get_instance
122 problem = Problem.get_instance
122
123
123 if problem.well_formed? == false
124 if problem.well_formed? == false
124 log "The problem specification is not well formed."
125 log "The problem specification is not well formed."
125 exit(127)
126 exit(127)
126 end
127 end
127
128
128 # Doing the testing.
129 # Doing the testing.
129 (1..(problem.num_tests)).each do |test_num|
130 (1..(problem.num_tests)).each do |test_num|
130
131
131 $stdout.print "[#{test_num}]"
132 $stdout.print "[#{test_num}]"
132 $stdout.flush
133 $stdout.flush
133
134
134 log "Test number: #{test_num}"
135 log "Test number: #{test_num}"
135
136
136 call_and_log("Cannot copy the compiled program into #{sandbox_dir}") {
137 call_and_log("Cannot copy the compiled program into #{sandbox_dir}") {
137 FileUtils.cp("#{test_result_dir}/a.out", sandbox_dir, :preserve => true)
138 FileUtils.cp("#{test_result_dir}/a.out", sandbox_dir, :preserve => true)
138 if language == "java" then Dir["#{test_result_dir}/*.class"].each { |file| FileUtils.cp(file,sandbox_dir)} end
139 if language == "java" then Dir["#{test_result_dir}/*.class"].each { |file| FileUtils.cp(file,sandbox_dir)} end
139 if language == "python" then Dir["#{test_result_dir}/*.pyc"].each { |file| FileUtils.cp(file,sandbox_dir)} end
140 if language == "python" then Dir["#{test_result_dir}/*.pyc"].each { |file| FileUtils.cp(file,sandbox_dir)} end
140 }
141 }
141
142
142 begin
143 begin
143 - execute("#{problem_home}/script/run #{language} #{test_num}", "Error occured during execution of the run script")
144 + execute("#{problem_home}/script/run #{language} #{test_num} ", "Error occured during execution of the run script")
144 rescue
145 rescue
145 # do nothing
146 # do nothing
146 end
147 end
147
148
148 call_and_log("Cannot create directory #{test_result_dir}/#{test_num}") {
149 call_and_log("Cannot create directory #{test_result_dir}/#{test_num}") {
149 FileUtils.mkdir "#{test_result_dir}/#{test_num}"
150 FileUtils.mkdir "#{test_result_dir}/#{test_num}"
150 }
151 }
151 call_and_log("Cannot copy the result file into #{test_result_dir}/#{test_num}") {
152 call_and_log("Cannot copy the result file into #{test_result_dir}/#{test_num}") {
152 FileUtils.mv "#{sandbox_dir}/result", "#{test_result_dir}/#{test_num}"
153 FileUtils.mv "#{sandbox_dir}/result", "#{test_result_dir}/#{test_num}"
153 }
154 }
154 call_and_log("Cannot copy the comment file into #{test_result_dir}/#{test_num}") {
155 call_and_log("Cannot copy the comment file into #{test_result_dir}/#{test_num}") {
155 FileUtils.mv "#{sandbox_dir}/comment", "#{test_result_dir}/#{test_num}"
156 FileUtils.mv "#{sandbox_dir}/comment", "#{test_result_dir}/#{test_num}"
156 }
157 }
157 call_and_log("Cannot copy the output file into #{test_result_dir}/#{test_num}") {
158 call_and_log("Cannot copy the output file into #{test_result_dir}/#{test_num}") {
158 FileUtils.mv "#{sandbox_dir}/output.txt", "#{test_result_dir}/#{test_num}"
159 FileUtils.mv "#{sandbox_dir}/output.txt", "#{test_result_dir}/#{test_num}"
159 }
160 }
160 call_and_log("Cannot clear #{sandbox_dir}") {
161 call_and_log("Cannot clear #{sandbox_dir}") {
161 FileUtils.rm_rf(Dir.glob("#{sandbox_dir}/*"), :secure => true)
162 FileUtils.rm_rf(Dir.glob("#{sandbox_dir}/*"), :secure => true)
162 }
163 }
163 end
164 end
164
165
165 $stdout.print "[done]\n"
166 $stdout.print "[done]\n"
166
167
167 # Grade
168 # Grade
@@ -22,159 +22,167
22 #{:real => 0, :user => 0, :sys => 0}
22 #{:real => 0, :user => 0, :sys => 0}
23 #puts "ERROR READING RUNNING TIME: #{t}"
23 #puts "ERROR READING RUNNING TIME: #{t}"
24 raise "Error reading running time: #{t}"
24 raise "Error reading running time: #{t}"
25 end
25 end
26 end
26 end
27
27
28 def compile_box(source,bin)
28 def compile_box(source,bin)
29 system("g++ #{source} -o #{bin}")
29 system("g++ #{source} -o #{bin}")
30 end
30 end
31
31
32 if ARGV.length < 2 || ARGV.length > 3
32 if ARGV.length < 2 || ARGV.length > 3
33 puts "Usage: run <language> <test-num> [<program-name>]"
33 puts "Usage: run <language> <test-num> [<program-name>]"
34 exit(127)
34 exit(127)
35 end
35 end
36
36
37 language = ARGV[0]
37 language = ARGV[0]
38 test_num = ARGV[1].to_i
38 test_num = ARGV[1].to_i
39 if ARGV.length > 2
39 if ARGV.length > 2
40 program_name = ARGV[2]
40 program_name = ARGV[2]
41 else
41 else
42 program_name = "a.out"
42 program_name = "a.out"
43 end
43 end
44
44
45 problem_home = ENV['PROBLEM_HOME']
45 problem_home = ENV['PROBLEM_HOME']
46 + source_name = ENV['SOURCE_NAME']
46 require "#{problem_home}/script/test_dsl.rb"
47 require "#{problem_home}/script/test_dsl.rb"
47 load "#{problem_home}/test_cases/all_tests.cfg"
48 load "#{problem_home}/test_cases/all_tests.cfg"
48 problem = Problem.get_instance
49 problem = Problem.get_instance
49
50
51 + sandbox_dir = Dir.getwd
52 +
50 if problem.well_formed? == false
53 if problem.well_formed? == false
51 log "The problem specification is not well formed."
54 log "The problem specification is not well formed."
52 exit(127)
55 exit(127)
53 end
56 end
54
57
55 # Check if the test number is okay.
58 # Check if the test number is okay.
56 if test_num <= 0 || test_num > problem.num_tests
59 if test_num <= 0 || test_num > problem.num_tests
57 log "You have specified a wrong test number."
60 log "You have specified a wrong test number."
58 exit(127)
61 exit(127)
59 end
62 end
60
63
61 #####################################
64 #####################################
62 # Set the relavant file names here. #
65 # Set the relavant file names here. #
63 #####################################
66 #####################################
64
67
65 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
68 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
66
69
67 #####################################
70 #####################################
68
71
69 time_limit = problem.get_time_limit test_num
72 time_limit = problem.get_time_limit test_num
70 mem_limit = problem.get_mem_limit(test_num) * 1024
73 mem_limit = problem.get_mem_limit(test_num) * 1024
71
74
72 # Copy the input file.
75 # Copy the input file.
73 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
76 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
74
77
75 # check if box is there, if not, compile it!
78 # check if box is there, if not, compile it!
76 if !File.exists?("#{problem_home}/script/box")
79 if !File.exists?("#{problem_home}/script/box")
77 log "WARNING: Compiling box: to increase efficiency, it should be compile manually"
80 log "WARNING: Compiling box: to increase efficiency, it should be compile manually"
78 compile_box("#{problem_home}/script/box.cc",
81 compile_box("#{problem_home}/script/box.cc",
79 "#{problem_home}/script/box")
82 "#{problem_home}/script/box")
80 end
83 end
81
84
82 # Hide PROBLEM_HOME
85 # Hide PROBLEM_HOME
83 ENV['PROBLEM_HOME'] = nil
86 ENV['PROBLEM_HOME'] = nil
87 + ENV['SOURCE_NAME'] = nil
84
88
85 # Run the program.
89 # Run the program.
86 #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}"
90 #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}"
87 #
91 #
88
92
89 -
93 + JAVA_OPTION = "-s set_robust_list -s futex -s clone -s getppid -s clone -s wait4 -p /usr/bin/ -p ./"
90 -
94 + RUBY_OPTION = "-p /usr/lib64/ -p /usr/local/lib64/ -p /usr/local/lib/ -p /lib64/ -p /dev/urandom -p #{sandbox_dir}/#{program_name} -p #{sandbox_dir}/ -s set_robust_list -s sched_getaffinity -s clock_gettime -s sigaltstack -s pipe2 -s clone -s futex -s openat -s pipe"
95 + PYTHON_OPTION = "-p /usr/lib64/ -p /usr/local/lib64/ -p /usr/local/lib/ -p /usr/bin/ -p /lib64/ -p #{sandbox_dir}/#{program_name} -p ./#{program_name} -p #{sandbox_dir}/#{source_name} -s set_robust_list -s openat -s recvmsg -s connect -s socket -s sendto -s futex -E PYTHONNOUSERSITE=yes"
96 + PHP_OPTION = "-p /usr/lib64/ -p/lib64/ -p /usr/bin/ -p #{sandbox_dir}/#{program_name} -p ./#{program_name} -p /usr/share/ -s setfsuid -s setfsgid -s openat -s set_robust_list -s futex -s clone -s socket -s connect"
91
97
92 case language
98 case language
93 when "java"
99 when "java"
94 -
95 # for java, extract the classname
100 # for java, extract the classname
96 # wne have to add additional systemcall and we don't check the mem limit (dunno how to fix...)
101 # wne have to add additional systemcall and we don't check the mem limit (dunno how to fix...)
97 classname = 'DUMMY'
102 classname = 'DUMMY'
98 File.open(program_name,"r").each do |line|
103 File.open(program_name,"r").each do |line|
99 classname = line
104 classname = line
100 end
105 end
101 - run_command = "#{problem_home}/script/box -T -t #{time_limit} -s getppid -s clone -s wait4 -p /usr/bin/ -p ./ -i #{input_file_name} -o output.txt /usr/bin/java #{classname} 2>run_result"
106 + #for java, we cannot really check the memory limit...
107 + run_command = "#{problem_home}/script/box -a 3 -f -T -t #{time_limit} #{JAVA_OPTION} -i #{input_file_name} -o output.txt /usr/bin/java #{classname} 2>run_result"
102 when "ruby"
108 when "ruby"
103 - run_command = "#{problem_home}/script/box -T -t #{time_limit} -s getppid -s wait4 -s clone -s set_robust_list -s futex -s sigaltstack -p /dev/urandom -p ./ -p /home/dae/.rvm/rubies/ruby-1.9.2-p320/ -p #{problem_home}/ -i #{input_file_name} -o output.txt #{program_name} 2>run_result"
109 + run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{mem_limit} #{RUBY_OPTION} -i #{input_file_name} -o output.txt /usr/bin/ruby #{program_name} 2>run_result"
104 when "python"
110 when "python"
105 - #this code just run without any checking
111 + run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{mem_limit} #{PYTHON_OPTION} -i #{input_file_name} -o output.txt /usr/bin/python #{program_name} 2>run_result"
106 - run_command = "#{problem_home}/script/box -T -t #{time_limit} -p #{problem_home}/ -i #{input_file_name} -o output.txt #{program_name} 2>run_result"
112 + when "php"
113 + run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} #{PHP_OPTION} -i #{input_file_name} -o output.txt /usr/bin/php #{program_name} 2>run_result"
107 else # for c++, pascal, we do the normal checking
114 else # for c++, pascal, we do the normal checking
108 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name} 2>run_result"
115 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name} 2>run_result"
109 end
116 end
110
117
111
118
112 log "Running test #{test_num}..."
119 log "Running test #{test_num}..."
113 log run_command
120 log run_command
114 - log
121 + log
115 system(run_command)
122 system(run_command)
116
123
117 # Restore PROBLEM_HOME
124 # Restore PROBLEM_HOME
118 ENV['PROBLEM_HOME'] = problem_home
125 ENV['PROBLEM_HOME'] = problem_home
119
126
120 # Create the result file.
127 # Create the result file.
121 result_file = File.new("result", "w")
128 result_file = File.new("result", "w")
122 comment_file = File.new("comment", "w")
129 comment_file = File.new("comment", "w")
123
130
124 # Check if the program actually produced any output.
131 # Check if the program actually produced any output.
125 run_result_file = File.new("run_result", "r")
132 run_result_file = File.new("run_result", "r")
126 run_result = run_result_file.readlines
133 run_result = run_result_file.readlines
127 run_result_file.close
134 run_result_file.close
128
135
129 run_stat = run_result[run_result.length-1]
136 run_stat = run_result[run_result.length-1]
130 running_time = extract_time(run_stat)
137 running_time = extract_time(run_stat)
131
138
132 report = lambda{ |status, points, comment|
139 report = lambda{ |status, points, comment|
133 result_file.write status.strip
140 result_file.write status.strip
134 result_file.write "\n"
141 result_file.write "\n"
135 result_file.write points.to_s.strip
142 result_file.write points.to_s.strip
136 result_file.write "\n"
143 result_file.write "\n"
137 result_file.write run_stat.strip
144 result_file.write run_stat.strip
138 result_file.write "\n"
145 result_file.write "\n"
139 result_file.close
146 result_file.close
140 FileUtils.rm "run_result"
147 FileUtils.rm "run_result"
141 # `rm output.txt` --- keep the output
148 # `rm output.txt` --- keep the output
142
149
143 comment_file.write comment
150 comment_file.write comment
144
151
145 # added for debuggin --- jittat
152 # added for debuggin --- jittat
146 comment_file.write "--run-result--\n"
153 comment_file.write "--run-result--\n"
147 run_result.each do |l|
154 run_result.each do |l|
148 comment_file.write l
155 comment_file.write l
149 end
156 end
150
157
151 comment_file.close
158 comment_file.close
152
159
153 log "Done!"
160 log "Done!"
154 exit(0)
161 exit(0)
155 }
162 }
156
163
164 +
157 if run_result[0][0,2] != "OK"
165 if run_result[0][0,2] != "OK"
158 log "There was a runtime error."
166 log "There was a runtime error."
159 report.call(run_result[0], 0, "No comment.\n")
167 report.call(run_result[0], 0, "No comment.\n")
160 end
168 end
161
169
162 if running_time[:user].to_f > time_limit
170 if running_time[:user].to_f > time_limit
163 log "Time limit exceeded."
171 log "Time limit exceeded."
164 report.call("Time limit exceeded", 0, "No comment.\n")
172 report.call("Time limit exceeded", 0, "No comment.\n")
165 end
173 end
166
174
167 # Run 'check' to evaluate the output.
175 # Run 'check' to evaluate the output.
168 #puts "There was no runtime error. Proceed to checking the output."
176 #puts "There was no runtime error. Proceed to checking the output."
169 check_command = "#{problem_home}/script/check #{language} #{test_num}"
177 check_command = "#{problem_home}/script/check #{language} #{test_num}"
170 log "Checking the output..."
178 log "Checking the output..."
171 log check_command
179 log check_command
172 if not system(check_command)
180 if not system(check_command)
173 log "Problem with check script"
181 log "Problem with check script"
174 report.call("Incorrect",0,"Check script error.\n")
182 report.call("Incorrect",0,"Check script error.\n")
175 exit(127)
183 exit(127)
176 end
184 end
177
185
178 check_file = File.new("check_result", "r")
186 check_file = File.new("check_result", "r")
179 check_file_lines = check_file.readlines
187 check_file_lines = check_file.readlines
180
188
You need to be logged in to leave comments. Login now