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

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: fix tests. 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 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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/devtools/devtools.gypi ('k') | third_party/WebKit/Source/devtools/front_end/sdk/ProfileTreeModel.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698