@ifc-lite/load-trace 0.0.0-stage → 0.1.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/LICENSE +373 -0
- package/README.md +120 -2
- package/dist/call-meter.d.ts +16 -0
- package/dist/call-meter.d.ts.map +1 -0
- package/dist/call-meter.js +50 -0
- package/dist/call-meter.js.map +1 -0
- package/dist/chrome-trace.d.ts +23 -0
- package/dist/chrome-trace.d.ts.map +1 -0
- package/dist/chrome-trace.js +53 -0
- package/dist/chrome-trace.js.map +1 -0
- package/dist/counters.d.ts +34 -0
- package/dist/counters.d.ts.map +1 -0
- package/dist/counters.js +69 -0
- package/dist/counters.js.map +1 -0
- package/dist/frames.d.ts +69 -0
- package/dist/frames.d.ts.map +1 -0
- package/dist/frames.js +85 -0
- package/dist/frames.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/load-trace.d.ts +62 -0
- package/dist/load-trace.d.ts.map +1 -0
- package/dist/load-trace.js +199 -0
- package/dist/load-trace.js.map +1 -0
- package/dist/messages.d.ts +38 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +116 -0
- package/dist/messages.js.map +1 -0
- package/dist/perf-sink.d.ts +15 -0
- package/dist/perf-sink.d.ts.map +1 -0
- package/dist/perf-sink.js +37 -0
- package/dist/perf-sink.js.map +1 -0
- package/dist/tree.d.ts +11 -0
- package/dist/tree.d.ts.map +1 -0
- package/dist/tree.js +25 -0
- package/dist/tree.js.map +1 -0
- package/dist/types.d.ts +70 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/worker-phases.d.ts +39 -0
- package/dist/worker-phases.d.ts.map +1 -0
- package/dist/worker-phases.js +60 -0
- package/dist/worker-phases.js.map +1 -0
- package/dist/worker.d.ts +68 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +112 -0
- package/dist/worker.js.map +1 -0
- package/package.json +46 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEvE;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxD,4EAA4E;IAC5E,gBAAgB,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI,CAAC;CACxF;AAED,MAAM,WAAW,aAAa;IAAG,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE;AAYjG;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,OAAO,EAAE,WAAW,GAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAqB,GAC1E,aAAa,CA+Bf;AAED,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,eAAe,EAC7D,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,EAAE,QAAQ,GAAE,mBAAkC,GACtF,CAAC,CA8BH"}
|
package/dist/messages.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import { perfCounters } from './counters.js';
|
|
5
|
+
const isTraceControl = (message) => {
|
|
6
|
+
const type = message?.type;
|
|
7
|
+
return typeof type === 'string' && type.startsWith('load-trace:');
|
|
8
|
+
};
|
|
9
|
+
function transferSet(arg) {
|
|
10
|
+
const list = Array.isArray(arg) ? arg : arg?.transfer;
|
|
11
|
+
return new Set(list ?? []);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Estimate what structured clone serialises for `value`. Typed-array views
|
|
15
|
+
* count their WHOLE backing buffer once (clone copies the buffer, not the
|
|
16
|
+
* view's window); strings count one byte per code unit; numbers 8. Iterative,
|
|
17
|
+
* so a deep payload cannot overflow the stack.
|
|
18
|
+
*/
|
|
19
|
+
export function estimateCloneBytes(value, transferred = () => false) {
|
|
20
|
+
const out = { cloneBytes: 0, transferBytes: 0, sharedBytes: 0 };
|
|
21
|
+
const seen = new Set();
|
|
22
|
+
const stack = [value];
|
|
23
|
+
while (stack.length > 0) {
|
|
24
|
+
const v = stack.pop();
|
|
25
|
+
switch (typeof v) {
|
|
26
|
+
case 'string':
|
|
27
|
+
out.cloneBytes += v.length;
|
|
28
|
+
continue;
|
|
29
|
+
case 'number':
|
|
30
|
+
case 'bigint':
|
|
31
|
+
out.cloneBytes += 8;
|
|
32
|
+
continue;
|
|
33
|
+
case 'boolean':
|
|
34
|
+
out.cloneBytes += 1;
|
|
35
|
+
continue;
|
|
36
|
+
case 'object': break;
|
|
37
|
+
default: continue;
|
|
38
|
+
}
|
|
39
|
+
if (v === null || seen.has(v))
|
|
40
|
+
continue;
|
|
41
|
+
seen.add(v);
|
|
42
|
+
if (ArrayBuffer.isView(v)) {
|
|
43
|
+
stack.push(v.buffer);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (typeof SharedArrayBuffer !== 'undefined' && v instanceof SharedArrayBuffer) {
|
|
47
|
+
out.sharedBytes += v.byteLength;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (v instanceof ArrayBuffer) {
|
|
51
|
+
if (transferred(v))
|
|
52
|
+
out.transferBytes += v.byteLength;
|
|
53
|
+
else
|
|
54
|
+
out.cloneBytes += v.byteLength;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (Array.isArray(v)) {
|
|
58
|
+
for (let i = 0; i < v.length; i++)
|
|
59
|
+
stack.push(v[i]);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (v instanceof Map) {
|
|
63
|
+
for (const [k, e] of v)
|
|
64
|
+
stack.push(k, e);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (v instanceof Set) {
|
|
68
|
+
for (const e of v)
|
|
69
|
+
stack.push(e);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (v instanceof Date) {
|
|
73
|
+
out.cloneBytes += 8;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
for (const key of Object.keys(v)) {
|
|
77
|
+
out.cloneBytes += key.length;
|
|
78
|
+
stack.push(v[key]);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
export function accountWorkerMessages(worker, label, registry = perfCounters) {
|
|
84
|
+
if (!registry.enabled)
|
|
85
|
+
return worker;
|
|
86
|
+
// A pooled worker (#7036) changes role between loads, so it passes a getter.
|
|
87
|
+
const prefixOf = typeof label === 'function' ? () => `msg.${label()}` : () => `msg.${label}`;
|
|
88
|
+
const nativePost = worker.postMessage.bind(worker);
|
|
89
|
+
const counted = (message, transfer) => {
|
|
90
|
+
if (!isTraceControl(message)) {
|
|
91
|
+
const list = transferSet(transfer);
|
|
92
|
+
const est = estimateCloneBytes(message, (b) => list.has(b));
|
|
93
|
+
const prefix = prefixOf();
|
|
94
|
+
registry.add(`${prefix}.out.count`);
|
|
95
|
+
registry.add(`${prefix}.out.cloneBytes`, est.cloneBytes);
|
|
96
|
+
registry.add(`${prefix}.out.transferBytes`, est.transferBytes);
|
|
97
|
+
registry.add(`${prefix}.out.sharedBytes`, est.sharedBytes);
|
|
98
|
+
}
|
|
99
|
+
nativePost(message, transfer);
|
|
100
|
+
};
|
|
101
|
+
worker.postMessage = counted;
|
|
102
|
+
worker.addEventListener?.('message', ({ data }) => {
|
|
103
|
+
if (isTraceControl(data))
|
|
104
|
+
return;
|
|
105
|
+
// A received ArrayBuffer may have been transferred or cloned; this side
|
|
106
|
+
// cannot tell, so every buffer lands in bufferBytes, the rest in cloneBytes.
|
|
107
|
+
const est = estimateCloneBytes(data, () => true);
|
|
108
|
+
const prefix = prefixOf();
|
|
109
|
+
registry.add(`${prefix}.in.count`);
|
|
110
|
+
registry.add(`${prefix}.in.cloneBytes`, est.cloneBytes);
|
|
111
|
+
registry.add(`${prefix}.in.bufferBytes`, est.transferBytes);
|
|
112
|
+
registry.add(`${prefix}.in.sharedBytes`, est.sharedBytes);
|
|
113
|
+
});
|
|
114
|
+
return worker;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TraceAttrValue } from './types.js';
|
|
2
|
+
/** Namespace for every User Timing entry this package writes. */
|
|
3
|
+
export declare const MEASURE_PREFIX = "ifc:";
|
|
4
|
+
/** Where finished spans are mirrored (the browser's User Timing buffer by default). */
|
|
5
|
+
export interface PerfSink {
|
|
6
|
+
measure(name: string, start: number, end: number, detail: Record<string, TraceAttrValue>): void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Mirror spans into `performance.measure`. Prefers the instance method but
|
|
10
|
+
* falls back to `Performance.prototype.measure`: the viewer's DEV bootstrap
|
|
11
|
+
* deliberately nulls the INSTANCE property so React's dev render tracker stays
|
|
12
|
+
* off (apps/viewer/AGENTS.md), and the prototype is untouched by that.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createPerfSink(perf?: Performance | undefined): PerfSink | null;
|
|
15
|
+
//# sourceMappingURL=perf-sink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"perf-sink.d.ts","sourceRoot":"","sources":["../src/perf-sink.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,iEAAiE;AACjE,eAAO,MAAM,cAAc,SAAS,CAAC;AAErC,uFAAuF;AACvF,MAAM,WAAW,QAAQ;IACvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,IAAI,CAAC;CACjG;AAID;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,GAAE,WAAW,GAAG,SAAkC,GAAG,QAAQ,GAAG,IAAI,CAqBtG"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/** Namespace for every User Timing entry this package writes. */
|
|
5
|
+
export const MEASURE_PREFIX = 'ifc:';
|
|
6
|
+
/**
|
|
7
|
+
* Mirror spans into `performance.measure`. Prefers the instance method but
|
|
8
|
+
* falls back to `Performance.prototype.measure`: the viewer's DEV bootstrap
|
|
9
|
+
* deliberately nulls the INSTANCE property so React's dev render tracker stays
|
|
10
|
+
* off (apps/viewer/AGENTS.md), and the prototype is untouched by that.
|
|
11
|
+
*/
|
|
12
|
+
export function createPerfSink(perf = globalThis.performance) {
|
|
13
|
+
if (!perf)
|
|
14
|
+
return null;
|
|
15
|
+
const proto = typeof Performance !== 'undefined' ? Performance.prototype : undefined;
|
|
16
|
+
const instance = perf;
|
|
17
|
+
const measure = typeof instance.measure === 'function' ? instance.measure : proto?.measure;
|
|
18
|
+
if (typeof measure !== 'function')
|
|
19
|
+
return null;
|
|
20
|
+
let warned = false;
|
|
21
|
+
return {
|
|
22
|
+
measure(name, start, end, detail) {
|
|
23
|
+
// User Timing rejects negative timestamps and end < start.
|
|
24
|
+
const s = Math.max(0, start);
|
|
25
|
+
try {
|
|
26
|
+
measure.call(perf, name, { start: s, end: Math.max(s, end), detail });
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
if (!warned) {
|
|
30
|
+
warned = true;
|
|
31
|
+
console.warn('[load-trace] performance.measure failed; span tree still recorded', err);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=perf-sink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"perf-sink.js","sourceRoot":"","sources":["../src/perf-sink.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAI/D,iEAAiE;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AASrC;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAgC,UAAU,CAAC,WAAW;IACnF,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,KAAK,GAAG,OAAO,WAAW,KAAK,WAAW,CAAC,CAAC,CAAE,WAAW,CAAC,SAAgD,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7H,MAAM,QAAQ,GAAG,IAA0C,CAAC;IAC5D,MAAM,OAAO,GAAG,OAAO,QAAQ,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC;IAC3F,IAAI,OAAO,OAAO,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IAC/C,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,OAAO;QACL,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM;YAC9B,2DAA2D;YAC3D,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YAC7B,IAAI,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;YACxE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,MAAM,GAAG,IAAI,CAAC;oBACd,OAAO,CAAC,IAAI,CAAC,mEAAmE,EAAE,GAAG,CAAC,CAAC;gBACzF,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/tree.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { LoadTraceSnapshot, TraceSpan } from './types.js';
|
|
2
|
+
export interface SpanTreeNode {
|
|
3
|
+
span: TraceSpan;
|
|
4
|
+
children: SpanTreeNode[];
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Nest a snapshot's flat span list by `parentId`. Returns the root's direct
|
|
8
|
+
* children, each list ordered by start time (ties keep recording order).
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildSpanTree(snapshot: LoadTraceSnapshot): SpanTreeNode[];
|
|
11
|
+
//# sourceMappingURL=tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../src/tree.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,iBAAiB,GAAG,YAAY,EAAE,CAczE"}
|
package/dist/tree.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* Nest a snapshot's flat span list by `parentId`. Returns the root's direct
|
|
6
|
+
* children, each list ordered by start time (ties keep recording order).
|
|
7
|
+
*/
|
|
8
|
+
export function buildSpanTree(snapshot) {
|
|
9
|
+
const nodes = new Map();
|
|
10
|
+
for (const span of snapshot.spans)
|
|
11
|
+
nodes.set(span.id, { span, children: [] });
|
|
12
|
+
const roots = [];
|
|
13
|
+
for (const node of nodes.values()) {
|
|
14
|
+
const parent = node.span.parentId === null ? undefined : nodes.get(node.span.parentId);
|
|
15
|
+
(parent ? parent.children : roots).push(node);
|
|
16
|
+
}
|
|
17
|
+
const order = (list) => {
|
|
18
|
+
list.sort((a, b) => a.span.start - b.span.start || a.span.id - b.span.id);
|
|
19
|
+
for (const n of list)
|
|
20
|
+
order(n.children);
|
|
21
|
+
};
|
|
22
|
+
order(roots);
|
|
23
|
+
return roots;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=tree.js.map
|
package/dist/tree.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.js","sourceRoot":"","sources":["../src/tree.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAS/D;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,QAA2B;IACvD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAwB,CAAC;IAC9C,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK;QAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IAC9E,MAAM,KAAK,GAAmB,EAAE,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvF,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,IAAoB,EAAQ,EAAE;QAC3C,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1E,KAAK,MAAM,CAAC,IAAI,IAAI;YAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC,CAAC;IACF,KAAK,CAAC,KAAK,CAAC,CAAC;IACb,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { FrameSummary } from './frames.js';
|
|
2
|
+
/** Attribute values that survive structured clone, JSON and the User Timing `detail`. */
|
|
3
|
+
export type TraceAttrValue = string | number | boolean;
|
|
4
|
+
export type TraceAttrs = Readonly<Record<string, TraceAttrValue>>;
|
|
5
|
+
/**
|
|
6
|
+
* Per-load attributes (charter #6954). `journey` is the charter journey id
|
|
7
|
+
* (`J1` cold open, `J2` warm/cache open, `J4` federated add, ...).
|
|
8
|
+
*/
|
|
9
|
+
export interface LoadTraceAttributes {
|
|
10
|
+
journey?: string;
|
|
11
|
+
modelKind?: 'primary' | 'federated';
|
|
12
|
+
cacheTier?: string;
|
|
13
|
+
loadPath?: string;
|
|
14
|
+
workerCount?: number;
|
|
15
|
+
[key: string]: TraceAttrValue | undefined;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* One span on the load timeline. Times are milliseconds on the MAIN thread's
|
|
19
|
+
* `performance.now()` clock; worker spans are shifted onto it at merge time.
|
|
20
|
+
*/
|
|
21
|
+
export interface TraceSpan {
|
|
22
|
+
id: number;
|
|
23
|
+
name: string;
|
|
24
|
+
/** `main`, or the worker label given to `enableWorkerTrace` (e.g. `geom-0`). */
|
|
25
|
+
thread: string;
|
|
26
|
+
start: number;
|
|
27
|
+
/** `null` while the span is still open. */
|
|
28
|
+
end: number | null;
|
|
29
|
+
/** Parent span id; `null` means the load root. */
|
|
30
|
+
parentId: number | null;
|
|
31
|
+
/** Milestones start at the load root, so their duration is time-to-milestone. */
|
|
32
|
+
milestone?: true;
|
|
33
|
+
attrs?: Record<string, TraceAttrValue>;
|
|
34
|
+
}
|
|
35
|
+
/** JSON-safe copy of one load's span tree (`spans` is flat; `parentId` links it). */
|
|
36
|
+
export interface LoadTraceSnapshot {
|
|
37
|
+
loadId: string;
|
|
38
|
+
attrs: LoadTraceAttributes;
|
|
39
|
+
/** Main-thread `performance.timeOrigin`, so absolute times can be recovered. */
|
|
40
|
+
timeOrigin: number;
|
|
41
|
+
start: number;
|
|
42
|
+
end: number | null;
|
|
43
|
+
spans: TraceSpan[];
|
|
44
|
+
/**
|
|
45
|
+
* Structural counters (#6957) moved while this load was the latest one:
|
|
46
|
+
* from its start until the next load starts (or the snapshot is taken),
|
|
47
|
+
* plus every counter its workers posted back. Absent when counters are off.
|
|
48
|
+
*/
|
|
49
|
+
counters?: Record<string, number>;
|
|
50
|
+
/** The worker share of `counters`, per worker thread label. */
|
|
51
|
+
workerCounters?: Record<string, Record<string, number>>;
|
|
52
|
+
/** Long-frame summary over the same window; absent without a frame monitor. */
|
|
53
|
+
mainThread?: FrameSummary;
|
|
54
|
+
}
|
|
55
|
+
/** A finished span recorded inside a worker, on that worker's own clock. */
|
|
56
|
+
export interface WorkerSpan {
|
|
57
|
+
name: string;
|
|
58
|
+
start: number;
|
|
59
|
+
end: number;
|
|
60
|
+
attrs?: Record<string, TraceAttrValue>;
|
|
61
|
+
}
|
|
62
|
+
/** What a worker posts back: its spans plus the clock origin needed to align them. */
|
|
63
|
+
export interface WorkerTracePayload {
|
|
64
|
+
thread: string;
|
|
65
|
+
timeOrigin: number;
|
|
66
|
+
spans: WorkerSpan[];
|
|
67
|
+
/** Counter increments in the worker since its previous payload (#6957). */
|
|
68
|
+
counters?: Record<string, number>;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,yFAAyF;AACzF,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AACvD,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;AAElE;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,SAAS,GAAG,WAAW,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;CAC3C;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,iFAAiF;IACjF,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACxC;AAED,qFAAqF;AACrF,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,mBAAmB,CAAC;IAC3B,gFAAgF;IAChF,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,+DAA+D;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACxD,+EAA+E;IAC/E,UAAU,CAAC,EAAE,YAAY,CAAC;CAC3B;AAED,4EAA4E;AAC5E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACxC;AAED,sFAAsF;AACtF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;+DAE+D"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { TraceAttrs } from './types.js';
|
|
2
|
+
import { type TraceSpansMessage } from './worker.js';
|
|
3
|
+
/**
|
|
4
|
+
* Spans for the phases INSIDE one long worker handler (#6979), where
|
|
5
|
+
* `createWorkerTraceHost`'s one-span-per-message is too coarse. The parser
|
|
6
|
+
* worker runs a whole parse in a single `parse` message and the host
|
|
7
|
+
* terminates it the moment `complete` arrives, so a span posted after the
|
|
8
|
+
* handler settles would die with the worker: `flush()` posts what has
|
|
9
|
+
* finished so far, and the worker calls it before any message after which it
|
|
10
|
+
* may be terminated.
|
|
11
|
+
*
|
|
12
|
+
* Enabled by the same `enableWorkerTrace` request as the host. Until then
|
|
13
|
+
* every method is a null check, so an untraced load pays nothing measurable.
|
|
14
|
+
*/
|
|
15
|
+
export interface WorkerPhaseTrace {
|
|
16
|
+
readonly enabled: boolean;
|
|
17
|
+
/** Feed each inbound message first; `true` = it was the enable request (consumed). */
|
|
18
|
+
accept(data: unknown): boolean;
|
|
19
|
+
/** Open a span; `-1` when disabled. */
|
|
20
|
+
begin(name: string, attrs?: TraceAttrs): number;
|
|
21
|
+
end(token: number, attrs?: TraceAttrs): void;
|
|
22
|
+
/** Time `fn`; a returned promise ends the span when it settles. Returns `fn`'s result unchanged. */
|
|
23
|
+
span<T>(name: string, fn: () => T, attrs?: TraceAttrs): T;
|
|
24
|
+
/**
|
|
25
|
+
* Sequential phases: end the previous `step` span and open `name` (or just
|
|
26
|
+
* end it, for `null`). A repeated name keeps the open span, so a progress
|
|
27
|
+
* callback that reports one phase many times can drive this directly.
|
|
28
|
+
*/
|
|
29
|
+
step(name: string | null): void;
|
|
30
|
+
/** Post every finished span now (nothing when there are none). */
|
|
31
|
+
flush(): void;
|
|
32
|
+
}
|
|
33
|
+
export interface WorkerPhaseTraceOptions {
|
|
34
|
+
post: (message: TraceSpansMessage) => void;
|
|
35
|
+
now?: () => number;
|
|
36
|
+
timeOrigin?: number;
|
|
37
|
+
}
|
|
38
|
+
export declare function createWorkerPhaseTrace(options: WorkerPhaseTraceOptions): WorkerPhaseTrace;
|
|
39
|
+
//# sourceMappingURL=worker-phases.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-phases.d.ts","sourceRoot":"","sources":["../src/worker-phases.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAIL,KAAK,iBAAiB,EAEvB,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,sFAAsF;IACtF,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IAC/B,uCAAuC;IACvC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAChD,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC7C,oGAAoG;IACpG,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC;IAC1D;;;;OAIG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAChC,kEAAkE;IAClE,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC3C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,gBAAgB,CA4CzF"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import { createWorkerSpanRecorder, TRACE_ENABLE_MESSAGE, TRACE_SPANS_MESSAGE, } from './worker.js';
|
|
5
|
+
export function createWorkerPhaseTrace(options) {
|
|
6
|
+
let recorder = null;
|
|
7
|
+
let stepName = null;
|
|
8
|
+
let stepToken = -1;
|
|
9
|
+
const trace = {
|
|
10
|
+
get enabled() { return recorder !== null; },
|
|
11
|
+
accept(data) {
|
|
12
|
+
if (data?.type !== TRACE_ENABLE_MESSAGE)
|
|
13
|
+
return false;
|
|
14
|
+
const thread = data.thread;
|
|
15
|
+
recorder = createWorkerSpanRecorder(typeof thread === 'string' ? thread : 'worker', options.now, options.timeOrigin);
|
|
16
|
+
return true;
|
|
17
|
+
},
|
|
18
|
+
begin: (name, attrs) => recorder?.begin(name, attrs) ?? -1,
|
|
19
|
+
end: (token, attrs) => { if (token >= 0)
|
|
20
|
+
recorder?.end(token, attrs); },
|
|
21
|
+
span(name, fn, attrs) {
|
|
22
|
+
if (!recorder)
|
|
23
|
+
return fn();
|
|
24
|
+
const token = recorder.begin(name, attrs);
|
|
25
|
+
let result;
|
|
26
|
+
try {
|
|
27
|
+
result = fn();
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
trace.end(token, { error: true });
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
if (isThenable(result)) {
|
|
34
|
+
// Observes settlement only; the caller still owns the returned promise.
|
|
35
|
+
result.then(() => trace.end(token), () => trace.end(token, { error: true }));
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
trace.end(token);
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
},
|
|
42
|
+
step(name) {
|
|
43
|
+
if (!recorder || name === stepName)
|
|
44
|
+
return;
|
|
45
|
+
trace.end(stepToken);
|
|
46
|
+
stepName = name;
|
|
47
|
+
stepToken = name === null ? -1 : recorder.begin(name);
|
|
48
|
+
},
|
|
49
|
+
flush() {
|
|
50
|
+
const payload = recorder?.drain();
|
|
51
|
+
if (payload)
|
|
52
|
+
options.post({ type: TRACE_SPANS_MESSAGE, payload });
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
return trace;
|
|
56
|
+
}
|
|
57
|
+
function isThenable(value) {
|
|
58
|
+
return typeof value === 'object' && value !== null && typeof value.then === 'function';
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=worker-phases.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-phases.js","sourceRoot":"","sources":["../src/worker-phases.ts"],"names":[],"mappings":"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"}
|
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type PerfCounterRegistry } from './counters.js';
|
|
2
|
+
import type { LoadTrace } from './load-trace.js';
|
|
3
|
+
import type { TraceAttrs, WorkerTracePayload } from './types.js';
|
|
4
|
+
/** Main -> worker: start recording, labelling spans with `thread`. */
|
|
5
|
+
export declare const TRACE_ENABLE_MESSAGE = "load-trace:enable";
|
|
6
|
+
/** Worker -> main: finished spans, see `WorkerTracePayload`. */
|
|
7
|
+
export declare const TRACE_SPANS_MESSAGE = "load-trace:spans";
|
|
8
|
+
export interface TraceEnableMessage {
|
|
9
|
+
type: typeof TRACE_ENABLE_MESSAGE;
|
|
10
|
+
thread: string;
|
|
11
|
+
}
|
|
12
|
+
export interface TraceSpansMessage {
|
|
13
|
+
type: typeof TRACE_SPANS_MESSAGE;
|
|
14
|
+
payload: WorkerTracePayload;
|
|
15
|
+
}
|
|
16
|
+
export interface WorkerSpanRecorder {
|
|
17
|
+
readonly thread: string;
|
|
18
|
+
begin(name: string, attrs?: TraceAttrs): number;
|
|
19
|
+
end(token: number, attrs?: TraceAttrs): void;
|
|
20
|
+
/** Finish named work before publishing an event that makes the host terminate us. */
|
|
21
|
+
endNamed(name: string): void;
|
|
22
|
+
/** Finished spans since the last drain, or `null` when there are none. */
|
|
23
|
+
drain(): WorkerTracePayload | null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Records spans on the worker's own clock. `timeOrigin` travels with every
|
|
27
|
+
* drained payload so the main thread can shift the spans onto its clock: a
|
|
28
|
+
* worker's `performance.now()` is relative to the WORKER's creation time.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createWorkerSpanRecorder(thread: string, now?: () => number, timeOrigin?: number): WorkerSpanRecorder;
|
|
31
|
+
export interface WorkerTraceHostOptions {
|
|
32
|
+
/** Request message `type` -> span name. Unlisted types are never traced. */
|
|
33
|
+
spanNames: Readonly<Record<string, string>>;
|
|
34
|
+
/** Types traced only on their first occurrence (e.g. the first geometry chunk). */
|
|
35
|
+
onceTypes?: readonly string[];
|
|
36
|
+
post: (message: TraceSpansMessage) => void;
|
|
37
|
+
now?: () => number;
|
|
38
|
+
timeOrigin?: number;
|
|
39
|
+
/**
|
|
40
|
+
* This worker's counter registry (#6957), switched on with tracing. Its
|
|
41
|
+
* increments ride back with the spans after every handler, so per-chunk
|
|
42
|
+
* work (wasm copies, ...) reaches the load even for untraced message types.
|
|
43
|
+
*/
|
|
44
|
+
counters?: PerfCounterRegistry;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Wrap a worker's message dispatch. Until the main thread sends
|
|
48
|
+
* `TRACE_ENABLE_MESSAGE` the wrapper is one null check and a direct call, so
|
|
49
|
+
* an untraced load pays nothing measurable. Once enabled, each listed message
|
|
50
|
+
* type becomes one span, posted back as soon as its handler settles, and any
|
|
51
|
+
* handler that moved a counter posts those increments (#6957).
|
|
52
|
+
*/
|
|
53
|
+
/**
|
|
54
|
+
* The wrapped dispatch, plus `flush()`: post what is pending NOW. A worker the
|
|
55
|
+
* main thread terminates on a message it is about to send (the pre-pass
|
|
56
|
+
* worker on its final event) calls it first, or the counters die with it.
|
|
57
|
+
*/
|
|
58
|
+
export type WorkerTraceHost = ((data: unknown, run: () => Promise<void>) => Promise<void>) & {
|
|
59
|
+
/** Drain finished work; optionally finish all open spans with the given name before a terminal event. */
|
|
60
|
+
flush(completedSpan?: string): void;
|
|
61
|
+
};
|
|
62
|
+
export declare function createWorkerTraceHost(options: WorkerTraceHostOptions): WorkerTraceHost;
|
|
63
|
+
/** Ask `worker` to record spans for `trace` (no-op when tracing is off). Returns `worker`. */
|
|
64
|
+
export declare function enableWorkerTrace<W extends {
|
|
65
|
+
postMessage(message: unknown): void;
|
|
66
|
+
}>(worker: W, trace: LoadTrace, thread: string): W;
|
|
67
|
+
export declare function isTraceSpansMessage(message: unknown): message is TraceSpansMessage;
|
|
68
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAc,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE7E,sEAAsE;AACtE,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AACxD,gEAAgE;AAChE,eAAO,MAAM,mBAAmB,qBAAqB,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IAAG,IAAI,EAAE,OAAO,oBAAoB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACzF,MAAM,WAAW,iBAAiB;IAAG,IAAI,EAAE,OAAO,mBAAmB,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE;AAEpG,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAChD,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC7C,qFAAqF;IACrF,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,0EAA0E;IAC1E,KAAK,IAAI,kBAAkB,GAAG,IAAI,CAAC;CACpC;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,MAAM,MAAgC,EAC3C,UAAU,GAAE,MAA+B,GAC1C,kBAAkB,CAkCpB;AAED,MAAM,WAAW,sBAAsB;IACrC,4EAA4E;IAC5E,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5C,mFAAmF;IACnF,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,IAAI,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC3C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,mBAAmB,CAAC;CAChC;AAED;;;;;;GAMG;AACH;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG;IAC3F,yGAAyG;IACzG,KAAK,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CA0CtF;AAED,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAAC,CAAC,SAAS;IAAE,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,EACjF,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAC1C,CAAC,CAGH;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,iBAAiB,CAElF"}
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import { perfCounters } from './counters.js';
|
|
5
|
+
/** Main -> worker: start recording, labelling spans with `thread`. */
|
|
6
|
+
export const TRACE_ENABLE_MESSAGE = 'load-trace:enable';
|
|
7
|
+
/** Worker -> main: finished spans, see `WorkerTracePayload`. */
|
|
8
|
+
export const TRACE_SPANS_MESSAGE = 'load-trace:spans';
|
|
9
|
+
/**
|
|
10
|
+
* Records spans on the worker's own clock. `timeOrigin` travels with every
|
|
11
|
+
* drained payload so the main thread can shift the spans onto its clock: a
|
|
12
|
+
* worker's `performance.now()` is relative to the WORKER's creation time.
|
|
13
|
+
*/
|
|
14
|
+
export function createWorkerSpanRecorder(thread, now = () => performance.now(), timeOrigin = performance.timeOrigin) {
|
|
15
|
+
const open = new Map();
|
|
16
|
+
let done = [];
|
|
17
|
+
let next = 0;
|
|
18
|
+
return {
|
|
19
|
+
thread,
|
|
20
|
+
begin(name, attrs) {
|
|
21
|
+
const token = next++;
|
|
22
|
+
open.set(token, { name, start: now(), end: Number.NaN, ...(attrs ? { attrs: { ...attrs } } : {}) });
|
|
23
|
+
return token;
|
|
24
|
+
},
|
|
25
|
+
end(token, attrs) {
|
|
26
|
+
const span = open.get(token);
|
|
27
|
+
if (!span)
|
|
28
|
+
return;
|
|
29
|
+
open.delete(token);
|
|
30
|
+
span.end = now();
|
|
31
|
+
if (attrs)
|
|
32
|
+
span.attrs = { ...span.attrs, ...attrs };
|
|
33
|
+
done.push(span);
|
|
34
|
+
},
|
|
35
|
+
endNamed(name) {
|
|
36
|
+
for (const [token, span] of open) {
|
|
37
|
+
if (span.name !== name)
|
|
38
|
+
continue;
|
|
39
|
+
open.delete(token);
|
|
40
|
+
span.end = now();
|
|
41
|
+
done.push(span);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
drain() {
|
|
45
|
+
if (done.length === 0)
|
|
46
|
+
return null;
|
|
47
|
+
const spans = done;
|
|
48
|
+
done = [];
|
|
49
|
+
return { thread, timeOrigin, spans };
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function createWorkerTraceHost(options) {
|
|
54
|
+
let recorder = null;
|
|
55
|
+
const seenOnce = new Set();
|
|
56
|
+
const once = new Set(options.onceTypes ?? []);
|
|
57
|
+
const counters = options.counters ?? perfCounters;
|
|
58
|
+
const flush = (rec) => {
|
|
59
|
+
const payload = rec.drain();
|
|
60
|
+
const moved = counters.drain();
|
|
61
|
+
if (!payload && !moved)
|
|
62
|
+
return;
|
|
63
|
+
const out = payload ?? { thread: rec.thread, timeOrigin: options.timeOrigin ?? performance.timeOrigin, spans: [] };
|
|
64
|
+
options.post({ type: TRACE_SPANS_MESSAGE, payload: moved ? { ...out, counters: moved } : out });
|
|
65
|
+
};
|
|
66
|
+
const host = async (data, run) => {
|
|
67
|
+
const type = data?.type;
|
|
68
|
+
if (type === TRACE_ENABLE_MESSAGE) {
|
|
69
|
+
const thread = data.thread;
|
|
70
|
+
recorder = createWorkerSpanRecorder(typeof thread === 'string' ? thread : 'worker', options.now, options.timeOrigin);
|
|
71
|
+
seenOnce.clear(); // a pooled worker (#7036) serves several loads: each gets its first-chunk span
|
|
72
|
+
counters.enable();
|
|
73
|
+
counters.drain(); // only what happens from here on belongs to a load
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const rec = recorder;
|
|
77
|
+
if (!rec)
|
|
78
|
+
return run();
|
|
79
|
+
const name = typeof type === 'string' ? options.spanNames[type] : undefined;
|
|
80
|
+
const traced = name !== undefined && !(once.has(type) && seenOnce.has(type));
|
|
81
|
+
if (traced && once.has(type))
|
|
82
|
+
seenOnce.add(type);
|
|
83
|
+
const token = traced ? rec.begin(name) : -1;
|
|
84
|
+
try {
|
|
85
|
+
await run();
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
if (traced)
|
|
89
|
+
rec.end(token);
|
|
90
|
+
flush(rec);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
return Object.assign(host, {
|
|
94
|
+
flush: (completedSpan) => {
|
|
95
|
+
if (!recorder)
|
|
96
|
+
return;
|
|
97
|
+
if (completedSpan !== undefined)
|
|
98
|
+
recorder.endNamed(completedSpan);
|
|
99
|
+
flush(recorder);
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/** Ask `worker` to record spans for `trace` (no-op when tracing is off). Returns `worker`. */
|
|
104
|
+
export function enableWorkerTrace(worker, trace, thread) {
|
|
105
|
+
if (trace.enabled)
|
|
106
|
+
worker.postMessage({ type: TRACE_ENABLE_MESSAGE, thread });
|
|
107
|
+
return worker;
|
|
108
|
+
}
|
|
109
|
+
export function isTraceSpansMessage(message) {
|
|
110
|
+
return message?.type === TRACE_SPANS_MESSAGE;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
|