]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
scriptreplay: fix --divisor discarding fractional seconds
authordengzhongyuan365-dev <dengzhongyuan@uniontech.com>
Thu, 23 Jul 2026 01:42:15 +0000 (09:42 +0800)
committerdengzhongyuan365-dev <dengzhongyuan@uniontech.com>
Thu, 23 Jul 2026 01:42:15 +0000 (09:42 +0800)
replay_get_next_step() divided the tv_sec and tv_usec fields of the
recorded delay by the divisor independently.  Both fields are integers, so
the fractional part of the whole-seconds field was discarded instead of
being carried into microseconds.  Replaying a 1.000000 s delay with
--divisor 2 therefore produced a 0.000000 s delay (the next step was
emitted immediately) instead of the expected 0.500000 s, and the error
could approach one second per step and accumulate across recordings with
multiple pauses.

Scale the complete duration once in microseconds, then split the result
back into seconds and microseconds so the fractional whole-seconds part is
preserved.  This affects both scriptreplay and scriptlive, which share the
helper.

Add a "divisor-fractional" subtest to tests/ts/script/replay that verifies
a 1.000000 s delay scaled by --divisor 2 takes about half a second rather
than collapsing to zero.

Addresses: https://github.com/util-linux/util-linux/issues/4500
Signed-off-by: dengzhongyuan365-dev <dengzhongyuan@uniontech.com>
term-utils/script-playutils.c
tests/ts/script/replay

index 8c68ad14ca538d4b4c03344a76a1c1ce14e8edc3..c978d133d8d9f9b7fcbeef441f08d52069ce99b6 100644 (file)
@@ -490,9 +490,23 @@ done:
 
        /* normalize delay */
        if (stp->delay_div) {
+               /*
+                * Scale the complete duration as a single value in
+                * microseconds, then split the result back into seconds and
+                * microseconds.  Dividing tv_sec and tv_usec independently
+                * truncates the fractional part of the whole-seconds field
+                * instead of carrying it into microseconds, so for example a
+                * 1.000000 s delay scaled by divisor 2 became 0.000000 s rather
+                * than the expected 0.500000 s.
+                */
+               double total = (double) step->delay.tv_sec * 1000000.0
+                               + (double) step->delay.tv_usec;
+
                DBG(TIMING, ul_debug(" normalize delay: divide"));
-               step->delay.tv_sec /= stp->delay_div;
-               step->delay.tv_usec /= stp->delay_div;
+               total /= stp->delay_div;
+               step->delay.tv_sec  = (time_t) (total / 1000000.0);
+               step->delay.tv_usec = (suseconds_t) (total
+                               - (double) step->delay.tv_sec * 1000000.0);
        }
        if (timerisset(&stp->delay_max) &&
            timercmp(&step->delay, &stp->delay_max, >)) {
index d002077ff5f5ae5baaafe817e3865b8be0257a8d..4bb029bf0c5de75073beb98181e9cc87f3b5c1cc 100755 (executable)
@@ -47,6 +47,34 @@ sed -i "s|$TIMING_FILE|timingfile|g; s|$LOG_OUT_FILE|outlog|g" "$TS_OUTPUT" "$TS
 ts_finalize_subtest
 
 
+#
+# Divisor must scale the complete delay, not divide tv_sec and tv_usec
+# independently (see https://github.com/util-linux/util-linux/issues/4500).
+# A 1.000000 s delay with divisor 2 has to become ~0.5 s; the old per-field
+# division truncated the whole-seconds field and collapsed it to 0 s.
+#
+ts_init_subtest "divisor-fractional"
+printf '1.000000 1\n' >"$TS_OUTDIR/${TS_TESTNAME}-div-timing"
+printf 'Script started\nX' >"$TS_OUTDIR/${TS_TESTNAME}-div-typescript"
+
+start=$(date +%s.%N)
+$TS_CMD_SCRIPTREPLAY \
+       --divisor 2 \
+       "$TS_OUTDIR/${TS_TESTNAME}-div-timing" \
+       "$TS_OUTDIR/${TS_TESTNAME}-div-typescript" \
+       </dev/null >/dev/null 2>>"$TS_ERRLOG"
+end=$(date +%s.%N)
+elapsed=$(awk -v e="$end" -v s="$start" 'BEGIN{printf "%.3f", e - s}')
+
+# The fix sleeps ~0.5 s; the bug returned in ~0 s.  Use a window that is
+# robust to scheduler jitter but excludes both 0 s (bug) and 1 s (no scaling).
+awk -v t="$elapsed" 'BEGIN{exit (t < 0.20 || t > 0.90) ? 1 : 0}' || {
+       echo "elapsed=${elapsed}s (expected ~0.5s)" >>"$TS_OUTPUT"
+       ts_failed "divisor collapsed delay (elapsed=${elapsed}s)"
+}
+ts_finalize_subtest
+
+
 #
 # New command line format
 #