# BST-TIMEOUT: 300 . "$BST_LIB" require iotop iotop/present # iotop 1.26 -- Tomas-M/iotop, the C rewrite of the per-process I/O monitor # (meta-oe recipes-extended, installed as /usr/sbin/iotop). 85-htop covers the # frame-drawing shape and 85-atop the parseable-record shape; this file drives # the ONE surface iotop has that needs no terminal, `-b` batch mode, which is # also the pass criterion this port was opened against. # # THE SUITE SPLITS IN TWO AND THE SPLIT IS THE POINT. Everything above the # "SAMPLING" banner is green and stays green: it is the argument handling that # exits inside parse_args() -- main.c calls parse_args() BEFORE system_checks() # (main.c:4-6), and -v/-h exit from inside it (main.c:251-259), so those cases # never reach the kernel facility the rest of the program is built on. # Everything below the banner is RED on this image, for a platform reason iotop # cannot defend against, and is written to assert what a WORKING platform # produces so the cases flip green by themselves when the kernel gains the # option (the 85-atop / 85-nmon / 99-sharutils convention). # # WHY EVERY SAMPLE IS RED: this kernel is built without CONFIG_TASKSTATS, and # with it without CONFIG_TASK_XACCT/CONFIG_TASK_IO_ACCOUNTING, which is what # creates /proc//io. arch/wasm32/configs/wasm32_defconfig says so in its # own words -- `# CONFIG_TASKSTATS is not set`, in a block whose stated reason # is "genetlink consumer -- nothing reads it yet". iotop IS that reader: it # takes per-task I/O over a NETLINK_GENERIC taskstats socket # (xxxid_info.c:97-115), and there is no /proc-only mode to fall back to. # # THE REFUSAL IS FAST AND CLEAN, SO NO IOTOP INVOCATION NEEDS BOUNDING. # system_checks() (checks.c) stats /proc/self/io first and returns 1 when it is # absent, so iotop exits 1 with its own diagnostic BEFORE it opens a netlink # socket and before it walks /proc at all. That matters here twice over: it is # a deterministic message rather than a hang, and it means this suite never # reaches the whole-/proc walk that hangs procps' top and vmstat on this arm # (brintos/linux#9). No case below wraps iotop in `arun` or a background # subshell -- unlike 85-atop, whose every sampling case had to be rationed. # # WHAT DOES NEED BOUNDING IS THE WRITER, and the marker at the top of this file # is there for it alone. The last case backgrounds a `dd` so that iotop has # something to see; writes on this guest are slow and `kill` does not deliver # here (brintos/linux#21), so an oversized writer is unreapable and eats the # runner's bound. It is sized and reaped accordingly -- see that case. The # 300 s marker is headroom for it against the runner's 60 s default, not a # claim that iotop is slow. # # THE ORDER IS DELIBERATE: the CLI cases come FIRST so they are emitted even if # a later case runs long, the same reason 85-htop puts its CLI surface ahead of # its frames. # icheck : ok iff exits AND # says . check_out cannot express this -- it requires rc 0 as well as # the needle -- and iotop's refusals are all non-zero statuses carrying the # message that explains them, so asserting the two separately would leave the # message unchecked or the status unasserted. Same helper shape as 85-atop's # rcout and 85-htop's hrcout. icheck() { _id=$1; _want=$2; _needle=$3; shift 3 _o=$("$@" &1); _r=$? case "$_o" in *"$_needle"*) _h=1 ;; *) _h=0 ;; esac if [ "$_r" -eq "$_want" ] && [ "$_h" -eq 1 ]; then bst_ok "$_id" elif bst_execfail "$_o"; then bst_unrun "$_id" "$_o" else bst_notok "$_id" "rc=$_r want=$_want needle:'$_needle' got:'$(_bst_1line "$_o")'"; fi } # ---- packaging -------------------------------------------------------------- # iotop's Makefile installs to $(PREFIX)/sbin, not bindir. Asserting where it # resolves catches a packaging change that leaves it off the PATH the runner # actually has (same case as 63-tunctl.test). check_eq iotop/resolves-to-sbin "/usr/sbin/iotop" "$(command -v iotop)" # ---- the CLI surface: no netlink, no /proc --------------------------------- # -v prints argv[0] and VERSION together (main.c:252), so the needle pins the # built version rather than merely "something answered". icheck iotop/version 0 "iotop 1.26" iotop --version icheck iotop/help-rc 0 "Usage:" iotop --help # The two options this suite's red half drives, asserted out of the help text so # a build that dropped batch mode is caught here rather than below. icheck iotop/help-batch 0 "-b, --batch" iotop --help icheck iotop/help-iter 0 "--iter=NUM" iotop --help # The only non-zero status iotop has that is not the requirements refusal. icheck iotop/bad-option 1 "unrecognized option" iotop --nosuchopt # ---- SAMPLING: red on this image, brintos/linux#72 ------------------------- # Each case asserts what a working platform produces. The detail carries iotop's # own diagnostic, which names the three kernel options by name -- so a reader # of a red run needs nothing but the result line to see what is missing. # The platform fact itself, asserted directly rather than only through iotop, so # a run says whether the KERNEL changed independently of whether iotop did. # system_checks() reads exactly this file (checks.c: stat("/proc/self/io")). if [ -r /proc/self/io ]; then bst_ok iotop/kernel-io-accounting else bst_notok iotop/kernel-io-accounting "brintos/linux#72: /proc/self/io absent -- this kernel is built without CONFIG_TASKSTATS, so CONFIG_TASK_XACCT/CONFIG_TASK_IO_ACCOUNTING cannot be set either"; fi # idiag : name WHICH failure this is, for a red case's detail. iotop's # requirements refusal names the three kernel options itself, so a red carrying # that text is the known gap; anything else is a NEW failure and says so rather # than being filed under the same issue by a detail string that assumed it. # This is what a green "the refusal text is still what we expect" case would # have bought -- without a case that has to go red the day the kernel is fixed. idiag() { case "$1" in *"I/O accounting support (CONFIG_TASKSTATS, CONFIG_TASK_DELAY_ACCT, CONFIG_TASK_IO_ACCOUNTING)"*) printf 'brintos/linux#72: iotop refused on the known kernel gap' ;; *) printf 'UNEXPECTED -- not the brintos/linux#72 requirements refusal' ;; esac } # Surface 1 -- the batch table. -b -n1 takes ONE sample and prints the header # block plus the column row (view_batch.c:38-52). Asserting the column header # rather than a number keeps the case about "a parseable table was produced". _o=$(iotop -b -n 1 &1); _r=$? case "$_o" in *"DISK READ"*) _h=1 ;; *) _h=0 ;; esac if [ "$_r" -eq 0 ] && [ "$_h" -eq 1 ]; then bst_ok iotop/batch-table elif bst_execfail "$_o"; then bst_unrun iotop/batch-table "$_o" else bst_notok iotop/batch-table "$(idiag "$_o"): rc=$_r want a 'DISK READ' column header -- got:'$(_bst_1line "$_o")'"; fi # Surface 2 -- the ticket's own criterion: a process generating I/O appears in # that table. ONE dd, backgrounded, not a respawn loop: this guest does not # return memory on process exit (brintos/linux#21) and refuses to stage a large # exec under fragmentation (brintos/linux#44), and it was a retry loop around a # big exec that wedged the guest outright while 60-ckermit.test was being # written. # # THE WRITER IS SIZED, AND REAPED, AGAINST TWO MEASURED FACTS. First, writes # here are SLOW: a first draft wrote 256 MB and had not finished inside the # suite's 300 s bound, which cost this file an `iotop/suite-timeout` and lost # the case entirely. Second, `kill` DOES NOT DELIVER on this arm # (brintos/linux#21), so `kill "$_wp"; wait "$_wp"` does not shorten anything -- # it waits out the whole transfer, which is exactly how the first draft spent # the bound. 8 MB at 64k is enough writing to be sampled and completes on its # own, and the reaper below is a BOUNDED POLL rather than a signal, so a writer # that outlives its welcome costs this case a few seconds instead of the file. _w=${TMPDIR:-/tmp}/bst-iotop.$$ dd if=/dev/zero of="$_w" bs=64k count=128 >/dev/null 2>&1 & _wp=$! _o=$(iotop -b -n 1 -P &1); _r=$? _i=0; while kill -0 "$_wp" 2>/dev/null && [ "$_i" -lt 30 ]; do sleep 1; _i=$((_i+1)); done rm -f "$_w" case "$_o" in *"dd"*) _h=1 ;; *) _h=0 ;; esac if [ "$_r" -eq 0 ] && [ "$_h" -eq 1 ]; then bst_ok iotop/batch-writer-visible elif bst_execfail "$_o"; then bst_unrun iotop/batch-writer-visible "$_o" else bst_notok iotop/batch-writer-visible "$(idiag "$_o"): rc=$_r want the writing 'dd' in the table -- got:'$(_bst_1line "$_o")'"; fi