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