RustForge

rust-kernel

Rustbelt: a hobby x86_64 kernel written in Rust

rust-kernel / tests / libc_batch1.rs
4076 bytes ยท raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//! Stage 16, "libc batch 1": the system calls musl and dash use once a
//! program is running -- signal waiting (rt_sigsuspend, sigtimedwait,
//! sigqueueinfo, sigaltstack), umask and rlimits, fcntl record locks,
//! truncate/fsync/chmod/utimensat, prctl, sched_getaffinity, madvise,
//! robust futex lists, and what happens to orphaned children. The detailed
//! checks live in user/libctest.rs (exit code = failed checks); this file
//! runs each group and checks nothing is left behind.
#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![test_runner(rustbelt::test_runner)]
#![reexport_test_harness_main = "test_main"]

extern crate alloc;

use alloc::string::String;
use bootloader::{BootInfo, entry_point};
use core::panic::PanicInfo;
use rustbelt::process::{self, ExitStatus, SpawnArgs};
use rustbelt::{fs, memory, syscall, thread};

entry_point!(main);

fn main(boot_info: &'static BootInfo) -> ! {
    rustbelt::init();
    rustbelt::init_memory(boot_info);
    rustbelt::init_smp(); // all 4 CPUs run threads
    fs::init();
    rustbelt::init_storage();
    test_main();
    rustbelt::hlt_loop();
}

fn run(args: &[&str]) -> ExitStatus {
    let mut spawn = SpawnArgs::kernel("libctest");
    spawn.args.extend(args.iter().map(|a| String::from(*a)));
    let p = process::spawn_path("/mnt/disk0/bin/libctest", spawn).expect("spawn failed");
    process::wait(&p)
}

fn counts() -> (usize, usize, usize) {
    thread::sleep_ticks(3); // let exited threads' stacks be freed
    (thread::thread_count(), process::count(), memory::frames_in_use())
}

/// Run one group twice (the first warms caches), check it passes and
/// threads, processes, memory frames and file locks come back to where
/// they were.
fn run_without_leaks(group: &str) {
    assert_eq!(run(&[group]), ExitStatus::Exited(0), "libctest {} failed (warm-up)", group);
    let before = counts();
    assert_eq!(run(&[group]), ExitStatus::Exited(0), "libctest {} failed", group);
    assert_eq!(counts(), before, "libctest {} leaked something", group);
    assert_eq!(fs::lock::count(), 0, "libctest {} left file locks behind", group);
}

/// dash's `wait`: block SIGCHLD, fork, rt_sigsuspend until the handler ran.
/// Before stage 16 rt_sigsuspend was ENOSYS and dash spun forever here.
#[test_case]
fn dash_wait_with_sigsuspend() {
    run_without_leaks("wait");
}

/// rt_sigpending, rt_sigtimedwait, queued real-time signals, sigaltstack.
#[test_case]
fn signal_waiting_and_queueing() {
    run_without_leaks("signals");
}

/// fcntl: F_GETFL/F_SETFL, F_DUPFD vs RLIMIT_NOFILE, record locks between
/// processes (conflicts, waiting, EDEADLK, dropped on close and exit).
#[test_case]
fn fcntl_flags_and_record_locks() {
    run_without_leaks("locks");
}

/// truncate/ftruncate, fsync, chmod family, umask, utimensat on ramfs,
/// ext2 and FAT; EROFS on btrfs.
#[test_case]
fn file_metadata_calls() {
    run_without_leaks("files");
}

/// rlimits, prctl, sched_getaffinity, madvise, robust futex lists.
#[test_case]
fn process_odds_and_ends() {
    run_without_leaks("misc");
}

/// A process exits leaving a running child and a zombie child: both are
/// cleaned up (their frames come back); a PR_SET_CHILD_SUBREAPER ancestor
/// adopts and reaps orphans instead.
#[test_case]
fn orphans_are_freed() {
    assert_eq!(run(&["orphans"]), ExitStatus::Exited(0), "libctest orphans failed (warm-up)");
    thread::sleep_ticks(10);
    let before = counts();
    for _ in 0..3 {
        assert_eq!(run(&["orphans"]), ExitStatus::Exited(0), "libctest orphans failed");
    }
    thread::sleep_ticks(10); // the last orphan is still sleeping 40 ms
    assert_eq!(counts(), before, "orphaned children leaked");
}

/// `--trace` names syscalls it doesn't know by number.
#[test_case]
fn trace_names_unknown_syscalls() {
    assert_eq!(syscall::trace_name(130), "rt_sigsuspend");
    assert_eq!(syscall::trace_name(204), "sched_getaffinity");
    assert_eq!(syscall::trace_name(999), "syscall_999");
}

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    rustbelt::test_panic_handler(info)
}