add language: java

This commit is contained in:
MeiK 2020-11-05 16:02:31 +08:00
parent c582bc8757
commit 26d8727db7
14 changed files with 313 additions and 78 deletions

View File

@ -17,6 +17,8 @@ tempfile = "3"
libc = "0.2.76"
log = "0.4.0"
env_logger = "0.8.1"
nix = "0.19.0"
[build-dependencies]
tonic-build ={ version = "0.3" }
cc = "1.0"

View File

@ -1,4 +1,5 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::compile_protos("proto/river.proto")?;
cc::Build::new().file("src/memory.c").compile("memory");
Ok(())
}

View File

@ -0,0 +1,5 @@
public class Main {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}

0
example/java/1000/in.txt Normal file
View File

View File

@ -0,0 +1 @@
Hello World!

View File

@ -0,0 +1,12 @@
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
int a, b;
a = reader.nextInt();
b = reader.nextInt();
System.out.println(a + b);
reader.close();
}
}

1
example/java/1001/in.txt Normal file
View File

@ -0,0 +1 @@
2 3

View File

@ -0,0 +1 @@
5

View File

@ -13,6 +13,8 @@ def judge(path, language):
filename = "main.cpp"
elif language == river_pb2.Python:
filename = "main.py"
elif language == river_pb2.Java:
filename = "Main.java"
with open(path.joinpath(filename), "rb") as fr:
code = fr.read()
with open(path.joinpath("in.txt"), "rb") as fr:
@ -25,19 +27,30 @@ def judge(path, language):
judge_type=river_pb2.Standard,
compile_data=river_pb2.CompileData(code=code),
)
# if language == river_pb2.Java:
# import time
# time.sleep(1000)
# judge
yield river_pb2.JudgeRequest(
language=language,
judge_type=river_pb2.Standard,
judge_data=river_pb2.JudgeData(
in_data=in_data, out_data=out_data, time_limit=1000, memory_limit=65535
in_data=in_data, out_data=out_data, time_limit=10000, memory_limit=65535
),
)
# if language == river_pb2.Java:
# import time
# time.sleep(1000)
def run():
with grpc.insecure_channel("localhost:4003") as channel:
stub = river_pb2_grpc.RiverStub(channel)
for path in Path("java").iterdir():
print(f"开始评测 {path}")
for item in stub.Judge(judge(path, river_pb2.Java)):
print(item)
print(f"{path} 评测完成")
for path in Path("c").iterdir():
print(f"开始评测 {path}")
for item in stub.Judge(judge(path, river_pb2.C)):

View File

@ -7,17 +7,22 @@ pub fn gen_rules() -> Vec<SyscallRuleSet> {
allow_syscall(libc::SYS_arch_prctl),
// allow_syscall(libc::SYS_brk),
allow_syscall(libc::SYS_chmod),
allow_syscall(libc::SYS_clock_adjtime),
allow_syscall(libc::SYS_clock_getres),
allow_syscall(libc::SYS_clock_gettime),
allow_syscall(libc::SYS_clone),
allow_syscall(libc::SYS_close),
allow_syscall(libc::SYS_connect),
allow_syscall(libc::SYS_dup),
allow_syscall(libc::SYS_dup2),
allow_syscall(libc::SYS_execve),
allow_syscall(libc::SYS_exit),
allow_syscall(libc::SYS_exit_group),
allow_syscall(libc::SYS_fchdir),
allow_syscall(libc::SYS_fcntl),
allow_syscall(libc::SYS_fork),
allow_syscall(libc::SYS_fstat),
allow_syscall(libc::SYS_ftruncate),
allow_syscall(libc::SYS_futex),
allow_syscall(libc::SYS_getcwd),
allow_syscall(libc::SYS_getdents),
@ -25,6 +30,8 @@ pub fn gen_rules() -> Vec<SyscallRuleSet> {
allow_syscall(libc::SYS_geteuid),
allow_syscall(libc::SYS_getgid),
allow_syscall(libc::SYS_getpid),
allow_syscall(libc::SYS_getsockname),
allow_syscall(libc::SYS_getsockopt),
allow_syscall(libc::SYS_gettid),
allow_syscall(libc::SYS_getrandom),
allow_syscall(libc::SYS_getrlimit),
@ -33,12 +40,16 @@ pub fn gen_rules() -> Vec<SyscallRuleSet> {
allow_syscall(libc::SYS_ioctl),
allow_syscall(libc::SYS_lseek),
allow_syscall(libc::SYS_lstat),
allow_syscall(libc::SYS_madvise),
// allow_syscall(libc::SYS_mmap),
allow_syscall(libc::SYS_mkdir),
allow_syscall(libc::SYS_mprotect),
// allow_syscall(libc::SYS_munmap),
allow_syscall(libc::SYS_nanosleep),
allow_syscall(libc::SYS_open),
allow_syscall(libc::SYS_openat),
allow_syscall(libc::SYS_prctl),
allow_syscall(libc::SYS_pread64),
allow_syscall(libc::SYS_prlimit64),
allow_syscall(libc::SYS_read),
allow_syscall(libc::SYS_readlink),
@ -46,10 +57,15 @@ pub fn gen_rules() -> Vec<SyscallRuleSet> {
allow_syscall(libc::SYS_rt_sigaction),
allow_syscall(libc::SYS_rt_sigprocmask),
allow_syscall(libc::SYS_rt_sigreturn),
allow_syscall(libc::SYS_sched_getaffinity),
allow_syscall(libc::SYS_sched_yield),
allow_syscall(libc::SYS_select),
allow_syscall(libc::SYS_set_robust_list),
allow_syscall(libc::SYS_set_tid_address),
allow_syscall(libc::SYS_setsockopt),
allow_syscall(libc::SYS_sigaltstack),
allow_syscall(libc::SYS_socket),
allow_syscall(libc::SYS_socketpair),
allow_syscall(libc::SYS_stat),
allow_syscall(libc::SYS_sysinfo),
allow_syscall(libc::SYS_umask),

View File

@ -44,7 +44,7 @@ pub async fn judger(
Some(Language::Node) => "node main.js",
Some(Language::TypeScript) => "node main.js",
Some(Language::Go) => "./a.out",
Some(Language::Java) => "Main.java",
Some(Language::Java) => "/usr/bin/java -cp . Main",
None => return Err(Error::LanguageNotFound(request.language)),
};
let process = Process::new(
@ -54,9 +54,15 @@ pub async fn judger(
data.time_limit,
data.memory_limit,
)?;
debug!("run command: {}", cmd);
// 开始执行并等待返回结果
let runner = process.runner.clone();
let mut runner = process.runner.clone();
// 为 Java 虚拟机取消内存限制和 trace万恶的 JVM
if request.language == Language::Java as i32 {
runner.memory_limit = -1;
runner.traceme = false;
}
let status = runner.await?;
resp.set_process_status(&status);
@ -126,7 +132,7 @@ pub async fn compile(
Some(Language::Node) => "/usr/bin/node /plugins/js/validate.js main.js",
Some(Language::TypeScript) => "/usr/bin/tsc main.ts",
Some(Language::Go) => "/usr/bin/go build -ldflags \"-s -w\" main.go",
Some(Language::Java) => "/usr/bin/java -cp . Main",
Some(Language::Java) => "/usr/bin/javac Main.java",
None => return Err(Error::LanguageNotFound(request.language)),
};
debug!("build command: {}", cmd);
@ -142,6 +148,10 @@ pub async fn compile(
let mut runner = process.runner.clone();
runner.traceme = false;
// 为 Java 虚拟机取消内存限制(万恶的 JVM
if request.language == Language::Java as i32 {
runner.memory_limit = -1;
}
let status = runner.await?;
resp.set_process_status(&status);
if status.exit_code != 0 || status.signal != 0 {

160
src/memory.c Normal file
View File

@ -0,0 +1,160 @@
#include <ctype.h>
#include <linux/limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/**
* in: ' 42964 kB'
* out: 42964
* */
int GetNumByVmLine(char body[])
{
int offset, ans, start;
offset = ans = 0;
start = 0; // FALSE on C
while (1)
{
if (!start)
{
if (isdigit(body[offset]))
{
start = 1; // TRUE on C
}
else
{
offset++;
continue;
}
}
if (start)
{
if (isdigit(body[offset]))
{
ans *= 10;
ans += (int)(body[offset] - '0');
offset++;
}
else
{
break;
}
}
}
return ans;
}
long MemoryUsage(int fd)
{
int i;
ssize_t len;
char body[4096];
long vm_data = 0, vm_stk = 0;
if ((len = pread(fd, body, 4096, 0)) == -1)
{
return -1;
}
for (i = 0; i < len; i++)
{
switch (body[i])
{
case 'V':
goto V;
default:
goto NEXTLINE;
}
V:
i++;
switch (body[i])
{
case 'm':
goto Vm;
default:
goto NEXTLINE;
}
Vm:
i++;
switch (body[i])
{
case 'R':
i += 2;
goto VmRSS;
case 'D':
i += 3;
goto VmData;
case 'S':
goto VmS;
case 'E':
i += 2;
goto VmExe;
case 'L':
goto VmL;
default:
goto NEXTLINE;
}
VmRSS:
i += 2;
// vm_rss = GetNumByVmLine(body + i);
goto NEXTLINE;
VmData:
i += 2;
vm_data = GetNumByVmLine(body + i);
goto NEXTLINE;
VmS:
i++;
switch (body[i])
{
case 't':
i++;
goto VmStk;
case 'i':
i += 2;
goto VmSize;
default:
goto NEXTLINE;
}
VmStk:
i += 2;
vm_stk = GetNumByVmLine(body + i);
goto NEXTLINE;
VmSize:
i += 2;
// vm_size = GetNumByVmLine(body + i);
goto NEXTLINE;
VmExe:
i += 2;
// vm_exe = GetNumByVmLine(body + i);
goto NEXTLINE;
VmL:
i++;
switch (body[i])
{
case 'i':
i++;
goto VmLib;
}
VmLib:
i += 2;
// vm_lib = GetNumByVmLine(body + i);
goto NEXTLINE;
NEXTLINE:
while (body[i] != '\n')
{
i++;
}
}
return vm_data + vm_stk;
}

View File

@ -8,7 +8,7 @@ pub fn standard_result(out: &[u8], ans: &[u8]) -> Result<JudgeResult> {
let mut ans_offset = 0;
// 没有 PEPE 直接 WA
let mut r = JudgeResult::Accepted;
while out_offset < out_len && ans_offset < ans_len {
while out_offset <= out_len && ans_offset <= ans_len {
let (out_start, out_end, out_exists) = next_line(&out, out_offset, out_len);
let (ans_start, ans_end, ans_exists) = next_line(&ans, ans_offset, ans_len);
if !out_exists || !ans_exists {
@ -159,4 +159,25 @@ mod tests {
let out: &[u8] = "Hello World!\t\t\t\t\n\n\n\n \n\n\n\n\t\t\t\t".as_bytes();
assert_eq!(standard_result(out, ans).unwrap(), JudgeResult::Accepted);
}
#[test]
fn test8() {
let ans: &[u8] = "Hello World!".as_bytes();
let out: &[u8] = "".as_bytes();
assert_eq!(standard_result(out, ans).unwrap(), JudgeResult::WrongAnswer);
}
#[test]
fn test9() {
let ans: &[u8] = "".as_bytes();
let out: &[u8] = "".as_bytes();
assert_eq!(standard_result(out, ans).unwrap(), JudgeResult::Accepted);
}
#[test]
fn test10() {
let ans: &[u8] = "Hello World!".as_bytes();
let out: &[u8] = "Hello World!\n".as_bytes();
assert_eq!(standard_result(out, ans).unwrap(), JudgeResult::Accepted);
}
}

View File

@ -1,23 +1,30 @@
extern crate nix;
use super::allow::{gen_rules, trace_syscall};
use super::config::{STDERR_FILENAME, STDIN_FILENAME, STDOUT_FILENAME};
use super::error::{errno_str, Error, Result};
use super::exec_args::ExecArgs;
use super::seccomp::*;
use nix::unistd::close;
use std::convert::TryInto;
use std::env;
use std::ffi::CString;
use std::fs::File;
use std::fs::{remove_file, File};
use std::future::Future;
use std::io::{self, prelude::*, BufReader};
use std::path::PathBuf;
use std::io;
use std::os::unix::io::IntoRawFd;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::ptr;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::sync::{mpsc, Arc, Mutex};
use std::task::{Context, Poll};
use std::thread;
use std::time::SystemTime;
extern "C" {
fn MemoryUsage(fd: i32) -> i64;
}
#[derive(Clone)]
pub struct Runner {
pub pid: i32,
@ -161,12 +168,18 @@ impl Runner {
unsafe {
// 重定向文件描述符
dup(STDIN_FILENAME, libc::STDIN_FILENO, libc::O_RDONLY, 0o644);
if Path::new(STDOUT_FILENAME).exists() {
remove_file(STDOUT_FILENAME).unwrap();
}
dup(
STDOUT_FILENAME,
libc::STDOUT_FILENO,
libc::O_CREAT | libc::O_RDWR,
0o644,
);
if Path::new(STDERR_FILENAME).exists() {
remove_file(STDERR_FILENAME).unwrap();
}
dup(
STDERR_FILENAME,
libc::STDERR_FILENO,
@ -180,7 +193,7 @@ impl Runner {
panic!("How dare you!");
}
// CPU 时间限制,粒度为 S
rl.rlim_cur = (self.time_limit / 1000 + 1) as u64;
rl.rlim_cur = (self.time_limit as u64) / 1000 + 1;
rl.rlim_max = rl.rlim_cur + 1;
if libc::setrlimit(libc::RLIMIT_CPU, &rl) != 0 {
eprintln!("setrlimit failure!");
@ -188,17 +201,19 @@ impl Runner {
panic!("How dare you!");
}
// 设置内存限制
rl.rlim_cur = (self.memory_limit * 1024) as u64;
rl.rlim_max = rl.rlim_cur + 1024;
if libc::setrlimit(libc::RLIMIT_DATA, &rl) != 0 {
eprintln!("setrlimit failure!");
eprintln!("{:?}", io::Error::last_os_error().raw_os_error());
panic!("How dare you!");
}
if libc::setrlimit(libc::RLIMIT_AS, &rl) != 0 {
eprintln!("setrlimit failure!");
eprintln!("{:?}", io::Error::last_os_error().raw_os_error());
panic!("How dare you!");
if self.memory_limit > 0 {
rl.rlim_cur = (self.memory_limit as u64) * 1024;
rl.rlim_max = rl.rlim_cur + 1024;
if libc::setrlimit(libc::RLIMIT_DATA, &rl) != 0 {
eprintln!("setrlimit failure!");
eprintln!("{:?}", io::Error::last_os_error().raw_os_error());
panic!("How dare you!");
}
if libc::setrlimit(libc::RLIMIT_AS, &rl) != 0 {
eprintln!("setrlimit failure!");
eprintln!("{:?}", io::Error::last_os_error().raw_os_error());
panic!("How dare you!");
}
}
if self.traceme {
// 设置 trace 模式
@ -229,6 +244,7 @@ impl Runner {
}
SeccompFilter::apply(filter.try_into().unwrap()).unwrap();
libc::execve(exec_args.pathname, exec_args.argv, exec_args.envp);
libc::kill(libc::getpid(), libc::SIGKILL);
}
panic!("How dare you!");
}
@ -267,6 +283,17 @@ impl Runner {
// from: https://www.hackerearth.com/practice/notes/vivekprakash/technical-diving-into-memory-used-by-a-program-in-online-judges/
// VmRSS 为程序当前驻留在物理内存中的大小,对虚拟内存等无效
let mut vm_mem = 0;
let status_file = format!("/proc/{}/status", pid);
let file = match File::open(status_file) {
Ok(val) => val,
Err(_) => {
return judge_error(format!(
"open file `{}` failure!",
format!("/proc/{}/status", pid)
))
}
};
let status_fd = file.into_raw_fd();
unsafe {
if self.traceme {
@ -283,52 +310,15 @@ impl Runner {
libc::ptrace(libc::PTRACE_SYSCALL, pid, 0, 0);
libc::waitpid(pid, &mut status, 0);
// 通过 /proc/pid/status 读取内存占用
let status_file = format!("/proc/{}/status", pid);
debug!("{}", status_file);
let file = match File::open(status_file) {
Ok(val) => val,
Err(_) => {
return judge_error(format!(
"open file `{}` failure!",
format!("/proc/{}/status", pid)
))
}
};
let reader = BufReader::new(file);
let mut vm_data = 0;
let mut vm_stk = 0;
for line in reader.lines() {
let s = match line {
Ok(val) => val,
Err(_) => return judge_error("readline failure!".to_string()),
};
if s.starts_with("VmData") {
let len = s.len() - 2;
let mem = &s[7..len].trim();
let mem = match mem.parse::<i64>() {
Ok(val) => val,
Err(_) => return judge_error(format!("parse {} failure!", mem)),
};
if vm_data < mem {
vm_data = mem;
}
debug!("vm_data: {}", vm_data);
} else if s.starts_with("VmStk") {
let len = s.len() - 2;
let mem = &s[6..len].trim();
let mem = match mem.parse::<i64>() {
Ok(val) => val,
Err(_) => return judge_error(format!("parse {} failure!", mem)),
};
if vm_stk < mem {
vm_stk = mem;
}
debug!("vm_stk: {}", vm_stk);
}
let vmem = MemoryUsage(status_fd);
if vm_mem < vmem {
vm_mem = vmem;
}
if vm_mem < vm_stk + vm_data {
vm_mem = vm_stk + vm_data;
// trace 模式下,如果检测到内存已经超出限制,则直接 kill & break
if vm_mem > self.memory_limit.into() {
debug!("MemoryLimitExceeded! break");
libc::kill(pid, libc::SIGKILL);
break;
}
debug!("vm_mem: {}", vm_mem);
@ -341,23 +331,25 @@ impl Runner {
debug!("exited: {}", libc::WIFEXITED(status));
break;
}
debug!("exited: {}", libc::WIFEXITED(status));
// debug!("exited: {}", libc::WIFEXITED(status));
}
}
match close(status_fd) {
Ok(_) => {}
Err(_) => return judge_error("close status file failure!".to_string()),
};
let mut exit_code = 0;
let exited = unsafe { libc::WIFEXITED(status) };
let exited = libc::WIFEXITED(status);
if exited {
exit_code = unsafe { libc::WEXITSTATUS(status) };
exit_code = libc::WEXITSTATUS(status);
}
let signal = unsafe {
if libc::WIFSIGNALED(status) {
libc::WTERMSIG(status)
} else if libc::WIFSTOPPED(status) {
libc::WSTOPSIG(status)
} else {
0
}
let signal = if libc::WIFSIGNALED(status) {
libc::WTERMSIG(status)
} else if libc::WIFSTOPPED(status) {
libc::WSTOPSIG(status)
} else {
0
};
// TODO: 添加 CGroup 的量度
let time_used = rusage.ru_utime.tv_sec * 1000