Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(68)

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: addressing comments. Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
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, 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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698