. "$BST_LIB" # iperf3 3.18 (meta-oe recipes-benchmark, SRCREV 2a298448) -- ESnet's network # throughput benchmark, and the FIRST tool on this image whose primary function # is a CLIENT AND A SERVER talking to each other. Every benchmark before it # (dhrystone, whetstone, libc-bench, mbw, memtester) is one process measuring # itself; iperf3 measures a socket, so the suite has to bring up a second process # and make the two meet -- the same shape 86-sysklogd.test established, and the # same polling discipline, because `sleep N` does not sleep on this arm # (linux-in-the-browser#120) and timeout(1) never fires. # # ORDERING IS LOAD-BEARING. Packaging first, then identity, then the argument and # error surface, then the socket surface, and the THREADED transfer LAST -- see # the block comment above it. A red in an early block is a packaging or build # change; a red in the last one is the platform, and it is expected today. require iperf3 iperf3/present PORT=5271 D=/tmp/.bst-iperf3 rm -rf "$D"; mkdir -p "$D" # --- packaging --------------------------------------------------------------- inv="" for f in /usr/bin/iperf3 /usr/include/iperf_api.h /usr/lib/libiperf.a; do [ -e "$f" ] || inv="$inv $f" done check_eq iperf3/inventory "" "$inv" # The static-only arm's standing invariant. iperf3 builds libiperf with libtool, # which asks for a versioned shared object by default, so this is a real test of # the distro's --disable-shared rather than a formality. check_eq iperf3/no-shared-objects "" "$(ls /usr/lib/libiperf*.so* 2>/dev/null)" # busybox has no iperf applet, so a bare `iperf3` can only be this port. check_eq iperf3/resolves-to-port "/usr/bin/iperf3" "$(command -v iperf3)" # --- identity and what configure resolved ------------------------------------ # `iperf3 --version` prints three lines, and the third is the only on-target # register of what the cross configure actually decided. ver=$(iperf3 --version 2>&1) check_eq iperf3/version "iperf 3.18 (cJSON 1.7.15)" "$(printf '%s\n' "$ver" | sed -n 1p)" feat=$(printf '%s\n' "$ver" | sed -n 3p) printf 'IPERF3-FEATURES %s\n' "$feat" check_rc iperf3/version-rc 0 iperf3 --version # The feature list is measured to be IDENTICAL to the native x86_64 control's, so # each entry is asserted on its own -- a probe that flips answers is then # attributable to itself rather than to "the version line changed". # # features-affinity is the one that earns its place. HAVE_CPU_AFFINITY is defined # by whichever of cpuset_setaffinity (BSD), sched_setaffinity (Linux) or # SetProcessAffinityMask (Win32) the configure link probe finds. Only the Linux # one is seeded in the wasm32 site file; the other two run REAL link probes, # which is exactly the false-positive risk the musl-era bbappend added # CACHED_CONFIGUREVARS for. This port dropped those overrides after measuring # that the probes answer correctly, and this case is the standing on-target # witness for that decision -- a false positive would have failed the build on a # missing or , so a red here means the probe started # lying in some new way. case "$feat" in *"CPU affinity setting"*) bst_ok iperf3/features-affinity ;; *) bst_notok iperf3/features-affinity "no 'CPU affinity setting' in: $(_bst_1line "$feat")" ;; esac case "$feat" in *"POSIX threads"*) bst_ok iperf3/features-threads ;; *) bst_notok iperf3/features-threads "no 'POSIX threads' in: $(_bst_1line "$feat")" ;; esac case "$feat" in *"TCP congestion algorithm setting"*) bst_ok iperf3/features-congestion ;; *) bst_notok iperf3/features-congestion "no 'TCP congestion algorithm setting' in: $(_bst_1line "$feat")" ;; esac case "$feat" in *"sendfile / zerocopy"*) bst_ok iperf3/features-sendfile ;; *) bst_notok iperf3/features-sendfile "no 'sendfile / zerocopy' in: $(_bst_1line "$feat")" ;; esac # PACKAGECONFIG = "" drops openssl, whose only consumer is the RSA client # authentication. --username exists in the help ONLY when HAVE_SSL is defined, so # its absence is what proves the overlay's one setting took. if iperf3 -h 2>&1 | grep -q -- "--username"; then bst_notok iperf3/no-openssl-auth "--username is in the help: HAVE_SSL is defined, so PACKAGECONFIG did not take" else bst_ok iperf3/no-openssl-auth fi check_rc iperf3/help-rc 0 iperf3 -h check_out iperf3/help-usage "Usage: iperf3 [-s|-c host] [options]" iperf3 -h # --- argument and error surface (no sockets involved) ------------------------ check_rc iperf3/no-args-rc 1 iperf3 check_out iperf3/no-args-msg "parameter error - must either be a client (-c) or server (-s)" sh -c 'iperf3 2>&1; exit 0' check_rc iperf3/bad-option-rc 1 iperf3 --nosuchopt check_out iperf3/bad-port-msg "port number must be between 1 and 65535 inclusive" sh -c 'iperf3 -s -p 99999 2>&1; exit 0' check_rc iperf3/bad-port-rc 1 iperf3 -s -p 99999 # --- socket surface, still single-threaded ----------------------------------- # A connect() to a port nothing is listening on. This is a real assertion about # the substrate's TCP: the message names strerror(ECONNREFUSED), so a stack that # swallowed the RST or reported a different errno would be visible here. check_out iperf3/connect-refused "Connection refused" \ sh -c "iperf3 -c 127.0.0.1 -p $((PORT + 20)) -t 1 --connect-timeout 2000 2>&1; exit 0" check_rc iperf3/connect-refused-rc 1 iperf3 -c 127.0.0.1 -p $((PORT + 20)) -t 1 --connect-timeout 2000 # listen_family() -- echoes the address families carrying a LISTEN # (state 0A) row for that local port, read from /proc rather than through `ss` so # the case asserts the KERNEL's view and cannot fail for iproute2's reasons. # # BOTH files are scanned, and that is not defensive coding. iperf3's server binds # IPv6-any by default and so appears in /proc/net/tcp6 alone -- a first draft of # this suite read only /proc/net/tcp and went red against a server that was # listening perfectly, on the native control. Which family it lands in is a # property of the target's stack rather than of iperf3, so the case passes on # either and the family is printed for the record instead of being asserted. # # Note the `hit` flag rather than a bare `exit 0` in the rule: awk's exit jumps to # END, whose own exit status wins, so the obvious spelling reports "not found" # every time. That is the second thing this suite's native dry-run caught. listen_family() { _hex=$(printf '%04X' "$1"); _f="" _one() { awk -v p=":$_hex" '$4 == "0A" && index($2, p) { hit = 1 } END { exit !hit }' "$1" 2>/dev/null; } _one /proc/net/tcp && _f="$_f inet" _one /proc/net/tcp6 && _f="$_f inet6" printf '%s' "${_f# }" } listening() { [ -n "$(listen_family "$1")" ]; } # BRINTOS-WORKAROUND: brintos/linux-in-the-browser#85 revisit — both iperf3 # processes run under setsid. A thread-Worker trap leaves park state that a # LATER, UNRELATED exec can hit, and under brintos-selftest's busy-polling # supervisor that wedges the whole guest rather than the process (measured for # libc-bench, BRINTOS-347: same binary, 6-9 s standalone vs. never-recovering # under the runner; a session boundary reliably changes the outcome). iperf3 # creates a thread per stream on BOTH sides, so both get the boundary. Drop the # setsid once #85's per-task park slots land. # # -1 makes the server handle one client and exit on its own; the kill below is # the belt to that braces. -D (daemonise) is deliberately NOT used: it forks # twice, and this suite has no business finding out what that does under the # runner's supervisor. setsid iperf3 -s -p "$PORT" -1 > "$D/server.log" 2>&1 & SRV=$! i=0 while [ $i -lt 25 ]; do listening "$PORT" && break; i=$((i + 1)); sleep 1; done fam=$(listen_family "$PORT") printf 'IPERF3-LISTEN-FAMILY %s\n' "${fam:-none}" if [ -n "$fam" ]; then bst_ok iperf3/server-listens else bst_notok iperf3/server-listens "no LISTEN row for port $PORT in /proc/net/tcp or /proc/net/tcp6 after $i polls: $(_bst_1line "$(cat "$D/server.log" 2>&1)")"; fi # WHICH FAMILY IT GOT IS A PLATFORM FINDING, so it is recorded in the CASE NAME # rather than asserted one way -- a verdict that passed either way would say # nothing, and pinning v4 would go red the day IPv6 arrives, which is good news. # Measured: this image has NO IPv6. iperf3 prefers AF_INET6 and falls back with # `warning: this system does not seem to support IPv6 - trying IPv4` on STDERR. # The stderr half matters: it is unbuffered, so it survives the trap below that # eats the server's buffered stdout entirely. srverr=$(cat "$D/server.log" 2>/dev/null) case "$srverr:$fam" in *"does not seem to support IPv6"*:*inet*) bst_ok iperf3/server-ipv4-only ;; *:*inet6*) bst_ok iperf3/server-ipv6-capable ;; *) bst_notok iperf3/server-ip-family "neither shape: family='$fam' log='$(_bst_1line "$srverr")'" ;; esac # --- the threaded transfer: the platform gate, and it runs LAST -------------- # iperf 3.16 replaced the select() loop over streams with ONE THREAD PER STREAM, # and there is no threadless build mode: src/iperf_client_api.c:714 and # src/iperf_server_api.c:910 both call pthread_create() unconditionally, once per # stream, for every protocol and every -P. So everything above this line is what # iperf3 can do on a substrate where pthread_create does not work, and the case # below is the one thing the tool exists for. # # EXPECTED RED, attributed to brintos/linux-in-the-browser#85. Leave it failing # rather than skipping it: it flips green by itself when the per-task park slots # land, and that regression signal is what the case is worth. The detail carries # the failure text so a CHANGE in how it fails is visible, which matters because # #85 has now presented three different ways. MEASURED HERE (superproject # b0b2dac, kernel c861e8bb): # # rc=132 (SIGILL), hwjs-user-rt VIOLATION: orphan at clear: parked slot not # empty at bracket entry ... a leaked slice reached the next bracketed call # # i.e. the ORPHAN-AT-CLEAR shape, not the glibc futex fatal ("The futex facility # returned an unexpected error code") that #85's mbuffer witness records at this # same superproject, and not the set-over-set-at-create shape the md5deep and # squashfs-tools witnesses record. The client and the server hit it with the # IDENTICAL orphaned handle, which is worth knowing: both sides create their # stream thread at the same point in the same protocol exchange. out=$(setsid iperf3 -c 127.0.0.1 -p "$PORT" -t 1 -P 1 --connect-timeout 2000 --rcv-timeout 5000 2>&1) rc=$? printf 'IPERF3-CLIENT-RC %s\n' "$rc" printf '%s\n' "$out" | sed 's/^/IPERF3-CLIENT /' case "$out" in *"iperf Done."*) bst_ok iperf3/loopback-transfer ;; *) bst_notok iperf3/loopback-transfer "brintos/linux-in-the-browser#85: rc=$rc $(_bst_1line "$out")" ;; esac # THE SERVER'S BANNER IS DELIBERATELY NOT ASSERTED, and the reason is worth # keeping so nobody adds the case back. `Server listening on ` goes to # STDOUT, which is a file here and therefore block-buffered; the server does not # exit cleanly -- it is killed by the same #85 trap as the client -- so the # buffer is never flushed and the banner is lost. A case on it could not pass # while #85 holds, and it would report the platform defect a second time under a # name that sounds like a packaging fault. What survives the trap is the server's # STDERR, which is unbuffered, and that is what iperf3/server-ipv4-only asserts. # The kernel's own view of the bind is iperf3/server-listens, above. printf '%s\n' "$(cat "$D/server.log" 2>/dev/null)" | sed 's/^/IPERF3-SERVER /' kill "$SRV" 2>/dev/null rm -rf "$D"