traceperf 0.2.1 → 1.3.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 +608 -253
- package/dist/__tests__/helpers/console.d.ts +9 -0
- package/dist/__tests__/helpers/console.js +25 -0
- package/dist/__tests__/helpers/console.js.map +1 -0
- package/dist/auto.d.ts +30 -0
- package/dist/auto.js +91 -0
- package/dist/auto.js.map +1 -0
- package/dist/browser.d.ts +52 -144
- package/dist/browser.js +112 -439
- package/dist/browser.js.map +1 -1
- package/dist/cli.d.ts +68 -0
- package/dist/cli.js +528 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/allocations.d.ts +38 -0
- package/dist/core/allocations.js +85 -0
- package/dist/core/allocations.js.map +1 -0
- package/dist/core/cpu-profile.d.ts +84 -0
- package/dist/core/cpu-profile.js +181 -0
- package/dist/core/cpu-profile.js.map +1 -0
- package/dist/core/env.d.ts +10 -0
- package/dist/core/env.js +40 -0
- package/dist/core/env.js.map +1 -0
- package/dist/core/event-loop.d.ts +30 -0
- package/dist/core/event-loop.js +43 -0
- package/dist/core/event-loop.js.map +1 -0
- package/dist/core/explain.d.ts +48 -0
- package/dist/core/explain.js +236 -0
- package/dist/core/explain.js.map +1 -0
- package/dist/core/exporters.d.ts +111 -0
- package/dist/core/exporters.js +236 -0
- package/dist/core/exporters.js.map +1 -0
- package/dist/core/index.d.ts +9 -3
- package/dist/core/index.js +9 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/optimized-performance-monitor.d.ts +180 -26
- package/dist/core/optimized-performance-monitor.js +687 -175
- package/dist/core/optimized-performance-monitor.js.map +1 -1
- package/dist/core/prometheus.d.ts +36 -0
- package/dist/core/prometheus.js +93 -0
- package/dist/core/prometheus.js.map +1 -0
- package/dist/core/records.d.ts +97 -0
- package/dist/core/records.js +87 -0
- package/dist/core/records.js.map +1 -0
- package/dist/core/stats.d.ts +45 -0
- package/dist/core/stats.js +136 -0
- package/dist/core/stats.js.map +1 -0
- package/dist/core/timeline.d.ts +28 -0
- package/dist/core/timeline.js +65 -0
- package/dist/core/timeline.js.map +1 -0
- package/dist/drivers.d.ts +51 -0
- package/dist/drivers.js +205 -0
- package/dist/drivers.js.map +1 -0
- package/dist/esm/auto.d.ts +30 -0
- package/dist/esm/auto.js +87 -0
- package/dist/esm/auto.js.map +1 -0
- package/dist/esm/browser.d.ts +52 -144
- package/dist/esm/browser.js +101 -445
- package/dist/esm/browser.js.map +1 -1
- package/dist/esm/cli.d.ts +68 -0
- package/dist/esm/cli.js +520 -0
- package/dist/esm/cli.js.map +1 -0
- package/dist/esm/core/allocations.d.ts +38 -0
- package/dist/esm/core/allocations.js +82 -0
- package/dist/esm/core/allocations.js.map +1 -0
- package/dist/esm/core/cpu-profile.d.ts +84 -0
- package/dist/esm/core/cpu-profile.js +176 -0
- package/dist/esm/core/cpu-profile.js.map +1 -0
- package/dist/esm/core/env.d.ts +10 -0
- package/dist/esm/core/env.js +35 -0
- package/dist/esm/core/env.js.map +1 -0
- package/dist/esm/core/event-loop.d.ts +30 -0
- package/dist/esm/core/event-loop.js +40 -0
- package/dist/esm/core/event-loop.js.map +1 -0
- package/dist/esm/core/explain.d.ts +48 -0
- package/dist/esm/core/explain.js +232 -0
- package/dist/esm/core/explain.js.map +1 -0
- package/dist/esm/core/exporters.d.ts +111 -0
- package/dist/esm/core/exporters.js +229 -0
- package/dist/esm/core/exporters.js.map +1 -0
- package/dist/esm/core/index.d.ts +10 -4
- package/dist/esm/core/index.js +10 -4
- package/dist/esm/core/index.js.map +1 -1
- package/dist/esm/core/optimized-performance-monitor.d.ts +180 -26
- package/dist/esm/core/optimized-performance-monitor.js +685 -173
- package/dist/esm/core/optimized-performance-monitor.js.map +1 -1
- package/dist/esm/core/prometheus.d.ts +36 -0
- package/dist/esm/core/prometheus.js +89 -0
- package/dist/esm/core/prometheus.js.map +1 -0
- package/dist/esm/core/records.d.ts +97 -0
- package/dist/esm/core/records.js +82 -0
- package/dist/esm/core/records.js.map +1 -0
- package/dist/esm/core/stats.d.ts +45 -0
- package/dist/esm/core/stats.js +131 -0
- package/dist/esm/core/stats.js.map +1 -0
- package/dist/esm/core/timeline.d.ts +28 -0
- package/dist/esm/core/timeline.js +62 -0
- package/dist/esm/core/timeline.js.map +1 -0
- package/dist/esm/drivers.d.ts +51 -0
- package/dist/esm/drivers.js +197 -0
- package/dist/esm/drivers.js.map +1 -0
- package/dist/esm/index.d.ts +13 -5
- package/dist/esm/index.js +21 -17
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/instrument.d.ts +88 -0
- package/dist/esm/instrument.js +274 -0
- package/dist/esm/instrument.js.map +1 -0
- package/dist/esm/integrations.d.ts +70 -0
- package/dist/esm/integrations.js +197 -0
- package/dist/esm/integrations.js.map +1 -0
- package/dist/esm/node-builtins.d.ts +1 -0
- package/dist/esm/node-builtins.js +32 -0
- package/dist/esm/node-builtins.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/register.d.ts +20 -0
- package/dist/esm/register.js +98 -0
- package/dist/esm/register.js.map +1 -0
- package/dist/esm/report-html.d.ts +11 -0
- package/dist/esm/report-html.js +106 -0
- package/dist/esm/report-html.js.map +1 -0
- package/dist/esm/types/index.d.ts +4 -80
- package/dist/esm/types/index.js +1 -2
- package/dist/esm/types/index.js.map +1 -1
- package/dist/esm/types/options.d.ts +72 -0
- package/dist/esm/types/options.js +2 -0
- package/dist/esm/types/options.js.map +1 -0
- package/dist/esm/watch.d.ts +17 -0
- package/dist/esm/watch.js +87 -0
- package/dist/esm/watch.js.map +1 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.js +26 -29
- package/dist/index.js.map +1 -1
- package/dist/instrument.d.ts +88 -0
- package/dist/instrument.js +282 -0
- package/dist/instrument.js.map +1 -0
- package/dist/integrations.d.ts +70 -0
- package/dist/integrations.js +205 -0
- package/dist/integrations.js.map +1 -0
- package/dist/node-builtins.d.ts +1 -0
- package/dist/node-builtins.js +67 -0
- package/dist/node-builtins.js.map +1 -0
- package/dist/register.d.ts +20 -0
- package/dist/register.js +103 -0
- package/dist/register.js.map +1 -0
- package/dist/report-html.d.ts +11 -0
- package/dist/report-html.js +109 -0
- package/dist/report-html.js.map +1 -0
- package/dist/traceperf.browser.js +1910 -0
- package/dist/traceperf.browser.min.js +6 -0
- package/dist/types/index.d.ts +4 -80
- package/dist/types/index.js +1 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/options.d.ts +72 -0
- package/dist/types/{config.js → options.js} +1 -1
- package/dist/types/options.js.map +1 -0
- package/dist/watch.d.ts +17 -0
- package/dist/watch.js +90 -0
- package/dist/watch.js.map +1 -0
- package/package.json +99 -19
- package/dist/core/config.d.ts +0 -77
- package/dist/core/config.js +0 -116
- package/dist/core/config.js.map +0 -1
- package/dist/core/constants.d.ts +0 -78
- package/dist/core/constants.js +0 -109
- package/dist/core/constants.js.map +0 -1
- package/dist/core/logger.d.ts +0 -97
- package/dist/core/logger.js +0 -186
- package/dist/core/logger.js.map +0 -1
- package/dist/esm/core/config.d.ts +0 -77
- package/dist/esm/core/config.js +0 -112
- package/dist/esm/core/config.js.map +0 -1
- package/dist/esm/core/constants.d.ts +0 -78
- package/dist/esm/core/constants.js +0 -106
- package/dist/esm/core/constants.js.map +0 -1
- package/dist/esm/core/logger.d.ts +0 -97
- package/dist/esm/core/logger.js +0 -182
- package/dist/esm/core/logger.js.map +0 -1
- package/dist/esm/formatters/ascii.d.ts +0 -52
- package/dist/esm/formatters/ascii.js +0 -100
- package/dist/esm/formatters/ascii.js.map +0 -1
- package/dist/esm/formatters/cli.d.ts +0 -50
- package/dist/esm/formatters/cli.js +0 -96
- package/dist/esm/formatters/cli.js.map +0 -1
- package/dist/esm/formatters/index.d.ts +0 -3
- package/dist/esm/formatters/index.js +0 -4
- package/dist/esm/formatters/index.js.map +0 -1
- package/dist/esm/formatters/json.d.ts +0 -45
- package/dist/esm/formatters/json.js +0 -85
- package/dist/esm/formatters/json.js.map +0 -1
- package/dist/esm/trackers/execution.d.ts +0 -101
- package/dist/esm/trackers/execution.js +0 -226
- package/dist/esm/trackers/execution.js.map +0 -1
- package/dist/esm/trackers/index.d.ts +0 -2
- package/dist/esm/trackers/index.js +0 -3
- package/dist/esm/trackers/index.js.map +0 -1
- package/dist/esm/trackers/performance.d.ts +0 -68
- package/dist/esm/trackers/performance.js +0 -112
- package/dist/esm/trackers/performance.js.map +0 -1
- package/dist/esm/types/config.d.ts +0 -142
- package/dist/esm/types/config.js +0 -2
- package/dist/esm/types/config.js.map +0 -1
- package/dist/esm/types/logger.d.ts +0 -147
- package/dist/esm/types/logger.js +0 -2
- package/dist/esm/types/logger.js.map +0 -1
- package/dist/esm/utils/colors.d.ts +0 -87
- package/dist/esm/utils/colors.js +0 -146
- package/dist/esm/utils/colors.js.map +0 -1
- package/dist/esm/utils/index.d.ts +0 -3
- package/dist/esm/utils/index.js +0 -4
- package/dist/esm/utils/index.js.map +0 -1
- package/dist/esm/utils/proxy-tracker.d.ts +0 -68
- package/dist/esm/utils/proxy-tracker.js +0 -127
- package/dist/esm/utils/proxy-tracker.js.map +0 -1
- package/dist/esm/utils/stack.d.ts +0 -63
- package/dist/esm/utils/stack.js +0 -110
- package/dist/esm/utils/stack.js.map +0 -1
- package/dist/esm/utils/string.d.ts +0 -29
- package/dist/esm/utils/string.js +0 -45
- package/dist/esm/utils/string.js.map +0 -1
- package/dist/esm/utils/timing.d.ts +0 -65
- package/dist/esm/utils/timing.js +0 -99
- package/dist/esm/utils/timing.js.map +0 -1
- package/dist/formatters/ascii.d.ts +0 -52
- package/dist/formatters/ascii.js +0 -104
- package/dist/formatters/ascii.js.map +0 -1
- package/dist/formatters/cli.d.ts +0 -50
- package/dist/formatters/cli.js +0 -100
- package/dist/formatters/cli.js.map +0 -1
- package/dist/formatters/index.d.ts +0 -3
- package/dist/formatters/index.js +0 -20
- package/dist/formatters/index.js.map +0 -1
- package/dist/formatters/json.d.ts +0 -45
- package/dist/formatters/json.js +0 -89
- package/dist/formatters/json.js.map +0 -1
- package/dist/trackers/execution.d.ts +0 -101
- package/dist/trackers/execution.js +0 -230
- package/dist/trackers/execution.js.map +0 -1
- package/dist/trackers/index.d.ts +0 -2
- package/dist/trackers/index.js +0 -19
- package/dist/trackers/index.js.map +0 -1
- package/dist/trackers/performance.d.ts +0 -68
- package/dist/trackers/performance.js +0 -116
- package/dist/trackers/performance.js.map +0 -1
- package/dist/types/config.d.ts +0 -142
- package/dist/types/config.js.map +0 -1
- package/dist/types/logger.d.ts +0 -147
- package/dist/types/logger.js +0 -3
- package/dist/types/logger.js.map +0 -1
- package/dist/types.d.ts +0 -65
- package/dist/types.js +0 -6
- package/dist/types.js.map +0 -1
- package/dist/utils/colors.d.ts +0 -87
- package/dist/utils/colors.js +0 -160
- package/dist/utils/colors.js.map +0 -1
- package/dist/utils/index.d.ts +0 -3
- package/dist/utils/index.js +0 -20
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/proxy-tracker.d.ts +0 -68
- package/dist/utils/proxy-tracker.js +0 -131
- package/dist/utils/proxy-tracker.js.map +0 -1
- package/dist/utils/stack.d.ts +0 -63
- package/dist/utils/stack.js +0 -116
- package/dist/utils/stack.js.map +0 -1
- package/dist/utils/string.d.ts +0 -29
- package/dist/utils/string.js +0 -50
- package/dist/utils/string.js.map +0 -1
- package/dist/utils/timing.d.ts +0 -65
- package/dist/utils/timing.js +0 -106
- package/dist/utils/timing.js.map +0 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface AllocationSite {
|
|
2
|
+
functionName: string;
|
|
3
|
+
url: string;
|
|
4
|
+
line: number;
|
|
5
|
+
column: number;
|
|
6
|
+
/** Estimated bytes allocated at this site */
|
|
7
|
+
bytes: number;
|
|
8
|
+
}
|
|
9
|
+
export interface AllocationProfile<T> {
|
|
10
|
+
result: T;
|
|
11
|
+
/** Estimated total bytes allocated while fn ran */
|
|
12
|
+
allocatedBytes: number;
|
|
13
|
+
/** Top allocation sites, largest first */
|
|
14
|
+
sites: AllocationSite[];
|
|
15
|
+
/**
|
|
16
|
+
* Whether allocations that were garbage-collected before the profile
|
|
17
|
+
* ended are included (V8 >= 11, i.e. Node.js 20+). When false, only
|
|
18
|
+
* allocations still alive at the end are counted.
|
|
19
|
+
*/
|
|
20
|
+
countsCollected: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface ProfileAllocationsOptions {
|
|
23
|
+
/** Average bytes between samples; smaller = more precise, more overhead (default 16384) */
|
|
24
|
+
samplingInterval?: number;
|
|
25
|
+
/** Number of sites to return (default 10) */
|
|
26
|
+
top?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Profile the allocations of `fn` (sync or async).
|
|
30
|
+
*
|
|
31
|
+
* const { result, allocatedBytes, sites } = await profileAllocations(() => buildReport(data));
|
|
32
|
+
*
|
|
33
|
+
* Needs Node.js 20+ to include allocations that were collected again
|
|
34
|
+
* (see `countsCollected`). Only one profile can run at a time per process. Estimates are
|
|
35
|
+
* statistical: expect accuracy within a few percent for allocations well
|
|
36
|
+
* above the sampling interval.
|
|
37
|
+
*/
|
|
38
|
+
export declare function profileAllocations<T>(fn: () => T | Promise<T>, options?: ProfileAllocationsOptions): Promise<AllocationProfile<Awaited<T>>>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Allocation profiling with V8's sampling heap profiler (Node.js only).
|
|
3
|
+
*
|
|
4
|
+
* The heap delta shown in trees is "heap after - heap before", which GC and
|
|
5
|
+
* concurrent work distort. This measures what a piece of code actually
|
|
6
|
+
* allocated, including objects that were collected again, and where.
|
|
7
|
+
*/
|
|
8
|
+
import { loadNodeBuiltin } from './env.js';
|
|
9
|
+
const v8Major = () => {
|
|
10
|
+
const proc = typeof process !== 'undefined' ? process : undefined;
|
|
11
|
+
return proc && proc.versions && proc.versions.v8 ? parseInt(proc.versions.v8, 10) : 0;
|
|
12
|
+
};
|
|
13
|
+
let active = false;
|
|
14
|
+
/**
|
|
15
|
+
* Profile the allocations of `fn` (sync or async).
|
|
16
|
+
*
|
|
17
|
+
* const { result, allocatedBytes, sites } = await profileAllocations(() => buildReport(data));
|
|
18
|
+
*
|
|
19
|
+
* Needs Node.js 20+ to include allocations that were collected again
|
|
20
|
+
* (see `countsCollected`). Only one profile can run at a time per process. Estimates are
|
|
21
|
+
* statistical: expect accuracy within a few percent for allocations well
|
|
22
|
+
* above the sampling interval.
|
|
23
|
+
*/
|
|
24
|
+
export async function profileAllocations(fn, options = {}) {
|
|
25
|
+
var _a, _b;
|
|
26
|
+
const inspector = loadNodeBuiltin('inspector');
|
|
27
|
+
if (!inspector || typeof inspector.Session !== 'function') {
|
|
28
|
+
throw new Error('profileAllocations requires Node.js (node:inspector)');
|
|
29
|
+
}
|
|
30
|
+
if (active)
|
|
31
|
+
throw new Error('profileAllocations: another profile is already running');
|
|
32
|
+
active = true;
|
|
33
|
+
const session = new inspector.Session();
|
|
34
|
+
const post = (method, params) => new Promise((resolve, reject) => session.post(method, params, (err, res) => (err ? reject(err) : resolve(res))));
|
|
35
|
+
session.connect();
|
|
36
|
+
try {
|
|
37
|
+
await post('HeapProfiler.enable');
|
|
38
|
+
await post('HeapProfiler.startSampling', {
|
|
39
|
+
samplingInterval: Math.max(1, (_a = options.samplingInterval) !== null && _a !== void 0 ? _a : 16384),
|
|
40
|
+
// Count objects that were collected again too (V8 >= 11; ignored by older versions)
|
|
41
|
+
includeObjectsCollectedByMajorGC: true,
|
|
42
|
+
includeObjectsCollectedByMinorGC: true,
|
|
43
|
+
});
|
|
44
|
+
let result;
|
|
45
|
+
let stopped;
|
|
46
|
+
try {
|
|
47
|
+
result = await fn();
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
stopped = await post('HeapProfiler.stopSampling');
|
|
51
|
+
await post('HeapProfiler.disable');
|
|
52
|
+
}
|
|
53
|
+
const bySite = new Map();
|
|
54
|
+
let total = 0;
|
|
55
|
+
const visit = (node) => {
|
|
56
|
+
var _a;
|
|
57
|
+
if (node.selfSize > 0) {
|
|
58
|
+
const cf = node.callFrame;
|
|
59
|
+
const key = `${cf.functionName}|${cf.url}|${cf.lineNumber}|${cf.columnNumber}`;
|
|
60
|
+
const site = (_a = bySite.get(key)) !== null && _a !== void 0 ? _a : {
|
|
61
|
+
functionName: cf.functionName || '(anonymous)',
|
|
62
|
+
url: cf.url,
|
|
63
|
+
line: cf.lineNumber + 1,
|
|
64
|
+
column: cf.columnNumber + 1,
|
|
65
|
+
bytes: 0,
|
|
66
|
+
};
|
|
67
|
+
site.bytes += node.selfSize;
|
|
68
|
+
bySite.set(key, site);
|
|
69
|
+
total += node.selfSize;
|
|
70
|
+
}
|
|
71
|
+
(node.children || []).forEach(visit);
|
|
72
|
+
};
|
|
73
|
+
visit(stopped.profile.head);
|
|
74
|
+
const sites = [...bySite.values()].sort((a, b) => b.bytes - a.bytes).slice(0, Math.max(0, (_b = options.top) !== null && _b !== void 0 ? _b : 10));
|
|
75
|
+
return { result, allocatedBytes: total, sites, countsCollected: v8Major() >= 11 };
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
session.disconnect();
|
|
79
|
+
active = false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=allocations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allocations.js","sourceRoot":"","sources":["../../../src/core/allocations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Line-level detail for slow calls: runs the V8 sampling profiler (Node.js
|
|
3
|
+
* inspector) and, for every top-level call slower than `threshold`, reports
|
|
4
|
+
* the functions that were on the CPU during it - including code that is not
|
|
5
|
+
* tracked at all.
|
|
6
|
+
*
|
|
7
|
+
* tp.use(profileSlowCalls({
|
|
8
|
+
* threshold: 200,
|
|
9
|
+
* onProfile: (call, p) => console.log(call.label, p.hotspots),
|
|
10
|
+
* }));
|
|
11
|
+
*
|
|
12
|
+
* The profiler runs in windows (default 2 s); results arrive when the window
|
|
13
|
+
* containing the call closes. Samples are process-wide, so concurrent work
|
|
14
|
+
* shows up too. Sampling every 1 ms costs a few percent CPU: enable it while
|
|
15
|
+
* investigating, or on a fraction of instances.
|
|
16
|
+
*/
|
|
17
|
+
import type { ExecutionRecord, TracePerfPlugin } from './records.js';
|
|
18
|
+
export interface Hotspot {
|
|
19
|
+
/** Function name ("(anonymous)" when unnamed) */
|
|
20
|
+
functionName: string;
|
|
21
|
+
/** Script URL or path ("" for native code) */
|
|
22
|
+
url: string;
|
|
23
|
+
/** 1-based line and column */
|
|
24
|
+
line: number;
|
|
25
|
+
column: number;
|
|
26
|
+
/** CPU time sampled in this function itself, in ms */
|
|
27
|
+
selfMs: number;
|
|
28
|
+
}
|
|
29
|
+
export interface SlowCallProfile {
|
|
30
|
+
/** Functions with the most self time during the call, largest first */
|
|
31
|
+
hotspots: Hotspot[];
|
|
32
|
+
/** Time the thread was idle (waiting for I/O or timers) during the call, ms */
|
|
33
|
+
idleMs: number;
|
|
34
|
+
/** Time in garbage collection during the call, ms */
|
|
35
|
+
gcMs: number;
|
|
36
|
+
/** Time on the CPU during the call, ms (hotspots are the top of this) */
|
|
37
|
+
cpuMs: number;
|
|
38
|
+
/** Number of samples that fell within the call */
|
|
39
|
+
samples: number;
|
|
40
|
+
}
|
|
41
|
+
export interface ProfileSlowCallsOptions {
|
|
42
|
+
/** Profile top-level calls at least this slow (ms, default 100) */
|
|
43
|
+
threshold?: number;
|
|
44
|
+
/** Called once per slow call with its profile */
|
|
45
|
+
onProfile: (call: ExecutionRecord, profile: SlowCallProfile) => void;
|
|
46
|
+
/** Profiler window length in ms (default 2000) */
|
|
47
|
+
window?: number;
|
|
48
|
+
/** Sampling interval in microseconds (default 1000) */
|
|
49
|
+
samplingIntervalUs?: number;
|
|
50
|
+
/** Number of hotspots to report (default 5) */
|
|
51
|
+
top?: number;
|
|
52
|
+
/** Skip functions from these paths, e.g. /node_modules/ (default: none) */
|
|
53
|
+
exclude?: RegExp;
|
|
54
|
+
}
|
|
55
|
+
export interface SlowCallProfiler extends TracePerfPlugin {
|
|
56
|
+
/** Whether the inspector was available (false in browsers and edge runtimes) */
|
|
57
|
+
readonly available: boolean;
|
|
58
|
+
/** Close the current window now and deliver pending profiles */
|
|
59
|
+
flush(): Promise<void>;
|
|
60
|
+
/** Stop profiling (pending profiles are delivered first) */
|
|
61
|
+
stop(): Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
/** A node in a V8 CPU profile */
|
|
64
|
+
export interface CpuProfileNode {
|
|
65
|
+
id: number;
|
|
66
|
+
callFrame: {
|
|
67
|
+
functionName: string;
|
|
68
|
+
url: string;
|
|
69
|
+
lineNumber: number;
|
|
70
|
+
columnNumber: number;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** A V8 CPU profile (inspector Profiler.stop result) */
|
|
74
|
+
export interface CpuProfile {
|
|
75
|
+
nodes: CpuProfileNode[];
|
|
76
|
+
startTime: number;
|
|
77
|
+
samples: number[];
|
|
78
|
+
timeDeltas: number[];
|
|
79
|
+
}
|
|
80
|
+
/** Summarise the samples of a profile that fall within [start, end] (ms, profile clock) */
|
|
81
|
+
export declare function summariseProfile(profile: CpuProfile, start: number, end: number, top?: number, exclude?: RegExp): SlowCallProfile;
|
|
82
|
+
/** Text rendering: " 42.0 ms parseRow /app/csv.js:18" */
|
|
83
|
+
export declare function formatHotspots(profile: SlowCallProfile): string;
|
|
84
|
+
export declare function profileSlowCalls(options: ProfileSlowCallsOptions): SlowCallProfiler;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { loadNodeBuiltin } from './env.js';
|
|
2
|
+
/** Summarise the samples of a profile that fall within [start, end] (ms, profile clock) */
|
|
3
|
+
export function summariseProfile(profile, start, end, top = 5, exclude) {
|
|
4
|
+
var _a;
|
|
5
|
+
const nodes = new Map(profile.nodes.map(n => [n.id, n]));
|
|
6
|
+
const self = new Map();
|
|
7
|
+
let idleMs = 0;
|
|
8
|
+
let gcMs = 0;
|
|
9
|
+
let cpuMs = 0;
|
|
10
|
+
let samples = 0;
|
|
11
|
+
let t = profile.startTime / 1000;
|
|
12
|
+
for (let i = 0; i < profile.samples.length; i++) {
|
|
13
|
+
t += (profile.timeDeltas[i] || 0) / 1000;
|
|
14
|
+
// Each sample stands for the interval up to the next one
|
|
15
|
+
const next = i + 1 < profile.timeDeltas.length ? (profile.timeDeltas[i + 1] || 0) / 1000 : 0;
|
|
16
|
+
if (t < start || t > end)
|
|
17
|
+
continue;
|
|
18
|
+
samples++;
|
|
19
|
+
const node = nodes.get(profile.samples[i]);
|
|
20
|
+
if (!node)
|
|
21
|
+
continue;
|
|
22
|
+
const { functionName, url, lineNumber, columnNumber } = node.callFrame;
|
|
23
|
+
if (functionName === '(idle)') {
|
|
24
|
+
idleMs += next;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (functionName === '(garbage collector)') {
|
|
28
|
+
gcMs += next;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (functionName === '(program)' || functionName === '(root)') {
|
|
32
|
+
cpuMs += next;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
cpuMs += next;
|
|
36
|
+
if (exclude && exclude.test(url))
|
|
37
|
+
continue;
|
|
38
|
+
const key = `${functionName}|${url}|${lineNumber}|${columnNumber}`;
|
|
39
|
+
const entry = (_a = self.get(key)) !== null && _a !== void 0 ? _a : {
|
|
40
|
+
functionName: functionName || '(anonymous)', url, line: lineNumber + 1, column: columnNumber + 1, selfMs: 0,
|
|
41
|
+
};
|
|
42
|
+
entry.selfMs += next;
|
|
43
|
+
self.set(key, entry);
|
|
44
|
+
}
|
|
45
|
+
const hotspots = [...self.values()].sort((a, b) => b.selfMs - a.selfMs).slice(0, top);
|
|
46
|
+
return { hotspots, idleMs, gcMs, cpuMs, samples };
|
|
47
|
+
}
|
|
48
|
+
function mergeProfiles(a, b, top) {
|
|
49
|
+
const all = new Map();
|
|
50
|
+
for (const h of [...a.hotspots, ...b.hotspots]) {
|
|
51
|
+
const key = `${h.functionName}|${h.url}|${h.line}|${h.column}`;
|
|
52
|
+
const e = all.get(key);
|
|
53
|
+
if (e)
|
|
54
|
+
e.selfMs += h.selfMs;
|
|
55
|
+
else
|
|
56
|
+
all.set(key, { ...h });
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
hotspots: [...all.values()].sort((x, y) => y.selfMs - x.selfMs).slice(0, top),
|
|
60
|
+
idleMs: a.idleMs + b.idleMs,
|
|
61
|
+
gcMs: a.gcMs + b.gcMs,
|
|
62
|
+
cpuMs: a.cpuMs + b.cpuMs,
|
|
63
|
+
samples: a.samples + b.samples,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Text rendering: " 42.0 ms parseRow /app/csv.js:18" */
|
|
67
|
+
export function formatHotspots(profile) {
|
|
68
|
+
const lines = profile.hotspots.map(h => ` ${h.selfMs.toFixed(1).padStart(7)} ms ${h.functionName} ${h.url ? `${h.url.replace(/^file:\/\//, '')}:${h.line}` : '(native)'}`);
|
|
69
|
+
lines.push(` cpu ${profile.cpuMs.toFixed(1)} ms, idle ${profile.idleMs.toFixed(1)} ms, gc ${profile.gcMs.toFixed(1)} ms`);
|
|
70
|
+
return lines.join('\n');
|
|
71
|
+
}
|
|
72
|
+
/** Same clock as ExecutionRecord.startTime */
|
|
73
|
+
const nowMs = () => {
|
|
74
|
+
const perf = globalThis.performance || (loadNodeBuiltin('perf_hooks') || {}).performance;
|
|
75
|
+
return perf ? perf.now() : Date.now();
|
|
76
|
+
};
|
|
77
|
+
export function profileSlowCalls(options) {
|
|
78
|
+
var _a, _b, _c, _d;
|
|
79
|
+
const threshold = (_a = options.threshold) !== null && _a !== void 0 ? _a : 100;
|
|
80
|
+
const windowMs = (_b = options.window) !== null && _b !== void 0 ? _b : 2000;
|
|
81
|
+
const interval = (_c = options.samplingIntervalUs) !== null && _c !== void 0 ? _c : 1000;
|
|
82
|
+
const top = (_d = options.top) !== null && _d !== void 0 ? _d : 5;
|
|
83
|
+
const inspector = loadNodeBuiltin('inspector');
|
|
84
|
+
const available = !!(inspector && inspector.Session);
|
|
85
|
+
let session;
|
|
86
|
+
let pending = [];
|
|
87
|
+
/** performance.now() at the profile's start, and the profile's own start (ms) */
|
|
88
|
+
let windowStartNow = 0;
|
|
89
|
+
let stopped = false;
|
|
90
|
+
let timer;
|
|
91
|
+
let busy = Promise.resolve();
|
|
92
|
+
/** The previous window, for calls that started before the current one */
|
|
93
|
+
let previous;
|
|
94
|
+
const post = (method, params) => new Promise((resolve, reject) => {
|
|
95
|
+
session.post(method, params || {}, (err, result) => (err ? reject(err) : resolve(result)));
|
|
96
|
+
});
|
|
97
|
+
const startWindow = async () => {
|
|
98
|
+
windowStartNow = nowMs();
|
|
99
|
+
await post('Profiler.start');
|
|
100
|
+
};
|
|
101
|
+
const rotate = (restart) => {
|
|
102
|
+
busy = busy.then(async () => {
|
|
103
|
+
if (!session)
|
|
104
|
+
return;
|
|
105
|
+
const calls = pending;
|
|
106
|
+
pending = [];
|
|
107
|
+
const startNow = windowStartNow;
|
|
108
|
+
const { profile } = await post('Profiler.stop');
|
|
109
|
+
if (restart && !stopped)
|
|
110
|
+
await startWindow();
|
|
111
|
+
// Map the call's times (performance.now clock) onto the profile clock
|
|
112
|
+
const offset = profile.startTime / 1000 - startNow;
|
|
113
|
+
for (const call of calls) {
|
|
114
|
+
let result = summariseProfile(profile, call.startTime + offset, call.endTime + offset, top, options.exclude);
|
|
115
|
+
if (previous && call.startTime < startNow) {
|
|
116
|
+
const earlier = summariseProfile(previous.profile, call.startTime + previous.offset, call.endTime + previous.offset, top, options.exclude);
|
|
117
|
+
result = mergeProfiles(earlier, result, top);
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
options.onProfile(call, result);
|
|
121
|
+
}
|
|
122
|
+
catch (_a) {
|
|
123
|
+
// a failing callback must not stop profiling
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
previous = { profile, offset, startNow };
|
|
127
|
+
}).catch(() => undefined);
|
|
128
|
+
return busy;
|
|
129
|
+
};
|
|
130
|
+
if (available) {
|
|
131
|
+
try {
|
|
132
|
+
session = new inspector.Session();
|
|
133
|
+
session.connect();
|
|
134
|
+
busy = post('Profiler.enable')
|
|
135
|
+
.then(() => post('Profiler.setSamplingInterval', { interval }))
|
|
136
|
+
.then(startWindow)
|
|
137
|
+
.catch(() => { session = undefined; });
|
|
138
|
+
timer = setInterval(() => rotate(true), windowMs);
|
|
139
|
+
if (timer && typeof timer.unref === 'function')
|
|
140
|
+
timer.unref();
|
|
141
|
+
}
|
|
142
|
+
catch (_e) {
|
|
143
|
+
session = undefined;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
name: 'profile-slow-calls',
|
|
148
|
+
available,
|
|
149
|
+
onComplete(root) {
|
|
150
|
+
if (!session || stopped || root.duration < threshold)
|
|
151
|
+
return;
|
|
152
|
+
// Calls reaching back further than the previous window are skipped
|
|
153
|
+
if (root.startTime >= (previous ? previous.startNow : windowStartNow))
|
|
154
|
+
pending.push(root);
|
|
155
|
+
},
|
|
156
|
+
flush: () => rotate(true),
|
|
157
|
+
async stop() {
|
|
158
|
+
if (stopped)
|
|
159
|
+
return;
|
|
160
|
+
stopped = true;
|
|
161
|
+
if (timer)
|
|
162
|
+
clearInterval(timer);
|
|
163
|
+
await rotate(false);
|
|
164
|
+
await busy;
|
|
165
|
+
if (session) {
|
|
166
|
+
try {
|
|
167
|
+
await post('Profiler.disable');
|
|
168
|
+
}
|
|
169
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
170
|
+
session.disconnect();
|
|
171
|
+
session = undefined;
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=cpu-profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cpu-profile.js","sourceRoot":"","sources":["../../../src/core/cpu-profile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment helpers (internal, not part of the public API).
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Hand Node built-ins to loadNodeBuiltin. Node-only entry points (traceperf/auto)
|
|
6
|
+
* import them statically and call this, because the ESM build has no `require`
|
|
7
|
+
* and `process.getBuiltinModule` only exists from Node 20.16 / 22.3.
|
|
8
|
+
*/
|
|
9
|
+
export declare const provideNodeBuiltins: (modules: Record<string, any>) => void;
|
|
10
|
+
export declare const loadNodeBuiltin: (id: string) => any;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment helpers (internal, not part of the public API).
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Load a Node.js built-in module without a static import, so the package
|
|
6
|
+
* still loads (and bundles) in browsers. Returns undefined outside Node.
|
|
7
|
+
*/
|
|
8
|
+
const provided = {};
|
|
9
|
+
/**
|
|
10
|
+
* Hand Node built-ins to loadNodeBuiltin. Node-only entry points (traceperf/auto)
|
|
11
|
+
* import them statically and call this, because the ESM build has no `require`
|
|
12
|
+
* and `process.getBuiltinModule` only exists from Node 20.16 / 22.3.
|
|
13
|
+
*/
|
|
14
|
+
export const provideNodeBuiltins = (modules) => {
|
|
15
|
+
Object.assign(provided, modules);
|
|
16
|
+
};
|
|
17
|
+
export const loadNodeBuiltin = (id) => {
|
|
18
|
+
if (provided[id])
|
|
19
|
+
return provided[id];
|
|
20
|
+
try {
|
|
21
|
+
const proc = typeof process !== 'undefined' ? process : undefined;
|
|
22
|
+
if (!proc || !proc.versions || !proc.versions.node)
|
|
23
|
+
return undefined;
|
|
24
|
+
if (typeof proc.getBuiltinModule === 'function') {
|
|
25
|
+
return proc.getBuiltinModule(id);
|
|
26
|
+
}
|
|
27
|
+
// Indirect require so browser bundlers don't try to bundle the module
|
|
28
|
+
const req = typeof require === 'function' ? require : undefined;
|
|
29
|
+
return req ? req(id) : undefined;
|
|
30
|
+
}
|
|
31
|
+
catch (_a) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../../src/core/env.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,MAAM,QAAQ,GAAwB,EAAE,CAAC;AAEzC;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,OAA4B,EAAQ,EAAE;IACxE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AACnC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,EAAU,EAAO,EAAE;IACjD,IAAI,QAAQ,CAAC,EAAE,CAAC;QAAE,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC;IACtC,IAAI,CAAC;QACH,MAAM,IAAI,GAAQ,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QACvE,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI;YAAE,OAAO,SAAS,CAAC;QACrE,IAAI,OAAO,IAAI,CAAC,gBAAgB,KAAK,UAAU,EAAE,CAAC;YAChD,OAAO,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC;QACD,sEAAsE;QACtE,MAAM,GAAG,GAAwC,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QACrG,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACnC,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Event-loop delay distribution in milliseconds */
|
|
2
|
+
export interface EventLoopStats {
|
|
3
|
+
min: number;
|
|
4
|
+
max: number;
|
|
5
|
+
mean: number;
|
|
6
|
+
stddev: number;
|
|
7
|
+
p50: number;
|
|
8
|
+
p90: number;
|
|
9
|
+
p99: number;
|
|
10
|
+
}
|
|
11
|
+
export interface EventLoopMonitor {
|
|
12
|
+
/** False outside Node.js (or Node < 11.10); stats() then returns null */
|
|
13
|
+
readonly supported: boolean;
|
|
14
|
+
/** Delay distribution since start (or the last reset) */
|
|
15
|
+
stats(): EventLoopStats | null;
|
|
16
|
+
/** Start a new measurement window */
|
|
17
|
+
reset(): void;
|
|
18
|
+
/** Stop sampling */
|
|
19
|
+
stop(): void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Start sampling event-loop delay.
|
|
23
|
+
*
|
|
24
|
+
* const loop = monitorEventLoop();
|
|
25
|
+
* // ... later
|
|
26
|
+
* console.log(loop.stats()); // { p99: 12.3, max: 48.1, ... } (ms)
|
|
27
|
+
*/
|
|
28
|
+
export declare function monitorEventLoop(options?: {
|
|
29
|
+
resolution?: number;
|
|
30
|
+
}): EventLoopMonitor;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event-loop delay monitoring (Node.js only).
|
|
3
|
+
*
|
|
4
|
+
* A tracked call tells you how long *it* took; event-loop delay tells you
|
|
5
|
+
* whether something blocked everything else. Node's monitorEventLoopDelay
|
|
6
|
+
* samples the delay with negligible overhead.
|
|
7
|
+
*/
|
|
8
|
+
import { loadNodeBuiltin } from './env.js';
|
|
9
|
+
/**
|
|
10
|
+
* Start sampling event-loop delay.
|
|
11
|
+
*
|
|
12
|
+
* const loop = monitorEventLoop();
|
|
13
|
+
* // ... later
|
|
14
|
+
* console.log(loop.stats()); // { p99: 12.3, max: 48.1, ... } (ms)
|
|
15
|
+
*/
|
|
16
|
+
export function monitorEventLoop(options = {}) {
|
|
17
|
+
var _a;
|
|
18
|
+
const perfHooks = loadNodeBuiltin('perf_hooks');
|
|
19
|
+
if (!perfHooks || typeof perfHooks.monitorEventLoopDelay !== 'function') {
|
|
20
|
+
return { supported: false, stats: () => null, reset: () => undefined, stop: () => undefined };
|
|
21
|
+
}
|
|
22
|
+
const histogram = perfHooks.monitorEventLoopDelay({ resolution: Math.max(1, (_a = options.resolution) !== null && _a !== void 0 ? _a : 10) });
|
|
23
|
+
histogram.enable();
|
|
24
|
+
const ms = (ns) => (Number.isFinite(ns) ? ns / 1e6 : 0);
|
|
25
|
+
return {
|
|
26
|
+
supported: true,
|
|
27
|
+
stats: () => ({
|
|
28
|
+
min: ms(histogram.min),
|
|
29
|
+
max: ms(histogram.max),
|
|
30
|
+
mean: ms(histogram.mean),
|
|
31
|
+
stddev: ms(histogram.stddev),
|
|
32
|
+
p50: ms(histogram.percentile(50)),
|
|
33
|
+
p90: ms(histogram.percentile(90)),
|
|
34
|
+
p99: ms(histogram.percentile(99)),
|
|
35
|
+
}),
|
|
36
|
+
reset: () => histogram.reset(),
|
|
37
|
+
stop: () => histogram.disable(),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=event-loop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-loop.js","sourceRoot":"","sources":["../../../src/core/event-loop.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAwBxC;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAmC,EAAE;;IACpE,MAAM,SAAS,GAAG,eAAe,CAAC,YAAY,CAAC,CAAC;IAChD,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,CAAC,qBAAqB,KAAK,UAAU,EAAE,CAAC;QACxE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC;IAChG,CAAC;IACD,MAAM,SAAS,GAAG,SAAS,CAAC,qBAAqB,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAA,OAAO,CAAC,UAAU,mCAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IACzG,SAAS,CAAC,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,OAAO;QACL,SAAS,EAAE,IAAI;QACf,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YACZ,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC;YACtB,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC;YACtB,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC;YACxB,MAAM,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC;YAC5B,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACjC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACjC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;SAClC,CAAC;QACF,KAAK,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE;QAC9B,IAAI,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE;KAChC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plain-English explanations of finished trees: what made a call slow.
|
|
3
|
+
*
|
|
4
|
+
* const findings = explain(roots);
|
|
5
|
+
* console.log(formatFindings(findings));
|
|
6
|
+
*
|
|
7
|
+
* Works on records from onComplete / a trace recorder, or on files via
|
|
8
|
+
* `traceperf explain <file>`. Every finding is a heuristic with the numbers
|
|
9
|
+
* that triggered it, so it can be checked against the tree.
|
|
10
|
+
*/
|
|
11
|
+
/** The fields explain() reads; ExecutionRecord satisfies it */
|
|
12
|
+
export interface ExplainNode {
|
|
13
|
+
label: string;
|
|
14
|
+
/** Start time in ms (any clock, consistent within one input) */
|
|
15
|
+
startTime: number;
|
|
16
|
+
duration: number;
|
|
17
|
+
selfDuration: number;
|
|
18
|
+
/** Synchronous part in ms (blocked the event loop); defaults to 0 for async calls */
|
|
19
|
+
syncDuration?: number;
|
|
20
|
+
async?: boolean;
|
|
21
|
+
error?: unknown;
|
|
22
|
+
children: ExplainNode[];
|
|
23
|
+
}
|
|
24
|
+
export type FindingKind = 'blocked' | 'blocking' | 'sequential' | 'repeated' | 'dominant' | 'error';
|
|
25
|
+
export interface Finding {
|
|
26
|
+
kind: FindingKind;
|
|
27
|
+
/** Top-level call the finding is about */
|
|
28
|
+
root: string;
|
|
29
|
+
/** One-sentence explanation */
|
|
30
|
+
message: string;
|
|
31
|
+
/** Estimated milliseconds involved (delay, possible saving, or time taken) */
|
|
32
|
+
ms: number;
|
|
33
|
+
}
|
|
34
|
+
export interface ExplainOptions {
|
|
35
|
+
/** Synchronous work at least this long counts as blocking (ms, default 10) */
|
|
36
|
+
blockingMs?: number;
|
|
37
|
+
/** Ignore findings smaller than this (ms, default 5) */
|
|
38
|
+
minMs?: number;
|
|
39
|
+
/** A child taking at least this fraction of its parent is "dominant" (default 0.6) */
|
|
40
|
+
dominantShare?: number;
|
|
41
|
+
/** Same label this many times under one parent is "repeated" (default 5) */
|
|
42
|
+
repeatCount?: number;
|
|
43
|
+
/** Return at most this many findings, largest first (default 20) */
|
|
44
|
+
max?: number;
|
|
45
|
+
}
|
|
46
|
+
export declare function explain(roots: ExplainNode[], options?: ExplainOptions): Finding[];
|
|
47
|
+
/** Render findings as text, one per line */
|
|
48
|
+
export declare function formatFindings(findings: Finding[]): string;
|