From: dengzhongyuan365-dev Date: Thu, 23 Jul 2026 01:42:15 +0000 (+0800) Subject: scriptreplay: fix --divisor discarding fractional seconds X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=309039b3c36e2861ec2c16440ecaf95a39725154;p=thirdparty%2Futil-linux.git scriptreplay: fix --divisor discarding fractional seconds 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 --- diff --git a/term-utils/script-playutils.c b/term-utils/script-playutils.c index 8c68ad14c..c978d133d 100644 --- a/term-utils/script-playutils.c +++ b/term-utils/script-playutils.c @@ -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, >)) { diff --git a/tests/ts/script/replay b/tests/ts/script/replay index d002077ff..4bb029bf0 100755 --- a/tests/ts/script/replay +++ b/tests/ts/script/replay @@ -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 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 #