@mark1russell7/harness 0.10.0 → 0.11.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 CHANGED
@@ -203,6 +203,8 @@ Each run starts from kernels that keep nothing from the run before it (`lab.forg
203
203
  - The second view is the stamps of the program: from its `ping` line to its `drop` line. The steps are the later pings, each from the reference.
204
204
  - The lines of a client name the socket by its local address (`local`), and the lines of a server by its peer (`peer`). For the server, the lines are the lines of the target. A line whose address is null is about a try that did not open.
205
205
  - A prediction can say how the program drops (`drop`). A close frame (`frame`) is a write, and the socket stays ESTABLISHED after it. A close (`close`) closes the socket in δ_abs: a FIN or an abort. Another drop is a finding. A FIN of the peer that answers a close frame is no close by the program. The run gives the time from the `drop` line to the close by the program (`left`).
206
+ - The client process ends at the end of each run, and its kernel then closes each socket that is still open. A prediction can say that the program is that client process (`exits`). Then a close in δ_abs of that end is no close by the program: `left` stays null, and `exited` gives the time.
207
+ - The end of the process is the first change out of ESTABLISHED of another socket of the client after its last `dial` line (`exitOf`). The program dropped none of these sockets.
206
208
  - A heartbeat is a timer of the program. The retransmit timer of its ping fires in the same interval. Thus a run whose retransmit timer was two jiffies or more behind its wheel slot is void: the VM stopped a vCPU.
207
209
  - **The reconnect** (`reconnectOf`). A prediction with `reconnects` also reads what the client did after the detection.
208
210
  - Each wait goes from a `reconnecting` line, where the program starts its timer, to its next `dial` line, where it makes the socket. A wait outside its band, widened by δ_abs, is a finding. A try that did not come is a finding too.
@@ -263,7 +265,7 @@ Each run starts from kernels that keep nothing from the run before it (`lab.forg
263
265
  - **An HTTP client** (`Verdict.requests`). The verdict counts each outcome of the second request over the counted runs. A measurement-only cell also gives them in its reason.
264
266
  - **The reconnect** (`Verdict.reconnect`). A wait outside its band is a finding of its run, thus the cell fails. The verdict gives the spread of each wait, of the first SYN, of the first ESTABLISHED and of the work before each connect(2). Its reason says the medians.
265
267
  - **A call in flight** (`Verdict.calls`). The verdict counts each outcome of the call in flight of a WebSocket client, and its reason says them.
266
- - **The socket after a drop** (`Verdict.left`). The verdict gives the spread of the time from a drop to the close of its socket by the program. It also counts the runs where the program did not close it.
268
+ - **The socket after a drop** (`Verdict.left`). The verdict gives the spread of the time from a drop to the close of its socket by the program. It also counts the runs where the program did not close it. For the runs where the socket stayed open until the end of the client process, it gives the spread of that end (`exited`).
267
269
 
268
270
  The distribution test needs a simulator, which does not exist at this time.
269
271
 
package/build/index.d.ts CHANGED
@@ -468,6 +468,12 @@ type Prediction = {
468
468
  * ESTABLISHED at once (`close`).
469
469
  */
470
470
  readonly drop?: "frame" | "close";
471
+ /**
472
+ * This flag tells that the program of the observer is the client process, which ends at the
473
+ * end of the run. Its kernel then closes each socket that is still open. A close of the
474
+ * socket at that end is not a close by the program, thus `left` stays null for it (`exited`).
475
+ */
476
+ readonly exits?: true;
471
477
  } | {
472
478
  readonly detects: false;
473
479
  readonly window: Quantity;
@@ -923,12 +929,29 @@ export declare const programLines: (cell: Cell, run: Run) => readonly ProbeEvent
923
929
  * the peer (CLOSE_WAIT) is not such a close. It is null when the socket did not close itself.
924
930
  */
925
931
  export declare const leftAfter: (socket: readonly KernelRecord[], line: ProbeEvent) => Quantity | null;
932
+ /**
933
+ * This function gives the end of the client process in a run, as the kernel stamps it. The process
934
+ * ends after its last try, and its kernel then closes each socket that is still open, at one
935
+ * instant. Thus the end is the first change out of ESTABLISHED of another socket of the client
936
+ * after its last `dial` line. The program dropped none of these sockets. It is undefined when the
937
+ * record has no such change, for example when the last try did not open.
938
+ */
939
+ export declare const exitOf: (run: Run, cookie: number | undefined) => number | undefined;
940
+ /**
941
+ * This function separates a close of the socket by the program from the end of the client process.
942
+ * `left` is the time from the drop line to the close, and `exit` is the end of the process. A close
943
+ * in δ_abs of that end is the end, not the program: `left` becomes null, and `exited` keeps the time.
944
+ */
945
+ export declare const closeOrExit: (line: ProbeEvent, left: Quantity | null, exit: number | undefined, delta?: Quantity) => {
946
+ readonly left: Quantity | null;
947
+ readonly exited?: Quantity;
948
+ };
926
949
  /**
927
950
  * This function tells how a drop differs from the way that its prediction gives, or gives null.
928
951
  * A close closes the socket in δ_abs. A close frame is a write, and the program does not close
929
952
  * the socket at the drop. A peer that answers the close frame can close it, and that is no finding.
930
953
  */
931
- export declare const dropFinding: (expected: "frame" | "close", drop: ProgramEvent, left: Quantity | null, delta?: Quantity) => string | null;
954
+ export declare const dropFinding: (expected: "frame" | "close", drop: ProgramEvent, left: Quantity | null, delta?: Quantity, exited?: Quantity) => string | null;
932
955
  /** This function gives each ping of the program on the connection of the run, with the kernel's stamp of its write. */
933
956
  export declare const pingsOf: (lines: readonly ProbeEvent[], socket: readonly KernelRecord[], local: unknown, delta?: Quantity) => ProgramEvent[];
934
957
  /**
@@ -1239,9 +1262,15 @@ interface Measured {
1239
1262
  /**
1240
1263
  * This time is from the drop line of the program to the close of its socket by its own side: a
1241
1264
  * FIN or an abort. A prediction that says how the program drops (`drop`) gets it. It is null
1242
- * when the socket did not close itself in the run.
1265
+ * when the socket did not close itself in the run. A prediction can say that the client process
1266
+ * ends with the run (`exits`). Then a close at that end is no close by the program, and `left` is null.
1243
1267
  */
1244
1268
  readonly left?: Quantity | null;
1269
+ /**
1270
+ * This time is from the drop line of the program to the end of the client process. A socket gets
1271
+ * it when it stayed open until that end. The kernel closed it then, not the program (`exits`).
1272
+ */
1273
+ readonly exited?: Quantity;
1245
1274
  /** This text is the reason that the run cannot count, or null. A void run is not a pass and not a finding. */
1246
1275
  readonly void: string | null;
1247
1276
  /** This text tells what in the run disagrees with the prediction, other than its timing, or null. */
@@ -1421,10 +1450,13 @@ interface Verdict {
1421
1450
  /**
1422
1451
  * This record is the time from a drop of the program to the close of its socket by its own side,
1423
1452
  * over the counted runs. It also counts the runs where the socket did not close itself.
1453
+ * `exited` is the spread of the end of the client process, for the runs where the socket stayed
1454
+ * open until that end (`Measured.exited`).
1424
1455
  */
1425
1456
  readonly left?: {
1426
1457
  readonly spread: Spread | null;
1427
1458
  readonly stayed: number;
1459
+ readonly exited?: Spread | null;
1428
1460
  };
1429
1461
  readonly why: string;
1430
1462
  }
package/build/index.js CHANGED
@@ -166,12 +166,15 @@ const withProgram = (verdict, counted) => {
166
166
  const outcomes = {};
167
167
  for (const call of calls) outcomes[call.outcome] = (outcomes[call.outcome] ?? 0) + 1;
168
168
  const gone = lefts.flatMap((each) => each === null ? [] : [each]);
169
+ const ends = counted.flatMap((run) => run.exited === void 0 ? [] : [run.exited]);
169
170
  const left = lefts.length === 0 ? void 0 : {
170
171
  spread: spreadOf(gone),
171
- stayed: lefts.length - gone.length
172
+ stayed: lefts.length - gone.length,
173
+ ...ends.length === 0 ? {} : { exited: spreadOf(ends) }
172
174
  };
175
+ const ended = ends.length === 0 ? "" : `: in ${ends.length} the socket stayed open until the client process ended, median ${ms(spreadOf(ends))} after the drop line`;
173
176
  const said = [
174
- ...left === void 0 ? [] : [`After the drop, the program closed its socket in ${gone.length} runs, median ${ms(left.spread)} after its drop line${left.stayed === 0 ? "" : `, and it did not close it in ${left.stayed}`}.`],
177
+ ...left === void 0 ? [] : [`After the drop, the program closed its socket in ${gone.length} runs, median ${ms(left.spread)} after its drop line${left.stayed === 0 ? "" : `, and it did not close it in ${left.stayed}${ended}`}.`],
175
178
  ...reconnect === void 0 ? [] : [`Reconnect: the waits before each try, median ${reconnect.waits.map(ms).join(", ")}; the first SYN median ${ms(reconnect.syn)} and ESTABLISHED median ${ms(reconnect.established)} after the detection.`],
176
179
  ...calls.length === 0 ? [] : [`Calls in flight: ${Object.entries(outcomes).map(([outcome, n]) => `${n} ${outcome}`).join("; ")}.`]
177
180
  ];
@@ -500,15 +503,41 @@ const leftAfter = (socket, line) => {
500
503
  return left === void 0 ? null : between$3(line.t, left.mono);
501
504
  };
502
505
  /**
506
+ * This function gives the end of the client process in a run, as the kernel stamps it. The process
507
+ * ends after its last try, and its kernel then closes each socket that is still open, at one
508
+ * instant. Thus the end is the first change out of ESTABLISHED of another socket of the client
509
+ * after its last `dial` line. The program dropped none of these sockets. It is undefined when the
510
+ * record has no such change, for example when the last try did not open.
511
+ */
512
+ const exitOf = (run, cookie) => {
513
+ const last = run.events.filter((line) => line.ev === "dial").at(-1);
514
+ if (last === void 0) return void 0;
515
+ const dropped = new Set(run.events.filter((line) => line.ev === "drop").map((line) => line["local"]));
516
+ return run.kernel.find((record) => record.netns === run.netns && record.cookie !== cookie && record.ev === "state" && oldState$2(record) === 1 && (newState$2(record) === 4 || newState$2(record) === 7) && record.mono >= last.t && !dropped.has(record.src))?.mono;
517
+ };
518
+ /**
519
+ * This function separates a close of the socket by the program from the end of the client process.
520
+ * `left` is the time from the drop line to the close, and `exit` is the end of the process. A close
521
+ * in δ_abs of that end is the end, not the program: `left` becomes null, and `exited` keeps the time.
522
+ */
523
+ const closeOrExit = (line, left, exit, delta = NOISE) => {
524
+ if (left === null || exit === void 0) return { left };
525
+ const at = line.t + valueIn(left, "s") * 1e9;
526
+ return Math.abs(at - exit) <= valueIn(delta, "s") * 1e9 ? {
527
+ left: null,
528
+ exited: left
529
+ } : { left };
530
+ };
531
+ /**
503
532
  * This function tells how a drop differs from the way that its prediction gives, or gives null.
504
533
  * A close closes the socket in δ_abs. A close frame is a write, and the program does not close
505
534
  * the socket at the drop. A peer that answers the close frame can close it, and that is no finding.
506
535
  */
507
- const dropFinding = (expected, drop, left, delta = NOISE) => {
536
+ const dropFinding = (expected, drop, left, delta = NOISE, exited) => {
508
537
  const noise = valueIn(delta, "s");
509
538
  const soon = left !== null && valueIn(left, "s") <= noise;
510
539
  if (expected === "close") {
511
- if (left === null) return "the program did not close its socket after the drop, where a close is at once";
540
+ if (left === null) return `the program did not close its socket after the drop, where a close is at once${exited === void 0 ? "" : ": it stayed open until the client process ended"}`;
512
541
  return soon ? null : `the program closed its socket ${left.value} s after the drop, not at once`;
513
542
  }
514
543
  if (drop.record !== "write") return "the drop wrote no close frame on its socket";
@@ -1822,8 +1851,12 @@ const measureSocket = (cell, run) => {
1822
1851
  const words = outcome.kind === "error" ? outcome.errno : outcome.kind === "closed" ? "its own close" : outcome.kind === "dropped" ? "a drop by its own program" : "end of file";
1823
1852
  const reconnect = prediction.reconnects === void 0 ? void 0 : reconnectOf(run, ended, cookie, drop?.line.t ?? ended);
1824
1853
  const late = reconnect === void 0 || prediction.reconnects === void 0 ? null : reconnectFinding(prediction.reconnects, reconnect, noiseOf(cell));
1825
- const left = prediction.drop === void 0 || drop === void 0 ? void 0 : leftAfter(socket, drop.line);
1826
- const how = prediction.drop === void 0 || drop === void 0 || left === void 0 ? null : dropFinding(prediction.drop, drop, left, noiseOf(cell));
1854
+ const closed = prediction.drop === void 0 || drop === void 0 ? void 0 : leftAfter(socket, drop.line);
1855
+ const { left, exited } = closed === void 0 || drop === void 0 || prediction.exits !== true ? {
1856
+ left: closed,
1857
+ exited: void 0
1858
+ } : closeOrExit(drop.line, closed, exitOf(run, cookie), noiseOf(cell));
1859
+ const how = prediction.drop === void 0 || drop === void 0 || left === void 0 ? null : dropFinding(prediction.drop, drop, left, noiseOf(cell), exited);
1827
1860
  const found = [
1828
1861
  matches ? null : `it ended with ${words}, not ${prediction.errno}`,
1829
1862
  late,
@@ -1851,6 +1884,7 @@ const measureSocket = (cell, run) => {
1851
1884
  steps,
1852
1885
  ...reconnect === void 0 ? {} : { reconnect },
1853
1886
  ...left === void 0 ? {} : { left },
1887
+ ...exited === void 0 ? {} : { exited },
1854
1888
  ...cell.probe.ws !== void 0 && inflight ? { call: callOf(events, request, ended) } : {},
1855
1889
  void: voided,
1856
1890
  finding
@@ -4723,6 +4757,6 @@ const runCell = async (cell, say = () => {}) => {
4723
4757
  }
4724
4758
  };
4725
4759
  //#endregion
4726
- export { CONVERGE_MS, INJECT, KEPT, LINGER_MS, LISTING, MEMBER_MARKS, NOISE, PUMP, QUIET_MS, RELATIVE, REPLICA_SET_STAGES, STAGES, STATUS_SCRIPT, STEPS, STEP_DOWN, callOf, clockOffsetOf, crossingsOf, cutOf, deadlineOf, delayVoids, deviceOf, dropFinding, dropOf, endpointAfter, errorsOf, fieldOf, fieldsOf, firstUnanswered, goHttpImage, groupOnce, groupVerdictOf, harnessImage, hzOf, infoOf, initiateScript, isHopPlace, judge, lagsOf, leftAfter, matches, measure, measureDriver, measureGroup, medianInterval, memberCommand, memberConfig, memberLineOf, mongoClientImage, nodeClientImage, nodeHttpImage, noiseOf, onMonotonic, openGroupLab, openLab, openReplicaSetLab, peersOf, pingsOf, proceduresImage, programLines, quantileOf, reconnectFinding, reconnectOf, referenceOf, reorderingOf, replicaSetArgs, replicaSetOnce, replicaSetStagesOf, requestOf, requestWords, routerImage, routesOf, runCell, sentinelConfig, socketAt, socketOf, socketsOf, spread, spreadOf, stagesOf, statusViewOf, subscriptionsOf, targetImageOf, templated, toxicBody, verdictOf, viewOf, writesOf };
4760
+ export { CONVERGE_MS, INJECT, KEPT, LINGER_MS, LISTING, MEMBER_MARKS, NOISE, PUMP, QUIET_MS, RELATIVE, REPLICA_SET_STAGES, STAGES, STATUS_SCRIPT, STEPS, STEP_DOWN, callOf, clockOffsetOf, closeOrExit, crossingsOf, cutOf, deadlineOf, delayVoids, deviceOf, dropFinding, dropOf, endpointAfter, errorsOf, exitOf, fieldOf, fieldsOf, firstUnanswered, goHttpImage, groupOnce, groupVerdictOf, harnessImage, hzOf, infoOf, initiateScript, isHopPlace, judge, lagsOf, leftAfter, matches, measure, measureDriver, measureGroup, medianInterval, memberCommand, memberConfig, memberLineOf, mongoClientImage, nodeClientImage, nodeHttpImage, noiseOf, onMonotonic, openGroupLab, openLab, openReplicaSetLab, peersOf, pingsOf, proceduresImage, programLines, quantileOf, reconnectFinding, reconnectOf, referenceOf, reorderingOf, replicaSetArgs, replicaSetOnce, replicaSetStagesOf, requestOf, requestWords, routerImage, routesOf, runCell, sentinelConfig, socketAt, socketOf, socketsOf, spread, spreadOf, stagesOf, statusViewOf, subscriptionsOf, targetImageOf, templated, toxicBody, verdictOf, viewOf, writesOf };
4727
4761
 
4728
4762
  //# sourceMappingURL=index.js.map