OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | |
6 * @constructor | |
7 * @extends {WebInspector.ProfileNode} | |
8 * @param {!ProfilerAgent.CPUProfileNode} sourceNode | |
9 * @param {number} sampleTime | |
10 */ | |
11 WebInspector.CPUProfileNode = function(sourceNode, sampleTime) | |
12 { | |
13 WebInspector.ProfileNode.call(this, sourceNode.functionName, sourceNode.scri
ptId, sourceNode.url, sourceNode.lineNumber, sourceNode.columnNumber); | |
14 this.id = sourceNode.id; | |
15 this.self = sourceNode.hitCount * sampleTime; | |
16 this.callUID = sourceNode.callUID; | |
17 this.positionTicks = sourceNode.positionTicks; | |
18 this.deoptReason = sourceNode.deoptReason; | |
19 // TODO: Remove the following field in favor of this.self | |
20 this.selfTime = this.self; | |
21 } | |
22 | |
23 WebInspector.CPUProfileNode.prototype = { | |
24 __proto__: WebInspector.ProfileNode.prototype | |
25 } | |
26 | 5 |
27 /** | 6 /** |
28 * @constructor | 7 * @constructor |
29 * @extends {WebInspector.ProfileTreeModel} | |
30 * @param {!ProfilerAgent.CPUProfile} profile | 8 * @param {!ProfilerAgent.CPUProfile} profile |
31 */ | 9 */ |
32 WebInspector.CPUProfileDataModel = function(profile) | 10 WebInspector.CPUProfileDataModel = function(profile) |
33 { | 11 { |
| 12 this.profileHead = profile.head; |
34 this.samples = profile.samples; | 13 this.samples = profile.samples; |
35 this.timestamps = profile.timestamps; | 14 this.timestamps = profile.timestamps; |
36 // Convert times from sec to msec. | |
37 this.profileStartTime = profile.startTime * 1000; | 15 this.profileStartTime = profile.startTime * 1000; |
38 this.profileEndTime = profile.endTime * 1000; | 16 this.profileEndTime = profile.endTime * 1000; |
39 this.totalHitCount = 0; | 17 this._assignParentsInProfile(); |
40 if (!WebInspector.moduleSetting("showNativeFunctionsInJSProfile").get()) | |
41 this._filterNativeFrames(profile.head); | |
42 this.profileHead = this._translateProfileTree(profile.head); | |
43 WebInspector.ProfileTreeModel.call(this, this.profileHead, this.profileStart
Time, this.profileEndTime); | |
44 this._extractMetaNodes(); | |
45 if (this.samples) { | 18 if (this.samples) { |
46 this._buildIdToNodeMap(); | |
47 this._sortSamples(); | 19 this._sortSamples(); |
48 this._normalizeTimestamps(); | 20 this._normalizeTimestamps(); |
| 21 this._buildIdToNodeMap(); |
49 this._fixMissingSamples(); | 22 this._fixMissingSamples(); |
50 } | 23 } |
51 this._assignTotalTimes(this.profileHead); | 24 if (!WebInspector.moduleSetting("showNativeFunctionsInJSProfile").get()) |
| 25 this._filterNativeFrames(); |
| 26 this._assignDepthsInProfile(); |
| 27 this._calculateTimes(profile); |
52 } | 28 } |
53 | 29 |
54 WebInspector.CPUProfileDataModel.prototype = { | 30 WebInspector.CPUProfileDataModel.prototype = { |
55 /** | 31 /** |
56 * @param {!ProfilerAgent.CPUProfileNode} root | 32 * @param {!ProfilerAgent.CPUProfile} profile |
57 */ | 33 */ |
58 _filterNativeFrames: function(root) | 34 _calculateTimes: function(profile) |
59 { | 35 { |
60 // TODO: get rid of this function and do the filtering while _translateP
rofileTree | 36 function totalHitCount(node) { |
| 37 var result = node.hitCount; |
| 38 for (var i = 0; i < node.children.length; i++) |
| 39 result += totalHitCount(node.children[i]); |
| 40 return result; |
| 41 } |
| 42 profile.totalHitCount = totalHitCount(profile.head); |
| 43 this.totalHitCount = profile.totalHitCount; |
| 44 |
| 45 var duration = this.profileEndTime - this.profileStartTime; |
| 46 var samplingInterval = duration / profile.totalHitCount; |
| 47 this.samplingInterval = samplingInterval; |
| 48 |
| 49 function calculateTimesForNode(node) { |
| 50 node.selfTime = node.hitCount * samplingInterval; |
| 51 var totalHitCount = node.hitCount; |
| 52 for (var i = 0; i < node.children.length; i++) |
| 53 totalHitCount += calculateTimesForNode(node.children[i]); |
| 54 node.totalTime = totalHitCount * samplingInterval; |
| 55 return totalHitCount; |
| 56 } |
| 57 calculateTimesForNode(profile.head); |
| 58 }, |
| 59 |
| 60 _filterNativeFrames: function() |
| 61 { |
61 if (this.samples) { | 62 if (this.samples) { |
62 /** @type {!Map<number, !ProfilerAgent.CPUProfileNode>} */ | |
63 var idToNode = new Map(); | |
64 var stack = [root]; | |
65 while (stack.length) { | |
66 var node = stack.pop(); | |
67 idToNode.set(node.id, node); | |
68 for (var i = 0; i < node.children.length; i++) { | |
69 node.children[i].parent = node; | |
70 stack.push(node.children[i]); | |
71 } | |
72 } | |
73 for (var i = 0; i < this.samples.length; ++i) { | 63 for (var i = 0; i < this.samples.length; ++i) { |
74 var node = idToNode.get(this.samples[i]); | 64 var node = this.nodeByIndex(i); |
75 while (isNativeNode(node)) | 65 while (isNativeNode(node)) |
76 node = node.parent; | 66 node = node.parent; |
77 this.samples[i] = node.id; | 67 this.samples[i] = node.id; |
78 } | 68 } |
79 } | 69 } |
80 processSubtree(root); | 70 processSubtree(this.profileHead); |
81 | 71 |
82 /** | 72 /** |
83 * @param {!ProfilerAgent.CPUProfileNode} node | 73 * @param {!ProfilerAgent.CPUProfileNode} node |
84 * @return {boolean} | 74 * @return {boolean} |
85 */ | 75 */ |
86 function isNativeNode(node) | 76 function isNativeNode(node) |
87 { | 77 { |
88 return !!node.url && node.url.startsWith("native "); | 78 return !!node.url && node.url.startsWith("native "); |
89 } | 79 } |
90 | 80 |
(...skipping 30 matching lines...) Expand all Loading... |
121 mergeChildren(node, child); | 111 mergeChildren(node, child); |
122 } else { | 112 } else { |
123 node.children.push(child); | 113 node.children.push(child); |
124 child.parent = node; | 114 child.parent = node; |
125 processSubtree(child); | 115 processSubtree(child); |
126 } | 116 } |
127 } | 117 } |
128 } | 118 } |
129 }, | 119 }, |
130 | 120 |
131 /** | 121 _assignParentsInProfile: function() |
132 * @param {!ProfilerAgent.CPUProfileNode} root | |
133 * @return {!WebInspector.CPUProfileNode} | |
134 */ | |
135 _translateProfileTree: function(root) | |
136 { | 122 { |
137 /** | 123 var head = this.profileHead; |
138 * @param {!ProfilerAgent.CPUProfileNode} node | 124 head.parent = null; |
139 * @return {number} | 125 var nodesToTraverse = [ head ]; |
140 */ | 126 while (nodesToTraverse.length) { |
141 function computeHitCountForSubtree(node) | 127 var parent = nodesToTraverse.pop(); |
142 { | 128 var children = parent.children; |
143 return node.children.reduce((acc, node) => acc + computeHitCountForS
ubtree(node), node.hitCount); | 129 var length = children.length; |
| 130 for (var i = 0; i < length; ++i) { |
| 131 var child = children[i]; |
| 132 child.parent = parent; |
| 133 if (child.children.length) |
| 134 nodesToTraverse.push(child); |
| 135 } |
144 } | 136 } |
145 this.totalHitCount = computeHitCountForSubtree(root); | |
146 var sampleTime = (this.profileEndTime - this.profileStartTime) / this.to
talHitCount; | |
147 var resultRoot = new WebInspector.CPUProfileNode(root, sampleTime); | |
148 var targetNodeStack = [resultRoot]; | |
149 var sourceNodeStack = [root]; | |
150 while (sourceNodeStack.length) { | |
151 var sourceNode = sourceNodeStack.pop(); | |
152 var parentNode = targetNodeStack.pop(); | |
153 parentNode.children = sourceNode.children.map(child => new WebInspec
tor.CPUProfileNode(child, sampleTime)); | |
154 sourceNodeStack.push.apply(sourceNodeStack, sourceNode.children); | |
155 targetNodeStack.push.apply(targetNodeStack, parentNode.children); | |
156 } | |
157 return resultRoot; | |
158 }, | 137 }, |
159 | 138 |
160 /** | 139 _assignDepthsInProfile: function() |
161 * @param {!WebInspector.ProfileNode} node | |
162 */ | |
163 _assignTotalTimes: function(node) | |
164 { | 140 { |
165 // TODO: get rid of this field in favor of this.total | 141 var head = this.profileHead; |
166 node.totalTime = node.total; | 142 head.depth = -1; |
167 node.children.forEach(this._assignTotalTimes, this); | 143 this.maxDepth = 0; |
| 144 var nodesToTraverse = [ head ]; |
| 145 while (nodesToTraverse.length) { |
| 146 var parent = nodesToTraverse.pop(); |
| 147 var depth = parent.depth + 1; |
| 148 if (depth > this.maxDepth) |
| 149 this.maxDepth = depth; |
| 150 var children = parent.children; |
| 151 var length = children.length; |
| 152 for (var i = 0; i < length; ++i) { |
| 153 var child = children[i]; |
| 154 child.depth = depth; |
| 155 if (child.children.length) |
| 156 nodesToTraverse.push(child); |
| 157 } |
| 158 } |
168 }, | 159 }, |
169 | 160 |
170 _sortSamples: function() | 161 _sortSamples: function() |
171 { | 162 { |
172 var timestamps = this.timestamps; | 163 var timestamps = this.timestamps; |
173 if (!timestamps) | 164 if (!timestamps) |
174 return; | 165 return; |
175 var samples = this.samples; | 166 var samples = this.samples; |
176 var indices = timestamps.map((x, index) => index); | 167 var indices = timestamps.map((x, index) => index); |
177 indices.sort((a, b) => timestamps[a] - timestamps[b]); | 168 indices.sort((a, b) => timestamps[a] - timestamps[b]); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
215 timestamps[i] /= 1000; | 206 timestamps[i] /= 1000; |
216 var averageSample = (timestamps.peekLast() - timestamps[0]) / (timestamp
s.length - 1); | 207 var averageSample = (timestamps.peekLast() - timestamps[0]) / (timestamp
s.length - 1); |
217 // Add an extra timestamp used to calculate the last sample duration. | 208 // Add an extra timestamp used to calculate the last sample duration. |
218 this.timestamps.push(timestamps.peekLast() + averageSample); | 209 this.timestamps.push(timestamps.peekLast() + averageSample); |
219 this.profileStartTime = timestamps[0]; | 210 this.profileStartTime = timestamps[0]; |
220 this.profileEndTime = timestamps.peekLast(); | 211 this.profileEndTime = timestamps.peekLast(); |
221 }, | 212 }, |
222 | 213 |
223 _buildIdToNodeMap: function() | 214 _buildIdToNodeMap: function() |
224 { | 215 { |
225 /** @type {!Object<number, !WebInspector.CPUProfileNode>} */ | 216 /** @type {!Object.<number, !ProfilerAgent.CPUProfileNode>} */ |
226 this._idToNode = {}; | 217 this._idToNode = {}; |
227 var idToNode = this._idToNode; | 218 var idToNode = this._idToNode; |
228 var stack = [this.profileHead]; | 219 var stack = [this.profileHead]; |
229 while (stack.length) { | 220 while (stack.length) { |
230 var node = stack.pop(); | 221 var node = stack.pop(); |
231 idToNode[node.id] = node; | 222 idToNode[node.id] = node; |
232 for (var i = 0; i < node.children.length; i++) | 223 for (var i = 0; i < node.children.length; i++) |
233 stack.push(node.children[i]); | 224 stack.push(node.children[i]); |
234 } | 225 } |
235 }, | |
236 | 226 |
237 _extractMetaNodes: function() | |
238 { | |
239 var topLevelNodes = this.profileHead.children; | 227 var topLevelNodes = this.profileHead.children; |
240 for (var i = 0; i < topLevelNodes.length && !(this.gcNode && this.progra
mNode && this.idleNode); i++) { | 228 for (var i = 0; i < topLevelNodes.length && !(this.gcNode && this.progra
mNode && this.idleNode); i++) { |
241 var node = topLevelNodes[i]; | 229 var node = topLevelNodes[i]; |
242 if (node.functionName === "(garbage collector)") | 230 if (node.functionName === "(garbage collector)") |
243 this.gcNode = node; | 231 this.gcNode = node; |
244 else if (node.functionName === "(program)") | 232 else if (node.functionName === "(program)") |
245 this.programNode = node; | 233 this.programNode = node; |
246 else if (node.functionName === "(idle)") | 234 else if (node.functionName === "(idle)") |
247 this.idleNode = node; | 235 this.idleNode = node; |
248 } | 236 } |
(...skipping 21 matching lines...) Expand all Loading... |
270 var nextNodeId = samples[sampleIndex + 1]; | 258 var nextNodeId = samples[sampleIndex + 1]; |
271 if (nodeId === programNodeId && !isSystemNode(prevNodeId) && !isSyst
emNode(nextNodeId) | 259 if (nodeId === programNodeId && !isSystemNode(prevNodeId) && !isSyst
emNode(nextNodeId) |
272 && bottomNode(idToNode[prevNodeId]) === bottomNode(idToNode[next
NodeId])) { | 260 && bottomNode(idToNode[prevNodeId]) === bottomNode(idToNode[next
NodeId])) { |
273 samples[sampleIndex] = prevNodeId; | 261 samples[sampleIndex] = prevNodeId; |
274 } | 262 } |
275 prevNodeId = nodeId; | 263 prevNodeId = nodeId; |
276 nodeId = nextNodeId; | 264 nodeId = nextNodeId; |
277 } | 265 } |
278 | 266 |
279 /** | 267 /** |
280 * @param {!WebInspector.ProfileNode} node | 268 * @param {!ProfilerAgent.CPUProfileNode} node |
281 * @return {!WebInspector.ProfileNode} | 269 * @return {!ProfilerAgent.CPUProfileNode} |
282 */ | 270 */ |
283 function bottomNode(node) | 271 function bottomNode(node) |
284 { | 272 { |
285 while (node.parent.parent) | 273 while (node.parent.parent) |
286 node = node.parent; | 274 node = node.parent; |
287 return node; | 275 return node; |
288 } | 276 } |
289 | 277 |
290 /** | 278 /** |
291 * @param {number} nodeId | 279 * @param {number} nodeId |
292 * @return {boolean} | 280 * @return {boolean} |
293 */ | 281 */ |
294 function isSystemNode(nodeId) | 282 function isSystemNode(nodeId) |
295 { | 283 { |
296 return nodeId === programNodeId || nodeId === gcNodeId || nodeId ===
idleNodeId; | 284 return nodeId === programNodeId || nodeId === gcNodeId || nodeId ===
idleNodeId; |
297 } | 285 } |
298 }, | 286 }, |
299 | 287 |
300 /** | 288 /** |
301 * @param {function(number, !WebInspector.CPUProfileNode, number)} openFrame
Callback | 289 * @param {function(number, !ProfilerAgent.CPUProfileNode, number)} openFram
eCallback |
302 * @param {function(number, !WebInspector.CPUProfileNode, number, number, nu
mber)} closeFrameCallback | 290 * @param {function(number, !ProfilerAgent.CPUProfileNode, number, number, n
umber)} closeFrameCallback |
303 * @param {number=} startTime | 291 * @param {number=} startTime |
304 * @param {number=} stopTime | 292 * @param {number=} stopTime |
305 */ | 293 */ |
306 forEachFrame: function(openFrameCallback, closeFrameCallback, startTime, sto
pTime) | 294 forEachFrame: function(openFrameCallback, closeFrameCallback, startTime, sto
pTime) |
307 { | 295 { |
308 if (!this.profileHead) | 296 if (!this.profileHead) |
309 return; | 297 return; |
310 | 298 |
311 startTime = startTime || 0; | 299 startTime = startTime || 0; |
312 stopTime = stopTime || Infinity; | 300 stopTime = stopTime || Infinity; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
363 while (node.depth > prevNode.depth) { | 351 while (node.depth > prevNode.depth) { |
364 stackNodes.push(node); | 352 stackNodes.push(node); |
365 node = node.parent; | 353 node = node.parent; |
366 } | 354 } |
367 | 355 |
368 // Go down to the LCA and close current intervals. | 356 // Go down to the LCA and close current intervals. |
369 while (prevNode !== node) { | 357 while (prevNode !== node) { |
370 var start = stackStartTimes[stackTop]; | 358 var start = stackStartTimes[stackTop]; |
371 var duration = sampleTime - start; | 359 var duration = sampleTime - start; |
372 stackChildrenDuration[stackTop - 1] += duration; | 360 stackChildrenDuration[stackTop - 1] += duration; |
373 closeFrameCallback(prevNode.depth, /** @type {!WebInspector.CPUP
rofileNode} */(prevNode), start, duration, duration - stackChildrenDuration[stac
kTop]); | 361 closeFrameCallback(prevNode.depth, prevNode, start, duration, du
ration - stackChildrenDuration[stackTop]); |
374 --stackTop; | 362 --stackTop; |
375 if (node.depth === prevNode.depth) { | 363 if (node.depth === prevNode.depth) { |
376 stackNodes.push(node); | 364 stackNodes.push(node); |
377 node = node.parent; | 365 node = node.parent; |
378 } | 366 } |
379 prevNode = prevNode.parent; | 367 prevNode = prevNode.parent; |
380 } | 368 } |
381 | 369 |
382 // Go up the nodes stack and open new intervals. | 370 // Go up the nodes stack and open new intervals. |
383 while (stackNodes.length) { | 371 while (stackNodes.length) { |
(...skipping 11 matching lines...) Expand all Loading... |
395 var duration = sampleTime - start; | 383 var duration = sampleTime - start; |
396 stackChildrenDuration[stackTop - 1] += duration; | 384 stackChildrenDuration[stackTop - 1] += duration; |
397 closeFrameCallback(gcParentNode.depth + 1, node, start, duration, du
ration - stackChildrenDuration[stackTop]); | 385 closeFrameCallback(gcParentNode.depth + 1, node, start, duration, du
ration - stackChildrenDuration[stackTop]); |
398 --stackTop; | 386 --stackTop; |
399 } | 387 } |
400 | 388 |
401 for (var node = idToNode[prevId]; node.parent; node = node.parent) { | 389 for (var node = idToNode[prevId]; node.parent; node = node.parent) { |
402 var start = stackStartTimes[stackTop]; | 390 var start = stackStartTimes[stackTop]; |
403 var duration = sampleTime - start; | 391 var duration = sampleTime - start; |
404 stackChildrenDuration[stackTop - 1] += duration; | 392 stackChildrenDuration[stackTop - 1] += duration; |
405 closeFrameCallback(node.depth, /** @type {!WebInspector.CPUProfileNo
de} */(node), start, duration, duration - stackChildrenDuration[stackTop]); | 393 closeFrameCallback(node.depth, node, start, duration, duration - sta
ckChildrenDuration[stackTop]); |
406 --stackTop; | 394 --stackTop; |
407 } | 395 } |
408 }, | 396 }, |
409 | 397 |
410 /** | 398 /** |
411 * @param {number} index | 399 * @param {number} index |
412 * @return {!WebInspector.CPUProfileNode} | 400 * @return {!ProfilerAgent.CPUProfileNode} |
413 */ | 401 */ |
414 nodeByIndex: function(index) | 402 nodeByIndex: function(index) |
415 { | 403 { |
416 return this._idToNode[this.samples[index]]; | 404 return this._idToNode[this.samples[index]]; |
417 }, | 405 } |
418 | 406 |
419 __proto__: WebInspector.ProfileTreeModel.prototype | |
420 } | 407 } |
OLD | NEW |