Commit 4abcdbdf authored by Yuan Zhixiang's avatar Yuan Zhixiang
Browse files

迁移到新仓库

parents
Pipeline #46 failed with stages
in 0 seconds
# 基础镜像,必须选择ubuntu14,在高版本ubuntu中c_backend不能编译成功
FROM ubuntu:14.04.1
RUN apt-get update && apt-get install -y \
build-essential \
autotools-dev \
automake \
libc6-dbg \
python \
zlib1g-dev
RUN mkdir /app
WORKDIR /app
COPY ./c_backend.tar.gz ./
COPY ./openssl-3.2.1.tar ./
COPY ./Python-3.12.2.tgz ./
# 合并为一个RUN命令,减少镜像大小
RUN tar -zxf c_backend.tar.gz && \
tar -xf openssl-3.2.1.tar && \
tar -zxf Python-3.12.2.tgz && \
# 构建c_backend核心分析器
cd /app/c_backend && ./auto-everything.sh && \
# 构建openssl,作为python依赖
cd /app/openssl-3.2.1 && ./config && make -j && make install && \
echo "/usr/local/lib64/" >> /etc/ld.so.conf && ldconfig && \
# 构建python3.12
cd /app/Python-3.12.2 && ./configure && make -j && make install && \
# 清理多余文件
cd /app/ && \
rm -r Python-3.12.2 && \
rm -r openssl-3.2.1 && \
rm c_backend.tar.gz && \
rm openssl-3.2.1.tar && \
rm Python-3.12.2.tgz
# 安装依赖
RUN pip3.12 install flask flask-cors gevent
# 替代旧的分析脚本(python2)
COPY ./run_cpp_backend.py ./c_backend/
# 加入新的网页后端脚本(python3)
COPY ./backend.py ./c_backend/
# 设定自启动
WORKDIR /app/c_backend
ENTRYPOINT ["python3.12", "./backend.py"]
\ No newline at end of file
## 构建单个基础镜像
核心分析器不支持多线程,Web后端也没有添加多线程支持
构建镜像
```
sudo docker build -t c-backend:latest .
```
运行镜像
```
sudo docker run -d -p 5000:5000 c-backend:latest
```
临时前台运行镜像
```
sudo docker run -it -p 5000:5000 c-backend:latest
```
## 运行集群
初始化
```
sudo docker swarm init
```
创建服务 (--replicas 实例数量)
```
sudo docker service create --name c-backend-service --replicas 8 -p 5000:5000 c-backend:latest
```
删除服务
```
sudo docker service rm c-backend-service
```
## 额外
后端为http,请自行代理为https
后端设置为完全允许跨域
## 请求
#### 获取后端状态
路径: `/state`
方法: `GET`
返回: `OK`
#### 分析代码
路径: `/visualize`
方法: `POST`
请求: form-data:
`code` : `代码`
`stdin` : `测试用例`(可选)
返回: `{...json...}`
## 文件列表
`c_backend.tar.gz`
来自仓库 [https://github.com/pathrise-eng/pathrise-python-tutor ](https://github.com/pathrise-eng/pathrise-python-tutor/tree/master/v4-cokapi/backends/c_cpp) 的部分代码打包,减少构建时的网络影响
`openssl-3.2.1.tar`
来自相关网站,减少构建时的网络影响
`Python-3.12.2.tgz`
来自相关网站,减少构建时的网络影响
`backend.py`
后端脚本
`run_cpp_backend.py`
替换`c_backend.tar.gz`里原有的脚本,使其适配后端
\ No newline at end of file
from flask import Flask, request
import subprocess
import json
from gevent.pywsgi import WSGIServer
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
# 用于测试后端是否正常运行
@app.route('/state')
def hello_world():
return 'OK'
@app.route('/visualize', methods=['POST'])
def visualize():
# 获取POST内容
code = request.form.get('code', None) # 测试的代码
stdin = request.form.get('stdin', '') # 用例输入(如果有)
# 校验测试的代码
if code is None:
return {'error': 'Code does not exist.'}
# 执行分析脚本
result = run_python2_script_and_get_output(code, stdin)
if result is None:
return {'error': 'Analysis failed.'}
else:
if len(result) == 0:
return {'error': 'Analysis failed.'}
else:
return json.loads(result)
# 调用核心分析脚本
def run_python2_script_and_get_output(code, stdin):
script_path = './run_cpp_backend.py'
python2_interpreter = '/usr/bin/python2'
process = subprocess.Popen([python2_interpreter, script_path, code, 'c', stdin], stdout=subprocess.PIPE)
output, error = process.communicate()
if process.returncode != 0:
# raise Exception('Python 2 script failed with error: {}'.format(error.decode('utf-8')))
return None
return output.decode('utf-8')
if __name__ == '__main__':
print('c-backend server start.')
# 启动后端
http_server = WSGIServer(('', 5000), app)
http_server.serve_forever()
# -*- coding: UTF-8 -*-
# 使用Valgrind为基础,运行OPT的C/C++后端,并将JSON输出到stdout以便于管
# 道传输到web应用中,同时妥善处理错误情况。
#
# 创建日期:2016-05-09
import json
import os
from subprocess import Popen, PIPE
import re
import sys
import uuid
import shutil
# 定义用于匹配Valgrind输出的正则表达式
VALGRIND_MSG_RE = re.compile('==\d+== (.*)$')
end_of_trace_error_msg = None
# 获取当前脚本的目录,确保可执行文件路径总是相对的
DN = os.path.dirname(sys.argv[0])
if not DN:
DN = '.' # 所以我们总是有一个类似 ./usercode.exe 的可执行文件路径
USER_PROGRAM = sys.argv[1] # 要运行的程序字符串
LANG = sys.argv[2] # 'c' 表示C语言,'cpp' 表示C++语言
# 新增随机目录
uuid = str(uuid.uuid4())
RANDOM_DIR = DN + '/' + uuid
os.makedirs(RANDOM_DIR)
# 添加输入用例功能
input_data = None
if len(sys.argv) > 3:
input_data = sys.argv[3]
# 命令行参数是否启用美化输出
prettydump = False
# 仅美化终端输出内容,不影响实际结果,此参数位置用于输入用例
# if len(sys.argv) > 3:
# if sys.argv[3] == '--prettydump':
# prettydump = True
# 根据编程语言选择编译器和语言标准
if LANG == 'c':
CC = 'gcc'
DIALECT = '-std=c11'
FN = 'usercode.c'
else:
CC = 'g++'
DIALECT = '-std=c++11'
FN = 'usercode.cpp'
# 拼接各种文件路径
# 此处使用随机路径
# F_PATH = os.path.join(DN, FN)
# VGTRACE_PATH = os.path.join(DN, 'usercode.vgtrace')
# EXE_PATH = os.path.join(DN, 'usercode.exe')
F_PATH = os.path.join(RANDOM_DIR, FN)
VGTRACE_PATH = os.path.join(RANDOM_DIR, 'usercode.vgtrace')
EXE_PATH = os.path.join(RANDOM_DIR, 'usercode.exe')
# 删除旧文件,避免使用错误的文件
for f in (F_PATH, VGTRACE_PATH, EXE_PATH):
if os.path.exists(f):
os.remove(f)
# 将USER_PROGRAM写入到F_PATH中
with open(F_PATH, 'w') as f:
f.write(USER_PROGRAM)
# 编译用户代码
p = Popen([CC, DIALECT, '-ggdb', '-O0', '-fno-omit-frame-pointer', '-o', EXE_PATH, F_PATH],
stdout=PIPE, stderr=PIPE)
(gcc_stdout, gcc_stderr) = p.communicate()
gcc_retcode = p.returncode
# 输出gcc的错误信息
if gcc_retcode == 0:
# 防止输出多余信息
# print >> sys.stderr, '=== gcc stderr ==='
# print >> sys.stderr, gcc_stderr
# print >> sys.stderr, '==='
if input_data is None:
# 无用例输入
# 使用Valgrind运行编译后的代码
VALGRIND_EXE = os.path.join(DN, 'valgrind-3.11.0/inst/bin/valgrind')
valgrind_p = Popen(['stdbuf', '-o0', # 确保stdout不会被缓冲,以便正确追踪
VALGRIND_EXE,
'--tool=memcheck',
'--source-filename=' + FN,
'--trace-filename=' + VGTRACE_PATH,
EXE_PATH],
stdout=PIPE, stderr=PIPE)
(valgrind_stdout, valgrind_stderr) = valgrind_p.communicate()
valgrind_retcode = valgrind_p.returncode
else:
# 有用例输入
# 使用Valgrind运行编译后的代码
VALGRIND_EXE = os.path.join(DN, 'valgrind-3.11.0/inst/bin/valgrind')
valgrind_p = Popen(['stdbuf', '-o0', # 确保stdout不会被缓冲,以便正确追踪
VALGRIND_EXE,
'--tool=memcheck',
'--source-filename=' + FN,
'--trace-filename=' + VGTRACE_PATH,
EXE_PATH],
stdout=PIPE,
stdin=PIPE,
stderr=PIPE)
valgrind_p.stdin.write(input_data.encode('utf-8'))
(valgrind_stdout, valgrind_stderr) = valgrind_p.communicate()
valgrind_retcode = valgrind_p.returncode
# 输出Valgrind的错误信息
# 防止输出多余信息
# print >> sys.stderr, '=== Valgrind stdout ==='
# print >> sys.stderr, valgrind_stdout
# print >> sys.stderr, '=== Valgrind stderr ==='
# print >> sys.stderr, valgrind_stderr
# 处理Valgrind检测到的错误
error_lines = []
in_error_msg = False
if valgrind_retcode != 0: # 如果Valgrind运行出错
for line in valgrind_stderr.splitlines():
m = VALGRIND_MSG_RE.match(line)
if m:
msg = m.group(1).rstrip()
# 如果检测到进程终止错误,则记录后续错误信息
if 'Process terminating' in msg:
in_error_msg = True
if in_error_msg:
if not msg:
in_error_msg = False
if in_error_msg:
error_lines.append(msg)
if error_lines:
end_of_trace_error_msg = '\n'.join(error_lines)
# 将Valgrind追踪文件转换为OPT追踪格式
# TODO: 考虑将这部分集成到同一个脚本中,因为它是Python代码,无需作为外部脚本调用
POSTPROCESS_EXE = os.path.join(DN, 'vg_to_opt_trace.py')
args = ['python', POSTPROCESS_EXE]
if prettydump:
args.append('--prettydump')
else:
args.append('--jsondump')
if end_of_trace_error_msg:
args += ['--end-of-trace-error-msg', end_of_trace_error_msg]
args.append(F_PATH)
postprocess_p = Popen(args, stdout=PIPE, stderr=PIPE)
(postprocess_stdout, postprocess_stderr) = postprocess_p.communicate()
postprocess_retcode = postprocess_p.returncode
# 防止输出多余信息
# print >> sys.stderr, '=== postprocess stderr ==='
# print >> sys.stderr, postprocess_stderr
# print >> sys.stderr, '==='
print postprocess_stdout
else:
# 输出gcc错误信息,并优雅地解析并报告编译错误
# 防止输出多余信息
# print >> sys.stderr, '=== gcc stderr ==='
# print >> sys.stderr, gcc_stderr
# print >> sys.stderr, '==='
exception_msg = 'unknown compiler error'
lineno = None
column = None
# 只报告能够检测到行和列号的第一行错误
for line in gcc_stderr.splitlines():
m = re.search(FN + ':(\d+):(\d+):.+?(error:.*$)', line)
if m:
lineno = int(m.group(1))
column = int(m.group(2))
exception_msg = m.group(3).strip()
break
# 链接错误通常是 'undefined ' 某某
if 'undefined ' in line:
parts = line.split(':')
exception_msg = parts[-1].strip()
# 匹配类似如下的错误信息
# /home/pgbovine/opt-cpp-backend/./usercode.c:2: undefined reference to `asdf'
if FN in parts[0]:
try:
lineno = int(parts[1])
except:
pass
break
ret = {'code': USER_PROGRAM,
'trace': [{'event': 'uncaught_exception',
'exception_msg': exception_msg,
'line': lineno}],
'gcc_stderr': gcc_stderr}
print json.dumps(ret)
# 清理临时随机文件夹
if os.path.exists(RANDOM_DIR) and os.path.isdir(RANDOM_DIR):
shutil.rmtree(RANDOM_DIR)
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment