Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 27 additions & 9 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# The nightly: every pin in the current set on X64, built with gcc-14 and with rucc from tinyconfig
# and the test fragment, and graded by rk test on boot, smoke, KUnit and the selftests, which are
# built with gcc-14 and with rucc.
# and the test fragment, and graded by rk test on boot, smoke, KUnit, the selftests and LTP, which
# are built with gcc-14 and with rucc.
#
# Both builds of a version run in one job, because rk test and rk mixed need both build directories
# and the objects are too big to move between jobs. When the rucc kernel fails a unit the reference
Expand Down Expand Up @@ -78,7 +78,8 @@ jobs:
run: |
sudo apt-get update -q
sudo apt-get install -qy --no-install-recommends gcc-14 flex bison bc libelf-dev libssl-dev \
cpio qemu-system-x86 busybox-static libcap-dev libnuma-dev rsync
cpio qemu-system-x86 busybox-static libcap-dev libnuma-dev rsync libaio-dev libacl1-dev \
libkeyutils-dev
- name: let the runner user open /dev/kvm
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
Expand Down Expand Up @@ -130,16 +131,30 @@ jobs:
run: |
target/release/rk selftests --build "$RUNNER_TEMP/gcc" --cc rucc \
--out "$RUNNER_TEMP/selftests-rucc"
- uses: actions/cache@v6.1.0
with:
path: ${{ env.RK_CACHE }}/ltp
key: ltp-${{ hashFiles('ltp.toml') }}
- name: rk ltp with gcc-14
run: |
target/release/rk ltp --build "$RUNNER_TEMP/gcc" --cc gcc-14 --out "$RUNNER_TEMP/ltp-gcc"
- name: rk ltp with rucc
continue-on-error: true
run: |
target/release/rk ltp --build "$RUNNER_TEMP/gcc" --cc rucc --out "$RUNNER_TEMP/ltp-rucc"
- name: rk test
id: test
run: |
rucc_selftests=()
rucc_userland=()
if [ -f "$RUNNER_TEMP/selftests-rucc/selftests.json" ]; then
rucc_selftests=(--rucc-selftests "$RUNNER_TEMP/selftests-rucc")
rucc_userland+=(--rucc-selftests "$RUNNER_TEMP/selftests-rucc")
fi
if [ -f "$RUNNER_TEMP/ltp-rucc/ltp.json" ]; then
rucc_userland+=(--rucc-ltp "$RUNNER_TEMP/ltp-rucc")
fi
target/release/rk test --reference "$RUNNER_TEMP/gcc" --other "$RUNNER_TEMP/rucc" \
--kinds boot,smoke,kunit,kselftest --selftests "$RUNNER_TEMP/selftests-gcc" \
"${rucc_selftests[@]}" --out "$RUNNER_TEMP/test"
--kinds boot,smoke,kunit,kselftest,ltp --selftests "$RUNNER_TEMP/selftests-gcc" \
--ltp "$RUNNER_TEMP/ltp-gcc" "${rucc_userland[@]}" --out "$RUNNER_TEMP/test"
- name: rk cross-modules
if: always() && steps.test.outcome != 'skipped'
continue-on-error: true
Expand All @@ -153,11 +168,12 @@ jobs:
unit=$(python3 -c 'import json, sys; t = json.load(open(sys.argv[1])); print(next((u["name"] for u in t["units"] if u["graded"] and not u["passed"] and u.get("attribution", "kernel") == "kernel"), ""))' "$RUNNER_TEMP/test/test.json")
if [ -z "$unit" ]; then echo "no kernel failure to bisect"; exit 0; fi
target/release/rk mixed --reference "$RUNNER_TEMP/gcc" --other "$RUNNER_TEMP/rucc" \
--unit "$unit" --selftests "$RUNNER_TEMP/selftests-gcc" --out "$RUNNER_TEMP/mixed"
--unit "$unit" --selftests "$RUNNER_TEMP/selftests-gcc" --ltp "$RUNNER_TEMP/ltp-gcc" \
--out "$RUNNER_TEMP/mixed"
- name: keep the record
if: always()
run: |
mkdir -p record/gcc record/rucc record/test record/mixed record/cross record/selftests
mkdir -p record/gcc record/rucc record/test record/mixed record/cross record/selftests record/ltp
for side in gcc rucc; do
cp "$RUNNER_TEMP/$side"/{build.json,summary.md,build.log,.config} "record/$side/" 2>/dev/null || true
done
Expand All @@ -166,6 +182,8 @@ jobs:
for side in gcc rucc; do
mkdir -p "record/selftests/$side"
cp -r "$RUNNER_TEMP/selftests-$side"/{selftests.json,logs} "record/selftests/$side/" 2>/dev/null || true
mkdir -p "record/ltp/$side"
cp -r "$RUNNER_TEMP/ltp-$side"/{ltp.json,logs} "record/ltp/$side/" 2>/dev/null || true
done
cp "$RUNNER_TEMP"/mixed/{mixed.json,summary.md,relink.jsonl} "$RUNNER_TEMP"/mixed/*.log record/mixed/ 2>/dev/null || true
cp -r "$RUNNER_TEMP/mixed/fuel" record/mixed/ 2>/dev/null || true
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,14 @@ The reference compilers live in era containers built from `provision/eras` and p

`rk boot --build DIR` boots a build under QEMU with `rk-init/init.sh` as PID 1 and a static busybox, and reads the smoke checks off the serial console. `rk baseline` builds and boots with the reference three times and writes the result under `results/baseline`.

`rk test --reference DIR --other DIR --kinds boot,smoke,kunit` is the graded run. It boots the reference kernel and the rucc kernel once per suite, with `rk.suite=` on the command line, and compares them unit by unit: `boot`, each smoke check, each KUnit result read from the TAP on the console, and each test module loading. The kunit boot carries the build's modules, so build with `--targets "bzImage modules"` when the test fragment makes KUnit tests modules. The units the reference passes in every run (`--runs`) are graded and the rucc kernel must pass all of them. The splats in both consoles are normalized, and one that only the rucc kernel prints fails the run too. Given a rucc-built busybox with `--rucc-busybox`, every failure is attributed to the kernel, the userland or the two together by running the other two cells of the 2x2. With `--kinds kselftest` and `--selftests DIR` from `rk selftests`, every pinned collection is a boot of its own, and each program `run_kselftest.sh` reports is a unit, `kselftest:<collection>:<program>`. A rucc-built set passed with `--rucc-selftests` counts as part of the rucc userland for attribution. LTP is not run yet. The run directory gets `test.json`, `summary.md` and the console of every boot.
`rk test --reference DIR --other DIR --kinds boot,smoke,kunit` is the graded run. It boots the reference kernel and the rucc kernel once per suite, with `rk.suite=` on the command line, and compares them unit by unit: `boot`, each smoke check, each KUnit result read from the TAP on the console, and each test module loading. The kunit boot carries the build's modules, so build with `--targets "bzImage modules"` when the test fragment makes KUnit tests modules. The units the reference passes in every run (`--runs`) are graded and the rucc kernel must pass all of them. The splats in both consoles are normalized, and one that only the rucc kernel prints fails the run too. Given a rucc-built busybox with `--rucc-busybox`, every failure is attributed to the kernel, the userland or the two together by running the other two cells of the 2x2. With `--kinds kselftest` and `--selftests DIR` from `rk selftests`, every pinned collection is a boot of its own, and each program `run_kselftest.sh` reports is a unit, `kselftest:<collection>:<program>`. A rucc-built set passed with `--rucc-selftests` counts as part of the rucc userland for attribution. `--kinds ltp` with `--ltp DIR` from `rk ltp` does the same for LTP: every pinned runtest file is a boot, every test in it is a unit, `ltp:<runtest>:<tag>`, and `--rucc-ltp` is the rucc-built LTP. The run directory gets `test.json`, `summary.md` and the console of every boot.

`rk mixed --reference DIR --other DIR --unit UNIT` finds what makes one unit that `rk test` failed go wrong. Every object the two builds have with different bytes is a candidate. A trial copies the reference build, puts some rucc objects in place of the reference's, relinks with the reference's own make command so that nothing is compiled again, and boots the suite that reports the unit. Delta debugging finds a smallest set of rucc objects that still fails. When one object is left and the other build is rucc's, the object is compiled again with `-fpass-fuel-global=N` and the fuel is bisected to the first transformation that breaks the unit, and the `-fdump-ir=all` dumps on each side of it name the pass and the functions it changed. A trial that does not link is a finding of its own and ends the search. The run directory gets `mixed.json`, `summary.md`, the console of every boot and the dumps.

`rk selftests --build DIR --cc COMPILER` builds the kselftest collections that `rows.toml` pins for the build's row, from the build's own kernel tree and against its exported headers, and installs them the way `run_kselftest.sh` expects. Each collection is built on its own with `make -k`, so one program that needs a library the machine lacks leaves the rest standing, and the programs that did not build are listed in `selftests.json`. The programs are linked dynamically, because static linking breaks real collections, so the initramfs that `rk test` makes for a collection carries the interpreter and the shared libraries its programs need. `--collections` picks others, and the output goes to `selftests` in the build directory unless `--out` says otherwise.

`rk ltp --build DIR --cc COMPILER` builds the LTP release pinned in `ltp.toml` with that compiler. The tarball is checked against its hash and unpacked into the cache once, and each build configures and builds a copy of it with `make -k`, then installs it under `/ltp`. `ltp.json` lists the tests of each runtest file `rows.toml` pins for the row, and which of them were not built. The initramfs for a runtest file carries only the programs its commands name, the shared scripts and `tst_` helpers, the tests' data and the libraries they need, since the whole install does not fit in the guest. Each test runs with a ten minute limit, its output goes to the console only when it fails, and LTP's exit code 32, nothing to test here, is a skip. `--runtests` picks other files, and the output goes to `ltp` in the build directory unless `--out` says otherwise.

`rk cross-modules --reference DIR --other DIR` loads the rucc modules into the reference kernel and the reference modules into the rucc kernel. It first checks every module against the other kernel without booting: the relocation types, the whole `vermagic`, symbols the kernel does not export and CRCs that differ from its `Module.symvers`. Then it runs the kunit suite three times per run: the reference kernel with its own modules, which finds the units that pass in QEMU without hardware, and then each kernel with the other build's modules. Both crossed boots must pass every unit the first one passed, and a splat only a crossed boot prints fails the run. Both builds need `--targets "bzImage modules"`. The run directory gets `cross-modules.json`, `summary.md` and the console of every boot.

`rk config-diff`, `rk probes`, `rk flags-diff`, `rk demands` and `rk asm-inventory` read build directories and write markdown tables: the `.config` differences, the compiler probes the two compilers answered differently, the flags each unit was compiled with on one side only (read from the `.cmd` files kbuild writes), the failed units by error, and the instructions the kernel writes itself. `rk config-diff --why` also names the Kconfig expressions behind each difference and the probe that decided them, and needs the kernel tree, which it finds through `build.json` or `--source`. `rk syntax --build DIR --cc rucc` replays every unit of a reference build through rucc's front end with the reference's own command lines, `-fsyntax-only` for C and `-E` for assembly, and writes the failing units grouped by their first error. Units listed with an issue in `syntax-known.toml` are known failures, so the command fails only on new ones.
Expand Down
21 changes: 20 additions & 1 deletion crates/rk/src/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,15 @@ fn kvm_usable(row: &Row) -> bool {
.is_ok()
}

/// The guest's memory in MiB: 1 GiB, and more when the initramfs is big, since the kernel holds
/// the archive and its unpacked files at the same time while it boots. An LTP runtest file can
/// bring a few hundred megabytes of programs.
#[must_use]
pub fn memory_mib(initramfs: u64) -> u64 {
let archive = initramfs.div_ceil(1 << 20);
(512 + 2 * archive).max(1024).next_multiple_of(256)
}

/// The QEMU command line.
#[must_use]
pub fn qemu_command(plan: &Plan, image: &Path, kvm: bool) -> Vec<String> {
Expand All @@ -311,12 +320,14 @@ pub fn qemu_command(plan: &Plan, image: &Path, kvm: bool) -> Vec<String> {
} else {
plan.row.machine.clone()
};
let initramfs = std::fs::metadata(&plan.initramfs).map_or(0, |m| m.len());
let memory = format!("{}M", memory_mib(initramfs));
let mut words: Vec<String> = [
plan.row.qemu.as_str(),
"-M",
machine.as_str(),
"-m",
"1G",
memory.as_str(),
"-smp",
"2",
"-nographic",
Expand Down Expand Up @@ -517,6 +528,14 @@ mod tests {
assert!(!o.passed());
}

#[test]
fn a_big_initramfs_gets_more_memory() {
assert_eq!(memory_mib(0), 1024);
assert_eq!(memory_mib(200 << 20), 1024);
assert_eq!(memory_mib(400 << 20), 1536);
assert_eq!(memory_mib((400 << 20) + 1), 1536);
}

#[test]
fn x86_images_live_under_arch_x86() {
assert_eq!(srcarch("x86_64"), "x86");
Expand Down
51 changes: 28 additions & 23 deletions crates/rk/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,30 @@ const SWITCHES: &[&str] = &[
"no-fuel",
];

/// The flags of `rk build`.
const BUILD: &[&str] = &[
"row",
"config",
"cc",
"out",
"jobs",
"targets",
"bringup",
"bringup-cc",
"fragment",
"keep-going",
"twice",
"stack-usage",
"config-only",
"no-upstream-check",
];

/// Options each command accepts, and whether it takes a positional word.
fn accepted(command: &str) -> Option<(bool, &'static [&'static str])> {
Some(match command {
"fetch" => (true, &["set", "no-upstream-check", "all"]),
"sets" => (false, &["releases", "write"]),
"build" => (
true,
&[
"row",
"config",
"cc",
"out",
"jobs",
"targets",
"bringup",
"bringup-cc",
"fragment",
"keep-going",
"twice",
"stack-usage",
"config-only",
"no-upstream-check",
],
),
"build" => (true, BUILD),
"config-diff" => (false, &["reference", "other", "why", "source"]),
"probes" | "flags-diff" | "symvers-diff" | "objtool-report" => {
(false, &["reference", "other"])
Expand All @@ -81,12 +81,15 @@ fn accepted(command: &str) -> Option<(bool, &'static [&'static str])> {
"rucc-busybox",
"selftests",
"rucc-selftests",
"ltp",
"rucc-ltp",
"runs",
"timeout",
"out",
],
),
"selftests" => (false, &["build", "cc", "collections", "jobs", "out"]),
"ltp" => (false, &["build", "cc", "runtests", "jobs", "out"]),
"mixed" => (
false,
&[
Expand All @@ -96,6 +99,7 @@ fn accepted(command: &str) -> Option<(bool, &'static [&'static str])> {
"unit",
"busybox",
"selftests",
"ltp",
"timeout",
"jobs",
"out",
Expand Down Expand Up @@ -157,14 +161,15 @@ usage:
rk syntax --build DIR [--cc rucc] [--allow FILE] [--jobs N]
rk boot --build DIR [--row X64] [--initramfs FILE | --busybox PATH] [--timeout 300]
[--append WORDS]
rk test --reference DIR --other DIR [--kinds boot,smoke,kunit,kselftest] [--row X64] [--runs 1]
[--selftests DIR] [--rucc-selftests DIR]
rk mixed --reference DIR --other DIR --unit UNIT [--row X64] [--timeout 600] [--no-fuel]
[--selftests DIR]
rk test --reference DIR --other DIR [--kinds boot,smoke,kunit,kselftest,ltp] [--row X64]
[--runs 1] [--selftests DIR] [--rucc-selftests DIR] [--ltp DIR] [--rucc-ltp DIR]
[--busybox PATH] [--rucc-busybox PATH] [--timeout 600] [--out DIR]
rk mixed --reference DIR --other DIR --unit UNIT [--row X64] [--timeout 600] [--no-fuel]
[--selftests DIR] [--ltp DIR]
rk cross-modules --reference DIR --other DIR [--row X64] [--runs 1] [--timeout 600]
[--busybox PATH] [--out DIR]
rk selftests --build DIR [--cc COMPILER] [--collections timers,size] [--jobs N] [--out DIR]
rk ltp --build DIR [--cc COMPILER] [--runtests syscalls,mm] [--jobs N] [--out DIR]
rk initramfs --out FILE [--busybox PATH]
rk baseline [VERSION] --cc COMPILER [--row X64] [--config defconfig] [--fragment test]
[--runs 3] [--timeout 600] [--stack-usage]
Expand Down
Loading
Loading