@paulirish/trace_engine 0.0.10 → 0.0.11

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 (205) hide show
  1. package/analyze-trace.mjs +1 -1
  2. package/core/platform/DevToolsPath.d.ts +4 -13
  3. package/core/platform/DevToolsPath.js +7 -4
  4. package/core/platform/DevToolsPath.js.map +1 -7
  5. package/core/platform/MimeType.d.ts +27 -0
  6. package/core/platform/MimeType.js +119 -86
  7. package/core/platform/MimeType.js.map +1 -7
  8. package/core/platform/Timing.d.ts +7 -0
  9. package/core/platform/Timing.js +7 -4
  10. package/core/platform/Timing.js.map +1 -7
  11. package/core/platform/UIString.d.ts +2 -5
  12. package/core/platform/UIString.js +5 -2
  13. package/core/platform/UIString.js.map +1 -7
  14. package/core/platform/UserVisibleError.js +19 -10
  15. package/core/platform/UserVisibleError.js.map +1 -7
  16. package/core/platform/array-utilities.d.ts +48 -10
  17. package/core/platform/array-utilities.js +160 -124
  18. package/core/platform/array-utilities.js.map +1 -7
  19. package/core/platform/brand.d.ts +14 -0
  20. package/core/platform/brand.js +5 -1
  21. package/core/platform/brand.js.map +1 -7
  22. package/core/platform/date-utilities.js +10 -6
  23. package/core/platform/date-utilities.js.map +1 -7
  24. package/core/platform/dom-utilities.d.ts +3 -1
  25. package/core/platform/dom-utilities.js +94 -83
  26. package/core/platform/dom-utilities.js.map +1 -7
  27. package/core/platform/keyboard-utilities.d.ts +2 -0
  28. package/core/platform/keyboard-utilities.js +15 -24
  29. package/core/platform/keyboard-utilities.js.map +1 -7
  30. package/core/platform/map-utilities.d.ts +4 -0
  31. package/core/platform/map-utilities.js +66 -60
  32. package/core/platform/map-utilities.js.map +1 -7
  33. package/core/platform/number-utilities.js +66 -55
  34. package/core/platform/number-utilities.js.map +1 -7
  35. package/core/platform/platform.d.ts +5 -1
  36. package/core/platform/platform.js +54 -37
  37. package/core/platform/platform.js.map +1 -7
  38. package/core/platform/promise-utilities.d.ts +10 -0
  39. package/core/platform/promise-utilities.js +16 -8
  40. package/core/platform/promise-utilities.js.map +1 -7
  41. package/core/platform/set-utilities.js +20 -17
  42. package/core/platform/set-utilities.js.map +1 -7
  43. package/core/platform/string-utilities.d.ts +32 -1
  44. package/core/platform/string-utilities.js +453 -379
  45. package/core/platform/string-utilities.js.map +1 -7
  46. package/core/platform/typescript-utilities.d.ts +5 -5
  47. package/core/platform/typescript-utilities.js +19 -7
  48. package/core/platform/typescript-utilities.js.map +1 -7
  49. package/generated/protocol.d.ts +2081 -347
  50. package/generated/protocol.js +5 -2230
  51. package/models/cpu_profile/CPUProfileDataModel.d.ts +77 -0
  52. package/models/cpu_profile/CPUProfileDataModel.js +492 -359
  53. package/models/cpu_profile/CPUProfileDataModel.js.map +1 -7
  54. package/models/cpu_profile/ProfileTreeModel.d.ts +29 -0
  55. package/models/cpu_profile/ProfileTreeModel.js +87 -82
  56. package/models/cpu_profile/ProfileTreeModel.js.map +1 -7
  57. package/models/cpu_profile/cpu_profile.d.ts +3 -0
  58. package/models/cpu_profile/cpu_profile.js +7 -7
  59. package/models/cpu_profile/cpu_profile.js.map +1 -7
  60. package/models/trace/EntriesFilter.d.ts +55 -0
  61. package/models/trace/EntriesFilter.js +227 -166
  62. package/models/trace/EntriesFilter.js.map +1 -7
  63. package/models/trace/LegacyTracingModel.js.map +1 -7
  64. package/models/trace/ModelImpl.d.ts +110 -0
  65. package/models/trace/ModelImpl.js +161 -102
  66. package/models/trace/ModelImpl.js.map +1 -7
  67. package/models/trace/Processor.d.ts +36 -0
  68. package/models/trace/Processor.js +197 -163
  69. package/models/trace/Processor.js.map +1 -7
  70. package/models/trace/TracingManager.js.map +1 -7
  71. package/models/trace/extras/FetchNodes.d.ts +46 -0
  72. package/models/trace/extras/FetchNodes.js +132 -91
  73. package/models/trace/extras/FetchNodes.js.map +1 -7
  74. package/models/trace/extras/FilmStrip.d.ts +19 -0
  75. package/models/trace/extras/FilmStrip.js +38 -31
  76. package/models/trace/extras/FilmStrip.js.map +1 -7
  77. package/models/trace/extras/MainThreadActivity.d.ts +2 -0
  78. package/models/trace/extras/MainThreadActivity.js +72 -56
  79. package/models/trace/extras/MainThreadActivity.js.map +1 -7
  80. package/models/trace/extras/Metadata.d.ts +2 -0
  81. package/models/trace/extras/Metadata.js +42 -26
  82. package/models/trace/extras/Metadata.js.map +1 -7
  83. package/models/trace/extras/extras.js.map +1 -7
  84. package/models/trace/handlers/AnimationHandler.d.ts +8 -0
  85. package/models/trace/handlers/AnimationHandler.js +22 -20
  86. package/models/trace/handlers/AnimationHandler.js.map +1 -7
  87. package/models/trace/handlers/AuctionWorkletsHandler.d.ts +8 -0
  88. package/models/trace/handlers/AuctionWorkletsHandler.js +143 -89
  89. package/models/trace/handlers/AuctionWorkletsHandler.js.map +1 -7
  90. package/models/trace/handlers/FramesHandler.d.ts +76 -0
  91. package/models/trace/handlers/FramesHandler.js +424 -355
  92. package/models/trace/handlers/FramesHandler.js.map +1 -7
  93. package/models/trace/handlers/GPUHandler.d.ts +11 -0
  94. package/models/trace/handlers/GPUHandler.js +41 -37
  95. package/models/trace/handlers/GPUHandler.js.map +1 -7
  96. package/models/trace/handlers/InitiatorsHandler.d.ts +10 -0
  97. package/models/trace/handlers/InitiatorsHandler.js +164 -113
  98. package/models/trace/handlers/InitiatorsHandler.js.map +1 -7
  99. package/models/trace/handlers/InvalidationsHandler.d.ts +10 -0
  100. package/models/trace/handlers/InvalidationsHandler.js +101 -79
  101. package/models/trace/handlers/InvalidationsHandler.js.map +1 -7
  102. package/models/trace/handlers/LargestImagePaintHandler.d.ts +5 -0
  103. package/models/trace/handlers/LargestImagePaintHandler.js +32 -12
  104. package/models/trace/handlers/LargestImagePaintHandler.js.map +1 -7
  105. package/models/trace/handlers/LargestTextPaintHandler.d.ts +5 -0
  106. package/models/trace/handlers/LargestTextPaintHandler.js +20 -12
  107. package/models/trace/handlers/LargestTextPaintHandler.js.map +1 -7
  108. package/models/trace/handlers/LayerTreeHandler.d.ts +13 -0
  109. package/models/trace/handlers/LayerTreeHandler.js +96 -70
  110. package/models/trace/handlers/LayerTreeHandler.js.map +1 -7
  111. package/models/trace/handlers/LayoutShiftsHandler.d.ts +44 -0
  112. package/models/trace/handlers/LayoutShiftsHandler.js +304 -227
  113. package/models/trace/handlers/LayoutShiftsHandler.js.map +1 -7
  114. package/models/trace/handlers/MemoryHandler.d.ts +7 -0
  115. package/models/trace/handlers/MemoryHandler.js +14 -11
  116. package/models/trace/handlers/MemoryHandler.js.map +1 -7
  117. package/models/trace/handlers/MetaHandler.d.ts +37 -0
  118. package/models/trace/handlers/MetaHandler.js +314 -226
  119. package/models/trace/handlers/MetaHandler.js.map +1 -7
  120. package/models/trace/handlers/ModelHandlers.d.ts +21 -0
  121. package/models/trace/handlers/ModelHandlers.js +25 -22
  122. package/models/trace/handlers/ModelHandlers.js.map +1 -7
  123. package/models/trace/handlers/NetworkRequestsHandler.d.ts +17 -0
  124. package/models/trace/handlers/NetworkRequestsHandler.js +342 -218
  125. package/models/trace/handlers/NetworkRequestsHandler.js.map +1 -7
  126. package/models/trace/handlers/PageLoadMetricsHandler.d.ts +67 -0
  127. package/models/trace/handlers/PageLoadMetricsHandler.js +357 -284
  128. package/models/trace/handlers/PageLoadMetricsHandler.js.map +1 -7
  129. package/models/trace/handlers/RendererHandler.d.ts +101 -0
  130. package/models/trace/handlers/RendererHandler.js +295 -191
  131. package/models/trace/handlers/RendererHandler.js.map +1 -7
  132. package/models/trace/handlers/SamplesHandler.d.ts +46 -0
  133. package/models/trace/handlers/SamplesHandler.js +195 -158
  134. package/models/trace/handlers/SamplesHandler.js.map +1 -7
  135. package/models/trace/handlers/ScreenshotsHandler.d.ts +7 -0
  136. package/models/trace/handlers/ScreenshotsHandler.js +63 -41
  137. package/models/trace/handlers/ScreenshotsHandler.js.map +1 -7
  138. package/models/trace/handlers/Threads.d.ts +33 -0
  139. package/models/trace/handlers/Threads.js +85 -67
  140. package/models/trace/handlers/Threads.js.map +1 -7
  141. package/models/trace/handlers/UserInteractionsHandler.d.ts +57 -0
  142. package/models/trace/handlers/UserInteractionsHandler.js +240 -141
  143. package/models/trace/handlers/UserInteractionsHandler.js.map +1 -7
  144. package/models/trace/handlers/UserTimingsHandler.d.ts +28 -0
  145. package/models/trace/handlers/UserTimingsHandler.js +91 -80
  146. package/models/trace/handlers/UserTimingsHandler.js.map +1 -7
  147. package/models/trace/handlers/WarningsHandler.d.ts +14 -0
  148. package/models/trace/handlers/WarningsHandler.js +100 -62
  149. package/models/trace/handlers/WarningsHandler.js.map +1 -7
  150. package/models/trace/handlers/WorkersHandler.d.ts +11 -0
  151. package/models/trace/handlers/WorkersHandler.js +40 -38
  152. package/models/trace/handlers/WorkersHandler.js.map +1 -7
  153. package/models/trace/handlers/handlers.d.ts +3 -0
  154. package/models/trace/handlers/handlers.js +7 -4
  155. package/models/trace/handlers/handlers.js.map +1 -7
  156. package/models/trace/handlers/types.d.ts +45 -0
  157. package/models/trace/handlers/types.js +15 -15
  158. package/models/trace/handlers/types.js.map +1 -7
  159. package/models/trace/helpers/SamplesIntegrator.d.ts +49 -0
  160. package/models/trace/helpers/SamplesIntegrator.js +381 -204
  161. package/models/trace/helpers/SamplesIntegrator.js.map +1 -7
  162. package/models/trace/helpers/Timing.d.ts +26 -0
  163. package/models/trace/helpers/Timing.js +131 -110
  164. package/models/trace/helpers/Timing.js.map +1 -7
  165. package/models/trace/helpers/Trace.d.ts +37 -0
  166. package/models/trace/helpers/Trace.js +200 -166
  167. package/models/trace/helpers/Trace.js.map +1 -7
  168. package/models/trace/helpers/TreeHelpers.d.ts +90 -0
  169. package/models/trace/helpers/TreeHelpers.js +203 -100
  170. package/models/trace/helpers/TreeHelpers.js.map +1 -7
  171. package/models/trace/helpers/helpers.d.ts +4 -0
  172. package/models/trace/helpers/helpers.js +8 -5
  173. package/models/trace/helpers/helpers.js.map +1 -7
  174. package/models/trace/root-causes/LayoutShift.d.ts +119 -0
  175. package/models/trace/root-causes/LayoutShift.js +470 -323
  176. package/models/trace/root-causes/LayoutShift.js.map +1 -7
  177. package/models/trace/root-causes/RootCauses.d.ts +14 -0
  178. package/models/trace/root-causes/RootCauses.js +9 -6
  179. package/models/trace/root-causes/RootCauses.js.map +1 -7
  180. package/models/trace/root-causes/root-causes.d.ts +1 -0
  181. package/models/trace/root-causes/root-causes.js +5 -2
  182. package/models/trace/root-causes/root-causes.js.map +1 -7
  183. package/models/trace/trace.d.ts +11 -0
  184. package/models/trace/trace.js +17 -23
  185. package/models/trace/trace.js.map +1 -7
  186. package/models/trace/types/Configuration.d.ts +33 -0
  187. package/models/trace/types/Configuration.js +25 -14
  188. package/models/trace/types/Configuration.js.map +1 -7
  189. package/models/trace/types/File.d.ts +23 -0
  190. package/models/trace/types/File.js +5 -6
  191. package/models/trace/types/File.js.map +1 -7
  192. package/models/trace/types/Timing.d.ts +25 -0
  193. package/models/trace/types/Timing.js +10 -11
  194. package/models/trace/types/Timing.js.map +1 -7
  195. package/models/trace/types/TraceEvents.d.ts +1571 -0
  196. package/models/trace/types/TraceEvents.js +174 -381
  197. package/models/trace/types/TraceEvents.js.map +1 -7
  198. package/models/trace/types/types.d.ts +4 -0
  199. package/models/trace/types/types.js +8 -5
  200. package/models/trace/types/types.js.map +1 -7
  201. package/package.json +1 -1
  202. package/TracingManager.js +0 -0
  203. package/extras/extras.js +0 -0
  204. package/trace.mjs +0 -6980
  205. package/trace.mjs.map +0 -8
@@ -1,375 +1,508 @@
1
- import * as Platform from "../../core/platform/platform.js";
2
- import { ProfileNode, ProfileTreeModel } from "./ProfileTreeModel.js";
1
+ // Copyright 2014 The Chromium Authors. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+ import * as Platform from '../../core/platform/platform.js';
5
+ import { ProfileNode, ProfileTreeModel } from './ProfileTreeModel.js';
3
6
  export class CPUProfileNode extends ProfileNode {
4
- id;
5
- self;
6
- positionTicks;
7
- deoptReason;
8
- constructor(node, samplingInterval) {
9
- const callFrame = node.callFrame || {
10
- functionName: node["functionName"],
11
- scriptId: node["scriptId"],
12
- url: node["url"],
13
- lineNumber: node["lineNumber"] - 1,
14
- columnNumber: node["columnNumber"] - 1
15
- };
16
- super(callFrame);
17
- this.id = node.id;
18
- this.self = (node.hitCount || 0) * samplingInterval;
19
- this.positionTicks = node.positionTicks;
20
- this.deoptReason = node.deoptReason && node.deoptReason !== "no reason" ? node.deoptReason : null;
21
- }
7
+ id;
8
+ self;
9
+ // Position ticks are available in profile nodes coming from CDP
10
+ // profiles and not in those coming from tracing. They are used to
11
+ // calculate the line level execution time shown in the Sources panel
12
+ // after recording a profile. For trace CPU profiles we use the
13
+ // `lines` array instead.
14
+ positionTicks;
15
+ deoptReason;
16
+ constructor(node, samplingInterval /* milliseconds */) {
17
+ const callFrame = node.callFrame || {
18
+ // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
19
+ // @ts-expect-error
20
+ functionName: node['functionName'],
21
+ // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
22
+ // @ts-expect-error
23
+ scriptId: node['scriptId'],
24
+ // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
25
+ // @ts-expect-error
26
+ url: node['url'],
27
+ // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
28
+ // @ts-expect-error
29
+ lineNumber: node['lineNumber'] - 1,
30
+ // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
31
+ // @ts-expect-error
32
+ columnNumber: node['columnNumber'] - 1,
33
+ };
34
+ super(callFrame);
35
+ this.id = node.id;
36
+ this.self = (node.hitCount || 0) * samplingInterval;
37
+ this.positionTicks = node.positionTicks;
38
+ // Compatibility: legacy backends could provide "no reason" for optimized functions.
39
+ this.deoptReason = node.deoptReason && node.deoptReason !== 'no reason' ? node.deoptReason : null;
40
+ }
22
41
  }
23
42
  export class CPUProfileDataModel extends ProfileTreeModel {
24
- profileStartTime;
25
- profileEndTime;
26
- timestamps;
27
- samples;
28
- lines;
29
- totalHitCount;
30
- profileHead;
31
- #idToParsedNode;
32
- gcNode;
33
- programNode;
34
- idleNode;
35
- #stackStartTimes;
36
- #stackChildrenDuration;
37
- constructor(profile) {
38
- super();
39
- const isLegacyFormat = Boolean(profile["head"]);
40
- if (isLegacyFormat) {
41
- this.profileStartTime = profile.startTime * 1e3;
42
- this.profileEndTime = profile.endTime * 1e3;
43
- this.timestamps = profile.timestamps;
44
- this.compatibilityConversionHeadToNodes(profile);
45
- } else {
46
- this.profileStartTime = profile.startTime / 1e3;
47
- this.profileEndTime = profile.endTime / 1e3;
48
- this.timestamps = this.convertTimeDeltas(profile);
49
- }
50
- this.samples = profile.samples;
51
- this.lines = profile.lines;
52
- this.totalHitCount = 0;
53
- this.profileHead = this.translateProfileTree(profile.nodes);
54
- this.initialize(this.profileHead);
55
- this.extractMetaNodes();
56
- if (this.samples?.length) {
57
- this.sortSamples();
58
- this.normalizeTimestamps();
59
- this.fixMissingSamples();
60
- }
61
- }
62
- compatibilityConversionHeadToNodes(profile) {
63
- if (!profile.head || profile.nodes) {
64
- return;
65
- }
66
- const nodes = [];
67
- convertNodesTree(profile.head);
68
- profile.nodes = nodes;
69
- delete profile.head;
70
- function convertNodesTree(node) {
71
- nodes.push(node);
72
- node.children = node.children.map(convertNodesTree);
73
- return node.id;
74
- }
75
- }
76
- convertTimeDeltas(profile) {
77
- if (!profile.timeDeltas) {
78
- return [];
79
- }
80
- let lastTimeMicroSec = profile.startTime;
81
- const timestamps = new Array(profile.timeDeltas.length);
82
- for (let i = 0; i < profile.timeDeltas.length; ++i) {
83
- lastTimeMicroSec += profile.timeDeltas[i];
84
- timestamps[i] = lastTimeMicroSec;
85
- }
86
- return timestamps;
87
- }
88
- translateProfileTree(nodes) {
89
- function buildChildrenFromParents(nodes2) {
90
- if (nodes2[0].children) {
91
- return;
92
- }
93
- nodes2[0].children = [];
94
- for (let i = 1; i < nodes2.length; ++i) {
95
- const node = nodes2[i];
96
- const parentNode = protocolNodeById.get(node.parent);
97
- if (parentNode.children) {
98
- parentNode.children.push(node.id);
99
- } else {
100
- parentNode.children = [node.id];
101
- }
102
- }
103
- }
104
- function buildHitCountFromSamples(nodes2, samples) {
105
- if (typeof nodes2[0].hitCount === "number") {
106
- return;
107
- }
108
- if (!samples) {
109
- throw new Error("Error: Neither hitCount nor samples are present in profile.");
110
- }
111
- for (let i = 0; i < nodes2.length; ++i) {
112
- nodes2[i].hitCount = 0;
113
- }
114
- for (let i = 0; i < samples.length; ++i) {
115
- const node = protocolNodeById.get(samples[i]);
116
- if (!node || node.hitCount === void 0) {
117
- continue;
118
- }
119
- node.hitCount++;
120
- }
121
- }
122
- const protocolNodeById = /* @__PURE__ */ new Map();
123
- for (let i = 0; i < nodes.length; ++i) {
124
- const node = nodes[i];
125
- protocolNodeById.set(node.id, node);
126
- }
127
- buildHitCountFromSamples(nodes, this.samples);
128
- buildChildrenFromParents(nodes);
129
- this.totalHitCount = nodes.reduce((acc, node) => acc + (node.hitCount || 0), 0);
130
- const sampleTime = (this.profileEndTime - this.profileStartTime) / this.totalHitCount;
131
- const root = nodes[0];
132
- const idToUseForRemovedNode = /* @__PURE__ */ new Map([[root.id, root.id]]);
133
- this.#idToParsedNode = /* @__PURE__ */ new Map();
134
- const resultRoot = new CPUProfileNode(root, sampleTime);
135
- this.#idToParsedNode.set(root.id, resultRoot);
136
- if (!root.children) {
137
- throw new Error("Missing children for root");
138
- }
139
- const parentNodeStack = root.children.map(() => resultRoot);
140
- const sourceNodeStack = root.children.map((id) => protocolNodeById.get(id));
141
- while (sourceNodeStack.length) {
142
- let parentNode = parentNodeStack.pop();
143
- const sourceNode = sourceNodeStack.pop();
144
- if (!sourceNode || !parentNode) {
145
- continue;
146
- }
147
- if (!sourceNode.children) {
148
- sourceNode.children = [];
149
- }
150
- const targetNode = new CPUProfileNode(sourceNode, sampleTime);
151
- parentNode.children.push(targetNode);
152
- parentNode = targetNode;
153
- idToUseForRemovedNode.set(sourceNode.id, parentNode.id);
154
- parentNodeStack.push.apply(parentNodeStack, sourceNode.children.map(() => parentNode));
155
- sourceNodeStack.push.apply(sourceNodeStack, sourceNode.children.map((id) => protocolNodeById.get(id)));
156
- this.#idToParsedNode.set(sourceNode.id, targetNode);
157
- }
158
- if (this.samples) {
159
- this.samples = this.samples.map((id) => idToUseForRemovedNode.get(id));
160
- }
161
- return resultRoot;
162
- }
163
- sortSamples() {
164
- if (!this.timestamps || !this.samples) {
165
- return;
166
- }
167
- const timestamps = this.timestamps;
168
- const samples = this.samples;
169
- const orderedIndices = timestamps.map((_x, index) => index);
170
- orderedIndices.sort((a, b) => timestamps[a] - timestamps[b]);
171
- this.timestamps = [];
172
- this.samples = [];
173
- for (let i = 0; i < orderedIndices.length; i++) {
174
- const orderedIndex = orderedIndices[i];
175
- this.timestamps.push(timestamps[orderedIndex]);
176
- this.samples.push(samples[orderedIndex]);
177
- }
178
- }
179
- normalizeTimestamps() {
180
- if (!this.samples) {
181
- return;
182
- }
183
- let timestamps = this.timestamps;
184
- if (!timestamps) {
185
- const profileStartTime = this.profileStartTime;
186
- const interval = (this.profileEndTime - profileStartTime) / this.samples.length;
187
- timestamps = new Array(this.samples.length + 1);
188
- for (let i = 0; i < timestamps.length; ++i) {
189
- timestamps[i] = profileStartTime + i * interval;
190
- }
191
- this.timestamps = timestamps;
192
- return;
193
- }
194
- for (let i = 0; i < timestamps.length; ++i) {
195
- timestamps[i] /= 1e3;
196
- }
197
- if (this.samples.length === timestamps.length) {
198
- const lastTimestamp = timestamps.at(-1) || 0;
199
- const averageIntervalTime = (lastTimestamp - timestamps[0]) / (timestamps.length - 1);
200
- this.timestamps.push(lastTimestamp + averageIntervalTime);
201
- }
202
- this.profileStartTime = timestamps.at(0) || this.profileStartTime;
203
- this.profileEndTime = timestamps.at(-1) || this.profileEndTime;
204
- }
205
- extractMetaNodes() {
206
- const topLevelNodes = this.profileHead.children;
207
- for (let i = 0; i < topLevelNodes.length && !(this.gcNode && this.programNode && this.idleNode); i++) {
208
- const node = topLevelNodes[i];
209
- if (node.functionName === "(garbage collector)") {
210
- this.gcNode = node;
211
- } else if (node.functionName === "(program)") {
212
- this.programNode = node;
213
- } else if (node.functionName === "(idle)") {
214
- this.idleNode = node;
215
- }
216
- }
217
- }
218
- fixMissingSamples() {
219
- const samples = this.samples;
220
- if (!samples) {
221
- return;
43
+ profileStartTime;
44
+ profileEndTime;
45
+ timestamps;
46
+ samples;
47
+ lines;
48
+ totalHitCount;
49
+ profileHead;
50
+ /**
51
+ * A cache for the nodes we have parsed.
52
+ * Note: "Parsed" nodes are different from the "Protocol" nodes, the
53
+ * latter being the raw data we receive from the backend.
54
+ */
55
+ #idToParsedNode;
56
+ gcNode;
57
+ programNode;
58
+ idleNode;
59
+ #stackStartTimes;
60
+ #stackChildrenDuration;
61
+ constructor(profile) {
62
+ super();
63
+ // @ts-ignore Legacy types
64
+ const isLegacyFormat = Boolean(profile['head']);
65
+ if (isLegacyFormat) {
66
+ // Legacy format contains raw timestamps and start/stop times are in seconds.
67
+ this.profileStartTime = profile.startTime * 1000;
68
+ this.profileEndTime = profile.endTime * 1000;
69
+ // @ts-ignore Legacy types
70
+ this.timestamps = profile.timestamps;
71
+ this.compatibilityConversionHeadToNodes(profile);
72
+ }
73
+ else {
74
+ // Current format encodes timestamps as deltas. Start/stop times are in microseconds.
75
+ this.profileStartTime = profile.startTime / 1000;
76
+ this.profileEndTime = profile.endTime / 1000;
77
+ this.timestamps = this.convertTimeDeltas(profile);
78
+ }
79
+ this.samples = profile.samples;
80
+ // Lines are available only in profiles coming from tracing.
81
+ // Elements in the lines array have a 1 to 1 correspondance with
82
+ // samples, by array position. They can be 1 or 0 and indicate if
83
+ // there is line data for a given sample, i.e. if a given sample
84
+ // needs to be included to calculate the line level execution time
85
+ // data, which we show in the sources panel after recording a
86
+ // profile.
87
+ this.lines = profile.lines;
88
+ this.totalHitCount = 0;
89
+ this.profileHead = this.translateProfileTree(profile.nodes);
90
+ this.initialize(this.profileHead);
91
+ this.extractMetaNodes();
92
+ if (this.samples?.length) {
93
+ this.sortSamples();
94
+ this.normalizeTimestamps();
95
+ this.fixMissingSamples();
96
+ }
222
97
  }
223
- const samplesCount = samples.length;
224
- if (!this.programNode || samplesCount < 3) {
225
- return;
98
+ compatibilityConversionHeadToNodes(profile) {
99
+ // @ts-ignore Legacy types
100
+ if (!profile.head || profile.nodes) {
101
+ return;
102
+ }
103
+ const nodes = [];
104
+ // @ts-ignore Legacy types
105
+ convertNodesTree(profile.head);
106
+ profile.nodes = nodes;
107
+ // @ts-ignore Legacy types
108
+ delete profile.head;
109
+ function convertNodesTree(node) {
110
+ nodes.push(node);
111
+ // @ts-ignore Legacy types
112
+ node.children = node.children.map(convertNodesTree);
113
+ return node.id;
114
+ }
226
115
  }
227
- const idToNode = this.#idToParsedNode;
228
- const programNodeId = this.programNode.id;
229
- const gcNodeId = this.gcNode ? this.gcNode.id : -1;
230
- const idleNodeId = this.idleNode ? this.idleNode.id : -1;
231
- let prevNodeId = samples[0];
232
- let nodeId = samples[1];
233
- for (let sampleIndex = 1; sampleIndex < samplesCount - 1; sampleIndex++) {
234
- const nextNodeId = samples[sampleIndex + 1];
235
- const prevNode = idToNode.get(prevNodeId);
236
- const nextNode = idToNode.get(nextNodeId);
237
- if (prevNodeId === void 0 || nextNodeId === void 0 || !prevNode || !nextNode) {
238
- console.error(`Unexpectedly found undefined nodes: ${prevNodeId} ${nextNodeId}`);
239
- continue;
240
- }
241
- if (nodeId === programNodeId && !isSystemNode(prevNodeId) && !isSystemNode(nextNodeId) && bottomNode(prevNode) === bottomNode(nextNode)) {
242
- samples[sampleIndex] = prevNodeId;
243
- }
244
- prevNodeId = nodeId;
245
- nodeId = nextNodeId;
116
+ /**
117
+ * Calculate timestamps using timeDeltas. Some CPU profile formats,
118
+ * like the ones contained in traces have timeDeltas instead of
119
+ * timestamps.
120
+ */
121
+ convertTimeDeltas(profile) {
122
+ if (!profile.timeDeltas) {
123
+ return [];
124
+ }
125
+ let lastTimeMicroSec = profile.startTime;
126
+ const timestamps = new Array(profile.timeDeltas.length);
127
+ for (let i = 0; i < profile.timeDeltas.length; ++i) {
128
+ lastTimeMicroSec += profile.timeDeltas[i];
129
+ timestamps[i] = lastTimeMicroSec;
130
+ }
131
+ return timestamps;
246
132
  }
247
- function bottomNode(node) {
248
- while (node.parent && node.parent.parent) {
249
- node = node.parent;
250
- }
251
- return node;
133
+ /**
134
+ * Creates a Tree of CPUProfileNodes using the Protocol.Profiler.ProfileNodes.
135
+ * As the tree is built, samples of native code (prefixed with "native ") are
136
+ * filtered out. Samples of filtered nodes are replaced with the parent of the
137
+ * node being filtered.
138
+ *
139
+ * This function supports legacy and new definitions of the CDP Profiler.Profile
140
+ * type.
141
+ */
142
+ translateProfileTree(nodes) {
143
+ function buildChildrenFromParents(nodes) {
144
+ if (nodes[0].children) {
145
+ return;
146
+ }
147
+ nodes[0].children = [];
148
+ for (let i = 1; i < nodes.length; ++i) {
149
+ const node = nodes[i];
150
+ // @ts-ignore Legacy types
151
+ const parentNode = protocolNodeById.get(node.parent);
152
+ // @ts-ignore Legacy types
153
+ if (parentNode.children) {
154
+ // @ts-ignore Legacy types
155
+ parentNode.children.push(node.id);
156
+ }
157
+ else {
158
+ // @ts-ignore Legacy types
159
+ parentNode.children = [node.id];
160
+ }
161
+ }
162
+ }
163
+ /**
164
+ * Calculate how many times each node was sampled in the profile, if
165
+ * not available in the profile data.
166
+ */
167
+ function buildHitCountFromSamples(nodes, samples) {
168
+ // If hit count is available, this profile has the new format, so
169
+ // no need to continue.`
170
+ if (typeof (nodes[0].hitCount) === 'number') {
171
+ return;
172
+ }
173
+ if (!samples) {
174
+ throw new Error('Error: Neither hitCount nor samples are present in profile.');
175
+ }
176
+ for (let i = 0; i < nodes.length; ++i) {
177
+ nodes[i].hitCount = 0;
178
+ }
179
+ for (let i = 0; i < samples.length; ++i) {
180
+ const node = protocolNodeById.get(samples[i]);
181
+ if (!node || node.hitCount === undefined) {
182
+ continue;
183
+ }
184
+ node.hitCount++;
185
+ }
186
+ }
187
+ // A cache for the raw nodes received from the traces / CDP.
188
+ const protocolNodeById = new Map();
189
+ for (let i = 0; i < nodes.length; ++i) {
190
+ const node = nodes[i];
191
+ protocolNodeById.set(node.id, node);
192
+ }
193
+ buildHitCountFromSamples(nodes, this.samples);
194
+ buildChildrenFromParents(nodes);
195
+ this.totalHitCount = nodes.reduce((acc, node) => acc + (node.hitCount || 0), 0);
196
+ const sampleTime = (this.profileEndTime - this.profileStartTime) / this.totalHitCount;
197
+ const root = nodes[0];
198
+ // If a node is filtered out, its samples are replaced with its parent,
199
+ // so we keep track of the which id to use in the samples data.
200
+ const idToUseForRemovedNode = new Map([[root.id, root.id]]);
201
+ this.#idToParsedNode = new Map();
202
+ const resultRoot = new CPUProfileNode(root, sampleTime);
203
+ this.#idToParsedNode.set(root.id, resultRoot);
204
+ if (!root.children) {
205
+ throw new Error('Missing children for root');
206
+ }
207
+ const parentNodeStack = root.children.map(() => resultRoot);
208
+ const sourceNodeStack = root.children.map(id => protocolNodeById.get(id));
209
+ while (sourceNodeStack.length) {
210
+ let parentNode = parentNodeStack.pop();
211
+ const sourceNode = sourceNodeStack.pop();
212
+ if (!sourceNode || !parentNode) {
213
+ continue;
214
+ }
215
+ if (!sourceNode.children) {
216
+ sourceNode.children = [];
217
+ }
218
+ const targetNode = new CPUProfileNode(sourceNode, sampleTime);
219
+ parentNode.children.push(targetNode);
220
+ parentNode = targetNode;
221
+ idToUseForRemovedNode.set(sourceNode.id, parentNode.id);
222
+ parentNodeStack.push.apply(parentNodeStack, sourceNode.children.map(() => parentNode));
223
+ sourceNodeStack.push.apply(sourceNodeStack, sourceNode.children.map(id => protocolNodeById.get(id)));
224
+ this.#idToParsedNode.set(sourceNode.id, targetNode);
225
+ }
226
+ if (this.samples) {
227
+ this.samples = this.samples.map(id => idToUseForRemovedNode.get(id));
228
+ }
229
+ return resultRoot;
252
230
  }
253
- function isSystemNode(nodeId2) {
254
- return nodeId2 === programNodeId || nodeId2 === gcNodeId || nodeId2 === idleNodeId;
231
+ /**
232
+ * Sorts the samples array using the timestamps array (there is a one
233
+ * to one matching by index between the two).
234
+ */
235
+ sortSamples() {
236
+ if (!this.timestamps || !this.samples) {
237
+ return;
238
+ }
239
+ const timestamps = this.timestamps;
240
+ const samples = this.samples;
241
+ const orderedIndices = timestamps.map((_x, index) => index);
242
+ orderedIndices.sort((a, b) => timestamps[a] - timestamps[b]);
243
+ this.timestamps = [];
244
+ this.samples = [];
245
+ for (let i = 0; i < orderedIndices.length; i++) {
246
+ const orderedIndex = orderedIndices[i];
247
+ this.timestamps.push(timestamps[orderedIndex]);
248
+ this.samples.push(samples[orderedIndex]);
249
+ }
255
250
  }
256
- }
257
- forEachFrame(openFrameCallback, closeFrameCallback, startTime, stopTime) {
258
- if (!this.profileHead || !this.samples) {
259
- return;
251
+ /**
252
+ * Fills in timestamps and/or time deltas from legacy profiles where
253
+ * they could be missing.
254
+ */
255
+ normalizeTimestamps() {
256
+ if (!this.samples) {
257
+ return;
258
+ }
259
+ let timestamps = this.timestamps;
260
+ if (!timestamps) {
261
+ // Support loading CPU profiles that are missing timestamps and
262
+ // timedeltas
263
+ const profileStartTime = this.profileStartTime;
264
+ const interval = (this.profileEndTime - profileStartTime) / this.samples.length;
265
+ // Add an extra timestamp used to calculate the last sample duration.
266
+ timestamps = new Array(this.samples.length + 1);
267
+ for (let i = 0; i < timestamps.length; ++i) {
268
+ timestamps[i] = profileStartTime + i * interval;
269
+ }
270
+ this.timestamps = timestamps;
271
+ return;
272
+ }
273
+ // Convert samples from micro to milliseconds
274
+ for (let i = 0; i < timestamps.length; ++i) {
275
+ timestamps[i] /= 1000;
276
+ }
277
+ if (this.samples.length === timestamps.length) {
278
+ // Add an extra timestamp used to calculate the last sample duration.
279
+ const lastTimestamp = timestamps.at(-1) || 0;
280
+ const averageIntervalTime = (lastTimestamp - timestamps[0]) / (timestamps.length - 1);
281
+ this.timestamps.push(lastTimestamp + averageIntervalTime);
282
+ }
283
+ this.profileStartTime = timestamps.at(0) || this.profileStartTime;
284
+ this.profileEndTime = timestamps.at(-1) || this.profileEndTime;
260
285
  }
261
- startTime = startTime || 0;
262
- stopTime = stopTime || Infinity;
263
- const samples = this.samples;
264
- const timestamps = this.timestamps;
265
- const idToNode = this.#idToParsedNode;
266
- const gcNode = this.gcNode;
267
- const samplesCount = samples.length;
268
- const startIndex = Platform.ArrayUtilities.lowerBound(timestamps, startTime, Platform.ArrayUtilities.DEFAULT_COMPARATOR);
269
- let stackTop = 0;
270
- const stackNodes = [];
271
- let prevId = this.profileHead.id;
272
- let sampleTime;
273
- let gcParentNode = null;
274
- const stackDepth = this.maxDepth + 3;
275
- if (!this.#stackStartTimes) {
276
- this.#stackStartTimes = new Array(stackDepth);
286
+ /**
287
+ * Some nodes do not refer to JS samples but to V8 system tasks, AKA
288
+ * "meta" nodes. This function extracts those nodes from the profile.
289
+ */
290
+ extractMetaNodes() {
291
+ const topLevelNodes = this.profileHead.children;
292
+ for (let i = 0; i < topLevelNodes.length && !(this.gcNode && this.programNode && this.idleNode); i++) {
293
+ const node = topLevelNodes[i];
294
+ if (node.functionName === '(garbage collector)') {
295
+ this.gcNode = node;
296
+ }
297
+ else if (node.functionName === '(program)') {
298
+ this.programNode = node;
299
+ }
300
+ else if (node.functionName === '(idle)') {
301
+ this.idleNode = node;
302
+ }
303
+ }
277
304
  }
278
- const stackStartTimes = this.#stackStartTimes;
279
- if (!this.#stackChildrenDuration) {
280
- this.#stackChildrenDuration = new Array(stackDepth);
305
+ fixMissingSamples() {
306
+ // Sometimes the V8 sampler is not able to parse the JS stack and returns
307
+ // a (program) sample instead. The issue leads to call frames being split
308
+ // apart when they shouldn't.
309
+ // Here's a workaround for that. When there's a single (program) sample
310
+ // between two call stacks sharing the same bottom node, it is replaced
311
+ // with the preceeding sample.
312
+ const samples = this.samples;
313
+ if (!samples) {
314
+ return;
315
+ }
316
+ const samplesCount = samples.length;
317
+ if (!this.programNode || samplesCount < 3) {
318
+ return;
319
+ }
320
+ const idToNode = this.#idToParsedNode;
321
+ const programNodeId = this.programNode.id;
322
+ const gcNodeId = this.gcNode ? this.gcNode.id : -1;
323
+ const idleNodeId = this.idleNode ? this.idleNode.id : -1;
324
+ let prevNodeId = samples[0];
325
+ let nodeId = samples[1];
326
+ for (let sampleIndex = 1; sampleIndex < samplesCount - 1; sampleIndex++) {
327
+ const nextNodeId = samples[sampleIndex + 1];
328
+ const prevNode = idToNode.get(prevNodeId);
329
+ const nextNode = idToNode.get(nextNodeId);
330
+ if (prevNodeId === undefined || nextNodeId === undefined || !prevNode || !nextNode) {
331
+ console.error(`Unexpectedly found undefined nodes: ${prevNodeId} ${nextNodeId}`);
332
+ continue;
333
+ }
334
+ if (nodeId === programNodeId && !isSystemNode(prevNodeId) && !isSystemNode(nextNodeId) &&
335
+ bottomNode(prevNode) === bottomNode(nextNode)) {
336
+ samples[sampleIndex] = prevNodeId;
337
+ }
338
+ prevNodeId = nodeId;
339
+ nodeId = nextNodeId;
340
+ }
341
+ function bottomNode(node) {
342
+ while (node.parent && node.parent.parent) {
343
+ node = node.parent;
344
+ }
345
+ return node;
346
+ }
347
+ function isSystemNode(nodeId) {
348
+ return nodeId === programNodeId || nodeId === gcNodeId || nodeId === idleNodeId;
349
+ }
281
350
  }
282
- const stackChildrenDuration = this.#stackChildrenDuration;
283
- let node;
284
- let sampleIndex;
285
- for (sampleIndex = startIndex; sampleIndex < samplesCount; sampleIndex++) {
286
- sampleTime = timestamps[sampleIndex];
287
- if (sampleTime >= stopTime) {
288
- break;
289
- }
290
- const id = samples[sampleIndex];
291
- if (id === prevId) {
292
- continue;
293
- }
294
- node = idToNode.get(id);
295
- let prevNode = idToNode.get(prevId) || null;
296
- if (!prevNode) {
297
- continue;
298
- }
299
- if (gcNode && node === gcNode) {
300
- gcParentNode = prevNode;
301
- openFrameCallback(gcParentNode.depth + 1, gcNode, sampleTime);
302
- stackStartTimes[++stackTop] = sampleTime;
303
- stackChildrenDuration[stackTop] = 0;
304
- prevId = id;
305
- continue;
306
- }
307
- if (gcNode && prevNode === gcNode && gcParentNode) {
308
- const start = stackStartTimes[stackTop];
309
- const duration = sampleTime - start;
310
- stackChildrenDuration[stackTop - 1] += duration;
311
- closeFrameCallback(gcParentNode.depth + 1, gcNode, start, duration, duration - stackChildrenDuration[stackTop]);
312
- --stackTop;
313
- prevNode = gcParentNode;
314
- prevId = prevNode.id;
315
- gcParentNode = null;
316
- }
317
- while (node && node.depth > prevNode.depth) {
318
- stackNodes.push(node);
319
- node = node.parent;
320
- }
321
- while (prevNode && prevNode !== node) {
322
- const start = stackStartTimes[stackTop];
323
- const duration = sampleTime - start;
324
- stackChildrenDuration[stackTop - 1] += duration;
325
- closeFrameCallback(prevNode.depth, prevNode, start, duration, duration - stackChildrenDuration[stackTop]);
326
- --stackTop;
327
- if (node && node.depth === prevNode.depth) {
328
- stackNodes.push(node);
329
- node = node.parent;
330
- }
331
- prevNode = prevNode.parent;
332
- }
333
- while (stackNodes.length) {
334
- const currentNode = stackNodes.pop();
335
- if (!currentNode) {
336
- break;
337
- }
338
- node = currentNode;
339
- openFrameCallback(currentNode.depth, currentNode, sampleTime);
340
- stackStartTimes[++stackTop] = sampleTime;
341
- stackChildrenDuration[stackTop] = 0;
342
- }
343
- prevId = id;
351
+ /**
352
+ * Traverses the call tree derived from the samples calling back when a call is opened
353
+ * and when it's closed
354
+ */
355
+ forEachFrame(openFrameCallback, closeFrameCallback, startTime, stopTime) {
356
+ if (!this.profileHead || !this.samples) {
357
+ return;
358
+ }
359
+ startTime = startTime || 0;
360
+ stopTime = stopTime || Infinity;
361
+ const samples = this.samples;
362
+ const timestamps = this.timestamps;
363
+ const idToNode = this.#idToParsedNode;
364
+ const gcNode = this.gcNode;
365
+ const samplesCount = samples.length;
366
+ const startIndex = Platform.ArrayUtilities.lowerBound(timestamps, startTime, Platform.ArrayUtilities.DEFAULT_COMPARATOR);
367
+ let stackTop = 0;
368
+ const stackNodes = [];
369
+ let prevId = this.profileHead.id;
370
+ let sampleTime;
371
+ let gcParentNode = null;
372
+ // Extra slots for gc being put on top,
373
+ // and one at the bottom to allow safe stackTop-1 access.
374
+ const stackDepth = this.maxDepth + 3;
375
+ if (!this.#stackStartTimes) {
376
+ this.#stackStartTimes = new Array(stackDepth);
377
+ }
378
+ const stackStartTimes = this.#stackStartTimes;
379
+ if (!this.#stackChildrenDuration) {
380
+ this.#stackChildrenDuration = new Array(stackDepth);
381
+ }
382
+ const stackChildrenDuration = this.#stackChildrenDuration;
383
+ let node;
384
+ let sampleIndex;
385
+ for (sampleIndex = startIndex; sampleIndex < samplesCount; sampleIndex++) {
386
+ sampleTime = timestamps[sampleIndex];
387
+ if (sampleTime >= stopTime) {
388
+ break;
389
+ }
390
+ const id = samples[sampleIndex];
391
+ if (id === prevId) {
392
+ continue;
393
+ }
394
+ node = idToNode.get(id);
395
+ let prevNode = idToNode.get(prevId) || null;
396
+ if (!prevNode) {
397
+ continue;
398
+ }
399
+ if (gcNode && node === gcNode) {
400
+ // GC samples have no stack, so we just put GC node on top of the last recorded sample.
401
+ gcParentNode = prevNode;
402
+ openFrameCallback(gcParentNode.depth + 1, gcNode, sampleTime);
403
+ stackStartTimes[++stackTop] = sampleTime;
404
+ stackChildrenDuration[stackTop] = 0;
405
+ prevId = id;
406
+ continue;
407
+ }
408
+ if (gcNode && prevNode === gcNode && gcParentNode) {
409
+ // end of GC frame
410
+ const start = stackStartTimes[stackTop];
411
+ const duration = sampleTime - start;
412
+ stackChildrenDuration[stackTop - 1] += duration;
413
+ closeFrameCallback(gcParentNode.depth + 1, gcNode, start, duration, duration - stackChildrenDuration[stackTop]);
414
+ --stackTop;
415
+ prevNode = gcParentNode;
416
+ prevId = prevNode.id;
417
+ gcParentNode = null;
418
+ }
419
+ // If the depth of this node is greater than the depth of the
420
+ // previous one, new calls happened in between and we need to open
421
+ // them, so track all of them in stackNodes.
422
+ while (node && node.depth > prevNode.depth) {
423
+ stackNodes.push(node);
424
+ node = node.parent;
425
+ }
426
+ // If `prevNode` differs from `node`, the current sample was taken
427
+ // after a change in the call stack, meaning that frames in the
428
+ // path of `prevNode` that differ from those in the path of `node`
429
+ // can be closed. So go down to the lowest common ancestor and
430
+ // close current intervals.
431
+ //
432
+ // For example:
433
+ //
434
+ // prevNode node
435
+ // | |
436
+ // v v
437
+ // [---D--]
438
+ // [---C--][--E--]
439
+ // [------B------] <- LCA
440
+ // [------A------]
441
+ //
442
+ // Because a sample was taken with A, B and E in the stack, it
443
+ // means C and D finished and we can close them.
444
+ while (prevNode && prevNode !== node) {
445
+ const start = stackStartTimes[stackTop];
446
+ const duration = sampleTime - start;
447
+ stackChildrenDuration[stackTop - 1] += duration;
448
+ closeFrameCallback(prevNode.depth, prevNode, start, duration, duration - stackChildrenDuration[stackTop]);
449
+ --stackTop;
450
+ // Track calls to open after previous calls were closed
451
+ // In the example above, this would add E to the tracking stack.
452
+ if (node && node.depth === prevNode.depth) {
453
+ stackNodes.push(node);
454
+ node = node.parent;
455
+ }
456
+ prevNode = prevNode.parent;
457
+ }
458
+ // Go up the nodes stack and open new intervals.
459
+ while (stackNodes.length) {
460
+ const currentNode = stackNodes.pop();
461
+ if (!currentNode) {
462
+ break;
463
+ }
464
+ node = currentNode;
465
+ openFrameCallback(currentNode.depth, currentNode, sampleTime);
466
+ stackStartTimes[++stackTop] = sampleTime;
467
+ stackChildrenDuration[stackTop] = 0;
468
+ }
469
+ prevId = id;
470
+ }
471
+ // Close remaining intervals.
472
+ sampleTime = timestamps[sampleIndex] || this.profileEndTime;
473
+ if (node && gcParentNode && idToNode.get(prevId) === gcNode) {
474
+ const start = stackStartTimes[stackTop];
475
+ const duration = sampleTime - start;
476
+ stackChildrenDuration[stackTop - 1] += duration;
477
+ closeFrameCallback(gcParentNode.depth + 1, node, start, duration, duration - stackChildrenDuration[stackTop]);
478
+ --stackTop;
479
+ prevId = gcParentNode.id;
480
+ }
481
+ for (let node = idToNode.get(prevId); node && node.parent; node = node.parent) {
482
+ const start = stackStartTimes[stackTop];
483
+ const duration = sampleTime - start;
484
+ stackChildrenDuration[stackTop - 1] += duration;
485
+ closeFrameCallback(node.depth, node, start, duration, duration - stackChildrenDuration[stackTop]);
486
+ --stackTop;
487
+ }
344
488
  }
345
- sampleTime = timestamps[sampleIndex] || this.profileEndTime;
346
- if (node && gcParentNode && idToNode.get(prevId) === gcNode) {
347
- const start = stackStartTimes[stackTop];
348
- const duration = sampleTime - start;
349
- stackChildrenDuration[stackTop - 1] += duration;
350
- closeFrameCallback(gcParentNode.depth + 1, node, start, duration, duration - stackChildrenDuration[stackTop]);
351
- --stackTop;
352
- prevId = gcParentNode.id;
489
+ /**
490
+ * Returns the node that corresponds to a given index of a sample.
491
+ */
492
+ nodeByIndex(index) {
493
+ return this.samples && this.#idToParsedNode.get(this.samples[index]) || null;
353
494
  }
354
- for (let node2 = idToNode.get(prevId); node2 && node2.parent; node2 = node2.parent) {
355
- const start = stackStartTimes[stackTop];
356
- const duration = sampleTime - start;
357
- stackChildrenDuration[stackTop - 1] += duration;
358
- closeFrameCallback(node2.depth, node2, start, duration, duration - stackChildrenDuration[stackTop]);
359
- --stackTop;
495
+ /**
496
+ * Returns the node that corresponds to a given node id.
497
+ */
498
+ nodeById(nodeId) {
499
+ return this.#idToParsedNode.get(nodeId) || null;
360
500
  }
361
- }
362
- nodeByIndex(index) {
363
- return this.samples && this.#idToParsedNode.get(this.samples[index]) || null;
364
- }
365
- nodeById(nodeId) {
366
- return this.#idToParsedNode.get(nodeId) || null;
367
- }
368
- nodes() {
369
- if (!this.#idToParsedNode) {
370
- return null;
501
+ nodes() {
502
+ if (!this.#idToParsedNode) {
503
+ return null;
504
+ }
505
+ return [...this.#idToParsedNode.values()];
371
506
  }
372
- return [...this.#idToParsedNode.values()];
373
- }
374
507
  }
375
- //# sourceMappingURL=CPUProfileDataModel.js.map
508
+ //# sourceMappingURL=CPUProfileDataModel.js.map