@mark1russell7/harness 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -19
- package/build/index.d.ts +446 -31
- package/build/index.js +1106 -336
- package/build/index.js.map +1 -1
- package/image/native/src/bin/client.rs +63 -3
- package/image/native/src/bin/inject.rs +119 -7
- package/image/router/Dockerfile +8 -0
- package/image/router/ctwatch.py +66 -0
- package/image/router/mbx.sh +61 -0
- package/image/router/resetter.py +104 -0
- package/package.json +7 -3
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/observe.ts","../src/verdict.ts","../src/lab.ts","../src/run.ts"],"sourcesContent":["import type { Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\nimport type { Cell, KernelRecord, ProbeEvent, Run } from \"./cell.ts\";\n\n/**\n * An outcome tells how the wait of a run ended. The socket failed with an errno, or it read the\n * end of the file. Or nothing came, or the reply came back after a stall.\n */\nexport type Outcome =\n | { readonly kind: \"error\"; readonly errno: string }\n | { readonly kind: \"eof\" }\n | { readonly kind: \"silent\" }\n | { readonly kind: \"answered\" };\n\n/** A measurement is what one run measured, against the prediction of its cell. */\nexport interface Measured {\n readonly environment: number;\n readonly index: number;\n readonly outcome: Outcome;\n /**\n * This time is from the reference event of the prediction to the detection, both as the kernel\n * stamped them in the records of the prober. It is null when nothing detected the fault.\n */\n readonly elapsed: Quantity | null;\n /** This time is the same interval from the TCP_INFO samples of the client, the second vantage. */\n readonly socket: Quantity | null;\n /** This time is how long the client took to see the error after the kernel reported it. */\n readonly noticed: Quantity | null;\n /**\n * These are the steps of the kernel after the reference: each keepalive probe, or each firing of\n * the RTO timer. After a stall, they are each retransmission from the send, the loss probe first.\n */\n readonly steps: readonly Quantity[];\n /**\n * These are the reply times of a measurement-only cell. For each request after the first, a\n * sample is the time from its send to the first data after it.\n */\n readonly samples?: readonly Quantity[];\n /** This count is the number of keepalive probes that the kernel sent. */\n readonly probes: number;\n /** This count is the number of segments that the kernel sent again. */\n readonly retransmissions: number;\n /** This count is the number of segments that the socket received after the request. */\n readonly acks: number;\n /** This text is the reason that the run cannot count, or null. A void run is not a pass and not a finding. */\n readonly void: string | null;\n /** This text tells what in the run disagrees with the prediction, other than its timing, or null. */\n readonly finding: string | null;\n}\n\n/** These are the errno numbers that the kernel reports to a socket, by the names that predictions use. */\nconst ERRNO: Readonly<Record<number, string>> = {\n 32: \"EPIPE\",\n 101: \"ENETUNREACH\",\n 103: \"ECONNABORTED\",\n 104: \"ECONNRESET\",\n 110: \"ETIMEDOUT\",\n 111: \"ECONNREFUSED\",\n 113: \"EHOSTUNREACH\",\n};\n\n/** This function gives the interval between two instants on the one clock, in seconds to the microsecond. */\nconst between = (from: number, to: number): Quantity =>\n quantity(Math.round((to - from) / 1e3) / 1e6, \"s\");\n\n/** These functions give the states of a state record: before the change, and after it. TCP_ESTABLISHED is 1, TCP_CLOSE_WAIT 8. */\nconst oldState = (record: KernelRecord): number | undefined => (record as { readonly old?: number }).old;\nconst newState = (record: KernelRecord): number | undefined => (record as { readonly new?: number }).new;\n\nconst count = (event: ProbeEvent | undefined, field: string): number =>\n event === undefined ? 0 : Number(event[field] ?? 0);\n\n/** This function gives the view of the client on the same interval: TCP_INFO, with a sample each 2 ms. */\nconst socketVantage = (\n cell: Cell,\n events: readonly ProbeEvent[],\n ready: ProbeEvent,\n request: ProbeEvent,\n): Quantity | null => {\n if (!cell.prediction.detects || cell.prediction.from === \"peer-closed\") return null;\n const ended = events.find((event) => event.ev === \"error\" || event.ev === \"eof\");\n const reference =\n cell.prediction.from === \"last-received\"\n ? ready\n : cell.prediction.from === \"request-sent\"\n ? request\n : events.find(\n (event) =>\n event.ev === \"kernel\" && event.t >= request.t && count(event, \"retransmits\") > 0,\n );\n return ended === undefined || reference === undefined ? null : between(reference.t, ended.t);\n};\n\n/**\n * This function reads one run against its cell, from the records of the kernel first.\n * - The socket of the run is the socket that the client connected from, in its namespace.\n * - The fault must hold before the event that it must meet. That is before the request goes into\n * it, and before the first keepalive probe. A live peer answers that probe, and the answer\n * starts the idle time again. A run where the fault was late is void. A run whose records the\n * prober lost is also void.\n */\nexport const measure = (cell: Cell, run: Run): Measured => {\n const { events, kernel } = run;\n const ready = events.find((event) => event.ev === \"ready\");\n const local = events.find((event) => event.ev === \"connected\")?.[\"local\"];\n const inflight = cell.probe.mode !== \"idle\";\n const request = inflight ? events.filter((event) => event.ev === \"sent\")[1] : ready;\n // the socket of the observer: the client socket, by the address that it connected from, or the\n // socket of the target for the same connection, in its namespace, whose peer is that address\n const server = cell.observer === \"server\";\n const netns = server ? run.targetNetns : run.netns;\n const cookie = kernel.find(\n (record) => record.netns === netns && (server ? record.dst : record.src) === local,\n )?.cookie;\n const socket = kernel.filter((record) => record.netns === netns && record.cookie === cookie);\n // the socket of the peer for the same connection, in the other namespace: a FIN counts from the\n // change of state that its close makes\n const peerNetns = server ? run.netns : run.targetNetns;\n const peerCookie = kernel.find(\n (record) => record.netns === peerNetns && (server ? record.src : record.dst) === local,\n )?.cookie;\n const keepalives = socket.filter(\n (record) => record.ev === \"probe\" && record.what === \"keepalive\",\n );\n const retransmitted = socket.filter((record) => record.ev === \"retrans\");\n /**\n * This function examines the timer that drives the measurement: keepalive or retransmit. It\n * finds the first firing in an interval that was two jiffies or more behind its wheel slot. The\n * wheel does a slot in the tick when it is due, thus such a delay comes from the CPU: the host\n * stopped a vCPU. The prediction is about the timing of the kernel, and that firing measured\n * the timing of the host. One jiffy is a tick that occurred while the wheel worked, and δ_abs\n * includes it. The function ignores the other timer: the retransmit timer fires one time, idle,\n * after a reply, and it does not move a keepalive.\n */\n const delayed = (from: number, to: number, timer: \"keepalive\" | \"retransmit\"): string | null => {\n const firings = socket.filter(\n (record) =>\n record.ev === \"timer\" && record.what === timer && record.mono >= from && record.mono <= to,\n );\n const late = firings.find((record) => (record.behind?.value ?? 0) >= 2);\n if (late !== undefined)\n return `its ${timer} timer ran ${late.behind!.value} jiffies behind its wheel slot: the VM delayed it, not the kernel`;\n // a keepalive timer that finds its socket held by its owner sends nothing, and it tries again\n // HZ/20 later (tcp_keepalive_timer). The retransmit timer only gives its work to the release\n // by the owner, some microseconds later\n const held = firings.find((record) => timer === \"keepalive\" && record.owned === true);\n return held === undefined\n ? null\n : \"its keepalive timer found the socket held by its owner and put itself off by HZ/20: the run measured the owner, not the timer\";\n };\n const acks =\n request === undefined\n ? 0\n : socket.filter((record) => record.ev === \"rx\" && record.mono > request.t).length;\n const base = {\n environment: run.environment,\n index: run.index,\n probes: keepalives.length,\n retransmissions: retransmitted.length,\n acks,\n };\n const nothing = {\n outcome: { kind: \"silent\" } as const,\n elapsed: null,\n socket: null,\n noticed: null,\n steps: [],\n finding: null,\n };\n\n if (ready === undefined) return { ...base, ...nothing, void: \"the client never had its reply\" };\n if (request === undefined)\n return { ...base, ...nothing, void: \"the request was never sent into the fault\" };\n if (run.lost === null)\n return {\n ...base,\n ...nothing,\n void: \"the prober wrote no clock line after the run, so nothing says what it dropped\",\n };\n if (run.lost > 0) return { ...base, ...nothing, void: `the prober dropped ${run.lost} records` };\n if (cookie === undefined)\n return { ...base, ...nothing, void: \"the prober saw nothing of this connection\" };\n let voided: string | null = null;\n if (run.fault !== null) {\n // the request of a client must go into the fault. The request of a cell on the server side must\n // reach the server first, and its reply must not reach the client before the fault holds. An\n // mtu-blackhole drops only the long reply, thus its request goes after the fault, as a client's does\n const replyFirst = server && inflight && cell.fault !== \"mtu-blackhole\";\n const replied = events.some(\n (event) => event.ev === \"data\" && event.t > request.t && event.t < run.fault!.t1,\n );\n if (inflight && !replyFirst && run.fault.t1 > request.t)\n voided = \"the request went out before the fault held\";\n else if (replyFirst && run.fault.t1 < request.t)\n voided = \"the request went out into the fault, so the server never had it\";\n else if (replyFirst && replied)\n voided = \"the server's reply arrived before the fault held\";\n else if (keepalives[0] !== undefined && keepalives[0].mono < run.fault.t1)\n voided = \"a keepalive probe went out before the fault held\";\n }\n\n // the detection as the kernel reported it to the owner of the socket, and as the client saw it.\n // The process of a server tells nothing that the harness reads, thus its kernel tells all\n const reported = socket.find((record) => record.ev === \"error\" && (record.err ?? 0) !== 0);\n const seen = server\n ? undefined\n : events.find((event) => event.ev === \"error\" || event.ev === \"eof\");\n // an end of file without an error is the change of state that the FIN of the peer makes, from\n // ESTABLISHED to CLOSE_WAIT, as the kernel stamps it\n const closeWait = socket.find((record) => record.ev === \"state\" && newState(record) === 8);\n const outcome: Outcome =\n reported !== undefined\n ? { kind: \"error\", errno: ERRNO[reported.err!] ?? `E${reported.err}` }\n : closeWait !== undefined || seen?.ev === \"eof\"\n ? { kind: \"eof\" }\n : seen !== undefined\n ? { kind: \"error\", errno: String(seen[\"errno\"]) }\n : { kind: \"silent\" };\n const ended = reported?.mono ?? closeWait?.mono ?? seen?.t;\n const noticed =\n reported !== undefined && seen !== undefined ? between(reported.mono, seen.t) : null;\n const { prediction } = cell;\n\n // a cell whose outcome is a distribution: each request of the run, and the time to its reply\n if (prediction.detects === null) {\n const sends = events.filter((event) => event.ev === \"sent\").slice(1);\n const samples = sends.flatMap((send, n) => {\n const next = sends[n + 1]?.t ?? Infinity;\n const reply = events.find((event) => event.ev === \"data\" && event.t > send.t && event.t < next);\n return reply === undefined ? [] : [between(send.t, reply.t)];\n });\n return {\n ...base,\n ...nothing,\n outcome: ended !== undefined ? outcome : { kind: \"answered\" },\n steps: retransmitted\n .filter((record) => record.mono > request.t)\n .map((record) => between(request.t, record.mono)),\n samples,\n void: voided,\n };\n }\n\n // a stall: nothing must detect it, and the connection must move again at the first\n // retransmission after it stops, timed from the send of the request\n if (!prediction.detects && prediction.recovers !== undefined) {\n const { recovers } = prediction;\n const heal = run.heal ?? null;\n if (voided === null && heal === null) voided = \"the stall never ended\";\n else if (voided === null && heal !== null) {\n const from = between(request.t, heal.t0);\n const to = between(request.t, heal.t1);\n if (\n valueIn(from, \"s\") <= valueIn(recovers.heal.low, \"s\") ||\n valueIn(to, \"s\") >= valueIn(recovers.heal.high, \"s\")\n )\n voided = `the stall ended ${from.value}–${to.value} s after the send, outside the ${recovers.heal.low.value}–${recovers.heal.high.value} s its prediction assumed`;\n }\n const steps = retransmitted\n .filter((record) => record.mono > request.t)\n .map((record) => between(request.t, record.mono));\n if (ended !== undefined)\n return {\n ...base,\n ...nothing,\n outcome,\n steps,\n void: voided,\n finding: `detected after ${between(request.t, ended).value} s, where it should recover`,\n };\n // the first segment back after the stall stopped: the answer of the server to the\n // retransmission that got through, and the read of the reply by the client\n const answer =\n heal === null\n ? undefined\n : socket.find((record) => record.ev === \"rx\" && record.mono > heal.t0);\n if (answer === undefined)\n return {\n ...base,\n ...nothing,\n steps,\n void: voided,\n finding: \"it never moved again: nothing came back after the stall ended\",\n };\n const reply = events.find((event) => event.ev === \"data\" && event.t > request.t);\n voided ??= delayed(request.t, answer.mono, \"retransmit\");\n return {\n ...base,\n outcome: { kind: \"answered\" },\n elapsed: between(request.t, answer.mono),\n socket: reply === undefined ? null : between(request.t, reply.t),\n noticed: reply === undefined ? null : between(answer.mono, reply.t),\n steps,\n void: voided,\n finding: null,\n };\n }\n\n if (!prediction.detects) {\n // the test of silence covers the window from the send of the request\n const window = valueIn(prediction.window, \"s\") * 1e9;\n if (ended === undefined || ended - request.t > window)\n return { ...base, ...nothing, void: voided };\n return {\n ...base,\n ...nothing,\n outcome,\n elapsed: between(request.t, ended),\n void: voided,\n finding: `detected after ${between(request.t, ended).value} s, where nothing should`,\n };\n }\n\n if (ended === undefined)\n return {\n ...base,\n ...nothing,\n void: voided,\n finding: `nothing detected it: ${prediction.timer} never fired`,\n };\n // keepalive counts from the last segment received before the fault. The budget of the give-up\n // counts from the first retransmission of the RTO timer, not from the tail loss probe before it.\n // A reset counts from the send of the client. The kernel has no record of that send, thus the\n // stamp is the stamp of the client\n const before = run.fault?.t1 ?? ready.t;\n const kernelReference: KernelRecord | undefined =\n prediction.from === \"last-received\"\n ? socket.filter((record) => record.ev === \"rx\" && record.mono <= before).at(-1)\n : prediction.from === \"first-retransmission\"\n ? retransmitted.find((record) => record.why === \"rto\")\n : prediction.from === \"peer-closed\"\n ? kernel.find(\n (record) =>\n record.netns === peerNetns &&\n record.cookie === peerCookie &&\n record.ev === \"state\" &&\n oldState(record) === 1,\n )\n : undefined;\n const reference: number | undefined =\n prediction.from === \"request-sent\" ? request.t : kernelReference?.mono;\n if (reference === undefined) {\n return {\n ...base,\n ...nothing,\n outcome,\n noticed,\n void: voided,\n finding: `it ended without its reference event (${prediction.from})`,\n };\n }\n const steps = (\n prediction.from === \"last-received\"\n ? keepalives\n : prediction.from === \"first-retransmission\"\n ? retransmitted.filter((record) => record.why === \"rto\" && record.mono > reference)\n : []\n ).map((record) => between(reference, record.mono));\n const matches =\n prediction.errno === \"EOF\"\n ? outcome.kind === \"eof\"\n : outcome.kind === \"error\" && outcome.errno === prediction.errno;\n const finding = matches\n ? null\n : `it ended with ${outcome.kind === \"error\" ? outcome.errno : \"end of file\"}, not ${prediction.errno}`;\n // a firing at the reference itself only moves the start of the interval. A reset meets the send\n // with no timer, and a FIN arrives with none\n if (prediction.from !== \"request-sent\" && prediction.from !== \"peer-closed\")\n voided ??= delayed(\n reference,\n ended,\n prediction.from === \"last-received\" ? \"keepalive\" : \"retransmit\",\n );\n return {\n ...base,\n outcome,\n elapsed: between(reference, ended),\n socket: server ? null : socketVantage(cell, events, ready, request),\n noticed,\n steps,\n void: voided,\n finding,\n };\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\nimport type { Prediction } from \"./cell.ts\";\nimport type { Measured } from \"./observe.ts\";\n\n/**\n * This amount is the noise of the pipeline itself, δ_abs, as the calibration measured it. It is\n * the 99.9th percentile of the whole latency of a reset, at the concurrency cap or less. Its\n * minimum is one jiffy. On 2026-10-12 the worst of 60 resets at 1 and 4 environments took\n * 1.141 ms, thus the minimum sets it to 4 ms. The calibration occurs again after each change to\n * the harness, the kernel or Docker.\n */\nexport const NOISE: Quantity = quantity(4, \"ms\");\n/** This fraction, δ_rel, is how far the median of a cell can be outside the prediction and stay equivalent. */\nexport const RELATIVE = 0.05;\n/** These are the run counts of a cell: 11, then 22 and 44 while its median crosses the edge of the band. */\nexport const STEPS = [11, 22, 44] as const;\n\n/**\n * A decision is the result of a verdict. A cell whose outcome is a distribution has no band, and\n * its decision is `measured`: a record, not a test.\n */\nexport type Decision = \"holds\" | \"fails\" | \"extend\" | \"inconclusive\" | \"measured\";\n\n/** An interval is a range of times. */\nexport interface Interval {\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/** A verdict is the judgement of a cell on its runs. */\nexport interface Verdict {\n readonly decision: Decision;\n readonly counted: number;\n readonly voided: number;\n /** This band is where each run must land: [T_lo − δ_abs, T_hi + δ_abs]. It is null for a silent prediction. */\n readonly band: Interval | null;\n /** These are the runs outside the band, and the reason for each. Each is a finding. */\n readonly outside: ReadonlyArray<{ readonly run: string; readonly why: string }>;\n readonly median: Quantity | null;\n /** This interval is the order-statistic interval of the median at 95 %, or the whole range for fewer than six runs. */\n readonly medianInterval: Interval | null;\n readonly why: string;\n}\n\nconst s = (value: number): Quantity => quantity(Math.round(value * 1e6) / 1e6, \"s\");\nconst name = (run: Measured): string => `env ${run.environment} run ${run.index}`;\n\n/** This function gives P(X ≤ j) for X ~ Binomial(n, 1/2), exactly. */\nconst below = (n: number, j: number): number => {\n let term = 1;\n let sum = 0;\n for (let i = 0; i <= j; i += 1) {\n sum += term;\n term = (term * (n - i)) / (i + 1);\n }\n return sum / 2 ** n;\n};\n\n/**\n * This function gives the distribution-free interval of a median: [x_(k), x_(n+1−k)], for the\n * largest k whose two tails each hold 2.5 % or less. For eleven runs, it gives [x_(2), x_(10)],\n * at 98.8 %.\n */\nexport const medianInterval = (sorted: readonly number[]): [number, number] => {\n const n = sorted.length;\n let k = 0;\n while (k + 1 <= n / 2 && below(n, k) <= 0.025) k += 1;\n return k === 0 ? [sorted[0]!, sorted[n - 1]!] : [sorted[k - 1]!, sorted[n - k]!];\n};\n\nconst middle = (sorted: readonly number[]): number => {\n const half = Math.floor(sorted.length / 2);\n return sorted.length % 2 === 1 ? sorted[half]! : (sorted[half - 1]! + sorted[half]!) / 2;\n};\n\n/**\n * This function gives the verdict of a cell, from two tests:\n * - Containment: each run lands in [T_lo − δ_abs, T_hi + δ_abs]. A run outside is a finding.\n * - Median equivalence: the interval of the median is in the band, made wider by δ_rel. An\n * interval outside the band fails. An interval across its edge extends the cell to 22 runs, then\n * 44, and then the verdict is inconclusive.\n *\n * A silent prediction holds only if no run detected the fault in its window. After a stall, the\n * return of the reply gets the same test as a detection, by its interval, and a detection is a\n * finding. The distribution test (a DKW band) needs a predicted distribution from a simulator,\n * which does not exist at this time.\n */\nexport const verdictOf = (prediction: Prediction, runs: readonly Measured[]): Verdict => {\n const counted = runs.filter((run) => run.void === null);\n const voided = runs.length - counted.length;\n const outside = counted\n .filter((run) => run.finding !== null)\n .map((run) => ({ run: name(run), why: run.finding! }));\n const empty = { counted: counted.length, voided, median: null, medianInterval: null };\n if (counted.length === 0)\n return { ...empty, decision: \"inconclusive\", band: null, outside, why: \"no run counted\" };\n\n if (prediction.detects === null) {\n const samples = counted\n .flatMap((run) => (run.samples ?? []).map((sample) => valueIn(sample, \"s\")))\n .sort((a, b) => a - b);\n const retransmissions = counted.reduce((sum, run) => sum + run.retransmissions, 0);\n const ended = counted.filter((run) => run.outcome.kind === \"error\" || run.outcome.kind === \"eof\");\n const why =\n samples.length === 0\n ? `${counted.length} runs measured, with no reply: ${prediction.why}`\n : `${counted.length} runs, ${samples.length} replies: median ${s(middle(samples)).value} s, slowest ${s(samples.at(-1)!).value} s, ${retransmissions} retransmissions, ${ended.length} connections ended. ${prediction.why}`;\n return {\n ...empty,\n decision: \"measured\",\n band: null,\n outside: [],\n median: samples.length === 0 ? null : s(middle(samples)),\n why,\n };\n }\n\n if (!prediction.detects && prediction.recovers === undefined) {\n const decision = outside.length === 0 ? \"holds\" : \"fails\";\n const why =\n decision === \"holds\"\n ? `nothing detected it in ${counted.length} runs of ${valueIn(prediction.window, \"s\")} s`\n : `${outside.length} of ${counted.length} runs detected it`;\n return { ...empty, decision, band: null, outside, why };\n }\n\n // the interval of a detection, or of the return of the reply after a stall\n const timed = prediction.detects ? prediction : prediction.recovers!;\n const noise = valueIn(NOISE, \"s\");\n const low = valueIn(timed.low, \"s\");\n const high = valueIn(timed.high, \"s\");\n const band = { low: s(low - noise), high: s(high + noise) };\n const values: number[] = [];\n for (const run of counted) {\n if (run.elapsed === null) continue;\n const value = valueIn(run.elapsed, \"s\");\n values.push(value);\n if (value < low - noise || value > high + noise)\n outside.push({\n run: name(run),\n why: `${value} s is outside [${band.low.value}, ${band.high.value}] s`,\n });\n }\n values.sort((a, b) => a - b);\n if (values.length === 0)\n return { ...empty, decision: \"fails\", band, outside, why: \"no run measured its time\" };\n const [from, to] = medianInterval(values);\n const median = s(middle(values));\n const interval = { low: s(from), high: s(to) };\n const wide = { low: low * (1 - RELATIVE), high: high * (1 + RELATIVE) };\n const inside = from >= wide.low && to <= wide.high;\n const disjoint = to < wide.low || from > wide.high;\n const counts = `${counted.length - outside.length} of ${counted.length} runs inside [${band.low.value}, ${band.high.value}] s`;\n const centre = `median ${median.value} s, its interval [${interval.low.value}, ${interval.high.value}] s`;\n const result = {\n counted: counted.length,\n voided,\n band,\n outside,\n median,\n medianInterval: interval,\n };\n if (outside.length > 0 || disjoint)\n return { ...result, decision: \"fails\", why: `${counts}; ${centre}` };\n if (inside) return { ...result, decision: \"holds\", why: `${counts}; ${centre}` };\n const decision = counted.length < STEPS[2] ? \"extend\" : \"inconclusive\";\n return { ...result, decision, why: `${counts}; ${centre} straddles the band` };\n};\n","import { createHash } from \"node:crypto\";\nimport {\n copyFileSync,\n mkdirSync,\n mkdtempSync,\n readFileSync,\n readdirSync,\n realpathSync,\n rmSync,\n statSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, relative } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { StartedNetwork, StartedTestContainer } from \"testcontainers\";\nimport type * as Testcontainers from \"testcontainers\";\nimport type { Client, KernelRecord, ProbeEvent, Target } from \"./cell.ts\";\n\n/**\n * These are the roots of the two packages that the images read: this package, and the prober\n * that Node resolves for it. In a workspace, they are two folders side by side. From npm, the\n * prober is a link, and a Docker build context does not follow a link. Thus each image builds\n * from a context that the harness copies from the two roots.\n */\nconst ROOTS: Readonly<Record<string, string>> = {\n harness: fileURLToPath(new URL(\"..\", import.meta.url)),\n prober: dirname(\n realpathSync(createRequire(import.meta.url).resolve(\"@mark1russell7/prober/package.json\")),\n ),\n};\n\n/** This function gives the path of a part of an image, which is `<package>/<path>` in the context. */\nconst pathOf = (part: string): string => {\n const [root, ...rest] = part.split(\"/\");\n const base = ROOTS[root!];\n if (base === undefined) throw new Error(`${part} is in no package that the images read`);\n return join(base, ...rest);\n};\n\n/** This function loads Testcontainers when a lab first opens. Code that only measures and judges does not load it. */\nconst containers = (): Promise<typeof Testcontainers> => import(\"testcontainers\");\n\n/** An image is an image that the harness builds: its Dockerfile, and each file or folder that the Dockerfile reads. */\ninterface Image {\n readonly name: string;\n readonly dockerfile: string;\n readonly from: readonly string[];\n}\n\n/** This image holds the prober, the TCP client, the injector and the namespace admin. */\nconst HARNESS: Image = {\n name: \"loom-harness\",\n dockerfile: \"harness/image/Dockerfile\",\n from: [\n \"harness/image/Dockerfile\",\n \"harness/image/native\",\n \"prober/bpf\",\n \"prober/native\",\n \"prober/fixtures\",\n ],\n};\n/** This image holds the Node client: ioredis 6.0.0 on Node 24.21.0. */\nconst NODE: Image = {\n name: \"loom-harness-node\",\n dockerfile: \"harness/image/node/Dockerfile\",\n from: [\"harness/image/node\"],\n};\n\n/** This function gives each file under a path, in a stable sequence, but not the output of a local build. */\nconst sources = (path: string): string[] => {\n if (statSync(path).isFile()) return [path];\n const entries = readdirSync(path, { withFileTypes: true });\n return entries\n .filter((entry) => entry.name !== \"target\" && entry.name !== \"node_modules\")\n .flatMap((entry) => sources(join(path, entry.name)))\n .sort();\n};\n\n/** This function gives each file of an image, with its path in the build context. */\nconst filesOf = (image: Image): Array<{ readonly file: string; readonly at: string }> =>\n image.from.flatMap((part) => {\n const base = pathOf(part);\n return sources(base).map((file) => ({\n file,\n at: join(part, relative(base, file)).replaceAll(\"\\\\\", \"/\"),\n }));\n });\n\n/** This function gives the tag of an image from its content. Thus a changed probe or client cannot operate as an old one. */\nconst imageTag = (image: Image): string => {\n const hash = createHash(\"sha256\");\n for (const { file, at } of filesOf(image)) {\n hash.update(at);\n hash.update(readFileSync(file));\n }\n return `${image.name}:${hash.digest(\"hex\").slice(0, 12)}`;\n};\n\n/** This function copies the files of an image into a new build context, and gives its path. */\nconst stage = (image: Image): string => {\n const context = mkdtempSync(join(tmpdir(), `${image.name}-`));\n for (const { file, at } of filesOf(image)) {\n mkdirSync(dirname(join(context, at)), { recursive: true });\n copyFileSync(file, join(context, at));\n }\n return context;\n};\n\nconst built = new Map<string, Promise<string>>();\n/** This function builds an image one time for each process. Docker keeps its layers between runs. */\nconst build = (image: Image): Promise<string> => {\n let tag = built.get(image.name);\n if (tag === undefined) {\n tag = (async () => {\n const name = imageTag(image);\n const { GenericContainer } = await containers();\n const context = stage(image);\n try {\n await GenericContainer.fromDockerfile(context, image.dockerfile).build(name, {\n deleteOnExit: false,\n });\n } finally {\n rmSync(context, { recursive: true, force: true });\n }\n return name;\n })();\n built.set(image.name, tag);\n }\n return tag;\n};\n\n/** This function gives the image of the harness. */\nexport const harnessImage = (): Promise<string> => build(HARNESS);\n/** This function gives the image of the Node client. */\nexport const nodeClientImage = (): Promise<string> => build(NODE);\n\n/**\n * A network fault is a fault that the injector applies as a filter rule. It is a black hole (both\n * directions, or one), or a reset on the next segment. It can also be an ICMP unreachable for each\n * segment, or a drop of each segment that is longer than a length.\n */\nexport type NetworkFault = \"blackhole\" | \"rst\" | \"reject\" | \"mtu\";\n\n/** These are the options of a network fault: the direction of a black hole, the code of a reject, the length of an MTU drop. */\nexport interface FaultOptions {\n /** This direction is relative to the namespace of the rule: `in` drops what comes from the peer, `out` what goes to it. */\n readonly direction?: \"in\" | \"out\";\n readonly code?: \"host\" | \"net\" | \"port\" | \"admin\";\n /** This length is the longest segment in bytes that an MTU drop lets through. */\n readonly above?: number;\n}\n\n/** This function gives the arguments of the injector for the options of a fault. */\nconst optionArgs = (options: FaultOptions): string[] => [\n ...(options.direction === undefined ? [] : [\"--direction\", options.direction]),\n ...(options.code === undefined ? [] : [\"--code\", options.code]),\n ...(options.above === undefined ? [] : [\"--above\", String(options.above)]),\n];\n\n/** These are the actions of the injector that lift each network fault. */\nconst LIFT: Readonly<Record<NetworkFault, string>> = {\n blackhole: \"clear\",\n rst: \"clear-rst\",\n reject: \"clear-reject\",\n mtu: \"clear-mtu\",\n};\n\n/**\n * A fault side is the namespace that gets the rules of a fault: the namespace of the peer of the\n * observer. For the client, it is the namespace of the target (the injector). For the server, it\n * is the namespace of the client (its privileged admin). Rules in the namespace of the observer\n * make its sends fail locally, with EPERM, and the network does not lose them. Thus its\n * retransmissions do not go out. The retransmission tracepoint of the kernel then records none,\n * because it fires only for a retransmission that went out.\n */\nexport type FaultSide = \"target\" | \"client\";\n\n/** A manifest is what a lab records about its machine, its images and its settings. */\nexport interface Manifest {\n readonly kernel: string;\n /** This text is the architecture of the machine, as uname -m gives it: x86_64. */\n readonly architecture: string;\n readonly hz: string | null;\n readonly boot_id: string;\n readonly docker: string;\n /** This record is the target, and its settings as it read them back. */\n readonly target: {\n readonly image: string;\n readonly id: string;\n readonly settings: Readonly<Record<string, string>>;\n /** These are the sysctls of its namespace, as read back after the harness set them. */\n readonly sysctls: Readonly<Record<string, string>>;\n };\n readonly harness: { readonly image: string; readonly id: string; readonly packages: string[] };\n /** This record is the client of each run: the TCP client of the harness, or a driver from its own image. */\n readonly client: { readonly kind: Client; readonly image: string };\n /** These are the namespaces that the prober watched, by inode. */\n readonly netns: { readonly client: number; readonly target: number };\n /** These are the sysctls of the client namespace, as read back after the harness set them. */\n readonly sysctls: Readonly<Record<string, string>>;\n}\n\n/** These are the JSON lines of a program, as they arrive. */\ninterface Lines<T extends { readonly ev: string }> {\n readonly seen: T[];\n /** These are the other lines that it wrote, for example an error. They explain a program that stopped its output. */\n readonly other: string[];\n consume: (stream: NodeJS.ReadableStream) => void;\n /** This method gives the first line of one of these kinds, or it rejects after `within` ms. */\n until(kinds: readonly string[], within: number, who: string): Promise<T>;\n}\n\nconst lines = <T extends { readonly ev: string }>(): Lines<T> => {\n const seen: T[] = [];\n const other: string[] = [];\n const waiting: Array<{ kinds: readonly string[]; resolve: (line: T) => void }> = [];\n let partial = \"\";\n const take = (text: string) => {\n if (!text.startsWith(\"{\")) {\n if (text.length > 0) other.push(text);\n return;\n }\n const line = JSON.parse(text) as T;\n seen.push(line);\n for (const wait of waiting.splice(0)) {\n if (wait.kinds.includes(line.ev)) wait.resolve(line);\n else waiting.push(wait);\n }\n };\n return {\n seen,\n other,\n consume: (stream) =>\n stream.on(\"data\", (chunk: Buffer | string) => {\n partial += chunk.toString();\n const split = partial.split(\"\\n\");\n partial = split.pop() ?? \"\";\n for (const text of split) take(text.trim());\n }),\n until(kinds, within, who) {\n const found = seen.find((line) => kinds.includes(line.ev));\n if (found !== undefined) return Promise.resolve(found);\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n const said = other.length === 0 ? \"\" : `; it wrote: ${other.slice(-3).join(\" | \")}`;\n reject(new Error(`no ${kinds.join(\" or \")} from the ${who} in ${within} ms${said}`));\n }, within);\n waiting.push({\n kinds,\n resolve: (line) => {\n clearTimeout(timer);\n resolve(line);\n },\n });\n });\n },\n };\n};\n\n/** A probe is the client container of one run. */\nexport interface Probe {\n /** This method gives the first event of one of these kinds, or it rejects after `within` ms. */\n until(kinds: readonly string[], within: number): Promise<ProbeEvent>;\n /** This method lets an in-flight probe send its request. */\n go(): Promise<void>;\n /** This method freezes the client with the cgroup freezer: its process stops, and its kernel continues to answer. */\n freeze(): Promise<void>;\n thaw(): Promise<void>;\n /** This method stops the process of the client with SIGKILL. Its kernel then closes its socket with a FIN, or a reset. */\n kill(): Promise<void>;\n readonly events: readonly ProbeEvent[];\n stop(): Promise<void>;\n}\n\n/** A lab is one environment: the target, the client, the injector and the prober. */\nexport interface Lab {\n readonly targetIp: string;\n readonly clientIp: string;\n readonly netns: { readonly client: number; readonly target: number };\n /** These are all the records that the prober wrote, in sequence. */\n readonly kernel: readonly KernelRecord[];\n /** This method sets the sysctls of the client namespace through its privileged sidecar, and reads them back. */\n sysctl(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /** This method sets the settings of the target in it before a client connects, and reads them back. */\n configure(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /** This method sets the sysctls of the target namespace through a privileged sidecar there, and reads them back. */\n targetSysctl(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /**\n * This method applies a fault between the client and the target, from the namespace of the\n * target. The fault is a black hole, or a reset that answers the next segment of the client. The stamps of\n * the injector bracket it.\n */\n fault(\n kind: NetworkFault,\n at?: FaultSide,\n options?: FaultOptions,\n ): Promise<{ t0: number; t1: number }>;\n /** This method removes a fault. */\n lift(kind: NetworkFault, at?: FaultSide, options?: FaultOptions): Promise<void>;\n /**\n * This method puts tc netem on the egress of a namespace, for example \"delay 20ms\" or \"loss 5%\",\n * or removes it with null. The stamps of the injector bracket it.\n */\n netem(spec: string | null, at?: FaultSide): Promise<{ t0: number; t1: number }>;\n /**\n * This method sets the TSO, GSO and GRO of a namespace. With them off, netem and a length match\n * act on segments, not on GSO super-packets of up to 64 KB.\n */\n offload(on: boolean, at?: FaultSide): Promise<void>;\n /** This method stops the server process in the target with SIGKILL, with the command of the target. */\n kill(): Promise<void>;\n /**\n * This method resolves the neighbors of the path in the two directions, with a ping from each\n * namespace. Without it, the first connection of an environment waits for ARP. Under a netem\n * delay, that wait is one more delayed round, and its round trip and RTO are longer.\n */\n warm(): Promise<void>;\n /** This method runs commands in the target before a client connects, each as argv. */\n setup(commands: ReadonlyArray<readonly string[]>): Promise<void>;\n /**\n * This method stops a black hole at an instant on the monotonic clock of the VM. The injector\n * waits for that instant in the VM, thus the length of a stall does not include the latency of\n * a docker exec. Stamps bracket it, as for a fault.\n */\n heal(at: FaultSide, when: number): Promise<{ t0: number; t1: number }>;\n /**\n * This method removes what the two kernels learned from their earlier connections. These are\n * the TCP metrics that each kernel keeps for a peer address when a connection closes. These metrics set the\n * first round trip of the next connection, and thus its RTO. A run with these metrics is not\n * independent of the run before it.\n */\n forget(): Promise<void>;\n /** This method freezes the target with the cgroup freezer: its process stops, and its kernel continues to answer. */\n freeze(): Promise<void>;\n thaw(): Promise<void>;\n probe(args: readonly string[]): Promise<Probe>;\n /**\n * This method waits for the next clock line of the prober, which gives the count of lost\n * records. It gives null when no clock line comes in 5 s, because then nothing tells the count.\n */\n settle(): Promise<number | null>;\n manifest(\n sysctls: Readonly<Record<string, string>>,\n settings?: Readonly<Record<string, string>>,\n targetSysctls?: Readonly<Record<string, string>>,\n ): Promise<Manifest>;\n close(): Promise<void>;\n}\n\n// each container here is a holder, a probe or the prober: nothing drains, thus the stop is immediate\nconst STOP = { timeout: 0 };\n\nconst output = async (container: StartedTestContainer, argv: string[]): Promise<string> => {\n const result = await container.exec(argv);\n if (result.exitCode !== 0)\n throw new Error(`${argv.join(\" \")} exited ${result.exitCode}: ${result.output.trim()}`);\n return result.stdout.trim();\n};\n\n/** This function gives the inode of the network namespace of a container: net:[4026532583]. */\nconst netnsOf = async (container: StartedTestContainer): Promise<number> => {\n const link = await output(container, [\"readlink\", \"/proc/self/ns/net\"]);\n const inode = /^net:\\[(\\d+)\\]$/.exec(link)?.[1];\n if (inode === undefined) throw new Error(`not a network namespace: ${link}`);\n return Number(inode);\n};\n\n/**\n * This function opens one environment:\n * - a new network and the stock target.\n * - a client that holds its own network namespace, with a privileged sidecar in it for its\n * sysctls. Docker Desktop keeps /proc/sys read-only in a container that operates.\n * - an injector sidecar in the namespace of the target.\n * - the prober, a privileged container that loads the kernel program and watches the two\n * namespaces.\n *\n * The client of each run is its own container in the namespace of the client. It is the TCP\n * client of the harness, or a driver from its own image (ioredis on Node). The two print the same\n * lines.\n */\nexport const openLab = async (target: Target, kind: Client = \"tcp\"): Promise<Lab> => {\n const image = await harnessImage();\n const clientImage = kind === \"ioredis\" ? await nodeClientImage() : image;\n const command =\n kind === \"ioredis\" ? [\"node\", \"/opt/harness/node/client-ioredis.mjs\"] : [\"/opt/harness/client\"];\n const { GenericContainer, Network, Wait, getContainerRuntimeClient } = await containers();\n const opened: StartedTestContainer[] = [];\n let network: StartedNetwork | undefined;\n const close = async () => {\n for (const container of opened.reverse()) await container.stop(STOP).catch(() => undefined);\n await network?.stop().catch(() => undefined);\n };\n try {\n network = await new Network().start();\n const stock = new GenericContainer(target.image)\n .withNetwork(network)\n .withWaitStrategy(Wait.forLogMessage(target.ready));\n // a holder command keeps the container up when a fin fault kills the server process\n const server = await (\n target.command === undefined ? stock : stock.withCommand([...target.command])\n ).start();\n opened.push(server);\n const client = await new GenericContainer(image).withNetwork(network).start();\n opened.push(client);\n const admin = await new GenericContainer(image)\n .withNetworkMode(`container:${client.getId()}`)\n .withPrivilegedMode()\n .start();\n opened.push(admin);\n const injector = await new GenericContainer(image)\n .withNetworkMode(`container:${server.getId()}`)\n .withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\")\n .start();\n opened.push(injector);\n const netns = { client: await netnsOf(admin), target: await netnsOf(injector) };\n const records = lines<KernelRecord>();\n const prober = await new GenericContainer(image)\n .withPrivilegedMode()\n .withNetworkMode(\"none\")\n .withCommand([\"/opt/harness/prober\", String(netns.client), String(netns.target)])\n .withLogConsumer(records.consume)\n .start();\n opened.push(prober);\n const begun = await records.until([\"begin\", \"fail\"], 30_000, \"prober\");\n if (begun.ev === \"fail\") throw new Error(`the prober failed: ${String(begun[\"why\"])}`);\n const targetIp = server.getIpAddress(network.getName());\n const clientIp = client.getIpAddress(network.getName());\n const runtime = await getContainerRuntimeClient();\n const paused = runtime.container.getById(server.getId());\n let targetAdmin: StartedTestContainer | undefined;\n\n return {\n targetIp,\n clientIp,\n netns,\n kernel: records.seen,\n async sysctl(values) {\n const settings = Object.entries(values).map(([key, value]) => `${key}=${value}`);\n if (settings.length > 0) await output(admin, [\"sysctl\", \"-q\", \"-w\", ...settings]);\n const read: Record<string, string> = {};\n for (const key of Object.keys(values))\n read[key] = await output(admin, [\"sysctl\", \"-n\", key]);\n return read;\n },\n async targetSysctl(values) {\n const read: Record<string, string> = {};\n if (Object.keys(values).length === 0) return read;\n // privileged, and only when a cell asks for it: otherwise the namespace of the target stays as it is\n targetAdmin ??= await new GenericContainer(image)\n .withNetworkMode(`container:${server.getId()}`)\n .withPrivilegedMode()\n .start();\n opened.push(targetAdmin);\n const settings = Object.entries(values).map(([key, value]) => `${key}=${value}`);\n await output(targetAdmin, [\"sysctl\", \"-q\", \"-w\", ...settings]);\n for (const key of Object.keys(values))\n read[key] = await output(targetAdmin, [\"sysctl\", \"-n\", key]);\n return read;\n },\n async configure(values) {\n const read: Record<string, string> = {};\n if (Object.keys(values).length === 0) return read;\n const how = target.settings;\n if (how === undefined) throw new Error(`${target.image} names no way to set its settings`);\n for (const [name, value] of Object.entries(values)) {\n await output(server, [...how.set, name, value]);\n read[name] = (await output(server, [...how.get, name])).split(\"\\n\").at(-1) ?? \"\";\n }\n return read;\n },\n async fault(kind, at = \"target\", options = {}) {\n const [where, peer] = at === \"client\" ? [admin, targetIp] : [injector, clientIp];\n const args = [\"/opt/harness/inject\", kind, peer, ...optionArgs(options)];\n const stamp = JSON.parse(await output(where, args)) as { t0: number; t1: number };\n return { t0: stamp.t0, t1: stamp.t1 };\n },\n async lift(kind, at = \"target\", options = {}) {\n const [where, peer] = at === \"client\" ? [admin, targetIp] : [injector, clientIp];\n await output(where, [\"/opt/harness/inject\", LIFT[kind], peer, ...optionArgs(options)]);\n },\n async netem(spec, at = \"target\") {\n const where = at === \"client\" ? admin : injector;\n const args =\n spec === null\n ? [\"/opt/harness/inject\", \"clear-netem\", \"eth0\"]\n : [\"/opt/harness/inject\", \"netem\", \"eth0\", ...spec.split(/\\s+/)];\n const stamp = JSON.parse(await output(where, args)) as { t0: number; t1: number };\n return { t0: stamp.t0, t1: stamp.t1 };\n },\n async offload(on, at = \"target\") {\n const where = at === \"client\" ? admin : injector;\n await output(where, [\"/opt/harness/inject\", \"offload\", \"eth0\", on ? \"on\" : \"off\"]);\n },\n async kill() {\n if (target.kill === undefined) throw new Error(`${target.image} names no way to kill its server`);\n await output(server, [...target.kill]);\n },\n async warm() {\n await output(admin, [\"ping\", \"-c\", \"2\", \"-W\", \"2\", targetIp]);\n await output(injector, [\"ping\", \"-c\", \"2\", \"-W\", \"2\", clientIp]);\n },\n async setup(commands) {\n for (const argv of commands) await output(server, [...argv]);\n },\n async heal(at, when) {\n const [where, peer] = at === \"client\" ? [admin, targetIp] : [injector, clientIp];\n const args = [\"/opt/harness/inject\", \"clear\", peer, \"--at\", String(Math.round(when))];\n const stamp = JSON.parse(await output(where, args)) as { t0: number; t1: number };\n return { t0: stamp.t0, t1: stamp.t1 };\n },\n async forget() {\n await output(admin, [\"ip\", \"tcp_metrics\", \"flush\", \"all\"]);\n await output(injector, [\"ip\", \"tcp_metrics\", \"flush\", \"all\"]);\n },\n freeze: () => paused.pause().then(() => undefined),\n thaw: () => paused.unpause().then(() => undefined),\n async probe(args) {\n const events = lines<ProbeEvent>();\n const container = await new GenericContainer(clientImage)\n .withNetworkMode(`container:${client.getId()}`)\n .withCommand([...command, ...args])\n .withLogConsumer(events.consume)\n .start();\n return {\n events: events.seen,\n until: (kinds, within) => events.until(kinds, within, \"client\"),\n go: () => output(container, [\"touch\", \"/tmp/go\"]).then(() => undefined),\n freeze: () =>\n runtime.container\n .getById(container.getId())\n .pause()\n .then(() => undefined),\n thaw: () =>\n runtime.container\n .getById(container.getId())\n .unpause()\n .then(() => undefined),\n kill: () =>\n runtime.container\n .getById(container.getId())\n .kill({ signal: \"SIGKILL\" })\n .then(() => undefined),\n // a killed client has no container to stop\n stop: () =>\n container\n .stop(STOP)\n .then(() => undefined)\n .catch(() => undefined),\n };\n },\n async settle() {\n // the prober drains its ring each 20 ms, and it writes a clock line each second\n const after = records.seen.length;\n const clock = await new Promise<KernelRecord | null>((resolve) => {\n const started = Date.now();\n const look = () => {\n const line = records.seen.slice(after).find((record) => record.ev === \"clock\");\n if (line !== undefined) resolve(line);\n else if (Date.now() - started > 5_000) resolve(null);\n else setTimeout(look, 50);\n };\n look();\n });\n return clock === null ? null : Number(clock[\"lost\"] ?? 0);\n },\n async manifest(sysctls, settings = {}, targetSysctls = {}) {\n const config = await output(admin, [\"zcat\", \"/proc/config.gz\"]).catch(() => \"\");\n const inspect = async (container: StartedTestContainer) =>\n runtime.container.inspect(runtime.container.getById(container.getId()));\n return {\n kernel: await output(client, [\"uname\", \"-r\"]),\n architecture: await output(client, [\"uname\", \"-m\"]),\n hz: /^CONFIG_HZ=(\\d+)$/m.exec(config)?.[1] ?? null,\n boot_id: await output(client, [\"cat\", \"/proc/sys/kernel/random/boot_id\"]),\n docker: `${runtime.info.containerRuntime.serverVersion} on ${runtime.info.containerRuntime.operatingSystem}`,\n target: {\n image: target.image,\n id: (await inspect(server)).Image,\n settings,\n sysctls: targetSysctls,\n },\n harness: {\n image,\n id: (await inspect(client)).Image,\n packages: (await output(client, [\"apk\", \"info\", \"-v\"])).split(\"\\n\").sort(),\n },\n client: { kind, image: clientImage },\n netns,\n sysctls,\n };\n },\n close,\n };\n } catch (error) {\n await close();\n throw error;\n }\n};\n","import { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cell, KernelRecord, ProbeEvent, Run } from \"./cell.ts\";\nimport type { FaultOptions, Lab, Manifest, Probe } from \"./lab.ts\";\nimport { openLab } from \"./lab.ts\";\nimport type { Measured } from \"./observe.ts\";\nimport { measure } from \"./observe.ts\";\nimport type { Verdict } from \"./verdict.ts\";\nimport { STEPS, verdictOf } from \"./verdict.ts\";\n\n/** A cell result is a cell with its manifests, its runs, their measurements and its verdict. */\nexport interface CellResult {\n readonly cell: Cell;\n readonly manifests: readonly Manifest[];\n readonly runs: readonly Run[];\n readonly measured: readonly Measured[];\n readonly verdict: Verdict;\n /** These times are when its first environment opened and its last environment closed, on the wall clock. */\n readonly at: { readonly started: string; readonly ended: string };\n}\n\n/**\n * This function measures and judges the raw records of a cell, with the code of the harness at\n * this time. `runCell` uses it during a cell. A holder of the raw records of a cell can use it\n * again later. Thus the records get a new analysis after a change to observe.ts or to the verdict.\n */\nexport const judge = (\n cell: Cell,\n runs: readonly Run[],\n): { measured: Measured[]; verdict: Verdict } => {\n const measured = runs.map((run) => measure(cell, run));\n return { measured, verdict: verdictOf(cell.prediction, measured) };\n};\n\n/** This function gives the count of lost records in the latest clock line of the prober, since it began. */\nconst droppedSoFar = (records: readonly KernelRecord[]): number => {\n for (let at = records.length - 1; at >= 0; at -= 1)\n if (records[at]!.ev === \"clock\") return Number(records[at]![\"lost\"] ?? 0);\n return 0;\n};\n\n/** This function gives the argv of the client for a cell, with a deadline far after all that it waits for. */\nconst probeArgs = (cell: Cell, lab: Lab): string[] => {\n const { prediction, probe } = cell;\n const longest = prediction.detects\n ? valueIn(prediction.high, \"s\")\n : valueIn(prediction.window, \"s\");\n const args = [lab.targetIp, String(cell.target.port), \"--mode\", probe.mode];\n if (probe.request !== undefined) args.push(\"--request\", probe.request);\n if (probe.count !== undefined) args.push(\"--count\", String(probe.count));\n if (probe.interval !== undefined) args.push(\"--interval-ms\", String(valueIn(probe.interval, \"ms\")));\n if (probe.driver !== undefined) {\n // a driver sets its socket options from its own options: the keepAlive of ioredis\n args.push(\"--keepalive-ms\", String(valueIn(probe.driver.keepAlive, \"ms\")));\n args.push(\"--max-s\", String(Math.ceil(longest * 2 + 10)));\n return args;\n }\n if (probe.keepalive) args.push(\"--keepalive\");\n if (probe.userTimeout !== undefined)\n args.push(\"--user-timeout-ms\", String(valueIn(probe.userTimeout, \"ms\")));\n args.push(\"--max-s\", String(Math.ceil(longest * 2 + 10)));\n return args;\n};\n\n/**\n * This function waits for an error on the socket of the target for the connection of the\n * client, or for its end of file (CLOSE_WAIT, state 8). The prober records both in the namespace\n * of the target. A cell on the server side waits for that. The promise resolves after `within` ms\n * at the latest.\n */\nconst serverError = (lab: Lab, local: unknown, within: number): Promise<void> =>\n new Promise((resolve) => {\n const started = Date.now();\n const look = () => {\n const seen = lab.kernel.some(\n (record) =>\n record.netns === lab.netns.target &&\n record.dst === local &&\n ((record.ev === \"error\" && (record.err ?? 0) !== 0) ||\n (record.ev === \"state\" && (record as { readonly new?: number }).new === 8)),\n );\n if (seen || Date.now() - started > within) resolve();\n else setTimeout(look, 50);\n };\n look();\n });\n\n/**\n * This function waits for the request that an in-flight client sends on the signal: its second\n * send, after the PING that made it ready. Its stamp is when the request first met the fault.\n */\nconst requestSent = (probe: Probe, within: number): Promise<ProbeEvent> =>\n new Promise((resolve, reject) => {\n const started = Date.now();\n const look = () => {\n const sent = probe.events.filter((event) => event.ev === \"sent\")[1];\n if (sent !== undefined) resolve(sent);\n else if (Date.now() - started > within)\n reject(new Error(`the client sent no request within ${within} ms of being told to`));\n else setTimeout(look, 1);\n };\n look();\n });\n\n/**\n * This function does one run. The client connects and gets its reply, the fault occurs, and an\n * in-flight client sends into it. The harness then waits for the observer to see the fault, or for\n * the silent window to end. The observer is the client, or the socket of the target for a cell on\n * the server side.\n *\n * A stall stops by itself, at its length after the send of the request, and the\n * run waits for the reply. Then the harness removes the fault, thus the environment can serve the\n * next run. The run keeps the records of the prober from its start to its end. The frozen process\n * is the peer of the observer: the target, or the client for the server.\n */\n/** This function gives the netem specification of a cell: its latency, then the netem of its fault when it has one. */\nconst netemSpec = (cell: Cell, fault: string | undefined): string | null => {\n const parts = [\n ...(cell.latency === undefined ? [] : [`delay ${valueIn(cell.latency, \"ms\")}ms`]),\n ...(fault === undefined ? [] : [fault]),\n ];\n return parts.length === 0 ? null : parts.join(\" \");\n};\n\nconst once = async (lab: Lab, cell: Cell, environment: number, index: number): Promise<Run> => {\n // each run starts from kernels that keep nothing from the runs before it\n await lab.forget();\n const from = lab.kernel.length;\n const lostBefore = droppedSoFar(lab.kernel);\n const probe = await lab.probe(probeArgs(cell, lab));\n const server = cell.observer === \"server\";\n const peer = server\n ? { freeze: () => probe.freeze(), thaw: () => probe.thaw() }\n : { freeze: () => lab.freeze(), thaw: () => lab.thaw() };\n // a client that stopped before the signal to send tells why in its last lines\n const go = () =>\n probe.go().catch((error: unknown) => {\n const last = probe.events.slice(-3).map((event) => JSON.stringify(event));\n throw new Error(`${(error as Error).message}; the client's last lines: ${last.join(\" \")}`);\n });\n let fault: Run[\"fault\"];\n let heal: { t0: number; t1: number } | null = null;\n try {\n await probe.until([\"ready\"], 15_000);\n // the in-flight data of a run on the server side is the reply of the server. The request goes\n // out first, and the fault occurs while the server holds it (the timeout of BLPOP). A fin with\n // a request in flight kills the server while it holds the request, so it goes first too\n const replyInFlight =\n cell.probe.mode === \"inflight\" &&\n ((server && cell.fault !== \"mtu-blackhole\") || cell.fault === \"fin\");\n if (replyInFlight) {\n // the client sends one sample after the file appears. The request reaches the server well\n // in the pause, and the reply waits for the timeout of the request\n await go();\n await new Promise((resolve) => setTimeout(resolve, 200));\n }\n const faultAt = server ? \"client\" : \"target\";\n // the direction of a simplex drop, relative to the namespace of the rule: in the target, the\n // segments to the server come in; in the client, they go out\n const direction: FaultOptions[\"direction\"] =\n cell.drops === undefined\n ? undefined\n : (cell.drops === \"to-server\") === (faultAt === \"target\")\n ? \"in\"\n : \"out\";\n const options: FaultOptions = {\n ...(direction === undefined ? {} : { direction }),\n ...(cell.icmp === undefined ? {} : { code: cell.icmp }),\n ...(cell.mtu === undefined ? {} : { above: cell.mtu }),\n };\n // an MTU drop goes where the long reply arrives: the namespace of the client\n const apply = async (): Promise<Run[\"fault\"]> => {\n switch (cell.fault) {\n case \"freeze-process\":\n await peer.freeze();\n return null;\n case \"fin\":\n await (server ? probe.kill() : lab.kill());\n return null;\n case \"unreachable\":\n return lab.fault(\"reject\", faultAt, { code: cell.icmp ?? \"host\" });\n case \"simplex\":\n return lab.fault(\"blackhole\", faultAt, options);\n case \"mtu-blackhole\":\n return lab.fault(\"mtu\", \"client\", { above: cell.mtu ?? 1200 });\n case \"netem\":\n return lab.netem(netemSpec(cell, cell.netem), \"target\");\n case \"rst\":\n return lab.fault(\"rst\", faultAt);\n default:\n // a stall is a black hole that stops by itself\n return lab.fault(\"blackhole\", faultAt);\n }\n };\n const lift = async (): Promise<void> => {\n switch (cell.fault) {\n case \"freeze-process\":\n return peer.thaw();\n case \"fin\":\n return undefined;\n case \"unreachable\":\n return lab.lift(\"reject\", faultAt, { code: cell.icmp ?? \"host\" });\n case \"simplex\":\n return lab.lift(\"blackhole\", faultAt, options);\n case \"mtu-blackhole\":\n return lab.lift(\"mtu\", \"client\", { above: cell.mtu ?? 1200 });\n case \"netem\":\n await lab.netem(netemSpec(cell, undefined), \"target\");\n return undefined;\n case \"rst\":\n return lab.lift(\"rst\", faultAt);\n default:\n return heal === null ? lab.lift(\"blackhole\", faultAt) : undefined;\n }\n };\n fault = await apply();\n try {\n if (cell.probe.mode !== \"idle\" && !replyInFlight) await go();\n const { prediction } = cell;\n if (cell.fault === \"stall\") {\n if (cell.stall === undefined) throw new Error(`${cell.name} says no stall's length`);\n // the injector holds the end in the VM until the length of the stall after the send\n const sent = await requestSent(probe, 5_000);\n heal = await lab.heal(faultAt, sent.t + valueIn(cell.stall, \"s\") * 1e9);\n const window = valueIn(prediction.detects ? prediction.high : prediction.window, \"s\");\n await probe.until([\"data\", \"error\", \"eof\"], window * 1_000).catch(() => undefined);\n } else if (prediction.detects === null) {\n // a distribution: the stream ends by itself, or the connection does\n const window = valueIn(prediction.window, \"s\") * 1_000;\n await probe.until([\"done\", \"error\", \"eof\", \"deadline\"], window).catch(() => undefined);\n } else if (prediction.detects) {\n const within = valueIn(prediction.high, \"s\") * 2_000 + 10_000;\n if (server) {\n const local = probe.events.find((event) => event.ev === \"connected\")?.[\"local\"];\n await serverError(lab, local, within);\n } else {\n await probe.until([\"error\", \"eof\", \"deadline\"], within).catch(() => undefined);\n }\n } else {\n await new Promise((resolve) =>\n setTimeout(resolve, valueIn(prediction.window, \"s\") * 1_000),\n );\n }\n } finally {\n await lift();\n }\n // a thawed target answers the request that it held: the client sees the late reply\n if (cell.fault === \"freeze-process\" && !server)\n await probe.until([\"data\"], 5_000).catch(() => undefined);\n } finally {\n await probe.stop();\n }\n // the prober drains its ring each 20 ms. Its next clock line closes the records of the run\n const settled = await lab.settle();\n const lost = settled === null ? null : settled - lostBefore;\n return {\n environment,\n index,\n events: [...probe.events],\n fault,\n ...(cell.fault === \"stall\" ? { heal } : {}),\n kernel: lab.kernel.slice(from),\n netns: lab.netns.client,\n targetNetns: lab.netns.target,\n lost,\n };\n};\n\n/** This function divides a count of runs across environments, as equally as possible. */\nexport const spread = (runs: number, environments: number): number[] =>\n Array.from(\n { length: environments },\n (_, at) => Math.floor(runs / environments) + (at < runs % environments ? 1 : 0),\n ).filter((count) => count > 0);\n\n/** This function does the part of the runs of a cell in one environment, and records its manifest. */\nconst inEnvironment = async (\n cell: Cell,\n environment: number,\n count: number,\n say: (line: string) => void,\n): Promise<{ manifest: Manifest; runs: Run[] }> => {\n const lab = await openLab(cell.target, cell.probe.driver?.name ?? \"tcp\");\n try {\n // the settings of the target first: Redis applies tcp-keepalive to a client when it accepts it\n const settings = await lab.configure(cell.targetSettings ?? {});\n const targetSysctls = await lab.targetSysctl(cell.targetSysctls ?? {});\n if (cell.targetSetup !== undefined) await lab.setup(cell.targetSetup);\n // netem and a length match act on segments only when TSO, GSO and GRO are off on the two ends\n if (cell.fault === \"mtu-blackhole\" || cell.fault === \"netem\") {\n await lab.offload(false, \"target\");\n await lab.offload(false, \"client\");\n }\n // a latency is on the path before the client connects, thus its round trip and RTO include it\n const latency = netemSpec(cell, undefined);\n if (latency !== null) await lab.netem(latency, \"target\");\n // the first connection must not wait for ARP: under a delay, that is a longer first round trip\n await lab.warm();\n const manifest = await lab.manifest(await lab.sysctl(cell.sysctls), settings, targetSysctls);\n const runs: Run[] = [];\n for (let index = 0; index < count; index += 1) {\n const run = await once(lab, cell, environment, index);\n runs.push(run);\n const read = measure(cell, run);\n say(\n `${cell.name}: env ${environment} run ${index} ${read.outcome.kind}` +\n `${read.elapsed === null ? \"\" : ` ${read.elapsed.value} s`}` +\n `${read.void === null ? \"\" : ` (void: ${read.void})`}`,\n );\n }\n return { manifest, runs };\n } finally {\n await lab.close();\n }\n};\n\n/**\n * This function does the runs of a cell. They go across new environments, and each environment records\n * its manifest. Then the verdict judges the runs. A median across the edge of the band extends\n * the cell from 11 runs to 22, and then to 44, each extension in new environments. With a\n * concurrency of more than one, that many environments operate at the same time, each with its\n * own prober. The runs then go across at least as many environments.\n */\nexport const runCell = async (\n cell: Cell,\n say: (line: string) => void = () => {},\n): Promise<CellResult> => {\n const started = new Date().toISOString();\n const manifests: Manifest[] = [];\n const runs: Run[] = [];\n const concurrency = Math.max(1, cell.concurrency ?? 1);\n let target: number = cell.runs;\n let next = 0;\n for (;;) {\n const counts = spread(target - runs.length, Math.max(cell.environments, concurrency));\n for (let at = 0; at < counts.length; at += concurrency) {\n const batch = counts.slice(at, at + concurrency).map((count) => ({ count, env: next++ }));\n const done = await Promise.all(\n batch.map(({ count, env }) => inEnvironment(cell, env, count, say)),\n );\n for (const each of done) {\n manifests.push(each.manifest);\n runs.push(...each.runs);\n }\n }\n const { measured, verdict } = judge(cell, runs);\n const larger = STEPS.find((step) => step > runs.length);\n if (verdict.decision !== \"extend\" || larger === undefined) {\n const at = { started, ended: new Date().toISOString() };\n return { cell, manifests, runs, measured, verdict, at };\n }\n target = larger;\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/verdict.ts","../src/observe.ts","../src/lab.ts","../src/run.ts"],"sourcesContent":["import type { Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\nimport type { Prediction } from \"./cell.ts\";\nimport type { Measured, Reordering } from \"./observe.ts\";\n\n/**\n * This amount is the noise of the pipeline itself, δ_abs, as the calibration measured it. It is\n * the 99.9th percentile of the whole latency of a reset, at the concurrency cap or less. Its\n * minimum is one jiffy. On 2026-10-12 the worst of 60 resets at 1 and 4 environments took\n * 1.141 ms, thus the minimum sets it to 4 ms. The calibration occurs again after each change to\n * the harness, the kernel or Docker.\n */\nexport const NOISE: Quantity = quantity(4, \"ms\");\n/** This fraction, δ_rel, is how far the median of a cell can be outside the prediction and stay equivalent. */\nexport const RELATIVE = 0.05;\n/** These are the run counts of a cell: 11, then 22 and 44 while its median crosses the edge of the band. */\nexport const STEPS = [11, 22, 44] as const;\n\n/**\n * A decision is the result of a verdict. A cell whose outcome is a distribution has no band, and\n * its decision is `measured`: a record, not a test.\n */\nexport type Decision = \"holds\" | \"fails\" | \"extend\" | \"inconclusive\" | \"measured\";\n\n/** An interval is a range of times. */\nexport interface Interval {\n readonly low: Quantity;\n readonly high: Quantity;\n}\n\n/** A spread is the distribution of a set of times: its count, its median and its upper quantiles by nearest rank. */\nexport interface Spread {\n readonly count: number;\n readonly median: Quantity;\n readonly p99: Quantity;\n readonly p999: Quantity;\n readonly max: Quantity;\n}\n\n/** A verdict is the judgement of a cell on its runs. */\nexport interface Verdict {\n readonly decision: Decision;\n readonly counted: number;\n readonly voided: number;\n /** This band is where each run must land: [T_lo − δ_abs, T_hi + δ_abs]. It is null for a silent prediction. */\n readonly band: Interval | null;\n /** These are the runs outside the band, and the reason for each. Each is a finding. */\n readonly outside: ReadonlyArray<{ readonly run: string; readonly why: string }>;\n readonly median: Quantity | null;\n /** This interval is the order-statistic interval of the median at 95 %, or the whole range for fewer than six runs. */\n readonly medianInterval: Interval | null;\n /** This record is the sum of what reordering did in each counted run of a measurement-only cell. */\n readonly reordering?: Reordering;\n /** This record is the time of the proxy of a calibration cell, in each direction. */\n readonly proxy?: { readonly hop: string; readonly up: Spread | null; readonly down: Spread | null };\n readonly why: string;\n}\n\nconst s = (value: number): Quantity => quantity(Math.round(value * 1e6) / 1e6, \"s\");\nconst name = (run: Measured): string => `env ${run.environment} run ${run.index}`;\n\n/** This function gives the value at quantile p of sorted values, by nearest rank: x_(⌈p·n⌉). */\nexport const quantileOf = (sorted: readonly number[], p: number): number =>\n sorted[Math.min(sorted.length, Math.max(1, Math.ceil(p * sorted.length))) - 1]!;\n\n/** This function gives the spread of a set of times, or null for none. */\nexport const spreadOf = (times: readonly Quantity[]): Spread | null => {\n if (times.length === 0) return null;\n const sorted = times.map((time) => valueIn(time, \"s\")).sort((a, b) => a - b);\n return {\n count: sorted.length,\n median: s(middle(sorted)),\n p99: s(quantileOf(sorted, 0.99)),\n p999: s(quantileOf(sorted, 0.999)),\n max: s(sorted.at(-1)!),\n };\n};\n\n/** This function adds the reordering records of runs: the counts add, and each largest value is the largest of all. */\nconst reorderingTotal = (runs: readonly Measured[]): Reordering | undefined => {\n const each = runs.flatMap((run) => (run.reordering === undefined ? [] : [run.reordering]));\n if (each.length === 0) return undefined;\n const sum = (key: \"outOfOrder\" | \"duplicates\" | \"fast\") => each.reduce((total, one) => total + one[key], 0);\n const add = (key: \"ooo\" | \"dsacks\" | \"seen\") =>\n each.some((one) => one[key] !== null) ? each.reduce((total, one) => total + (one[key] ?? 0), 0) : null;\n const top = (key: \"sacked\" | \"lost\" | \"degree\") =>\n each.some((one) => one[key] !== null) ? Math.max(...each.map((one) => one[key] ?? 0)) : null;\n return {\n outOfOrder: sum(\"outOfOrder\"),\n duplicates: sum(\"duplicates\"),\n ooo: add(\"ooo\"),\n sacked: top(\"sacked\"),\n lost: top(\"lost\"),\n dsacks: add(\"dsacks\"),\n fast: sum(\"fast\"),\n degree: top(\"degree\"),\n seen: add(\"seen\"),\n };\n};\n\n/** This function says a reordering record in words. */\nconst reorderingWords = (total: Reordering): string =>\n [\n `${total.outOfOrder} arrivals out of order and ${total.duplicates} duplicate arrivals at the client`,\n ...(total.ooo === null ? [] : [`its kernel counted ${total.ooo} segments out of order`]),\n ...(total.sacked === null ? [] : [`the server held up to ${total.sacked} segments SACKed`]),\n ...(total.dsacks === null ? [] : [`got ${total.dsacks} DSACKs`]),\n `sent ${total.fast} fast retransmissions`,\n ...(total.seen === null ? [] : [`saw ${total.seen} reorderings`]),\n ...(total.degree === null ? [] : [`and its reordering degree went to ${total.degree}`]),\n ].join(\", \");\n\n/** This function says a spread in words, in milliseconds. */\nconst spreadWords = (spread: Spread | null): string => {\n if (spread === null) return \"no time\";\n const ms = (time: Quantity) => Math.round(valueIn(time, \"s\") * 1e6) / 1e3;\n return `median ${ms(spread.median)} ms, p99 ${ms(spread.p99)} ms, p99.9 ${ms(spread.p999)} ms, max ${ms(spread.max)} ms of ${spread.count}`;\n};\n\n/** This function gives P(X ≤ j) for X ~ Binomial(n, 1/2), exactly. */\nconst below = (n: number, j: number): number => {\n let term = 1;\n let sum = 0;\n for (let i = 0; i <= j; i += 1) {\n sum += term;\n term = (term * (n - i)) / (i + 1);\n }\n return sum / 2 ** n;\n};\n\n/**\n * This function gives the distribution-free interval of a median: [x_(k), x_(n+1−k)], for the\n * largest k whose two tails each hold 2.5 % or less. For eleven runs, it gives [x_(2), x_(10)],\n * at 98.8 %.\n */\nexport const medianInterval = (sorted: readonly number[]): [number, number] => {\n const n = sorted.length;\n let k = 0;\n while (k + 1 <= n / 2 && below(n, k) <= 0.025) k += 1;\n return k === 0 ? [sorted[0]!, sorted[n - 1]!] : [sorted[k - 1]!, sorted[n - k]!];\n};\n\nconst middle = (sorted: readonly number[]): number => {\n const half = Math.floor(sorted.length / 2);\n return sorted.length % 2 === 1 ? sorted[half]! : (sorted[half - 1]! + sorted[half]!) / 2;\n};\n\n/**\n * This function gives the verdict of a cell, from two tests:\n * - Containment: each run lands in [T_lo − δ_abs, T_hi + δ_abs]. A run outside is a finding.\n * - Median equivalence: the interval of the median is in the band, made wider by δ_rel. An\n * interval outside the band fails. An interval across its edge extends the cell to 22 runs, then\n * 44, and then the verdict is inconclusive.\n *\n * A silent prediction holds only if no run detected the fault in its window. After a stall, the\n * return of the reply gets the same test as a detection, by its interval, and a detection is a\n * finding. The distribution test (a DKW band) needs a predicted distribution from a simulator,\n * which does not exist at this time.\n */\nexport const verdictOf = (prediction: Prediction, runs: readonly Measured[]): Verdict => {\n const counted = runs.filter((run) => run.void === null);\n const voided = runs.length - counted.length;\n const outside = counted\n .filter((run) => run.finding !== null)\n .map((run) => ({ run: name(run), why: run.finding! }));\n const empty = { counted: counted.length, voided, median: null, medianInterval: null };\n if (counted.length === 0)\n return { ...empty, decision: \"inconclusive\", band: null, outside, why: \"no run counted\" };\n\n if (prediction.detects === null) {\n // a calibration of a proxy: its time in each direction, over all the requests of the runs\n const crossed = counted.flatMap((run) => (run.crossings === undefined ? [] : [run.crossings]));\n if (crossed.length > 0) {\n const hop = crossed[0]!.hop;\n const up = spreadOf(crossed.flatMap((each) => each.up));\n const down = spreadOf(crossed.flatMap((each) => each.down));\n return {\n ...empty,\n decision: \"measured\",\n band: null,\n outside: [],\n median: up?.median ?? null,\n proxy: { hop, up, down },\n why: `${counted.length} runs measured ${hop}: toward the target ${spreadWords(up)}; toward the client ${spreadWords(down)}. ${prediction.why}`,\n };\n }\n // a measurement-only cell with a reference: the outcomes, and the times from the reference\n if (prediction.from !== undefined) {\n const times = counted\n .flatMap((run) => (run.elapsed === null ? [] : [valueIn(run.elapsed, \"s\")]))\n .sort((a, b) => a - b);\n const kinds = new Map<string, number>();\n for (const run of counted) {\n const kind = run.outcome.kind === \"error\" ? run.outcome.errno : run.outcome.kind;\n kinds.set(kind, (kinds.get(kind) ?? 0) + 1);\n }\n const outcomes = [...kinds].map(([kind, n]) => `${n} ${kind}`).join(\", \");\n const timed =\n times.length === 0\n ? \"no time\"\n : `median ${s(middle(times)).value} s from ${prediction.from}, range [${s(times[0]!).value}, ${s(times.at(-1)!).value}] s`;\n return {\n ...empty,\n decision: \"measured\",\n band: null,\n outside: [],\n median: times.length === 0 ? null : s(middle(times)),\n why: `${counted.length} runs measured: ${outcomes}; ${timed}. ${prediction.why}`,\n };\n }\n const samples = counted\n .flatMap((run) => (run.samples ?? []).map((sample) => valueIn(sample, \"s\")))\n .sort((a, b) => a - b);\n const retransmissions = counted.reduce((sum, run) => sum + run.retransmissions, 0);\n const ended = counted.filter((run) => run.outcome.kind === \"error\" || run.outcome.kind === \"eof\");\n const reordering = reorderingTotal(counted);\n // a run in which nothing came out of order, twice or SACKed says nothing about reordering\n const anything =\n reordering !== undefined &&\n [reordering.outOfOrder, reordering.duplicates, reordering.ooo, reordering.sacked, reordering.dsacks, reordering.fast, reordering.seen].some(\n (count) => (count ?? 0) > 0,\n );\n const reordered = anything ? ` Reordering: ${reorderingWords(reordering)}.` : \"\";\n const why =\n samples.length === 0\n ? `${counted.length} runs measured, with no reply: ${prediction.why}`\n : `${counted.length} runs, ${samples.length} replies: median ${s(middle(samples)).value} s, p99 ${s(quantileOf(samples, 0.99)).value} s, slowest ${s(samples.at(-1)!).value} s, ${retransmissions} retransmissions, ${ended.length} connections ended.${reordered} ${prediction.why}`;\n return {\n ...empty,\n decision: \"measured\",\n band: null,\n outside: [],\n median: samples.length === 0 ? null : s(middle(samples)),\n ...(reordering === undefined ? {} : { reordering }),\n why,\n };\n }\n\n if (!prediction.detects && prediction.recovers === undefined && prediction.replies === undefined) {\n const decision = outside.length === 0 ? \"holds\" : \"fails\";\n const why =\n decision === \"holds\"\n ? `nothing detected it in ${counted.length} runs of ${valueIn(prediction.window, \"s\")} s`\n : `${outside.length} of ${counted.length} runs detected it`;\n return { ...empty, decision, band: null, outside, why };\n }\n\n // the interval of a detection, of the return of the reply after a stall, or of a reply in its band\n const timed = prediction.detects ? prediction : (prediction.recovers ?? prediction.replies)!;\n const noise = valueIn(NOISE, \"s\");\n const low = valueIn(timed.low, \"s\");\n const high = valueIn(timed.high, \"s\");\n const band = { low: s(low - noise), high: s(high + noise) };\n const values: number[] = [];\n for (const run of counted) {\n if (run.elapsed === null) continue;\n const value = valueIn(run.elapsed, \"s\");\n values.push(value);\n if (value < low - noise || value > high + noise)\n outside.push({\n run: name(run),\n why: `${value} s is outside [${band.low.value}, ${band.high.value}] s`,\n });\n }\n values.sort((a, b) => a - b);\n if (values.length === 0)\n return { ...empty, decision: \"fails\", band, outside, why: \"no run measured its time\" };\n const [from, to] = medianInterval(values);\n const median = s(middle(values));\n const interval = { low: s(from), high: s(to) };\n const wide = { low: low * (1 - RELATIVE), high: high * (1 + RELATIVE) };\n const inside = from >= wide.low && to <= wide.high;\n const disjoint = to < wide.low || from > wide.high;\n // a run can be outside for its time and for a finding: it counts once\n const off = new Set(outside.map((each) => each.run)).size;\n const counts = `${counted.length - off} of ${counted.length} runs inside [${band.low.value}, ${band.high.value}] s`;\n const centre = `median ${median.value} s, its interval [${interval.low.value}, ${interval.high.value}] s`;\n const result = {\n counted: counted.length,\n voided,\n band,\n outside,\n median,\n medianInterval: interval,\n };\n if (outside.length > 0 || disjoint)\n return { ...result, decision: \"fails\", why: `${counts}; ${centre}` };\n if (inside) return { ...result, decision: \"holds\", why: `${counts}; ${centre}` };\n const decision = counted.length < STEPS[2] ? \"extend\" : \"inconclusive\";\n return { ...result, decision, why: `${counts}; ${centre} straddles the band` };\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { quantity, valueIn } from \"@mark1russell7/quantity\";\nimport type { Cell, KernelRecord, ProbeEvent, Reference, Run } from \"./cell.ts\";\nimport { NOISE } from \"./verdict.ts\";\n\n/**\n * An outcome tells how the wait of a run ended. The socket failed with an errno, or it read the\n * end of the file. Or nothing came, or the reply came back after a stall.\n */\nexport type Outcome =\n | { readonly kind: \"error\"; readonly errno: string }\n | { readonly kind: \"eof\" }\n | { readonly kind: \"silent\" }\n | { readonly kind: \"answered\" };\n\n/** A measurement is what one run measured, against the prediction of its cell. */\nexport interface Measured {\n readonly environment: number;\n readonly index: number;\n readonly outcome: Outcome;\n /**\n * This time is from the reference event of the prediction to the detection, both as the kernel\n * stamped them in the records of the prober. It is null when nothing detected the fault.\n */\n readonly elapsed: Quantity | null;\n /** This time is the same interval from the TCP_INFO samples of the client, the second vantage. */\n readonly socket: Quantity | null;\n /** This time is how long the client took to see the error after the kernel reported it. */\n readonly noticed: Quantity | null;\n /**\n * These are the steps of the kernel after the reference: each keepalive probe, or each firing of\n * the RTO timer. After a stall, they are each retransmission from the send, the loss probe first.\n */\n readonly steps: readonly Quantity[];\n /**\n * These are the reply times of a measurement-only cell. For each request after the first, a\n * sample is the time from its send to the first data after it.\n */\n readonly samples?: readonly Quantity[];\n /** This count is the number of keepalive probes that the kernel sent. */\n readonly probes: number;\n /** This count is the number of segments that the kernel sent again. */\n readonly retransmissions: number;\n /** This count is the number of segments that the socket received after the request. */\n readonly acks: number;\n /** This count is the number of bytes that the client read after the request. */\n readonly bytes?: number;\n /**\n * This time is when a router of the topology forgot the flow of the client. It counts from the\n * last segment that the client received before the fault. It is null when no router forgot the flow.\n */\n readonly forgot?: Quantity | null;\n /**\n * This time is the first round trip of the socket of the observer. It is the smoothed round trip\n * in its first record, which is the sample of the handshake. It is null when no record has it.\n */\n readonly rtt?: Quantity | null;\n /** This record tells what reordering did in a run of a measurement-only cell, from the records of its two ends. */\n readonly reordering?: Reordering;\n /** These are the times of the proxy of a calibration cell, one for each request and its reply. */\n readonly crossings?: Crossings;\n /** This text is the reason that the run cannot count, or null. A void run is not a pass and not a finding. */\n readonly void: string | null;\n /** This text tells what in the run disagrees with the prediction, other than its timing, or null. */\n readonly finding: string | null;\n}\n\n/**\n * A reordering record is what reordering, duplicates and losses did to the data of a run. The\n * receiver is the client, and the sender is the socket of its peer.\n * - `outOfOrder` counts the arrivals at the client beyond a hole, and `duplicates` counts the\n * arrivals whose data the client had. The prober reads the two from each arrival, against `rcv_nxt`.\n * - An arrival can hold more than one segment. The kernel joins the segments that wait in the\n * backlog of a socket while its owner holds it.\n * - `ooo` is the count of the kernel of the client in segments (`rcv_ooopack`).\n * - `sacked` and `lost` are the most segments that the sender held as SACKed and as lost.\n * - `dsacks` is the DSACK blocks that the sender received, and `fast` its fast retransmissions.\n * - `degree` is the highest reordering degree of the sender (`tp->reordering`), and `seen` the\n * reorderings that it detected (`reord_seen`).\n *\n * The counts of the kernel are null for a record of the prober before version 0.2.\n */\nexport interface Reordering {\n readonly outOfOrder: number;\n readonly duplicates: number;\n readonly ooo: number | null;\n readonly sacked: number | null;\n readonly lost: number | null;\n readonly dsacks: number | null;\n readonly fast: number;\n readonly degree: number | null;\n readonly seen: number | null;\n}\n\n/**\n * A crossings record is the time of a proxy for each request (`up`) and for each reply (`down`).\n * The time of a request starts at its arrival on the socket toward the client. It stops at the\n * write on the socket toward the target. The time of a reply goes the other way. A write is a\n * `tx` record of the prober.\n */\nexport interface Crossings {\n readonly hop: string;\n readonly up: readonly Quantity[];\n readonly down: readonly Quantity[];\n}\n\n/** These are the errno numbers that the kernel reports to a socket, by the names that predictions use. */\nconst ERRNO: Readonly<Record<number, string>> = {\n 32: \"EPIPE\",\n 101: \"ENETUNREACH\",\n 103: \"ECONNABORTED\",\n 104: \"ECONNRESET\",\n 110: \"ETIMEDOUT\",\n 111: \"ECONNREFUSED\",\n 113: \"EHOSTUNREACH\",\n};\n\n/** This function gives the interval between two instants on the one clock, in seconds to the microsecond. */\nconst between = (from: number, to: number): Quantity =>\n quantity(Math.round((to - from) / 1e3) / 1e6, \"s\");\n\n/** These functions give the states of a state record: before the change, and after it. TCP_ESTABLISHED is 1, TCP_CLOSE_WAIT 8. */\nconst oldState = (record: KernelRecord): number | undefined => (record as { readonly old?: number }).old;\nconst newState = (record: KernelRecord): number | undefined => (record as { readonly new?: number }).new;\n\nconst count = (event: ProbeEvent | undefined, field: string): number =>\n event === undefined ? 0 : Number(event[field] ?? 0);\n\n/** This function gives the view of the client on the same interval: TCP_INFO, with a sample each 2 ms. */\nconst socketVantage = (\n cell: Cell,\n events: readonly ProbeEvent[],\n ready: ProbeEvent,\n request: ProbeEvent,\n): Quantity | null => {\n if (!cell.prediction.detects || cell.prediction.from === \"peer-closed\" || cell.prediction.from === \"fault\")\n return null;\n const ended = events.find((event) => event.ev === \"error\" || event.ev === \"eof\");\n const reference =\n cell.prediction.from === \"last-received\"\n ? ready\n : cell.prediction.from === \"request-sent\"\n ? request\n : events.find(\n (event) =>\n event.ev === \"kernel\" && event.t >= request.t && count(event, \"retransmits\") > 0,\n );\n return ended === undefined || reference === undefined ? null : between(reference.t, ended.t);\n};\n\n/** A socket place is a socket of a run: its namespace and its cookie. */\nexport interface SocketPlace {\n readonly netns: number;\n readonly cookie: number;\n}\n\nconst ipOf = (address: string): string => address.split(\":\")[0]!;\n\n/**\n * This function finds the socket that became ESTABLISHED in a run, in a namespace, whose peer\n * matches. In a topology, an address of a hop serves each run, thus only the socket that the\n * run opened is the socket of the run.\n */\nconst opened = (\n run: Run,\n netns: number,\n peer: (dst: string) => boolean,\n): SocketPlace | undefined => {\n const record = run.kernel.find(\n (each) =>\n each.netns === netns &&\n each.ev === \"state\" &&\n newState(each) === 1 &&\n each.dst !== undefined &&\n peer(each.dst),\n );\n return record?.cookie === undefined ? undefined : { netns, cookie: record.cookie };\n};\n\n/** This function finds the socket of the target for the connection of a run. In a topology, its peer is the last hop. */\nconst targetSocket = (run: Run, local: unknown): SocketPlace | undefined => {\n if (run.topology === undefined) {\n const cookie = run.kernel.find(\n (record) => record.netns === run.targetNetns && record.dst === local,\n )?.cookie;\n return cookie === undefined ? undefined : { netns: run.targetNetns, cookie };\n }\n const peer = run.topology.targetPeer;\n return opened(run, run.targetNetns, (dst) => ipOf(dst) === peer);\n};\n\n/**\n * This function finds the socket of the observer of a cell in a run:\n * - the client socket, by the address that it connected from, in its namespace.\n * - the socket of the target for the same connection, in its namespace.\n * - a socket of a proxy of the topology, toward the client or toward the target.\n */\nexport const socketOf = (cell: Cell, run: Run): SocketPlace | undefined => {\n const local = run.events.find((event) => event.ev === \"connected\")?.[\"local\"];\n const observer = cell.observer ?? \"client\";\n if (observer === \"client\") {\n const cookie = run.kernel.find((record) => record.netns === run.netns && record.src === local)?.cookie;\n return cookie === undefined ? undefined : { netns: run.netns, cookie };\n }\n if (observer === \"server\") return targetSocket(run, local);\n const hop = run.topology?.hops[observer.hop];\n if (hop === undefined) return undefined;\n return observer.toward === \"client\"\n ? opened(run, hop.netns, (dst) => ipOf(dst) === hop.client)\n : opened(run, hop.netns, (dst) => dst === hop.target);\n};\n\n/**\n * This function finds the socket of the peer of the observer: its close is the reference of a FIN.\n * In a direct pair, it is the other end of the connection. In a topology, it is the socket of the\n * target, for the client.\n */\nconst peerOf = (cell: Cell, run: Run): SocketPlace | undefined => {\n const local = run.events.find((event) => event.ev === \"connected\")?.[\"local\"];\n const server = cell.observer === \"server\";\n if (run.topology !== undefined) return server ? undefined : targetSocket(run, local);\n const netns = server ? run.netns : run.targetNetns;\n const cookie = run.kernel.find(\n (record) => record.netns === netns && (server ? record.src : record.dst) === local,\n )?.cookie;\n return cookie === undefined ? undefined : { netns, cookie };\n};\n\n/** This function tells if sequence number a is after b, in the 32-bit space of TCP (`after` in include/net/tcp.h). */\nconst after32 = (a: number, b: number): boolean => ((a - b) | 0) > 0;\n\n/** This function gives the largest value of a count of the kernel in records, or null when no record has it. */\nconst most = (records: readonly KernelRecord[], count: (record: KernelRecord) => number | undefined): number | null =>\n records.reduce<number | null>((top, record) => {\n const value = count(record);\n return value === undefined ? top : Math.max(top ?? 0, value);\n }, null);\n\n/** This function gives what reordering did in a run, from the records of the receiver and of the sender. */\nexport const reorderingOf = (\n receiver: readonly KernelRecord[],\n sender: readonly KernelRecord[],\n): Reordering => {\n const arrivals = receiver.filter(\n (record) => record.ev === \"rx\" && (record.seg?.len.value ?? 0) > 0 && record.tcp?.rcv_nxt !== undefined,\n );\n const outOfOrder = arrivals.filter((record) => after32(record.seg!.seq, record.tcp!.rcv_nxt!)).length;\n const duplicates = arrivals.filter(\n (record) => !after32(record.seg!.seq + record.seg!.len.value, record.tcp!.rcv_nxt!),\n ).length;\n return {\n outOfOrder,\n duplicates,\n ooo: most(receiver, (record) => record.tcp?.rcv_ooopack),\n sacked: most(sender, (record) => record.tcp?.sacked),\n lost: most(sender, (record) => record.tcp?.lost),\n dsacks: most(sender, (record) => record.tcp?.dsack_dups),\n fast: sender.filter((record) => record.ev === \"retrans\" && record.why === \"fast\").length,\n degree: most(sender, (record) => record.tcp?.reordering),\n seen: most(sender, (record) => record.tcp?.reord_seen),\n };\n};\n\n/**\n * This function gives the times of a proxy in a run, for each request after the first. The time\n * of a request starts at its arrival on the socket of the proxy toward the client. It stops at the\n * next write on the socket toward the target. The time of its reply starts at its arrival on the\n * socket toward the target, and it stops at the next write toward the client. A request without\n * each record gives no time.\n */\nexport const crossingsOf = (\n run: Run,\n hop: string,\n toClient: SocketPlace | undefined,\n toTarget: SocketPlace | undefined,\n): Crossings => {\n const up: Quantity[] = [];\n const down: Quantity[] = [];\n if (toClient === undefined || toTarget === undefined) return { hop, up, down };\n const of = (place: SocketPlace) =>\n run.kernel.filter((record) => record.netns === place.netns && record.cookie === place.cookie);\n const near = of(toClient);\n const far = of(toTarget);\n const arrival = (records: readonly KernelRecord[], from: number, to: number) =>\n records.find((record) => record.ev === \"rx\" && (record.seg?.len.value ?? 0) > 0 && record.mono > from && record.mono < to);\n const write = (records: readonly KernelRecord[], from: number, to: number) =>\n records.find((record) => record.ev === \"tx\" && record.mono >= from && record.mono < to);\n const sends = run.events.filter((event) => event.ev === \"sent\").slice(1);\n sends.forEach((send, n) => {\n const next = sends[n + 1]?.t ?? Infinity;\n const request = arrival(near, send.t, next);\n const relayed = request === undefined ? undefined : write(far, request.mono, next);\n if (request === undefined || relayed === undefined) return;\n up.push(between(request.mono, relayed.mono));\n const reply = arrival(far, relayed.mono, next);\n const answered = reply === undefined ? undefined : write(near, reply.mono, next);\n if (reply !== undefined && answered !== undefined) down.push(between(reply.mono, answered.mono));\n });\n return { hop, up, down };\n};\n\n/**\n * This function reads one run against its cell, from the records of the kernel first.\n * - The socket of the run is the socket of its observer. It is the socket that the client connected\n * from, in its namespace, or the socket of the target or of a proxy for that connection.\n * - The fault must hold before the event that it must meet. That is before the request goes into\n * it, and before the first keepalive probe. A live peer answers that probe, and the answer\n * starts the idle time again. A run where the fault was late is void. A run whose records the\n * prober lost is also void.\n */\nexport const measure = (cell: Cell, run: Run): Measured => {\n const { events, kernel } = run;\n const ready = events.find((event) => event.ev === \"ready\");\n const local = events.find((event) => event.ev === \"connected\")?.[\"local\"];\n const inflight = cell.probe.mode !== \"idle\";\n const request = inflight ? events.filter((event) => event.ev === \"sent\")[1] : ready;\n const server = cell.observer === \"server\";\n const place = socketOf(cell, run);\n const netns = place?.netns;\n const cookie = place?.cookie;\n const socket = kernel.filter((record) => record.netns === netns && record.cookie === cookie);\n // the socket of the peer for the same connection: a FIN counts from the change of state that its close makes\n const peerPlace = peerOf(cell, run);\n const peerNetns = peerPlace?.netns;\n const peerCookie = peerPlace?.cookie;\n // the bytes that the client read after its request, and the first and last time that data came\n const after = request === undefined ? [] : events.filter((event) => event.ev === \"data\" && event.t > request.t);\n const bytes = after.reduce((sum, event) => sum + Number(event[\"n\"] ?? 0), 0);\n // the end of the flow in a router of the topology: its DESTROY line, by the address of the client\n const forgotten = (run.middlebox ?? []).find(\n (line) =>\n line.ev === \"ct\" &&\n line[\"type\"] === \"DESTROY\" &&\n typeof line[\"orig\"] === \"string\" &&\n (line[\"orig\"] as string).startsWith(`${String(local)}>`),\n );\n const keepalives = socket.filter(\n (record) => record.ev === \"probe\" && record.what === \"keepalive\",\n );\n const retransmitted = socket.filter((record) => record.ev === \"retrans\");\n /**\n * This function examines the timer that drives the measurement: keepalive or retransmit. It\n * finds the first firing in an interval that was two jiffies or more behind its wheel slot. The\n * wheel does a slot in the tick when it is due, thus such a delay comes from the CPU: the host\n * stopped a vCPU. The prediction is about the timing of the kernel, and that firing measured\n * the timing of the host. One jiffy is a tick that occurred while the wheel worked, and δ_abs\n * includes it. The function ignores the other timer: the retransmit timer fires one time, idle,\n * after a reply, and it does not move a keepalive.\n */\n const delayed = (from: number, to: number, timer: \"keepalive\" | \"retransmit\"): string | null => {\n const firings = socket.filter(\n (record) =>\n record.ev === \"timer\" && record.what === timer && record.mono >= from && record.mono <= to,\n );\n const late = firings.find((record) => (record.behind?.value ?? 0) >= 2);\n if (late !== undefined)\n return `its ${timer} timer ran ${late.behind!.value} jiffies behind its wheel slot: the VM delayed it, not the kernel`;\n // a keepalive timer that finds its socket held by its owner sends nothing, and it tries again\n // HZ/20 later (tcp_keepalive_timer). The retransmit timer only gives its work to the release\n // by the owner, some microseconds later\n const held = firings.find((record) => timer === \"keepalive\" && record.owned === true);\n return held === undefined\n ? null\n : \"its keepalive timer found the socket held by its owner and put itself off by HZ/20: the run measured the owner, not the timer\";\n };\n const acks =\n request === undefined\n ? 0\n : socket.filter((record) => record.ev === \"rx\" && record.mono > request.t).length;\n // the first round trip of the observer: the smoothed round trip in its first record, the sample of the handshake\n const srtt = socket.find((record) => record.tcp?.srtt !== undefined)?.tcp?.srtt;\n const rtt = srtt === undefined ? null : quantity(valueIn(srtt, \"us\") / 1000, \"ms\");\n const base = {\n environment: run.environment,\n index: run.index,\n probes: keepalives.length,\n retransmissions: retransmitted.length,\n acks,\n rtt,\n };\n const nothing = {\n outcome: { kind: \"silent\" } as const,\n elapsed: null,\n socket: null,\n noticed: null,\n steps: [],\n finding: null,\n };\n\n if (ready === undefined) return { ...base, ...nothing, void: \"the client never had its reply\" };\n if (request === undefined)\n return { ...base, ...nothing, void: \"the request was never sent into the fault\" };\n if (run.lost === null)\n return {\n ...base,\n ...nothing,\n void: \"the prober wrote no clock line after the run, so nothing says what it dropped\",\n };\n if (run.lost > 0) return { ...base, ...nothing, void: `the prober dropped ${run.lost} records` };\n if (cookie === undefined)\n return { ...base, ...nothing, void: \"the prober saw nothing of this connection\" };\n let voided: string | null = null;\n if (run.fault !== null) {\n // the request of a client must go into the fault. The request of a cell on the server side must\n // reach the server first, and its reply must not reach the client before the fault holds. An\n // mtu-blackhole drops only the long reply, thus its request goes after the fault, as a client's does\n const replyFirst = server && inflight && cell.fault !== \"mtu-blackhole\";\n const replied = events.some(\n (event) => event.ev === \"data\" && event.t > request.t && event.t < run.fault!.t1,\n );\n if (inflight && !replyFirst && run.fault.t1 > request.t)\n voided = \"the request went out before the fault held\";\n else if (replyFirst && run.fault.t1 < request.t)\n voided = \"the request went out into the fault, so the server never had it\";\n else if (replyFirst && replied)\n voided = \"the server's reply arrived before the fault held\";\n else if (keepalives[0] !== undefined && keepalives[0].mono < run.fault.t1)\n voided = \"a keepalive probe went out before the fault held\";\n }\n // a band holds for the first round trips that its prediction assumes. Above them, the run measured another path\n const assumed = cell.prediction.detects === null ? undefined : cell.prediction.rtt;\n if (voided === null && assumed !== undefined && rtt !== null && valueIn(rtt, \"ms\") > valueIn(assumed.high, \"ms\"))\n voided = `rtt-over-assumption: its first round trip was ${rtt.value} ms, above the ${valueIn(assumed.high, \"ms\")} ms that the prediction assumes`;\n\n // the detection as the kernel reported it to the owner of the socket, and as the client saw it.\n // The process of a server or of a proxy tells nothing that the harness reads, thus its kernel tells all\n const remote = cell.observer !== undefined && cell.observer !== \"client\";\n const reported = socket.find((record) => record.ev === \"error\" && (record.err ?? 0) !== 0);\n const seen = remote\n ? undefined\n : events.find((event) => event.ev === \"error\" || event.ev === \"eof\");\n // an end of file without an error is the change of state that the FIN of the peer makes, from\n // ESTABLISHED to CLOSE_WAIT, as the kernel stamps it\n const closeWait = socket.find((record) => record.ev === \"state\" && newState(record) === 8);\n const outcome: Outcome =\n reported !== undefined\n ? { kind: \"error\", errno: ERRNO[reported.err!] ?? `E${reported.err}` }\n : closeWait !== undefined || seen?.ev === \"eof\"\n ? { kind: \"eof\" }\n : seen !== undefined\n ? { kind: \"error\", errno: String(seen[\"errno\"]) }\n : { kind: \"silent\" };\n const ended = reported?.mono ?? closeWait?.mono ?? seen?.t;\n const noticed =\n reported !== undefined && seen !== undefined ? between(reported.mono, seen.t) : null;\n const { prediction } = cell;\n // keepalive counts from the last segment received before the fault. The budget of the give-up\n // counts from the first retransmission of the RTO timer, not from the tail loss probe before it.\n // A reset counts from the send of the client. The kernel has no record of that send, thus the\n // stamp is the stamp of the client. A toxic counts from the start of its commit\n const before = run.fault?.t1 ?? ready.t;\n const referenceAt = (from: Reference): number | undefined => {\n if (from === \"request-sent\") return request.t;\n if (from === \"fault\") return run.fault?.t0;\n const record: KernelRecord | undefined =\n from === \"last-received\"\n ? socket.filter((each) => each.ev === \"rx\" && each.mono <= before).at(-1)\n : from === \"first-retransmission\"\n ? retransmitted.find((each) => each.why === \"rto\")\n : kernel.find(\n (each) =>\n each.netns === peerNetns &&\n each.cookie === peerCookie &&\n each.ev === \"state\" &&\n oldState(each) === 1,\n );\n return record?.mono;\n };\n // in a topology, the bytes after the request and the instant that a router forgot the flow\n const lastRx =\n kernel\n .filter((each) => each.netns === run.netns && each.src === local && each.ev === \"rx\" && each.mono <= before)\n .at(-1)?.mono ?? ready.t;\n const chain =\n run.topology === undefined\n ? {}\n : { bytes, forgot: forgotten === undefined ? null : between(lastRx, forgotten.t) };\n\n // a measurement-only cell: each request of the run and the time to its reply, and the time from\n // its reference to the end of the connection, or to the last data\n if (prediction.detects === null) {\n const sends = events.filter((event) => event.ev === \"sent\").slice(1);\n // a reply ends at its last byte when the client says so (`replied`), and at its first data otherwise\n const answers = events.some((event) => event.ev === \"replied\") ? \"replied\" : \"data\";\n const samples = sends.flatMap((send, n) => {\n const next = sends[n + 1]?.t ?? Infinity;\n const reply = events.find((event) => event.ev === answers && event.t > send.t && event.t < next);\n return reply === undefined ? [] : [between(send.t, reply.t)];\n });\n const from = prediction.from === undefined ? undefined : referenceAt(prediction.from);\n const until = ended ?? after.at(-1)?.t;\n // the data of the client comes from its peer: the receiver and the sender of each reply\n const peerRecords =\n peerCookie === undefined\n ? []\n : kernel.filter((record) => record.netns === peerNetns && record.cookie === peerCookie);\n const proxy = cell.calibrates === undefined ? undefined : run.topology?.hops[cell.calibrates];\n const crossings =\n cell.calibrates === undefined\n ? {}\n : {\n crossings: crossingsOf(\n run,\n cell.calibrates,\n proxy === undefined ? undefined : socketOf({ ...cell, observer: { hop: cell.calibrates, toward: \"client\" } }, run),\n proxy === undefined ? undefined : socketOf({ ...cell, observer: { hop: cell.calibrates, toward: \"target\" } }, run),\n ),\n };\n return {\n ...base,\n ...nothing,\n ...chain,\n outcome: ended !== undefined ? outcome : { kind: \"answered\" },\n ...(from === undefined || until === undefined ? {} : { elapsed: between(from, until) }),\n noticed: prediction.from === undefined ? null : noticed,\n steps: retransmitted\n .filter((record) => record.mono > request.t)\n .map((record) => between(request.t, record.mono)),\n samples,\n reordering: reorderingOf(\n socket.filter((record) => record.mono > request.t),\n peerRecords.filter((record) => record.mono > request.t),\n ),\n ...crossings,\n void: voided,\n };\n }\n\n // a stall: nothing must detect it, and the connection must move again at the first\n // retransmission after it stops, timed from the send of the request\n if (!prediction.detects && prediction.recovers !== undefined) {\n const { recovers } = prediction;\n const heal = run.heal ?? null;\n if (voided === null && heal === null) voided = \"the stall never ended\";\n else if (voided === null && heal !== null) {\n const from = between(request.t, heal.t0);\n const to = between(request.t, heal.t1);\n if (\n valueIn(from, \"s\") <= valueIn(recovers.heal.low, \"s\") ||\n valueIn(to, \"s\") >= valueIn(recovers.heal.high, \"s\")\n )\n voided = `the stall ended ${from.value}–${to.value} s after the send, outside the ${recovers.heal.low.value}–${recovers.heal.high.value} s its prediction assumed`;\n }\n const steps = retransmitted\n .filter((record) => record.mono > request.t)\n .map((record) => between(request.t, record.mono));\n if (ended !== undefined)\n return {\n ...base,\n ...nothing,\n outcome,\n steps,\n void: voided,\n finding: `detected after ${between(request.t, ended).value} s, where it should recover`,\n };\n // the first segment back after the stall stopped: the answer of the server to the\n // retransmission that got through, and the read of the reply by the client\n const answer =\n heal === null\n ? undefined\n : socket.find((record) => record.ev === \"rx\" && record.mono > heal.t0);\n if (answer === undefined)\n return {\n ...base,\n ...nothing,\n steps,\n void: voided,\n finding: \"it never moved again: nothing came back after the stall ended\",\n };\n const reply = events.find((event) => event.ev === \"data\" && event.t > request.t);\n voided ??= delayed(request.t, answer.mono, \"retransmit\");\n return {\n ...base,\n outcome: { kind: \"answered\" },\n elapsed: between(request.t, answer.mono),\n socket: reply === undefined ? null : between(request.t, reply.t),\n noticed: reply === undefined ? null : between(answer.mono, reply.t),\n steps,\n void: voided,\n finding: null,\n };\n }\n\n // a reply in a band: nothing must detect the fault, and the reply must come back in its time,\n // from the send of the request to the first segment that the socket receives after it\n if (!prediction.detects && prediction.replies !== undefined) {\n const window = valueIn(prediction.window, \"s\") * 1e9;\n if (ended !== undefined && ended - request.t <= window)\n return {\n ...base,\n ...nothing,\n ...chain,\n outcome,\n elapsed: between(request.t, ended),\n void: voided,\n finding: `detected after ${between(request.t, ended).value} s, where the reply should come`,\n };\n // the first segment with data: a proxy can acknowledge the request first, with no data\n const answer = socket.find(\n (record) => record.ev === \"rx\" && record.mono > request.t && (record.seg?.len.value ?? 0) > 0,\n );\n const reply = after[0];\n if (answer === undefined)\n return { ...base, ...nothing, ...chain, void: voided, finding: \"the reply never came\" };\n return {\n ...base,\n ...chain,\n outcome: { kind: \"answered\" },\n elapsed: between(request.t, answer.mono),\n socket: reply === undefined ? null : between(request.t, reply.t),\n noticed: reply === undefined ? null : between(answer.mono, reply.t),\n steps: [],\n void: voided,\n finding: null,\n };\n }\n\n if (!prediction.detects) {\n // the test of silence covers the window from the send of the request\n const window = valueIn(prediction.window, \"s\") * 1e9;\n if (ended === undefined || ended - request.t > window) {\n // a router that forgot the flow in the window broke the silence, though nothing detected it yet\n const forgot =\n forgotten !== undefined && forgotten.t - request.t <= window\n ? `a router forgot the flow ${between(lastRx, forgotten.t).value} s after the last segment, where it should keep it`\n : null;\n return { ...base, ...nothing, ...chain, void: voided, finding: forgot };\n }\n return {\n ...base,\n ...nothing,\n ...chain,\n outcome,\n elapsed: between(request.t, ended),\n void: voided,\n finding: `detected after ${between(request.t, ended).value} s, where nothing should`,\n };\n }\n\n if (ended === undefined)\n return {\n ...base,\n ...nothing,\n ...chain,\n void: voided,\n finding: `nothing detected it: ${prediction.timer} never fired`,\n };\n const reference = referenceAt(prediction.from);\n if (reference === undefined) {\n return {\n ...base,\n ...nothing,\n ...chain,\n outcome,\n noticed,\n void: voided,\n finding: `it ended without its reference event (${prediction.from})`,\n };\n }\n const steps = (\n prediction.from === \"last-received\"\n ? keepalives\n : prediction.from === \"first-retransmission\"\n ? retransmitted.filter((record) => record.why === \"rto\" && record.mono > reference)\n : []\n ).map((record) => between(reference, record.mono));\n const matches =\n prediction.errno === \"EOF\"\n ? outcome.kind === \"eof\"\n : outcome.kind === \"error\" && outcome.errno === prediction.errno;\n const finding = matches\n ? null\n : `it ended with ${outcome.kind === \"error\" ? outcome.errno : \"end of file\"}, not ${prediction.errno}`;\n // a firing at the reference itself only moves the start of the interval. A reset meets the send\n // with no timer, a FIN arrives with none, and a toxic has its own clock\n if (prediction.from === \"last-received\" || prediction.from === \"first-retransmission\")\n voided ??= delayed(\n reference,\n ended,\n prediction.from === \"last-received\" ? \"keepalive\" : \"retransmit\",\n );\n // a timer of conntrack (expire_…) reaches its DESTROY through the listing of the router. A slow\n // listing between the expiry and the detection breaks the bound of its lag: the run measured the\n // pump, not the router. The expiry is at most the width of the band before the detection\n const lag = (valueIn(prediction.high, \"s\") - valueIn(prediction.low, \"s\")) * 1e9;\n const slow = prediction.timer.startsWith(\"expire_\")\n ? (run.middlebox ?? []).find(\n (line) =>\n line.ev === \"pump-slow\" &&\n line.t <= ended &&\n line.t + Number(line[\"took\"]) * 1e9 >= ended - lag,\n )\n : undefined;\n if (slow !== undefined)\n voided ??= `a listing of the router took ${Math.round(Number(slow[\"took\"]) * 1e4) / 10} ms, longer than the bound of its prediction`;\n // a toxic starts somewhere in the commit of its request. A commit longer than the noise of the\n // pipeline leaves its start unknown within δ_abs\n const commit = run.fault === null ? 0 : (run.fault.t1 - run.fault.t0) / 1e6;\n if (prediction.from === \"fault\" && commit > valueIn(NOISE, \"ms\"))\n voided ??= `the commit of the fault took ${Math.round(commit * 10) / 10} ms, longer than the noise of the pipeline: its start is not known`;\n return {\n ...base,\n ...chain,\n outcome,\n elapsed: between(reference, ended),\n socket: remote ? null : socketVantage(cell, events, ready, request),\n noticed,\n steps,\n void: voided,\n finding,\n };\n};\n","import { createHash } from \"node:crypto\";\nimport {\n copyFileSync,\n mkdirSync,\n mkdtempSync,\n readFileSync,\n readdirSync,\n realpathSync,\n rmSync,\n statSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, relative } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { StartedNetwork, StartedTestContainer } from \"testcontainers\";\nimport type * as Testcontainers from \"testcontainers\";\nimport type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type {\n Client,\n ClientImage,\n FaultPlace,\n Hop,\n KernelRecord,\n MiddleboxEvent,\n ProbeEvent,\n ProxyHop,\n RunTopology,\n Target,\n Topology,\n Toxic,\n} from \"./cell.ts\";\n\n/**\n * These are the roots of the two packages that the images read: this package, and the prober\n * that Node resolves for it. In a workspace, they are two folders side by side. From npm, the\n * prober is a link, and a Docker build context does not follow a link. Thus each image builds\n * from a context that the harness copies from the two roots.\n */\nconst ROOTS: Readonly<Record<string, string>> = {\n harness: fileURLToPath(new URL(\"..\", import.meta.url)),\n prober: dirname(\n realpathSync(createRequire(import.meta.url).resolve(\"@mark1russell7/prober/package.json\")),\n ),\n};\n\n/** This function gives the path of a part of an image, which is `<package>/<path>` in the context. */\nconst pathOf = (part: string): string => {\n const [root, ...rest] = part.split(\"/\");\n const base = ROOTS[root!];\n if (base === undefined) throw new Error(`${part} is in no package that the images read`);\n return join(base, ...rest);\n};\n\n/** This function loads Testcontainers when a lab first opens. Code that only measures and judges does not load it. */\nexport const containers = (): Promise<typeof Testcontainers> => import(\"testcontainers\");\n\n/** An image is an image that the harness builds: its Dockerfile, and each file or folder that the Dockerfile reads. */\ninterface Image {\n readonly name: string;\n readonly dockerfile: string;\n readonly from: readonly string[];\n}\n\n/** This image holds the prober, the TCP client, the injector and the namespace admin. */\nconst HARNESS: Image = {\n name: \"loom-harness\",\n dockerfile: \"harness/image/Dockerfile\",\n from: [\n \"harness/image/Dockerfile\",\n \"harness/image/native\",\n \"prober/bpf\",\n \"prober/native\",\n \"prober/fixtures\",\n ],\n};\n/** This image holds the Node client: ioredis 6.0.0 on Node 24.21.0. */\nconst NODE: Image = {\n name: \"loom-harness-node\",\n dockerfile: \"harness/image/node/Dockerfile\",\n from: [\"harness/image/node\"],\n};\n/** This image is the router of a middlebox chain: NAT and conntrack, with a form for a forgotten flow. */\nconst ROUTER: Image = {\n name: \"loom-harness-router\",\n dockerfile: \"harness/image/router/Dockerfile\",\n from: [\"harness/image/router\"],\n};\n\n/** This function gives each file under a path, in a stable sequence, but not the output of a local build. */\nconst sources = (path: string): string[] => {\n if (statSync(path).isFile()) return [path];\n const entries = readdirSync(path, { withFileTypes: true });\n return entries\n .filter((entry) => entry.name !== \"target\" && entry.name !== \"node_modules\")\n .flatMap((entry) => sources(join(path, entry.name)))\n .sort();\n};\n\n/** This function gives each file of an image, with its path in the build context. */\nconst filesOf = (image: Image): Array<{ readonly file: string; readonly at: string }> =>\n image.from.flatMap((part) => {\n const base = pathOf(part);\n return sources(base).map((file) => ({\n file,\n at: join(part, relative(base, file)).replaceAll(\"\\\\\", \"/\"),\n }));\n });\n\n/** This function gives the tag of an image from its content. Thus a changed probe or client cannot operate as an old one. */\nconst imageTag = (image: Image): string => {\n const hash = createHash(\"sha256\");\n for (const { file, at } of filesOf(image)) {\n hash.update(at);\n hash.update(readFileSync(file));\n }\n return `${image.name}:${hash.digest(\"hex\").slice(0, 12)}`;\n};\n\n/** This function copies the files of an image into a new build context, and gives its path. */\nconst stage = (image: Image): string => {\n const context = mkdtempSync(join(tmpdir(), `${image.name}-`));\n for (const { file, at } of filesOf(image)) {\n mkdirSync(dirname(join(context, at)), { recursive: true });\n copyFileSync(file, join(context, at));\n }\n return context;\n};\n\nconst built = new Map<string, Promise<string>>();\n/** This function builds an image one time for each process. Docker keeps its layers between runs. */\nconst build = (image: Image): Promise<string> => {\n let tag = built.get(image.name);\n if (tag === undefined) {\n tag = (async () => {\n const name = imageTag(image);\n const { GenericContainer } = await containers();\n const context = stage(image);\n try {\n await GenericContainer.fromDockerfile(context, image.dockerfile).build(name, {\n deleteOnExit: false,\n });\n } finally {\n rmSync(context, { recursive: true, force: true });\n }\n return name;\n })();\n built.set(image.name, tag);\n }\n return tag;\n};\n\n/** This function gives the image of the harness. */\nexport const harnessImage = (): Promise<string> => build(HARNESS);\n/** This function gives the image of the Node client. */\nexport const nodeClientImage = (): Promise<string> => build(NODE);\n/** This function gives the image of the router. */\nexport const routerImage = (): Promise<string> => build(ROUTER);\n\n/**\n * This time is the interval of the listing of a router, when its hop does not set one. A listing\n * evicts each expired flow that it walks past, and the DESTROY event of that flow follows.\n */\nexport const PUMP: Quantity = { value: 0.1, unit: \"s\" };\n/**\n * This time is the longest listing of the conntrack table of a router that a prediction assumes.\n * A DESTROY follows an expiry within the interval of the pump and two listings. On 2026-10-15,\n * the slowest of 500 listings in an idle router took 19.4 ms, and the median took 5.5 ms. A\n * longer listing writes a `pump-slow` line, and a run with one near its detection is void.\n */\nexport const LISTING: Quantity = { value: 20, unit: \"ms\" };\n\n/**\n * A network fault is a fault that the injector applies as a filter rule. It is a black hole (both\n * directions, or one), or a reset on the next segment. It can also be an ICMP unreachable for each\n * segment, or a drop of each segment that is longer than a length.\n */\nexport type NetworkFault = \"blackhole\" | \"rst\" | \"reject\" | \"mtu\";\n\n/** These are the options of a network fault: the direction of a black hole, the code of a reject, the length of an MTU drop. */\nexport interface FaultOptions {\n /** This direction is relative to the namespace of the rule: `in` drops what comes from the peer, `out` what goes to it. */\n readonly direction?: \"in\" | \"out\";\n readonly code?: \"host\" | \"net\" | \"port\" | \"admin\";\n /** This length is the longest segment in bytes that an MTU drop lets through. */\n readonly above?: number;\n}\n\n/** This function gives the arguments of the injector for the options of a fault. */\nexport const optionArgs = (options: FaultOptions): string[] => [\n ...(options.direction === undefined ? [] : [\"--direction\", options.direction]),\n ...(options.code === undefined ? [] : [\"--code\", options.code]),\n ...(options.above === undefined ? [] : [\"--above\", String(options.above)]),\n];\n\n/** These are the actions of the injector that lift each network fault. */\nexport const LIFT: Readonly<Record<NetworkFault, string>> = {\n blackhole: \"clear\",\n rst: \"clear-rst\",\n reject: \"clear-reject\",\n mtu: \"clear-mtu\",\n};\n\n/**\n * A fault side is the namespace that gets the rules of a fault: the namespace of the peer of the\n * observer. For the client, it is the namespace of the target (the injector). For the server, it\n * is the namespace of the client (its privileged admin). Rules in the namespace of the observer\n * make its sends fail locally, with EPERM, and the network does not lose them. Thus its\n * retransmissions do not go out. The retransmission tracepoint of the kernel then records none,\n * because it fires only for a retransmission that went out.\n */\nexport type FaultSide = \"target\" | \"client\";\n\n/** A hop of a manifest is one hop of a topology, as the lab started it. */\nexport interface ManifestHop {\n readonly name: string;\n readonly kind: \"router\" | \"proxy\";\n readonly image: string;\n readonly id: string;\n /** These are the settings of the hop: the form and the idle timeout of a router, the files and settings of a proxy. */\n readonly settings: Readonly<Record<string, string>>;\n /** These are the sysctls of the namespace of the hop, as read back after the harness set them. */\n readonly sysctls: Readonly<Record<string, string>>;\n}\n\n/** A manifest is what a lab records about its machine, its images and its settings. */\nexport interface Manifest {\n readonly kernel: string;\n /** This text is the architecture of the machine, as uname -m gives it: x86_64. */\n readonly architecture: string;\n readonly hz: string | null;\n readonly boot_id: string;\n readonly docker: string;\n /** This record is the target, and its settings as it read them back. */\n readonly target: {\n readonly image: string;\n readonly id: string;\n readonly settings: Readonly<Record<string, string>>;\n /** These are the sysctls of its namespace, as read back after the harness set them. */\n readonly sysctls: Readonly<Record<string, string>>;\n };\n readonly harness: { readonly image: string; readonly id: string; readonly packages: string[] };\n /** This record is the client of each run: the TCP client of the harness, a driver, or an image that the cell gives. */\n readonly client: { readonly kind: Client | \"image\"; readonly image: string };\n /** These are the namespaces that the prober watched, by inode. */\n readonly netns: {\n readonly client: number;\n readonly target: number;\n readonly hops?: Readonly<Record<string, number>>;\n };\n /** These are the sysctls of the client namespace, as read back after the harness set them. */\n readonly sysctls: Readonly<Record<string, string>>;\n /** These are the hops of a topology, in sequence from the client. */\n readonly hops?: readonly ManifestHop[];\n}\n\n/** These are the JSON lines of a program, as they arrive. */\nexport interface Lines<T extends { readonly ev: string }> {\n readonly seen: T[];\n /** These are the other lines that it wrote, for example an error. They explain a program that stopped its output. */\n readonly other: string[];\n consume: (stream: NodeJS.ReadableStream) => void;\n /** This method gives the first line of one of these kinds, or it rejects after `within` ms. */\n until(kinds: readonly string[], within: number, who: string): Promise<T>;\n}\n\n/** This function collects the JSON lines of a program. It gives each line to `also` too, when the caller gives it. */\nexport const lines = <T extends { readonly ev: string }>(also?: (line: T) => void): Lines<T> => {\n const seen: T[] = [];\n const other: string[] = [];\n const waiting: Array<{ kinds: readonly string[]; resolve: (line: T) => void }> = [];\n let partial = \"\";\n const take = (text: string) => {\n if (!text.startsWith(\"{\")) {\n if (text.length > 0) other.push(text);\n return;\n }\n const line = JSON.parse(text) as T;\n seen.push(line);\n also?.(line);\n for (const wait of waiting.splice(0)) {\n if (wait.kinds.includes(line.ev)) wait.resolve(line);\n else waiting.push(wait);\n }\n };\n return {\n seen,\n other,\n consume: (stream) =>\n stream.on(\"data\", (chunk: Buffer | string) => {\n partial += chunk.toString();\n const split = partial.split(\"\\n\");\n partial = split.pop() ?? \"\";\n for (const text of split) take(text.trim());\n }),\n until(kinds, within, who) {\n const found = seen.find((line) => kinds.includes(line.ev));\n if (found !== undefined) return Promise.resolve(found);\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n const said = other.length === 0 ? \"\" : `; it wrote: ${other.slice(-3).join(\" | \")}`;\n reject(new Error(`no ${kinds.join(\" or \")} from the ${who} in ${within} ms${said}`));\n }, within);\n waiting.push({\n kinds,\n resolve: (line) => {\n clearTimeout(timer);\n resolve(line);\n },\n });\n });\n },\n };\n};\n\n/** A probe is the client container of one run. */\nexport interface Probe {\n /** This method gives the first event of one of these kinds, or it rejects after `within` ms. */\n until(kinds: readonly string[], within: number): Promise<ProbeEvent>;\n /** This method lets an in-flight probe send its request. */\n go(): Promise<void>;\n /** This method freezes the client with the cgroup freezer: its process stops, and its kernel continues to answer. */\n freeze(): Promise<void>;\n thaw(): Promise<void>;\n /** This method stops the process of the client with SIGKILL. Its kernel then closes its socket with a FIN, or a reset. */\n kill(): Promise<void>;\n readonly events: readonly ProbeEvent[];\n stop(): Promise<void>;\n}\n\n/** An agent is a container that a lab starts in the namespaces of the target, for example a Node process that signals the server. */\nexport interface Agent {\n /** These are the JSON lines that it wrote, as they arrive. */\n readonly lines: Lines<{ readonly ev: string }>;\n /** This method starts a command in the agent, and gives its standard output. */\n exec(argv: readonly string[]): Promise<string>;\n stop(): Promise<void>;\n}\n\n/** A lab is one environment: the target, the client, the hops of a topology, the injectors and the prober. */\nexport interface Lab {\n /** This address is the address that the client connects to: the target, or the first proxy of a topology. */\n readonly targetIp: string;\n /** This port is the port that the client connects to. */\n readonly targetPort: number;\n readonly clientIp: string;\n readonly netns: {\n readonly client: number;\n readonly target: number;\n readonly hops?: Readonly<Record<string, number>>;\n };\n /** This record is where each socket of a topology is, or null for a direct pair. */\n readonly topology: RunTopology | null;\n /** These are the lines of the routers of a topology, in sequence, each with the name of its hop. A direct pair has none. */\n readonly middlebox: readonly MiddleboxEvent[];\n /** This method adds a toxic to the Toxiproxy hop of a topology, or removes it with null. The stamps of the injector bracket it. */\n toxic(toxic: Toxic | null): Promise<{ t0: number; t1: number }>;\n /** This method makes the target close the connection of each client, with the command of the target. */\n serverClose(): Promise<void>;\n /**\n * This method starts an agent in the network namespace of the target, and in its process\n * namespace with `pid`. Its command keeps it up, for example `sleep infinity`.\n */\n agent(image: string, command: readonly string[], options?: { readonly pid?: boolean }): Promise<Agent>;\n /** These are all the records that the prober wrote, in sequence. */\n readonly kernel: readonly KernelRecord[];\n /** This method sets the sysctls of the client namespace through its privileged sidecar, and reads them back. */\n sysctl(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /** This method sets the settings of the target in it before a client connects, and reads them back. */\n configure(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /** This method sets the sysctls of the target namespace through a privileged sidecar there, and reads them back. */\n targetSysctl(values: Readonly<Record<string, string>>): Promise<Record<string, string>>;\n /**\n * This method applies a fault between the client and the target, by default from the namespace\n * of the target. The fault is a black hole, or a reset that answers the next segment of the\n * client. At a proxy of a topology, the rules face the peer of the proxy on one side. The stamps\n * of the injector bracket it.\n */\n fault(\n kind: NetworkFault,\n at?: FaultPlace,\n options?: FaultOptions,\n ): Promise<{ t0: number; t1: number }>;\n /** This method removes a fault. */\n lift(kind: NetworkFault, at?: FaultPlace, options?: FaultOptions): Promise<void>;\n /**\n * This method puts tc netem on the egress of a namespace, for example \"delay 20ms\" or \"loss 5%\",\n * or removes it with null. At a hop, it is the egress of the device of the hop toward one side.\n * The stamps of the injector bracket it.\n */\n netem(spec: string | null, at?: FaultPlace): Promise<{ t0: number; t1: number }>;\n /**\n * This method sets the TSO, GSO and GRO of a namespace. With them off, netem and a length match\n * act on segments, not on GSO super-packets of up to 64 KB. At a hop, it sets the two devices of the hop.\n */\n offload(on: boolean, at?: FaultPlace): Promise<void>;\n /** This method stops the server process in the target with SIGKILL, with the command of the target. */\n kill(): Promise<void>;\n /**\n * This method resolves the neighbors of the path in the two directions, with a ping from each\n * namespace. Without it, the first connection of an environment waits for ARP. Under a netem\n * delay, that wait is one more delayed round, and its round trip and RTO are longer.\n */\n warm(): Promise<void>;\n /** This method runs commands in the target before a client connects, each as argv. */\n setup(commands: ReadonlyArray<readonly string[]>): Promise<void>;\n /**\n * This method stops a black hole at an instant on the monotonic clock of the VM. The injector\n * waits for that instant in the VM, thus the length of a stall does not include the latency of\n * a docker exec. Stamps bracket it, as for a fault.\n */\n heal(at: FaultPlace, when: number): Promise<{ t0: number; t1: number }>;\n /**\n * This method removes what the two kernels learned from their earlier connections. These are\n * the TCP metrics that each kernel keeps for a peer address when a connection closes. These metrics set the\n * first round trip of the next connection, and thus its RTO. A run with these metrics is not\n * independent of the run before it.\n */\n forget(): Promise<void>;\n /** This method freezes the target with the cgroup freezer: its process stops, and its kernel continues to answer. */\n freeze(): Promise<void>;\n thaw(): Promise<void>;\n probe(args: readonly string[]): Promise<Probe>;\n /**\n * This method waits for the next clock line of the prober, which gives the count of lost\n * records. It gives null when no clock line comes in 5 s, because then nothing tells the count.\n */\n settle(): Promise<number | null>;\n manifest(\n sysctls: Readonly<Record<string, string>>,\n settings?: Readonly<Record<string, string>>,\n targetSysctls?: Readonly<Record<string, string>>,\n ): Promise<Manifest>;\n close(): Promise<void>;\n}\n\n/** This container class can share the process namespace of another container. */\nconst shared = (Base: typeof Testcontainers.GenericContainer) =>\n class extends Base {\n withPidMode(mode: string): this {\n (this as unknown as { hostConfig: { PidMode?: string } }).hostConfig.PidMode = mode;\n return this;\n }\n };\n\n/** This function gives the content of a file of a proxy, with the address of the next hop for each {{next}}. */\nexport const templated = (content: string, next: string): string => content.replaceAll(\"{{next}}\", next);\n\n/** This function gives the JSON body of a toxic for the API of Toxiproxy. Each toxic of a cell has the name `fault`. */\nexport const toxicBody = (toxic: Toxic): string =>\n JSON.stringify({\n name: \"fault\",\n type: toxic.type,\n stream: toxic.stream,\n toxicity: 1,\n attributes: toxic.attributes,\n });\n\n/**\n * This function gives the routes of a chain: for each router, the networks after it, which the\n * element before it reaches through it. Element 0 is the client, and element k is hop k - 1. Network\n * j is the network between element j and element j + 1.\n */\nexport const routesOf = (\n hops: readonly Pick<Hop, \"kind\">[],\n): Array<{ readonly from: number; readonly via: number; readonly networks: readonly number[] }> =>\n hops.flatMap((hop, at) =>\n hop.kind === \"router\"\n ? [\n {\n from: at,\n via: at + 1,\n networks: Array.from({ length: hops.length - at }, (_, k) => at + 1 + k),\n },\n ]\n : [],\n );\n\n/**\n * This function gives the endpoint that an element connects to: the first element after it that\n * is not a router. Element 0 is the client, element k is hop k - 1, and the last element is the target.\n */\nexport const endpointAfter = (hops: readonly Pick<Hop, \"kind\">[], element: number): number => {\n let at = element + 1;\n while (at <= hops.length && hops[at - 1]!.kind === \"router\") at += 1;\n return at;\n};\n\n// each container here is a holder, a probe or the prober: nothing drains, thus the stop is immediate\nexport const STOP = { timeout: 0 };\n\nexport const output = async (container: StartedTestContainer, argv: string[]): Promise<string> => {\n const result = await container.exec(argv);\n if (result.exitCode !== 0)\n throw new Error(`${argv.join(\" \")} exited ${result.exitCode}: ${result.output.trim()}`);\n return result.stdout.trim();\n};\n\n/** This function gives the inode of the network namespace of a container: net:[4026532583]. */\nexport const netnsOf = async (container: StartedTestContainer): Promise<number> => {\n const link = await output(container, [\"readlink\", \"/proc/self/ns/net\"]);\n const inode = /^net:\\[(\\d+)\\]$/.exec(link)?.[1];\n if (inode === undefined) throw new Error(`not a network namespace: ${link}`);\n return Number(inode);\n};\n\n/** This function gives the image and the command of the client of each run, and its kind for the manifest. */\nexport const clientOf = async (\n kind: Client | ClientImage,\n image: string,\n): Promise<{ clientImage: string; command: readonly string[]; clientKind: Client | \"image\" }> => {\n if (typeof kind === \"object\") return { clientImage: kind.image, command: kind.command, clientKind: \"image\" };\n if (kind === \"ioredis\")\n return {\n clientImage: await nodeClientImage(),\n command: [\"node\", \"/opt/harness/node/client-ioredis.mjs\"],\n clientKind: kind,\n };\n return { clientImage: image, command: [\"/opt/harness/client\"], clientKind: kind };\n};\n\n/** These are the containers of one hop of a lab, and the addresses that its rules and the measurement read. */\ninterface HopIn {\n readonly hop: Hop;\n /** This container is the hop itself: the router, or the stock proxy. */\n readonly box: StartedTestContainer;\n /** This sidecar shares the namespace of the hop: the injector of its faults and its toxics. */\n readonly injector: StartedTestContainer;\n /** This record is the address of the hop on the network of each of its sides. */\n readonly ip: { readonly client: string; readonly target: string };\n /** These are the peers of the hop: an address on the side of the client, and an address with its port toward the target. */\n readonly peer: { readonly client: string; readonly target: string };\n /** These are the devices of the hop toward each side, as its routes give them. The rules of a router and netem at a hop face one. */\n readonly devices: { readonly client: string; readonly target: string };\n readonly netns: number;\n sysctls: Record<string, string>;\n}\n\n/** This function gives the device of a route from its line in `ip -o route get`: \"10.0.0.2 dev eth0 src …\". */\nexport const deviceOf = (route: string): string => {\n const device = /\\bdev (\\S+)/.exec(route)?.[1];\n if (device === undefined) throw new Error(`no device in the route: ${route}`);\n return device;\n};\n\n/**\n * This function opens one environment:\n * - a chain of new networks, and the stock target on the last one. A direct pair has one network.\n * - a client that holds its own network namespace, with a privileged sidecar in it for its\n * sysctls and its routes. Docker Desktop keeps /proc/sys read-only in a container that operates\n * without privileges.\n * - each hop of the topology, on the network of each of its sides. A hop is a router, or a stock\n * proxy with an injector sidecar in its namespace.\n * - an injector sidecar in the namespace of the target.\n * - the prober, a privileged container that loads the kernel program and watches the namespace of\n * the client, of the target and of each hop.\n *\n * The client of each run is its own container in the namespace of the client. It is the TCP\n * client of the harness, a driver from its own image (ioredis on Node), or an image that the\n * caller gives. Each prints the same lines.\n *\n * The client reaches the networks after a router through that router, by a route that its\n * privileged sidecar adds. A router translates the address of the side of the client, thus the\n * target and each later hop see the router as the peer.\n */\nexport const openLab = async (\n target: Target,\n kind: Client | ClientImage = \"tcp\",\n topology?: Topology,\n): Promise<Lab> => {\n const image = await harnessImage();\n const { clientImage, command, clientKind } = await clientOf(kind, image);\n const hops = topology?.hops ?? [];\n const { GenericContainer, Network, Wait, getContainerRuntimeClient } = await containers();\n const runtime = await getContainerRuntimeClient();\n const opened: StartedTestContainer[] = [];\n const networks: StartedNetwork[] = [];\n const close = async () => {\n for (const container of opened.reverse()) await container.stop(STOP).catch(() => undefined);\n for (const network of networks.reverse()) await network.stop().catch(() => undefined);\n };\n /** This function gives the address of a container on a network, as Docker has it at this time. A container can join a network after its start. */\n const ipOn = async (container: StartedTestContainer, network: StartedNetwork): Promise<string> => {\n const info = await runtime.container.inspect(runtime.container.getById(container.getId()));\n const ip = info.NetworkSettings.Networks[network.getName()]?.IPAddress;\n if (ip === undefined || ip === \"\") throw new Error(`${container.getName()} has no address on ${network.getName()}`);\n return ip;\n };\n const join = (container: StartedTestContainer, network: StartedNetwork) =>\n runtime.container.connectToNetwork(\n runtime.container.getById(container.getId()),\n runtime.network.getById(network.getId()),\n [],\n );\n const subnetOf = async (network: StartedNetwork): Promise<string> => {\n const info = await runtime.network.getById(network.getId()).inspect();\n const subnet = (info as { IPAM?: { Config?: Array<{ Subnet?: string }> } }).IPAM?.Config?.[0]?.Subnet;\n if (subnet === undefined) throw new Error(`${network.getName()} has no subnet`);\n return subnet;\n };\n try {\n // network j joins element j and element j + 1: 0 is the client, i + 1 is hop i, the last is the target\n for (let at = 0; at <= hops.length; at += 1) networks.push(await new Network().start());\n const last = networks[hops.length]!;\n const stock = new GenericContainer(target.image)\n .withNetwork(last)\n .withWaitStrategy(Wait.forLogMessage(target.ready));\n // a holder command keeps the container up when a fin fault kills the server process\n const server = await (\n target.command === undefined ? stock : stock.withCommand([...target.command])\n ).start();\n opened.push(server);\n const serverIp = await ipOn(server, last);\n\n // the hops, from the target to the client: each proxy gets the address of the endpoint after it\n const middlebox: MiddleboxEvent[] = [];\n const inChain: HopIn[] = [];\n const endpointOf = (element: number): string => {\n const at = endpointAfter(hops, element);\n if (at === hops.length + 1) return `${serverIp}:${target.port}`;\n const proxy = inChain[at - 1]!;\n return `${proxy.ip.client}:${(proxy.hop as ProxyHop).port}`;\n };\n for (let at = hops.length - 1; at >= 0; at -= 1) {\n const hop = hops[at]!;\n const [clientSide, targetSide] = [networks[at]!, networks[at + 1]!];\n if (hop.kind === \"router\") {\n const seen = lines<MiddleboxEvent>((line) => middlebox.push({ ...line, hop: hop.name }));\n // the router is privileged: it sets the sysctls of its own namespace, conntrack's among them\n const box = await new GenericContainer(await routerImage())\n .withNetwork(clientSide)\n .withPrivilegedMode()\n .withEnvironment({\n FORM: hop.form,\n IDLE: String(valueIn(hop.idle, \"s\")),\n PUMP: String(valueIn(hop.pump ?? PUMP, \"s\")),\n SLOW: String(valueIn(LISTING, \"s\")),\n })\n .withLogConsumer(seen.consume)\n .start();\n opened.push(box);\n // the router waits for the network of the side of the target, its eth1\n await join(box, targetSide);\n const ready = await seen.until([\"mbx-ready\"], 30_000, `router ${hop.name}`);\n await seen.until(hop.form === \"reset-both\" ? [\"resetter\"] : [\"ct-watch\"], 30_000, `router ${hop.name}`);\n // the injector of a fault at the router: FORWARD rules and netem in its namespace\n const injector = await new GenericContainer(image)\n .withNetworkMode(`container:${box.getId()}`)\n .withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\")\n .start();\n opened.push(injector);\n inChain[at] = {\n hop,\n box,\n injector,\n ip: { client: await ipOn(box, clientSide), target: await ipOn(box, targetSide) },\n peer: { client: \"\", target: endpointOf(at + 1) },\n devices: { client: \"\", target: \"\" },\n netns: await netnsOf(box),\n sysctls: { \"net.netfilter.nf_conntrack_tcp_timeout_established\": String(ready[\"idle\"]) },\n };\n continue;\n }\n const next = endpointOf(at + 1);\n let proxy = new GenericContainer(hop.image).withNetwork(targetSide);\n if (hop.command !== undefined) proxy = proxy.withCommand([...hop.command]);\n if (hop.files !== undefined)\n proxy = proxy.withCopyContentToContainer(\n Object.entries(hop.files).map(([path, content]) => ({ content: templated(content, next), target: path })),\n );\n if (hop.ready !== undefined) proxy = proxy.withWaitStrategy(Wait.forLogMessage(hop.ready));\n const box = await proxy.start();\n opened.push(box);\n await join(box, clientSide);\n const injector = await new GenericContainer(image)\n .withNetworkMode(`container:${box.getId()}`)\n .withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\")\n .start();\n opened.push(injector);\n inChain[at] = {\n hop,\n box,\n injector,\n ip: { client: await ipOn(box, clientSide), target: await ipOn(box, targetSide) },\n peer: { client: \"\", target: next },\n devices: { client: \"\", target: \"\" },\n netns: await netnsOf(injector),\n sysctls: {},\n };\n }\n\n const client = await new GenericContainer(image).withNetwork(networks[0]!).start();\n opened.push(client);\n const admin = await new GenericContainer(image)\n .withNetworkMode(`container:${client.getId()}`)\n .withPrivilegedMode()\n .start();\n opened.push(admin);\n const injector = await new GenericContainer(image)\n .withNetworkMode(`container:${server.getId()}`)\n .withAddedCapabilities(\"NET_ADMIN\", \"NET_RAW\")\n .start();\n opened.push(injector);\n const clientIp = await ipOn(client, networks[0]!);\n // the peer of each hop on the side of the client: the element before it, on the network between them\n for (let at = 0; at < inChain.length; at += 1) {\n const before = at === 0 ? clientIp : inChain[at - 1]!.ip.target;\n inChain[at] = { ...inChain[at]!, peer: { ...inChain[at]!.peer, client: before } };\n }\n\n // a privileged sidecar for each proxy that needs one, for its sysctls and its routes\n const admins = new Map<number, StartedTestContainer>();\n const adminOf = async (element: number): Promise<StartedTestContainer> => {\n if (element === 0) return admin;\n const hop = inChain[element - 1]!;\n if (hop.hop.kind === \"router\") return hop.box;\n let found = admins.get(element);\n if (found === undefined) {\n found = await new GenericContainer(image)\n .withNetworkMode(`container:${hop.box.getId()}`)\n .withPrivilegedMode()\n .start();\n opened.push(found);\n admins.set(element, found);\n }\n return found;\n };\n for (const route of routesOf(hops)) {\n const where = await adminOf(route.from);\n const via = inChain[route.via - 1]!.ip.client;\n for (const network of route.networks)\n await output(where, [\"ip\", \"route\", \"replace\", await subnetOf(networks[network]!), \"via\", via]);\n }\n for (let at = 0; at < inChain.length; at += 1) {\n const hop = inChain[at]!;\n // the device toward each side, as the routes of the hop give it\n const toward = async (address: string) =>\n deviceOf(await output(hop.injector, [\"ip\", \"-o\", \"route\", \"get\", address.split(\":\")[0]!]));\n inChain[at] = { ...hop, devices: { client: await toward(hop.peer.client), target: await toward(hop.peer.target) } };\n if (hop.hop.sysctls === undefined) continue;\n // a router is privileged and sets its own sysctls. A proxy has a privileged sidecar for them\n const where = await adminOf(at + 1);\n const settings = Object.entries(hop.hop.sysctls).map(([key, value]) => `${key}=${value}`);\n await output(where, [\"sysctl\", \"-q\", \"-w\", ...settings]);\n for (const key of Object.keys(hop.hop.sysctls)) hop.sysctls[key] = await output(where, [\"sysctl\", \"-n\", key]);\n }\n\n const netns = {\n client: await netnsOf(admin),\n target: await netnsOf(injector),\n ...(inChain.length === 0\n ? {}\n : { hops: Object.fromEntries(inChain.map((hop) => [hop.hop.name, hop.netns])) }),\n };\n const records = lines<KernelRecord>();\n const prober = await new GenericContainer(image)\n .withPrivilegedMode()\n .withNetworkMode(\"none\")\n .withCommand([\n \"/opt/harness/prober\",\n String(netns.client),\n String(netns.target),\n ...inChain.map((hop) => String(hop.netns)),\n ])\n .withLogConsumer(records.consume)\n .start();\n opened.push(prober);\n const begun = await records.until([\"begin\", \"fail\"], 30_000, \"prober\");\n if (begun.ev === \"fail\") throw new Error(`the prober failed: ${String(begun[\"why\"])}`);\n const first = endpointAfter(hops, 0);\n const targetIp = first === hops.length + 1 ? serverIp : inChain[first - 1]!.ip.client;\n const targetPort = first === hops.length + 1 ? target.port : (inChain[first - 1]!.hop as ProxyHop).port;\n const targetPeer = inChain.length === 0 ? clientIp : inChain.at(-1)!.ip.target;\n const placed: RunTopology | null =\n inChain.length === 0\n ? null\n : {\n targetPeer,\n hops: Object.fromEntries(\n inChain.map((hop) => [hop.hop.name, { netns: hop.netns, client: hop.peer.client, target: hop.peer.target }]),\n ),\n };\n const paused = runtime.container.getById(server.getId());\n let targetAdmin: StartedTestContainer | undefined;\n const hopNamed = (name: string): HopIn => {\n const found = inChain.find((hop) => hop.hop.name === name);\n if (found === undefined) throw new Error(`the topology has no hop ${name}`);\n return found;\n };\n /**\n * This function gives the namespace and the peer of the rules of a fault at a place, and the\n * other arguments of the injector. At a proxy, the rules are INPUT and OUTPUT rules, which act\n * on its sockets. A router forwards past INPUT and OUTPUT, thus its rules are FORWARD rules on\n * its device toward the side (`--via`).\n */\n const placeOf = (at: FaultPlace): [StartedTestContainer, string, string[]] => {\n if (at === \"client\") return [admin, targetIp, []];\n if (at === \"target\") return [injector, targetPeer, []];\n const hop = hopNamed(at.hop);\n const peer = at.toward === \"client\" ? hop.peer.client : hop.peer.target.split(\":\")[0]!;\n const via = hop.hop.kind === \"router\" ? [\"--via\", hop.devices[at.toward]] : [];\n return [hop.injector, peer, via];\n };\n /** This function gives the namespace and the device of netem at a place: an end, or the device of a hop toward a side. */\n const deviceAt = (at: FaultPlace): [StartedTestContainer, string] => {\n if (at === \"client\") return [admin, \"eth0\"];\n if (at === \"target\") return [injector, \"eth0\"];\n const hop = hopNamed(at.hop);\n return [hop.injector, hop.devices[at.toward]];\n };\n const toxiproxy = (): HopIn & { hop: ProxyHop & { api: { port: number; proxy: string } } } => {\n const found = inChain.find((hop) => hop.hop.kind === \"proxy\" && hop.hop.api !== undefined);\n if (found === undefined) throw new Error(\"the topology has no hop with the API of Toxiproxy\");\n return found as HopIn & { hop: ProxyHop & { api: { port: number; proxy: string } } };\n };\n const stamped = async (where: StartedTestContainer, argv: string[]) => {\n const stamp = JSON.parse((await output(where, argv)).split(\"\\n\").at(-1)!) as { t0: number; t1: number };\n return { t0: stamp.t0, t1: stamp.t1 };\n };\n const proxies = inChain.filter((hop) => hop.hop.kind === \"proxy\");\n\n return {\n targetIp,\n targetPort,\n clientIp,\n netns,\n topology: placed,\n middlebox,\n kernel: records.seen,\n async sysctl(values) {\n const settings = Object.entries(values).map(([key, value]) => `${key}=${value}`);\n if (settings.length > 0) await output(admin, [\"sysctl\", \"-q\", \"-w\", ...settings]);\n const read: Record<string, string> = {};\n for (const key of Object.keys(values))\n read[key] = await output(admin, [\"sysctl\", \"-n\", key]);\n return read;\n },\n async targetSysctl(values) {\n const read: Record<string, string> = {};\n if (Object.keys(values).length === 0) return read;\n // privileged, and only when a cell asks for it: otherwise the namespace of the target stays as it is\n targetAdmin ??= await new GenericContainer(image)\n .withNetworkMode(`container:${server.getId()}`)\n .withPrivilegedMode()\n .start();\n opened.push(targetAdmin);\n const settings = Object.entries(values).map(([key, value]) => `${key}=${value}`);\n await output(targetAdmin, [\"sysctl\", \"-q\", \"-w\", ...settings]);\n for (const key of Object.keys(values))\n read[key] = await output(targetAdmin, [\"sysctl\", \"-n\", key]);\n return read;\n },\n async configure(values) {\n const read: Record<string, string> = {};\n if (Object.keys(values).length === 0) return read;\n const how = target.settings;\n if (how === undefined) throw new Error(`${target.image} names no way to set its settings`);\n for (const [name, value] of Object.entries(values)) {\n await output(server, [...how.set, name, value]);\n read[name] = (await output(server, [...how.get, name])).split(\"\\n\").at(-1) ?? \"\";\n }\n return read;\n },\n async fault(kind, at = \"target\", options = {}) {\n const [where, peer, via] = placeOf(at);\n return stamped(where, [\"/opt/harness/inject\", kind, peer, ...optionArgs(options), ...via]);\n },\n async lift(kind, at = \"target\", options = {}) {\n const [where, peer, via] = placeOf(at);\n await output(where, [\"/opt/harness/inject\", LIFT[kind], peer, ...optionArgs(options), ...via]);\n },\n async netem(spec, at = \"target\") {\n const [where, device] = deviceAt(at);\n const args =\n spec === null\n ? [\"/opt/harness/inject\", \"clear-netem\", device]\n : [\"/opt/harness/inject\", \"netem\", device, ...spec.split(/\\s+/)];\n return stamped(where, args);\n },\n async offload(on, at = \"target\") {\n // at a hop, the two devices: a router merges what it receives (GRO) and forwards it whole\n const devices =\n typeof at === \"object\" ? Object.values(hopNamed(at.hop).devices) : [deviceAt(at)[1]];\n const [where] = deviceAt(at);\n for (const device of devices)\n await output(where, [\"/opt/harness/inject\", \"offload\", device, on ? \"on\" : \"off\"]);\n },\n async toxic(toxic) {\n const hop = toxiproxy();\n const api = `127.0.0.1:${hop.hop.api.port}`;\n const path = `/proxies/${hop.hop.api.proxy}/toxics`;\n return stamped(\n hop.injector,\n toxic === null\n ? [\"/opt/harness/inject\", \"http\", \"DELETE\", api, `${path}/fault`]\n : [\"/opt/harness/inject\", \"http\", \"POST\", api, path, toxicBody(toxic)],\n );\n },\n async serverClose() {\n if (target.close === undefined) throw new Error(`${target.image} names no way to close its clients`);\n await output(server, [...target.close]);\n },\n async agent(agentImage, argv, options = {}) {\n const Shared = shared(GenericContainer);\n const seen = lines<{ readonly ev: string }>();\n let started = new Shared(agentImage)\n .withNetworkMode(`container:${server.getId()}`)\n .withCommand([...argv])\n .withLogConsumer(seen.consume);\n if (options.pid === true) started = started.withPidMode(`container:${server.getId()}`);\n const container = await started.start();\n opened.push(container);\n return {\n lines: seen,\n exec: (args) => output(container, [...args]),\n stop: () =>\n container\n .stop(STOP)\n .then(() => undefined)\n .catch(() => undefined),\n };\n },\n async kill() {\n if (target.kill === undefined) throw new Error(`${target.image} names no way to kill its server`);\n await output(server, [...target.kill]);\n },\n async warm() {\n await output(admin, [\"ping\", \"-c\", \"2\", \"-W\", \"2\", targetIp]);\n await output(injector, [\"ping\", \"-c\", \"2\", \"-W\", \"2\", targetPeer]);\n for (const hop of proxies)\n await output(hop.injector, [\"ping\", \"-c\", \"2\", \"-W\", \"2\", hop.peer.target.split(\":\")[0]!]);\n },\n async setup(commands) {\n for (const argv of commands) await output(server, [...argv]);\n },\n async heal(at, when) {\n const [where, peer, via] = placeOf(at);\n return stamped(where, [\"/opt/harness/inject\", \"clear\", peer, \"--at\", String(Math.round(when)), ...via]);\n },\n async forget() {\n await output(admin, [\"ip\", \"tcp_metrics\", \"flush\", \"all\"]);\n await output(injector, [\"ip\", \"tcp_metrics\", \"flush\", \"all\"]);\n for (const hop of proxies) await output(hop.injector, [\"ip\", \"tcp_metrics\", \"flush\", \"all\"]);\n },\n freeze: () => paused.pause().then(() => undefined),\n thaw: () => paused.unpause().then(() => undefined),\n async probe(args) {\n const events = lines<ProbeEvent>();\n const container = await new GenericContainer(clientImage)\n .withNetworkMode(`container:${client.getId()}`)\n .withCommand([...command, ...args])\n .withLogConsumer(events.consume)\n .start();\n return {\n events: events.seen,\n until: (kinds, within) => events.until(kinds, within, \"client\"),\n go: () => output(container, [\"touch\", \"/tmp/go\"]).then(() => undefined),\n freeze: () =>\n runtime.container\n .getById(container.getId())\n .pause()\n .then(() => undefined),\n thaw: () =>\n runtime.container\n .getById(container.getId())\n .unpause()\n .then(() => undefined),\n kill: () =>\n runtime.container\n .getById(container.getId())\n .kill({ signal: \"SIGKILL\" })\n .then(() => undefined),\n // a killed client has no container to stop\n stop: () =>\n container\n .stop(STOP)\n .then(() => undefined)\n .catch(() => undefined),\n };\n },\n async settle() {\n // the prober drains its ring each 20 ms, and it writes a clock line each second\n const after = records.seen.length;\n const clock = await new Promise<KernelRecord | null>((resolve) => {\n const started = Date.now();\n const look = () => {\n const line = records.seen.slice(after).find((record) => record.ev === \"clock\");\n if (line !== undefined) resolve(line);\n else if (Date.now() - started > 5_000) resolve(null);\n else setTimeout(look, 50);\n };\n look();\n });\n return clock === null ? null : Number(clock[\"lost\"] ?? 0);\n },\n async manifest(sysctls, settings = {}, targetSysctls = {}) {\n const config = await output(admin, [\"zcat\", \"/proc/config.gz\"]).catch(() => \"\");\n const inspect = async (container: StartedTestContainer) =>\n runtime.container.inspect(runtime.container.getById(container.getId()));\n const hopsOf = async (): Promise<ManifestHop[]> => {\n const out: ManifestHop[] = [];\n for (const hop of inChain) {\n const box = hop.hop;\n out.push({\n name: box.name,\n kind: box.kind,\n image: box.kind === \"router\" ? await routerImage() : box.image,\n id: (await inspect(hop.box)).Image,\n settings:\n box.kind === \"router\"\n ? {\n form: box.form,\n idle: String(valueIn(box.idle, \"s\")),\n pump: String(valueIn(box.pump ?? PUMP, \"s\")),\n slow: String(valueIn(LISTING, \"s\")),\n packages: (await output(hop.box, [\"apk\", \"info\", \"-v\"])).split(\"\\n\").sort().join(\" \"),\n }\n : { ...box.settings, ...box.files },\n sysctls: hop.sysctls,\n });\n }\n return out;\n };\n return {\n kernel: await output(client, [\"uname\", \"-r\"]),\n architecture: await output(client, [\"uname\", \"-m\"]),\n hz: /^CONFIG_HZ=(\\d+)$/m.exec(config)?.[1] ?? null,\n boot_id: await output(client, [\"cat\", \"/proc/sys/kernel/random/boot_id\"]),\n docker: `${runtime.info.containerRuntime.serverVersion} on ${runtime.info.containerRuntime.operatingSystem}`,\n target: {\n image: target.image,\n id: (await inspect(server)).Image,\n settings,\n sysctls: targetSysctls,\n },\n harness: {\n image,\n id: (await inspect(client)).Image,\n packages: (await output(client, [\"apk\", \"info\", \"-v\"])).split(\"\\n\").sort(),\n },\n client: { kind: clientKind, image: clientImage },\n netns,\n sysctls,\n ...(inChain.length === 0 ? {} : { hops: await hopsOf() }),\n };\n },\n close,\n };\n } catch (error) {\n await close();\n throw error;\n }\n};\n","import { valueIn } from \"@mark1russell7/quantity\";\nimport type { Cell, FaultPlace, KernelRecord, ProbeEvent, Run } from \"./cell.ts\";\nimport type { FaultOptions, Lab, Manifest, Probe } from \"./lab.ts\";\nimport { openLab } from \"./lab.ts\";\nimport type { Measured } from \"./observe.ts\";\nimport { measure, socketOf } from \"./observe.ts\";\nimport type { Verdict } from \"./verdict.ts\";\nimport { STEPS, verdictOf } from \"./verdict.ts\";\n\n/** A cell result is a cell with its manifests, its runs, their measurements and its verdict. */\nexport interface CellResult {\n readonly cell: Cell;\n readonly manifests: readonly Manifest[];\n readonly runs: readonly Run[];\n /** These are the warm-up runs, one for each environment before its counted runs. The verdict does not count them. */\n readonly warmups: readonly Run[];\n readonly measured: readonly Measured[];\n readonly verdict: Verdict;\n /** These times are when its first environment opened and its last environment closed, on the wall clock. */\n readonly at: { readonly started: string; readonly ended: string };\n}\n\n/**\n * This function measures and judges the raw records of a cell, with the code of the harness at\n * this time. `runCell` uses it during a cell. A holder of the raw records of a cell can use it\n * again later. Thus the records get a new analysis after a change to observe.ts or to the verdict.\n */\nexport const judge = (\n cell: Cell,\n runs: readonly Run[],\n): { measured: Measured[]; verdict: Verdict } => {\n // a warm-up run is a record, not a sample: the verdict never counts it\n const measured = runs.filter((run) => run.warmup !== true).map((run) => measure(cell, run));\n return { measured, verdict: verdictOf(cell.prediction, measured) };\n};\n\n/** This function gives the count of lost records in the latest clock line of the prober, since it began. */\nconst droppedSoFar = (records: readonly KernelRecord[]): number => {\n for (let at = records.length - 1; at >= 0; at -= 1)\n if (records[at]!.ev === \"clock\") return Number(records[at]![\"lost\"] ?? 0);\n return 0;\n};\n\n/** This function gives the argv of the client for a cell, with a deadline far after all that it waits for. */\nconst probeArgs = (cell: Cell, lab: Lab): string[] => {\n const { prediction, probe } = cell;\n const longest =\n (prediction.detects ? valueIn(prediction.high, \"s\") : valueIn(prediction.window, \"s\")) +\n (cell.idleBefore === undefined ? 0 : valueIn(cell.idleBefore, \"s\"));\n const args = [lab.targetIp, String(lab.targetPort), \"--mode\", probe.mode];\n if (probe.request !== undefined) args.push(\"--request\", probe.request);\n if (probe.count !== undefined) args.push(\"--count\", String(probe.count));\n if (probe.interval !== undefined) args.push(\"--interval-ms\", String(valueIn(probe.interval, \"ms\")));\n if (probe.driver !== undefined) {\n // a driver sets its socket options from its own options: the keepAlive of ioredis\n args.push(\"--keepalive-ms\", String(valueIn(probe.driver.keepAlive, \"ms\")));\n args.push(\"--max-s\", String(Math.ceil(longest * 2 + 10)));\n return args;\n }\n if (probe.keepalive) args.push(\"--keepalive\");\n if (probe.userTimeout !== undefined)\n args.push(\"--user-timeout-ms\", String(valueIn(probe.userTimeout, \"ms\")));\n args.push(\"--max-s\", String(Math.ceil(longest * 2 + 10)));\n return args;\n};\n\n/**\n * This function waits for an error on the socket of the observer, or for its end of file\n * (CLOSE_WAIT, state 8). The prober records both. A cell whose observer is the server, or a proxy\n * of a topology, waits for that. The promise resolves after `within` ms at the latest.\n */\nconst observerError = (\n cell: Cell,\n partial: () => Run,\n within: number,\n): Promise<void> =>\n new Promise((resolve) => {\n const started = Date.now();\n const look = () => {\n const run = partial();\n const at = socketOf(cell, run);\n const seen =\n at !== undefined &&\n run.kernel.some(\n (record) =>\n record.netns === at.netns &&\n record.cookie === at.cookie &&\n ((record.ev === \"error\" && (record.err ?? 0) !== 0) ||\n (record.ev === \"state\" && (record as { readonly new?: number }).new === 8)),\n );\n if (seen || Date.now() - started > within) resolve();\n else setTimeout(look, 50);\n };\n look();\n });\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * This function waits for the request that an in-flight client sends on the signal: its second\n * send, after the PING that made it ready. Its stamp is when the request first met the fault.\n */\nconst requestSent = (probe: Probe, within: number): Promise<ProbeEvent> =>\n new Promise((resolve, reject) => {\n const started = Date.now();\n const look = () => {\n const sent = probe.events.filter((event) => event.ev === \"sent\")[1];\n if (sent !== undefined) resolve(sent);\n else if (Date.now() - started > within)\n reject(new Error(`the client sent no request within ${within} ms of being told to`));\n else setTimeout(look, 1);\n };\n look();\n });\n\n/** This function gives the netem specification of a cell: its latency, then the netem of its fault when it has one. */\nconst netemSpec = (cell: Cell, fault: string | undefined): string | null => {\n const parts = [\n ...(cell.latency === undefined ? [] : [`delay ${valueIn(cell.latency, \"ms\")}ms`]),\n ...(fault === undefined ? [] : [fault]),\n ];\n return parts.length === 0 ? null : parts.join(\" \");\n};\n\n/**\n * This function does one run. The client connects and gets its reply, the fault occurs, and an\n * in-flight client sends into it. The harness then waits for the observer to see the fault, or for\n * the silent window to end. The observer is the client, or the socket of the target for a cell on\n * the server side. It can also be a socket of a proxy of a topology.\n *\n * A stall stops by itself, at its length after the send of the request, and the\n * run waits for the reply. Then the harness removes the fault, thus the environment can serve the\n * next run. The run keeps the records of the prober from its start to its end. The frozen process\n * is the peer of the observer: the target, or the client for the server.\n *\n * A dry run does all of this but the fault: the warm-up of a cell whose fault ends its environment.\n */\nconst once = async (\n lab: Lab,\n cell: Cell,\n environment: number,\n index: number,\n dry = false,\n): Promise<Run> => {\n // each run starts from kernels that keep nothing from the runs before it\n await lab.forget();\n const from = lab.kernel.length;\n const middleboxFrom = lab.middlebox.length;\n const lostBefore = droppedSoFar(lab.kernel);\n const probe = await lab.probe(probeArgs(cell, lab));\n const server = cell.observer === \"server\";\n const peer = server\n ? { freeze: () => probe.freeze(), thaw: () => probe.thaw() }\n : { freeze: () => lab.freeze(), thaw: () => lab.thaw() };\n // a client that stopped before the signal to send tells why in its last lines\n const go = () =>\n probe.go().catch((error: unknown) => {\n const last = probe.events.slice(-3).map((event) => JSON.stringify(event));\n throw new Error(`${(error as Error).message}; the client's last lines: ${last.join(\" \")}`);\n });\n const placed = lab.topology === null ? {} : { topology: lab.topology };\n /** This function gives the run as the records show it so far: the waits read it. */\n const partial = (): Run => ({\n environment,\n index,\n events: probe.events,\n fault: null,\n kernel: lab.kernel.slice(from),\n netns: lab.netns.client,\n targetNetns: lab.netns.target,\n lost: 0,\n ...placed,\n });\n let fault: Run[\"fault\"] = null;\n let heal: { t0: number; t1: number } | null = null;\n try {\n await probe.until([\"ready\"], 15_000);\n if (dry) {\n if (cell.probe.mode !== \"idle\") await go();\n await sleep(500);\n } else {\n // the in-flight data of a run on the server side is the reply of the server. The request goes\n // out first, and the fault occurs while the server holds it (the timeout of BLPOP). A fin with\n // a request in flight kills the server while it holds the request, so it goes first too\n const replyInFlight =\n cell.probe.mode === \"inflight\" &&\n ((server && cell.fault !== \"mtu-blackhole\") || cell.fault === \"fin\");\n if (replyInFlight) {\n // the client sends one sample after the file appears. The request reaches the server well\n // in the pause, and the reply waits for the timeout of the request\n await go();\n await sleep(200);\n }\n const faultAt: FaultPlace = cell.faultAt ?? (server ? \"client\" : \"target\");\n // the direction of a simplex drop, relative to the namespace of the rule: in the target, or at\n // a proxy toward the client, the segments to the server come in; in the client, they go out\n const towardServerComesIn =\n faultAt === \"target\" || (typeof faultAt === \"object\" && faultAt.toward === \"client\");\n const direction: FaultOptions[\"direction\"] =\n cell.drops === undefined\n ? undefined\n : (cell.drops === \"to-server\") === towardServerComesIn\n ? \"in\"\n : \"out\";\n const options: FaultOptions = {\n ...(direction === undefined ? {} : { direction }),\n ...(cell.icmp === undefined ? {} : { code: cell.icmp }),\n ...(cell.mtu === undefined ? {} : { above: cell.mtu }),\n };\n // a toxic of a cell whose fault is another one goes first: a slow close, then the close\n const toxicFirst = cell.toxic !== undefined && cell.fault !== \"toxic\";\n if (toxicFirst) await lab.toxic(cell.toxic!);\n // netem at the target shares its qdisc with the latency of the cell. At a hop, it is alone\n const netemAt: FaultPlace = cell.faultAt ?? \"target\";\n const netemOn = (fault: string | undefined) =>\n netemAt === \"target\" ? lab.netem(netemSpec(cell, fault), \"target\") : lab.netem(fault ?? null, netemAt);\n // an MTU drop goes where the long reply arrives: the namespace of the client\n const apply = async (): Promise<Run[\"fault\"]> => {\n switch (cell.fault) {\n case \"freeze-process\":\n await peer.freeze();\n return null;\n case \"fin\":\n await (server ? probe.kill() : lab.kill());\n return null;\n case \"unreachable\":\n return lab.fault(\"reject\", faultAt, { code: cell.icmp ?? \"host\" });\n case \"simplex\":\n return lab.fault(\"blackhole\", faultAt, options);\n case \"mtu-blackhole\":\n return lab.fault(\"mtu\", \"client\", { above: cell.mtu ?? 1200 });\n case \"netem\":\n return netemOn(cell.netem);\n case \"rst\":\n return lab.fault(\"rst\", faultAt);\n // the router or the proxy of the topology is the fault, or a calibration has none: nothing to inject\n case \"middlebox-forget\":\n case \"proxy-idle\":\n case \"none\":\n return null;\n case \"toxic\":\n if (cell.toxic === undefined) throw new Error(`${cell.name} names no toxic`);\n return lab.toxic(cell.toxic);\n case \"server-close\":\n await lab.serverClose();\n return null;\n default:\n // a stall is a black hole that stops by itself\n return lab.fault(\"blackhole\", faultAt);\n }\n };\n const lift = async (): Promise<void> => {\n switch (cell.fault) {\n case \"freeze-process\":\n return peer.thaw();\n case \"fin\":\n case \"middlebox-forget\":\n case \"proxy-idle\":\n case \"server-close\":\n case \"none\":\n return undefined;\n case \"unreachable\":\n return lab.lift(\"reject\", faultAt, { code: cell.icmp ?? \"host\" });\n case \"simplex\":\n return lab.lift(\"blackhole\", faultAt, options);\n case \"mtu-blackhole\":\n return lab.lift(\"mtu\", \"client\", { above: cell.mtu ?? 1200 });\n case \"netem\":\n await netemOn(undefined);\n return undefined;\n case \"rst\":\n return lab.lift(\"rst\", faultAt);\n case \"toxic\":\n await lab.toxic(null);\n return undefined;\n default:\n return heal === null ? lab.lift(\"blackhole\", faultAt) : undefined;\n }\n };\n fault = await apply();\n try {\n // an idle time before the request: a router forgets the flow in it, or a keepalive keeps it\n if (cell.idleBefore !== undefined && cell.probe.mode !== \"idle\")\n await sleep(valueIn(cell.idleBefore, \"ms\"));\n if (cell.probe.mode !== \"idle\" && !replyInFlight) await go();\n const { prediction } = cell;\n if (cell.fault === \"stall\") {\n if (cell.stall === undefined) throw new Error(`${cell.name} says no stall's length`);\n // the injector holds the end in the VM until the length of the stall after the send\n const sent = await requestSent(probe, 5_000);\n heal = await lab.heal(faultAt, sent.t + valueIn(cell.stall, \"s\") * 1e9);\n const window = valueIn(prediction.detects ? prediction.high : prediction.window, \"s\");\n await probe.until([\"data\", \"error\", \"eof\"], window * 1_000).catch(() => undefined);\n } else if (prediction.detects === null) {\n // a distribution: the stream ends by itself, or the connection does\n const window = valueIn(prediction.window, \"s\") * 1_000;\n await probe.until([\"done\", \"error\", \"eof\", \"deadline\"], window).catch(() => undefined);\n } else if (prediction.detects) {\n const within = valueIn(prediction.high, \"s\") * 2_000 + 10_000;\n if (cell.observer !== undefined && cell.observer !== \"client\") {\n await observerError(cell, partial, within);\n } else {\n await probe.until([\"error\", \"eof\", \"deadline\"], within).catch(() => undefined);\n }\n } else {\n await sleep(valueIn(prediction.window, \"s\") * 1_000);\n }\n } finally {\n await lift();\n if (toxicFirst) await lab.toxic(null);\n }\n // a thawed target answers the request that it held: the client sees the late reply\n if (cell.fault === \"freeze-process\" && !server)\n await probe.until([\"data\"], 5_000).catch(() => undefined);\n }\n } finally {\n await probe.stop();\n }\n // the prober drains its ring each 20 ms. Its next clock line closes the records of the run\n const settled = await lab.settle();\n const lost = settled === null ? null : settled - lostBefore;\n return {\n environment,\n index,\n events: [...probe.events],\n fault,\n ...(cell.fault === \"stall\" ? { heal } : {}),\n kernel: lab.kernel.slice(from),\n netns: lab.netns.client,\n targetNetns: lab.netns.target,\n lost,\n ...placed,\n ...(lab.topology === null ? {} : { middlebox: lab.middlebox.slice(middleboxFrom) }),\n };\n};\n\n/** This function divides a count of runs across environments, as equally as possible. */\nexport const spread = (runs: number, environments: number): number[] =>\n Array.from(\n { length: environments },\n (_, at) => Math.floor(runs / environments) + (at < runs % environments ? 1 : 0),\n ).filter((count) => count > 0);\n\n/** This function tells if the fault of a cell ends its environment: a server that stopped cannot serve the next run. */\nconst endsEnvironment = (cell: Cell): boolean =>\n cell.fault === \"fin\" && (cell.observer === undefined || cell.observer === \"client\");\n\n/** This function says one line for a run. */\nconst line = (cell: Cell, run: Run, read: Measured): string =>\n `${cell.name}: env ${run.environment} ${run.warmup === true ? \"warm-up\" : `run ${run.index}`} ${read.outcome.kind}` +\n `${read.elapsed === null ? \"\" : ` ${read.elapsed.value} s`}` +\n `${read.void === null ? \"\" : ` (void: ${read.void})`}`;\n\n/**\n * This function does the part of the runs of a cell in one environment, and records its manifest.\n * A warm-up goes first, and the verdict does not count it. On a loaded machine, a new environment is\n * sometimes slow at first: its first exec, its first client, its first fault. The warm-up\n * of a cell whose fault ends the environment is a dry run, without the fault.\n */\nconst inEnvironment = async (\n cell: Cell,\n environment: number,\n count: number,\n say: (line: string) => void,\n): Promise<{ manifest: Manifest; runs: Run[]; warmup: Run }> => {\n const lab = await openLab(cell.target, cell.client ?? cell.probe.driver?.name ?? \"tcp\", cell.topology);\n try {\n // the settings of the target first: Redis applies tcp-keepalive to a client when it accepts it\n const settings = await lab.configure(cell.targetSettings ?? {});\n const targetSysctls = await lab.targetSysctl(cell.targetSysctls ?? {});\n if (cell.targetSetup !== undefined) await lab.setup(cell.targetSetup);\n // netem and a length match act on segments only when TSO, GSO and GRO are off on the two ends,\n // and on the hop that the fault is at\n if (cell.fault === \"mtu-blackhole\" || cell.fault === \"netem\") {\n await lab.offload(false, \"target\");\n await lab.offload(false, \"client\");\n if (typeof cell.faultAt === \"object\") await lab.offload(false, cell.faultAt);\n }\n // a latency is on the path before the client connects, thus its round trip and RTO include it\n const latency = netemSpec(cell, undefined);\n if (latency !== null) await lab.netem(latency, \"target\");\n // the first connection must not wait for ARP: under a delay, that is a longer first round trip\n await lab.warm();\n const manifest = await lab.manifest(await lab.sysctl(cell.sysctls), settings, targetSysctls);\n const warmed = await once(lab, cell, environment, -1, endsEnvironment(cell));\n const warmup: Run = { ...warmed, warmup: true };\n say(line(cell, warmup, measure(cell, warmup)));\n const runs: Run[] = [];\n for (let index = 0; index < count; index += 1) {\n const run = await once(lab, cell, environment, index);\n runs.push(run);\n say(line(cell, run, measure(cell, run)));\n }\n return { manifest, runs, warmup };\n } finally {\n await lab.close();\n }\n};\n\n/**\n * This function does the runs of a cell. They go across new environments, and each environment records\n * its manifest. Then the verdict judges the runs. A median across the edge of the band extends\n * the cell from 11 runs to 22, and then to 44, each extension in new environments. With a\n * concurrency of more than one, that many environments operate at the same time, each with its\n * own prober. The runs then go across at least as many environments.\n *\n * Each environment does a warm-up first, and the result keeps it apart from the counted runs.\n */\nexport const runCell = async (\n cell: Cell,\n say: (line: string) => void = () => {},\n): Promise<CellResult> => {\n const started = new Date().toISOString();\n const manifests: Manifest[] = [];\n const runs: Run[] = [];\n const warmups: Run[] = [];\n const concurrency = Math.max(1, cell.concurrency ?? 1);\n let target: number = cell.runs;\n let next = 0;\n for (;;) {\n const counts = spread(target - runs.length, Math.max(cell.environments, concurrency));\n for (let at = 0; at < counts.length; at += concurrency) {\n const batch = counts.slice(at, at + concurrency).map((count) => ({ count, env: next++ }));\n const done = await Promise.all(\n batch.map(({ count, env }) => inEnvironment(cell, env, count, say)),\n );\n for (const each of done) {\n manifests.push(each.manifest);\n runs.push(...each.runs);\n warmups.push(each.warmup);\n }\n }\n const { measured, verdict } = judge(cell, runs);\n const larger = STEPS.find((step) => step > runs.length);\n if (verdict.decision !== \"extend\" || larger === undefined) {\n const at = { started, ended: new Date().toISOString() };\n return { cell, manifests, runs, warmups, measured, verdict, at };\n }\n target = larger;\n }\n};\n"],"mappings":"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"}
|