. "$BST_LIB" # sysstat is the performance-monitoring counterpart to procps (85-procps.test). # procps answers "what is the system doing now"; sysstat reads the same /proc # counters at two points in time and reports the RATE between them. HOW it gets # that second sample is the whole story of this port on this machine: # # - INTERVAL forms (`sar 1 3`, `iostat 2`, ...) arm alarm(2) and block in # pause(2). Here that pair returns AT ONCE instead of after the interval # (linux-in-the-browser#120), so they do not hang -- they complete with both # samples taken in the same instant and every rate computed over a ~0 s # window. So the interval case below asserts only that the form RUNS and # emits its samples; that the numbers are meaningless is recorded as a skip # rather than asserted, so it needs no edit if the timer is ever fixed. # - SINCE-BOOT forms take exactly one sample and never arm the timer: # `sar -u 0` (interval 0 = "since system startup"), and a bare # iostat/mpstat/pidstat. These are the forms that mean something here. # - The sadc data file reaches a real two-sample delta with NO timer at all: # `sadc 1 ` writes its one record and exits BEFORE the # pause, so two invocations append two records and `sar -f` deltas them. # That is what the data-file block exercises, and it is the only path on # this machine that produces a rate over a real elapsed interval. # # No busybox applet is named sar/iostat/mpstat/pidstat, so unlike net-tools and # wget there is no update-alternatives ambiguity to disprove: a `sar` that # answers at all can only be this port. require sar sysstat/present SADC=/usr/libexec/sa/sadc SADATA=/tmp/.bst-sysstat.dat # check_fail -- an error case is only right when # the exit status and the diagnostic both are; check_out cannot express it # (it requires rc 0). check_fail() { _id=$1; _want=$2; _needle=$3; shift 3 _out=$("$@" 2>&1); _rc=$? case "$_out" in *"$_needle"*) _hit=1 ;; *) _hit=0 ;; esac if [ "$_rc" -eq "$_want" ] && [ "$_hit" -eq 1 ]; then bst_ok "$_id" else bst_notok "$_id" "rc=$_rc want=$_want needle:'$_needle' got:'$(_bst_1line "$_out")'"; fi } # --- identity and inventory -------------------------------------------------- check_out sysstat/version "sysstat version 12.7.5" sar -V # All eight programs, at the paths the port puts them: seven in ${bindir} and the # collector under ${libexecdir}/sa, which is the SADC_PATH sar execs when it needs # a sample. A missing sadc would leave every sar case below failing for a reason # no single case would name. _missing= for _c in /usr/bin/sar /usr/bin/sadf /usr/bin/iostat /usr/bin/mpstat \ /usr/bin/pidstat /usr/bin/tapestat /usr/bin/cifsiostat "$SADC"; do [ -x "$_c" ] || _missing="$_missing $_c" done if [ -z "$_missing" ]; then bst_ok sysstat/commands else bst_notok sysstat/commands "not executable:$_missing"; fi # --- sar, since boot --------------------------------------------------------- # Each of these forks sadc for a single record, so they exercise the sar->sadc # pipe as well as the /proc reader named in the comment. check_out sysstat/sar-cpu "%idle" sar -u 0 ;# /proc/stat check_out sysstat/sar-mem "kbmemfree" sar -r 0 ;# /proc/meminfo check_out sysstat/sar-load "ldavg-1" sar -q 0 ;# /proc/loadavg check_out sysstat/sar-paging "pgpgin/s" sar -B 0 ;# /proc/vmstat # /proc/net/dev: assert the row, not just the header -- this machine has exactly # one interface (lo, 127.0.0.1/8, empty routing table), so the count is exact. _n=$(sar -n DEV 0 2>/dev/null | grep -c ' lo ') check_eq sysstat/sar-net 1 "$_n" # --- iostat / mpstat / pidstat, single-shot ---------------------------------- check_out sysstat/iostat-cpu "avg-cpu" iostat -c check_out sysstat/mpstat-percpu "%gnice" mpstat -P ALL check_out sysstat/mpstat-irq "intr/s" mpstat -I SUM ;# /proc/interrupts check_out sysstat/pidstat-mem "minflt/s" pidstat -r -p 1 # pidstat -d runs and prints its header; the VALUES are -1.00 because this kernel # has no /proc//io (see the skip below). Asserting the header keeps this case # true either way. check_out sysstat/pidstat-io "kB_rd/s" pidstat -d -p 1 # Exactly one data row for a single pid... _n=$(pidstat -p 1 2>/dev/null | grep -c '^[0-9][0-9]:') check_eq sysstat/pidstat-pid1 2 "$_n" ;# column header + 1 process row # ...and a full /proc scan lists the whole process table. Worth asserting rather # than assuming: procps' equivalent `ps -e` is runtime-blocked on this machine # (brintos/linux#9, 85-procps.test), and pidstat walking the same /proc is not. _n=$(pidstat 2>/dev/null | grep -c '^[0-9][0-9]:') if [ "$_n" -ge 6 ]; then bst_ok sysstat/pidstat-scan else bst_notok sysstat/pidstat-scan "want >=6 timestamped rows, got $_n"; fi check_out sysstat/cifsiostat "Filesystem" cifsiostat # tapestat exits 1 when it finds no tape drive with statistics, which is the # correct answer on a machine with no SCSI tape class -- the status is part of # the contract, so assert both halves. check_fail sysstat/tapestat 1 "No tape drives with statistics found" tapestat # --- the one source fix this port needed ------------------------------------- # pidstat -e forks and execs the program it is to monitor. Its exec_pgm() # built the child's argv in a VLA and handed the address to execvp; a dynamic # stack allocation escaping to an opaque callee is not coroutinizable, and with # exec_pgm also on glibc's setjmp/longjmp unwind path the hwjs-cc pass refused # the whole cross-frame lowering and pidstat did not link at all. The port moves # the vector to the heap. This case runs that exact path: the nonce can only # reach the console through the exec'd child. _nonce="bst-sysstat-$$" check_out sysstat/pidstat-exec "$_nonce" pidstat -e /bin/echo "$_nonce" # --- the sadc/sar -f/sadf data pipeline (no timer involved) ------------------ rm -f "$SADATA" $SADC 1 1 "$SADATA" 2>/dev/null $SADC 1 1 "$SADATA" 2>/dev/null if [ -s "$SADATA" ]; then bst_ok sysstat/sadc-record else bst_notok sysstat/sadc-record "$SADATA absent or empty after two sadc runs"; fi # Two records in, so sar has a real elapsed interval to divide by: one data row # plus the Average line it only prints when it has something to average. check_out sysstat/sar-from-file "Average:" sar -f "$SADATA" # sadf renders the same binary file in its three interchange formats. check_out sysstat/sadf-csv "hostname;interval;timestamp" sadf -d "$SADATA" check_out sysstat/sadf-json "\"number-of-cpus\": 1" sadf -j -- -u "$SADATA" check_out sysstat/sadf-xml "" sadf -x -- -u "$SADATA" rm -f "$SADATA" # --- the interval form completes (whatever the timer does) ------------------- # `sar -u 1 2` prints the column header plus one row per sample: 3 timestamped # lines. True whether the interval elapses or collapses, so this case measures # that the alarm/pause/two-sample machinery runs to completion, not the clock. _n=$(sar -u 1 2 2>/dev/null | grep -c '^[0-9][0-9]:') check_eq sysstat/sar-interval 3 "$_n" # --- error contracts --------------------------------------------------------- check_fail sysstat/no-such-file 2 "Cannot open" sar -f /tmp/.bst-no-such-sa-file check_fail sysstat/bad-option 1 "Usage: sar" sar --no-such-option 0 # --- present but not reachable on this machine (attributed, NOT executed as # assertions: each would flip on its own if the platform gained the source) bst_skip sysstat/interval-honoured \ "the interval does not elapse: alarm(2)+pause(2) returns at once, so \`sar -u 5 2\` finishes in ~1s with both samples at the same timestamp and rates over a ~0s window (linux-in-the-browser#120)" bst_skip sysstat/disk \ "no /proc/diskstats on this kernel: sar -d exits 3 'Requested activities not available' and iostat's device table is empty (its avg-cpu half is asserted above)" bst_skip sysstat/filesystem \ "sar -F exits 3: read_filesystem() only counts /etc/mtab entries whose device field starts with '/', and every mount here is nodev (virtiofs root, proc, sysfs, devtmpfs, tmpfs)" bst_skip sysstat/pidstat-io-values \ "no /proc//io on this kernel, so pidstat -d reports -1.00 for every counter; its header is asserted above" bst_skip sysstat/sar-default-file \ "a bare \`sar\` reads /var/log/sa/saNN and exits 2: /var/log is a dangling symlink to volatile/log on this image, which nothing mounts or creates -- the data-file cases above use an explicit path" bst_skip sysstat/lm-sensors \ "sar -m/sadc sensor collection is not built: lm-sensors is unported and the browser machine exposes no hwmon devices (PACKAGECONFIG pinned empty)"