Add syscall test framwork from gvisor

This commit is contained in:
LI Qing 2023-05-29 13:29:53 +08:00 committed by Tate, Hongliang Tian
parent 8e199f46ef
commit e2f3932cb8
18 changed files with 256 additions and 43 deletions

View File

@ -9,7 +9,7 @@ setup:
@cargo install mdbook
build:
@make --no-print-directory -C regression/ramdisk
@make --no-print-directory -C regression
@cargo kbuild
tools:
@ -33,4 +33,4 @@ check:
clean:
@cargo clean
@cd docs && mdbook clean
@make --no-print-directory -C regression/ramdisk clean
@make --no-print-directory -C regression clean

View File

@ -77,6 +77,23 @@ Then we can use the tool to check access control policy.
cargo component-check
```
### Syscall Test
If we have already built the project for normal use, please clean the project before running syscall test.
```bash
make clean
```
For running syscall tests, we can run following command to build test binaries and run the OS for testing purpose.
```bash
ENABLE_SYSCALL_TEST=1 make run
```
Then, we can run the following script inside the OS to run all syscall test cases.
```bash
/opt/syscall_test/run_syscall_test.sh
```
## Code organization
The codebase of Jinux is organized as below.

View File

@ -25,7 +25,7 @@ cp target/limine/limine-cd.bin target/iso_root
cp target/limine/limine-cd-efi.bin target/iso_root
# Copy ramdisk
cp regression/ramdisk/build/ramdisk.cpio target/iso_root
cp regression/build/ramdisk.cpio target/iso_root
xorriso -as mkisofs \
-b limine-cd.bin \

1
regression/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build/

View File

@ -1,47 +1,45 @@
MAKEFLAGS += --no-builtin-rules # Prevent the implicit rules from compiling ".c" or ".s" files automatically.
APPS := ../apps
BUILD_DIR := ./build
INITRAMFS := ./initramfs
MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH)))
BUILD_DIR := $(CUR_DIR)/build
INITRAMFS := $(BUILD_DIR)/initramfs
RAMDISK := $(BUILD_DIR)/ramdisk.cpio
SHELL := /bin/bash
ifneq (, $(wildcard $(APPS)/. ))
APPS_DIRS := $(shell find $(APPS) -type d 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true)
APPS_FILES := $(shell find $(APPS) -type f 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true)
endif
ifneq (, $(wildcard $(INITRAMFS)/. ))
INITRAMFS_DIRS := $(shell find $(INITRAMFS) -type d 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true)
INITRAMFS_FILES := $(shell find $(INITRAMFS) -type f 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true)
endif
.PHONY: all clean
.PHONY: all clean prepare_libs copy_special_files
all: build
all: $(RAMDISK)
$(INITRAMFS): $(APPS) $(APPS_DIRS) $(APPS_FILES)
$(INITRAMFS):
@rm -rf $@ && mkdir -p $@
# Copy Apps
@cp -a $(APPS)/* $@
@cd $@ && find . \( -name "*.s" -o -name "*.c" -o -name "Makefile" -o -name "README.md" \) -delete
# Mkdir folders
@# Mkdir necessary folders
@mkdir -p $@/tmp
@mkdir -p $@/test
@mkdir -p $@/opt
@mkdir -p $@/lib64
@mkdir -p $@/lib/x86_64-linux-gnu
# Copy libs
@# Copy necessary libs
@cp -L /lib64/ld-linux-x86-64.so.2 $@/lib64
@cp -L /lib/x86_64-linux-gnu/libc.so.6 $@/lib/x86_64-linux-gnu
@cp -L /lib/x86_64-linux-gnu/libstdc++.so.6 $@/lib/x86_64-linux-gnu
@cp -L /lib/x86_64-linux-gnu/libm.so.6 $@/lib/x86_64-linux-gnu
@cp -L /lib/x86_64-linux-gnu/libgcc_s.so.1 $@/lib/x86_64-linux-gnu
@cp -L /lib/x86_64-linux-gnu/libpthread.so.0 $@/lib/x86_64-linux-gnu
@# Copy from apps
@make --no-print-directory -C apps
ifeq ($(ENABLE_SYSCALL_TEST), 1)
@# Copy syscall test suite
@make --no-print-directory -C syscall_test
endif
$(RAMDISK): $(INITRAMFS) $(INITRAMFS_DIRS) $(INITRAMFS_FILES)
@echo "Generating the ramdisk image..."
@rm -rf $(BUILD_DIR) && mkdir -p $(BUILD_DIR)
@./mkinitramfs $(INITRAMFS) $@
@(cd $(INITRAMFS); find . | cpio -o -H newc) > $@
build: $(RAMDISK)
clean:
@rm -rf $(INITRAMFS) $(BUILD_DIR)
@rm -rf $(BUILD_DIR)

12
regression/apps/Makefile Normal file
View File

@ -0,0 +1,12 @@
MAKEFLAGS += --no-builtin-rules # Prevent the implicit rules from compiling ".c" or ".s" files automatically.
MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH)))
INITRAMFS ?= $(CUR_DIR)/../build/initramfs
.PHONY: all
all: build
build:
@cp -a $(CUR_DIR)/* $(INITRAMFS)
@cd $(INITRAMFS) && find . \( -name "*.s" -o -name "*.c" -o -name "Makefile" -o -name "README.md" \) -delete

View File

@ -1,15 +0,0 @@
#!/bin/bash
set -e
if [ $# -ne 2 ]; then
echo "Usage: mkinitramfs <dir> <cpio>"
exit 1
fi
if [ -d "$1" ]; then
echo "Creating $2 from $1"
(cd "$1"; find . | cpio -o -H newc) > "$2"
else
echo "The first argument must be a directory"
exit 1
fi

View File

@ -0,0 +1,43 @@
TESTS ?= open_test read_test
MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH)))
BUILD_DIR := $(CUR_DIR)/../build
SRC_DIR := $(BUILD_DIR)/gvisor_src
BIN_DIR := $(BUILD_DIR)/syscall_test_bins
INITRAMFS ?= $(CUR_DIR)/../build/initramfs
TARGET_DIR := $(INITRAMFS)/opt/syscall_test
RUN_BASH := $(CUR_DIR)/run_syscall_test.sh
BLOCK_LIST := $(CUR_DIR)/blocklists
.PHONY: all clean
all: $(TESTS)
$(SRC_DIR):
@if ! type bazel > /dev/null; then \
echo "bazel is not installed, please run $(CUR_DIR)/install_bazel.sh with sudo permission to install it."; \
exit 1 ; \
fi
@rm -rf $@ && mkdir -p $@
@cd $@ && git clone -b 20200921.0 https://github.com/jinzhao-dev/gvisor.git .
$(BIN_DIR): $(SRC_DIR)
@rm -rf $@ && mkdir -p $@
@cd $(SRC_DIR) && bazel build --test_tag_filters=native //test/syscalls/...
@cp $(SRC_DIR)/bazel-bin/test/syscalls/linux/*_test $@
$(TARGET_DIR): $(RUN_BASH) $(BLOCK_LIST) $(BIN_DIR)
@rm -rf $@ && mkdir -p $@
@# Prepare tests dir for test binaries
@mkdir $@/tests
@# Copy blocklists
@cp -rf $(BLOCK_LIST) $@
@# Copy bash script
@cp -f $(RUN_BASH) $@
$(TESTS): $(TARGET_DIR)
@cp -f $(BIN_DIR)/$@ $(TARGET_DIR)/tests
clean:
@rm -rf $(BIN_DIR) $(TARGET_DIR)

View File

@ -0,0 +1,15 @@
OpenTest.AppendConcurrentWrite
OpenTest.CanTruncateReadOnly
OpenTest.CanTruncateWithStrangePermissions
OpenTest.CanTruncateReadOnlyNoWritePermission_NoRandomSave
OpenTest.CanTruncateWriteOnlyNoReadPermission_NoRandomSave
OpenTest.FileNotDirectory
OpenTest.MustCreateExisting
OpenTest.NameTooLong
OpenTest.Null
OpenTest.OTrunc
OpenTest.OTruncAndReadOnlyDir
OpenTest.OpenNoFollowStillFollowsLinksInPath
OpenTest.OpenNonDirectoryWithTrailingSlash
OpenTest.SymlinkRecurse
OpenTest.Truncate

View File

@ -0,0 +1,2 @@
ReadTest.EofAfterRead
ReadTest.ZeroBuffer

View File

@ -0,0 +1,17 @@
#! /bin/bash
set -e
if type bazel > /dev/null; then
echo "Bazel has been installed already"
exit 0
fi
apt update && apt install curl gnupg -y
curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg
mv bazel.gpg /etc/apt/trusted.gpg.d/
echo 'deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8' | tee /etc/apt/sources.list.d/bazel.list
apt update && apt install bazel=5.4.0 -y
echo "Bazel is installed successfully"

View File

@ -0,0 +1,60 @@
#!/bin/sh
SCRIPT_DIR=$(dirname "$0")
TEST_BIN_DIR=$SCRIPT_DIR/tests
BLOCKLIST_DIR=$SCRIPT_DIR/blocklists
FAIL_CASES=$SCRIPT_DIR/fail_cases
BLOCK=""
TESTS=0
PASSED_TESTS=0
RESULT=0
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'
get_blocklist_subtests(){
if [ -f $BLOCKLIST_DIR/$1 ]; then
BLOCK=$(sed ':a;N;$!ba;s/\n/:/g' $BLOCKLIST_DIR/$1)
return 0
else
BLOCK=""
return 1
fi
}
run_one_test(){
echo -e "Run Test Case: $1"
ret=0
if [ -f $TEST_BIN_DIR/$1 ]; then
get_blocklist_subtests $1
$TEST_BIN_DIR/$1 --gtest_filter=-$BLOCK
ret=$?
else
echo -e "Warning: $1 test does not exit"
ret=1
fi
echo ""
return $ret
}
rm -f $FAIL_CASES && touch $FAIL_CASES
for syscall_test in $(find $TEST_BIN_DIR/. -name \*_test) ; do
test_name=$(basename "$syscall_test")
run_one_test $test_name
if [ $? -eq 0 ] && PASSED_TESTS=$((PASSED_TESTS+1));then
TESTS=$((TESTS+1))
else
echo -e "$test_name" >> $FAIL_CASES
TESTS=$((TESTS+1))
fi
done
echo -e "$GREEN$PASSED_TESTS$NC of $GREEN$TESTS$NC test cases are passed."
[ $PASSED_TESTS -ne $TESTS ] && RESULT=1
if [ $TESTS != $PASSED_TESTS ]; then
echo -e "The $RED$(($TESTS-$PASSED_TESTS))$NC failed test cases in this run are as follows:"
cat $FAIL_CASES
fi
exit $RESULT

View File

@ -108,6 +108,23 @@ impl FileTable {
entry.map(|e| e.file)
}
pub fn close_all(&mut self) -> Vec<Arc<dyn FileLike>> {
let mut closed_files = Vec::new();
let closed_fds: Vec<FileDescripter> = self
.table
.idxes_and_items()
.map(|(idx, _)| idx as FileDescripter)
.collect();
for fd in closed_fds {
let entry = self.table.remove(fd as usize).unwrap();
let events = FdEvents::Close(fd);
self.notify_fd_events(&events);
entry.notify_fd_events(&events);
closed_files.push(entry.file);
}
closed_files
}
pub fn get_file(&self, fd: FileDescripter) -> Result<&Arc<dyn FileLike>> {
self.table
.get(fd as usize)

View File

@ -2,7 +2,9 @@ use crate::events::Observer;
use crate::prelude::*;
use super::file_handle::FileLike;
use super::utils::{AccessMode, Consumer, IoEvents, Poller, Producer, StatusFlags};
use super::utils::{
AccessMode, Consumer, InodeMode, InodeType, IoEvents, Metadata, Poller, Producer, StatusFlags,
};
pub struct PipeReader {
consumer: Consumer<u8>,
@ -55,6 +57,25 @@ impl FileLike for PipeReader {
AccessMode::O_RDONLY
}
fn metadata(&self) -> Metadata {
Metadata {
dev: 0,
ino: 0,
size: 0,
blk_size: 0,
blocks: 0,
atime: Default::default(),
mtime: Default::default(),
ctime: Default::default(),
type_: InodeType::NamedPipe,
mode: InodeMode::from_bits_truncate(0o400),
nlinks: 1,
uid: 0,
gid: 0,
rdev: 0,
}
}
fn register_observer(
&self,
observer: Weak<dyn Observer<IoEvents>>,
@ -122,6 +143,25 @@ impl FileLike for PipeWriter {
AccessMode::O_WRONLY
}
fn metadata(&self) -> Metadata {
Metadata {
dev: 0,
ino: 0,
size: 0,
blk_size: 0,
blocks: 0,
atime: Default::default(),
mtime: Default::default(),
ctime: Default::default(),
type_: InodeType::NamedPipe,
mode: InodeMode::from_bits_truncate(0o200),
nlinks: 1,
uid: 0,
gid: 0,
rdev: 0,
}
}
fn register_observer(
&self,
observer: Weak<dyn Observer<IoEvents>>,

View File

@ -270,6 +270,11 @@ impl Process {
for thread in &*self.threads.lock() {
thread.exit();
}
// close all files then exit the process
let files = self.file_table().lock().close_all();
for file in files {
let _ = file.clean_for_close();
}
// move children to the init process
if !self.is_init_process() {
if let Some(init_process) = get_init_process() {

View File

@ -1,6 +1,6 @@
use crate::prelude::*;
pub const MAX_THREAD_NAME_LEN: usize = 256;
pub const MAX_THREAD_NAME_LEN: usize = 16;
#[derive(Debug)]
pub struct ThreadName {

View File

@ -4,5 +4,5 @@
pub const MAX_FILENAME_LEN: usize = 128;
pub const MAX_ARGV_NUMBER: usize = 128;
pub const MAX_ENVP_NUMBER: usize = 128;
pub const MAX_ARG_LEN: usize = 128;
pub const MAX_ARG_LEN: usize = 2048;
pub const MAX_ENV_LEN: usize = 128;

View File

@ -28,6 +28,7 @@ pub fn sys_pipe2(fds: Vaddr, flags: u32) -> Result<SyscallReturn> {
let mut file_table = current.file_table().lock();
pipe_fds.reader_fd = file_table.insert(pipe_reader);
pipe_fds.writer_fd = file_table.insert(pipe_writer);
debug!("pipe_fds: {:?}", pipe_fds);
write_val_to_user(fds, &pipe_fds)?;
Ok(SyscallReturn::Return(0))