testplane 9.1.1 → 9.2.1
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/build/package.json +2 -2
- package/build/src/base-testplane.d.ts +17 -1
- package/build/src/base-testplane.js +43 -9
- package/build/src/base-testplane.js.map +1 -1
- package/build/src/browser/browser.d.ts +3 -0
- package/build/src/browser/browser.js +4 -2
- package/build/src/browser/browser.js.map +1 -1
- package/build/src/browser/cdp/selectivity/testplane-selectivity.js +19 -19
- package/build/src/browser/cdp/selectivity/testplane-selectivity.js.map +1 -1
- package/build/src/browser/client-scripts/screen-shooter/build/bundle.compat.js +2 -2
- package/build/src/browser/client-scripts/screen-shooter/build/bundle.native.js +2 -2
- package/build/src/browser/client-scripts/screen-shooter/tsc-out/client-scripts/screen-shooter/implementation.js +52 -84
- package/build/src/browser/client-scripts/screen-shooter/tsc-out/client-scripts/screen-shooter/operations.js +4 -1
- package/build/src/browser/commands/assert-view/index.js +25 -2
- package/build/src/browser/commands/assert-view/index.js.map +1 -1
- package/build/src/browser/existing-browser.js +8 -1
- package/build/src/browser/existing-browser.js.map +1 -1
- package/build/src/browser/history/index.d.ts +4 -2
- package/build/src/browser/history/index.js +146 -67
- package/build/src/browser/history/index.js.map +1 -1
- package/build/src/browser/new-browser.js +1 -1
- package/build/src/browser/new-browser.js.map +1 -1
- package/build/src/browser/screen-shooter/elements-screen-shooter.d.ts +5 -0
- package/build/src/browser/screen-shooter/elements-screen-shooter.js +102 -55
- package/build/src/browser/screen-shooter/elements-screen-shooter.js.map +1 -1
- package/build/src/browser/screen-shooter/viewport-screen-shooter.d.ts +4 -0
- package/build/src/browser/screen-shooter/viewport-screen-shooter.js +29 -7
- package/build/src/browser/screen-shooter/viewport-screen-shooter.js.map +1 -1
- package/build/src/browser-pool/basic-pool.d.ts +4 -3
- package/build/src/browser-pool/basic-pool.js +39 -7
- package/build/src/browser-pool/basic-pool.js.map +1 -1
- package/build/src/browser-pool/caching-pool.d.ts +3 -2
- package/build/src/browser-pool/caching-pool.js +19 -7
- package/build/src/browser-pool/caching-pool.js.map +1 -1
- package/build/src/browser-pool/index.d.ts +2 -1
- package/build/src/browser-pool/index.js +8 -4
- package/build/src/browser-pool/index.js.map +1 -1
- package/build/src/browser-pool/limited-pool.d.ts +5 -1
- package/build/src/browser-pool/limited-pool.js +42 -4
- package/build/src/browser-pool/limited-pool.js.map +1 -1
- package/build/src/browser-pool/per-browser-limited-pool.d.ts +2 -2
- package/build/src/browser-pool/per-browser-limited-pool.js +2 -1
- package/build/src/browser-pool/per-browser-limited-pool.js.map +1 -1
- package/build/src/browser-pool/types.d.ts +8 -0
- package/build/src/cli/commands/config/index.js +7 -3
- package/build/src/cli/commands/config/index.js.map +1 -1
- package/build/src/cli/commands/install-deps/index.js +46 -38
- package/build/src/cli/commands/install-deps/index.js.map +1 -1
- package/build/src/cli/commands/list-browsers/index.js +15 -11
- package/build/src/cli/commands/list-browsers/index.js.map +1 -1
- package/build/src/cli/commands/list-tests/index.js +25 -21
- package/build/src/cli/commands/list-tests/index.js.map +1 -1
- package/build/src/cli/commands/selectivity-merge-dumps/index.js +12 -5
- package/build/src/cli/commands/selectivity-merge-dumps/index.js.map +1 -1
- package/build/src/cli/index.js +10 -6
- package/build/src/cli/index.js.map +1 -1
- package/build/src/config/defaults.d.ts +5 -0
- package/build/src/config/defaults.js +4 -0
- package/build/src/config/defaults.js.map +1 -1
- package/build/src/config/index.d.ts +6 -4
- package/build/src/config/index.js +60 -19
- package/build/src/config/index.js.map +1 -1
- package/build/src/config/options.js +26 -0
- package/build/src/config/options.js.map +1 -1
- package/build/src/config/types.d.ts +19 -0
- package/build/src/constants/process-messages.d.ts +3 -0
- package/build/src/constants/process-messages.js +4 -1
- package/build/src/constants/process-messages.js.map +1 -1
- package/build/src/events/async-emitter/index.d.ts +38 -0
- package/build/src/events/async-emitter/index.js +248 -5
- package/build/src/events/async-emitter/index.js.map +1 -1
- package/build/src/events/index.d.ts +3 -0
- package/build/src/events/index.js +1 -0
- package/build/src/events/index.js.map +1 -1
- package/build/src/index.d.ts +2 -1
- package/build/src/profiler/analysis/aggregator.d.ts +19 -0
- package/build/src/profiler/analysis/aggregator.js +229 -0
- package/build/src/profiler/analysis/aggregator.js.map +1 -0
- package/build/src/profiler/analysis/analyzers.d.ts +10 -0
- package/build/src/profiler/analysis/analyzers.js +1228 -0
- package/build/src/profiler/analysis/analyzers.js.map +1 -0
- package/build/src/profiler/analysis/compact.d.ts +5 -0
- package/build/src/profiler/analysis/compact.js +46 -0
- package/build/src/profiler/analysis/compact.js.map +1 -0
- package/build/src/profiler/analysis/finding-order.d.ts +4 -0
- package/build/src/profiler/analysis/finding-order.js +28 -0
- package/build/src/profiler/analysis/finding-order.js.map +1 -0
- package/build/src/profiler/analysis/outliers.d.ts +6 -0
- package/build/src/profiler/analysis/outliers.js +49 -0
- package/build/src/profiler/analysis/outliers.js.map +1 -0
- package/build/src/profiler/analysis/policy-v1.d.ts +71 -0
- package/build/src/profiler/analysis/policy-v1.js +43 -0
- package/build/src/profiler/analysis/policy-v1.js.map +1 -0
- package/build/src/profiler/collectors/module-graph.d.ts +14 -0
- package/build/src/profiler/collectors/module-graph.js +99 -0
- package/build/src/profiler/collectors/module-graph.js.map +1 -0
- package/build/src/profiler/collectors/pool-observer.d.ts +9 -0
- package/build/src/profiler/collectors/pool-observer.js +40 -0
- package/build/src/profiler/collectors/pool-observer.js.map +1 -0
- package/build/src/profiler/manager.d.ts +55 -0
- package/build/src/profiler/manager.js +343 -0
- package/build/src/profiler/manager.js.map +1 -0
- package/build/src/profiler/output/console.d.ts +6 -0
- package/build/src/profiler/output/console.js +443 -0
- package/build/src/profiler/output/console.js.map +1 -0
- package/build/src/profiler/output/json.d.ts +2 -0
- package/build/src/profiler/output/json.js +24 -0
- package/build/src/profiler/output/json.js.map +1 -0
- package/build/src/profiler/retention/operation-store.d.ts +22 -0
- package/build/src/profiler/retention/operation-store.js +110 -0
- package/build/src/profiler/retention/operation-store.js.map +1 -0
- package/build/src/profiler/retention/policy-v1.d.ts +22 -0
- package/build/src/profiler/retention/policy-v1.js +49 -0
- package/build/src/profiler/retention/policy-v1.js.map +1 -0
- package/build/src/profiler/retention/result-budget.d.ts +2 -0
- package/build/src/profiler/retention/result-budget.js +117 -0
- package/build/src/profiler/retention/result-budget.js.map +1 -0
- package/build/src/profiler/retention/serialized-budget.d.ts +5 -0
- package/build/src/profiler/retention/serialized-budget.js +58 -0
- package/build/src/profiler/retention/serialized-budget.js.map +1 -0
- package/build/src/profiler/retention/streaming-statistics.d.ts +38 -0
- package/build/src/profiler/retention/streaming-statistics.js +191 -0
- package/build/src/profiler/retention/streaming-statistics.js.map +1 -0
- package/build/src/profiler/retention/top-k.d.ts +13 -0
- package/build/src/profiler/retention/top-k.js +57 -0
- package/build/src/profiler/retention/top-k.js.map +1 -0
- package/build/src/profiler/runtime/async-activity.d.ts +19 -0
- package/build/src/profiler/runtime/async-activity.js +113 -0
- package/build/src/profiler/runtime/async-activity.js.map +1 -0
- package/build/src/profiler/runtime/bootstrap-probe.d.ts +42 -0
- package/build/src/profiler/runtime/bootstrap-probe.js +91 -0
- package/build/src/profiler/runtime/bootstrap-probe.js.map +1 -0
- package/build/src/profiler/runtime/clock.d.ts +17 -0
- package/build/src/profiler/runtime/clock.js +15 -0
- package/build/src/profiler/runtime/clock.js.map +1 -0
- package/build/src/profiler/runtime/event-observer.d.ts +4 -0
- package/build/src/profiler/runtime/event-observer.js +142 -0
- package/build/src/profiler/runtime/event-observer.js.map +1 -0
- package/build/src/profiler/runtime/noop.d.ts +20 -0
- package/build/src/profiler/runtime/noop.js +37 -0
- package/build/src/profiler/runtime/noop.js.map +1 -0
- package/build/src/profiler/runtime/runtime.d.ts +95 -0
- package/build/src/profiler/runtime/runtime.js +1343 -0
- package/build/src/profiler/runtime/runtime.js.map +1 -0
- package/build/src/profiler/runtime/types.d.ts +88 -0
- package/build/src/profiler/runtime/types.js +3 -0
- package/build/src/profiler/runtime/types.js.map +1 -0
- package/build/src/profiler/sanitize.d.ts +15 -0
- package/build/src/profiler/sanitize.js +194 -0
- package/build/src/profiler/sanitize.js.map +1 -0
- package/build/src/profiler/schema/index.d.ts +1 -0
- package/build/src/profiler/schema/index.js +18 -0
- package/build/src/profiler/schema/index.js.map +1 -0
- package/build/src/profiler/schema/v1.d.ts +281 -0
- package/build/src/profiler/schema/v1.js +3 -0
- package/build/src/profiler/schema/v1.js.map +1 -0
- package/build/src/runner/browser-env/vite/browser-modules/mocha/index.js +30 -0
- package/build/src/runner/browser-env/vite/browser-modules/mocha/index.js.map +1 -1
- package/build/src/runner/browser-env/vite/browser-modules/tsconfig.tsbuildinfo +1 -1
- package/build/src/runner/browser-env/vite/browser-modules/types.d.ts +31 -6
- package/build/src/runner/browser-env/vite/browser-modules/types.js +8 -8
- package/build/src/runner/browser-env/vite/browser-modules/types.js.map +1 -1
- package/build/src/runner/browser-env/vite/plugins/generate-index-html.d.ts +7 -1
- package/build/src/runner/browser-env/vite/plugins/generate-index-html.js +28 -6
- package/build/src/runner/browser-env/vite/plugins/generate-index-html.js.map +1 -1
- package/build/src/runner/browser-env/vite/server.js +17 -1
- package/build/src/runner/browser-env/vite/server.js.map +1 -1
- package/build/src/runner/browser-env/vite/socket.js +4 -0
- package/build/src/runner/browser-env/vite/socket.js.map +1 -1
- package/build/src/runner/browser-env/vite/types.d.ts +9 -7
- package/build/src/runner/browser-env/vite/types.js +8 -8
- package/build/src/runner/browser-env/vite/types.js.map +1 -1
- package/build/src/runner/browser-env/vite/utils.js +9 -2
- package/build/src/runner/browser-env/vite/utils.js.map +1 -1
- package/build/src/runner/browser-runner.d.ts +3 -1
- package/build/src/runner/browser-runner.js +4 -2
- package/build/src/runner/browser-runner.js.map +1 -1
- package/build/src/runner/index.d.ts +3 -1
- package/build/src/runner/index.js +7 -4
- package/build/src/runner/index.js.map +1 -1
- package/build/src/runner/test-runner/index.d.ts +2 -1
- package/build/src/runner/test-runner/index.js +2 -2
- package/build/src/runner/test-runner/index.js.map +1 -1
- package/build/src/runner/test-runner/insistant-test-runner.d.ts +2 -1
- package/build/src/runner/test-runner/insistant-test-runner.js +3 -2
- package/build/src/runner/test-runner/insistant-test-runner.js.map +1 -1
- package/build/src/runner/test-runner/regular-test-runner.d.ts +9 -3
- package/build/src/runner/test-runner/regular-test-runner.js +77 -16
- package/build/src/runner/test-runner/regular-test-runner.js.map +1 -1
- package/build/src/signal-handler.js +14 -5
- package/build/src/signal-handler.js.map +1 -1
- package/build/src/test-reader/index.d.ts +2 -1
- package/build/src/test-reader/index.js +19 -10
- package/build/src/test-reader/index.js.map +1 -1
- package/build/src/test-reader/mocha-reader/index.d.ts +2 -1
- package/build/src/test-reader/mocha-reader/index.js +41 -24
- package/build/src/test-reader/mocha-reader/index.js.map +1 -1
- package/build/src/test-reader/mocha-reader/tree-builder-decorator.js +2 -2
- package/build/src/test-reader/mocha-reader/tree-builder-decorator.js.map +1 -1
- package/build/src/test-reader/mocha-reader/utils.d.ts +2 -0
- package/build/src/test-reader/mocha-reader/utils.js +18 -2
- package/build/src/test-reader/mocha-reader/utils.js.map +1 -1
- package/build/src/test-reader/test-object/hook.d.ts +4 -2
- package/build/src/test-reader/test-object/hook.js +3 -1
- package/build/src/test-reader/test-object/hook.js.map +1 -1
- package/build/src/test-reader/test-parser.d.ts +2 -1
- package/build/src/test-reader/test-parser.js +48 -4
- package/build/src/test-reader/test-parser.js.map +1 -1
- package/build/src/testplane.d.ts +6 -2
- package/build/src/testplane.js +82 -27
- package/build/src/testplane.js.map +1 -1
- package/build/src/types/index.d.ts +4 -1
- package/build/src/types/index.js.map +1 -1
- package/build/src/utils/exit-code.d.ts +1 -0
- package/build/src/utils/exit-code.js +28 -0
- package/build/src/utils/exit-code.js.map +1 -0
- package/build/src/utils/module-observer-registry.d.ts +43 -0
- package/build/src/utils/module-observer-registry.js +257 -0
- package/build/src/utils/module-observer-registry.js.map +1 -0
- package/build/src/utils/processor.js +1 -0
- package/build/src/utils/processor.js.map +1 -1
- package/build/src/utils/workers-registry.d.ts +48 -1
- package/build/src/utils/workers-registry.js +162 -4
- package/build/src/utils/workers-registry.js.map +1 -1
- package/build/src/worker/browser-env/runner/test-runner/execution-thread.js +15 -1
- package/build/src/worker/browser-env/runner/test-runner/execution-thread.js.map +1 -1
- package/build/src/worker/browser-env/runner/test-runner/index.js +20 -0
- package/build/src/worker/browser-env/runner/test-runner/index.js.map +1 -1
- package/build/src/worker/index.js +39 -1
- package/build/src/worker/index.js.map +1 -1
- package/build/src/worker/runner/browser-pool.d.ts +3 -2
- package/build/src/worker/runner/browser-pool.js +5 -3
- package/build/src/worker/runner/browser-pool.js.map +1 -1
- package/build/src/worker/runner/caching-test-parser.d.ts +6 -1
- package/build/src/worker/runner/caching-test-parser.js +40 -4
- package/build/src/worker/runner/caching-test-parser.js.map +1 -1
- package/build/src/worker/runner/index.d.ts +10 -9
- package/build/src/worker/runner/index.js +52 -6
- package/build/src/worker/runner/index.js.map +1 -1
- package/build/src/worker/runner/sequence-test-parser.d.ts +4 -1
- package/build/src/worker/runner/sequence-test-parser.js +17 -3
- package/build/src/worker/runner/sequence-test-parser.js.map +1 -1
- package/build/src/worker/runner/simple-test-parser.d.ts +3 -1
- package/build/src/worker/runner/simple-test-parser.js +4 -2
- package/build/src/worker/runner/simple-test-parser.js.map +1 -1
- package/build/src/worker/runner/test-runner/execution-thread.d.ts +20 -2
- package/build/src/worker/runner/test-runner/execution-thread.js +37 -2
- package/build/src/worker/runner/test-runner/execution-thread.js.map +1 -1
- package/build/src/worker/runner/test-runner/hook-runner.d.ts +1 -1
- package/build/src/worker/runner/test-runner/hook-runner.js +4 -4
- package/build/src/worker/runner/test-runner/hook-runner.js.map +1 -1
- package/build/src/worker/runner/test-runner/index.d.ts +8 -7
- package/build/src/worker/runner/test-runner/index.js +36 -25
- package/build/src/worker/runner/test-runner/index.js.map +1 -1
- package/build/src/worker/runner/test-runner/types.d.ts +8 -1
- package/build/src/worker/testplane-facade.js +63 -4
- package/build/src/worker/testplane-facade.js.map +1 -1
- package/build/src/worker/testplane.d.ts +6 -0
- package/build/src/worker/testplane.js +7 -1
- package/build/src/worker/testplane.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,1343 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ProfilerRuntime = void 0;
|
|
7
|
+
const node_async_hooks_1 = require("node:async_hooks");
|
|
8
|
+
const node_perf_hooks_1 = require("node:perf_hooks");
|
|
9
|
+
const node_perf_hooks_2 = require("node:perf_hooks");
|
|
10
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
11
|
+
const types_1 = require("node:util/types");
|
|
12
|
+
const operation_store_1 = require("../retention/operation-store");
|
|
13
|
+
const policy_v1_1 = require("../retention/policy-v1");
|
|
14
|
+
const streaming_statistics_1 = require("../retention/streaming-statistics");
|
|
15
|
+
const clock_1 = require("./clock");
|
|
16
|
+
const async_activity_1 = require("./async-activity");
|
|
17
|
+
const DEFAULT_QUALITY = {
|
|
18
|
+
timing: "exact",
|
|
19
|
+
cpu: "process-window",
|
|
20
|
+
};
|
|
21
|
+
const MAX_PENDING_FRAGMENTS_PER_SOURCE = 32;
|
|
22
|
+
const MAX_PENDING_FRAGMENT_BYTES = 20 * 1024 * 1024;
|
|
23
|
+
const SAMPLING_INTERVAL = {
|
|
24
|
+
1: 500,
|
|
25
|
+
2: 250,
|
|
26
|
+
3: 100,
|
|
27
|
+
};
|
|
28
|
+
const EVENT_LOOP_DELAY_RESOLUTION_MS = 10;
|
|
29
|
+
const NOOP_SPAN = Object.freeze({ end: () => undefined });
|
|
30
|
+
class ProfilerRuntime {
|
|
31
|
+
constructor(options) {
|
|
32
|
+
this._context = new node_async_hooks_1.AsyncLocalStorage();
|
|
33
|
+
this._openSpans = new Map();
|
|
34
|
+
this._operations = new operation_store_1.OperationStore();
|
|
35
|
+
this._aggregates = new Map();
|
|
36
|
+
this._fragmentAggregates = new Map();
|
|
37
|
+
this._metrics = new Map();
|
|
38
|
+
this._fragmentMetrics = new Map();
|
|
39
|
+
this._errors = [];
|
|
40
|
+
this._idSequence = 0;
|
|
41
|
+
this._stopped = false;
|
|
42
|
+
this._overheadMs = 0;
|
|
43
|
+
this._transferredOperationIds = new Set();
|
|
44
|
+
this._transferredErrorCount = 0;
|
|
45
|
+
this._ingestedFragmentSequences = new Map();
|
|
46
|
+
/** Out-of-order fragments waiting for a contiguous predecessor (RPC vs IPC race). */
|
|
47
|
+
this._pendingFragments = new Map();
|
|
48
|
+
this._pendingFragmentBytes = 0;
|
|
49
|
+
this._externalTruncation = new Map();
|
|
50
|
+
this._maxClockUncertaintyMs = 0;
|
|
51
|
+
this._unalignedFragments = 0;
|
|
52
|
+
this._aggregateIdentitiesSeen = 0;
|
|
53
|
+
this._aggregateIdentityOverflow = 0;
|
|
54
|
+
this._metricIdentitiesSeen = 0;
|
|
55
|
+
this._metricIdentityOverflow = 0;
|
|
56
|
+
this._fragmentSequence = 0;
|
|
57
|
+
this.runId = options.runId;
|
|
58
|
+
this.level = options.level;
|
|
59
|
+
this._clock = options.clock ?? clock_1.systemClock;
|
|
60
|
+
this._process = options.process ?? { type: "master", pid: process.pid };
|
|
61
|
+
this._operationIdPrefix = operationIdPrefix(this.runId, this._process);
|
|
62
|
+
this.originMonotonicMs = options.originMonotonicMs ?? this._clock.now();
|
|
63
|
+
this.originEpochMs =
|
|
64
|
+
options.originEpochMs ?? this._clock.epochNow() - (this._clock.now() - this.originMonotonicMs);
|
|
65
|
+
this._clockUncertaintyMs = options.clockUncertaintyMs;
|
|
66
|
+
if (this.level >= 3) {
|
|
67
|
+
try {
|
|
68
|
+
this._asyncActivity = new async_activity_1.AsyncActivityTracker(this._clock, () => this._context.getStore()?.spanId, error => this.recordError("runtime.asyncActivity", error));
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
this.recordError("runtime.asyncActivity.setup", error);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
this._startResourceSampler();
|
|
75
|
+
}
|
|
76
|
+
isEnabled(minLevel = 1) {
|
|
77
|
+
return !this._stopped && this.level >= minLevel;
|
|
78
|
+
}
|
|
79
|
+
adoptBootstrapProbe(probe) {
|
|
80
|
+
const records = probe.takeRecords();
|
|
81
|
+
const operationIds = new Map(records.map(record => [record.id, this._nextId("bootstrap")]));
|
|
82
|
+
for (const record of records) {
|
|
83
|
+
const operation = {
|
|
84
|
+
id: operationIds.get(record.id),
|
|
85
|
+
parentId: record.parentId === undefined ? undefined : operationIds.get(record.parentId),
|
|
86
|
+
kind: record.kind,
|
|
87
|
+
name: record.name,
|
|
88
|
+
process: this._process,
|
|
89
|
+
context: { runId: this.runId },
|
|
90
|
+
startOffsetMs: Math.max(0, record.startMs - this.originMonotonicMs),
|
|
91
|
+
timing: {
|
|
92
|
+
wallMs: Math.max(0, record.endMs - record.startMs),
|
|
93
|
+
processCpuMs: record.processCpuMs,
|
|
94
|
+
},
|
|
95
|
+
attributes: {},
|
|
96
|
+
quality: DEFAULT_QUALITY,
|
|
97
|
+
status: record.status,
|
|
98
|
+
};
|
|
99
|
+
this._recordOperation(operation);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
startSpan(kind, options = {}) {
|
|
103
|
+
if (!this.isEnabled(options.minLevel)) {
|
|
104
|
+
return NOOP_SPAN;
|
|
105
|
+
}
|
|
106
|
+
const overheadStart = this._clock.now();
|
|
107
|
+
try {
|
|
108
|
+
const current = this._context.getStore();
|
|
109
|
+
const id = this._nextId("span");
|
|
110
|
+
const context = {
|
|
111
|
+
runId: this.runId,
|
|
112
|
+
...current,
|
|
113
|
+
...options.context,
|
|
114
|
+
spanId: id,
|
|
115
|
+
};
|
|
116
|
+
const span = {
|
|
117
|
+
id,
|
|
118
|
+
kind,
|
|
119
|
+
name: options.name ?? kind,
|
|
120
|
+
parentId: options.parentId ?? current?.spanId,
|
|
121
|
+
context,
|
|
122
|
+
process: options.process ?? this._process,
|
|
123
|
+
source: options.source,
|
|
124
|
+
attributes: options.attributes ?? {},
|
|
125
|
+
quality: { ...DEFAULT_QUALITY, ...options.quality },
|
|
126
|
+
startMs: this._clock.now(),
|
|
127
|
+
processCpu: this._clock.cpuUsage(),
|
|
128
|
+
threadCpu: this.level >= 3 && this._clock.threadCpuUsage ? this._clock.threadCpuUsage() : undefined,
|
|
129
|
+
eventLoopUtilization: typeof node_perf_hooks_1.performance.eventLoopUtilization === "function"
|
|
130
|
+
? node_perf_hooks_1.performance.eventLoopUtilization()
|
|
131
|
+
: undefined,
|
|
132
|
+
ended: false,
|
|
133
|
+
childUnionMs: 0,
|
|
134
|
+
childUnionEndMs: Number.NEGATIVE_INFINITY,
|
|
135
|
+
};
|
|
136
|
+
this._openSpans.set(id, span);
|
|
137
|
+
this._asyncActivity?.startSpan(id);
|
|
138
|
+
this._registerChildInterval(span);
|
|
139
|
+
return {
|
|
140
|
+
id,
|
|
141
|
+
end: status => this._endSpan(span, status),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
this.recordError("runtime.startSpan", error);
|
|
146
|
+
return NOOP_SPAN;
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
this._overheadMs += Math.max(0, this._clock.now() - overheadStart);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
withSpan(kind, options, action) {
|
|
153
|
+
if (!this.isEnabled(options.minLevel)) {
|
|
154
|
+
return action();
|
|
155
|
+
}
|
|
156
|
+
const span = this.startSpan(kind, options);
|
|
157
|
+
const context = { ...(this._context.getStore() ?? { runId: this.runId }), spanId: span.id };
|
|
158
|
+
return this._context.run(context, () => {
|
|
159
|
+
try {
|
|
160
|
+
const result = this._asyncActivity ? this._asyncActivity.measureSynchronous(span.id, action) : action();
|
|
161
|
+
if (isPromiseLike(result)) {
|
|
162
|
+
return result.then(value => {
|
|
163
|
+
span.end();
|
|
164
|
+
return value;
|
|
165
|
+
}, error => {
|
|
166
|
+
span.end("failed");
|
|
167
|
+
throw error;
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
span.end();
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
span.end("failed");
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
withContext(context, action) {
|
|
180
|
+
if (this._stopped) {
|
|
181
|
+
return action();
|
|
182
|
+
}
|
|
183
|
+
return this._context.run({
|
|
184
|
+
runId: this.runId,
|
|
185
|
+
...this._context.getStore(),
|
|
186
|
+
...context,
|
|
187
|
+
}, action);
|
|
188
|
+
}
|
|
189
|
+
increment(metric, value = 1, dimensions = {}) {
|
|
190
|
+
if (!this.isEnabled()) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
this._updateMetric(metric, value, dimensions, true);
|
|
194
|
+
}
|
|
195
|
+
sample(metric, value, dimensions = {}) {
|
|
196
|
+
if (!this.isEnabled()) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
this._updateMetric(metric, value, dimensions, false);
|
|
200
|
+
}
|
|
201
|
+
recordError(stage, error) {
|
|
202
|
+
if (this._errors.length >= policy_v1_1.RETENTION_POLICY_V1.maxErrors) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const code = readOwnDataProperty(error, "code");
|
|
206
|
+
const ownMessage = readOwnDataProperty(error, "message");
|
|
207
|
+
this._errors.push({
|
|
208
|
+
stage: stage.slice(0, 200),
|
|
209
|
+
code: typeof code === "string" ? code.slice(0, 100) : undefined,
|
|
210
|
+
message: (typeof ownMessage === "string" ? ownMessage : safeString(error)).slice(0, 1000),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
recordMeasurement(kind, durationMs, options = {}, status = "completed") {
|
|
214
|
+
if (!this.isEnabled(options.minLevel) || !Number.isFinite(durationMs) || durationMs < 0) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
const current = this._context.getStore();
|
|
219
|
+
const id = this._nextId("measurement");
|
|
220
|
+
this._recordOperation({
|
|
221
|
+
id,
|
|
222
|
+
parentId: options.parentId ?? current?.spanId,
|
|
223
|
+
kind,
|
|
224
|
+
name: options.name ?? kind,
|
|
225
|
+
process: options.process ?? this._process,
|
|
226
|
+
context: { runId: this.runId, ...current, ...options.context, spanId: id },
|
|
227
|
+
startOffsetMs: Math.max(0, this._clock.now() - this.originMonotonicMs - durationMs),
|
|
228
|
+
timing: { wallMs: durationMs },
|
|
229
|
+
source: options.source,
|
|
230
|
+
attributes: options.attributes ?? {},
|
|
231
|
+
quality: { ...DEFAULT_QUALITY, timing: "estimated", ...options.quality },
|
|
232
|
+
status,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
this.recordError("runtime.recordMeasurement", error);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
closeOpenSpans() {
|
|
240
|
+
for (const span of [...this._openSpans.values()].reverse()) {
|
|
241
|
+
this._endSpan(span, "interrupted");
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
stop() {
|
|
245
|
+
if (this._stopped) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
this.closeOpenSpans();
|
|
249
|
+
this._reconcilePendingFragments();
|
|
250
|
+
this._stopped = true;
|
|
251
|
+
if (this._sampler) {
|
|
252
|
+
clearInterval(this._sampler);
|
|
253
|
+
this._sampler = undefined;
|
|
254
|
+
}
|
|
255
|
+
this._eventLoopDelay?.disable();
|
|
256
|
+
this._eventLoopDelay = undefined;
|
|
257
|
+
this._asyncActivity?.disable();
|
|
258
|
+
}
|
|
259
|
+
snapshot() {
|
|
260
|
+
const { operations, truncation } = this._operations.snapshot();
|
|
261
|
+
return {
|
|
262
|
+
runId: this.runId,
|
|
263
|
+
level: this.level,
|
|
264
|
+
originEpochMs: this.originEpochMs,
|
|
265
|
+
operations,
|
|
266
|
+
aggregates: this._snapshotAggregates(this._aggregates),
|
|
267
|
+
metrics: [...this._metrics.values()],
|
|
268
|
+
errors: [...this._errors],
|
|
269
|
+
truncation: [
|
|
270
|
+
...truncation,
|
|
271
|
+
...(this._aggregateIdentityOverflow > 0
|
|
272
|
+
? [
|
|
273
|
+
{
|
|
274
|
+
collector: "aggregate-identities",
|
|
275
|
+
seen: this._aggregateIdentitiesSeen,
|
|
276
|
+
retained: this._aggregates.size,
|
|
277
|
+
rule: `retain at most ${policy_v1_1.RETENTION_POLICY_V1.maxAggregateIdentities} aggregate identities; merge overflow into <other>`,
|
|
278
|
+
truncated: true,
|
|
279
|
+
},
|
|
280
|
+
]
|
|
281
|
+
: []),
|
|
282
|
+
...(this._metricIdentityOverflow > 0
|
|
283
|
+
? [
|
|
284
|
+
{
|
|
285
|
+
collector: "metric-identities",
|
|
286
|
+
seen: this._metricIdentitiesSeen,
|
|
287
|
+
retained: this._metrics.size,
|
|
288
|
+
rule: `retain at most ${policy_v1_1.RETENTION_POLICY_V1.maxAggregateIdentities} metric identities; merge overflow into profiler.metric.other`,
|
|
289
|
+
truncated: true,
|
|
290
|
+
},
|
|
291
|
+
]
|
|
292
|
+
: []),
|
|
293
|
+
...this._externalTruncation.values(),
|
|
294
|
+
],
|
|
295
|
+
overheadMs: this._overheadMs,
|
|
296
|
+
clock: {
|
|
297
|
+
maxUncertaintyMs: this._maxClockUncertaintyMs || undefined,
|
|
298
|
+
unalignedFragments: this._unalignedFragments,
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
takeFragment() {
|
|
303
|
+
const snapshot = this.snapshot();
|
|
304
|
+
const retainedOperationIds = new Set(snapshot.operations.map(operation => operation.id));
|
|
305
|
+
for (const operationId of this._transferredOperationIds) {
|
|
306
|
+
if (!retainedOperationIds.has(operationId)) {
|
|
307
|
+
this._transferredOperationIds.delete(operationId);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
const operations = snapshot.operations.filter(operation => {
|
|
311
|
+
if (this._transferredOperationIds.has(operation.id)) {
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
this._transferredOperationIds.add(operation.id);
|
|
315
|
+
return true;
|
|
316
|
+
});
|
|
317
|
+
const errors = snapshot.errors.slice(this._transferredErrorCount);
|
|
318
|
+
this._transferredErrorCount = snapshot.errors.length;
|
|
319
|
+
const aggregates = this._snapshotAggregates(this._fragmentAggregates);
|
|
320
|
+
const metrics = [...this._fragmentMetrics.values()];
|
|
321
|
+
this._fragmentAggregates.clear();
|
|
322
|
+
this._fragmentMetrics.clear();
|
|
323
|
+
if (!operations.length && !aggregates.length && !metrics.length && !errors.length) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
transportVersion: 1,
|
|
328
|
+
sequence: ++this._fragmentSequence,
|
|
329
|
+
sourceRunId: this.runId,
|
|
330
|
+
level: this.level,
|
|
331
|
+
originEpochMs: snapshot.originEpochMs,
|
|
332
|
+
clockUncertaintyMs: this._clockUncertaintyMs,
|
|
333
|
+
process: this._process,
|
|
334
|
+
operations,
|
|
335
|
+
aggregates,
|
|
336
|
+
metrics,
|
|
337
|
+
errors,
|
|
338
|
+
truncation: snapshot.truncation,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
ingestFragment(fragment) {
|
|
342
|
+
if (!this.isEnabled()) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (!isProfilerFragmentV1(fragment)) {
|
|
346
|
+
const transportVersion = readTransportVersion(fragment);
|
|
347
|
+
if (transportVersion !== 1) {
|
|
348
|
+
this.recordError("transport.version", new Error(`Unsupported profiler transport version ${safeString(transportVersion)}`));
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
this.recordError("transport.fragment", new Error("Invalid profiler fragment payload"));
|
|
352
|
+
}
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
if (!this._hasMatchingFragmentLevel(fragment)) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const sourceKey = fragmentSourceKey(fragment);
|
|
359
|
+
const sequence = readOwnDataProperty(fragment, "sequence");
|
|
360
|
+
const previousSequence = this._ingestedFragmentSequences.get(sourceKey) ?? 0;
|
|
361
|
+
if (sequence <= previousSequence) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
const existing = this._pendingFragments.get(sourceKey);
|
|
365
|
+
if (sequence > previousSequence + 1 && existing?.has(sequence)) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
let ownedFragment;
|
|
369
|
+
try {
|
|
370
|
+
ownedFragment = cloneProfilerFragment(fragment);
|
|
371
|
+
}
|
|
372
|
+
catch (error) {
|
|
373
|
+
this.recordError("transport.fragment", error);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (sequence > previousSequence + 1) {
|
|
377
|
+
// Dual transport (IPC batch + RPC profileFragment) can deliver out of order.
|
|
378
|
+
// Buffer until the predecessor arrives; report a real gap only on stop (or when
|
|
379
|
+
// the per-source pending buffer exceeds the cap — likely a permanent hole).
|
|
380
|
+
const pending = existing ?? new Map();
|
|
381
|
+
if (!existing) {
|
|
382
|
+
this._pendingFragments.set(sourceKey, pending);
|
|
383
|
+
}
|
|
384
|
+
const serializedBytes = serializedSize(ownedFragment);
|
|
385
|
+
pending.set(ownedFragment.sequence, { fragment: ownedFragment, serializedBytes });
|
|
386
|
+
this._pendingFragmentBytes += serializedBytes;
|
|
387
|
+
if (pending.size > MAX_PENDING_FRAGMENTS_PER_SOURCE ||
|
|
388
|
+
this._pendingFragmentBytes > MAX_PENDING_FRAGMENT_BYTES) {
|
|
389
|
+
this._forceApplyPendingFragments(sourceKey);
|
|
390
|
+
}
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
this._applyFragmentSafely(sourceKey, ownedFragment, ownedFragment.sequence);
|
|
394
|
+
this._drainPendingFragments(sourceKey);
|
|
395
|
+
}
|
|
396
|
+
_applyFragmentSafely(sourceKey, fragment, expectedSequence) {
|
|
397
|
+
if (!isProfilerFragmentV1(fragment)) {
|
|
398
|
+
this.recordError("transport.fragment", new Error("Invalid profiler fragment payload"));
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (!this._hasMatchingFragmentLevel(fragment)) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (fragment.sequence !== expectedSequence || fragmentSourceKey(fragment) !== sourceKey) {
|
|
405
|
+
this.recordError("transport.fragment", new Error("Profiler fragment identity changed before application"));
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
try {
|
|
409
|
+
this._applyIngestedFragment(sourceKey, fragment);
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
this.recordError("transport.fragment", error);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
_hasMatchingFragmentLevel(fragment) {
|
|
416
|
+
if (fragment.level === this.level) {
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
this.recordError("transport.level", new Error(`Profiler fragment level ${fragment.level} does not match master level ${this.level}`));
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
_applyIngestedFragment(sourceKey, fragment) {
|
|
423
|
+
this._ingestedFragmentSequences.set(sourceKey, fragment.sequence);
|
|
424
|
+
const epochOffsetMs = fragment.originEpochMs === undefined ? 0 : fragment.originEpochMs - this.originEpochMs;
|
|
425
|
+
const hasAlignedClock = fragment.originEpochMs !== undefined && Number.isFinite(epochOffsetMs);
|
|
426
|
+
if (!hasAlignedClock) {
|
|
427
|
+
this._unalignedFragments += 1;
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
this._maxClockUncertaintyMs = Math.max(this._maxClockUncertaintyMs, fragment.clockUncertaintyMs ?? 5);
|
|
431
|
+
}
|
|
432
|
+
const operations = [];
|
|
433
|
+
for (const sourceOperation of fragment.operations) {
|
|
434
|
+
if (!isRetainedOperation(sourceOperation)) {
|
|
435
|
+
this.recordError("transport.operation", new Error("Dropped invalid profiler operation"));
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
const adjustedStartOffsetMs = (hasAlignedClock ? epochOffsetMs : 0) + sourceOperation.startOffsetMs;
|
|
439
|
+
if (!Number.isFinite(adjustedStartOffsetMs)) {
|
|
440
|
+
this.recordError("transport.operation", new Error("Dropped profiler operation with invalid start offset"));
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
const process = sourceOperation.process.type === "browser" ? sourceOperation.process : fragment.process;
|
|
444
|
+
const operation = copyDataRecord(sourceOperation, {
|
|
445
|
+
process,
|
|
446
|
+
context: copyDataRecord(sourceOperation.context, { runId: this.runId }),
|
|
447
|
+
startOffsetMs: Math.max(0, adjustedStartOffsetMs),
|
|
448
|
+
});
|
|
449
|
+
operations.push(operation);
|
|
450
|
+
}
|
|
451
|
+
// Local spans close child-first. Preserve that order after transport snapshots,
|
|
452
|
+
// which are sorted by start time, so bounded retention can pin a parent on arrival.
|
|
453
|
+
for (const operation of orderChildrenBeforeParents(operations)) {
|
|
454
|
+
if (!fragment.aggregates?.length) {
|
|
455
|
+
this._recordOperation(operation);
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
this._operations.add(operation);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
for (const aggregate of fragment.aggregates ?? []) {
|
|
462
|
+
if (!this._mergeAggregate(this._aggregates, aggregate.kind, aggregate.name, aggregate.statistics, aggregate.attributes)) {
|
|
463
|
+
this.recordError("transport.aggregate", new Error(`Dropped overflowing aggregate ${aggregate.kind}`));
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
for (const metric of fragment.metrics ?? []) {
|
|
467
|
+
this._updateMetric(metric.name, metric.value, metric.dimensions, metric.mode === "counter", false);
|
|
468
|
+
}
|
|
469
|
+
for (const truncation of fragment.truncation) {
|
|
470
|
+
const key = `${sourceKey}\0${truncation.collector}`;
|
|
471
|
+
this._externalTruncation.set(key, { ...truncation });
|
|
472
|
+
}
|
|
473
|
+
for (const error of fragment.errors) {
|
|
474
|
+
this.recordError(`worker.${error.stage}`, error);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
_drainPendingFragments(sourceKey) {
|
|
478
|
+
const pending = this._pendingFragments.get(sourceKey);
|
|
479
|
+
if (!pending?.size) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
for (;;) {
|
|
483
|
+
const nextSequence = (this._ingestedFragmentSequences.get(sourceKey) ?? 0) + 1;
|
|
484
|
+
const fragment = pending.get(nextSequence);
|
|
485
|
+
if (!fragment) {
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
this._applyFragmentSafely(sourceKey, this._takePendingFragment(pending, nextSequence), nextSequence);
|
|
489
|
+
}
|
|
490
|
+
if (!pending.size) {
|
|
491
|
+
this._pendingFragments.delete(sourceKey);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
_reconcilePendingFragments() {
|
|
495
|
+
for (const sourceKey of [...this._pendingFragments.keys()]) {
|
|
496
|
+
this._forceApplyPendingFragments(sourceKey);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
/** Apply buffered fragments for a source, recording transport.gap for any holes. */
|
|
500
|
+
_forceApplyPendingFragments(sourceKey) {
|
|
501
|
+
const pending = this._pendingFragments.get(sourceKey);
|
|
502
|
+
if (!pending?.size) {
|
|
503
|
+
this._pendingFragments.delete(sourceKey);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
const sequences = [...pending.keys()].sort((left, right) => left - right);
|
|
507
|
+
for (const sequence of sequences) {
|
|
508
|
+
const previousSequence = this._ingestedFragmentSequences.get(sourceKey) ?? 0;
|
|
509
|
+
if (sequence <= previousSequence) {
|
|
510
|
+
this._takePendingFragment(pending, sequence);
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
if (sequence > previousSequence + 1) {
|
|
514
|
+
const { fragment } = pending.get(sequence);
|
|
515
|
+
this.recordError("transport.gap", new Error(`Missing profiler fragment sequence ${previousSequence + 1}-${sequence - 1} from ${fragment.sourceRunId}`));
|
|
516
|
+
}
|
|
517
|
+
this._applyFragmentSafely(sourceKey, this._takePendingFragment(pending, sequence), sequence);
|
|
518
|
+
}
|
|
519
|
+
this._pendingFragments.delete(sourceKey);
|
|
520
|
+
}
|
|
521
|
+
_takePendingFragment(pending, sequence) {
|
|
522
|
+
const current = pending.get(sequence);
|
|
523
|
+
pending.delete(sequence);
|
|
524
|
+
this._pendingFragmentBytes = Math.max(0, this._pendingFragmentBytes - current.serializedBytes);
|
|
525
|
+
return current.fragment;
|
|
526
|
+
}
|
|
527
|
+
_endSpan(span, status = "completed") {
|
|
528
|
+
if (span.ended) {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const overheadStart = this._clock.now();
|
|
532
|
+
try {
|
|
533
|
+
span.ended = true;
|
|
534
|
+
this._openSpans.delete(span.id);
|
|
535
|
+
const endMs = this._clock.now();
|
|
536
|
+
span.endMs = endMs;
|
|
537
|
+
const wallMs = Math.max(0, endMs - span.startMs);
|
|
538
|
+
this._completeChildInterval(span);
|
|
539
|
+
this._flushOpenChildIntervals(span, endMs);
|
|
540
|
+
const elu = span.eventLoopUtilization
|
|
541
|
+
? node_perf_hooks_1.performance.eventLoopUtilization(span.eventLoopUtilization)
|
|
542
|
+
: undefined;
|
|
543
|
+
const threadCpuMs = span.threadCpu && this._clock.threadCpuUsage
|
|
544
|
+
? (0, clock_1.cpuUsageToMs)(this._clock.threadCpuUsage(span.threadCpu))
|
|
545
|
+
: undefined;
|
|
546
|
+
const measuredActiveJsMs = this._asyncActivity?.finishSpan(span.id);
|
|
547
|
+
const activeJsMs = measuredActiveJsMs === undefined ? undefined : Math.min(wallMs, measuredActiveJsMs);
|
|
548
|
+
this._recordOperation({
|
|
549
|
+
id: span.id,
|
|
550
|
+
parentId: span.parentId,
|
|
551
|
+
kind: span.kind,
|
|
552
|
+
name: span.name,
|
|
553
|
+
process: span.process,
|
|
554
|
+
context: span.context,
|
|
555
|
+
startOffsetMs: Math.max(0, span.startMs - this.originMonotonicMs),
|
|
556
|
+
timing: {
|
|
557
|
+
wallMs,
|
|
558
|
+
processCpuMs: (0, clock_1.cpuUsageToMs)(this._clock.cpuUsage(span.processCpu)),
|
|
559
|
+
threadCpuMs,
|
|
560
|
+
activeJsMs,
|
|
561
|
+
waitingMs: activeJsMs === undefined ? undefined : Math.max(0, wallMs - activeJsMs),
|
|
562
|
+
eventLoopUtilization: elu?.utilization,
|
|
563
|
+
observedChildUnionMs: span.childUnionMs > 0 ? Math.min(wallMs, span.childUnionMs) : undefined,
|
|
564
|
+
},
|
|
565
|
+
source: span.source,
|
|
566
|
+
attributes: span.attributes,
|
|
567
|
+
quality: {
|
|
568
|
+
...span.quality,
|
|
569
|
+
cpu: threadCpuMs === undefined ? span.quality.cpu : "thread",
|
|
570
|
+
notes: activeJsMs === undefined
|
|
571
|
+
? span.quality.notes
|
|
572
|
+
: [
|
|
573
|
+
...(span.quality.notes ?? []),
|
|
574
|
+
"activeJsMs is best-effort async_hooks callback attribution",
|
|
575
|
+
],
|
|
576
|
+
},
|
|
577
|
+
status,
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
catch (error) {
|
|
581
|
+
this.recordError("runtime.endSpan", error);
|
|
582
|
+
}
|
|
583
|
+
finally {
|
|
584
|
+
span.childIntervalHead = undefined;
|
|
585
|
+
span.childIntervalTail = undefined;
|
|
586
|
+
this._overheadMs += Math.max(0, this._clock.now() - overheadStart);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
_registerChildInterval(span) {
|
|
590
|
+
if (!span.parentId) {
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
const parent = this._openSpans.get(span.parentId);
|
|
594
|
+
if (!parent) {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
if (parent.childIntervalTail) {
|
|
598
|
+
span.previousSibling = parent.childIntervalTail;
|
|
599
|
+
parent.childIntervalTail.nextSibling = span;
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
parent.childIntervalHead = span;
|
|
603
|
+
}
|
|
604
|
+
parent.childIntervalTail = span;
|
|
605
|
+
}
|
|
606
|
+
_completeChildInterval(span) {
|
|
607
|
+
if (!span.parentId) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
const parent = this._openSpans.get(span.parentId);
|
|
611
|
+
if (!parent) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
const previous = span.previousSibling;
|
|
615
|
+
const next = span.nextSibling;
|
|
616
|
+
if (previous) {
|
|
617
|
+
previous.nextSibling = next;
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
parent.childIntervalHead = next;
|
|
621
|
+
this._mergeChildInterval(parent, span.startMs, span.endMs);
|
|
622
|
+
}
|
|
623
|
+
if (next) {
|
|
624
|
+
next.previousSibling = previous;
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
parent.childIntervalTail = previous;
|
|
628
|
+
}
|
|
629
|
+
span.previousSibling = undefined;
|
|
630
|
+
span.nextSibling = undefined;
|
|
631
|
+
}
|
|
632
|
+
_flushOpenChildIntervals(parent, endMs) {
|
|
633
|
+
let child = parent.childIntervalHead;
|
|
634
|
+
if (child) {
|
|
635
|
+
this._mergeChildInterval(parent, child.startMs, endMs);
|
|
636
|
+
}
|
|
637
|
+
while (child) {
|
|
638
|
+
const next = child.nextSibling;
|
|
639
|
+
child.previousSibling = undefined;
|
|
640
|
+
child.nextSibling = undefined;
|
|
641
|
+
child = next;
|
|
642
|
+
}
|
|
643
|
+
parent.childIntervalHead = undefined;
|
|
644
|
+
parent.childIntervalTail = undefined;
|
|
645
|
+
}
|
|
646
|
+
// Intervals reach this point in non-decreasing start order, so the running union stays exact in O(1).
|
|
647
|
+
_mergeChildInterval(parent, startMs, endMs) {
|
|
648
|
+
if (startMs >= parent.childUnionEndMs) {
|
|
649
|
+
parent.childUnionMs += Math.max(0, endMs - startMs);
|
|
650
|
+
parent.childUnionEndMs = endMs;
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
parent.childUnionMs += Math.max(0, endMs - parent.childUnionEndMs);
|
|
654
|
+
parent.childUnionEndMs = Math.max(parent.childUnionEndMs, endMs);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
_recordOperation(operation) {
|
|
658
|
+
this._operations.add(operation);
|
|
659
|
+
if (operation.kind === "test.file.load" && operation.process.type === "master") {
|
|
660
|
+
this._addLocalAggregate("test.file.load.summary", operation.process.type, operation.timing.wallMs, {
|
|
661
|
+
process: operation.process.type,
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
if (operation.kind === "browser.command") {
|
|
665
|
+
this._addLocalAggregate("browser.command.cumulative", "<all>", operation.timing.wallMs);
|
|
666
|
+
const parentKind = operation.parentId ? this._openSpans.get(operation.parentId)?.kind : undefined;
|
|
667
|
+
if (parentKind !== "browser.command") {
|
|
668
|
+
this._addLocalAggregate("browser.command.root", "<root>", operation.timing.wallMs);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
this._addLocalAggregate(operation.kind, operation.name, operation.timing.wallMs);
|
|
672
|
+
}
|
|
673
|
+
_addLocalAggregate(kind, name, value, attributes) {
|
|
674
|
+
this._addAggregate(this._aggregates, kind, name, value, true, attributes);
|
|
675
|
+
this._addAggregate(this._fragmentAggregates, kind, name, value, false, attributes);
|
|
676
|
+
}
|
|
677
|
+
_addAggregate(store, kind, name, value, trackIdentity = false, attributes) {
|
|
678
|
+
if (!this._updateAggregate(store, kind, name, attributes, trackIdentity, statistics => statistics.add(value))) {
|
|
679
|
+
this.recordError("runtime.aggregate", new Error(`Aggregate ${kind} overflowed`));
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
_mergeAggregate(store, kind, name, statistics, attributes) {
|
|
683
|
+
return this._updateAggregate(store, kind, name, attributes, true, current => current.merge(statistics));
|
|
684
|
+
}
|
|
685
|
+
_updateAggregate(store, kind, name, attributes, trackIdentity, update) {
|
|
686
|
+
let key = `${kind}\0${name}\0${stableAttributesKey(attributes)}`;
|
|
687
|
+
const isNewIdentity = !store.has(key);
|
|
688
|
+
let overflowed = false;
|
|
689
|
+
if (isNewIdentity) {
|
|
690
|
+
if (store.size >= policy_v1_1.RETENTION_POLICY_V1.maxAggregateIdentities - 1) {
|
|
691
|
+
overflowed = true;
|
|
692
|
+
kind = "profiler.aggregate.other";
|
|
693
|
+
name = "<other>";
|
|
694
|
+
attributes = undefined;
|
|
695
|
+
key = `${kind}\0${name}\0`;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
let aggregate = store.get(key);
|
|
699
|
+
if (!aggregate) {
|
|
700
|
+
aggregate = {
|
|
701
|
+
kind,
|
|
702
|
+
name,
|
|
703
|
+
attributes: attributes && copyDataRecord(attributes),
|
|
704
|
+
statistics: new streaming_statistics_1.StreamingStatistics(),
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
if (!update(aggregate.statistics)) {
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
710
|
+
if (!store.has(key)) {
|
|
711
|
+
store.set(key, aggregate);
|
|
712
|
+
}
|
|
713
|
+
if (trackIdentity && isNewIdentity) {
|
|
714
|
+
this._aggregateIdentitiesSeen += 1;
|
|
715
|
+
if (overflowed) {
|
|
716
|
+
this._aggregateIdentityOverflow += 1;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return true;
|
|
720
|
+
}
|
|
721
|
+
_updateMetric(metric, value, dimensions, increment, trackFragment = true) {
|
|
722
|
+
if (!Number.isFinite(value)) {
|
|
723
|
+
this.recordError("runtime.metric", new Error(`Metric ${metric} has a non-finite value`));
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
let normalizedDimensions = copyEntriesToDataRecord(Object.entries(dimensions)
|
|
727
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
728
|
+
.map(([key, dimension]) => [key.slice(0, 100), dimension]));
|
|
729
|
+
let normalizedMetric = metric;
|
|
730
|
+
let key = `${normalizedMetric}\0${stableAttributesKey(normalizedDimensions)}`;
|
|
731
|
+
const isNewIdentity = !this._metrics.has(key);
|
|
732
|
+
let overflowed = false;
|
|
733
|
+
if (isNewIdentity) {
|
|
734
|
+
if (this._metrics.size >= policy_v1_1.RETENTION_POLICY_V1.maxAggregateIdentities - 1) {
|
|
735
|
+
overflowed = true;
|
|
736
|
+
normalizedMetric = "profiler.metric.other";
|
|
737
|
+
normalizedDimensions = Object.create(null);
|
|
738
|
+
key = `${normalizedMetric}\0{}`;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
const current = this._metrics.get(key);
|
|
742
|
+
const nextValue = increment ? (current?.value ?? 0) + value : value;
|
|
743
|
+
const fragmentCurrent = trackFragment ? this._fragmentMetrics.get(key) : undefined;
|
|
744
|
+
const nextFragmentValue = increment ? (fragmentCurrent?.value ?? 0) + value : value;
|
|
745
|
+
if (!Number.isFinite(nextValue) || (trackFragment && !Number.isFinite(nextFragmentValue))) {
|
|
746
|
+
this.recordError("runtime.metric", new Error(`Metric ${metric} overflowed`));
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
this._metrics.set(key, {
|
|
750
|
+
name: normalizedMetric,
|
|
751
|
+
value: nextValue,
|
|
752
|
+
dimensions: normalizedDimensions,
|
|
753
|
+
mode: increment ? "counter" : "gauge",
|
|
754
|
+
});
|
|
755
|
+
if (isNewIdentity) {
|
|
756
|
+
this._metricIdentitiesSeen += 1;
|
|
757
|
+
if (overflowed) {
|
|
758
|
+
this._metricIdentityOverflow += 1;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
if (trackFragment) {
|
|
762
|
+
this._fragmentMetrics.set(key, {
|
|
763
|
+
name: normalizedMetric,
|
|
764
|
+
value: nextFragmentValue,
|
|
765
|
+
dimensions: normalizedDimensions,
|
|
766
|
+
mode: increment ? "counter" : "gauge",
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
_startResourceSampler() {
|
|
771
|
+
if (typeof node_perf_hooks_1.performance.eventLoopUtilization === "function") {
|
|
772
|
+
this._lastElu = node_perf_hooks_1.performance.eventLoopUtilization();
|
|
773
|
+
}
|
|
774
|
+
try {
|
|
775
|
+
this._eventLoopDelay = (0, node_perf_hooks_2.monitorEventLoopDelay)({ resolution: EVENT_LOOP_DELAY_RESOLUTION_MS });
|
|
776
|
+
this._eventLoopDelay.enable();
|
|
777
|
+
}
|
|
778
|
+
catch (error) {
|
|
779
|
+
this.recordError("runtime.eventLoopDelay.setup", error);
|
|
780
|
+
}
|
|
781
|
+
if (this._process.type === "master") {
|
|
782
|
+
this._lastHostCpu = readHostCpu();
|
|
783
|
+
}
|
|
784
|
+
this._sampler = setInterval(() => {
|
|
785
|
+
try {
|
|
786
|
+
if (this._lastElu) {
|
|
787
|
+
const current = node_perf_hooks_1.performance.eventLoopUtilization(this._lastElu);
|
|
788
|
+
this._lastElu = node_perf_hooks_1.performance.eventLoopUtilization();
|
|
789
|
+
this._recordResourceSample("process.eventLoopUtilization", current.utilization, {
|
|
790
|
+
process: this._process.type,
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
const memory = process.memoryUsage();
|
|
794
|
+
this._recordResourceSample("process.rssBytes", memory.rss, { process: this._process.type });
|
|
795
|
+
if (this._eventLoopDelay && Number.isFinite(this._eventLoopDelay.percentile(95))) {
|
|
796
|
+
this._recordResourceSample("process.eventLoopDelayP95Ms", this._eventLoopDelay.percentile(95) / 1000000, { process: this._process.type });
|
|
797
|
+
this._eventLoopDelay.reset();
|
|
798
|
+
}
|
|
799
|
+
if (this._process.type === "master" && this._lastHostCpu) {
|
|
800
|
+
const hostCpu = readHostCpu();
|
|
801
|
+
const totalDelta = hostCpu.total - this._lastHostCpu.total;
|
|
802
|
+
const busyDelta = hostCpu.busy - this._lastHostCpu.busy;
|
|
803
|
+
this._lastHostCpu = hostCpu;
|
|
804
|
+
if (totalDelta > 0) {
|
|
805
|
+
this._recordResourceSample("host.cpuUtilization", busyDelta / totalDelta, {
|
|
806
|
+
process: "host",
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
catch (error) {
|
|
812
|
+
this.recordError("runtime.resourceSampler", error);
|
|
813
|
+
}
|
|
814
|
+
}, SAMPLING_INTERVAL[this.level]);
|
|
815
|
+
this._sampler.unref();
|
|
816
|
+
}
|
|
817
|
+
_recordResourceSample(metric, value, dimensions) {
|
|
818
|
+
this.sample(metric, value, dimensions);
|
|
819
|
+
this._addLocalAggregate("metric.sample", metric, value, dimensions);
|
|
820
|
+
}
|
|
821
|
+
_snapshotAggregates(store) {
|
|
822
|
+
return [...store.values()].map(({ kind, name, attributes, statistics }) => ({
|
|
823
|
+
kind,
|
|
824
|
+
name,
|
|
825
|
+
attributes,
|
|
826
|
+
statistics: statistics.snapshot(),
|
|
827
|
+
}));
|
|
828
|
+
}
|
|
829
|
+
_nextId(prefix) {
|
|
830
|
+
this._idSequence += 1;
|
|
831
|
+
return `${this._operationIdPrefix}:${prefix}:${this._idSequence}`;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
exports.ProfilerRuntime = ProfilerRuntime;
|
|
835
|
+
function operationIdPrefix(runId, processRef) {
|
|
836
|
+
if (processRef.type === "master") {
|
|
837
|
+
return runId;
|
|
838
|
+
}
|
|
839
|
+
const processIdentity = processRef.workerInstanceId ??
|
|
840
|
+
processRef.browserId ??
|
|
841
|
+
(processRef.pid === undefined ? "unknown" : processRef.pid);
|
|
842
|
+
return `${runId}:${processRef.type}:${processIdentity}`;
|
|
843
|
+
}
|
|
844
|
+
function fragmentSourceKey(fragment) {
|
|
845
|
+
const process = readOwnDataProperty(fragment, "process");
|
|
846
|
+
return [
|
|
847
|
+
readOwnDataProperty(fragment, "sourceRunId"),
|
|
848
|
+
readOwnDataProperty(process, "type"),
|
|
849
|
+
readOwnDataProperty(process, "pid"),
|
|
850
|
+
readOwnDataProperty(process, "workerInstanceId"),
|
|
851
|
+
readOwnDataProperty(process, "browserId"),
|
|
852
|
+
]
|
|
853
|
+
.map(keyPart)
|
|
854
|
+
.join("|");
|
|
855
|
+
}
|
|
856
|
+
function stableAttributesKey(attributes) {
|
|
857
|
+
if (!attributes) {
|
|
858
|
+
return "";
|
|
859
|
+
}
|
|
860
|
+
return stringifyDataValue(copyEntriesToDataRecord(Object.entries(attributes).sort(([left], [right]) => left.localeCompare(right))));
|
|
861
|
+
}
|
|
862
|
+
function serializedSize(value) {
|
|
863
|
+
try {
|
|
864
|
+
return Buffer.byteLength(stringifyDataValue(value));
|
|
865
|
+
}
|
|
866
|
+
catch {
|
|
867
|
+
return MAX_PENDING_FRAGMENT_BYTES + 1;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
function cloneProfilerFragment(fragment) {
|
|
871
|
+
const normalized = Object.create(null);
|
|
872
|
+
for (const key of Object.keys(fragment)) {
|
|
873
|
+
if (key !== "operations") {
|
|
874
|
+
normalized[key] = normalizeDataValue(readOwnDataProperty(fragment, key));
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
const operations = readOwnDataProperty(fragment, "operations");
|
|
878
|
+
const operationsLength = readOwnDataProperty(operations, "length");
|
|
879
|
+
const normalizedOperations = new Array(operationsLength);
|
|
880
|
+
for (let index = 0; index < operations.length; index += 1) {
|
|
881
|
+
const operation = readOwnDataProperty(operations, String(index));
|
|
882
|
+
// Invalid operations are still dropped individually when the fragment is applied.
|
|
883
|
+
normalizedOperations[index] = isRetainedOperation(operation) ? normalizeDataValue(operation) : null;
|
|
884
|
+
}
|
|
885
|
+
normalized.operations = normalizedOperations;
|
|
886
|
+
const cloned = structuredClone(normalized);
|
|
887
|
+
if (!isProfilerFragmentV1(cloned)) {
|
|
888
|
+
throw new Error("Cloned profiler fragment failed validation");
|
|
889
|
+
}
|
|
890
|
+
return normalizeDataValue(cloned);
|
|
891
|
+
}
|
|
892
|
+
function copyDataRecord(source, overrides) {
|
|
893
|
+
const target = Object.create(null);
|
|
894
|
+
for (const key of Object.keys(source)) {
|
|
895
|
+
target[key] = normalizeDataValue(readOwnDataProperty(source, key));
|
|
896
|
+
}
|
|
897
|
+
for (const key of Object.keys(overrides ?? {})) {
|
|
898
|
+
target[key] = normalizeDataValue(readOwnDataProperty(overrides, key));
|
|
899
|
+
}
|
|
900
|
+
return target;
|
|
901
|
+
}
|
|
902
|
+
function copyEntriesToDataRecord(entries) {
|
|
903
|
+
const target = Object.create(null);
|
|
904
|
+
for (const [key, value] of entries) {
|
|
905
|
+
target[key] = value;
|
|
906
|
+
}
|
|
907
|
+
return target;
|
|
908
|
+
}
|
|
909
|
+
function normalizeDataValue(value) {
|
|
910
|
+
if (Array.isArray(value)) {
|
|
911
|
+
const length = readOwnDataProperty(value, "length");
|
|
912
|
+
if (!Number.isSafeInteger(length) || length < 0) {
|
|
913
|
+
throw new Error("Invalid data array length");
|
|
914
|
+
}
|
|
915
|
+
const normalized = new Array(length);
|
|
916
|
+
for (let index = 0; index < normalized.length; index += 1) {
|
|
917
|
+
normalized[index] = normalizeDataValue(readOwnDataProperty(value, String(index)));
|
|
918
|
+
}
|
|
919
|
+
return normalized;
|
|
920
|
+
}
|
|
921
|
+
if (value && typeof value === "object") {
|
|
922
|
+
return copyDataRecord(value);
|
|
923
|
+
}
|
|
924
|
+
return value;
|
|
925
|
+
}
|
|
926
|
+
function stringifyDataValue(value) {
|
|
927
|
+
if (value === null) {
|
|
928
|
+
return "null";
|
|
929
|
+
}
|
|
930
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
931
|
+
return JSON.stringify(value);
|
|
932
|
+
}
|
|
933
|
+
if (value === undefined) {
|
|
934
|
+
return "null";
|
|
935
|
+
}
|
|
936
|
+
if (Array.isArray(value)) {
|
|
937
|
+
const entries = [];
|
|
938
|
+
const length = readOwnDataProperty(value, "length");
|
|
939
|
+
for (let index = 0; index < length; index += 1) {
|
|
940
|
+
entries.push(stringifyDataValue(readOwnDataProperty(value, String(index))));
|
|
941
|
+
}
|
|
942
|
+
return `[${entries.join(",")}]`;
|
|
943
|
+
}
|
|
944
|
+
if (typeof value === "object") {
|
|
945
|
+
const entries = [];
|
|
946
|
+
for (const key of Object.keys(value)) {
|
|
947
|
+
const entry = readOwnDataProperty(value, key);
|
|
948
|
+
if (entry !== undefined) {
|
|
949
|
+
entries.push(`${JSON.stringify(key)}:${stringifyDataValue(entry)}`);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
return `{${entries.join(",")}}`;
|
|
953
|
+
}
|
|
954
|
+
throw new Error("Unsupported profiler data value");
|
|
955
|
+
}
|
|
956
|
+
function keyPart(value) {
|
|
957
|
+
if (value === undefined) {
|
|
958
|
+
return "u";
|
|
959
|
+
}
|
|
960
|
+
return typeof value === "number" ? `n${value}` : `s${value.length}:${value}`;
|
|
961
|
+
}
|
|
962
|
+
function orderChildrenBeforeParents(operations) {
|
|
963
|
+
const byId = new Map(operations.map(operation => [operation.id, operation]));
|
|
964
|
+
const children = new Map();
|
|
965
|
+
for (const operation of operations) {
|
|
966
|
+
if (!operation.parentId || !byId.has(operation.parentId)) {
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
const siblings = children.get(operation.parentId) ?? [];
|
|
970
|
+
siblings.push(operation);
|
|
971
|
+
children.set(operation.parentId, siblings);
|
|
972
|
+
}
|
|
973
|
+
const ordered = [];
|
|
974
|
+
const visited = new Set();
|
|
975
|
+
const visit = (operation) => {
|
|
976
|
+
if (visited.has(operation.id)) {
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
visited.add(operation.id);
|
|
980
|
+
for (const child of children.get(operation.id) ?? []) {
|
|
981
|
+
visit(child);
|
|
982
|
+
}
|
|
983
|
+
ordered.push(operation);
|
|
984
|
+
};
|
|
985
|
+
operations.forEach(visit);
|
|
986
|
+
return ordered;
|
|
987
|
+
}
|
|
988
|
+
function isPromiseLike(value) {
|
|
989
|
+
return Boolean(value && typeof value.then === "function");
|
|
990
|
+
}
|
|
991
|
+
function readHostCpu() {
|
|
992
|
+
let busy = 0;
|
|
993
|
+
let total = 0;
|
|
994
|
+
for (const cpu of node_os_1.default.cpus()) {
|
|
995
|
+
const cpuTotal = Object.values(cpu.times).reduce((sum, value) => sum + value, 0);
|
|
996
|
+
total += cpuTotal;
|
|
997
|
+
busy += cpuTotal - cpu.times.idle;
|
|
998
|
+
}
|
|
999
|
+
return { busy, total };
|
|
1000
|
+
}
|
|
1001
|
+
const isProcessRef = recordShape({
|
|
1002
|
+
type: oneOf("master", "worker", "browser"),
|
|
1003
|
+
pid: optional(isPositiveSafeInteger),
|
|
1004
|
+
workerInstanceId: optional(isString),
|
|
1005
|
+
browserId: optional(isString),
|
|
1006
|
+
});
|
|
1007
|
+
const isOperationTiming = recordShape({
|
|
1008
|
+
wallMs: isNonNegativeFiniteNumber,
|
|
1009
|
+
selfWallMs: optional(isNonNegativeFiniteNumber),
|
|
1010
|
+
cumulativeWorkMs: optional(isNonNegativeFiniteNumber),
|
|
1011
|
+
overlapMs: optional(isNonNegativeFiniteNumber),
|
|
1012
|
+
criticalPathMs: optional(isNonNegativeFiniteNumber),
|
|
1013
|
+
observedChildUnionMs: optional(isNonNegativeFiniteNumber),
|
|
1014
|
+
processCpuMs: optional(isNonNegativeFiniteNumber),
|
|
1015
|
+
threadCpuMs: optional(isNonNegativeFiniteNumber),
|
|
1016
|
+
activeJsMs: optional(isNonNegativeFiniteNumber),
|
|
1017
|
+
waitingMs: optional(isNonNegativeFiniteNumber),
|
|
1018
|
+
eventLoopUtilization: optional(isUnitInterval),
|
|
1019
|
+
});
|
|
1020
|
+
const isCorrelationContext = recordShape({
|
|
1021
|
+
runId: optional(isString),
|
|
1022
|
+
spanId: optional(isString),
|
|
1023
|
+
testId: optional(isString),
|
|
1024
|
+
attemptId: optional(isString),
|
|
1025
|
+
attempt: optional(isNonNegativeSafeInteger),
|
|
1026
|
+
browserId: optional(isString),
|
|
1027
|
+
sessionId: optional(isString),
|
|
1028
|
+
runnableKind: optional(isString),
|
|
1029
|
+
});
|
|
1030
|
+
const isSourceRef = recordShape({
|
|
1031
|
+
file: optional(isString),
|
|
1032
|
+
line: optional(isFiniteNumber),
|
|
1033
|
+
column: optional(isFiniteNumber),
|
|
1034
|
+
functionName: optional(isString),
|
|
1035
|
+
plugin: optional(isString),
|
|
1036
|
+
confidence: oneOf("high", "medium", "low"),
|
|
1037
|
+
});
|
|
1038
|
+
const isMeasurementQuality = recordShape({
|
|
1039
|
+
timing: oneOf("exact", "estimated", "sampled"),
|
|
1040
|
+
cpu: oneOf("process-window", "thread", "sampled", "unavailable"),
|
|
1041
|
+
source: optional(oneOf("exact", "partial", "unavailable")),
|
|
1042
|
+
notes: optional(arrayOf(isString)),
|
|
1043
|
+
});
|
|
1044
|
+
const retainedOperationGuard = recordShape({
|
|
1045
|
+
id: isString,
|
|
1046
|
+
parentId: optional(isString),
|
|
1047
|
+
kind: isString,
|
|
1048
|
+
name: isString,
|
|
1049
|
+
process: isProcessRef,
|
|
1050
|
+
context: isCorrelationContext,
|
|
1051
|
+
startOffsetMs: isNonNegativeFiniteNumber,
|
|
1052
|
+
timing: isOperationTiming,
|
|
1053
|
+
source: optional(isSourceRef),
|
|
1054
|
+
attributes: isProfilerValueRecord,
|
|
1055
|
+
quality: isMeasurementQuality,
|
|
1056
|
+
status: optional(oneOf("completed", "failed", "interrupted")),
|
|
1057
|
+
});
|
|
1058
|
+
const statisticsSnapshotGuard = recordShape({
|
|
1059
|
+
count: isNonNegativeSafeInteger,
|
|
1060
|
+
sum: isFiniteNumber,
|
|
1061
|
+
min: isFiniteNumber,
|
|
1062
|
+
max: isFiniteNumber,
|
|
1063
|
+
mean: isFiniteNumber,
|
|
1064
|
+
variance: isFiniteNumber,
|
|
1065
|
+
p50: optional(isFiniteNumber),
|
|
1066
|
+
p95: optional(isFiniteNumber),
|
|
1067
|
+
samples: optional(arrayOf(isFiniteNumber)),
|
|
1068
|
+
});
|
|
1069
|
+
const isRuntimeAggregate = recordShape({
|
|
1070
|
+
kind: isString,
|
|
1071
|
+
name: isString,
|
|
1072
|
+
attributes: optional(isProfilerValueRecord),
|
|
1073
|
+
statistics: isStatisticsSnapshot,
|
|
1074
|
+
});
|
|
1075
|
+
const isRuntimeMetric = recordShape({
|
|
1076
|
+
name: isString,
|
|
1077
|
+
value: isFiniteNumber,
|
|
1078
|
+
dimensions: isProfilerValueRecord,
|
|
1079
|
+
mode: oneOf("counter", "gauge"),
|
|
1080
|
+
});
|
|
1081
|
+
const isProfilerError = recordShape({
|
|
1082
|
+
stage: isString,
|
|
1083
|
+
code: optional(isString),
|
|
1084
|
+
message: isString,
|
|
1085
|
+
});
|
|
1086
|
+
const truncationEntryGuard = recordShape({
|
|
1087
|
+
collector: isString,
|
|
1088
|
+
seen: isNonNegativeSafeInteger,
|
|
1089
|
+
retained: isNonNegativeSafeInteger,
|
|
1090
|
+
rule: isString,
|
|
1091
|
+
truncated: isBoolean,
|
|
1092
|
+
});
|
|
1093
|
+
const profilerFragmentGuard = recordShape({
|
|
1094
|
+
transportVersion: oneOf(1),
|
|
1095
|
+
sequence: isPositiveSafeInteger,
|
|
1096
|
+
sourceRunId: isString,
|
|
1097
|
+
level: oneOf(1, 2, 3),
|
|
1098
|
+
originEpochMs: optional(isFiniteNumber),
|
|
1099
|
+
clockUncertaintyMs: optional(isNonNegativeFiniteNumber),
|
|
1100
|
+
process: isProcessRef,
|
|
1101
|
+
operations: isDataArray,
|
|
1102
|
+
aggregates: optional(arrayOf(isRuntimeAggregate)),
|
|
1103
|
+
metrics: optional(arrayOf(isRuntimeMetric)),
|
|
1104
|
+
errors: arrayOf(isProfilerError),
|
|
1105
|
+
truncation: arrayOf(isTruncationEntry),
|
|
1106
|
+
});
|
|
1107
|
+
function isRetainedOperation(value) {
|
|
1108
|
+
try {
|
|
1109
|
+
return retainedOperationGuard(value);
|
|
1110
|
+
}
|
|
1111
|
+
catch {
|
|
1112
|
+
return false;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
function isProfilerFragmentV1(value) {
|
|
1116
|
+
try {
|
|
1117
|
+
return profilerFragmentGuard(value);
|
|
1118
|
+
}
|
|
1119
|
+
catch {
|
|
1120
|
+
return false;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
function isStatisticsSnapshot(value) {
|
|
1124
|
+
if (!statisticsSnapshotGuard(value)) {
|
|
1125
|
+
return false;
|
|
1126
|
+
}
|
|
1127
|
+
const count = readOwnDataProperty(value, "count");
|
|
1128
|
+
const sum = readOwnDataProperty(value, "sum");
|
|
1129
|
+
const min = readOwnDataProperty(value, "min");
|
|
1130
|
+
const max = readOwnDataProperty(value, "max");
|
|
1131
|
+
const mean = readOwnDataProperty(value, "mean");
|
|
1132
|
+
const variance = readOwnDataProperty(value, "variance");
|
|
1133
|
+
const p50 = readOwnDataProperty(value, "p50");
|
|
1134
|
+
const p95 = readOwnDataProperty(value, "p95");
|
|
1135
|
+
const samples = readOwnDataProperty(value, "samples") ?? [];
|
|
1136
|
+
if (!count) {
|
|
1137
|
+
return (sum === 0 &&
|
|
1138
|
+
min === 0 &&
|
|
1139
|
+
max === 0 &&
|
|
1140
|
+
mean === 0 &&
|
|
1141
|
+
variance === 0 &&
|
|
1142
|
+
p50 === undefined &&
|
|
1143
|
+
p95 === undefined &&
|
|
1144
|
+
!samples.length);
|
|
1145
|
+
}
|
|
1146
|
+
if (sum < 0 ||
|
|
1147
|
+
min < 0 ||
|
|
1148
|
+
min > mean ||
|
|
1149
|
+
mean > max ||
|
|
1150
|
+
variance < 0 ||
|
|
1151
|
+
samples.length > count ||
|
|
1152
|
+
samples.length > streaming_statistics_1.DEFAULT_STATISTICS_RESERVOIR_SIZE ||
|
|
1153
|
+
((count === 1 || min === max) && variance !== 0)) {
|
|
1154
|
+
return false;
|
|
1155
|
+
}
|
|
1156
|
+
const lowerSum = min * count;
|
|
1157
|
+
const upperSum = max * count;
|
|
1158
|
+
if (!Number.isFinite(lowerSum) || !Number.isFinite(upperSum)) {
|
|
1159
|
+
return false;
|
|
1160
|
+
}
|
|
1161
|
+
const tolerance = Math.max(1, Math.abs(sum), Math.abs(lowerSum), Math.abs(upperSum)) * 1e-9;
|
|
1162
|
+
const expectedMean = sum / count;
|
|
1163
|
+
const meanTolerance = Math.max(1, Math.abs(mean), Math.abs(expectedMean)) * 1e-9;
|
|
1164
|
+
if (sum < lowerSum - tolerance || sum > upperSum + tolerance || Math.abs(mean - expectedMean) > meanTolerance) {
|
|
1165
|
+
return false;
|
|
1166
|
+
}
|
|
1167
|
+
const m2 = variance * (count - 1);
|
|
1168
|
+
if (!Number.isFinite(m2)) {
|
|
1169
|
+
return false;
|
|
1170
|
+
}
|
|
1171
|
+
const maxM2 = count * (mean - min) * (max - mean);
|
|
1172
|
+
if (Number.isFinite(maxM2)) {
|
|
1173
|
+
const m2Tolerance = Math.max(1, Math.abs(m2), Math.abs(maxM2)) * 1e-9;
|
|
1174
|
+
if (m2 > maxM2 + m2Tolerance) {
|
|
1175
|
+
return false;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
const inRange = (entry) => entry === undefined || (entry >= min && entry <= max);
|
|
1179
|
+
if (!inRange(p50) || !inRange(p95) || (p50 !== undefined && p95 !== undefined && p50 > p95)) {
|
|
1180
|
+
return false;
|
|
1181
|
+
}
|
|
1182
|
+
for (let index = 0; index < samples.length; index += 1) {
|
|
1183
|
+
if (!inRange(samples[index]) || (index > 0 && samples[index - 1] > samples[index])) {
|
|
1184
|
+
return false;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
if (!samples.length) {
|
|
1188
|
+
return p50 === undefined && p95 === undefined;
|
|
1189
|
+
}
|
|
1190
|
+
if (p50 !== sampleQuantile(samples, 0.5) || p95 !== sampleQuantile(samples, 0.95)) {
|
|
1191
|
+
return false;
|
|
1192
|
+
}
|
|
1193
|
+
if (samples.length === count) {
|
|
1194
|
+
const completeStatistics = new streaming_statistics_1.StreamingStatistics(streaming_statistics_1.DEFAULT_STATISTICS_RESERVOIR_SIZE, () => 0);
|
|
1195
|
+
for (const sample of samples) {
|
|
1196
|
+
if (!completeStatistics.add(sample)) {
|
|
1197
|
+
return false;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
const completeSnapshot = completeStatistics.snapshot();
|
|
1201
|
+
return (min === completeSnapshot.min &&
|
|
1202
|
+
max === completeSnapshot.max &&
|
|
1203
|
+
approximatelyEqual(sum, completeSnapshot.sum) &&
|
|
1204
|
+
approximatelyEqual(mean, completeSnapshot.mean) &&
|
|
1205
|
+
approximatelyEqual(variance, completeSnapshot.variance));
|
|
1206
|
+
}
|
|
1207
|
+
return true;
|
|
1208
|
+
}
|
|
1209
|
+
function isTruncationEntry(value) {
|
|
1210
|
+
return (truncationEntryGuard(value) &&
|
|
1211
|
+
readOwnDataProperty(value, "retained") <= readOwnDataProperty(value, "seen"));
|
|
1212
|
+
}
|
|
1213
|
+
function readTransportVersion(value) {
|
|
1214
|
+
try {
|
|
1215
|
+
if (!value || typeof value !== "object" || (0, types_1.isProxy)(value)) {
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, "transportVersion");
|
|
1219
|
+
return descriptor && "value" in descriptor ? descriptor.value : undefined;
|
|
1220
|
+
}
|
|
1221
|
+
catch {
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
function safeString(value) {
|
|
1226
|
+
switch (typeof value) {
|
|
1227
|
+
case "string":
|
|
1228
|
+
case "number":
|
|
1229
|
+
case "boolean":
|
|
1230
|
+
case "undefined":
|
|
1231
|
+
case "bigint":
|
|
1232
|
+
return String(value);
|
|
1233
|
+
default:
|
|
1234
|
+
return value === null ? "null" : `<${typeof value}>`;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
function isProfilerValueRecord(value) {
|
|
1238
|
+
return isPlainRecord(value) && Object.keys(value).every(key => isProfilerValue(readOwnDataProperty(value, key)));
|
|
1239
|
+
}
|
|
1240
|
+
function isProfilerValue(value) {
|
|
1241
|
+
return value === null || typeof value === "string" || typeof value === "boolean" || isFiniteNumber(value);
|
|
1242
|
+
}
|
|
1243
|
+
function isPlainRecord(value) {
|
|
1244
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || (0, types_1.isProxy)(value)) {
|
|
1245
|
+
return false;
|
|
1246
|
+
}
|
|
1247
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1248
|
+
return (prototype === Object.prototype || prototype === null) && hasOnlyDataProperties(value, true);
|
|
1249
|
+
}
|
|
1250
|
+
function recordShape(fields) {
|
|
1251
|
+
const entries = Object.entries(fields);
|
|
1252
|
+
const allowed = new Set(Object.keys(fields));
|
|
1253
|
+
return (value) => isPlainRecord(value) &&
|
|
1254
|
+
Object.keys(value).every(key => allowed.has(key)) &&
|
|
1255
|
+
entries.every(([key, guard]) => guard(readOwnDataProperty(value, key)));
|
|
1256
|
+
}
|
|
1257
|
+
function optional(guard) {
|
|
1258
|
+
return (value) => value === undefined || guard(value);
|
|
1259
|
+
}
|
|
1260
|
+
function oneOf(...allowed) {
|
|
1261
|
+
return (value) => allowed.includes(value);
|
|
1262
|
+
}
|
|
1263
|
+
function arrayOf(guard) {
|
|
1264
|
+
return (value) => isDataArray(value) && Array.prototype.every.call(value, guard);
|
|
1265
|
+
}
|
|
1266
|
+
function isString(value) {
|
|
1267
|
+
return typeof value === "string";
|
|
1268
|
+
}
|
|
1269
|
+
function isBoolean(value) {
|
|
1270
|
+
return typeof value === "boolean";
|
|
1271
|
+
}
|
|
1272
|
+
function isFiniteNumber(value) {
|
|
1273
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
1274
|
+
}
|
|
1275
|
+
function isNonNegativeFiniteNumber(value) {
|
|
1276
|
+
return isFiniteNumber(value) && value >= 0;
|
|
1277
|
+
}
|
|
1278
|
+
function isUnitInterval(value) {
|
|
1279
|
+
return isNonNegativeFiniteNumber(value) && value <= 1;
|
|
1280
|
+
}
|
|
1281
|
+
function isPositiveSafeInteger(value) {
|
|
1282
|
+
return isFiniteNumber(value) && Number.isSafeInteger(value) && value > 0;
|
|
1283
|
+
}
|
|
1284
|
+
function isNonNegativeSafeInteger(value) {
|
|
1285
|
+
return isFiniteNumber(value) && Number.isSafeInteger(value) && value >= 0;
|
|
1286
|
+
}
|
|
1287
|
+
function isDataArray(value) {
|
|
1288
|
+
if (!Array.isArray(value) || (0, types_1.isProxy)(value) || Object.getPrototypeOf(value) !== Array.prototype) {
|
|
1289
|
+
return false;
|
|
1290
|
+
}
|
|
1291
|
+
const keys = Reflect.ownKeys(value);
|
|
1292
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
1293
|
+
if (!lengthDescriptor ||
|
|
1294
|
+
!("value" in lengthDescriptor) ||
|
|
1295
|
+
!Number.isSafeInteger(lengthDescriptor.value) ||
|
|
1296
|
+
lengthDescriptor.value < 0 ||
|
|
1297
|
+
!lengthDescriptor.writable ||
|
|
1298
|
+
lengthDescriptor.enumerable ||
|
|
1299
|
+
lengthDescriptor.configurable) {
|
|
1300
|
+
return false;
|
|
1301
|
+
}
|
|
1302
|
+
const length = lengthDescriptor.value;
|
|
1303
|
+
return (keys.length === length + 1 &&
|
|
1304
|
+
keys.every((key, index) => {
|
|
1305
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1306
|
+
return index === length
|
|
1307
|
+
? key === "length" &&
|
|
1308
|
+
"value" in descriptor &&
|
|
1309
|
+
descriptor.value === length &&
|
|
1310
|
+
descriptor.writable &&
|
|
1311
|
+
!descriptor.enumerable &&
|
|
1312
|
+
!descriptor.configurable
|
|
1313
|
+
: key === String(index) &&
|
|
1314
|
+
"value" in descriptor &&
|
|
1315
|
+
descriptor.writable &&
|
|
1316
|
+
descriptor.enumerable &&
|
|
1317
|
+
descriptor.configurable;
|
|
1318
|
+
}));
|
|
1319
|
+
}
|
|
1320
|
+
function hasOnlyDataProperties(value, enumerable = false) {
|
|
1321
|
+
return Reflect.ownKeys(value).every(key => {
|
|
1322
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1323
|
+
return "value" in descriptor && (!enumerable || (typeof key === "string" && descriptor.enumerable));
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
function readOwnDataProperty(value, key) {
|
|
1327
|
+
if (!value || (typeof value !== "object" && typeof value !== "function") || (0, types_1.isProxy)(value)) {
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1331
|
+
return descriptor && "value" in descriptor ? descriptor.value : undefined;
|
|
1332
|
+
}
|
|
1333
|
+
function sampleQuantile(values, percentile) {
|
|
1334
|
+
if (!values.length) {
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
return values[Math.min(values.length - 1, Math.floor((values.length - 1) * percentile))];
|
|
1338
|
+
}
|
|
1339
|
+
function approximatelyEqual(left, right) {
|
|
1340
|
+
const tolerance = Math.max(1, Math.abs(left), Math.abs(right)) * 1e-9;
|
|
1341
|
+
return Math.abs(left - right) <= tolerance;
|
|
1342
|
+
}
|
|
1343
|
+
//# sourceMappingURL=runtime.js.map
|