@gravitylabsllc/porthole 0.2.1 → 0.2.2
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/dist/device.js +56 -1
- package/dist/device.js.map +1 -1
- package/dist/eventKinds.js +115 -0
- package/dist/eventKinds.js.map +1 -0
- package/dist/index.js +65 -17
- package/dist/index.js.map +1 -1
- package/dist/moment.js +14 -0
- package/dist/moment.js.map +1 -1
- package/dist/timeline.js +10 -1
- package/dist/timeline.js.map +1 -1
- package/dist/trace.js +69 -0
- package/dist/trace.js.map +1 -1
- package/package.json +1 -1
- package/src/device.ts +58 -0
- package/src/eventKinds.ts +142 -0
- package/src/index.ts +76 -19
- package/src/moment.ts +24 -0
- package/src/timeline.ts +10 -1
- package/src/trace.ts +121 -0
- package/ui/dist/assets/index-ChVI8dOL.js +70 -0
- package/ui/dist/index.html +1 -1
- package/ui/dist/assets/index-h7VNB9Fl.js +0 -70
package/src/trace.ts
CHANGED
|
@@ -726,6 +726,127 @@ const EXIT_ERROR_REASONS = new Set([
|
|
|
726
726
|
"REASON_EXCESSIVE_RESOURCE_USAGE",
|
|
727
727
|
]);
|
|
728
728
|
|
|
729
|
+
// ---------------------------------------------------------------------------
|
|
730
|
+
// GRA-200: alsoInWindow — an inventory, not a second opinion
|
|
731
|
+
// ---------------------------------------------------------------------------
|
|
732
|
+
//
|
|
733
|
+
// findingsOf above answers "what is wrong" and is deliberately selective: a
|
|
734
|
+
// REASON_SIGNALED exit, a non-blocking gc, a plain memory sample produce no
|
|
735
|
+
// finding at all, because none of them is evidence of anything on their own.
|
|
736
|
+
// That selectivity is correct for `findings`' own job and was never the
|
|
737
|
+
// problem — the problem is that it left no OTHER way for an agent reading
|
|
738
|
+
// just `findings` to learn those events happened at all. `alsoInWindow` is
|
|
739
|
+
// that other way: a plain count (or, for exits, a full list) of what the
|
|
740
|
+
// window contained, built from the exact same `events` findingsOf was given,
|
|
741
|
+
// so the two can never disagree about which window they describe.
|
|
742
|
+
//
|
|
743
|
+
// "Also in this window" therefore intentionally overlaps `findings` rather
|
|
744
|
+
// than complementing it — an exit that already produced a finding still
|
|
745
|
+
// appears in `alsoInWindow.exits`, and a blocking gc still counts toward
|
|
746
|
+
// `alsoInWindow.gc`. The finding is the judgement; this is the inventory.
|
|
747
|
+
// Collapsing the two into "only show what findings missed" would silently
|
|
748
|
+
// reintroduce the exact bug this ticket exists to close the moment a second
|
|
749
|
+
// exit arrived in the same window as a first one that did get a finding.
|
|
750
|
+
|
|
751
|
+
export interface AlsoInWindowExit {
|
|
752
|
+
reason: string;
|
|
753
|
+
/** Epoch ms — `ApplicationExitInfo.getTimestamp()`'s own unit, exactly what `porthole_status`'s `exitTrace` param accepts (GRA-188). */
|
|
754
|
+
timestamp: number;
|
|
755
|
+
/** The same instant as `timestamp`, ISO-8601 — the same spelling `porthole_status`'s `exits.recent[].at` uses. */
|
|
756
|
+
at: string;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
export interface AlsoInWindow {
|
|
760
|
+
/** Every exit event in the window, including ones that already produced a finding. Absent (not `[]`) when there were none. */
|
|
761
|
+
exits?: AlsoInWindowExit[];
|
|
762
|
+
/**
|
|
763
|
+
* `device` events other than a `trimMemory` sub-kind — profile, lifecycle,
|
|
764
|
+
* rotation, theme, power, network. Counted separately from [trim] so a
|
|
765
|
+
* reader is not left guessing whether a "4 device events" count already
|
|
766
|
+
* includes the memory-pressure signal named right next to it.
|
|
767
|
+
*/
|
|
768
|
+
device?: number;
|
|
769
|
+
/** Periodic memory samples — never their own finding, always worth knowing they exist. */
|
|
770
|
+
memory?: number;
|
|
771
|
+
/** All `gc` events, blocking or not — `findingsOf` only turns the blocking ones into a finding. */
|
|
772
|
+
gc?: number;
|
|
773
|
+
/** `device` events whose `data.kind` is `trimMemory` — always already a finding when present (`findingsOf`'s `trim-memory`), counted again here for the same reason an exit is. */
|
|
774
|
+
trim?: number;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* Builds the block above from the same `events` `findingsOf` was given.
|
|
779
|
+
* Returns `undefined` — not an object with every field absent — when there
|
|
780
|
+
* is nothing to list, which is what keeps a `findings` payload with none of
|
|
781
|
+
* this in its window byte-identical to what it returned before this ticket.
|
|
782
|
+
*/
|
|
783
|
+
export function alsoInWindowOf(events: DeviceEvent[]): AlsoInWindow | undefined {
|
|
784
|
+
const exits = events
|
|
785
|
+
.filter((e) => e.event === "exit")
|
|
786
|
+
.map((e): AlsoInWindowExit => {
|
|
787
|
+
const timestamp = num(e.data.timestamp);
|
|
788
|
+
return { reason: str(e.data.reason), timestamp, at: new Date(timestamp).toISOString() };
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
const deviceEvents = events.filter((e) => e.event === "device");
|
|
792
|
+
const trimEvents = deviceEvents.filter((e) => str(e.data.kind) === "trimMemory");
|
|
793
|
+
const device = deviceEvents.length - trimEvents.length;
|
|
794
|
+
const trim = trimEvents.length;
|
|
795
|
+
const memory = events.filter((e) => e.event === "memory").length;
|
|
796
|
+
const gc = events.filter((e) => e.event === "gc").length;
|
|
797
|
+
|
|
798
|
+
const also: AlsoInWindow = {
|
|
799
|
+
...(exits.length > 0 ? { exits } : {}),
|
|
800
|
+
...(device > 0 ? { device } : {}),
|
|
801
|
+
...(memory > 0 ? { memory } : {}),
|
|
802
|
+
...(gc > 0 ? { gc } : {}),
|
|
803
|
+
...(trim > 0 ? { trim } : {}),
|
|
804
|
+
};
|
|
805
|
+
return Object.keys(also).length > 0 ? also : undefined;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* One sentence naming what [alsoInWindowOf] found, or `""` when it found
|
|
810
|
+
* nothing. `findings`' own summary and `what_was_happening`'s `describe()`
|
|
811
|
+
* both append exactly this — one function, not two hand-written near-copies
|
|
812
|
+
* that would drift the way this codebase's own history (see BRIEFING.md's
|
|
813
|
+
* "GRA-53: the third consumer") warns two independent copies always do.
|
|
814
|
+
*/
|
|
815
|
+
export function alsoInWindowSentence(also: AlsoInWindow | undefined): string {
|
|
816
|
+
if (!also) return "";
|
|
817
|
+
const parts: string[] = [];
|
|
818
|
+
|
|
819
|
+
if (also.exits?.length) {
|
|
820
|
+
const n = also.exits.length;
|
|
821
|
+
if (n === 1) {
|
|
822
|
+
const exit = also.exits[0];
|
|
823
|
+
parts.push(
|
|
824
|
+
`${n} process exit (${exit.reason}, full record via ` +
|
|
825
|
+
`\`porthole_status { exitTrace: ${exit.timestamp} }\`)`,
|
|
826
|
+
);
|
|
827
|
+
} else {
|
|
828
|
+
const latest = also.exits[also.exits.length - 1];
|
|
829
|
+
parts.push(
|
|
830
|
+
`${n} process exits (most recent ${latest.reason}, full records via \`porthole_status\`)`,
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
// Unlike an exit, none of these four carries a pointer of its own — a
|
|
835
|
+
// count with nowhere to go for the detail is a dead end, so one shared
|
|
836
|
+
// pointer to `timeline` (where every one of them is a raw, inspectable
|
|
837
|
+
// event) covers all four rather than repeating the same clause four times.
|
|
838
|
+
const rawCounts: string[] = [];
|
|
839
|
+
if (also.device) rawCounts.push(`${also.device} device event${also.device === 1 ? "" : "s"}`);
|
|
840
|
+
if (also.memory) rawCounts.push(`${also.memory} memory event${also.memory === 1 ? "" : "s"}`);
|
|
841
|
+
if (also.gc) rawCounts.push(`${also.gc} GC event${also.gc === 1 ? "" : "s"}`);
|
|
842
|
+
if (also.trim) rawCounts.push(`${also.trim} memory-trim event${also.trim === 1 ? "" : "s"}`);
|
|
843
|
+
if (rawCounts.length > 0) {
|
|
844
|
+
parts.push(`${rawCounts.join(", ")} (raw detail via \`timeline\`)`);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
return parts.length > 0 ? `Also in this window: ${parts.join(", ")}.` : "";
|
|
848
|
+
}
|
|
849
|
+
|
|
729
850
|
export function buildTrace(options: {
|
|
730
851
|
scenario: string;
|
|
731
852
|
driver?: string;
|