From 157ae59fa0a408967b2357081cbee08e35d6501b Mon Sep 17 00:00:00 2001 From: longjin Date: Mon, 28 Jul 2025 09:49:48 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat(tests):=20=E6=B7=BB=E5=8A=A0gvisor?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E8=B0=83=E7=94=A8=E6=B5=8B=E8=AF=95=E5=A5=97?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增gvisor系统调用测试框架,包含Makefile、运行脚本、文档和测试用例。调整rootfs镜像大小为2G以适应测试需求。 Signed-off-by: longjin --- config/rootfs.toml | 2 +- user/apps/tests/syscall/gvisor/.gitignore | 2 + user/apps/tests/syscall/gvisor/Makefile | 75 +++ user/apps/tests/syscall/gvisor/README.md | 190 +++++++ .../tests/syscall/gvisor/blocklists/README.md | 43 ++ .../syscall/gvisor/blocklists/epoll_test | 7 + .../tests/syscall/gvisor/download_tests.sh | 210 ++++++++ user/apps/tests/syscall/gvisor/run_tests.sh | 472 ++++++++++++++++++ user/apps/tests/syscall/gvisor/whitelist.txt | 30 ++ 9 files changed, 1030 insertions(+), 1 deletion(-) create mode 100644 user/apps/tests/syscall/gvisor/.gitignore create mode 100644 user/apps/tests/syscall/gvisor/Makefile create mode 100644 user/apps/tests/syscall/gvisor/README.md create mode 100644 user/apps/tests/syscall/gvisor/blocklists/README.md create mode 100644 user/apps/tests/syscall/gvisor/blocklists/epoll_test create mode 100755 user/apps/tests/syscall/gvisor/download_tests.sh create mode 100755 user/apps/tests/syscall/gvisor/run_tests.sh create mode 100644 user/apps/tests/syscall/gvisor/whitelist.txt diff --git a/config/rootfs.toml b/config/rootfs.toml index 03ff2c9f0..2a8289522 100644 --- a/config/rootfs.toml +++ b/config/rootfs.toml @@ -2,7 +2,7 @@ # Filesystem type (options: `fat32`) fs_type = "fat32" # Size of the rootfs disk image (eg, `1G`, `1024M`) -size = "1G" +size = "2G" [partition] # Partition type (options: "none", "mbr", "gpt") diff --git a/user/apps/tests/syscall/gvisor/.gitignore b/user/apps/tests/syscall/gvisor/.gitignore new file mode 100644 index 000000000..2d9d34973 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/.gitignore @@ -0,0 +1,2 @@ +/tests/ +/results/ diff --git a/user/apps/tests/syscall/gvisor/Makefile b/user/apps/tests/syscall/gvisor/Makefile new file mode 100644 index 000000000..f432a417b --- /dev/null +++ b/user/apps/tests/syscall/gvisor/Makefile @@ -0,0 +1,75 @@ +# gvisor系统调用测试 Makefile +# 用于DragonOS项目 + +.PHONY: all download test clean help setup list + +# 默认目标 +all: setup test + +# 显示帮助信息 +help: + @echo "gvisor系统调用测试 Makefile" + @echo "" + @echo "可用目标:" + @echo " download - 下载gvisor测试套件" + @echo " setup - 设置测试环境(包含下载)" + @echo " test - 运行白名单中的测试(默认)" + @echo " test-all - 运行所有测试(包括非白名单)" + @echo " test-quick - 运行快速测试" + @echo " test-verbose - 详细模式运行测试" + @echo " list - 列出所有可用测试" + @echo " clean - 清理测试文件和结果" + @echo " help - 显示此帮助信息" + @echo "" + @echo "环境变量:" + @echo " SYSCALL_TEST_WORKDIR - 测试工作目录(默认: /tmp/gvisor_tests)" + @echo " TEST_TIMEOUT - 单个测试超时时间(默认: 300秒)" + +# 下载测试套件 +download: + @echo "下载gvisor测试套件..." + @./download_tests.sh + +# 设置测试环境 +setup: download + @echo "设置测试环境..." + @chmod +x run_tests.sh download_tests.sh + @mkdir -p results + +# 运行白名单中的测试(默认行为) +test: setup + @echo "运行gvisor系统调用测试(白名单模式)..." + @./run_tests.sh + +# 运行所有测试(包括非白名单) +test-all: setup + @echo "运行所有gvisor系统调用测试..." + @./run_tests.sh --no-whitelist + +# 运行快速测试 +test-quick: setup + @echo "运行快速测试..." + @./run_tests.sh --timeout 60 + +# 详细模式运行测试 +test-verbose: setup + @echo "详细模式运行测试..." + @./run_tests.sh -v + +# 列出所有可用测试 +list: setup + @./run_tests.sh -l + +# 清理测试文件和结果 +clean: + @echo "清理测试文件..." + @rm -rf tests/ + @rm -rf results/ + @rm -f gvisor-syscalls-tests.tar.xz + @echo "清理完成" + +# 清理所有(包括下载的文件) +distclean: clean + @echo "完全清理..." + @rm -rf tests/ results/ + @echo "完全清理完成" diff --git a/user/apps/tests/syscall/gvisor/README.md b/user/apps/tests/syscall/gvisor/README.md new file mode 100644 index 000000000..cf8e65620 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/README.md @@ -0,0 +1,190 @@ +# gvisor系统调用测试套件 + +这是DragonOS项目中用于运行gvisor系统调用测试的自动化工具。 + +仓库:https://cnb.cool/DragonOS-Community/test-suites + + +## 概述 + +gvisor是Google开发的容器运行时沙箱,它包含了大量的系统调用兼容性测试。这些测试可以用来验证操作系统的系统调用实现是否符合Linux标准。 + +本测试框架默认启用**白名单模式**,只运行`whitelist.txt`文件中指定的测试程序,这样可以逐步验证DragonOS的系统调用实现。同时,每个测试程序内部的测试用例可以通过`blocklists/`目录中的黑名单文件进行过滤。 + +## 快速开始 + +### 1. 下载并运行白名单测试 + +```bash +# 进入gvisor测试目录 +cd user/apps/tests/syscall/gvisor + +# 运行白名单中的测试(默认行为,会自动下载测试套件) +make test + +# 运行所有测试(包括非白名单) +make test-all +``` + +### 2. 仅下载测试套件 + +```bash +make download +# 或者直接运行脚本 +./download_tests.sh +``` + +### 3. 查看可用测试 + +```bash +make list +# 或者 +./run_tests.sh -l +``` + +## 详细使用方法 + +### 运行特定测试 + +```bash +# 运行单个测试(必须在白名单中) +./run_tests.sh socket_test + +# 运行匹配模式的测试(仅白名单中的) +./run_tests.sh "*socket*" + +# 禁用白名单,运行所有测试 +./run_tests.sh --no-whitelist + +# 详细输出模式 +./run_tests.sh -v socket_test + +# 设置超时时间 +./run_tests.sh -t 60 socket_test + +# 使用自定义白名单文件 +./run_tests.sh --whitelist my_custom_whitelist.txt +``` + +### 使用Makefile + +```bash +make help # 显示帮助 +make download # 仅下载测试套件 +make setup # 设置环境 +make test # 运行白名单中的测试(默认) +make test-all # 运行所有测试(包括非白名单) +make test-verbose # 详细模式运行 +make test-quick # 快速测试(短超时) +make list # 列出可用测试 +make clean # 清理结果文件 +make distclean # 完全清理 +``` + +## Blocklist机制 + +### 什么是Blocklist + +Blocklist用于屏蔽某些在当前环境下不适用或不稳定的测试子用例。这对于逐步完善系统调用支持非常有用。 + +### 配置Blocklist + +1. 在`blocklists/`目录下创建与测试名称相同的文件 +2. 每行一个要屏蔽的测试用例名称 +3. 支持通配符和注释 + +示例blocklist文件(`blocklists/socket_test`): +``` +# 屏蔽IPv6相关测试 +SocketTest.IPv6* + +# 屏蔽特定的不稳定测试 +SocketTest.UnstableTest +``` + +### 禁用白名单或Blocklist + +```bash +# 禁用白名单,运行所有测试程序 +./run_tests.sh --no-whitelist + +# 忽略所有blocklist(但仍使用白名单) +./run_tests.sh --no-blocklist + +# 同时禁用白名单和blocklist +./run_tests.sh --no-whitelist --no-blocklist +``` + +## 文件结构 + +``` +user/apps/tests/syscall/gvisor/ +├── download_tests.sh # 下载脚本 +├── run_tests.sh # 测试运行脚本 +├── Makefile # Make构建文件 +├── README.md # 说明文档 +├── blocklists/ # Blocklist目录 +│ ├── README.md # Blocklist说明 +│ └── socket_test # 示例blocklist +├── tests/ # 测试可执行文件(下载后生成) +└── results/ # 测试结果(运行后生成) + ├── failed_cases.txt # 失败用例列表 + ├── test_report.txt # 测试报告 + └── *.output # 各测试的详细输出 +``` + +## 环境变量 + +- `SYSCALL_TEST_WORKDIR`: 测试工作目录,默认为`/tmp/gvisor_tests` +- `TEST_TIMEOUT`: 单个测试的超时时间,默认300秒 + +## 测试报告 + +测试完成后会生成: + +1. **控制台输出**: 实时显示测试进度和结果 +2. **测试报告**: `results/test_report.txt` - 包含统计信息和失败用例 +3. **失败用例列表**: `results/failed_cases.txt` - 仅包含失败的测试名称 +4. **详细输出**: `results/*.output` - 每个测试的详细输出 + +## 注意事项 + +1. **网络依赖**: 首次运行需要从网络下载测试套件 +2. **存储空间**: 测试套件解压后约占用几百MB空间 +3. **运行时间**: 完整测试可能需要较长时间,建议先运行部分测试 +4. **权限要求**: 某些测试可能需要特定的系统权限 + +## 故障排除 + +### 下载失败 +```bash +# 检查网络连接 +wget --spider https://cnb.cool/DragonOS-Community/test-suites/-/releases/download/release_20250626/gvisor-syscalls-tests.tar.xz + +# 手动下载并放置 +wget -O gvisor-syscalls-tests.tar.xz https://cnb.cool/DragonOS-Community/test-suites/-/releases/download/release_20250626/gvisor-syscalls-tests.tar.xz +``` + +### MD5校验失败 +```bash +# 重新下载文件 +rm -f gvisor-syscalls-tests.tar.xz +./download_tests.sh +``` + +### 测试失败过多 +1. 检查系统调用实现是否完整 +2. 调整blocklist屏蔽已知问题 +3. 检查测试环境配置 + +## 贡献 + +如果发现测试相关的问题或有改进建议,请: + +1. 提交issue描述问题 +2. 更新相应的blocklist文件 +3. 提交patch修复脚本问题 + +## 许可证 + +本测试框架代码遵循DragonOS项目的许可证。gvisor测试套件本身遵循其原始许可证。 \ No newline at end of file diff --git a/user/apps/tests/syscall/gvisor/blocklists/README.md b/user/apps/tests/syscall/gvisor/blocklists/README.md new file mode 100644 index 000000000..9abc6a517 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/blocklists/README.md @@ -0,0 +1,43 @@ +# Blocklist 目录 + +这个目录包含用于屏蔽特定gvisor测试子用例的blocklist文件。 + +## 文件格式 + +每个blocklist文件对应一个测试可执行文件,文件名应与测试可执行文件名相同。 + +例如: +- `socket_test` - 对应测试可执行文件 `socket_test` +- `pipe_test` - 对应测试可执行文件 `pipe_test` + +## 内容格式 + +blocklist文件中每一行包含一个要屏蔽的测试用例名称: + +``` +# 这是注释行,会被忽略 +# 屏蔽某个特定的测试用例 +TestCase.SpecificTest +# 屏蔽某个测试套件下的所有测试 +TestSuite.* +# 屏蔽包含特定模式的测试 +*PatternName* +``` + +## 注意事项 + +- 以 `#` 开头的行会被视为注释并忽略 +- 空行会被忽略 +- 支持通配符模式匹配 +- 测试用例名称格式通常为 `TestSuite.TestCase` + +## 示例 + +如果要屏蔽socket_test中的某些测试,创建文件`socket_test`: + +``` +# 屏蔽IPv6相关的测试(暂不支持) +SocketTest.IPv6* +# 屏蔽特定的不稳定测试 +SocketTest.UnstableTest +``` \ No newline at end of file diff --git a/user/apps/tests/syscall/gvisor/blocklists/epoll_test b/user/apps/tests/syscall/gvisor/blocklists/epoll_test new file mode 100644 index 000000000..0c2a7b992 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/blocklists/epoll_test @@ -0,0 +1,7 @@ +# EpollTest.Timeout_NoRandomSave +# EpollTest.CycleOfOneDisallowed +# EpollTest.CycleOfThreeDisallowed +# `UnblockWithSignal` contains races. Better not to enable it. +# See https://github.com/asterinas/asterinas/pull/1035 for details. +# EpollTest.UnblockWithSignal + diff --git a/user/apps/tests/syscall/gvisor/download_tests.sh b/user/apps/tests/syscall/gvisor/download_tests.sh new file mode 100755 index 000000000..2918d850a --- /dev/null +++ b/user/apps/tests/syscall/gvisor/download_tests.sh @@ -0,0 +1,210 @@ +#!/bin/bash + +# gvisor系统调用测试套件下载和校验脚本 +# 用于DragonOS项目 + +set -e + +SCRIPT_DIR=$(dirname "$(realpath "$0")") +TESTS_DIR="$SCRIPT_DIR/tests" +TEST_ARCHIVE="gvisor-syscalls-tests.tar.xz" +TEST_URL="https://cnb.cool/DragonOS-Community/test-suites/-/releases/download/release_20250626/$TEST_ARCHIVE" +MD5SUM_URL="https://cnb.cool/DragonOS-Community/test-suites/-/releases/download/release_20250626/$TEST_ARCHIVE.md5sum" + +# 颜色定义 +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +NC='\033[0m' + +print_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +print_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# 检查命令是否存在 +check_command() { + if ! command -v "$1" &> /dev/null; then + print_error "命令 '$1' 未找到,请安装相应软件包" + exit 1 + fi +} + +# 检查必要的命令 +check_dependencies() { + print_info "检查依赖..." + check_command wget + check_command md5sum + check_command tar +} + +# 检查测试套件是否已存在且完整 +check_existing_tests() { + if [ -d "$TESTS_DIR" ] && [ "$(find "$TESTS_DIR" -name "*_test" | wc -l)" -gt 0 ]; then + print_info "发现已存在的测试套件" + return 0 + else + return 1 + fi +} + +# 下载文件 +download_file() { + local url="$1" + local output="$2" + local quiet_mode="${3:-false}" + + if [ "$quiet_mode" = "true" ]; then + # 静默下载(用于小文件如MD5) + if wget -q -O "$output" "$url" 2>/dev/null; then + return 0 + else + return 1 + fi + else + # 显示进度的下载(用于大文件) + print_info "从 $url 下载文件..." + if wget -q --show-progress -O "$output" "$url"; then + print_info "下载完成: $output" + return 0 + else + print_error "下载失败: $url" + return 1 + fi + fi +} + +# 获取期望的MD5值 +get_expected_md5() { + # 将日志输出重定向到stderr,避免混入返回值 + print_info "获取期望的MD5校验和..." >&2 + local temp_md5_file=$(mktemp) + + # 确保在函数退出时清理临时文件 + trap "rm -f '$temp_md5_file'" RETURN + + if download_file "$MD5SUM_URL" "$temp_md5_file" "true"; then + # 解析MD5文件,格式通常是: "md5hash filename" + local expected_md5=$(head -1 "$temp_md5_file" | cut -d' ' -f1) + + if [ -n "$expected_md5" ] && [ ${#expected_md5} -eq 32 ]; then + echo "$expected_md5" + return 0 + else + print_error "MD5文件格式无效或为空" >&2 + return 1 + fi + else + print_error "无法下载MD5校验和文件" >&2 + return 1 + fi +} + +# 验证MD5校验和 +verify_md5() { + local file="$1" + local expected="$2" + + print_info "验证MD5校验和..." + local actual_md5=$(md5sum "$file" | cut -d' ' -f1) + + if [ "$actual_md5" = "$expected" ]; then + print_info "MD5校验和验证成功" + return 0 + else + print_error "MD5校验和验证失败" + print_error "期望: $expected" + print_error "实际: $actual_md5" + return 1 + fi +} + +# 解压测试套件 +extract_tests() { + local archive="$1" + + print_info "解压测试套件..." + mkdir -p "$TESTS_DIR" + + if tar -xf "$archive" -C "$TESTS_DIR" --strip-components=1; then + print_info "解压完成" + return 0 + else + print_error "解压失败" + return 1 + fi +} + +# 清理临时文件 +cleanup_temp_files() { + print_info "清理临时文件..." + rm -f "$SCRIPT_DIR/$TEST_ARCHIVE" +} + +# 主函数 +main() { + print_info "开始检查和下载gvisor系统调用测试套件" + + # 检查依赖 + check_dependencies + + # 检查是否已存在测试套件 + if check_existing_tests; then + read -p "测试套件已存在,是否重新下载?(y/N) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + print_info "使用现有测试套件" + exit 0 + fi + print_warn "删除现有测试套件..." + rm -rf "$TESTS_DIR" + fi + + # 下载测试套件 + if ! download_file "$TEST_URL" "$SCRIPT_DIR/$TEST_ARCHIVE"; then + exit 1 + fi + + # 获取期望的MD5值 + local expected_md5 + if ! expected_md5=$(get_expected_md5); then + cleanup_temp_files + exit 1 + fi + + print_info "期望的MD5校验和: $expected_md5" + + # 验证MD5 + if ! verify_md5 "$SCRIPT_DIR/$TEST_ARCHIVE" "$expected_md5"; then + cleanup_temp_files + exit 1 + fi + + # 解压测试套件 + if ! extract_tests "$SCRIPT_DIR/$TEST_ARCHIVE"; then + cleanup_temp_files + exit 1 + fi + + # 清理临时文件 + cleanup_temp_files + + # 统计测试数量 + local test_count=$(find "$TESTS_DIR" -name "*_test" | wc -l) + print_info "测试套件安装完成,共包含 $test_count 个测试用例" + + # 设置执行权限 + find "$TESTS_DIR" -name "*_test" -exec chmod +x {} \; + + print_info "gvisor测试套件准备就绪" +} + +# 运行主函数 +main "$@" \ No newline at end of file diff --git a/user/apps/tests/syscall/gvisor/run_tests.sh b/user/apps/tests/syscall/gvisor/run_tests.sh new file mode 100755 index 000000000..b4a04ff0b --- /dev/null +++ b/user/apps/tests/syscall/gvisor/run_tests.sh @@ -0,0 +1,472 @@ +#!/bin/bash + +# gvisor系统调用测试运行脚本 +# 用于DragonOS项目 + +set -o pipefail + +SCRIPT_DIR=$(dirname "$(realpath "$0")") +TESTS_DIR="$SCRIPT_DIR/tests" +BLOCKLISTS_DIR="$SCRIPT_DIR/blocklists" +WHITELIST_FILE="$SCRIPT_DIR/whitelist.txt" +RESULTS_DIR="$SCRIPT_DIR/results" +TEMP_DIR="${SYSCALL_TEST_WORKDIR:-/tmp/gvisor_tests}" + +# 测试统计 +TOTAL_TESTS=0 +PASSED_TESTS=0 +FAILED_TESTS=0 +SKIPPED_TESTS=0 + +# 颜色定义 +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' + +# 输出文件 +FAILED_CASES_FILE="$RESULTS_DIR/failed_cases.txt" +TEST_REPORT_FILE="$RESULTS_DIR/test_report.txt" + +print_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +print_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +print_test() { + echo -e "${BLUE}[TEST]${NC} $1" +} + +# 显示使用帮助 +show_help() { + cat << EOF +用法: $0 [选项] [测试名称模式...] + +选项: + -h, --help 显示此帮助信息 + -l, --list 列出所有可用的测试用例 + -v, --verbose 详细输出模式 + -t, --timeout SEC 设置单个测试的超时时间(默认:300秒) + -j, --parallel NUM 并行运行的测试数量(默认:1) + --no-blocklist 忽略所有blocklist文件 + --extra-blocklist DIR 指定额外的blocklist目录 + --no-whitelist 禁用白名单模式,运行所有测试程序 + --whitelist FILE 指定白名单文件路径(默认:whitelist.txt) + +说明: + 默认启用白名单模式,只运行whitelist.txt中的测试程序。 + 测试用例仍使用blocklist过滤机制。 + +示例: + $0 运行白名单中的测试程序 + $0 socket_test 运行socket_test(如果在白名单中) + $0 --no-whitelist 运行所有测试程序 + $0 --whitelist my_whitelist.txt 使用自定义白名单文件 + $0 -j 4 -v 使用4个并行进程详细运行白名单中的测试 +EOF +} + +# 解析命令行参数 +parse_args() { + VERBOSE=false + TIMEOUT=300 + PARALLEL=1 + USE_BLOCKLIST=true + USE_WHITELIST=true + EXTRA_BLOCKLIST_DIRS="" + TEST_PATTERNS=() + + while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + show_help + exit 0 + ;; + -l|--list) + list_tests + exit 0 + ;; + -v|--verbose) + VERBOSE=true + shift + ;; + -t|--timeout) + TIMEOUT="$2" + shift 2 + ;; + -j|--parallel) + PARALLEL="$2" + shift 2 + ;; + --no-blocklist) + USE_BLOCKLIST=false + shift + ;; + --extra-blocklist) + EXTRA_BLOCKLIST_DIRS="$EXTRA_BLOCKLIST_DIRS $2" + shift 2 + ;; + --no-whitelist) + USE_WHITELIST=false + shift + ;; + --whitelist) + WHITELIST_FILE="$2" + shift 2 + ;; + -*) + print_error "未知选项: $1" + show_help + exit 1 + ;; + *) + TEST_PATTERNS+=("$1") + shift + ;; + esac + done +} + +# 列出所有测试用例 +list_tests() { + if [ ! -d "$TESTS_DIR" ]; then + print_error "测试目录不存在: $TESTS_DIR" + print_info "请先运行 ./download_tests.sh 下载测试套件" + exit 1 + fi + + if [ "$USE_WHITELIST" = true ]; then + print_info "白名单模式 - 可运行的测试用例 (来自: $WHITELIST_FILE):" + get_test_list | while read -r test_name; do + if [ -n "$test_name" ]; then + echo -e " ${GREEN}✓${NC} $test_name" + fi + done + + print_info "所有可用测试用例 (包括未在白名单中的):" + find "$TESTS_DIR" -name "*_test" -executable | while read -r test_file; do + local test_name=$(basename "$test_file") + if is_test_whitelisted "$test_name"; then + echo -e " ${GREEN}✓${NC} $test_name (在白名单中)" + else + echo -e " ${YELLOW}○${NC} $test_name (不在白名单中)" + fi + done + else + print_info "所有可用的测试用例:" + find "$TESTS_DIR" -name "*_test" -executable | while read -r test_file; do + local test_name=$(basename "$test_file") + echo " $test_name" + done + fi +} + +# 检查测试套件是否存在 +check_test_suite() { + if [ ! -d "$TESTS_DIR" ] || [ "$(find "$TESTS_DIR" -name "*_test" | wc -l)" -eq 0 ]; then + print_error "测试套件未找到" + print_info "请先运行 ./download_tests.sh 下载测试套件" + exit 1 + fi +} + +# 创建必要的目录 +setup_directories() { + mkdir -p "$RESULTS_DIR" + mkdir -p "$TEMP_DIR" + mkdir -p "$BLOCKLISTS_DIR" +} + +# 读取白名单中的测试程序 +get_whitelist_tests() { + if [ ! -f "$WHITELIST_FILE" ]; then + print_error "白名单文件不存在: $WHITELIST_FILE" + return 1 + fi + + # 读取白名单文件,忽略注释和空行 + grep -v '^#' "$WHITELIST_FILE" 2>/dev/null | grep -v '^$' | tr -d ' \t' +} + +# 检查测试是否在白名单中 +is_test_whitelisted() { + local test_name="$1" + + if [ "$USE_WHITELIST" = false ]; then + return 0 # 不使用白名单时,所有测试都允许 + fi + + local whitelisted_tests + if ! whitelisted_tests=$(get_whitelist_tests); then + return 1 + fi + + # 检查测试是否在白名单中 + echo "$whitelisted_tests" | grep -q "^${test_name}$" +} + +# 获取测试的blocklist +get_test_blocklist() { + local test_name="$1" + local blocked_subtests="" + + if [ "$USE_BLOCKLIST" = false ]; then + echo "" + return + fi + + # 检查主blocklist目录 + local blocklist_file="$BLOCKLISTS_DIR/$test_name" + if [ -f "$blocklist_file" ]; then + blocked_subtests=$(grep -v '^#' "$blocklist_file" 2>/dev/null | grep -v '^$' | tr '\n' ':') + fi + + # 检查额外的blocklist目录 + for extra_dir in $EXTRA_BLOCKLIST_DIRS; do + local extra_blocklist="$SCRIPT_DIR/$extra_dir/$test_name" + if [ -f "$extra_blocklist" ]; then + local extra_blocked=$(grep -v '^#' "$extra_blocklist" 2>/dev/null | grep -v '^$' | tr '\n' ':') + blocked_subtests="${blocked_subtests}${extra_blocked}" + fi + done + + echo "$blocked_subtests" +} + +# 运行单个测试 +run_single_test() { + local test_name="$1" + local test_path="$TESTS_DIR/$test_name" + + if [ ! -f "$test_path" ] || [ ! -x "$test_path" ]; then + print_warn "测试不存在或不可执行: $test_name" + return 2 + fi + + print_test "运行测试用例: $test_name" + + # 获取blocklist + local blocked_subtests=$(get_test_blocklist "$test_name") + + # 准备测试环境 + export TEST_TMPDIR="$TEMP_DIR" + + # 构建测试命令 + local test_cmd="$test_path" + if [ -n "$blocked_subtests" ]; then + test_cmd="$test_cmd --gtest_filter=-$blocked_subtests" + [ "$VERBOSE" = true ] && print_info "屏蔽的子测试: $blocked_subtests" + fi + + # 执行测试 + local test_output_file="$RESULTS_DIR/${test_name}.output" + local start_time=$(date +%s) + + # 始终将测试输出同时显示到控制台和保存到文件 + # 在verbose模式下显示额外的调试信息 + if [ "$VERBOSE" = true ]; then + print_info "执行命令: $test_cmd" + print_info "输出文件: $test_output_file" + fi + + # 使用tee同时输出到控制台和文件 + timeout "$TIMEOUT" bash -c "cd '$TESTS_DIR' && $test_cmd" 2>&1 | tee "$test_output_file" + local test_result=${PIPESTATUS[0]} + + local end_time=$(date +%s) + local duration=$((end_time - start_time)) + + # 清理临时文件 + rm -rf "$TEMP_DIR"/* + + # 处理测试结果 + case $test_result in + 0) + print_info "✓ $test_name 通过 (${duration}s)" + return 0 + ;; + 124) + print_error "✗ $test_name 超时 (>${TIMEOUT}s)" + return 1 + ;; + *) + print_error "✗ $test_name 失败 (${duration}s)" + return 1 + ;; + esac +} + +# 获取要运行的测试列表 +get_test_list() { + local all_tests=() + local candidate_tests=() + + # 获取所有测试文件 + while IFS= read -r test_file; do + all_tests+=($(basename "$test_file")) + done < <(find "$TESTS_DIR" -name "*_test" -executable | sort) + + # 应用白名单过滤 + if [ "$USE_WHITELIST" = true ]; then + for test in "${all_tests[@]}"; do + if is_test_whitelisted "$test"; then + candidate_tests+=("$test") + fi + done + + if [ ${#candidate_tests[@]} -eq 0 ]; then + print_warn "没有测试通过白名单过滤" + return 1 + fi + + [ "$VERBOSE" = true ] && print_info "白名单过滤后有 ${#candidate_tests[@]} 个测试可用" + else + candidate_tests=("${all_tests[@]}") + fi + + # 如果没有指定模式,返回候选测试 + if [ ${#TEST_PATTERNS[@]} -eq 0 ]; then + printf '%s\n' "${candidate_tests[@]}" + return + fi + + # 根据模式过滤测试 + local filtered_tests=() + for pattern in "${TEST_PATTERNS[@]}"; do + for test in "${candidate_tests[@]}"; do + if [[ $test == $pattern ]]; then + filtered_tests+=("$test") + fi + done + done + + printf '%s\n' "${filtered_tests[@]}" | sort -u +} + +# 运行所有测试 +run_all_tests() { + local test_list=() + + # 获取测试列表 + while IFS= read -r test_name; do + [ -n "$test_name" ] && test_list+=("$test_name") + done < <(get_test_list) + + if [ ${#test_list[@]} -eq 0 ]; then + print_warn "没有找到匹配的测试用例" + return 1 + fi + + print_info "准备运行 ${#test_list[@]} 个测试用例" + + # 初始化结果文件 + > "$FAILED_CASES_FILE" + + # 运行测试 + for test_name in "${test_list[@]}"; do + ((TOTAL_TESTS++)) + + if run_single_test "$test_name"; then + ((PASSED_TESTS++)) + else + ((FAILED_TESTS++)) + echo "$test_name" >> "$FAILED_CASES_FILE" + fi + + echo "---" + done +} + +# 生成测试报告 +generate_report() { + local report_file="$TEST_REPORT_FILE" + + { + echo "gvisor系统调用测试报告" + echo "==========================" + echo "测试时间: $(date)" + echo "测试目录: $TESTS_DIR" + echo "" + echo "测试统计:" + echo " 总测试数: $TOTAL_TESTS" + echo " 通过: $PASSED_TESTS" + echo " 失败: $FAILED_TESTS" + echo " 成功率: $([ $TOTAL_TESTS -gt 0 ] && echo "scale=2; $PASSED_TESTS * 100 / $TOTAL_TESTS" | bc || echo "0")%" + echo "" + + if [ $FAILED_TESTS -gt 0 ]; then + echo "失败的测试用例:" + cat "$FAILED_CASES_FILE" | sed 's/^/ /' + fi + } | tee "$report_file" +} + +# 显示测试结果 +show_results() { + echo "" + echo "===============================================" + print_info "测试完成" + echo -e "${GREEN}$PASSED_TESTS${NC} / ${GREEN}$TOTAL_TESTS${NC} 测试用例通过" + + if [ $FAILED_TESTS -gt 0 ]; then + echo -e "${RED}$FAILED_TESTS${NC} 个测试用例失败:" + [ -f "$FAILED_CASES_FILE" ] && cat "$FAILED_CASES_FILE" | sed "s/^/ ${RED}✗${NC} /" + fi + + echo "" + echo "详细报告保存在: $TEST_REPORT_FILE" +} + +# 清理函数 +cleanup() { + print_info "清理临时文件..." + rm -rf "$TEMP_DIR" +} + +# 主函数 +main() { + # 设置清理陷阱 + trap cleanup EXIT + + # 解析命令行参数 + parse_args "$@" + + # 检查测试套件 + check_test_suite + + # 设置目录 + setup_directories + + print_info "开始运行gvisor系统调用测试" + + # 显示运行配置 + if [ "$USE_WHITELIST" = true ]; then + print_info "白名单模式已启用: $WHITELIST_FILE" + fi + if [ "$USE_BLOCKLIST" = false ]; then + print_info "黑名单已禁用" + fi + + # 运行测试 + run_all_tests + + # 生成报告 + generate_report + + # 显示结果 + show_results + + # 返回适当的退出码 + [ $FAILED_TESTS -eq 0 ] +} + +# 运行主函数 +main "$@" \ No newline at end of file diff --git a/user/apps/tests/syscall/gvisor/whitelist.txt b/user/apps/tests/syscall/gvisor/whitelist.txt new file mode 100644 index 000000000..1557c6157 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/whitelist.txt @@ -0,0 +1,30 @@ +# gvisor测试程序白名单 +# 每行一个测试程序名称,只有在此列表中的测试程序才会被执行 +# 以#开头的行为注释,空行会被忽略 + +# 基础系统调用测试 +chdir_test +read_test + +# 文件系统相关测试 +#stat_test +#chmod_test +#chown_test + +# 进程相关测试 +#fork_test +#exec_test +#wait_test + +# 内存管理测试 +#mmap_test +#brk_test + +# 网络相关测试 +#bind_test +#connect_test +#listen_test + +# 信号处理测试 +#signal_test +#sigaction_test \ No newline at end of file From 4ac9f60b57c87d2f75258a9185fd4300b0486980 Mon Sep 17 00:00:00 2001 From: longjin Date: Sun, 21 Sep 2025 22:36:39 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat(gvisor):=20=E9=87=8D=E6=9E=84=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E8=BF=90=E8=A1=8C=E5=99=A8=E4=B8=BARust=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E5=B9=B6=E6=B7=BB=E5=8A=A0DADK=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将Shell脚本实现的测试运行器重写为Rust版本 - 添加DADK构建配置文件以支持DragonOS应用开发工具链 - 更新Makefile以支持Rust工具链和DADK安装目标 - 增强下载脚本功能,支持跳过已存在测试套件和强制下载选项 - 移除原有的run_tests.sh脚本,替换为更高效的Rust实现 Signed-off-by: longjin --- user/apps/tests/syscall/gvisor/Makefile | 116 ++-- user/apps/tests/syscall/gvisor/README.md | 224 +++++-- .../tests/syscall/gvisor/download_tests.sh | 39 +- user/apps/tests/syscall/gvisor/run_tests.sh | 472 -------------- .../tests/syscall/gvisor/runner/.gitignore | 21 + .../tests/syscall/gvisor/runner/Cargo.toml | 19 + .../apps/tests/syscall/gvisor/runner/Makefile | 69 ++ .../tests/syscall/gvisor/runner/README.md | 171 +++++ .../syscall/gvisor/runner/src/lib_sync.rs | 594 ++++++++++++++++++ .../tests/syscall/gvisor/runner/src/main.rs | 159 +++++ user/apps/tests/syscall/gvisor/whitelist.txt | 4 +- .../config/gvisor_syscall_tests-1.0.0.toml | 39 ++ 12 files changed, 1348 insertions(+), 579 deletions(-) delete mode 100755 user/apps/tests/syscall/gvisor/run_tests.sh create mode 100644 user/apps/tests/syscall/gvisor/runner/.gitignore create mode 100644 user/apps/tests/syscall/gvisor/runner/Cargo.toml create mode 100644 user/apps/tests/syscall/gvisor/runner/Makefile create mode 100644 user/apps/tests/syscall/gvisor/runner/README.md create mode 100644 user/apps/tests/syscall/gvisor/runner/src/lib_sync.rs create mode 100644 user/apps/tests/syscall/gvisor/runner/src/main.rs create mode 100644 user/dadk/config/gvisor_syscall_tests-1.0.0.toml diff --git a/user/apps/tests/syscall/gvisor/Makefile b/user/apps/tests/syscall/gvisor/Makefile index f432a417b..0eb8afae0 100644 --- a/user/apps/tests/syscall/gvisor/Makefile +++ b/user/apps/tests/syscall/gvisor/Makefile @@ -1,23 +1,44 @@ # gvisor系统调用测试 Makefile # 用于DragonOS项目 -.PHONY: all download test clean help setup list +# Rust工具链配置 +TOOLCHAIN="+nightly-2025-08-10-x86_64-unknown-linux-gnu" +RUSTFLAGS+="" -# 默认目标 -all: setup test +ifdef DADK_CURRENT_BUILD_DIR +# 如果是在dadk中编译,那么安装到dadk的安装目录中 +INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR) +else +# 如果是在本地编译,那么安装到当前目录下的install目录中 +INSTALL_DIR = ./install +endif + +ifeq ($(ARCH), x86_64) + export RUST_TARGET=x86_64-unknown-linux-musl +else ifeq ($(ARCH), riscv64) + export RUST_TARGET=riscv64gc-unknown-linux-gnu +else +# 默认为x86_64,用于本地编译 + export RUST_TARGET=x86_64-unknown-linux-musl +endif + +.PHONY: all build install download test list run clean help + +# 默认目标:构建并安装 +all: build install # 显示帮助信息 help: @echo "gvisor系统调用测试 Makefile" @echo "" @echo "可用目标:" + @echo " all - 构建并安装测试运行器(默认)" + @echo " build - 构建Rust测试运行器" + @echo " install - 安装测试运行器和必要文件" @echo " download - 下载gvisor测试套件" - @echo " setup - 设置测试环境(包含下载)" - @echo " test - 运行白名单中的测试(默认)" - @echo " test-all - 运行所有测试(包括非白名单)" - @echo " test-quick - 运行快速测试" - @echo " test-verbose - 详细模式运行测试" + @echo " test - 运行白名单中的测试" @echo " list - 列出所有可用测试" + @echo " run - 运行测试并传递参数(如:make run ARGS=\"-v epoll_test\")" @echo " clean - 清理测试文件和结果" @echo " help - 显示此帮助信息" @echo "" @@ -25,51 +46,60 @@ help: @echo " SYSCALL_TEST_WORKDIR - 测试工作目录(默认: /tmp/gvisor_tests)" @echo " TEST_TIMEOUT - 单个测试超时时间(默认: 300秒)" +# 构建Rust测试运行器 +build: + @echo "构建gvisor测试运行器..." + @cd runner && RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release + # 下载测试套件 download: @echo "下载gvisor测试套件..." @./download_tests.sh -# 设置测试环境 -setup: download - @echo "设置测试环境..." - @chmod +x run_tests.sh download_tests.sh - @mkdir -p results +# 安装到目标目录 +install: build + @echo "安装gvisor测试套件到 $(INSTALL_DIR)" + @mkdir -p $(INSTALL_DIR) + # 安装Rust测试运行器二进制文件 + @cp -f runner/target/$(RUST_TARGET)/release/runner $(INSTALL_DIR)/gvisor-test-runner + # 安装测试配置文件 + @cp -f whitelist.txt $(INSTALL_DIR)/ + @cp -rf blocklists $(INSTALL_DIR)/ + # 安装下载脚本(用于目标系统上下载测试) + @cp -f download_tests.sh $(INSTALL_DIR)/ + @chmod +x $(INSTALL_DIR)/download_tests.sh + @chmod +x $(INSTALL_DIR)/gvisor-test-runner + @echo "安装完成" -# 运行白名单中的测试(默认行为) -test: setup - @echo "运行gvisor系统调用测试(白名单模式)..." - @./run_tests.sh +# 运行测试 +test: build + @echo "运行gvisor系统调用测试..." + @if [ ! -d tests ]; then \ + echo "测试套件不存在,正在下载..."; \ + ./download_tests.sh; \ + fi + @./runner/target/$(RUST_TARGET)/release/runner -# 运行所有测试(包括非白名单) -test-all: setup - @echo "运行所有gvisor系统调用测试..." - @./run_tests.sh --no-whitelist +# 列出所有测试 +list: build + @if [ ! -d tests ]; then \ + echo "测试套件不存在,正在下载..."; \ + ./download_tests.sh; \ + fi + @./runner/target/$(RUST_TARGET)/release/runner --list -# 运行快速测试 -test-quick: setup - @echo "运行快速测试..." - @./run_tests.sh --timeout 60 +# 运行测试并传递参数 +run: build + @if [ ! -d tests ]; then \ + echo "测试套件不存在,正在下载..."; \ + ./download_tests.sh; \ + fi + @./runner/target/$(RUST_TARGET)/release/runner $(ARGS) -# 详细模式运行测试 -test-verbose: setup - @echo "详细模式运行测试..." - @./run_tests.sh -v - -# 列出所有可用测试 -list: setup - @./run_tests.sh -l - -# 清理测试文件和结果 +# 清理 clean: - @echo "清理测试文件..." - @rm -rf tests/ + @echo "清理测试文件和结果..." @rm -rf results/ @rm -f gvisor-syscalls-tests.tar.xz + @cd runner && cargo clean @echo "清理完成" - -# 清理所有(包括下载的文件) -distclean: clean - @echo "完全清理..." - @rm -rf tests/ results/ - @echo "完全清理完成" diff --git a/user/apps/tests/syscall/gvisor/README.md b/user/apps/tests/syscall/gvisor/README.md index cf8e65620..e157365f7 100644 --- a/user/apps/tests/syscall/gvisor/README.md +++ b/user/apps/tests/syscall/gvisor/README.md @@ -2,8 +2,7 @@ 这是DragonOS项目中用于运行gvisor系统调用测试的自动化工具。 -仓库:https://cnb.cool/DragonOS-Community/test-suites - +测试用例仓库:https://cnb.cool/DragonOS-Community/test-suites ## 概述 @@ -23,7 +22,7 @@ cd user/apps/tests/syscall/gvisor make test # 运行所有测试(包括非白名单) -make test-all +make run ARGS="--no-whitelist" ``` ### 2. 仅下载测试套件 @@ -38,94 +37,183 @@ make download ```bash make list -# 或者 -./run_tests.sh -l ``` ## 详细使用方法 -### 运行特定测试 +### 基本使用 + +通过Makefile可以方便地运行测试: ```bash -# 运行单个测试(必须在白名单中) -./run_tests.sh socket_test +# 显示所有可用命令 +make help -# 运行匹配模式的测试(仅白名单中的) -./run_tests.sh "*socket*" +# 构建并安装测试运行器 +make -# 禁用白名单,运行所有测试 -./run_tests.sh --no-whitelist +# 下载测试套件 +make download -# 详细输出模式 -./run_tests.sh -v socket_test +# 运行白名单中的测试(自动下载测试套件) +make test + +# 列出所有可用测试 +make list -# 设置超时时间 -./run_tests.sh -t 60 socket_test +# 运行测试并传递参数 +make run ARGS="-v" +make run ARGS="epoll_test" +make run ARGS="-j 4 --no-whitelist" -# 使用自定义白名单文件 -./run_tests.sh --whitelist my_custom_whitelist.txt +# 清理测试结果 +make clean ``` -### 使用Makefile +### 使用示例 ```bash -make help # 显示帮助 -make download # 仅下载测试套件 -make setup # 设置环境 -make test # 运行白名单中的测试(默认) -make test-all # 运行所有测试(包括非白名单) -make test-verbose # 详细模式运行 -make test-quick # 快速测试(短超时) -make list # 列出可用测试 -make clean # 清理结果文件 -make distclean # 完全清理 +# 运行特定测试 +make run ARGS="epoll_test" + +# 使用模式匹配运行多个测试 +make run ARGS="socket*" + +# 详细输出模式 +make run ARGS="-v" + +# 并行运行4个测试 +make run ARGS="-j 4" + +# 禁用白名单,运行所有测试程序 +make run ARGS="--no-whitelist" + +# 忽略所有blocklist文件 +make run ARGS="--no-blocklist" + +# 设置超时时间为60秒 +make run ARGS="-t 60" + +# 组合多个参数 +make run ARGS="-v -j 2 epoll_test" ``` -## Blocklist机制 +## 添加新的测试 -### 什么是Blocklist +### 添加测试程序到白名单 -Blocklist用于屏蔽某些在当前环境下不适用或不稳定的测试子用例。这对于逐步完善系统调用支持非常有用。 +1. 编辑 `whitelist.txt` 文件 +2. 每行添加一个测试程序名称(不带路径) +3. 支持注释(以 `#` 开头) + +示例: +```text +# 核心系统调用测试 +socket_test +epoll_test +fcntl_test +ioctl_test -### 配置Blocklist +# 文件系统测试 +open_test +stat_test +mmap_test +``` + +### 创建blocklist过滤测试用例 + +对于每个测试程序,可以创建blocklist文件来屏蔽特定的测试用例: -1. 在`blocklists/`目录下创建与测试名称相同的文件 -2. 每行一个要屏蔽的测试用例名称 -3. 支持通配符和注释 +1. 在 `blocklists/` 目录下创建与测试程序同名的文件 +2. 每行添加要屏蔽的测试用例名称 +3. 支持注释和空行 示例blocklist文件(`blocklists/socket_test`): -``` -# 屏蔽IPv6相关测试 +```text +# 屏蔽IPv6相关测试(DragonOS暂不支持IPv6) SocketTest.IPv6* +SocketTest.IPv6Socket* + +# 屏蔽需要特殊权限的测试 +SocketTest.PrivilegedSocket -# 屏蔽特定的不稳定测试 -SocketTest.UnstableTest +# 屏蔽已知不稳定的测试 +SocketTest.UnimplementedFeature ``` -### 禁用白名单或Blocklist +### Blocklist文件格式 -```bash -# 禁用白名单,运行所有测试程序 -./run_tests.sh --no-whitelist +- 每行一个测试用例名称或模式 +- 支持通配符(`*` 匹配任意字符) +- 注释以 `#` 开头 +- 空行会被忽略 +- 测试用例名称通常格式为 `TestSuite.TestName` -# 忽略所有blocklist(但仍使用白名单) -./run_tests.sh --no-blocklist +## Blocklist机制详解 -# 同时禁用白名单和blocklist -./run_tests.sh --no-whitelist --no-blocklist -``` +### 什么是Blocklist + +Blocklist用于屏蔽某些在当前环境下不适用或不稳定的测试子用例。这对于逐步完善系统调用支持非常有用。 + +### Blocklist的工作原理 + +1. 当运行测试时,测试运行器会自动查找与测试程序同名的blocklist文件 +2. 文件位于 `blocklists/` 目录下 +3. 支持多个额外的blocklist目录(通过 `--extra-blocklist` 参数) +4. 匹配的测试用例会被跳过,不计入统计结果 + +### 示例:完整的测试配置 + +假设我们要添加对 `epoll_test` 的支持: + +1. **添加到白名单** (`whitelist.txt`): + ```text + epoll_test + ``` + +2. **创建blocklist** (`blocklists/epoll_test`): + ```text + # 屏蔽超时测试(需要更精确的时间控制) + EpollTest.Timeout* + + # 屏蔽循环测试(可能导致死锁) + EpollTest.CycleOfOneDisallowed + EpollTest.CycleOfThreeDisallowed + + # 屏蔽信号竞争测试 + # UnblockWithSignal contains races. Better not to enable it. + EpollTest.UnblockWithSignal + ``` + +3. **运行测试**: + ```bash + # 使用Makefile运行所有白名单测试(会自动应用blocklist) + make test + + # 只运行特定测试 + make run ARGS="epoll_test" + + # 查看详细输出 + make run ARGS="-v epoll_test" + ``` ## 文件结构 ``` user/apps/tests/syscall/gvisor/ ├── download_tests.sh # 下载脚本 -├── run_tests.sh # 测试运行脚本 ├── Makefile # Make构建文件 ├── README.md # 说明文档 +├── whitelist.txt # 测试程序白名单 +├── runner/ # Rust测试运行器 +│ ├── Cargo.toml +│ ├── Makefile +│ └── src/ +│ ├── main.rs +│ └── lib_sync.rs ├── blocklists/ # Blocklist目录 │ ├── README.md # Blocklist说明 -│ └── socket_test # 示例blocklist +│ └── epoll_test # 示例blocklist ├── tests/ # 测试可执行文件(下载后生成) └── results/ # 测试结果(运行后生成) ├── failed_cases.txt # 失败用例列表 @@ -137,6 +225,7 @@ user/apps/tests/syscall/gvisor/ - `SYSCALL_TEST_WORKDIR`: 测试工作目录,默认为`/tmp/gvisor_tests` - `TEST_TIMEOUT`: 单个测试的超时时间,默认300秒 +- `RUSTFLAGS`: Rust编译器标志 ## 测试报告 @@ -147,12 +236,32 @@ user/apps/tests/syscall/gvisor/ 3. **失败用例列表**: `results/failed_cases.txt` - 仅包含失败的测试名称 4. **详细输出**: `results/*.output` - 每个测试的详细输出 +## 开发者指南 + +### 编译和开发 + +```bash +# 构建Rust测试运行器 +make build + +# 安装到指定目录 +make install +``` + +### 性能提示 + +- 默认串行运行测试,确保稳定性 +- 如需并行测试,使用 `-j` 参数(谨慎使用) +- 合理设置超时时间,避免长时间等待 +- 使用blocklist跳过已知问题的测试 + ## 注意事项 -1. **网络依赖**: 首次运行需要从网络下载测试套件 -2. **存储空间**: 测试套件解压后约占用几百MB空间 +1. **网络依赖**: 首次运行 `make test` 时会自动下载测试套件 +2. **存储空间**: 测试套件解压后约占用1.1GB空间 3. **运行时间**: 完整测试可能需要较长时间,建议先运行部分测试 4. **权限要求**: 某些测试可能需要特定的系统权限 +5. **自动下载**: 运行 `make test` 或 `make list` 时会自动下载所需的测试套件 ## 故障排除 @@ -172,10 +281,16 @@ rm -f gvisor-syscalls-tests.tar.xz ./download_tests.sh ``` +### 测试运行失败 +1. 检查测试二进制文件是否存在 +2. 确认可执行权限 +3. 查看详细输出了解失败原因 + ### 测试失败过多 1. 检查系统调用实现是否完整 2. 调整blocklist屏蔽已知问题 3. 检查测试环境配置 +4. 考虑增加超时时间 ## 贡献 @@ -184,7 +299,8 @@ rm -f gvisor-syscalls-tests.tar.xz 1. 提交issue描述问题 2. 更新相应的blocklist文件 3. 提交patch修复脚本问题 +4. 帮助完善测试覆盖 ## 许可证 -本测试框架代码遵循DragonOS项目的许可证。gvisor测试套件本身遵循其原始许可证。 \ No newline at end of file +本测试框架代码遵循GPLv2许可证。gvisor测试套件本身遵循其原始许可证。 diff --git a/user/apps/tests/syscall/gvisor/download_tests.sh b/user/apps/tests/syscall/gvisor/download_tests.sh index 2918d850a..3fbfd80db 100755 --- a/user/apps/tests/syscall/gvisor/download_tests.sh +++ b/user/apps/tests/syscall/gvisor/download_tests.sh @@ -150,21 +150,44 @@ cleanup_temp_files() { # 主函数 main() { + local skip_if_exists=false + local force_download=false + + # 检查参数 + for arg in "$@"; do + case "$arg" in + --skip-if-exists) + skip_if_exists=true + ;; + --force-download) + force_download=true + ;; + esac + done + print_info "开始检查和下载gvisor系统调用测试套件" - + # 检查依赖 check_dependencies - + # 检查是否已存在测试套件 if check_existing_tests; then - read -p "测试套件已存在,是否重新下载?(y/N) " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - print_info "使用现有测试套件" + if [ "$skip_if_exists" = true ]; then + print_info "测试套件已存在,跳过下载" exit 0 + elif [ "$force_download" = true ]; then + print_warn "强制重新下载,删除现有测试套件..." + rm -rf "$TESTS_DIR" + else + read -p "测试套件已存在,是否重新下载?(y/N) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + print_info "使用现有测试套件" + exit 0 + fi + print_warn "删除现有测试套件..." + rm -rf "$TESTS_DIR" fi - print_warn "删除现有测试套件..." - rm -rf "$TESTS_DIR" fi # 下载测试套件 diff --git a/user/apps/tests/syscall/gvisor/run_tests.sh b/user/apps/tests/syscall/gvisor/run_tests.sh deleted file mode 100755 index b4a04ff0b..000000000 --- a/user/apps/tests/syscall/gvisor/run_tests.sh +++ /dev/null @@ -1,472 +0,0 @@ -#!/bin/bash - -# gvisor系统调用测试运行脚本 -# 用于DragonOS项目 - -set -o pipefail - -SCRIPT_DIR=$(dirname "$(realpath "$0")") -TESTS_DIR="$SCRIPT_DIR/tests" -BLOCKLISTS_DIR="$SCRIPT_DIR/blocklists" -WHITELIST_FILE="$SCRIPT_DIR/whitelist.txt" -RESULTS_DIR="$SCRIPT_DIR/results" -TEMP_DIR="${SYSCALL_TEST_WORKDIR:-/tmp/gvisor_tests}" - -# 测试统计 -TOTAL_TESTS=0 -PASSED_TESTS=0 -FAILED_TESTS=0 -SKIPPED_TESTS=0 - -# 颜色定义 -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -BLUE='\033[0;34m' -NC='\033[0m' - -# 输出文件 -FAILED_CASES_FILE="$RESULTS_DIR/failed_cases.txt" -TEST_REPORT_FILE="$RESULTS_DIR/test_report.txt" - -print_info() { - echo -e "${GREEN}[INFO]${NC} $1" -} - -print_warn() { - echo -e "${YELLOW}[WARN]${NC} $1" -} - -print_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -print_test() { - echo -e "${BLUE}[TEST]${NC} $1" -} - -# 显示使用帮助 -show_help() { - cat << EOF -用法: $0 [选项] [测试名称模式...] - -选项: - -h, --help 显示此帮助信息 - -l, --list 列出所有可用的测试用例 - -v, --verbose 详细输出模式 - -t, --timeout SEC 设置单个测试的超时时间(默认:300秒) - -j, --parallel NUM 并行运行的测试数量(默认:1) - --no-blocklist 忽略所有blocklist文件 - --extra-blocklist DIR 指定额外的blocklist目录 - --no-whitelist 禁用白名单模式,运行所有测试程序 - --whitelist FILE 指定白名单文件路径(默认:whitelist.txt) - -说明: - 默认启用白名单模式,只运行whitelist.txt中的测试程序。 - 测试用例仍使用blocklist过滤机制。 - -示例: - $0 运行白名单中的测试程序 - $0 socket_test 运行socket_test(如果在白名单中) - $0 --no-whitelist 运行所有测试程序 - $0 --whitelist my_whitelist.txt 使用自定义白名单文件 - $0 -j 4 -v 使用4个并行进程详细运行白名单中的测试 -EOF -} - -# 解析命令行参数 -parse_args() { - VERBOSE=false - TIMEOUT=300 - PARALLEL=1 - USE_BLOCKLIST=true - USE_WHITELIST=true - EXTRA_BLOCKLIST_DIRS="" - TEST_PATTERNS=() - - while [[ $# -gt 0 ]]; do - case $1 in - -h|--help) - show_help - exit 0 - ;; - -l|--list) - list_tests - exit 0 - ;; - -v|--verbose) - VERBOSE=true - shift - ;; - -t|--timeout) - TIMEOUT="$2" - shift 2 - ;; - -j|--parallel) - PARALLEL="$2" - shift 2 - ;; - --no-blocklist) - USE_BLOCKLIST=false - shift - ;; - --extra-blocklist) - EXTRA_BLOCKLIST_DIRS="$EXTRA_BLOCKLIST_DIRS $2" - shift 2 - ;; - --no-whitelist) - USE_WHITELIST=false - shift - ;; - --whitelist) - WHITELIST_FILE="$2" - shift 2 - ;; - -*) - print_error "未知选项: $1" - show_help - exit 1 - ;; - *) - TEST_PATTERNS+=("$1") - shift - ;; - esac - done -} - -# 列出所有测试用例 -list_tests() { - if [ ! -d "$TESTS_DIR" ]; then - print_error "测试目录不存在: $TESTS_DIR" - print_info "请先运行 ./download_tests.sh 下载测试套件" - exit 1 - fi - - if [ "$USE_WHITELIST" = true ]; then - print_info "白名单模式 - 可运行的测试用例 (来自: $WHITELIST_FILE):" - get_test_list | while read -r test_name; do - if [ -n "$test_name" ]; then - echo -e " ${GREEN}✓${NC} $test_name" - fi - done - - print_info "所有可用测试用例 (包括未在白名单中的):" - find "$TESTS_DIR" -name "*_test" -executable | while read -r test_file; do - local test_name=$(basename "$test_file") - if is_test_whitelisted "$test_name"; then - echo -e " ${GREEN}✓${NC} $test_name (在白名单中)" - else - echo -e " ${YELLOW}○${NC} $test_name (不在白名单中)" - fi - done - else - print_info "所有可用的测试用例:" - find "$TESTS_DIR" -name "*_test" -executable | while read -r test_file; do - local test_name=$(basename "$test_file") - echo " $test_name" - done - fi -} - -# 检查测试套件是否存在 -check_test_suite() { - if [ ! -d "$TESTS_DIR" ] || [ "$(find "$TESTS_DIR" -name "*_test" | wc -l)" -eq 0 ]; then - print_error "测试套件未找到" - print_info "请先运行 ./download_tests.sh 下载测试套件" - exit 1 - fi -} - -# 创建必要的目录 -setup_directories() { - mkdir -p "$RESULTS_DIR" - mkdir -p "$TEMP_DIR" - mkdir -p "$BLOCKLISTS_DIR" -} - -# 读取白名单中的测试程序 -get_whitelist_tests() { - if [ ! -f "$WHITELIST_FILE" ]; then - print_error "白名单文件不存在: $WHITELIST_FILE" - return 1 - fi - - # 读取白名单文件,忽略注释和空行 - grep -v '^#' "$WHITELIST_FILE" 2>/dev/null | grep -v '^$' | tr -d ' \t' -} - -# 检查测试是否在白名单中 -is_test_whitelisted() { - local test_name="$1" - - if [ "$USE_WHITELIST" = false ]; then - return 0 # 不使用白名单时,所有测试都允许 - fi - - local whitelisted_tests - if ! whitelisted_tests=$(get_whitelist_tests); then - return 1 - fi - - # 检查测试是否在白名单中 - echo "$whitelisted_tests" | grep -q "^${test_name}$" -} - -# 获取测试的blocklist -get_test_blocklist() { - local test_name="$1" - local blocked_subtests="" - - if [ "$USE_BLOCKLIST" = false ]; then - echo "" - return - fi - - # 检查主blocklist目录 - local blocklist_file="$BLOCKLISTS_DIR/$test_name" - if [ -f "$blocklist_file" ]; then - blocked_subtests=$(grep -v '^#' "$blocklist_file" 2>/dev/null | grep -v '^$' | tr '\n' ':') - fi - - # 检查额外的blocklist目录 - for extra_dir in $EXTRA_BLOCKLIST_DIRS; do - local extra_blocklist="$SCRIPT_DIR/$extra_dir/$test_name" - if [ -f "$extra_blocklist" ]; then - local extra_blocked=$(grep -v '^#' "$extra_blocklist" 2>/dev/null | grep -v '^$' | tr '\n' ':') - blocked_subtests="${blocked_subtests}${extra_blocked}" - fi - done - - echo "$blocked_subtests" -} - -# 运行单个测试 -run_single_test() { - local test_name="$1" - local test_path="$TESTS_DIR/$test_name" - - if [ ! -f "$test_path" ] || [ ! -x "$test_path" ]; then - print_warn "测试不存在或不可执行: $test_name" - return 2 - fi - - print_test "运行测试用例: $test_name" - - # 获取blocklist - local blocked_subtests=$(get_test_blocklist "$test_name") - - # 准备测试环境 - export TEST_TMPDIR="$TEMP_DIR" - - # 构建测试命令 - local test_cmd="$test_path" - if [ -n "$blocked_subtests" ]; then - test_cmd="$test_cmd --gtest_filter=-$blocked_subtests" - [ "$VERBOSE" = true ] && print_info "屏蔽的子测试: $blocked_subtests" - fi - - # 执行测试 - local test_output_file="$RESULTS_DIR/${test_name}.output" - local start_time=$(date +%s) - - # 始终将测试输出同时显示到控制台和保存到文件 - # 在verbose模式下显示额外的调试信息 - if [ "$VERBOSE" = true ]; then - print_info "执行命令: $test_cmd" - print_info "输出文件: $test_output_file" - fi - - # 使用tee同时输出到控制台和文件 - timeout "$TIMEOUT" bash -c "cd '$TESTS_DIR' && $test_cmd" 2>&1 | tee "$test_output_file" - local test_result=${PIPESTATUS[0]} - - local end_time=$(date +%s) - local duration=$((end_time - start_time)) - - # 清理临时文件 - rm -rf "$TEMP_DIR"/* - - # 处理测试结果 - case $test_result in - 0) - print_info "✓ $test_name 通过 (${duration}s)" - return 0 - ;; - 124) - print_error "✗ $test_name 超时 (>${TIMEOUT}s)" - return 1 - ;; - *) - print_error "✗ $test_name 失败 (${duration}s)" - return 1 - ;; - esac -} - -# 获取要运行的测试列表 -get_test_list() { - local all_tests=() - local candidate_tests=() - - # 获取所有测试文件 - while IFS= read -r test_file; do - all_tests+=($(basename "$test_file")) - done < <(find "$TESTS_DIR" -name "*_test" -executable | sort) - - # 应用白名单过滤 - if [ "$USE_WHITELIST" = true ]; then - for test in "${all_tests[@]}"; do - if is_test_whitelisted "$test"; then - candidate_tests+=("$test") - fi - done - - if [ ${#candidate_tests[@]} -eq 0 ]; then - print_warn "没有测试通过白名单过滤" - return 1 - fi - - [ "$VERBOSE" = true ] && print_info "白名单过滤后有 ${#candidate_tests[@]} 个测试可用" - else - candidate_tests=("${all_tests[@]}") - fi - - # 如果没有指定模式,返回候选测试 - if [ ${#TEST_PATTERNS[@]} -eq 0 ]; then - printf '%s\n' "${candidate_tests[@]}" - return - fi - - # 根据模式过滤测试 - local filtered_tests=() - for pattern in "${TEST_PATTERNS[@]}"; do - for test in "${candidate_tests[@]}"; do - if [[ $test == $pattern ]]; then - filtered_tests+=("$test") - fi - done - done - - printf '%s\n' "${filtered_tests[@]}" | sort -u -} - -# 运行所有测试 -run_all_tests() { - local test_list=() - - # 获取测试列表 - while IFS= read -r test_name; do - [ -n "$test_name" ] && test_list+=("$test_name") - done < <(get_test_list) - - if [ ${#test_list[@]} -eq 0 ]; then - print_warn "没有找到匹配的测试用例" - return 1 - fi - - print_info "准备运行 ${#test_list[@]} 个测试用例" - - # 初始化结果文件 - > "$FAILED_CASES_FILE" - - # 运行测试 - for test_name in "${test_list[@]}"; do - ((TOTAL_TESTS++)) - - if run_single_test "$test_name"; then - ((PASSED_TESTS++)) - else - ((FAILED_TESTS++)) - echo "$test_name" >> "$FAILED_CASES_FILE" - fi - - echo "---" - done -} - -# 生成测试报告 -generate_report() { - local report_file="$TEST_REPORT_FILE" - - { - echo "gvisor系统调用测试报告" - echo "==========================" - echo "测试时间: $(date)" - echo "测试目录: $TESTS_DIR" - echo "" - echo "测试统计:" - echo " 总测试数: $TOTAL_TESTS" - echo " 通过: $PASSED_TESTS" - echo " 失败: $FAILED_TESTS" - echo " 成功率: $([ $TOTAL_TESTS -gt 0 ] && echo "scale=2; $PASSED_TESTS * 100 / $TOTAL_TESTS" | bc || echo "0")%" - echo "" - - if [ $FAILED_TESTS -gt 0 ]; then - echo "失败的测试用例:" - cat "$FAILED_CASES_FILE" | sed 's/^/ /' - fi - } | tee "$report_file" -} - -# 显示测试结果 -show_results() { - echo "" - echo "===============================================" - print_info "测试完成" - echo -e "${GREEN}$PASSED_TESTS${NC} / ${GREEN}$TOTAL_TESTS${NC} 测试用例通过" - - if [ $FAILED_TESTS -gt 0 ]; then - echo -e "${RED}$FAILED_TESTS${NC} 个测试用例失败:" - [ -f "$FAILED_CASES_FILE" ] && cat "$FAILED_CASES_FILE" | sed "s/^/ ${RED}✗${NC} /" - fi - - echo "" - echo "详细报告保存在: $TEST_REPORT_FILE" -} - -# 清理函数 -cleanup() { - print_info "清理临时文件..." - rm -rf "$TEMP_DIR" -} - -# 主函数 -main() { - # 设置清理陷阱 - trap cleanup EXIT - - # 解析命令行参数 - parse_args "$@" - - # 检查测试套件 - check_test_suite - - # 设置目录 - setup_directories - - print_info "开始运行gvisor系统调用测试" - - # 显示运行配置 - if [ "$USE_WHITELIST" = true ]; then - print_info "白名单模式已启用: $WHITELIST_FILE" - fi - if [ "$USE_BLOCKLIST" = false ]; then - print_info "黑名单已禁用" - fi - - # 运行测试 - run_all_tests - - # 生成报告 - generate_report - - # 显示结果 - show_results - - # 返回适当的退出码 - [ $FAILED_TESTS -eq 0 ] -} - -# 运行主函数 -main "$@" \ No newline at end of file diff --git a/user/apps/tests/syscall/gvisor/runner/.gitignore b/user/apps/tests/syscall/gvisor/runner/.gitignore new file mode 100644 index 000000000..786a31746 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/runner/.gitignore @@ -0,0 +1,21 @@ +# Rust构建产物 +/target/ +Cargo.lock + +# IDE文件 +.vscode/ +.idea/ +*.swp +*.swo + +# 操作系统文件 +.DS_Store +Thumbs.db + +# 临时文件 +*~ +*.tmp +*.bak + +# 测试输出文件(如果在此目录运行) +/results/ \ No newline at end of file diff --git a/user/apps/tests/syscall/gvisor/runner/Cargo.toml b/user/apps/tests/syscall/gvisor/runner/Cargo.toml new file mode 100644 index 000000000..a762c06b8 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/runner/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "gvisor-test-runner" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "runner" +path = "src/main.rs" + +[dependencies] +clap = { version = "4.0", features = ["derive"] } +anyhow = "1.0" +regex = "1.0" +chrono = { version = "0.4", features = ["serde"] } +log = "0.4" +env_logger = "0.10" + +[profile.release] +lto = true diff --git a/user/apps/tests/syscall/gvisor/runner/Makefile b/user/apps/tests/syscall/gvisor/runner/Makefile new file mode 100644 index 000000000..bdaa42c16 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/runner/Makefile @@ -0,0 +1,69 @@ +# Makefile for gvisor-test-runner + +# 默认目标 +.PHONY: all build release clean install test help + +# 默认构建 +all: build + +# 开发构建 +build: + cargo build + +# Release构建(推荐) +release: + cargo build --release + +# 清理构建文件 +clean: + cargo clean + +# 运行测试(如果有单元测试) +test: + cargo test + +# 检查代码 +check: + cargo check + +# 格式化代码 +fmt: + cargo fmt + +# 代码检查 +clippy: + cargo clippy + +# 列出测试用例 +list: release + ./target/release/runner --list + +# 运行测试(使用默认配置) +run: release + ./target/release/runner + +# 显示帮助 +help: release + ./target/release/runner --help + +# 显示此Makefile的帮助 +show-help: + @echo "可用的make目标:" + @echo " all - 构建开发版本(默认)" + @echo " build - 构建开发版本" + @echo " release - 构建release版本(推荐)" + @echo " clean - 清理构建文件" + @echo " install - 安装到系统(需要sudo)" + @echo " test - 运行单元测试" + @echo " check - 检查代码编译" + @echo " fmt - 格式化代码" + @echo " clippy - 运行代码检查" + @echo " list - 列出所有测试用例" + @echo " run - 运行测试(默认配置)" + @echo " help - 显示程序帮助" + @echo "" + @echo "使用示例:" + @echo " make release # 构建release版本" + @echo " make list # 列出测试用例" + @echo " make run # 运行白名单中的测试" + @echo " ./target/release/runner --help # 查看详细帮助" diff --git a/user/apps/tests/syscall/gvisor/runner/README.md b/user/apps/tests/syscall/gvisor/runner/README.md new file mode 100644 index 000000000..1589a808a --- /dev/null +++ b/user/apps/tests/syscall/gvisor/runner/README.md @@ -0,0 +1,171 @@ +# gvisor系统调用测试运行器 (Rust版本) + +这是原Shell脚本 `run_tests.sh` 的Rust重写版本,用于在DragonOS上运行gvisor系统调用测试。 + +## 功能特点 + +- 🚀 使用Rust编写,性能更好,错误处理更完善 +- 📋 支持白名单和黑名单模式过滤测试 +- ⏱️ 可配置的测试超时时间 +- 📊 详细的测试报告和统计信息 +- 🎨 彩色输出,易于识别测试结果 +- 📁 自动创建测试目录和结果文件 + +## 构建和安装 + +```bash +# 进入runner目录 +cd user/apps/tests/syscall/gvisor/runner + +# 构建项目 +cargo build --release + +# 二进制文件位于 target/release/runner +``` + +## 使用方法 + +### 基本用法 + +```bash +# 运行白名单中的测试程序 +./target/release/runner + +# 列出所有可用的测试用例 +./target/release/runner --list + +# 详细模式运行测试 +./target/release/runner --verbose + +# 设置测试超时为180秒 +./target/release/runner --timeout 180 +``` + +### 高级选项 + +```bash +# 禁用白名单,运行所有测试 +./target/release/runner --no-whitelist + +# 禁用黑名单过滤 +./target/release/runner --no-blocklist + +# 使用自定义白名单文件 +./target/release/runner --whitelist my_whitelist.txt + +# 指定额外的黑名单目录 +./target/release/runner --extra-blocklist extra_blocks + +# 运行特定的测试程序 +./target/release/runner socket_test chdir_test + +# 详细帮助 +./target/release/runner --help +``` + +## 配置文件 + +### 白名单文件 (`whitelist.txt`) + +```text +# gvisor测试程序白名单 +# 每行一个测试程序名称,只有在此列表中的测试程序才会被执行 +# 以#开头的行为注释,空行会被忽略 + +# 基础系统调用测试 +chdir_test +read_test + +# 文件系统相关测试 +# stat_test # 被注释掉的测试不会运行 +``` + +### 黑名单文件 (`blocklists/测试名称`) + +每个测试程序都可以有对应的黑名单文件,用于屏蔽特定的子测试: + +```text +# epoll_test黑名单文件 (blocklists/epoll_test) +# 屏蔽的子测试名称,每行一个 +EpollTest.Timeout_NoRandomSave +EpollTest.CycleOfOneDisallowed +EpollTest.CycleOfThreeDisallowed +``` + +## 目录结构 + +``` +runner/ +├── Cargo.toml # Rust项目配置 +├── src/ +│ ├── main.rs # 主程序入口 +│ └── lib_sync.rs # 同步版本的核心库 +├── target/ # 编译输出目录 +│ └── release/ +│ └── runner # 最终可执行文件 +└── README.md # 本文件 +``` + +## 输出文件 + +程序运行时会在 `results/` 目录下生成以下文件: + +- `test_report.txt` - 完整的测试报告 +- `failed_cases.txt` - 失败的测试用例列表 +- `[测试名称].output` - 每个测试的详细输出 + +## 环境变量 + +- `SYSCALL_TEST_WORKDIR` - 临时工作目录路径(默认:`/tmp/gvisor_tests`) + +## 与Shell版本的区别 + +1. **性能**: Rust版本启动更快,内存使用更少 +2. **错误处理**: 更好的错误信息和异常处理 +3. **并发**: 为未来的并行测试执行准备了基础架构 +4. **可维护性**: 类型安全,更容易扩展和维护 +5. **依赖**: 减少了对系统命令的依赖 + +## 故障排除 + +### 常见问题 + +1. **测试套件未找到** + ``` + 错误: 测试目录不存在 + ``` + 解决: 先运行 `./download_tests.sh` 下载测试套件 + +2. **权限问题** + ``` + 错误: 测试不存在或不可执行 + ``` + 解决: 确保测试文件有执行权限 `chmod +x tests/*_test` + +3. **超时问题** + ``` + 错误: 测试超时 + ``` + 解决: 使用 `--timeout` 参数增加超时时间 + +### 调试模式 + +使用 `--verbose` 参数获得详细的调试信息: + +```bash +./target/release/runner --verbose --timeout 600 socket_test +``` + +## 开发说明 + +如果需要修改或扩展功能: + +1. 主要逻辑在 `src/lib_sync.rs` 中 +2. 命令行参数处理在 `src/main.rs` 中 +3. 使用 `cargo test` 运行单元测试(如果有) +4. 使用 `cargo fmt` 格式化代码 +5. 使用 `cargo clippy` 进行代码检查 + +## 许可证 + +与DragonOS项目保持一致的许可证。 \ No newline at end of file diff --git a/user/apps/tests/syscall/gvisor/runner/src/lib_sync.rs b/user/apps/tests/syscall/gvisor/runner/src/lib_sync.rs new file mode 100644 index 000000000..230b85141 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/runner/src/lib_sync.rs @@ -0,0 +1,594 @@ +use anyhow::Result; +use chrono::{DateTime, Local}; +use std::{ + collections::HashSet, + fs::{self, File}, + io::{BufRead, BufReader, Write}, + path::{Path, PathBuf}, + process::Command, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, + time::Instant, +}; + +/// 测试统计信息 +#[derive(Debug, Default)] +pub struct TestStats { + total: AtomicUsize, + passed: AtomicUsize, + failed: AtomicUsize, + skipped: AtomicUsize, +} + +impl TestStats { + pub fn increment_total(&self) { + self.total.fetch_add(1, Ordering::Relaxed); + } + + pub fn increment_passed(&self) { + self.passed.fetch_add(1, Ordering::Relaxed); + } + + pub fn increment_failed(&self) { + self.failed.fetch_add(1, Ordering::Relaxed); + } + + #[allow(dead_code)] + pub fn increment_skipped(&self) { + self.skipped.fetch_add(1, Ordering::Relaxed); + } + + pub fn get_totals(&self) -> (usize, usize, usize, usize) { + ( + self.total.load(Ordering::Relaxed), + self.passed.load(Ordering::Relaxed), + self.failed.load(Ordering::Relaxed), + self.skipped.load(Ordering::Relaxed), + ) + } +} + +/// 测试运行器配置 +#[derive(Debug, Clone)] +pub struct Config { + pub verbose: bool, + pub timeout: u64, + pub parallel: usize, + pub use_blocklist: bool, + pub use_whitelist: bool, + pub whitelist_file: PathBuf, + pub tests_dir: PathBuf, + pub blocklists_dir: PathBuf, + pub results_dir: PathBuf, + pub temp_dir: PathBuf, + pub extra_blocklist_dirs: Vec, + pub test_patterns: Vec, +} + +impl Default for Config { + fn default() -> Self { + let script_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); + Self { + verbose: false, + timeout: 300, + parallel: 1, + use_blocklist: true, + use_whitelist: true, + whitelist_file: script_dir.join("whitelist.txt"), + tests_dir: script_dir.join("tests"), + blocklists_dir: script_dir.join("blocklists"), + results_dir: script_dir.join("results"), + temp_dir: PathBuf::from( + std::env::var("SYSCALL_TEST_WORKDIR") + .unwrap_or_else(|_| "/tmp/gvisor_tests".to_string()), + ), + extra_blocklist_dirs: Vec::new(), + test_patterns: Vec::new(), + } + } +} + +/// 颜色输出辅助函数(简化版) +pub fn print_colored(color: &str, prefix: &str, msg: &str) { + match color { + "green" => println!("\x1b[32m[{}]\x1b[0m {}", prefix, msg), + "yellow" => println!("\x1b[33m[{}]\x1b[0m {}", prefix, msg), + "red" => eprintln!("\x1b[31m[{}]\x1b[0m {}", prefix, msg), + "blue" => println!("\x1b[34m[{}]\x1b[0m {}", prefix, msg), + _ => println!("[{}] {}", prefix, msg), + } +} + +/// gvisor系统调用测试运行器 +pub struct TestRunner { + pub config: Config, + pub stats: Arc, +} + +impl TestRunner { + pub fn new(config: Config) -> Self { + Self { + config, + stats: Arc::new(TestStats::default()), + } + } + + /// 打印信息日志 + fn print_info(&self, msg: &str) { + print_colored("green", "INFO", msg); + } + + /// 打印警告日志 + fn print_warn(&self, msg: &str) { + print_colored("yellow", "WARN", msg); + } + + /// 打印错误日志 + fn print_error(&self, msg: &str) { + print_colored("red", "ERROR", msg); + } + + /// 打印测试日志 + fn print_test(&self, msg: &str) { + print_colored("blue", "TEST", msg); + } + + /// 检查测试套件是否存在 + pub fn check_test_suite(&self) -> Result<()> { + if !self.config.tests_dir.exists() { + anyhow::bail!( + "测试目录不存在: {:?}\n请先运行 ./download_tests.sh 下载测试套件", + self.config.tests_dir + ); + } + + let test_files: Vec<_> = fs::read_dir(&self.config.tests_dir)? + .filter_map(|entry| entry.ok()) + .filter(|entry| { + entry.path().is_file() && entry.file_name().to_string_lossy().ends_with("_test") + }) + .collect(); + + if test_files.is_empty() { + anyhow::bail!("测试套件未找到\n请先运行 ./download_tests.sh 下载测试套件"); + } + + Ok(()) + } + + /// 创建必要的目录 + pub fn setup_directories(&self) -> Result<()> { + fs::create_dir_all(&self.config.results_dir)?; + fs::create_dir_all(&self.config.temp_dir)?; + fs::create_dir_all(&self.config.blocklists_dir)?; + Ok(()) + } + + /// 读取白名单中的测试程序 + fn get_whitelist_tests(&self) -> Result> { + if !self.config.whitelist_file.exists() { + anyhow::bail!("白名单文件不存在: {:?}", self.config.whitelist_file); + } + + let file = File::open(&self.config.whitelist_file)?; + let reader = BufReader::new(file); + let mut tests = HashSet::new(); + + for line in reader.lines() { + let line = line?; + let line = line.trim(); + if !line.is_empty() && !line.starts_with('#') { + tests.insert(line.to_string()); + } + } + + Ok(tests) + } + + /// 检查测试是否在白名单中 + fn is_test_whitelisted(&self, test_name: &str) -> bool { + if !self.config.use_whitelist { + return true; + } + + match self.get_whitelist_tests() { + Ok(whitelist) => whitelist.contains(test_name), + Err(_) => false, + } + } + + /// 获取测试的blocklist + fn get_test_blocklist(&self, test_name: &str) -> Vec { + if !self.config.use_blocklist { + return Vec::new(); + } + + let mut blocked_subtests = Vec::new(); + + // 检查主blocklist目录 + let blocklist_file = self.config.blocklists_dir.join(test_name); + if let Ok(content) = self.read_blocklist_file(&blocklist_file) { + blocked_subtests.extend(content); + } + + // 检查额外的blocklist目录 + for extra_dir in &self.config.extra_blocklist_dirs { + let extra_blocklist = extra_dir.join(test_name); + if let Ok(content) = self.read_blocklist_file(&extra_blocklist) { + blocked_subtests.extend(content); + } + } + + blocked_subtests + } + + /// 读取blocklist文件 + fn read_blocklist_file(&self, path: &Path) -> Result> { + if !path.exists() { + return Ok(Vec::new()); + } + + let file = File::open(path)?; + let reader = BufReader::new(file); + let mut blocked = Vec::new(); + + for line in reader.lines() { + let line = line?; + let line = line.trim(); + if !line.is_empty() && !line.starts_with('#') { + blocked.push(line.to_string()); + } + } + + Ok(blocked) + } + + /// 获取要运行的测试列表 + pub fn get_test_list(&self) -> Result> { + // 获取所有测试文件 + let mut all_tests = Vec::new(); + for entry in fs::read_dir(&self.config.tests_dir)? { + let entry = entry?; + if entry.path().is_file() { + let file_name = entry.file_name(); + let file_name_str = file_name.to_string_lossy(); + if file_name_str.ends_with("_test") { + all_tests.push(file_name_str.to_string()); + } + } + } + + // 应用白名单过滤 + let mut candidate_tests = Vec::new(); + if self.config.use_whitelist { + for test in &all_tests { + if self.is_test_whitelisted(test) { + candidate_tests.push(test.clone()); + } + } + + if candidate_tests.is_empty() { + self.print_warn("没有测试通过白名单过滤"); + return Ok(Vec::new()); + } + + if self.config.verbose { + self.print_info(&format!( + "白名单过滤后有 {} 个测试可用", + candidate_tests.len() + )); + } + } else { + candidate_tests = all_tests; + } + + // 如果没有指定模式,返回候选测试 + if self.config.test_patterns.is_empty() { + candidate_tests.sort(); + return Ok(candidate_tests); + } + + // 根据模式过滤测试 + let mut filtered_tests = HashSet::new(); + for pattern in &self.config.test_patterns { + for test in &candidate_tests { + if test == pattern { + filtered_tests.insert(test.clone()); + } + } + } + + let mut result: Vec<_> = filtered_tests.into_iter().collect(); + result.sort(); + Ok(result) + } + + /// 运行单个测试 + pub fn run_single_test(&self, test_name: &str) -> Result { + println!("[DEBUG] 开始运行测试: {}", test_name); + let test_path = self.config.tests_dir.join(test_name); + println!("[DEBUG] 测试路径: {:?}", test_path); + + if !test_path.exists() || !test_path.is_file() { + self.print_warn(&format!("测试不存在或不可执行: {}", test_name)); + return Ok(false); + } + + self.print_test(&format!("运行测试用例: {}", test_name)); + + // 获取blocklist + let blocked_subtests = self.get_test_blocklist(test_name); + + // 结果输出文件(使用绝对路径,避免工作目录切换影响) + let output_file = self + .config + .results_dir + .join(format!("{}.output", test_name)); + + println!("[DEBUG] 工作目录: {:?}", self.config.tests_dir); + println!("[DEBUG] TEST_TMPDIR: {:?}", self.config.temp_dir); + println!("[DEBUG] 直接执行: {:?}", test_path); + if !blocked_subtests.is_empty() { + println!("[DEBUG] gtest_filter: -{}", blocked_subtests.join(":")); + } + + // 确保结果目录存在 + if let Err(e) = fs::create_dir_all(&self.config.results_dir) { + self.print_error(&format!("创建结果目录失败: {}", e)); + } + + // 打开输出文件,并将 stdout/stderr 重定向到该文件 + let out = File::create(&output_file); + if let Err(e) = out.as_ref() { + self.print_error(&format!("创建输出文件失败: {:?}, 错误: {}", output_file, e)); + } + let out = out?; + let err = out.try_clone()?; + + // 构造并执行命令(不使用 shell,不捕获输出,不创建管道) + let start_time = Instant::now(); + let mut cmd = Command::new(&test_path); + if !blocked_subtests.is_empty() { + cmd.arg("--gtest_filter") + .arg(format!("-{}", blocked_subtests.join(":"))); + } + let status = cmd + .current_dir(&self.config.tests_dir) + .env("TEST_TMPDIR", &self.config.temp_dir) + .stdout(std::process::Stdio::from(out)) + .stderr(std::process::Stdio::from(err)) + .status(); + + // 清理临时目录 + let _ = fs::remove_dir_all(&self.config.temp_dir); + let _ = fs::create_dir_all(&self.config.temp_dir); + + let duration = start_time.elapsed(); + match status { + Ok(s) if s.success() => { + self.print_info(&format!( + "✓ {} 通过 ({:.2}s)", + test_name, + duration.as_secs_f64() + )); + // 将输出文件尾部打印一点,便于快速查看 + if let Ok(content) = fs::read_to_string(&output_file) { + let tail: String = content + .lines() + .rev() + .take(10) + .collect::>() + .into_iter() + .rev() + .map(|s| format!("{}\n", s)) + .collect(); + if !tail.is_empty() { + println!("[DEBUG] 输出尾部: \n{}", tail); + } + } + Ok(true) + } + Ok(s) => { + self.print_error(&format!( + "✗ {} 失败 ({:.2}s), 退出码: {:?}", + test_name, + duration.as_secs_f64(), + s.code() + )); + // 打印错误输出尾部 + if let Ok(content) = fs::read_to_string(&output_file) { + let tail: String = content + .lines() + .rev() + .take(20) + .collect::>() + .into_iter() + .rev() + .map(|s| format!("{}\n", s)) + .collect(); + if !tail.is_empty() { + println!("[DEBUG] 错误输出尾部: \n{}", tail); + } + } + Ok(false) + } + Err(e) => { + self.print_error(&format!("✗ {} 执行错误: {}", test_name, e)); + Ok(false) + } + } + } + + /// 运行所有测试 + pub fn run_all_tests(&self) -> Result<()> { + let test_list = self.get_test_list()?; + + if test_list.is_empty() { + self.print_warn("没有找到匹配的测试用例"); + return Ok(()); + } + + self.print_info(&format!("准备运行 {} 个测试用例", test_list.len())); + + // 初始化结果文件 + let failed_cases_file = self.config.results_dir.join("failed_cases.txt"); + let mut failed_cases = File::create(&failed_cases_file)?; + + // 运行测试 + for test_name in test_list { + self.stats.increment_total(); + + match self.run_single_test(&test_name) { + Ok(true) => { + self.stats.increment_passed(); + } + Ok(false) => { + self.stats.increment_failed(); + writeln!(failed_cases, "{}", test_name)?; + } + Err(e) => { + self.stats.increment_failed(); + writeln!(failed_cases, "{}", test_name)?; + self.print_error(&format!("测试 {} 出错: {}", test_name, e)); + } + } + + println!("---"); + } + + Ok(()) + } + + /// 生成测试报告 + pub fn generate_report(&self) -> Result<()> { + let report_file = self.config.results_dir.join("test_report.txt"); + let mut file = File::create(&report_file)?; + let (total, passed, failed, _skipped) = self.stats.get_totals(); + + let now: DateTime = Local::now(); + let success_rate = if total > 0 { + passed as f64 * 100.0 / total as f64 + } else { + 0.0 + }; + + let report = format!( + "gvisor系统调用测试报告\n\ + ==========================\n\ + 测试时间: {}\n\ + 测试目录: {:?}\n\ + \n\ + 测试统计:\n\ + 总测试数: {}\n\ + 通过: {}\n\ + 失败: {}\n\ + 成功率: {:.2}%\n\ + \n", + now.format("%Y-%m-%d %H:%M:%S"), + self.config.tests_dir, + total, + passed, + failed, + success_rate + ); + + file.write_all(report.as_bytes())?; + println!("{}", report); + + if failed > 0 { + let failed_cases_file = self.config.results_dir.join("failed_cases.txt"); + if failed_cases_file.exists() { + let failed_content = fs::read_to_string(&failed_cases_file)?; + let failed_section = format!("失败的测试用例:\n{}", failed_content); + file.write_all(failed_section.as_bytes())?; + println!("{}", failed_section); + } + } + + Ok(()) + } + + /// 显示测试结果 + pub fn show_results(&self) { + let (total, passed, failed, _skipped) = self.stats.get_totals(); + + log::info!(""); + log::info!("==============================================="); + self.print_info("测试完成"); + log::info!( + "\x1b[32m{}\x1b[0m / \x1b[32m{}\x1b[0m 测试用例通过", + passed, + total + ); + + if failed > 0 { + log::info!("\x1b[31m{}\x1b[0m 个测试用例失败:", failed); + let failed_cases_file = self.config.results_dir.join("failed_cases.txt"); + if let Ok(content) = fs::read_to_string(&failed_cases_file) { + for line in content.lines() { + if !line.trim().is_empty() { + log::info!(" \x1b[31m[X]\x1b[0m {}", line); + } + } + } + } + + log::info!(""); + log::info!( + "详细报告保存在: {:?}", + self.config.results_dir.join("test_report.txt") + ); + } + + /// 列出所有测试用例 + pub fn list_tests(&self) -> Result<()> { + if !self.config.tests_dir.exists() { + self.print_error(&format!("测试目录不存在: {:?}", self.config.tests_dir)); + self.print_info("请先运行 ./download_tests.sh 下载测试套件"); + return Ok(()); + } + + if self.config.use_whitelist { + self.print_info(&format!( + "白名单模式 - 可运行的测试用例 (来自: {:?}):", + self.config.whitelist_file + )); + let test_list = self.get_test_list()?; + for test_name in &test_list { + log::info!(" \x1b[32m✓\x1b[0m {}", test_name); + } + + self.print_info("所有可用测试用例 (包括未在白名单中的):"); + for entry in fs::read_dir(&self.config.tests_dir)? { + let entry = entry?; + if entry.path().is_file() { + let file_name = entry.file_name(); + let file_name_str = file_name.to_string_lossy(); + if file_name_str.ends_with("_test") { + if self.is_test_whitelisted(&file_name_str) { + log::info!(" \x1b[32m✓\x1b[0m {} (在白名单中)", file_name_str); + } else { + log::info!(" \x1b[33m○\x1b[0m {} (不在白名单中)", file_name_str); + } + } + } + } + } else { + self.print_info("所有可用的测试用例:"); + for entry in fs::read_dir(&self.config.tests_dir)? { + let entry = entry?; + if entry.path().is_file() { + let file_name = entry.file_name(); + let file_name_str = file_name.to_string_lossy(); + if file_name_str.ends_with("_test") { + log::info!(" {}", file_name_str); + } + } + } + } + + Ok(()) + } +} diff --git a/user/apps/tests/syscall/gvisor/runner/src/main.rs b/user/apps/tests/syscall/gvisor/runner/src/main.rs new file mode 100644 index 000000000..dff1ff4b5 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/runner/src/main.rs @@ -0,0 +1,159 @@ +use anyhow::Result; +use clap::{Arg, Command}; +use std::path::PathBuf; + +mod lib_sync; +use lib_sync::{Config, TestRunner}; + +fn main() -> Result<()> { + env_logger::Builder::from_default_env() + .filter_level(log::LevelFilter::Info) + .init(); + let app = Command::new("gvisor-test-runner") + .version("0.1.0") + .about("gvisor系统调用测试运行脚本 - Rust版本") + .arg( + Arg::new("help") + .short('h') + .long("help") + .action(clap::ArgAction::Help) + .help("显示此帮助信息"), + ) + .arg( + Arg::new("list") + .short('l') + .long("list") + .action(clap::ArgAction::SetTrue) + .help("列出所有可用的测试用例"), + ) + .arg( + Arg::new("verbose") + .short('v') + .long("verbose") + .action(clap::ArgAction::SetTrue) + .help("详细输出模式"), + ) + .arg( + Arg::new("timeout") + .short('t') + .long("timeout") + .value_name("SEC") + .help("设置单个测试的超时时间(默认:300秒)") + .value_parser(clap::value_parser!(u64)), + ) + .arg( + Arg::new("parallel") + .short('j') + .long("parallel") + .value_name("NUM") + .help("并行运行的测试数量(默认:1)") + .value_parser(clap::value_parser!(usize)), + ) + .arg( + Arg::new("no-blocklist") + .long("no-blocklist") + .action(clap::ArgAction::SetTrue) + .help("忽略所有blocklist文件"), + ) + .arg( + Arg::new("extra-blocklist") + .long("extra-blocklist") + .value_name("DIR") + .action(clap::ArgAction::Append) + .help("指定额外的blocklist目录"), + ) + .arg( + Arg::new("no-whitelist") + .long("no-whitelist") + .action(clap::ArgAction::SetTrue) + .help("禁用白名单模式,运行所有测试程序"), + ) + .arg( + Arg::new("whitelist") + .long("whitelist") + .value_name("FILE") + .help("指定白名单文件路径(默认:whitelist.txt)"), + ) + .arg( + Arg::new("test-patterns") + .value_name("PATTERN") + .action(clap::ArgAction::Append) + .help("测试名称模式"), + ); + + let matches = app.get_matches(); + + // 解析配置 + let mut config = Config::default(); + + config.verbose = matches.get_flag("verbose"); + + if let Some(timeout) = matches.get_one::("timeout") { + config.timeout = *timeout; + } + + if let Some(parallel) = matches.get_one::("parallel") { + config.parallel = *parallel; + } + + config.use_blocklist = !matches.get_flag("no-blocklist"); + config.use_whitelist = !matches.get_flag("no-whitelist"); + + if let Some(whitelist_file) = matches.get_one::("whitelist") { + config.whitelist_file = PathBuf::from(whitelist_file); + } + + if let Some(extra_dirs) = matches.get_many::("extra-blocklist") { + config.extra_blocklist_dirs = extra_dirs.map(|s| PathBuf::from(s)).collect(); + } + + if let Some(patterns) = matches.get_many::("test-patterns") { + config.test_patterns = patterns.cloned().collect(); + } + + // 创建测试运行器 + let runner = TestRunner::new(config); + + // 处理特殊命令 + if matches.get_flag("list") { + runner.list_tests()?; + return Ok(()); + } + + log::info!("=============================="); + log::info!(" gvisor系统调用测试运行器"); + log::info!("=============================="); + + // 检查测试套件 + runner.check_test_suite()?; + + // 设置目录 + runner.setup_directories()?; + + log::info!("开始运行gvisor系统调用测试"); + + // 显示运行配置 + if runner.config.use_whitelist { + log::info!("白名单模式已启用: {:?}", runner.config.whitelist_file); + } + if !runner.config.use_blocklist { + log::info!("黑名单已禁用"); + } + + // 运行测试 + runner.run_all_tests()?; + + // 生成报告 + runner.generate_report()?; + + // 显示结果 + runner.show_results(); + + // 返回适当的退出码 + let (_, _, failed, _) = runner.stats.get_totals(); + if failed > 0 { + std::process::exit(1); + } + + Ok(()) +} diff --git a/user/apps/tests/syscall/gvisor/whitelist.txt b/user/apps/tests/syscall/gvisor/whitelist.txt index 1557c6157..ede6fe27d 100644 --- a/user/apps/tests/syscall/gvisor/whitelist.txt +++ b/user/apps/tests/syscall/gvisor/whitelist.txt @@ -3,8 +3,8 @@ # 以#开头的行为注释,空行会被忽略 # 基础系统调用测试 -chdir_test read_test +#chdir_test # 文件系统相关测试 #stat_test @@ -27,4 +27,4 @@ read_test # 信号处理测试 #signal_test -#sigaction_test \ No newline at end of file +#sigaction_test diff --git a/user/dadk/config/gvisor_syscall_tests-1.0.0.toml b/user/dadk/config/gvisor_syscall_tests-1.0.0.toml new file mode 100644 index 000000000..bb62ea684 --- /dev/null +++ b/user/dadk/config/gvisor_syscall_tests-1.0.0.toml @@ -0,0 +1,39 @@ +# 用户程序名称 +name = "gvisor syscall tests" +# 版本号 +version = "1.0.0" +# 用户程序描述信息 +description = "gVisor系统调用兼容性测试套件" +# (可选)是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果 +build-once = false +# (可选) 是否只安装一次,如果为true,DADK会在安装成功后,不再重复安装 +install-once = false +# 目标架构 +# 可选值:"x86_64", "aarch64", "riscv64" +target-arch = ["x86_64"] +# 任务源 +[task-source] +# 构建类型 +# 可选值:"build-from-source", "install-from-prebuilt" +type = "build-from-source" +# 构建来源 +# "build_from_source" 可选值:"git", "local", "archive" +# "install_from_prebuilt" 可选值:"local", "archive" +source = "local" +# 路径或URL +source-path = "user/apps/tests/syscall/gvisor" + +# 构建相关信息 +[build] +# (可选)构建命令 +build-command = "make install" + +# 安装相关信息 +[install] +# (可选)安装到DragonOS的路径 +in-dragonos-path = "/opt/tests/gvisor" + +# 清除相关信息 +[clean] +# (可选)清除命令 +clean-command = "make clean" \ No newline at end of file From 15bd735b5be92b49f6c64a5c84e636ec088c3af3 Mon Sep 17 00:00:00 2001 From: longjin Date: Mon, 22 Sep 2025 00:41:23 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat(config):=20=E6=B7=BB=E5=8A=A0=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E7=A8=8B=E5=BA=8F=E9=BB=91=E5=90=8D=E5=8D=95=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 app-blocklist.toml 配置文件模板,支持多种应用程序匹配方式 - 支持严格模式和非严格模式配置 - 添加黑名单配置文件路径到 dadk-manifest.toml - 提供详细的配置说明和示例 Signed-off-by: longjin --- config/app-blocklist.toml | 67 +++++++++++++++++++++++++++++++++++++++ dadk-manifest.toml | 4 +++ user/Makefile | 2 +- 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 config/app-blocklist.toml diff --git a/config/app-blocklist.toml b/config/app-blocklist.toml new file mode 100644 index 000000000..19be09579 --- /dev/null +++ b/config/app-blocklist.toml @@ -0,0 +1,67 @@ +# ========================================================= +# DADK 应用程序黑名单配置文件模板 +# ========================================================= +# +# 应用程序黑名单功能允许用户指定不希望编译和安装的应用程序。 +# 当黑名单中的应用程序被检测到时,DADK 会根据配置自动跳过这些应用程序的构建和安装过程。 + +# ========================================================= +# 全局配置选项 +# ========================================================= + +# 是否启用严格模式(可选) +# - true(默认):严格模式,跳过被屏蔽的应用程序并记录警告 +# - false:非严格模式,只记录警告但不跳过应用程序 +strict = true + +# 是否在日志中显示被跳过的应用(可选) +# - true(默认):在日志中显示被跳过的应用程序信息 +# - false:静默模式,不显示被跳过的应用程序 +log_skipped = true + +# ========================================================= +# 被屏蔽的应用程序列表 +# ========================================================= +# +# 支持以下匹配方式: +# 1. 精确匹配: name = "app1" +# 2. 版本匹配: name = "openssl@1.1.1" +# 3. 通配符名称: name = "test-*" +# 4. 通配符版本: name = "nginx@1.*" +# 5. 复合模式: name = "lib*@2.*" +# +# 每个应用可以选择性地提供屏蔽原因(reason) + + +# ========================================================= +# 配置说明和注意事项 +# ========================================================= +# +# 1. **依赖关系**:如果其他应用程序依赖被屏蔽的应用程序,构建过程可能会失败。 +# 请确保处理好依赖关系。 +# +# 2. **模式匹配优先级**: +# - 精确匹配 > 版本匹配 > 模式匹配 +# - 如果有多个模式都匹配,使用第一个匹配的结果 +# +# 3. **通配符语法**: +# - "*" 匹配任意数量的字符(包括0个) +# - "?" 匹配单个字符 +# - 支持在名称和版本中使用通配符 +# +# 4. **版本格式**: +# - 版本号使用 "@" 符号分隔,如 "app@1.0.0" +# - 版本号支持通配符,如 "app@1.*" +# +# 5. **配置文件路径**: +# - 默认路径:config/app-blocklist.toml +# - 可在 dadk-manifest.toml 中通过 app-blocklist-config 字段自定义路径 +# +# 6. **字段说明**: +# - name:应用程序名称或匹配模式(必需) +# - reason:屏蔽原因说明(可选,建议提供以便调试和维护) + +# 屏蔽gvisor系统调用测试 +[[blocked_apps]] +name = "gvisor syscall tests" +reason = "由于文件较大,因此屏蔽。如果要允许系统调用测试,则把这几行取消注释即可" diff --git a/dadk-manifest.toml b/dadk-manifest.toml index bafabac49..b3ba968e6 100644 --- a/dadk-manifest.toml +++ b/dadk-manifest.toml @@ -22,3 +22,7 @@ cache-root-dir = "bin/dadk_cache" # User configuration directory path # 这个字段只是临时用于兼容旧版本,v0.2版本重构完成后会删除 user-config-dir = "user/dadk/config" + +# Application blocklist configuration file path +app-blocklist-config = "config/app-blocklist.toml" + diff --git a/user/Makefile b/user/Makefile index 1f720d748..10b1f9c9d 100644 --- a/user/Makefile +++ b/user/Makefile @@ -2,7 +2,7 @@ user_sub_dirs = apps DADK_VERSION=$(shell dadk -V | awk 'END {print $$2}') # 最小的DADK版本 -MIN_DADK_VERSION = 0.4.0 +MIN_DADK_VERSION = 0.5.0 DADK_CACHE_DIR = $(ROOT_PATH)/bin/dadk_cache ECHO: From 1f8c08fca72d4d2e10ca638484939d8ec4197458 Mon Sep 17 00:00:00 2001 From: longjin Date: Mon, 22 Sep 2025 01:05:21 +0800 Subject: [PATCH 4/4] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0gVisor=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E8=B0=83=E7=94=A8=E6=B5=8B=E8=AF=95=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增gVisor系统调用测试文档,包含概述、快速开始和测试机制说明 - 删除旧的测试运行器README文档 Signed-off-by: longjin --- docs/kernel/ktest/gvisor_syscall_test.rst | 79 ++++++++ docs/kernel/ktest/index.rst | 2 + .../tests/syscall/gvisor/runner/README.md | 171 ------------------ 3 files changed, 81 insertions(+), 171 deletions(-) create mode 100644 docs/kernel/ktest/gvisor_syscall_test.rst delete mode 100644 user/apps/tests/syscall/gvisor/runner/README.md diff --git a/docs/kernel/ktest/gvisor_syscall_test.rst b/docs/kernel/ktest/gvisor_syscall_test.rst new file mode 100644 index 000000000..ff3cb9ea4 --- /dev/null +++ b/docs/kernel/ktest/gvisor_syscall_test.rst @@ -0,0 +1,79 @@ +============================== +gVisor 系统调用测试 +============================== + +DragonOS 集成了 gVisor 系统调用测试套件,用于验证操作系统系统调用实现的兼容性和正确性。 + +概述 +======== + +gVisor 是 Google 开发的容器运行时沙箱,包含了大量的系统调用兼容性测试。这些测试用于验证操作系统的系统调用实现是否符合 Linux 标准。 + +主要特性: + +- **全面的测试覆盖**:包含数百个系统调用测试用例 +- **白名单机制**:默认只运行已验证的测试,逐步完善支持 +- **黑名单过滤**:可针对每个测试程序屏蔽特定的测试用例 +- **自动化运行**:提供 Makefile 和脚本简化测试流程 + +快速开始 +========== + +1. 进入测试目录: + + .. code-block:: bash + + cd user/apps/tests/syscall/gvisor + +2. 在Linux运行白名单测试(自动下载测试套件): + + .. code-block:: bash + + make test + +3. 如果需要运行测试,请先修改配置文件: + + 编辑 `config/app-blocklist.toml`,注释掉以下内容: + + .. code-block:: toml + + # 屏蔽gvisor系统调用测试 + # [[blocked_apps]] + # name = "gvisor syscall tests" + # reason = "由于文件较大,因此屏蔽。如果要允许系统调用测试,则把这几行取消注释即可" + +4. 在 DragonOS 系统内运行测试: + + 进入安装目录并运行测试程序: + + .. code-block:: bash + + cd /opt/tests/gvisor + ./gvisor-test-runner --help + + 使用 ``./gvisor-test-runner`` 可以运行具体的测试用例。 + +5. 查看详细文档: + + 请参阅 `user/apps/tests/syscall/gvisor/README.md` 获取完整的使用说明。 + +测试机制 +========== + +白名单模式 +----------- + +测试框架默认启用白名单模式,只运行 ``whitelist.txt`` 中指定的测试程序。这允许逐步验证 DragonOS 的系统调用实现。 + +黑名单过滤 +----------- + +对于每个测试程序,可以通过 ``blocklists/`` 目录下的文件屏蔽特定的测试用例。这对于跳过暂不支持或不稳定的测试非常有用。 + +更多详细信息 +============== + +关于 gVisor 系统调用测试的详细使用方法、配置选项和开发指南,请查看测试目录下的 README.md 文档: + +- 文档位置:`user/apps/tests/syscall/gvisor/README.md` + diff --git a/docs/kernel/ktest/index.rst b/docs/kernel/ktest/index.rst index bd73448b9..034023bed 100644 --- a/docs/kernel/ktest/index.rst +++ b/docs/kernel/ktest/index.rst @@ -17,3 +17,5 @@ :maxdepth: 1 :caption: 目录 + gvisor_syscall_test.rst + diff --git a/user/apps/tests/syscall/gvisor/runner/README.md b/user/apps/tests/syscall/gvisor/runner/README.md deleted file mode 100644 index 1589a808a..000000000 --- a/user/apps/tests/syscall/gvisor/runner/README.md +++ /dev/null @@ -1,171 +0,0 @@ -# gvisor系统调用测试运行器 (Rust版本) - -这是原Shell脚本 `run_tests.sh` 的Rust重写版本,用于在DragonOS上运行gvisor系统调用测试。 - -## 功能特点 - -- 🚀 使用Rust编写,性能更好,错误处理更完善 -- 📋 支持白名单和黑名单模式过滤测试 -- ⏱️ 可配置的测试超时时间 -- 📊 详细的测试报告和统计信息 -- 🎨 彩色输出,易于识别测试结果 -- 📁 自动创建测试目录和结果文件 - -## 构建和安装 - -```bash -# 进入runner目录 -cd user/apps/tests/syscall/gvisor/runner - -# 构建项目 -cargo build --release - -# 二进制文件位于 target/release/runner -``` - -## 使用方法 - -### 基本用法 - -```bash -# 运行白名单中的测试程序 -./target/release/runner - -# 列出所有可用的测试用例 -./target/release/runner --list - -# 详细模式运行测试 -./target/release/runner --verbose - -# 设置测试超时为180秒 -./target/release/runner --timeout 180 -``` - -### 高级选项 - -```bash -# 禁用白名单,运行所有测试 -./target/release/runner --no-whitelist - -# 禁用黑名单过滤 -./target/release/runner --no-blocklist - -# 使用自定义白名单文件 -./target/release/runner --whitelist my_whitelist.txt - -# 指定额外的黑名单目录 -./target/release/runner --extra-blocklist extra_blocks - -# 运行特定的测试程序 -./target/release/runner socket_test chdir_test - -# 详细帮助 -./target/release/runner --help -``` - -## 配置文件 - -### 白名单文件 (`whitelist.txt`) - -```text -# gvisor测试程序白名单 -# 每行一个测试程序名称,只有在此列表中的测试程序才会被执行 -# 以#开头的行为注释,空行会被忽略 - -# 基础系统调用测试 -chdir_test -read_test - -# 文件系统相关测试 -# stat_test # 被注释掉的测试不会运行 -``` - -### 黑名单文件 (`blocklists/测试名称`) - -每个测试程序都可以有对应的黑名单文件,用于屏蔽特定的子测试: - -```text -# epoll_test黑名单文件 (blocklists/epoll_test) -# 屏蔽的子测试名称,每行一个 -EpollTest.Timeout_NoRandomSave -EpollTest.CycleOfOneDisallowed -EpollTest.CycleOfThreeDisallowed -``` - -## 目录结构 - -``` -runner/ -├── Cargo.toml # Rust项目配置 -├── src/ -│ ├── main.rs # 主程序入口 -│ └── lib_sync.rs # 同步版本的核心库 -├── target/ # 编译输出目录 -│ └── release/ -│ └── runner # 最终可执行文件 -└── README.md # 本文件 -``` - -## 输出文件 - -程序运行时会在 `results/` 目录下生成以下文件: - -- `test_report.txt` - 完整的测试报告 -- `failed_cases.txt` - 失败的测试用例列表 -- `[测试名称].output` - 每个测试的详细输出 - -## 环境变量 - -- `SYSCALL_TEST_WORKDIR` - 临时工作目录路径(默认:`/tmp/gvisor_tests`) - -## 与Shell版本的区别 - -1. **性能**: Rust版本启动更快,内存使用更少 -2. **错误处理**: 更好的错误信息和异常处理 -3. **并发**: 为未来的并行测试执行准备了基础架构 -4. **可维护性**: 类型安全,更容易扩展和维护 -5. **依赖**: 减少了对系统命令的依赖 - -## 故障排除 - -### 常见问题 - -1. **测试套件未找到** - ``` - 错误: 测试目录不存在 - ``` - 解决: 先运行 `./download_tests.sh` 下载测试套件 - -2. **权限问题** - ``` - 错误: 测试不存在或不可执行 - ``` - 解决: 确保测试文件有执行权限 `chmod +x tests/*_test` - -3. **超时问题** - ``` - 错误: 测试超时 - ``` - 解决: 使用 `--timeout` 参数增加超时时间 - -### 调试模式 - -使用 `--verbose` 参数获得详细的调试信息: - -```bash -./target/release/runner --verbose --timeout 600 socket_test -``` - -## 开发说明 - -如果需要修改或扩展功能: - -1. 主要逻辑在 `src/lib_sync.rs` 中 -2. 命令行参数处理在 `src/main.rs` 中 -3. 使用 `cargo test` 运行单元测试(如果有) -4. 使用 `cargo fmt` 格式化代码 -5. 使用 `cargo clippy` 进行代码检查 - -## 许可证 - -与DragonOS项目保持一致的许可证。 \ No newline at end of file