@paulirish/trace_engine 0.0.50 → 0.0.52
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/.tmp/tsbuildinfo/tsconfig.tsbuildinfo +1 -1
- package/core/platform/Constructor.d.ts +2 -2
- package/core/platform/Constructor.js.map +1 -1
- package/core/platform/StringUtilities.d.ts +2 -5
- package/core/platform/StringUtilities.js +4 -4
- package/core/platform/StringUtilities.js.map +1 -1
- package/core/platform/TypescriptUtilities.d.ts +1 -1
- package/core/platform/TypescriptUtilities.js +1 -1
- package/core/platform/TypescriptUtilities.js.map +1 -1
- package/core/platform/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
- package/core/platform/platform-tsconfig.json +3 -4
- package/core/platform/platform.d.ts +1 -2
- package/core/platform/platform.js +1 -2
- package/core/platform/platform.js.map +1 -1
- package/generated/protocol.d.ts +306 -62
- package/locales/af.json +63 -3
- package/locales/am.json +63 -3
- package/locales/ar.json +63 -3
- package/locales/as.json +63 -3
- package/locales/az.json +63 -3
- package/locales/be.json +64 -4
- package/locales/bg.json +62 -2
- package/locales/bn.json +63 -3
- package/locales/bs.json +63 -3
- package/locales/ca.json +62 -2
- package/locales/cs.json +62 -2
- package/locales/cy.json +63 -3
- package/locales/da.json +63 -3
- package/locales/de.json +63 -3
- package/locales/el.json +62 -2
- package/locales/en-GB.json +63 -3
- package/locales/en-US.json +58 -22
- package/locales/en-XL.json +58 -22
- package/locales/es-419.json +63 -3
- package/locales/es.json +64 -4
- package/locales/et.json +64 -4
- package/locales/eu.json +79 -19
- package/locales/fa.json +62 -2
- package/locales/fi.json +63 -3
- package/locales/fil.json +63 -3
- package/locales/fr-CA.json +63 -3
- package/locales/fr.json +63 -3
- package/locales/gl.json +62 -2
- package/locales/gu.json +63 -3
- package/locales/he.json +63 -3
- package/locales/hi.json +62 -2
- package/locales/hr.json +63 -3
- package/locales/hu.json +62 -2
- package/locales/hy.json +63 -3
- package/locales/id.json +62 -2
- package/locales/is.json +64 -4
- package/locales/it.json +64 -4
- package/locales/ja.json +63 -3
- package/locales/ka.json +63 -3
- package/locales/kk.json +62 -2
- package/locales/km.json +62 -2
- package/locales/kn.json +63 -3
- package/locales/ko.json +63 -3
- package/locales/ky.json +63 -3
- package/locales/lo.json +63 -3
- package/locales/lt.json +62 -2
- package/locales/lv.json +62 -2
- package/locales/mk.json +62 -2
- package/locales/ml.json +63 -3
- package/locales/mn.json +63 -3
- package/locales/mr.json +64 -4
- package/locales/ms.json +63 -3
- package/locales/my.json +63 -3
- package/locales/ne.json +64 -4
- package/locales/nl.json +63 -3
- package/locales/no.json +62 -2
- package/locales/or.json +63 -3
- package/locales/pa.json +62 -2
- package/locales/pl.json +62 -2
- package/locales/pt-PT.json +62 -2
- package/locales/pt.json +63 -3
- package/locales/ro.json +63 -3
- package/locales/ru.json +63 -3
- package/locales/si.json +63 -3
- package/locales/sk.json +63 -3
- package/locales/sl.json +62 -2
- package/locales/sq.json +63 -3
- package/locales/sr-Latn.json +62 -2
- package/locales/sr.json +62 -2
- package/locales/sv.json +62 -2
- package/locales/sw.json +63 -3
- package/locales/ta.json +66 -6
- package/locales/te.json +63 -3
- package/locales/th.json +63 -3
- package/locales/tr.json +63 -3
- package/locales/uk.json +63 -3
- package/locales/ur.json +64 -4
- package/locales/uz.json +62 -2
- package/locales/vi.json +62 -2
- package/locales/zh-HK.json +63 -3
- package/locales/zh-TW.json +63 -3
- package/locales/zh.json +62 -2
- package/locales/zu.json +63 -3
- package/models/cpu_profile/CPUProfileDataModel.d.ts +8 -0
- package/models/cpu_profile/CPUProfileDataModel.js.map +1 -1
- package/models/cpu_profile/cpu_profile-tsconfig.json +3 -3
- package/models/cpu_profile/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
- package/models/trace/Processor.d.ts +1 -1
- package/models/trace/Processor.js +94 -74
- package/models/trace/Processor.js.map +1 -1
- package/models/trace/TracingManager.js.map +1 -1
- package/models/trace/bundle-tsconfig.json +1 -1
- package/models/trace/devtools_entrypoint-bundle-typescript-tsconfig.json +21 -3
- package/models/trace/extras/ScriptDuplication.d.ts +15 -16
- package/models/trace/extras/ScriptDuplication.js +63 -79
- package/models/trace/extras/ScriptDuplication.js.map +1 -1
- package/models/trace/extras/StackTraceForEvent.js +80 -47
- package/models/trace/extras/StackTraceForEvent.js.map +1 -1
- package/models/trace/extras/ThirdParties.d.ts +14 -20
- package/models/trace/extras/ThirdParties.js +97 -130
- package/models/trace/extras/ThirdParties.js.map +1 -1
- package/models/trace/extras/TraceTree.d.ts +15 -3
- package/models/trace/extras/TraceTree.js +36 -8
- package/models/trace/extras/TraceTree.js.map +1 -1
- package/models/trace/extras/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
- package/models/trace/extras/extras-tsconfig.json +3 -3
- package/models/trace/extras/extras.d.ts +1029 -844
- package/models/trace/extras/extras.js +1029 -844
- package/models/trace/handlers/AsyncJSCallsHandler.d.ts +5 -0
- package/models/trace/handlers/AsyncJSCallsHandler.js +48 -13
- package/models/trace/handlers/AsyncJSCallsHandler.js.map +1 -1
- package/models/trace/handlers/InvalidationsHandler.js +1 -0
- package/models/trace/handlers/InvalidationsHandler.js.map +1 -1
- package/models/trace/handlers/ModelHandlers.d.ts +0 -1
- package/models/trace/handlers/ModelHandlers.js +0 -1
- package/models/trace/handlers/ModelHandlers.js.map +1 -1
- package/models/trace/handlers/NetworkRequestsHandler.js +1 -1
- package/models/trace/handlers/NetworkRequestsHandler.js.map +1 -1
- package/models/trace/handlers/RendererHandler.d.ts +1 -1
- package/models/trace/handlers/RendererHandler.js +2 -2
- package/models/trace/handlers/RendererHandler.js.map +1 -1
- package/models/trace/handlers/ScreenshotsHandler.js +4 -1
- package/models/trace/handlers/ScreenshotsHandler.js.map +1 -1
- package/models/trace/handlers/ScriptsHandler.d.ts +12 -0
- package/models/trace/handlers/ScriptsHandler.js +99 -1
- package/models/trace/handlers/ScriptsHandler.js.map +1 -1
- package/models/trace/handlers/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
- package/models/trace/handlers/handlers-tsconfig.json +3 -4
- package/models/trace/handlers/helpers.js +9 -0
- package/models/trace/handlers/helpers.js.map +1 -1
- package/models/trace/helpers/Network.d.ts +5 -0
- package/models/trace/helpers/Network.js +17 -0
- package/models/trace/helpers/Network.js.map +1 -1
- package/models/trace/helpers/SamplesIntegrator.js +19 -23
- package/models/trace/helpers/SamplesIntegrator.js.map +1 -1
- package/models/trace/helpers/SyntheticEvents.d.ts +0 -1
- package/models/trace/helpers/SyntheticEvents.js +0 -4
- package/models/trace/helpers/SyntheticEvents.js.map +1 -1
- package/models/trace/helpers/Trace.d.ts +2 -1
- package/models/trace/helpers/Trace.js +101 -1
- package/models/trace/helpers/Trace.js.map +1 -1
- package/models/trace/helpers/TreeHelpers.d.ts +2 -5
- package/models/trace/helpers/TreeHelpers.js +0 -4
- package/models/trace/helpers/TreeHelpers.js.map +1 -1
- package/models/trace/helpers/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
- package/models/trace/helpers/helpers-tsconfig.json +3 -3
- package/models/trace/insights/CLSCulprits.d.ts +6 -1
- package/models/trace/insights/CLSCulprits.js +11 -2
- package/models/trace/insights/CLSCulprits.js.map +1 -1
- package/models/trace/insights/{UseCache.d.ts → Cache.d.ts} +2 -3
- package/models/trace/insights/{UseCache.js → Cache.js} +12 -6
- package/models/trace/insights/Cache.js.map +1 -0
- package/models/trace/insights/Common.d.ts +21 -2
- package/models/trace/insights/Common.js +89 -2
- package/models/trace/insights/Common.js.map +1 -1
- package/models/trace/insights/DocumentLatency.js +3 -8
- package/models/trace/insights/DocumentLatency.js.map +1 -1
- package/models/trace/insights/DuplicatedJavaScript.d.ts +7 -4
- package/models/trace/insights/DuplicatedJavaScript.js +31 -5
- package/models/trace/insights/DuplicatedJavaScript.js.map +1 -1
- package/models/trace/insights/ForcedReflow.d.ts +5 -1
- package/models/trace/insights/ForcedReflow.js +6 -2
- package/models/trace/insights/ForcedReflow.js.map +1 -1
- package/models/trace/insights/ImageDelivery.d.ts +0 -1
- package/models/trace/insights/ImageDelivery.js +1 -1
- package/models/trace/insights/ImageDelivery.js.map +1 -1
- package/models/trace/insights/InteractionToNextPaint.d.ts +1 -0
- package/models/trace/insights/InteractionToNextPaint.js +3 -0
- package/models/trace/insights/InteractionToNextPaint.js.map +1 -1
- package/models/trace/insights/LegacyJavaScript.d.ts +32 -0
- package/models/trace/insights/LegacyJavaScript.js +80 -0
- package/models/trace/insights/LegacyJavaScript.js.map +1 -0
- package/models/trace/insights/Models.d.ts +3 -1
- package/models/trace/insights/Models.js +3 -1
- package/models/trace/insights/Models.js.map +1 -1
- package/models/trace/insights/ModernHTTP.d.ts +51 -0
- package/models/trace/insights/ModernHTTP.js +187 -0
- package/models/trace/insights/ModernHTTP.js.map +1 -0
- package/models/trace/insights/NetworkDependencyTree.js +1 -1
- package/models/trace/insights/NetworkDependencyTree.js.map +1 -1
- package/models/trace/insights/ThirdParties.d.ts +1 -5
- package/models/trace/insights/ThirdParties.js +8 -21
- package/models/trace/insights/ThirdParties.js.map +1 -1
- package/models/trace/insights/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
- package/models/trace/insights/insights-tsconfig.json +9 -4
- package/models/trace/insights/types.d.ts +10 -0
- package/models/trace/insights/types.js.map +1 -1
- package/models/trace/lantern/core/core-tsconfig.json +3 -3
- package/models/trace/lantern/core/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
- package/models/trace/lantern/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
- package/models/trace/lantern/graph/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
- package/models/trace/lantern/graph/graph-tsconfig.json +3 -3
- package/models/trace/lantern/lantern-tsconfig.json +3 -3
- package/models/trace/lantern/metrics/Metric.d.ts +4 -4
- package/models/trace/lantern/metrics/Metric.js +4 -6
- package/models/trace/lantern/metrics/Metric.js.map +1 -1
- package/models/trace/lantern/metrics/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
- package/models/trace/lantern/metrics/metrics-tsconfig.json +3 -3
- package/models/trace/lantern/simulation/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
- package/models/trace/lantern/simulation/simulation-tsconfig.json +3 -3
- package/models/trace/lantern/types/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
- package/models/trace/lantern/types/types-tsconfig.json +3 -3
- package/models/trace/trace-tsconfig.json +3 -3
- package/models/trace/types/Configuration.d.ts +2 -0
- package/models/trace/types/Configuration.js.map +1 -1
- package/models/trace/types/TraceEvents.d.ts +30 -54
- package/models/trace/types/TraceEvents.js +8 -23
- package/models/trace/types/TraceEvents.js.map +1 -1
- package/models/trace/types/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
- package/models/trace/types/types-tsconfig.json +3 -3
- package/package.json +3 -2
- package/test/test-trace-engine.mjs +19 -17
- package/third_party/legacy-javascript/legacy-javascript.d.ts +1 -0
- package/third_party/legacy-javascript/legacy-javascript.js +1 -0
- package/core/platform/ServerTiming.d.ts +0 -31
- package/core/platform/ServerTiming.js +0 -212
- package/core/platform/ServerTiming.js.map +0 -1
- package/models/trace/handlers/ServerTimingsHandler.d.ts +0 -9
- package/models/trace/handlers/ServerTimingsHandler.js +0 -106
- package/models/trace/handlers/ServerTimingsHandler.js.map +0 -1
- package/models/trace/insights/UseCache.js.map +0 -1
|
@@ -1,66 +1,11 @@
|
|
|
1
1
|
// Copyright 2025 The Chromium Authors. All rights reserved.
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
|
+
import * as Handlers from '../handlers/handlers.js';
|
|
4
5
|
// Ignore modules smaller than an absolute threshold.
|
|
5
6
|
const ABSOLUTE_SIZE_THRESHOLD_BYTES = 1024 * 0.5;
|
|
6
7
|
// Ignore modules smaller than a % size of largest copy of the module.
|
|
7
8
|
const RELATIVE_SIZE_THRESHOLD = 0.1;
|
|
8
|
-
/**
|
|
9
|
-
* Using a script's contents and source map, attribute every generated byte to an authored source file.
|
|
10
|
-
*/
|
|
11
|
-
export function computeGeneratedFileSizes(script) {
|
|
12
|
-
if (!script.sourceMap) {
|
|
13
|
-
throw new Error('expected source map');
|
|
14
|
-
}
|
|
15
|
-
const map = script.sourceMap;
|
|
16
|
-
const content = script.content ?? '';
|
|
17
|
-
const contentLength = content.length;
|
|
18
|
-
const lines = content.split('\n');
|
|
19
|
-
const files = {};
|
|
20
|
-
const totalBytes = contentLength;
|
|
21
|
-
let unmappedBytes = totalBytes;
|
|
22
|
-
const lastGeneratedColumnMap = computeLastGeneratedColumnMap(script.sourceMap);
|
|
23
|
-
for (const mapping of map.mappings()) {
|
|
24
|
-
const source = mapping.sourceURL;
|
|
25
|
-
const lineNum = mapping.lineNumber;
|
|
26
|
-
const colNum = mapping.columnNumber;
|
|
27
|
-
const lastColNum = lastGeneratedColumnMap.get(mapping);
|
|
28
|
-
// Webpack sometimes emits null mappings.
|
|
29
|
-
// https://github.com/mozilla/source-map/pull/303
|
|
30
|
-
if (!source) {
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
// Lines and columns are zero-based indices. Visually, lines are shown as a 1-based index.
|
|
34
|
-
const line = lines[lineNum];
|
|
35
|
-
if (line === null || line === undefined) {
|
|
36
|
-
const errorMessage = `${map.url()} mapping for line out of bounds: ${lineNum + 1}`;
|
|
37
|
-
return { errorMessage };
|
|
38
|
-
}
|
|
39
|
-
if (colNum > line.length) {
|
|
40
|
-
const errorMessage = `${map.url()} mapping for column out of bounds: ${lineNum + 1}:${colNum}`;
|
|
41
|
-
return { errorMessage };
|
|
42
|
-
}
|
|
43
|
-
let mappingLength = 0;
|
|
44
|
-
if (lastColNum !== undefined) {
|
|
45
|
-
if (lastColNum > line.length) {
|
|
46
|
-
const errorMessage = `${map.url()} mapping for last column out of bounds: ${lineNum + 1}:${lastColNum}`;
|
|
47
|
-
return { errorMessage };
|
|
48
|
-
}
|
|
49
|
-
mappingLength = lastColNum - colNum;
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
// Add +1 to account for the newline.
|
|
53
|
-
mappingLength = line.length - colNum + 1;
|
|
54
|
-
}
|
|
55
|
-
files[source] = (files[source] || 0) + mappingLength;
|
|
56
|
-
unmappedBytes -= mappingLength;
|
|
57
|
-
}
|
|
58
|
-
return {
|
|
59
|
-
files,
|
|
60
|
-
unmappedBytes,
|
|
61
|
-
totalBytes,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
9
|
export function normalizeSource(source) {
|
|
65
10
|
// Trim trailing question mark - b/c webpack.
|
|
66
11
|
source = source.replace(/\?$/, '');
|
|
@@ -111,33 +56,66 @@ export function normalizeDuplication(duplication) {
|
|
|
111
56
|
data.estimatedDuplicateBytes = data.duplicates.slice(1).reduce((acc, cur) => acc + cur.attributedSize, 0);
|
|
112
57
|
}
|
|
113
58
|
}
|
|
114
|
-
function
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
59
|
+
function indexOfOrLength(haystack, needle, startPosition = 0) {
|
|
60
|
+
const index = haystack.indexOf(needle, startPosition);
|
|
61
|
+
return index === -1 ? haystack.length : index;
|
|
62
|
+
}
|
|
63
|
+
export function getNodeModuleName(source) {
|
|
64
|
+
const sourceSplit = source.split('node_modules/');
|
|
65
|
+
source = sourceSplit[sourceSplit.length - 1];
|
|
66
|
+
const indexFirstSlash = indexOfOrLength(source, '/');
|
|
67
|
+
if (source[0] === '@') {
|
|
68
|
+
return source.slice(0, indexOfOrLength(source, '/', indexFirstSlash + 1));
|
|
69
|
+
}
|
|
70
|
+
return source.slice(0, indexFirstSlash);
|
|
71
|
+
}
|
|
72
|
+
function groupByNodeModules(duplication) {
|
|
73
|
+
const groupedDuplication = new Map();
|
|
74
|
+
for (const [source, data] of duplication) {
|
|
75
|
+
if (!source.includes('node_modules')) {
|
|
76
|
+
groupedDuplication.set(source, data);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const nodeModuleKey = 'node_modules/' + getNodeModuleName(source);
|
|
80
|
+
const aggregatedData = groupedDuplication.get(nodeModuleKey) ?? {
|
|
81
|
+
duplicates: [],
|
|
82
|
+
// This is calculated in normalizeDuplication.
|
|
83
|
+
estimatedDuplicateBytes: 0,
|
|
84
|
+
};
|
|
85
|
+
groupedDuplication.set(nodeModuleKey, aggregatedData);
|
|
86
|
+
for (const { script, attributedSize } of data.duplicates) {
|
|
87
|
+
let duplicate = aggregatedData.duplicates.find(d => d.script === script);
|
|
88
|
+
if (!duplicate) {
|
|
89
|
+
duplicate = { script, attributedSize: 0 };
|
|
90
|
+
aggregatedData.duplicates.push(duplicate);
|
|
91
|
+
}
|
|
92
|
+
duplicate.attributedSize += attributedSize;
|
|
122
93
|
}
|
|
123
94
|
}
|
|
124
|
-
|
|
125
|
-
return result;
|
|
95
|
+
return groupedDuplication;
|
|
126
96
|
}
|
|
127
97
|
/**
|
|
128
|
-
*
|
|
98
|
+
* Sort by estimated savings.
|
|
129
99
|
*/
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
100
|
+
function sorted(duplication) {
|
|
101
|
+
return new Map([...duplication].sort((a, b) => b[1].estimatedDuplicateBytes - a[1].estimatedDuplicateBytes));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Returns 2 @see ScriptDuplication for the given collection of script contents + source maps:
|
|
105
|
+
*
|
|
106
|
+
* 1. `duplication` keys correspond to authored files
|
|
107
|
+
* 2. `duplication` keys correspond to authored files, except all files within the same
|
|
108
|
+
* node_module package are aggregated under the same entry.
|
|
109
|
+
*/
|
|
110
|
+
export function computeScriptDuplication(scriptsData, compressionRatios) {
|
|
137
111
|
const sourceDatasMap = new Map();
|
|
138
112
|
// Determine size of each `sources` entry.
|
|
139
|
-
for (const
|
|
140
|
-
if (!script.sourceMap) {
|
|
113
|
+
for (const script of scriptsData.scripts) {
|
|
114
|
+
if (!script.content || !script.sourceMap) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const sizes = Handlers.ModelHandlers.Scripts.getScriptGeneratedSizes(script);
|
|
118
|
+
if (!sizes) {
|
|
141
119
|
continue;
|
|
142
120
|
}
|
|
143
121
|
if ('errorMessage' in sizes) {
|
|
@@ -166,14 +144,20 @@ export function computeScriptDuplication(scriptsData) {
|
|
|
166
144
|
data = { estimatedDuplicateBytes: 0, duplicates: [] };
|
|
167
145
|
duplication.set(sourceData.source, data);
|
|
168
146
|
}
|
|
147
|
+
const compressionRatio = script.request ? compressionRatios.get(script.request?.args.data.requestId) ?? 1 : 1;
|
|
148
|
+
const transferSize = Math.round(sourceData.resourceSize * compressionRatio);
|
|
169
149
|
data.duplicates.push({
|
|
170
150
|
script,
|
|
171
|
-
attributedSize:
|
|
151
|
+
attributedSize: transferSize,
|
|
172
152
|
});
|
|
173
153
|
}
|
|
174
154
|
}
|
|
155
|
+
const duplicationGroupedByNodeModules = groupByNodeModules(duplication);
|
|
175
156
|
normalizeDuplication(duplication);
|
|
176
|
-
|
|
177
|
-
return
|
|
157
|
+
normalizeDuplication(duplicationGroupedByNodeModules);
|
|
158
|
+
return {
|
|
159
|
+
duplication: sorted(duplication),
|
|
160
|
+
duplicationGroupedByNodeModules: sorted(duplicationGroupedByNodeModules),
|
|
161
|
+
};
|
|
178
162
|
}
|
|
179
163
|
//# sourceMappingURL=ScriptDuplication.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScriptDuplication.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/extras/ScriptDuplication.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport type * as SDK from '../../../core/sdk/sdk.js';\nimport type * as Handlers from '../handlers/handlers.js';\n\n// Ignore modules smaller than an absolute threshold.\nconst ABSOLUTE_SIZE_THRESHOLD_BYTES = 1024 * 0.5;\n// Ignore modules smaller than a % size of largest copy of the module.\nconst RELATIVE_SIZE_THRESHOLD = 0.1;\n\ntype GeneratedFileSizes = {\n errorMessage: string,\n}|{files: Record<string, number>, unmappedBytes: number, totalBytes: number};\n\n/**\n * Using a script's contents and source map, attribute every generated byte to an authored source file.\n */\nexport function computeGeneratedFileSizes(script: Handlers.ModelHandlers.Scripts.Script): GeneratedFileSizes {\n if (!script.sourceMap) {\n throw new Error('expected source map');\n }\n\n const map = script.sourceMap;\n const content = script.content ?? '';\n const contentLength = content.length;\n const lines = content.split('\\n');\n const files: Record<string, number> = {};\n const totalBytes = contentLength;\n let unmappedBytes = totalBytes;\n\n const lastGeneratedColumnMap = computeLastGeneratedColumnMap(script.sourceMap);\n\n for (const mapping of map.mappings()) {\n const source = mapping.sourceURL;\n const lineNum = mapping.lineNumber;\n const colNum = mapping.columnNumber;\n const lastColNum = lastGeneratedColumnMap.get(mapping);\n\n // Webpack sometimes emits null mappings.\n // https://github.com/mozilla/source-map/pull/303\n if (!source) {\n continue;\n }\n\n // Lines and columns are zero-based indices. Visually, lines are shown as a 1-based index.\n\n const line = lines[lineNum];\n if (line === null || line === undefined) {\n const errorMessage = `${map.url()} mapping for line out of bounds: ${lineNum + 1}`;\n return {errorMessage};\n }\n\n if (colNum > line.length) {\n const errorMessage = `${map.url()} mapping for column out of bounds: ${lineNum + 1}:${colNum}`;\n return {errorMessage};\n }\n\n let mappingLength = 0;\n if (lastColNum !== undefined) {\n if (lastColNum > line.length) {\n const errorMessage = `${map.url()} mapping for last column out of bounds: ${lineNum + 1}:${lastColNum}`;\n return {errorMessage};\n }\n mappingLength = lastColNum - colNum;\n } else {\n // Add +1 to account for the newline.\n mappingLength = line.length - colNum + 1;\n }\n files[source] = (files[source] || 0) + mappingLength;\n unmappedBytes -= mappingLength;\n }\n\n return {\n files,\n unmappedBytes,\n totalBytes,\n };\n}\n\ninterface SourceData {\n source: string;\n resourceSize: number;\n}\n\nexport function normalizeSource(source: string): string {\n // Trim trailing question mark - b/c webpack.\n source = source.replace(/\\?$/, '');\n\n // Normalize paths for dependencies by only keeping everything after the last `node_modules`.\n const lastNodeModulesIndex = source.lastIndexOf('node_modules');\n if (lastNodeModulesIndex !== -1) {\n source = source.substring(lastNodeModulesIndex);\n }\n\n return source;\n}\n\nfunction shouldIgnoreSource(source: string): boolean {\n // Ignore bundle overhead.\n if (source.includes('webpack/bootstrap')) {\n return true;\n }\n if (source.includes('(webpack)/buildin')) {\n return true;\n }\n\n // Ignore webpack module shims, i.e. aliases of the form `module.exports = window.jQuery`\n if (source.includes('external ')) {\n return true;\n }\n\n return false;\n}\n\n/**\n * The key is a source map `sources` entry (these are URLs/file paths), but normalized\n * via `normalizeSource`.\n *\n * The value is an object with an entry for every script that has a source map which\n * denotes that this source was used, along with the estimated resource size it takes\n * up in the script.\n */\nexport type ScriptDuplication = Map<string, {\n /**\n * This is the sum of all (but one) `attributedSize` in `scripts`.\n *\n * One copy of this module is treated as the canonical version - the rest will\n * have non-zero `wastedBytes`. The canonical copy is the first entry of\n * `scripts`.\n *\n * In the case of all copies being the same version, all sizes are\n * equal and the selection doesn't matter (ignoring compression ratios). When\n * the copies are different versions, it does matter. Ideally the newest\n * version would be the canonical copy, but version information is not present.\n * Instead, size is used as a heuristic for latest version. This makes the\n * value here conserative in its estimation.\n */\n estimatedDuplicateBytes: number,\n duplicates: Array<{\n script: Handlers.ModelHandlers.Scripts.Script,\n /** The number of bytes in the script bundle that map back to this module. */\n attributedSize: number,\n }>,\n}>;\n\n/**\n * Sorts each array within @see ScriptDuplication by attributedSize, drops information\n * on sources that are too small, and calculates esimatedDuplicateBytes.\n */\nexport function normalizeDuplication(duplication: ScriptDuplication): void {\n for (const [key, data] of duplication) {\n // Sort by resource size.\n data.duplicates.sort((a, b) => b.attributedSize - a.attributedSize);\n\n // Ignore modules smaller than a % size of largest.\n if (data.duplicates.length > 1) {\n const largestResourceSize = data.duplicates[0].attributedSize;\n data.duplicates = data.duplicates.filter(duplicate => {\n const percentSize = duplicate.attributedSize / largestResourceSize;\n return percentSize >= RELATIVE_SIZE_THRESHOLD;\n });\n }\n\n // Ignore modules smaller than an absolute threshold.\n data.duplicates = data.duplicates.filter(duplicate => duplicate.attributedSize >= ABSOLUTE_SIZE_THRESHOLD_BYTES);\n\n // Delete any that now don't have multiple entries.\n if (data.duplicates.length <= 1) {\n duplication.delete(key);\n continue;\n }\n\n data.estimatedDuplicateBytes = data.duplicates.slice(1).reduce((acc, cur) => acc + cur.attributedSize, 0);\n }\n}\n\nfunction computeLastGeneratedColumnMap(map: SDK.SourceMap.SourceMap): Map<SDK.SourceMap.SourceMapEntry, number> {\n const result = new Map<SDK.SourceMap.SourceMapEntry, number>();\n\n const mappings = map.mappings();\n for (let i = 0; i < mappings.length - 1; i++) {\n const mapping = mappings[i];\n const nextMapping = mappings[i + 1];\n if (mapping.lineNumber === nextMapping.lineNumber) {\n result.set(mapping, nextMapping.columnNumber);\n }\n }\n\n // Now, all but the last mapping on each line will have 'lastColumnNumber' set to a number.\n return result;\n}\n\n/**\n * Returns a @see ScriptDuplication for the given collection of script contents + source maps.\n */\nexport function computeScriptDuplication(scriptsData: Handlers.ModelHandlers.Scripts.ScriptsData): ScriptDuplication {\n const sizesMap = new Map<Handlers.ModelHandlers.Scripts.Script, GeneratedFileSizes>();\n for (const script of scriptsData.scripts) {\n if (script.content && script.sourceMap) {\n sizesMap.set(script, computeGeneratedFileSizes(script));\n }\n }\n\n const sourceDatasMap = new Map<Handlers.ModelHandlers.Scripts.Script, SourceData[]>();\n\n // Determine size of each `sources` entry.\n for (const [script, sizes] of sizesMap) {\n if (!script.sourceMap) {\n continue;\n }\n\n if ('errorMessage' in sizes) {\n console.error(sizes.errorMessage);\n continue;\n }\n\n const sourceDataArray: SourceData[] = [];\n sourceDatasMap.set(script, sourceDataArray);\n\n const sources = script.sourceMap.sourceURLs();\n for (let i = 0; i < sources.length; i++) {\n if (shouldIgnoreSource(sources[i])) {\n continue;\n }\n\n const sourceSize = sizes.files[sources[i]];\n sourceDataArray.push({\n source: normalizeSource(sources[i]),\n resourceSize: sourceSize,\n });\n }\n }\n\n const duplication: ScriptDuplication = new Map();\n for (const [script, sourceDataArray] of sourceDatasMap) {\n for (const sourceData of sourceDataArray) {\n let data = duplication.get(sourceData.source);\n if (!data) {\n data = {estimatedDuplicateBytes: 0, duplicates: []};\n duplication.set(sourceData.source, data);\n }\n data.duplicates.push({\n script,\n attributedSize: sourceData.resourceSize,\n });\n }\n }\n\n normalizeDuplication(duplication);\n\n // Sort by estimated savings.\n return new Map([...duplication].sort((a, b) => b[1].estimatedDuplicateBytes - a[1].estimatedDuplicateBytes));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ScriptDuplication.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/extras/ScriptDuplication.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as Handlers from '../handlers/handlers.js';\n\n// Ignore modules smaller than an absolute threshold.\nconst ABSOLUTE_SIZE_THRESHOLD_BYTES = 1024 * 0.5;\n// Ignore modules smaller than a % size of largest copy of the module.\nconst RELATIVE_SIZE_THRESHOLD = 0.1;\n\ninterface SourceData {\n source: string;\n resourceSize: number;\n}\n\nexport function normalizeSource(source: string): string {\n // Trim trailing question mark - b/c webpack.\n source = source.replace(/\\?$/, '');\n\n // Normalize paths for dependencies by only keeping everything after the last `node_modules`.\n const lastNodeModulesIndex = source.lastIndexOf('node_modules');\n if (lastNodeModulesIndex !== -1) {\n source = source.substring(lastNodeModulesIndex);\n }\n\n return source;\n}\n\nfunction shouldIgnoreSource(source: string): boolean {\n // Ignore bundle overhead.\n if (source.includes('webpack/bootstrap')) {\n return true;\n }\n if (source.includes('(webpack)/buildin')) {\n return true;\n }\n\n // Ignore webpack module shims, i.e. aliases of the form `module.exports = window.jQuery`\n if (source.includes('external ')) {\n return true;\n }\n\n return false;\n}\n\n/**\n * The key is a source map `sources` entry (these are URLs/file paths), but normalized\n * via `normalizeSource`.\n *\n * The value is an object with an entry for every script that has a source map which\n * denotes that this source was used, along with the estimated resource size it takes\n * up in the script.\n */\nexport type ScriptDuplication = Map<string, {\n /**\n * This is the sum of all (but one) `attributedSize` in `scripts`.\n *\n * One copy of this module is treated as the canonical version - the rest will\n * have non-zero `wastedBytes`. The canonical copy is the first entry of\n * `scripts`.\n *\n * In the case of all copies being the same version, all sizes are\n * equal and the selection doesn't matter (ignoring compression ratios). When\n * the copies are different versions, it does matter. Ideally the newest\n * version would be the canonical copy, but version information is not present.\n * Instead, size is used as a heuristic for latest version. This makes the\n * value here conserative in its estimation.\n */\n estimatedDuplicateBytes: number,\n duplicates: Array<{\n script: Handlers.ModelHandlers.Scripts.Script,\n /**\n * The number of bytes in the script bundle that map back to this module,\n * in terms of estimated impact on transfer size.\n */\n attributedSize: number,\n }>,\n}>;\n\n/**\n * Sorts each array within @see ScriptDuplication by attributedSize, drops information\n * on sources that are too small, and calculates esimatedDuplicateBytes.\n */\nexport function normalizeDuplication(duplication: ScriptDuplication): void {\n for (const [key, data] of duplication) {\n // Sort by resource size.\n data.duplicates.sort((a, b) => b.attributedSize - a.attributedSize);\n\n // Ignore modules smaller than a % size of largest.\n if (data.duplicates.length > 1) {\n const largestResourceSize = data.duplicates[0].attributedSize;\n data.duplicates = data.duplicates.filter(duplicate => {\n const percentSize = duplicate.attributedSize / largestResourceSize;\n return percentSize >= RELATIVE_SIZE_THRESHOLD;\n });\n }\n\n // Ignore modules smaller than an absolute threshold.\n data.duplicates = data.duplicates.filter(duplicate => duplicate.attributedSize >= ABSOLUTE_SIZE_THRESHOLD_BYTES);\n\n // Delete any that now don't have multiple entries.\n if (data.duplicates.length <= 1) {\n duplication.delete(key);\n continue;\n }\n\n data.estimatedDuplicateBytes = data.duplicates.slice(1).reduce((acc, cur) => acc + cur.attributedSize, 0);\n }\n}\n\nfunction indexOfOrLength(haystack: string, needle: string, startPosition = 0): number {\n const index = haystack.indexOf(needle, startPosition);\n return index === -1 ? haystack.length : index;\n}\n\nexport function getNodeModuleName(source: string): string {\n const sourceSplit = source.split('node_modules/');\n source = sourceSplit[sourceSplit.length - 1];\n\n const indexFirstSlash = indexOfOrLength(source, '/');\n if (source[0] === '@') {\n return source.slice(0, indexOfOrLength(source, '/', indexFirstSlash + 1));\n }\n\n return source.slice(0, indexFirstSlash);\n}\n\nfunction groupByNodeModules(duplication: ScriptDuplication): ScriptDuplication {\n const groupedDuplication: ScriptDuplication = new Map();\n for (const [source, data] of duplication) {\n if (!source.includes('node_modules')) {\n groupedDuplication.set(source, data);\n continue;\n }\n\n const nodeModuleKey = 'node_modules/' + getNodeModuleName(source);\n const aggregatedData = groupedDuplication.get(nodeModuleKey) ?? {\n duplicates: [],\n // This is calculated in normalizeDuplication.\n estimatedDuplicateBytes: 0,\n };\n groupedDuplication.set(nodeModuleKey, aggregatedData);\n\n for (const {script, attributedSize} of data.duplicates) {\n let duplicate = aggregatedData.duplicates.find(d => d.script === script);\n if (!duplicate) {\n duplicate = {script, attributedSize: 0};\n aggregatedData.duplicates.push(duplicate);\n }\n duplicate.attributedSize += attributedSize;\n }\n }\n\n return groupedDuplication;\n}\n\n/**\n * Sort by estimated savings.\n */\nfunction sorted(duplication: ScriptDuplication): ScriptDuplication {\n return new Map([...duplication].sort((a, b) => b[1].estimatedDuplicateBytes - a[1].estimatedDuplicateBytes));\n}\n\n/**\n * Returns 2 @see ScriptDuplication for the given collection of script contents + source maps:\n *\n * 1. `duplication` keys correspond to authored files\n * 2. `duplication` keys correspond to authored files, except all files within the same\n * node_module package are aggregated under the same entry.\n */\nexport function computeScriptDuplication(\n scriptsData: Handlers.ModelHandlers.Scripts.ScriptsData, compressionRatios: Map<string, number>):\n {duplication: ScriptDuplication, duplicationGroupedByNodeModules: ScriptDuplication} {\n const sourceDatasMap = new Map<Handlers.ModelHandlers.Scripts.Script, SourceData[]>();\n\n // Determine size of each `sources` entry.\n for (const script of scriptsData.scripts) {\n if (!script.content || !script.sourceMap) {\n continue;\n }\n\n const sizes = Handlers.ModelHandlers.Scripts.getScriptGeneratedSizes(script);\n if (!sizes) {\n continue;\n }\n\n if ('errorMessage' in sizes) {\n console.error(sizes.errorMessage);\n continue;\n }\n\n const sourceDataArray: SourceData[] = [];\n sourceDatasMap.set(script, sourceDataArray);\n\n const sources = script.sourceMap.sourceURLs();\n for (let i = 0; i < sources.length; i++) {\n if (shouldIgnoreSource(sources[i])) {\n continue;\n }\n\n const sourceSize = sizes.files[sources[i]];\n sourceDataArray.push({\n source: normalizeSource(sources[i]),\n resourceSize: sourceSize,\n });\n }\n }\n\n const duplication: ScriptDuplication = new Map();\n for (const [script, sourceDataArray] of sourceDatasMap) {\n for (const sourceData of sourceDataArray) {\n let data = duplication.get(sourceData.source);\n if (!data) {\n data = {estimatedDuplicateBytes: 0, duplicates: []};\n duplication.set(sourceData.source, data);\n }\n const compressionRatio = script.request ? compressionRatios.get(script.request?.args.data.requestId) ?? 1 : 1;\n const transferSize = Math.round(sourceData.resourceSize * compressionRatio);\n data.duplicates.push({\n script,\n attributedSize: transferSize,\n });\n }\n }\n\n const duplicationGroupedByNodeModules = groupByNodeModules(duplication);\n\n normalizeDuplication(duplication);\n normalizeDuplication(duplicationGroupedByNodeModules);\n\n return {\n duplication: sorted(duplication),\n duplicationGroupedByNodeModules: sorted(duplicationGroupedByNodeModules),\n };\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Copyright 2024 The Chromium Authors. All rights reserved.
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
|
+
import * as Helpers from '../helpers/helpers.js';
|
|
4
5
|
import * as Types from '../types/types.js';
|
|
5
6
|
export const stackTraceForEventInTrace = new Map();
|
|
6
7
|
export function clearCacheForTrace(parsedTrace) {
|
|
@@ -23,20 +24,23 @@ export function get(event, parsedTrace) {
|
|
|
23
24
|
return resultFromCache;
|
|
24
25
|
}
|
|
25
26
|
let result = null;
|
|
26
|
-
if (Types.
|
|
27
|
-
result = getForProfileCall(event, parsedTrace);
|
|
28
|
-
}
|
|
29
|
-
else if (Types.Extensions.isSyntheticExtensionEntry(event)) {
|
|
27
|
+
if (Types.Extensions.isSyntheticExtensionEntry(event)) {
|
|
30
28
|
result = getForExtensionEntry(event, parsedTrace);
|
|
31
29
|
}
|
|
32
|
-
else if (Types.Events.
|
|
33
|
-
result =
|
|
30
|
+
else if (Types.Events.isPerformanceMeasureBegin(event)) {
|
|
31
|
+
result = getForPerformanceMeasure(event, parsedTrace);
|
|
34
32
|
}
|
|
35
|
-
else
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
|
|
33
|
+
else {
|
|
34
|
+
result = getForEvent(event, parsedTrace);
|
|
35
|
+
const maybeProtocolCallFrame = getPayloadStackAsProtocolCallFrame(event);
|
|
36
|
+
if (result && maybeProtocolCallFrame && !isNativeJSFunction(maybeProtocolCallFrame)) {
|
|
37
|
+
// If the event has a payload stack trace, replace the top frame
|
|
38
|
+
// of the calculated stack with the top frame of the payload stack
|
|
39
|
+
// because trace payload call frames contain call locations, unlike
|
|
40
|
+
// profile call frames (which contain function declaration locations).
|
|
41
|
+
// This way the user knows which exact JS location triggered an
|
|
42
|
+
// event.
|
|
43
|
+
result.callFrames[0] = maybeProtocolCallFrame;
|
|
40
44
|
}
|
|
41
45
|
}
|
|
42
46
|
if (result) {
|
|
@@ -44,21 +48,36 @@ export function get(event, parsedTrace) {
|
|
|
44
48
|
}
|
|
45
49
|
return result;
|
|
46
50
|
}
|
|
47
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Fallback method to obtain a stack trace using the parsed event tree
|
|
53
|
+
* hierarchy. This shouldn't be called outside of this file, use `get`
|
|
54
|
+
* instead to ensure the correct event in the tree hierarchy is used.
|
|
55
|
+
*/
|
|
56
|
+
function getForEvent(event, parsedTrace) {
|
|
48
57
|
// When working with a CPU profile the renderer handler won't have
|
|
49
58
|
// entries in its tree.
|
|
50
59
|
const entryToNode = parsedTrace.Renderer.entryToNode.size > 0 ? parsedTrace.Renderer.entryToNode : parsedTrace.Samples.entryToNode;
|
|
51
60
|
const topStackTrace = { callFrames: [] };
|
|
52
61
|
let stackTrace = topStackTrace;
|
|
53
|
-
let currentEntry
|
|
62
|
+
let currentEntry;
|
|
54
63
|
let node = entryToNode.get(event);
|
|
55
64
|
const traceCache = stackTraceForEventInTrace.get(parsedTrace) || new Map();
|
|
56
65
|
stackTraceForEventInTrace.set(parsedTrace, traceCache);
|
|
57
|
-
// Move up this node's ancestor tree appending frames to its
|
|
58
|
-
// stack trace.
|
|
66
|
+
// Move up this node's ancestor tree appending JS frames to its
|
|
67
|
+
// stack trace. If an async caller is detected, move up in the async
|
|
68
|
+
// stack instead.
|
|
59
69
|
while (node) {
|
|
60
70
|
if (!Types.Events.isProfileCall(node.entry)) {
|
|
61
|
-
|
|
71
|
+
const maybeAsyncParent = parsedTrace.AsyncJSCalls.runEntryPointToScheduler.get(node.entry);
|
|
72
|
+
if (!maybeAsyncParent) {
|
|
73
|
+
node = node.parent;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const maybeAsyncParentNode = maybeAsyncParent && entryToNode.get(maybeAsyncParent.scheduler);
|
|
77
|
+
if (maybeAsyncParentNode) {
|
|
78
|
+
stackTrace = addAsyncParentToStack(stackTrace, maybeAsyncParent.taskName);
|
|
79
|
+
node = maybeAsyncParentNode;
|
|
80
|
+
}
|
|
62
81
|
continue;
|
|
63
82
|
}
|
|
64
83
|
currentEntry = node.entry;
|
|
@@ -83,18 +102,7 @@ function getForProfileCall(event, parsedTrace) {
|
|
|
83
102
|
const maybeAsyncParentEvent = parsedTrace.AsyncJSCalls.asyncCallToScheduler.get(currentEntry);
|
|
84
103
|
const maybeAsyncParentNode = maybeAsyncParentEvent && entryToNode.get(maybeAsyncParentEvent.scheduler);
|
|
85
104
|
if (maybeAsyncParentNode) {
|
|
86
|
-
|
|
87
|
-
// move one level deeper in it as we walk up the ancestor tree.
|
|
88
|
-
stackTrace.parent = { callFrames: [] };
|
|
89
|
-
stackTrace = stackTrace.parent;
|
|
90
|
-
// Note: this description effectively corresponds to the name
|
|
91
|
-
// of the task that scheduled the stack trace we are jumping
|
|
92
|
-
// FROM, so it would make sense that it was set to that stack
|
|
93
|
-
// trace instead of the one we are jumping TO. However, the
|
|
94
|
-
// JS presentation utils we use to present async stack traces
|
|
95
|
-
// assume the description is added to the stack trace that
|
|
96
|
-
// scheduled the async task, so we build the data that way.
|
|
97
|
-
stackTrace.description = maybeAsyncParentEvent.taskName;
|
|
105
|
+
stackTrace = addAsyncParentToStack(stackTrace, maybeAsyncParentEvent.taskName);
|
|
98
106
|
node = maybeAsyncParentNode;
|
|
99
107
|
continue;
|
|
100
108
|
}
|
|
@@ -102,39 +110,54 @@ function getForProfileCall(event, parsedTrace) {
|
|
|
102
110
|
}
|
|
103
111
|
return topStackTrace;
|
|
104
112
|
}
|
|
113
|
+
function addAsyncParentToStack(stackTrace, taskName) {
|
|
114
|
+
const parent = { callFrames: [] };
|
|
115
|
+
// The Protocol.Runtime.StackTrace type is recursive, so we
|
|
116
|
+
// move one level deeper in it as we walk up the ancestor tree.
|
|
117
|
+
stackTrace.parent = parent;
|
|
118
|
+
// Note: this description effectively corresponds to the name
|
|
119
|
+
// of the task that scheduled the stack trace we are jumping
|
|
120
|
+
// FROM, so it would make sense that it was set to that stack
|
|
121
|
+
// trace instead of the one we are jumping TO. However, the
|
|
122
|
+
// JS presentation utils we use to present async stack traces
|
|
123
|
+
// assume the description is added to the stack trace that
|
|
124
|
+
// scheduled the async task, so we build the data that way.
|
|
125
|
+
parent.description = taskName;
|
|
126
|
+
return parent;
|
|
127
|
+
}
|
|
105
128
|
/**
|
|
106
129
|
* Finds the JS call in which an extension entry was injected (the
|
|
107
130
|
* code location that called the extension API), and returns its stack
|
|
108
131
|
* trace.
|
|
109
132
|
*/
|
|
110
133
|
function getForExtensionEntry(event, parsedTrace) {
|
|
111
|
-
|
|
134
|
+
const rawEvent = event.rawSourceEvent;
|
|
135
|
+
if (Types.Events.isPerformanceMeasureBegin(rawEvent)) {
|
|
136
|
+
return getForPerformanceMeasure(rawEvent, parsedTrace);
|
|
137
|
+
}
|
|
138
|
+
if (!rawEvent) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
return get(rawEvent, parsedTrace);
|
|
112
142
|
}
|
|
113
143
|
/**
|
|
114
|
-
*
|
|
115
|
-
* its stack trace.
|
|
144
|
+
* Gets the raw event for a user timing and obtains its stack trace.
|
|
116
145
|
*/
|
|
117
|
-
function
|
|
146
|
+
function getForPerformanceMeasure(event, parsedTrace) {
|
|
118
147
|
let rawEvent = event;
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
rawEvent = parsedTrace.UserTimings.measureTraceByTraceId.get(event.args.traceId);
|
|
148
|
+
if (event.args.traceId === undefined) {
|
|
149
|
+
return null;
|
|
124
150
|
}
|
|
151
|
+
// performance.measure calls dispatch 2 events: one for the call
|
|
152
|
+
// itself and another to represent the measured entry in the trace
|
|
153
|
+
// timeline. They are connected via a common traceId. At this
|
|
154
|
+
// point `rawEvent` corresponds to the second case, we must
|
|
155
|
+
// encounter the event for the call itself to obtain its callstack.
|
|
156
|
+
rawEvent = parsedTrace.UserTimings.measureTraceByTraceId.get(event.args.traceId);
|
|
125
157
|
if (!rawEvent) {
|
|
126
158
|
return null;
|
|
127
159
|
}
|
|
128
|
-
|
|
129
|
-
// the call to the API.
|
|
130
|
-
let node = parsedTrace.Renderer.entryToNode.get(rawEvent);
|
|
131
|
-
while (node && !Types.Events.isProfileCall(node.entry)) {
|
|
132
|
-
node = node.parent;
|
|
133
|
-
}
|
|
134
|
-
if (node && Types.Events.isProfileCall(node.entry)) {
|
|
135
|
-
return get(node.entry, parsedTrace);
|
|
136
|
-
}
|
|
137
|
-
return null;
|
|
160
|
+
return get(rawEvent, parsedTrace);
|
|
138
161
|
}
|
|
139
162
|
/**
|
|
140
163
|
* Determines if a function is a native JS API (like setTimeout,
|
|
@@ -148,4 +171,14 @@ function getForUserTiming(event, parsedTrace) {
|
|
|
148
171
|
function isNativeJSFunction({ columnNumber, lineNumber, url, scriptId }) {
|
|
149
172
|
return lineNumber === -1 && columnNumber === -1 && url === '' && scriptId === '0';
|
|
150
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Extracts the top frame in the stack contained in a trace event's payload
|
|
176
|
+
* (if any) and casts it as a Protocol.Runtime.CallFrame.
|
|
177
|
+
*/
|
|
178
|
+
function getPayloadStackAsProtocolCallFrame(event) {
|
|
179
|
+
const maybeCallStack = Helpers.Trace.getZeroIndexedStackTraceInEventPayload(event);
|
|
180
|
+
const maybeCallFrame = maybeCallStack?.at(0);
|
|
181
|
+
const maybeProtocolCallFrame = maybeCallFrame && { ...maybeCallFrame, scriptId: String(maybeCallFrame.scriptId) };
|
|
182
|
+
return maybeProtocolCallFrame || null;
|
|
183
|
+
}
|
|
151
184
|
//# sourceMappingURL=StackTraceForEvent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StackTraceForEvent.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/extras/StackTraceForEvent.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport type * as Protocol from '../../../generated/protocol.js';\nimport type * as Handlers from '../handlers/handlers.js';\nimport type * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nexport const stackTraceForEventInTrace =\n new Map<Handlers.Types.ParsedTrace, Map<Types.Events.Event, Protocol.Runtime.StackTrace>>();\n\nexport function clearCacheForTrace(parsedTrace: Handlers.Types.ParsedTrace): void {\n stackTraceForEventInTrace.delete(parsedTrace);\n}\n/**\n * This util builds a stack trace that includes async calls for a given\n * event. It leverages data we collect from sampling to deduce sync\n * stacks and trace event instrumentation on the V8 debugger to stitch\n * them together.\n */\nexport function get(event: Types.Events.Event, parsedTrace: Handlers.Types.ParsedTrace): Protocol.Runtime.StackTrace|\n null {\n let cacheForTrace = stackTraceForEventInTrace.get(parsedTrace);\n if (!cacheForTrace) {\n cacheForTrace = new Map();\n stackTraceForEventInTrace.set(parsedTrace, cacheForTrace);\n }\n const resultFromCache = cacheForTrace.get(event);\n if (resultFromCache) {\n return resultFromCache;\n }\n let result: Protocol.Runtime.StackTrace|null = null;\n if (Types.Events.isProfileCall(event)) {\n result = getForProfileCall(event, parsedTrace);\n } else if (Types.Extensions.isSyntheticExtensionEntry(event)) {\n result = getForExtensionEntry(event, parsedTrace);\n } else if (Types.Events.isUserTiming(event)) {\n result = getForUserTiming(event, parsedTrace);\n } else if (Types.Events.isLayout(event) || Types.Events.isUpdateLayoutTree(event)) {\n const node = parsedTrace.Renderer.entryToNode.get(event);\n const parent = node?.parent?.entry;\n if (parent) {\n result = get(parent, parsedTrace);\n }\n }\n if (result) {\n cacheForTrace.set(event, result);\n }\n return result;\n}\n\nfunction getForProfileCall(\n event: Types.Events.SyntheticProfileCall, parsedTrace: Handlers.Types.ParsedTrace): Protocol.Runtime.StackTrace {\n // When working with a CPU profile the renderer handler won't have\n // entries in its tree.\n const entryToNode =\n parsedTrace.Renderer.entryToNode.size > 0 ? parsedTrace.Renderer.entryToNode : parsedTrace.Samples.entryToNode;\n const topStackTrace: Protocol.Runtime.StackTrace = {callFrames: []};\n let stackTrace: Protocol.Runtime.StackTrace = topStackTrace;\n let currentEntry = event;\n let node: Helpers.TreeHelpers.TraceEntryNode|null|undefined = entryToNode.get(event);\n const traceCache =\n stackTraceForEventInTrace.get(parsedTrace) || new Map<Types.Events.Event, Protocol.Runtime.StackTrace>();\n stackTraceForEventInTrace.set(parsedTrace, traceCache);\n // Move up this node's ancestor tree appending frames to its\n // stack trace.\n while (node) {\n if (!Types.Events.isProfileCall(node.entry)) {\n node = node.parent;\n continue;\n }\n\n currentEntry = node.entry;\n // First check if this entry was processed before.\n const stackTraceFromCache = traceCache.get(node.entry);\n if (stackTraceFromCache) {\n stackTrace.callFrames.push(...stackTraceFromCache.callFrames.filter(callFrame => !isNativeJSFunction(callFrame)));\n stackTrace.parent = stackTraceFromCache.parent;\n // Only set the description to the cache value if we didn't\n // compute it in the previous iteration, since the async stack\n // trace descriptions / taskNames is only extracted when jumping\n // to the async parent, and that might not have happened when\n // the cached value was computed (e.g. the cached value\n // computation started at some point inside the parent stack\n // trace).\n stackTrace.description = stackTrace.description || stackTraceFromCache.description;\n break;\n }\n\n if (!isNativeJSFunction(currentEntry.callFrame)) {\n stackTrace.callFrames.push(currentEntry.callFrame);\n }\n const maybeAsyncParentEvent = parsedTrace.AsyncJSCalls.asyncCallToScheduler.get(currentEntry);\n const maybeAsyncParentNode = maybeAsyncParentEvent && entryToNode.get(maybeAsyncParentEvent.scheduler);\n if (maybeAsyncParentNode) {\n // The Protocol.Runtime.StackTrace type is recursive, so we\n // move one level deeper in it as we walk up the ancestor tree.\n stackTrace.parent = {callFrames: []};\n stackTrace = stackTrace.parent;\n // Note: this description effectively corresponds to the name\n // of the task that scheduled the stack trace we are jumping\n // FROM, so it would make sense that it was set to that stack\n // trace instead of the one we are jumping TO. However, the\n // JS presentation utils we use to present async stack traces\n // assume the description is added to the stack trace that\n // scheduled the async task, so we build the data that way.\n stackTrace.description = maybeAsyncParentEvent.taskName;\n node = maybeAsyncParentNode;\n continue;\n }\n node = node.parent;\n }\n return topStackTrace;\n}\n\n/**\n * Finds the JS call in which an extension entry was injected (the\n * code location that called the extension API), and returns its stack\n * trace.\n */\nfunction getForExtensionEntry(event: Types.Extensions.SyntheticExtensionEntry, parsedTrace: Handlers.Types.ParsedTrace):\n Protocol.Runtime.StackTrace|null {\n return getForUserTiming(event.rawSourceEvent, parsedTrace);\n}\n\n/**\n * Finds the JS call in which the user timing API was called and returns\n * its stack trace.\n */\nfunction getForUserTiming(\n event: Types.Events.UserTiming|Types.Events.ConsoleTimeStamp,\n parsedTrace: Handlers.Types.ParsedTrace): Protocol.Runtime.StackTrace|null {\n let rawEvent: Types.Events.Event|undefined = event;\n if (Types.Events.isPerformanceMeasureBegin(event)) {\n if (event.args.traceId === undefined) {\n return null;\n }\n rawEvent = parsedTrace.UserTimings.measureTraceByTraceId.get(event.args.traceId);\n }\n if (!rawEvent) {\n return null;\n }\n // Look for the nearest profile call ancestor of the event tracing\n // the call to the API.\n let node: Helpers.TreeHelpers.TraceEntryNode|undefined|null = parsedTrace.Renderer.entryToNode.get(rawEvent);\n while (node && !Types.Events.isProfileCall(node.entry)) {\n node = node.parent;\n }\n if (node && Types.Events.isProfileCall(node.entry)) {\n return get(node.entry, parsedTrace);\n }\n return null;\n}\n/**\n * Determines if a function is a native JS API (like setTimeout,\n * requestAnimationFrame, consoleTask.run. etc.). This is useful to\n * discard stack frames corresponding to the JS scheduler function\n * itself, since it's already being used as title of async stack traces\n * taken from the async `taskName`. This is also consistent with the\n * behaviour of the stack trace in the sources\n * panel.\n */\nfunction isNativeJSFunction({columnNumber, lineNumber, url, scriptId}: Protocol.Runtime.CallFrame): boolean {\n return lineNumber === -1 && columnNumber === -1 && url === '' && scriptId === '0';\n}\n"]}
|
|
1
|
+
{"version":3,"file":"StackTraceForEvent.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/extras/StackTraceForEvent.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport type * as Protocol from '../../../generated/protocol.js';\nimport type * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nexport const stackTraceForEventInTrace =\n new Map<Handlers.Types.ParsedTrace, Map<Types.Events.Event, Protocol.Runtime.StackTrace>>();\n\nexport function clearCacheForTrace(parsedTrace: Handlers.Types.ParsedTrace): void {\n stackTraceForEventInTrace.delete(parsedTrace);\n}\n/**\n * This util builds a stack trace that includes async calls for a given\n * event. It leverages data we collect from sampling to deduce sync\n * stacks and trace event instrumentation on the V8 debugger to stitch\n * them together.\n */\nexport function get(event: Types.Events.Event, parsedTrace: Handlers.Types.ParsedTrace): Protocol.Runtime.StackTrace|\n null {\n let cacheForTrace = stackTraceForEventInTrace.get(parsedTrace);\n if (!cacheForTrace) {\n cacheForTrace = new Map();\n stackTraceForEventInTrace.set(parsedTrace, cacheForTrace);\n }\n const resultFromCache = cacheForTrace.get(event);\n if (resultFromCache) {\n return resultFromCache;\n }\n let result: Protocol.Runtime.StackTrace|null = null;\n if (Types.Extensions.isSyntheticExtensionEntry(event)) {\n result = getForExtensionEntry(event, parsedTrace);\n } else if (Types.Events.isPerformanceMeasureBegin(event)) {\n result = getForPerformanceMeasure(event, parsedTrace);\n } else {\n result = getForEvent(event, parsedTrace);\n const maybeProtocolCallFrame = getPayloadStackAsProtocolCallFrame(event);\n if (result && maybeProtocolCallFrame && !isNativeJSFunction(maybeProtocolCallFrame)) {\n // If the event has a payload stack trace, replace the top frame\n // of the calculated stack with the top frame of the payload stack\n // because trace payload call frames contain call locations, unlike\n // profile call frames (which contain function declaration locations).\n // This way the user knows which exact JS location triggered an\n // event.\n result.callFrames[0] = maybeProtocolCallFrame;\n }\n }\n if (result) {\n cacheForTrace.set(event, result);\n }\n return result;\n}\n\n/**\n * Fallback method to obtain a stack trace using the parsed event tree\n * hierarchy. This shouldn't be called outside of this file, use `get`\n * instead to ensure the correct event in the tree hierarchy is used.\n */\nfunction getForEvent(event: Types.Events.Event, parsedTrace: Handlers.Types.ParsedTrace): Protocol.Runtime.StackTrace {\n // When working with a CPU profile the renderer handler won't have\n // entries in its tree.\n const entryToNode =\n parsedTrace.Renderer.entryToNode.size > 0 ? parsedTrace.Renderer.entryToNode : parsedTrace.Samples.entryToNode;\n const topStackTrace: Protocol.Runtime.StackTrace = {callFrames: []};\n let stackTrace: Protocol.Runtime.StackTrace = topStackTrace;\n let currentEntry: Types.Events.SyntheticProfileCall;\n let node: Helpers.TreeHelpers.TraceEntryNode|null|undefined = entryToNode.get(event);\n const traceCache =\n stackTraceForEventInTrace.get(parsedTrace) || new Map<Types.Events.Event, Protocol.Runtime.StackTrace>();\n stackTraceForEventInTrace.set(parsedTrace, traceCache);\n // Move up this node's ancestor tree appending JS frames to its\n // stack trace. If an async caller is detected, move up in the async\n // stack instead.\n while (node) {\n if (!Types.Events.isProfileCall(node.entry)) {\n const maybeAsyncParent = parsedTrace.AsyncJSCalls.runEntryPointToScheduler.get(node.entry);\n if (!maybeAsyncParent) {\n node = node.parent;\n continue;\n }\n const maybeAsyncParentNode = maybeAsyncParent && entryToNode.get(maybeAsyncParent.scheduler);\n if (maybeAsyncParentNode) {\n stackTrace = addAsyncParentToStack(stackTrace, maybeAsyncParent.taskName);\n node = maybeAsyncParentNode;\n }\n continue;\n }\n currentEntry = node.entry;\n // First check if this entry was processed before.\n const stackTraceFromCache = traceCache.get(node.entry);\n if (stackTraceFromCache) {\n stackTrace.callFrames.push(...stackTraceFromCache.callFrames.filter(callFrame => !isNativeJSFunction(callFrame)));\n stackTrace.parent = stackTraceFromCache.parent;\n // Only set the description to the cache value if we didn't\n // compute it in the previous iteration, since the async stack\n // trace descriptions / taskNames is only extracted when jumping\n // to the async parent, and that might not have happened when\n // the cached value was computed (e.g. the cached value\n // computation started at some point inside the parent stack\n // trace).\n stackTrace.description = stackTrace.description || stackTraceFromCache.description;\n break;\n }\n\n if (!isNativeJSFunction(currentEntry.callFrame)) {\n stackTrace.callFrames.push(currentEntry.callFrame);\n }\n const maybeAsyncParentEvent = parsedTrace.AsyncJSCalls.asyncCallToScheduler.get(currentEntry);\n const maybeAsyncParentNode = maybeAsyncParentEvent && entryToNode.get(maybeAsyncParentEvent.scheduler);\n if (maybeAsyncParentNode) {\n stackTrace = addAsyncParentToStack(stackTrace, maybeAsyncParentEvent.taskName);\n node = maybeAsyncParentNode;\n continue;\n }\n node = node.parent;\n }\n return topStackTrace;\n}\n\nfunction addAsyncParentToStack(stackTrace: Protocol.Runtime.StackTrace, taskName: string): Protocol.Runtime.StackTrace {\n const parent: Protocol.Runtime.StackTrace = {callFrames: []};\n // The Protocol.Runtime.StackTrace type is recursive, so we\n // move one level deeper in it as we walk up the ancestor tree.\n stackTrace.parent = parent;\n // Note: this description effectively corresponds to the name\n // of the task that scheduled the stack trace we are jumping\n // FROM, so it would make sense that it was set to that stack\n // trace instead of the one we are jumping TO. However, the\n // JS presentation utils we use to present async stack traces\n // assume the description is added to the stack trace that\n // scheduled the async task, so we build the data that way.\n parent.description = taskName;\n return parent;\n}\n\n/**\n * Finds the JS call in which an extension entry was injected (the\n * code location that called the extension API), and returns its stack\n * trace.\n */\nfunction getForExtensionEntry(event: Types.Extensions.SyntheticExtensionEntry, parsedTrace: Handlers.Types.ParsedTrace):\n Protocol.Runtime.StackTrace|null {\n const rawEvent: Types.Events.Event = event.rawSourceEvent;\n if (Types.Events.isPerformanceMeasureBegin(rawEvent)) {\n return getForPerformanceMeasure(rawEvent, parsedTrace);\n }\n if (!rawEvent) {\n return null;\n }\n return get(rawEvent, parsedTrace);\n}\n\n/**\n * Gets the raw event for a user timing and obtains its stack trace.\n */\nfunction getForPerformanceMeasure(event: Types.Events.PerformanceMeasureBegin, parsedTrace: Handlers.Types.ParsedTrace):\n Protocol.Runtime.StackTrace|null {\n let rawEvent: Types.Events.Event|undefined = event;\n if (event.args.traceId === undefined) {\n return null;\n }\n // performance.measure calls dispatch 2 events: one for the call\n // itself and another to represent the measured entry in the trace\n // timeline. They are connected via a common traceId. At this\n // point `rawEvent` corresponds to the second case, we must\n // encounter the event for the call itself to obtain its callstack.\n rawEvent = parsedTrace.UserTimings.measureTraceByTraceId.get(event.args.traceId);\n if (!rawEvent) {\n return null;\n }\n return get(rawEvent, parsedTrace);\n}\n/**\n * Determines if a function is a native JS API (like setTimeout,\n * requestAnimationFrame, consoleTask.run. etc.). This is useful to\n * discard stack frames corresponding to the JS scheduler function\n * itself, since it's already being used as title of async stack traces\n * taken from the async `taskName`. This is also consistent with the\n * behaviour of the stack trace in the sources\n * panel.\n */\nfunction isNativeJSFunction({columnNumber, lineNumber, url, scriptId}: Protocol.Runtime.CallFrame): boolean {\n return lineNumber === -1 && columnNumber === -1 && url === '' && scriptId === '0';\n}\n\n/**\n * Extracts the top frame in the stack contained in a trace event's payload\n * (if any) and casts it as a Protocol.Runtime.CallFrame.\n */\nfunction getPayloadStackAsProtocolCallFrame(event: Types.Events.Event): Protocol.Runtime.CallFrame|null {\n const maybeCallStack = Helpers.Trace.getZeroIndexedStackTraceInEventPayload(event);\n const maybeCallFrame = maybeCallStack?.at(0);\n const maybeProtocolCallFrame =\n maybeCallFrame && {...maybeCallFrame, scriptId: String(maybeCallFrame.scriptId) as Protocol.Runtime.ScriptId};\n return maybeProtocolCallFrame || null;\n}\n"]}
|
|
@@ -1,28 +1,22 @@
|
|
|
1
|
-
import * as ThirdPartyWeb from '../../../third_party/third-party-web/third-party-web.js';
|
|
1
|
+
import type * as ThirdPartyWeb from '../../../third_party/third-party-web/third-party-web.js';
|
|
2
2
|
import * as Handlers from '../handlers/handlers.js';
|
|
3
3
|
import * as Types from '../types/types.js';
|
|
4
4
|
export type Entity = typeof ThirdPartyWeb.ThirdPartyWeb.entities[number];
|
|
5
|
-
|
|
5
|
+
interface BaseSummary {
|
|
6
|
+
entity: Entity;
|
|
6
7
|
transferSize: number;
|
|
7
|
-
mainThreadTime: Types.Timing.
|
|
8
|
+
mainThreadTime: Types.Timing.Milli;
|
|
8
9
|
}
|
|
9
|
-
export interface
|
|
10
|
-
|
|
11
|
-
byUrl: Map<string, Summary>;
|
|
12
|
-
urlsByEntity: Map<Entity, Set<string>>;
|
|
13
|
-
eventsByEntity: Map<Entity, Types.Events.Event[]>;
|
|
14
|
-
madeUpEntityCache: Map<string, Entity>;
|
|
10
|
+
export interface EntitySummary extends BaseSummary {
|
|
11
|
+
relatedEvents: Types.Events.Event[];
|
|
15
12
|
}
|
|
13
|
+
export interface URLSummary extends BaseSummary {
|
|
14
|
+
url: string;
|
|
15
|
+
request?: Types.Events.SyntheticNetworkRequest;
|
|
16
|
+
}
|
|
17
|
+
export declare function summarizeByThirdParty(parsedTrace: Handlers.Types.ParsedTrace, traceBounds: Types.Timing.TraceWindowMicro): EntitySummary[];
|
|
16
18
|
/**
|
|
17
|
-
*
|
|
18
|
-
*/
|
|
19
|
-
export declare function summarizeThirdParties(parsedTrace: Handlers.Types.ParsedTrace, traceBounds: Types.Timing.TraceWindowMicro, networkRequests: Types.Events.SyntheticNetworkRequest[]): ThirdPartySummary;
|
|
20
|
-
/**
|
|
21
|
-
* Note: unlike summarizeThirdParties, this does not calculate mainThreadTime. The reason is that it is not
|
|
22
|
-
* needed for its one use case, and when dragging the trace bounds it takes a long time to calculate.
|
|
23
|
-
* If it is ever needed, we need to make getSelfTimeByUrl (see deleted code/blame) much faster (cache + bucket?).
|
|
19
|
+
* Used only by Lighthouse.
|
|
24
20
|
*/
|
|
25
|
-
export declare function
|
|
26
|
-
|
|
27
|
-
entityByEvent: Map<Types.Events.Event, Handlers.Helpers.Entity>;
|
|
28
|
-
};
|
|
21
|
+
export declare function summarizeByURL(parsedTrace: Handlers.Types.ParsedTrace, traceBounds: Types.Timing.TraceWindowMicro): URLSummary[];
|
|
22
|
+
export {};
|