diff --git a/grader b/grader --- a/grader +++ b/grader @@ -46,11 +46,22 @@ puts <=1 options[:mode] = ARGV.shift end + else + puts 'no argument specified, using default mode and environment.' end options[:dry_run] = (ARGV.delete('--dry') != nil) @@ -119,6 +132,8 @@ exit(0) end + options[:all_sub] = (ARGV.delete('--all-sub') != nil) + return options end @@ -295,7 +310,7 @@ if prob==nil puts "cannot find problem: #{prob_name}" else - runner.grade_problem(prob) + runner.grade_problem(prob,options) end end @@ -378,6 +393,7 @@ dry_run = options[:dry_run] puts "environment: #{GRADER_ENV}" +puts "grader mode: #{grader_mode}" require File.join(File.dirname(__FILE__),'config/environment') # add grader_mode to config diff --git a/lib/runner.rb b/lib/runner.rb --- a/lib/runner.rb +++ b/lib/runner.rb @@ -25,16 +25,21 @@ end def grade_problem(problem, options={}) - users = User.find(:all) - users.each do |u| + User.find_each do |u| puts "user: #{u.login}" if options[:user_conditions]!=nil con_proc = options[:user_conditions] next if not con_proc.call(u) end - last_sub = Submission.find_last_by_user_and_problem(u.id,problem.id) - if last_sub!=nil - @engine.grade(last_sub) + if options[:all_sub] + Submission.where(user_id: u.id,problem_id: problem.id).find_each do |sub| + @engine.grade(sub) + end + else + last_sub = Submission.find_last_by_user_and_problem(u.id,problem.id) + if last_sub!=nil + @engine.grade(last_sub) + end end end end diff --git a/lib/submission_helper.rb b/lib/submission_helper.rb --- a/lib/submission_helper.rb +++ b/lib/submission_helper.rb @@ -65,7 +65,8 @@ end result_fname = "#{test_result_dir}/result" - comment_fname = "#{test_result_dir}/comment" + comment_fname = "#{test_result_dir}/comment" + runstat_fname = "#{test_result_dir}/run_stat" if FileTest.exist?(result_fname) comment = "" begin @@ -85,9 +86,22 @@ comment += "" end - return {:points => result, - :comment => comment, - :cmp_msg => cmp_msg} + begin + runstat_file = File.open(runstat_fname) + max_runtime = runstat_file.readline.to_f + peak_memory = runstat_file.readline.to_i + rescue + max_runtime = -1 + peak_memory = -1 + end + + + return {points: result, + comment: comment, + cmp_msg: cmp_msg, + max_runtime: max_runtime, + peak_memory: peak_memory + } else if FileTest.exist?("#{test_result_dir}/a.out") return {:points => 0, @@ -108,6 +122,10 @@ submission.points = points comment = @config.report_comment(result[:comment]) + submission.peak_memory = result[:peak_memory] + submission.max_runtime = result[:max_runtime] + submission.effective_code_length =submission.source.length + # # TODO: FIX THIS MESSAGE # diff --git a/lib/test_request_helper.rb b/lib/test_request_helper.rb --- a/lib/test_request_helper.rb +++ b/lib/test_request_helper.rb @@ -198,7 +198,7 @@ end # extract memory usage - if res = /s(.*)m/.match(running_stat_line) + if res = /s(.*)kbytes/.match(running_stat_line) memory_used = res[1].to_i else memory_used = -1 diff --git a/std-script/box64-new.c b/std-script/box64-new.c --- a/std-script/box64-new.c +++ b/std-script/box64-new.c @@ -160,7 +160,7 @@ //total is user //wall is wall // - fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n", + fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dkbytes\n", wall_time, user_time, system_time, mem_usage); } @@ -209,7 +209,7 @@ (double)wall_ms/1000, (double)total_ms/1000, (double)sys_ms/1000, - (mem_peak_kb + 1023) / 1024); + mem_peak_kb); meta_close(); exit(rc); } @@ -1456,11 +1456,6 @@ err("TO: Time limit exceeded (wall clock)"); flush_line(); fprintf(stderr,"OK\n"); - print_running_stat( - (double)wall_ms/1000, - (double)total_ms/1000, - (double)sys_ms/1000, - (mem_peak_kb + 1023) / 1024); box_exit(0); } if (WIFSIGNALED(stat)) diff --git a/std-script/compile b/std-script/compile --- a/std-script/compile +++ b/std-script/compile @@ -30,12 +30,14 @@ RUBY_INTERPRETER = "/usr/bin/ruby" PYTHON_INTERPRETER = "/usr/bin/python" PYTHON_CHECKER = "/usr/bin/pyflakes" +PHP_INTERPRETER = "/usr/bin/php" C_OPTIONS = "-O2 -s -static -std=c99 -DCONTEST -lm -Wall" -CPLUSPLUS_OPTIONS = "-O2 -s -static -DCONTEST -lm -Wall" +CPLUSPLUS_OPTIONS = "-O2 -s -std=c++11 -static -DCONTEST -lm -Wall" PASCAL_OPTIONS = "-O1 -XS -dCONTEST" JAVA_OPTIONS = "" PYTHON_OPTIONS = "" +PHP_OPTIONS = "-l" # Check for the correct number of arguments. Otherwise, print usage. if ARGV.length == 0 or ARGV.length > 4 @@ -108,11 +110,18 @@ #get the class name classname = 'DUMMY' + source = Array.new File.foreach(params[:source_file]) do |line| md = /\s*public\s*class\s*(\w*)/.match(line) classname=md[1] if md + source << line unless line =~ /\s*package\s*\w+\s*\;/ end - system("cp #{params[:source_file]} #{classname}.java") + File.open("#{classname}.java","w") do |file| + source.each do |s| + file.puts s + end + end + #system("cp #{params[:source_file]} #{classname}.java") command = "#{JAVA_COMPILER} #{classname}.java 2> #{params[:message_file]}" system(command) if File.exists?(classname + ".class") @@ -147,8 +156,21 @@ out_file.puts "#!#{PYTHON_INTERPRETER} #{params[:source_file]}c" end File.chmod(0755, params[:output_file]) + FileUtils.cp("#{params[:source_file]}c",params[:output_file]) end - + +when "php" + command = "#{PHP_INTERPRETER} #{PHP_OPTIONS} #{params[:source_file]} 2> #{params[:message_file]}" + if system(command) + File.open(params[:output_file],"w") do |out_file| + out_file.puts "#!#{PHP_INTERPRETER}" + File.open(params[:source_file],"r").each do |line| + out_file.print line + end + end + File.chmod(0755, params[:output_file]) + end + else talk("ERROR: Invalid language specified!") open(params[:message_file],"w") do |f| diff --git a/std-script/grade b/std-script/grade --- a/std-script/grade +++ b/std-script/grade @@ -31,6 +31,17 @@ end end +def extract_time(t) + #puts "TIME: #{t}" + if (result=/^(.*)r(.*)u(.*)s(.*)kbytes/.match(t)) + {:real => result[1], :user => result[2], :sys => result[3], :mem => result[4]} + else + #{:real => 0, :user => 0, :sys => 0} + #puts "ERROR READING RUNNING TIME: #{t}" + raise "Error reading running time: #{t}" + end +end + problem_home = ENV['PROBLEM_HOME'] require "#{problem_home}/script/test_dsl.rb" load "#{problem_home}/test_cases/all_tests.cfg" @@ -43,6 +54,8 @@ all_score = 0 all_comment = '' +peak_memory = -1 +max_runtime = -1 (1..(problem.runs.length-1)).each do |k| log "grade run #{k}" run = problem.runs[k] @@ -58,10 +71,15 @@ else result_file = File.new(result_file_name, "r") result_file_lines = result_file.readlines - if result_file_lines.length>=2 + if result_file_lines.length>=3 current_run_score = result_file_lines[1].to_i run_comment += result_file_lines[0] run_comment_short += char_comment(result_file_lines[0].chomp) + + #update max runtime & memory + run_stat = extract_time result_file_lines[2] + peak_memory = [peak_memory,run_stat[:mem].to_i].max + max_runtime = [max_runtime,run_stat[:user].to_f + run_stat[:sys].to_f].max else current_run_score = 0 run_comment += "result file for test #{test_num} error\n" @@ -105,4 +123,11 @@ comment_file.write "#{all_comment}\n" comment_file.close -log "score = #{all_score} comment = #{all_comment}" + +File.open("run_stat","w") do |file| + file.puts max_runtime + file.puts peak_memory +end + +log "score = #{all_score}\ncomment = #{all_comment}" +log "max_runtime = #{max_runtime}\npeak_memory = #{peak_memory}" diff --git a/std-script/judge b/std-script/judge --- a/std-script/judge +++ b/std-script/judge @@ -53,12 +53,13 @@ end language = ARGV[0] -if language != "c" && language != "c++" && language != "pas" && language != "java" && language != "ruby" && language != "python" +if language != "c" && language != "c++" && language != "pas" && language != "java" && language != "ruby" && language != "python" && language != "php" log "You specified a language that is not supported: #{language}." exit(127) end source_file = ARGV[1] +ENV['SOURCE_NAME'] = source_file if File.exist?(source_file) == false log "The source file does not exist." exit(127) @@ -140,7 +141,7 @@ } begin - execute("#{problem_home}/script/run #{language} #{test_num}", "Error occured during execution of the run script") + execute("#{problem_home}/script/run #{language} #{test_num} ", "Error occured during execution of the run script") rescue # do nothing end diff --git a/std-script/run b/std-script/run --- a/std-script/run +++ b/std-script/run @@ -43,10 +43,13 @@ end problem_home = ENV['PROBLEM_HOME'] +source_name = ENV['SOURCE_NAME'] require "#{problem_home}/script/test_dsl.rb" load "#{problem_home}/test_cases/all_tests.cfg" problem = Problem.get_instance +sandbox_dir = Dir.getwd + if problem.well_formed? == false log "The problem specification is not well formed." exit(127) @@ -81,29 +84,33 @@ # Hide PROBLEM_HOME ENV['PROBLEM_HOME'] = nil +ENV['SOURCE_NAME'] = nil # Run the program. #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}" # - - +JAVA_OPTION = "-s set_robust_list -s futex -s clone -s getppid -s clone -s wait4 -p /usr/bin/ -p ./" +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" +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" +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" case language when "java" - # for java, extract the classname # wne have to add additional systemcall and we don't check the mem limit (dunno how to fix...) classname = 'DUMMY' File.open(program_name,"r").each do |line| classname = line end - 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" + #for java, we cannot really check the memory limit... + 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" when "ruby" - 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" + 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" when "python" - #this code just run without any checking - 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" + 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" + when "php" + 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" else # for c++, pascal, we do the normal checking 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" end @@ -111,7 +118,7 @@ log "Running test #{test_num}..." log run_command -log +log system(run_command) # Restore PROBLEM_HOME @@ -154,6 +161,7 @@ exit(0) } + if run_result[0][0,2] != "OK" log "There was a runtime error." report.call(run_result[0], 0, "No comment.\n")