The row299 root-side pair (mos-1409) spans supervisor start-up and clean_session, which scans every /proc entry on the host. With about 1,850 processes that adds 0.6 to 1.6 s, so the unchanged inner probe read 2.5 to 3.9 s and failed the [1.25, 2.75) band, while the 1 s inner-timer mutant read 2.06 to 2.38 s and passed. Widening the bound alone would have kept that mutant alive. A second observer pair, also confined to the private test copy, brackets only the inner timer: begin just before the supervisor starts its sleep, end at the start of finish(). It writes only for the SO_PEERCRED peer and at most one pair. The unchanged band now applies to that interval (2.01 s on base, 1.00 s and 3.00 s for the 1 s and 3 s mutants, so both fail). The root pair remains required and must contain the timer pair; probe-inner-timeout already places it before the outer bound.
99 lines
4.5 KiB
JavaScript
99 lines
4.5 KiB
JavaScript
// Row299 / mos-1409: observations exist only in a private test copy of the
|
|
// shipped script. Neither the timer arguments nor its control flow are changed.
|
|
const CAPTURE = 'tester_capture() (\n';
|
|
const LEGACY = '# row276-legacy-begin';
|
|
// Stable even when a mutant removes the outer timeout from this command.
|
|
const LAUNCH = ' LC_ALL=C ';
|
|
const WAIT = ' wait "$child" 2>/dev/null || rc=$?\n';
|
|
// org-base-reds: the root pair above also spans supervisor start-up and clean_session, a scan of
|
|
// every host /proc entry whose cost grows with the host's process count. The timer pair brackets
|
|
// only the inner timer: from just before the supervisor starts its sleep to the start of finish().
|
|
const SUPERVISOR = '# row253-supervisor-begin\n';
|
|
const SUPERVISOR_END = '# row253-supervisor-end\n';
|
|
const TIMER = '"$sleep_bin" "$seconds" & timer=$!\n';
|
|
const FINISH = 'finish() {\n local rc="$?"\n';
|
|
|
|
export const P10_OBSERVERS = Object.freeze({
|
|
begin: ` # row299-observer-begin-start
|
|
if [ "$maximum" = 128 ] && [ -n "\${ROW299_TIMING_FILE-}" ]; then
|
|
{ read -r row299_up row299_spare < /proc/uptime &&
|
|
printf 'begin %s\\n' "$row299_up" >> "$ROW299_TIMING_FILE"; } 2>/dev/null || :
|
|
fi
|
|
# row299-observer-begin-end
|
|
`,
|
|
end: ` # row299-observer-end-start
|
|
if [ "$maximum" = 128 ] && [ -n "\${ROW299_TIMING_FILE-}" ]; then
|
|
{ read -r row299_up row299_spare < /proc/uptime &&
|
|
printf 'end %s\\n' "$row299_up" >> "$ROW299_TIMING_FILE"; } 2>/dev/null || :
|
|
fi
|
|
# row299-observer-end-end
|
|
`,
|
|
// Supervisor-side pair. Only the P10 peer's arguments carry the peer-credential option name.
|
|
// The pattern is split so this block never adds that literal to the supervisor text: the fake
|
|
// sudo stalls any command whose arguments contain it, and the supervisor text is an argument of
|
|
// every capture. finish() has no arguments of its own, so its stamp follows exactly one begin
|
|
// and is written at most once.
|
|
timerBegin: `# row299-timer-begin-start
|
|
if [ -n "\${ROW299_TIMER_FILE-}" ] && [[ "$*" == *SO_PEER*CRED* ]]; then
|
|
{ read -r row299_up row299_spare < /proc/uptime &&
|
|
printf 'begin %s\\n' "$row299_up" >> "$ROW299_TIMER_FILE"; } 2>/dev/null || :
|
|
fi
|
|
# row299-timer-begin-end
|
|
`,
|
|
timerEnd: ` # row299-timer-end-start
|
|
if [ -n "\${ROW299_TIMER_FILE-}" ]; then
|
|
{ read -r row299_up row299_spare < /proc/uptime &&
|
|
[[ "$(< "$ROW299_TIMER_FILE")" == begin* && "$(< "$ROW299_TIMER_FILE")" != *end* ]] &&
|
|
printf 'end %s\\n' "$row299_up" >> "$ROW299_TIMER_FILE"; } 2>/dev/null || :
|
|
fi
|
|
# row299-timer-end-end
|
|
`,
|
|
});
|
|
|
|
function uniqueIndex(source, text) {
|
|
const first = source.indexOf(text);
|
|
if (first < 0 || source.indexOf(text, first + text.length) >= 0) {
|
|
throw new Error('P10 observer anchor must occur exactly once');
|
|
}
|
|
return first;
|
|
}
|
|
|
|
export function instrumentP10Probe(source) {
|
|
const start = uniqueIndex(source, CAPTURE);
|
|
const end = uniqueIndex(source, LEGACY);
|
|
if (end <= start) throw new Error('P10 observer capture scope is reversed');
|
|
let capture = source.slice(start, end);
|
|
uniqueIndex(capture, LAUNCH);
|
|
uniqueIndex(capture, WAIT);
|
|
capture = capture.replace(LAUNCH, P10_OBSERVERS.begin + LAUNCH);
|
|
capture = capture.replace(WAIT, WAIT + P10_OBSERVERS.end);
|
|
source = source.slice(0, start) + capture + source.slice(end);
|
|
const from = uniqueIndex(source, SUPERVISOR), to = uniqueIndex(source, SUPERVISOR_END);
|
|
if (to <= from) throw new Error('P10 observer supervisor scope is reversed');
|
|
let supervisor = source.slice(from, to);
|
|
uniqueIndex(supervisor, TIMER);
|
|
uniqueIndex(supervisor, FINISH);
|
|
supervisor = supervisor.replace(TIMER, P10_OBSERVERS.timerBegin + TIMER);
|
|
supervisor = supervisor.replace(FINISH, FINISH + P10_OBSERVERS.timerEnd);
|
|
return source.slice(0, from) + supervisor + source.slice(to);
|
|
}
|
|
|
|
export function removeP10Observers(source) {
|
|
for (const block of Object.values(P10_OBSERVERS)) {
|
|
uniqueIndex(source, block);
|
|
source = source.replace(block, '');
|
|
}
|
|
return source;
|
|
}
|
|
|
|
export function readProbeSeconds(text) {
|
|
const match = /^begin ([0-9]+)\.([0-9]{2})\nend ([0-9]+)\.([0-9]{2})\n$/.exec(text);
|
|
if (!match || match[0] !== text) throw new Error('P10 observer needs exactly one complete ordered pair');
|
|
const begin = BigInt(match[1]) * 100n + BigInt(match[2]);
|
|
const end = BigInt(match[3]) * 100n + BigInt(match[4]);
|
|
if (end <= begin || end - begin > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
throw new Error('P10 observer interval must be positive and safely representable');
|
|
}
|
|
return Number(end - begin) / 100;
|
|
}
|