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