@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.
Files changed (236) hide show
  1. package/.tmp/tsbuildinfo/tsconfig.tsbuildinfo +1 -1
  2. package/core/platform/Constructor.d.ts +2 -2
  3. package/core/platform/Constructor.js.map +1 -1
  4. package/core/platform/StringUtilities.d.ts +2 -5
  5. package/core/platform/StringUtilities.js +4 -4
  6. package/core/platform/StringUtilities.js.map +1 -1
  7. package/core/platform/TypescriptUtilities.d.ts +1 -1
  8. package/core/platform/TypescriptUtilities.js +1 -1
  9. package/core/platform/TypescriptUtilities.js.map +1 -1
  10. package/core/platform/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
  11. package/core/platform/platform-tsconfig.json +3 -4
  12. package/core/platform/platform.d.ts +1 -2
  13. package/core/platform/platform.js +1 -2
  14. package/core/platform/platform.js.map +1 -1
  15. package/generated/protocol.d.ts +306 -62
  16. package/locales/af.json +63 -3
  17. package/locales/am.json +63 -3
  18. package/locales/ar.json +63 -3
  19. package/locales/as.json +63 -3
  20. package/locales/az.json +63 -3
  21. package/locales/be.json +64 -4
  22. package/locales/bg.json +62 -2
  23. package/locales/bn.json +63 -3
  24. package/locales/bs.json +63 -3
  25. package/locales/ca.json +62 -2
  26. package/locales/cs.json +62 -2
  27. package/locales/cy.json +63 -3
  28. package/locales/da.json +63 -3
  29. package/locales/de.json +63 -3
  30. package/locales/el.json +62 -2
  31. package/locales/en-GB.json +63 -3
  32. package/locales/en-US.json +58 -22
  33. package/locales/en-XL.json +58 -22
  34. package/locales/es-419.json +63 -3
  35. package/locales/es.json +64 -4
  36. package/locales/et.json +64 -4
  37. package/locales/eu.json +79 -19
  38. package/locales/fa.json +62 -2
  39. package/locales/fi.json +63 -3
  40. package/locales/fil.json +63 -3
  41. package/locales/fr-CA.json +63 -3
  42. package/locales/fr.json +63 -3
  43. package/locales/gl.json +62 -2
  44. package/locales/gu.json +63 -3
  45. package/locales/he.json +63 -3
  46. package/locales/hi.json +62 -2
  47. package/locales/hr.json +63 -3
  48. package/locales/hu.json +62 -2
  49. package/locales/hy.json +63 -3
  50. package/locales/id.json +62 -2
  51. package/locales/is.json +64 -4
  52. package/locales/it.json +64 -4
  53. package/locales/ja.json +63 -3
  54. package/locales/ka.json +63 -3
  55. package/locales/kk.json +62 -2
  56. package/locales/km.json +62 -2
  57. package/locales/kn.json +63 -3
  58. package/locales/ko.json +63 -3
  59. package/locales/ky.json +63 -3
  60. package/locales/lo.json +63 -3
  61. package/locales/lt.json +62 -2
  62. package/locales/lv.json +62 -2
  63. package/locales/mk.json +62 -2
  64. package/locales/ml.json +63 -3
  65. package/locales/mn.json +63 -3
  66. package/locales/mr.json +64 -4
  67. package/locales/ms.json +63 -3
  68. package/locales/my.json +63 -3
  69. package/locales/ne.json +64 -4
  70. package/locales/nl.json +63 -3
  71. package/locales/no.json +62 -2
  72. package/locales/or.json +63 -3
  73. package/locales/pa.json +62 -2
  74. package/locales/pl.json +62 -2
  75. package/locales/pt-PT.json +62 -2
  76. package/locales/pt.json +63 -3
  77. package/locales/ro.json +63 -3
  78. package/locales/ru.json +63 -3
  79. package/locales/si.json +63 -3
  80. package/locales/sk.json +63 -3
  81. package/locales/sl.json +62 -2
  82. package/locales/sq.json +63 -3
  83. package/locales/sr-Latn.json +62 -2
  84. package/locales/sr.json +62 -2
  85. package/locales/sv.json +62 -2
  86. package/locales/sw.json +63 -3
  87. package/locales/ta.json +66 -6
  88. package/locales/te.json +63 -3
  89. package/locales/th.json +63 -3
  90. package/locales/tr.json +63 -3
  91. package/locales/uk.json +63 -3
  92. package/locales/ur.json +64 -4
  93. package/locales/uz.json +62 -2
  94. package/locales/vi.json +62 -2
  95. package/locales/zh-HK.json +63 -3
  96. package/locales/zh-TW.json +63 -3
  97. package/locales/zh.json +62 -2
  98. package/locales/zu.json +63 -3
  99. package/models/cpu_profile/CPUProfileDataModel.d.ts +8 -0
  100. package/models/cpu_profile/CPUProfileDataModel.js.map +1 -1
  101. package/models/cpu_profile/cpu_profile-tsconfig.json +3 -3
  102. package/models/cpu_profile/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
  103. package/models/trace/Processor.d.ts +1 -1
  104. package/models/trace/Processor.js +94 -74
  105. package/models/trace/Processor.js.map +1 -1
  106. package/models/trace/TracingManager.js.map +1 -1
  107. package/models/trace/bundle-tsconfig.json +1 -1
  108. package/models/trace/devtools_entrypoint-bundle-typescript-tsconfig.json +21 -3
  109. package/models/trace/extras/ScriptDuplication.d.ts +15 -16
  110. package/models/trace/extras/ScriptDuplication.js +63 -79
  111. package/models/trace/extras/ScriptDuplication.js.map +1 -1
  112. package/models/trace/extras/StackTraceForEvent.js +80 -47
  113. package/models/trace/extras/StackTraceForEvent.js.map +1 -1
  114. package/models/trace/extras/ThirdParties.d.ts +14 -20
  115. package/models/trace/extras/ThirdParties.js +97 -130
  116. package/models/trace/extras/ThirdParties.js.map +1 -1
  117. package/models/trace/extras/TraceTree.d.ts +15 -3
  118. package/models/trace/extras/TraceTree.js +36 -8
  119. package/models/trace/extras/TraceTree.js.map +1 -1
  120. package/models/trace/extras/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
  121. package/models/trace/extras/extras-tsconfig.json +3 -3
  122. package/models/trace/extras/extras.d.ts +1029 -844
  123. package/models/trace/extras/extras.js +1029 -844
  124. package/models/trace/handlers/AsyncJSCallsHandler.d.ts +5 -0
  125. package/models/trace/handlers/AsyncJSCallsHandler.js +48 -13
  126. package/models/trace/handlers/AsyncJSCallsHandler.js.map +1 -1
  127. package/models/trace/handlers/InvalidationsHandler.js +1 -0
  128. package/models/trace/handlers/InvalidationsHandler.js.map +1 -1
  129. package/models/trace/handlers/ModelHandlers.d.ts +0 -1
  130. package/models/trace/handlers/ModelHandlers.js +0 -1
  131. package/models/trace/handlers/ModelHandlers.js.map +1 -1
  132. package/models/trace/handlers/NetworkRequestsHandler.js +1 -1
  133. package/models/trace/handlers/NetworkRequestsHandler.js.map +1 -1
  134. package/models/trace/handlers/RendererHandler.d.ts +1 -1
  135. package/models/trace/handlers/RendererHandler.js +2 -2
  136. package/models/trace/handlers/RendererHandler.js.map +1 -1
  137. package/models/trace/handlers/ScreenshotsHandler.js +4 -1
  138. package/models/trace/handlers/ScreenshotsHandler.js.map +1 -1
  139. package/models/trace/handlers/ScriptsHandler.d.ts +12 -0
  140. package/models/trace/handlers/ScriptsHandler.js +99 -1
  141. package/models/trace/handlers/ScriptsHandler.js.map +1 -1
  142. package/models/trace/handlers/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
  143. package/models/trace/handlers/handlers-tsconfig.json +3 -4
  144. package/models/trace/handlers/helpers.js +9 -0
  145. package/models/trace/handlers/helpers.js.map +1 -1
  146. package/models/trace/helpers/Network.d.ts +5 -0
  147. package/models/trace/helpers/Network.js +17 -0
  148. package/models/trace/helpers/Network.js.map +1 -1
  149. package/models/trace/helpers/SamplesIntegrator.js +19 -23
  150. package/models/trace/helpers/SamplesIntegrator.js.map +1 -1
  151. package/models/trace/helpers/SyntheticEvents.d.ts +0 -1
  152. package/models/trace/helpers/SyntheticEvents.js +0 -4
  153. package/models/trace/helpers/SyntheticEvents.js.map +1 -1
  154. package/models/trace/helpers/Trace.d.ts +2 -1
  155. package/models/trace/helpers/Trace.js +101 -1
  156. package/models/trace/helpers/Trace.js.map +1 -1
  157. package/models/trace/helpers/TreeHelpers.d.ts +2 -5
  158. package/models/trace/helpers/TreeHelpers.js +0 -4
  159. package/models/trace/helpers/TreeHelpers.js.map +1 -1
  160. package/models/trace/helpers/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
  161. package/models/trace/helpers/helpers-tsconfig.json +3 -3
  162. package/models/trace/insights/CLSCulprits.d.ts +6 -1
  163. package/models/trace/insights/CLSCulprits.js +11 -2
  164. package/models/trace/insights/CLSCulprits.js.map +1 -1
  165. package/models/trace/insights/{UseCache.d.ts → Cache.d.ts} +2 -3
  166. package/models/trace/insights/{UseCache.js → Cache.js} +12 -6
  167. package/models/trace/insights/Cache.js.map +1 -0
  168. package/models/trace/insights/Common.d.ts +21 -2
  169. package/models/trace/insights/Common.js +89 -2
  170. package/models/trace/insights/Common.js.map +1 -1
  171. package/models/trace/insights/DocumentLatency.js +3 -8
  172. package/models/trace/insights/DocumentLatency.js.map +1 -1
  173. package/models/trace/insights/DuplicatedJavaScript.d.ts +7 -4
  174. package/models/trace/insights/DuplicatedJavaScript.js +31 -5
  175. package/models/trace/insights/DuplicatedJavaScript.js.map +1 -1
  176. package/models/trace/insights/ForcedReflow.d.ts +5 -1
  177. package/models/trace/insights/ForcedReflow.js +6 -2
  178. package/models/trace/insights/ForcedReflow.js.map +1 -1
  179. package/models/trace/insights/ImageDelivery.d.ts +0 -1
  180. package/models/trace/insights/ImageDelivery.js +1 -1
  181. package/models/trace/insights/ImageDelivery.js.map +1 -1
  182. package/models/trace/insights/InteractionToNextPaint.d.ts +1 -0
  183. package/models/trace/insights/InteractionToNextPaint.js +3 -0
  184. package/models/trace/insights/InteractionToNextPaint.js.map +1 -1
  185. package/models/trace/insights/LegacyJavaScript.d.ts +32 -0
  186. package/models/trace/insights/LegacyJavaScript.js +80 -0
  187. package/models/trace/insights/LegacyJavaScript.js.map +1 -0
  188. package/models/trace/insights/Models.d.ts +3 -1
  189. package/models/trace/insights/Models.js +3 -1
  190. package/models/trace/insights/Models.js.map +1 -1
  191. package/models/trace/insights/ModernHTTP.d.ts +51 -0
  192. package/models/trace/insights/ModernHTTP.js +187 -0
  193. package/models/trace/insights/ModernHTTP.js.map +1 -0
  194. package/models/trace/insights/NetworkDependencyTree.js +1 -1
  195. package/models/trace/insights/NetworkDependencyTree.js.map +1 -1
  196. package/models/trace/insights/ThirdParties.d.ts +1 -5
  197. package/models/trace/insights/ThirdParties.js +8 -21
  198. package/models/trace/insights/ThirdParties.js.map +1 -1
  199. package/models/trace/insights/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
  200. package/models/trace/insights/insights-tsconfig.json +9 -4
  201. package/models/trace/insights/types.d.ts +10 -0
  202. package/models/trace/insights/types.js.map +1 -1
  203. package/models/trace/lantern/core/core-tsconfig.json +3 -3
  204. package/models/trace/lantern/core/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
  205. package/models/trace/lantern/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
  206. package/models/trace/lantern/graph/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
  207. package/models/trace/lantern/graph/graph-tsconfig.json +3 -3
  208. package/models/trace/lantern/lantern-tsconfig.json +3 -3
  209. package/models/trace/lantern/metrics/Metric.d.ts +4 -4
  210. package/models/trace/lantern/metrics/Metric.js +4 -6
  211. package/models/trace/lantern/metrics/Metric.js.map +1 -1
  212. package/models/trace/lantern/metrics/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
  213. package/models/trace/lantern/metrics/metrics-tsconfig.json +3 -3
  214. package/models/trace/lantern/simulation/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
  215. package/models/trace/lantern/simulation/simulation-tsconfig.json +3 -3
  216. package/models/trace/lantern/types/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
  217. package/models/trace/lantern/types/types-tsconfig.json +3 -3
  218. package/models/trace/trace-tsconfig.json +3 -3
  219. package/models/trace/types/Configuration.d.ts +2 -0
  220. package/models/trace/types/Configuration.js.map +1 -1
  221. package/models/trace/types/TraceEvents.d.ts +30 -54
  222. package/models/trace/types/TraceEvents.js +8 -23
  223. package/models/trace/types/TraceEvents.js.map +1 -1
  224. package/models/trace/types/devtools_entrypoint-bundle-typescript-tsconfig.json +3 -3
  225. package/models/trace/types/types-tsconfig.json +3 -3
  226. package/package.json +3 -2
  227. package/test/test-trace-engine.mjs +19 -17
  228. package/third_party/legacy-javascript/legacy-javascript.d.ts +1 -0
  229. package/third_party/legacy-javascript/legacy-javascript.js +1 -0
  230. package/core/platform/ServerTiming.d.ts +0 -31
  231. package/core/platform/ServerTiming.js +0 -212
  232. package/core/platform/ServerTiming.js.map +0 -1
  233. package/models/trace/handlers/ServerTimingsHandler.d.ts +0 -9
  234. package/models/trace/handlers/ServerTimingsHandler.js +0 -106
  235. package/models/trace/handlers/ServerTimingsHandler.js.map +0 -1
  236. 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 computeLastGeneratedColumnMap(map) {
115
- const result = new Map();
116
- const mappings = map.mappings();
117
- for (let i = 0; i < mappings.length - 1; i++) {
118
- const mapping = mappings[i];
119
- const nextMapping = mappings[i + 1];
120
- if (mapping.lineNumber === nextMapping.lineNumber) {
121
- result.set(mapping, nextMapping.columnNumber);
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
- // Now, all but the last mapping on each line will have 'lastColumnNumber' set to a number.
125
- return result;
95
+ return groupedDuplication;
126
96
  }
127
97
  /**
128
- * Returns a @see ScriptDuplication for the given collection of script contents + source maps.
98
+ * Sort by estimated savings.
129
99
  */
130
- export function computeScriptDuplication(scriptsData) {
131
- const sizesMap = new Map();
132
- for (const script of scriptsData.scripts) {
133
- if (script.content && script.sourceMap) {
134
- sizesMap.set(script, computeGeneratedFileSizes(script));
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 [script, sizes] of sizesMap) {
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: sourceData.resourceSize,
151
+ attributedSize: transferSize,
172
152
  });
173
153
  }
174
154
  }
155
+ const duplicationGroupedByNodeModules = groupByNodeModules(duplication);
175
156
  normalizeDuplication(duplication);
176
- // Sort by estimated savings.
177
- return new Map([...duplication].sort((a, b) => b[1].estimatedDuplicateBytes - a[1].estimatedDuplicateBytes));
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.Events.isProfileCall(event)) {
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.isUserTiming(event)) {
33
- result = getForUserTiming(event, parsedTrace);
30
+ else if (Types.Events.isPerformanceMeasureBegin(event)) {
31
+ result = getForPerformanceMeasure(event, parsedTrace);
34
32
  }
35
- else if (Types.Events.isLayout(event) || Types.Events.isUpdateLayoutTree(event)) {
36
- const node = parsedTrace.Renderer.entryToNode.get(event);
37
- const parent = node?.parent?.entry;
38
- if (parent) {
39
- result = get(parent, parsedTrace);
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
- function getForProfileCall(event, parsedTrace) {
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 = event;
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
- node = node.parent;
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
- // The Protocol.Runtime.StackTrace type is recursive, so we
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
- return getForUserTiming(event.rawSourceEvent, parsedTrace);
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
- * Finds the JS call in which the user timing API was called and returns
115
- * its stack trace.
144
+ * Gets the raw event for a user timing and obtains its stack trace.
116
145
  */
117
- function getForUserTiming(event, parsedTrace) {
146
+ function getForPerformanceMeasure(event, parsedTrace) {
118
147
  let rawEvent = event;
119
- if (Types.Events.isPerformanceMeasureBegin(event)) {
120
- if (event.args.traceId === undefined) {
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
- // Look for the nearest profile call ancestor of the event tracing
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":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAK7B,OAAO,KAAK,KAAK,MAAM,mBAAmB,CAAC;AAE3C,MAAM,CAAC,MAAM,yBAAyB,GAClC,IAAI,GAAG,EAAoF,CAAC;AAEhG,MAAM,UAAU,kBAAkB,CAAC,WAAuC;IACxE,yBAAyB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AAChD,CAAC;AACD;;;;;GAKG;AACH,MAAM,UAAU,GAAG,CAAC,KAAyB,EAAE,WAAuC;IAEpF,IAAI,aAAa,GAAG,yBAAyB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC/D,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,aAAa,GAAG,IAAI,GAAG,EAAE,CAAC;QAC1B,yBAAyB,CAAC,GAAG,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,eAAe,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACjD,IAAI,eAAe,EAAE,CAAC;QACpB,OAAO,eAAe,CAAC;IACzB,CAAC;IACD,IAAI,MAAM,GAAqC,IAAI,CAAC;IACpD,IAAI,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,GAAG,iBAAiB,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IACjD,CAAC;SAAM,IAAI,KAAK,CAAC,UAAU,CAAC,yBAAyB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7D,MAAM,GAAG,oBAAoB,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IACpD,CAAC;SAAM,IAAI,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C,MAAM,GAAG,gBAAgB,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IAChD,CAAC;SAAM,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;QAClF,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC;QACnC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IACD,IAAI,MAAM,EAAE,CAAC;QACX,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,iBAAiB,CACtB,KAAwC,EAAE,WAAuC;IACnF,kEAAkE;IAClE,uBAAuB;IACvB,MAAM,WAAW,GACb,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,WAAW,CAAC;IACnH,MAAM,aAAa,GAAgC,EAAC,UAAU,EAAE,EAAE,EAAC,CAAC;IACpE,IAAI,UAAU,GAAgC,aAAa,CAAC;IAC5D,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,IAAI,IAAI,GAAsD,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACrF,MAAM,UAAU,GACZ,yBAAyB,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,IAAI,GAAG,EAAmD,CAAC;IAC7G,yBAAyB,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACvD,4DAA4D;IAC5D,eAAe;IACf,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5C,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC;YACnB,SAAS;QACX,CAAC;QAED,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC;QAC1B,kDAAkD;QAClD,MAAM,mBAAmB,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvD,IAAI,mBAAmB,EAAE,CAAC;YACxB,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAClH,UAAU,CAAC,MAAM,GAAG,mBAAmB,CAAC,MAAM,CAAC;YAC/C,2DAA2D;YAC3D,8DAA8D;YAC9D,gEAAgE;YAChE,6DAA6D;YAC7D,uDAAuD;YACvD,4DAA4D;YAC5D,UAAU;YACV,UAAU,CAAC,WAAW,GAAG,UAAU,CAAC,WAAW,IAAI,mBAAmB,CAAC,WAAW,CAAC;YACnF,MAAM;QACR,CAAC;QAED,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;YAChD,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;QACrD,CAAC;QACD,MAAM,qBAAqB,GAAG,WAAW,CAAC,YAAY,CAAC,oBAAoB,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC9F,MAAM,oBAAoB,GAAG,qBAAqB,IAAI,WAAW,CAAC,GAAG,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC;QACvG,IAAI,oBAAoB,EAAE,CAAC;YACzB,2DAA2D;YAC3D,+DAA+D;YAC/D,UAAU,CAAC,MAAM,GAAG,EAAC,UAAU,EAAE,EAAE,EAAC,CAAC;YACrC,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC;YAC/B,6DAA6D;YAC7D,4DAA4D;YAC5D,6DAA6D;YAC7D,2DAA2D;YAC3D,6DAA6D;YAC7D,0DAA0D;YAC1D,2DAA2D;YAC3D,UAAU,CAAC,WAAW,GAAG,qBAAqB,CAAC,QAAQ,CAAC;YACxD,IAAI,GAAG,oBAAoB,CAAC;YAC5B,SAAS;QACX,CAAC;QACD,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC;AAED;;;;GAIG;AACH,SAAS,oBAAoB,CAAC,KAA+C,EAAE,WAAuC;IAEpH,OAAO,gBAAgB,CAAC,KAAK,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;AAC7D,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CACrB,KAA4D,EAC5D,WAAuC;IACzC,IAAI,QAAQ,GAAiC,KAAK,CAAC;IACnD,IAAI,KAAK,CAAC,MAAM,CAAC,yBAAyB,CAAC,KAAK,CAAC,EAAE,CAAC;QAClD,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACrC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,QAAQ,GAAG,WAAW,CAAC,WAAW,CAAC,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnF,CAAC;IACD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC;IACD,kEAAkE;IAClE,uBAAuB;IACvB,IAAI,IAAI,GAAsD,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7G,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACvD,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IACD,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACnD,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AACD;;;;;;;;GAQG;AACH,SAAS,kBAAkB,CAAC,EAAC,YAAY,EAAE,UAAU,EAAE,GAAG,EAAE,QAAQ,EAA6B;IAC/F,OAAO,UAAU,KAAK,CAAC,CAAC,IAAI,YAAY,KAAK,CAAC,CAAC,IAAI,GAAG,KAAK,EAAE,IAAI,QAAQ,KAAK,GAAG,CAAC;AACpF,CAAC","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
- export interface Summary {
5
+ interface BaseSummary {
6
+ entity: Entity;
6
7
  transferSize: number;
7
- mainThreadTime: Types.Timing.Micro;
8
+ mainThreadTime: Types.Timing.Milli;
8
9
  }
9
- export interface ThirdPartySummary {
10
- byEntity: Map<Entity, Summary>;
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
- * @param networkRequests Won't be filtered by trace bounds, so callers should ensure it is filtered.
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 getSummariesAndEntitiesWithMapping(parsedTrace: Handlers.Types.ParsedTrace, traceBounds: Types.Timing.TraceWindowMicro, entityMapping: Handlers.Helpers.EntityMappings): {
26
- summaries: ThirdPartySummary;
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 {};