Chromium Code Reviews

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/sdk/CPUProfileDataModel.js

Issue 1873973002: DevTools: extract CPU profile independent part of CPUProfileNode. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff |
OLDNEW
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);
14 this.functionName = sourceNode.functionName;
15 this.scriptId = sourceNode.scriptId;
16 this.url = sourceNode.url;
17 this.lineNumber = sourceNode.lineNumber;
18 this.columnNumber = sourceNode.columnNumber;
19 this.id = sourceNode.id;
20 this.self = sourceNode.hitCount * sampleTime;
21 this.callUID = sourceNode.callUID;
22 this.positionTicks = sourceNode.positionTicks;
23 this.deoptReason = sourceNode.deoptReason;
caseq 2016/04/13 17:21:12 Why do we have to do this? Having to copy all fiel
alph 2016/04/13 19:05:07 This is done just once for the tree. I want to hav
24 // TODO: Remove the following field in favor of this.self
25 this.selfTime = this.self;
26 }
27
28 WebInspector.CPUProfileNode.prototype = {
29 __proto__: WebInspector.ProfileNode.prototype
30 }
5 31
6 /** 32 /**
7 * @constructor 33 * @constructor
34 * @extends {WebInspector.ProfileTreeModel}
8 * @param {!ProfilerAgent.CPUProfile} profile 35 * @param {!ProfilerAgent.CPUProfile} profile
9 */ 36 */
10 WebInspector.CPUProfileDataModel = function(profile) 37 WebInspector.CPUProfileDataModel = function(profile)
11 { 38 {
12 this.profileHead = profile.head;
13 this.samples = profile.samples; 39 this.samples = profile.samples;
14 this.timestamps = profile.timestamps; 40 this.timestamps = profile.timestamps;
41 // Convert times from sec to msec.
15 this.profileStartTime = profile.startTime * 1000; 42 this.profileStartTime = profile.startTime * 1000;
16 this.profileEndTime = profile.endTime * 1000; 43 this.profileEndTime = profile.endTime * 1000;
17 this._assignParentsInProfile(); 44 if (!WebInspector.moduleSetting("showNativeFunctionsInJSProfile").get())
45 this._filterNativeFrames(profile.head);
46 this.profileHead = this._translateProfileTree(profile.head);
47 WebInspector.ProfileTreeModel.call(this, this.profileHead, this.profileStart Time, this.profileEndTime);
48 this._extractMetaNodes();
18 if (this.samples) { 49 if (this.samples) {
50 this._buildIdToNodeMap();
19 this._sortSamples(); 51 this._sortSamples();
20 this._normalizeTimestamps(); 52 this._normalizeTimestamps();
21 this._buildIdToNodeMap();
22 this._fixMissingSamples(); 53 this._fixMissingSamples();
23 } 54 }
24 if (!WebInspector.moduleSetting("showNativeFunctionsInJSProfile").get()) 55 this._assignTotalTimes(this.profileHead);
25 this._filterNativeFrames();
26 this._assignDepthsInProfile();
27 this._calculateTimes(profile);
28 } 56 }
29 57
30 WebInspector.CPUProfileDataModel.prototype = { 58 WebInspector.CPUProfileDataModel.prototype = {
31 /** 59 /**
32 * @param {!ProfilerAgent.CPUProfile} profile 60 * @param {!ProfilerAgent.CPUProfileNode} head
33 */ 61 */
34 _calculateTimes: function(profile) 62 _filterNativeFrames: function(head)
35 {
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 { 63 {
62 if (this.samples) { 64 if (this.samples) {
65 var idToNode = {};
caseq 2016/04/13 17:21:12 use Map()?
alph 2016/04/13 19:05:07 Done.
66 var stack = [head];
67 while (stack.length) {
caseq 2016/04/13 17:21:12 can this be combined with tree traversal we perfor
alph 2016/04/13 19:05:07 Yes, building the tree along with filtering should
68 var node = stack.pop();
69 idToNode[node.id] = node;
70 for (var i = 0; i < node.children.length; i++) {
71 node.children[i].parent = node;
72 stack.push(node.children[i]);
73 }
74 }
63 for (var i = 0; i < this.samples.length; ++i) { 75 for (var i = 0; i < this.samples.length; ++i) {
64 var node = this.nodeByIndex(i); 76 var node = idToNode[this.samples[i]];
65 while (isNativeNode(node)) 77 while (isNativeNode(node))
66 node = node.parent; 78 node = node.parent;
caseq 2016/04/13 17:21:12 so we actually care to know nearest non-native par
alph 2016/04/13 19:05:07 I'm going to get rid of this function later. Added
67 this.samples[i] = node.id; 79 this.samples[i] = node.id;
68 } 80 }
69 } 81 }
70 processSubtree(this.profileHead); 82 processSubtree(head);
71 83
72 /** 84 /**
73 * @param {!ProfilerAgent.CPUProfileNode} node 85 * @param {!ProfilerAgent.CPUProfileNode} node
74 * @return {boolean} 86 * @return {boolean}
75 */ 87 */
76 function isNativeNode(node) 88 function isNativeNode(node)
77 { 89 {
78 return !!node.url && node.url.startsWith("native "); 90 return !!node.url && node.url.startsWith("native ");
79 } 91 }
80 92
(...skipping 30 matching lines...)
111 mergeChildren(node, child); 123 mergeChildren(node, child);
112 } else { 124 } else {
113 node.children.push(child); 125 node.children.push(child);
114 child.parent = node; 126 child.parent = node;
115 processSubtree(child); 127 processSubtree(child);
116 } 128 }
117 } 129 }
118 } 130 }
119 }, 131 },
120 132
121 _assignParentsInProfile: function() 133 /**
134 * @param {!ProfilerAgent.CPUProfileNode} head
135 * @return {!WebInspector.CPUProfileNode}
136 */
137 _translateProfileTree: function(head)
122 { 138 {
123 var head = this.profileHead; 139 /**
124 head.parent = null; 140 * @param {!ProfilerAgent.CPUProfileNode} node
125 var nodesToTraverse = [ head ]; 141 * @return {number}
142 */
143 function treeHitCount(node)
caseq 2016/04/13 17:21:12 nit: computeHitCountForSubtree()
alph 2016/04/13 19:05:07 Done.
144 {
145 return node.children.reduce((acc, node) => acc + treeHitCount(node), node.hitCount);
146 }
147 this.totalHitCount = treeHitCount(head);
148 var sampleTime = (this.profileEndTime - this.profileStartTime) / this.to talHitCount;
149 var root = new WebInspector.CPUProfileNode(head, sampleTime);
150 // The stack contains pairs: (parentNode, sourceNode.children)
caseq 2016/04/13 17:21:12 Please make it an array of objects, this way it's
alph 2016/04/13 19:05:07 Done.
151 var nodesToTraverse = [ root, head.children ];
126 while (nodesToTraverse.length) { 152 while (nodesToTraverse.length) {
127 var parent = nodesToTraverse.pop(); 153 var children = nodesToTraverse.pop();
128 var children = parent.children; 154 var parentNode = nodesToTraverse.pop();
129 var length = children.length; 155 for (var i = 0; i < children.length; ++i) {
130 for (var i = 0; i < length; ++i) { 156 var sourceNode = children[i];
131 var child = children[i]; 157 var node = new WebInspector.CPUProfileNode(sourceNode, sampleTim e);
132 child.parent = parent; 158 parentNode.children.push(node);
133 if (child.children.length) 159 if (sourceNode.children.length)
134 nodesToTraverse.push(child); 160 nodesToTraverse.push(node, sourceNode.children);
135 } 161 }
136 } 162 }
163 return root;
137 }, 164 },
138 165
139 _assignDepthsInProfile: function() 166 /**
167 * @param {!WebInspector.ProfileNode} node
168 */
169 _assignTotalTimes: function(node)
140 { 170 {
141 var head = this.profileHead; 171 // TODO: get rid of this field in favor of this.total
142 head.depth = -1; 172 node.totalTime = node.total;
143 this.maxDepth = 0; 173 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 }, 174 },
160 175
161 _sortSamples: function() 176 _sortSamples: function()
162 { 177 {
163 var timestamps = this.timestamps; 178 var timestamps = this.timestamps;
164 if (!timestamps) 179 if (!timestamps)
165 return; 180 return;
166 var samples = this.samples; 181 var samples = this.samples;
167 var indices = timestamps.map((x, index) => index); 182 var indices = timestamps.map((x, index) => index);
168 indices.sort((a, b) => timestamps[a] - timestamps[b]); 183 indices.sort((a, b) => timestamps[a] - timestamps[b]);
(...skipping 37 matching lines...)
206 timestamps[i] /= 1000; 221 timestamps[i] /= 1000;
207 var averageSample = (timestamps.peekLast() - timestamps[0]) / (timestamp s.length - 1); 222 var averageSample = (timestamps.peekLast() - timestamps[0]) / (timestamp s.length - 1);
208 // Add an extra timestamp used to calculate the last sample duration. 223 // Add an extra timestamp used to calculate the last sample duration.
209 this.timestamps.push(timestamps.peekLast() + averageSample); 224 this.timestamps.push(timestamps.peekLast() + averageSample);
210 this.profileStartTime = timestamps[0]; 225 this.profileStartTime = timestamps[0];
211 this.profileEndTime = timestamps.peekLast(); 226 this.profileEndTime = timestamps.peekLast();
212 }, 227 },
213 228
214 _buildIdToNodeMap: function() 229 _buildIdToNodeMap: function()
215 { 230 {
216 /** @type {!Object.<number, !ProfilerAgent.CPUProfileNode>} */ 231 /** @type {!Object<number, !WebInspector.CPUProfileNode>} */
217 this._idToNode = {}; 232 this._idToNode = {};
218 var idToNode = this._idToNode; 233 var idToNode = this._idToNode;
219 var stack = [this.profileHead]; 234 var stack = [this.profileHead];
220 while (stack.length) { 235 while (stack.length) {
221 var node = stack.pop(); 236 var node = stack.pop();
222 idToNode[node.id] = node; 237 idToNode[node.id] = node;
223 for (var i = 0; i < node.children.length; i++) 238 for (var i = 0; i < node.children.length; i++)
224 stack.push(node.children[i]); 239 stack.push(node.children[i]);
225 } 240 }
241 },
226 242
243 _extractMetaNodes: function()
244 {
227 var topLevelNodes = this.profileHead.children; 245 var topLevelNodes = this.profileHead.children;
228 for (var i = 0; i < topLevelNodes.length && !(this.gcNode && this.progra mNode && this.idleNode); i++) { 246 for (var i = 0; i < topLevelNodes.length && !(this.gcNode && this.progra mNode && this.idleNode); i++) {
229 var node = topLevelNodes[i]; 247 var node = topLevelNodes[i];
230 if (node.functionName === "(garbage collector)") 248 if (node.functionName === "(garbage collector)")
231 this.gcNode = node; 249 this.gcNode = node;
232 else if (node.functionName === "(program)") 250 else if (node.functionName === "(program)")
233 this.programNode = node; 251 this.programNode = node;
234 else if (node.functionName === "(idle)") 252 else if (node.functionName === "(idle)")
235 this.idleNode = node; 253 this.idleNode = node;
236 } 254 }
(...skipping 21 matching lines...)
258 var nextNodeId = samples[sampleIndex + 1]; 276 var nextNodeId = samples[sampleIndex + 1];
259 if (nodeId === programNodeId && !isSystemNode(prevNodeId) && !isSyst emNode(nextNodeId) 277 if (nodeId === programNodeId && !isSystemNode(prevNodeId) && !isSyst emNode(nextNodeId)
260 && bottomNode(idToNode[prevNodeId]) === bottomNode(idToNode[next NodeId])) { 278 && bottomNode(idToNode[prevNodeId]) === bottomNode(idToNode[next NodeId])) {
261 samples[sampleIndex] = prevNodeId; 279 samples[sampleIndex] = prevNodeId;
262 } 280 }
263 prevNodeId = nodeId; 281 prevNodeId = nodeId;
264 nodeId = nextNodeId; 282 nodeId = nextNodeId;
265 } 283 }
266 284
267 /** 285 /**
268 * @param {!ProfilerAgent.CPUProfileNode} node 286 * @param {!WebInspector.ProfileNode} node
269 * @return {!ProfilerAgent.CPUProfileNode} 287 * @return {!WebInspector.ProfileNode}
270 */ 288 */
271 function bottomNode(node) 289 function bottomNode(node)
272 { 290 {
273 while (node.parent.parent) 291 while (node.parent.parent)
274 node = node.parent; 292 node = node.parent;
275 return node; 293 return node;
276 } 294 }
277 295
278 /** 296 /**
279 * @param {number} nodeId 297 * @param {number} nodeId
280 * @return {boolean} 298 * @return {boolean}
281 */ 299 */
282 function isSystemNode(nodeId) 300 function isSystemNode(nodeId)
283 { 301 {
284 return nodeId === programNodeId || nodeId === gcNodeId || nodeId === idleNodeId; 302 return nodeId === programNodeId || nodeId === gcNodeId || nodeId === idleNodeId;
285 } 303 }
286 }, 304 },
287 305
288 /** 306 /**
289 * @param {function(number, !ProfilerAgent.CPUProfileNode, number)} openFram eCallback 307 * @param {function(number, !WebInspector.CPUProfileNode, number)} openFrame Callback
290 * @param {function(number, !ProfilerAgent.CPUProfileNode, number, number, n umber)} closeFrameCallback 308 * @param {function(number, !WebInspector.CPUProfileNode, number, number, nu mber)} closeFrameCallback
291 * @param {number=} startTime 309 * @param {number=} startTime
292 * @param {number=} stopTime 310 * @param {number=} stopTime
293 */ 311 */
294 forEachFrame: function(openFrameCallback, closeFrameCallback, startTime, sto pTime) 312 forEachFrame: function(openFrameCallback, closeFrameCallback, startTime, sto pTime)
295 { 313 {
296 if (!this.profileHead) 314 if (!this.profileHead)
297 return; 315 return;
298 316
299 startTime = startTime || 0; 317 startTime = startTime || 0;
300 stopTime = stopTime || Infinity; 318 stopTime = stopTime || Infinity;
(...skipping 50 matching lines...)
351 while (node.depth > prevNode.depth) { 369 while (node.depth > prevNode.depth) {
352 stackNodes.push(node); 370 stackNodes.push(node);
353 node = node.parent; 371 node = node.parent;
354 } 372 }
355 373
356 // Go down to the LCA and close current intervals. 374 // Go down to the LCA and close current intervals.
357 while (prevNode !== node) { 375 while (prevNode !== node) {
358 var start = stackStartTimes[stackTop]; 376 var start = stackStartTimes[stackTop];
359 var duration = sampleTime - start; 377 var duration = sampleTime - start;
360 stackChildrenDuration[stackTop - 1] += duration; 378 stackChildrenDuration[stackTop - 1] += duration;
361 closeFrameCallback(prevNode.depth, prevNode, start, duration, du ration - stackChildrenDuration[stackTop]); 379 closeFrameCallback(prevNode.depth, /** @type {!WebInspector.CPUP rofileNode} */(prevNode), start, duration, duration - stackChildrenDuration[stac kTop]);
362 --stackTop; 380 --stackTop;
363 if (node.depth === prevNode.depth) { 381 if (node.depth === prevNode.depth) {
364 stackNodes.push(node); 382 stackNodes.push(node);
365 node = node.parent; 383 node = node.parent;
366 } 384 }
367 prevNode = prevNode.parent; 385 prevNode = prevNode.parent;
368 } 386 }
369 387
370 // Go up the nodes stack and open new intervals. 388 // Go up the nodes stack and open new intervals.
371 while (stackNodes.length) { 389 while (stackNodes.length) {
(...skipping 11 matching lines...)
383 var duration = sampleTime - start; 401 var duration = sampleTime - start;
384 stackChildrenDuration[stackTop - 1] += duration; 402 stackChildrenDuration[stackTop - 1] += duration;
385 closeFrameCallback(gcParentNode.depth + 1, node, start, duration, du ration - stackChildrenDuration[stackTop]); 403 closeFrameCallback(gcParentNode.depth + 1, node, start, duration, du ration - stackChildrenDuration[stackTop]);
386 --stackTop; 404 --stackTop;
387 } 405 }
388 406
389 for (var node = idToNode[prevId]; node.parent; node = node.parent) { 407 for (var node = idToNode[prevId]; node.parent; node = node.parent) {
390 var start = stackStartTimes[stackTop]; 408 var start = stackStartTimes[stackTop];
391 var duration = sampleTime - start; 409 var duration = sampleTime - start;
392 stackChildrenDuration[stackTop - 1] += duration; 410 stackChildrenDuration[stackTop - 1] += duration;
393 closeFrameCallback(node.depth, node, start, duration, duration - sta ckChildrenDuration[stackTop]); 411 closeFrameCallback(node.depth, /** @type {!WebInspector.CPUProfileNo de} */(node), start, duration, duration - stackChildrenDuration[stackTop]);
394 --stackTop; 412 --stackTop;
395 } 413 }
396 }, 414 },
397 415
398 /** 416 /**
399 * @param {number} index 417 * @param {number} index
400 * @return {!ProfilerAgent.CPUProfileNode} 418 * @return {!WebInspector.CPUProfileNode}
401 */ 419 */
402 nodeByIndex: function(index) 420 nodeByIndex: function(index)
403 { 421 {
404 return this._idToNode[this.samples[index]]; 422 return this._idToNode[this.samples[index]];
405 } 423 },
406 424
425 __proto__: WebInspector.ProfileTreeModel.prototype
407 } 426 }
OLDNEW

Powered by Google App Engine