Verification
Everything here is in the repository and wired into CI. Nothing is claimed as proved beyond what the tools actually reported, and the limits are listed next to the results.
Current status
The results of every run are on the Actions page; this page does not copy them. Figures below were measured on a Linux x86_64 development host unless they say otherwise.
Techniques
| Technique | Covers |
|---|---|
Unit, CLI and integration tests (make check) | controller, /proc parser, process table, list, CLI, lock, snapshot selection, 55 end-to-end checks with real processes, and 123 checks of cpulimit together with nice, ionice, taskset, chrt, env, nohup, timeout and setsid |
| Sanitizers | ASan and UBSan on gcc and clang; clang's integer sanitizers |
| Fuzzing | the stat parser and the CLI parser against stated properties, not only "no crash" (libFuzzer in CI, a standalone driver locally) |
| Bounded model checking | CBMC harnesses for the controller invariants, tick conversion and the /proc parser (cbmc.yml, manual and weekly). experimental The same harnesses run as random tests on every push (make verif-sim). |
| Mutation testing | defects re-introduced one at a time, each of which must fail a unit test: 25 mutants, all detected locally |
| Coverage | gcov line and branch coverage with an 85 % line gate |
| Stress and valgrind | fork/exit churn under -i with 300 children: no leaks, flat memory, nothing left stopped |
| Static analysis | gcc -fanalyzer (must be silent), clang-tidy, cppcheck, scan-build, CodeQL |
| Format | clang-format 18 checked in CI (make format-check) |
| Portability | CI matrix of OS, compiler, libc and architecture; the experimental vm workflow |
Measurements
Every number is in data/results.json with where and when it was measured, and the charts are drawn from that file by tools/charts.py. They are single runs on a 2-CPU virtual machine with a 10 ms clock tick: treat differences of a point or two as noise.
-l N, 8 s per case. The two versions are the same within noise: both overshoot near 50 % and under-deliver at 10 % and 25 %, because they share one controller. Nothing here is an improvement.-i. The single-target CPU figure is close to the noise floor (an earlier run measured the baseline at 0.12 %), so only the system-call and -i rows were stable across runs. Measured on 1.0.0 against upstream 0.3.2 and not repeated for later releases.runtime/daemon.c and main.c are the weakest; they hold the process and signal plumbing that is hardest to drive from tests.Bugs found by the process
- The bounded model checker found
compute_slices()computing0 / NaNand cleaning it up afterwards (fixed in 1.0.1, the result is identical for every input). - Property checks found
ticks_to_ms()returning a negative number forticks = 2^63 + 1at 1000 Hz and saturating wrongly above 1.8e16 ticks per second (neither reachable with a real clock rate). - The OpenBSD runs exposed that
p_rtimeonly advances in scheduling-quantum steps and that zombies are listed; both are fixed and explained under platform notes.
Not verified
- Nothing is "proved". One CBMC harness has finished (the field positions of a well-formed stat line, within its bounds, CI run 39890593464); the others were narrowed and have no verdict yet. The harnesses cover three pure functions, not the control loop or signal handling.
- macOS, FreeBSD, NetBSD and Solaris/illumos code has not been run by hand; see the platform table for what CI ran.
- musl on non-x86 runs under qemu-user, which fakes parts of
/proc; some process-name tests are skipped there. - Real syslog delivery is not tested. No ACSL/Frama-C specification exists, and AFL++ was not run.
- The benchmark was measured once on a shared VM, on 1.0.0.
Reproduce
make check analyze verif-sim format-check # what a change must pass
sh tools/mutation.sh # mutants; every one must be killed
make coverage # gcov, with the 85 % gate
make stress # churn, 300 children, valgrind
make bench BASELINE=/path/to/old/cpulimit # two binaries back to back