Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 WebInspector.TimelineProfileTree = {}; | 4 WebInspector.TimelineProfileTree = {}; |
| 5 | 5 |
| 6 /** | 6 /** |
| 7 * @unrestricted | 7 * @unrestricted |
| 8 */ | 8 */ |
| 9 WebInspector.TimelineProfileTree.Node = class { | 9 WebInspector.TimelineProfileTree.Node = class { |
| 10 constructor() { | 10 constructor() { |
| 11 /** @type {number} */ | 11 /** @type {number} */ |
| 12 this.totalTime; | 12 this.totalTime; |
| 13 /** @type {number} */ | 13 /** @type {number} */ |
| 14 this.selfTime; | 14 this.selfTime; |
| 15 /** @type {string} */ | 15 /** @type {string} */ |
| 16 this.id; | 16 this.id; |
| 17 /** @type {!WebInspector.TracingModel.Event} */ | 17 /** @type {!WebInspector.TracingModel.Event} */ |
| 18 this.event; | 18 this.event; |
| 19 /** @type {?Map<string|symbol,!WebInspector.TimelineProfileTree.Node>} */ | 19 /** @type {?Map<string|symbol,!WebInspector.TimelineProfileTree.Node>} */ |
| 20 this.children; | 20 this.children; |
| 21 /** @type {?WebInspector.TimelineProfileTree.Node} */ | 21 /** @type {?WebInspector.TimelineProfileTree.Node} */ |
| 22 this.parent; | 22 this.parent; |
| 23 | |
| 24 /** @type {string} */ | |
| 25 this._groupId = ''; | |
| 23 this._isGroupNode = false; | 26 this._isGroupNode = false; |
| 24 } | 27 } |
| 25 | 28 |
| 26 /** | 29 /** |
| 27 * @return {boolean} | 30 * @return {boolean} |
| 28 */ | 31 */ |
| 29 isGroupNode() { | 32 isGroupNode() { |
| 30 return this._isGroupNode; | 33 return this._isGroupNode; |
| 31 } | 34 } |
| 32 }; | 35 }; |
| 33 | 36 |
| 34 /** | 37 /** |
| 35 * @param {!Array<!WebInspector.TracingModel.Event>} events | 38 * @param {!Array<!WebInspector.TracingModel.Event>} events |
| 36 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters | 39 * @param {!Array<!WebInspector.TimelineModel.Filter>} filters |
| 37 * @param {number} startTime | 40 * @param {number} startTime |
| 38 * @param {number} endTime | 41 * @param {number} endTime |
| 39 * @param {function(!WebInspector.TracingModel.Event):(string|symbol)=} eventIdC allback | 42 * @param {function(!WebInspector.TracingModel.Event, string):string=} eventGrou pIdCallback |
| 40 * @return {!WebInspector.TimelineProfileTree.Node} | 43 * @return {!WebInspector.TimelineProfileTree.Node} |
| 41 */ | 44 */ |
| 42 WebInspector.TimelineProfileTree.buildTopDown = function(events, filters, startT ime, endTime, eventIdCallback) { | 45 WebInspector.TimelineProfileTree.buildTopDown = function(events, filters, startT ime, endTime, eventGroupIdCallback) { |
| 43 // Temporarily deposit a big enough value that exceeds the max recording time. | 46 // Temporarily deposit a big enough value that exceeds the max recording time. |
| 44 var /** @const */ initialTime = 1e7; | 47 var /** @const */ initialTime = 1e7; |
| 45 var root = new WebInspector.TimelineProfileTree.Node(); | 48 var root = new WebInspector.TimelineProfileTree.Node(); |
| 46 root.totalTime = initialTime; | 49 root.totalTime = initialTime; |
| 47 root.selfTime = initialTime; | 50 root.selfTime = initialTime; |
| 48 root.children = /** @type {!Map<string, !WebInspector.TimelineProfileTree.Node >} */ (new Map()); | 51 root.children = /** @type {!Map<string, !WebInspector.TimelineProfileTree.Node >} */ (new Map()); |
| 52 var pageFrameIdStack = []; | |
| 49 var parent = root; | 53 var parent = root; |
| 50 | 54 |
| 51 /** | 55 /** |
| 52 * @param {!WebInspector.TracingModel.Event} e | 56 * @param {!WebInspector.TracingModel.Event} e |
| 53 */ | 57 */ |
| 54 function onStartEvent(e) { | 58 function onStartEvent(e) { |
| 59 var pageFrameId = WebInspector.TimelineModel.eventFrameId(e) || pageFrameIdS tack.peekLast(); | |
| 60 pageFrameIdStack.push(pageFrameId); | |
| 55 if (!WebInspector.TimelineModel.isVisible(filters, e)) | 61 if (!WebInspector.TimelineModel.isVisible(filters, e)) |
| 56 return; | 62 return; |
| 57 var time = e.endTime ? Math.min(endTime, e.endTime) - Math.max(startTime, e. startTime) : 0; | 63 var time = e.endTime ? Math.min(endTime, e.endTime) - Math.max(startTime, e. startTime) : 0; |
| 58 var id = eventIdCallback ? eventIdCallback(e) : Symbol('uniqueEventId'); | 64 var groupId = eventGroupIdCallback ? eventGroupIdCallback(e, pageFrameId) : Symbol('uniqueGroupId'); |
| 65 var id = eventGroupIdCallback ? WebInspector.TimelineProfileTree._eventId(e) : Symbol('uniqueEventId'); | |
|
dgozman
2016/11/07 17:43:16
Why do we need special ids in case of grouping?
| |
| 66 if (typeof groupId === 'string' && typeof id === 'string') | |
| 67 id += '/' + groupId; | |
| 59 if (!parent.children) | 68 if (!parent.children) |
| 60 parent.children = /** @type {!Map<string,!WebInspector.TimelineProfileTree .Node>} */ (new Map()); | 69 parent.children = /** @type {!Map<string,!WebInspector.TimelineProfileTree .Node>} */ (new Map()); |
| 61 var node = parent.children.get(id); | 70 var node = parent.children.get(id); |
| 62 if (node) { | 71 if (node) { |
| 63 node.selfTime += time; | 72 node.selfTime += time; |
| 64 node.totalTime += time; | 73 node.totalTime += time; |
| 65 } else { | 74 } else { |
| 66 node = new WebInspector.TimelineProfileTree.Node(); | 75 node = new WebInspector.TimelineProfileTree.Node(); |
| 67 node.totalTime = time; | 76 node.totalTime = time; |
| 68 node.selfTime = time; | 77 node.selfTime = time; |
| 69 node.parent = parent; | 78 node.parent = parent; |
| 70 node.id = id; | 79 node.id = id; |
| 71 node.event = e; | 80 node.event = e; |
| 81 node._groupId = groupId; | |
| 72 parent.children.set(id, node); | 82 parent.children.set(id, node); |
| 73 } | 83 } |
| 74 parent.selfTime -= time; | 84 parent.selfTime -= time; |
| 75 if (parent.selfTime < 0) { | 85 if (parent.selfTime < 0) { |
| 76 console.log('Error: Negative self of ' + parent.selfTime, e); | 86 console.log('Error: Negative self of ' + parent.selfTime, e); |
| 77 parent.selfTime = 0; | 87 parent.selfTime = 0; |
| 78 } | 88 } |
| 79 if (e.endTime) | 89 if (e.endTime) |
| 80 parent = node; | 90 parent = node; |
| 81 } | 91 } |
| 82 | 92 |
| 83 /** | 93 /** |
| 84 * @param {!WebInspector.TracingModel.Event} e | 94 * @param {!WebInspector.TracingModel.Event} e |
| 85 */ | 95 */ |
| 86 function onEndEvent(e) { | 96 function onEndEvent(e) { |
| 97 pageFrameIdStack.pop(); | |
| 87 if (!WebInspector.TimelineModel.isVisible(filters, e)) | 98 if (!WebInspector.TimelineModel.isVisible(filters, e)) |
| 88 return; | 99 return; |
| 89 parent = parent.parent; | 100 parent = parent.parent; |
| 90 } | 101 } |
| 91 | 102 |
| 92 var instantEventCallback = eventIdCallback ? undefined : onStartEvent; // Ign ore instant events when aggregating. | 103 var instantEventCallback = eventGroupIdCallback ? undefined : onStartEvent; / / Ignore instant events when aggregating. |
| 93 WebInspector.TimelineModel.forEachEvent(events, onStartEvent, onEndEvent, inst antEventCallback, startTime, endTime); | 104 WebInspector.TimelineModel.forEachEvent(events, onStartEvent, onEndEvent, inst antEventCallback, startTime, endTime); |
| 94 root.totalTime -= root.selfTime; | 105 root.totalTime -= root.selfTime; |
| 95 root.selfTime = 0; | 106 root.selfTime = 0; |
| 96 return root; | 107 return root; |
| 97 }; | 108 }; |
| 98 | 109 |
| 99 /** | 110 /** |
| 100 * @param {!WebInspector.TimelineProfileTree.Node} topDownTree | 111 * @param {!WebInspector.TimelineProfileTree.Node} topDownTree |
| 101 * @param {?function(!WebInspector.TimelineProfileTree.Node):!WebInspector.Timel ineProfileTree.Node=} groupingCallback | |
| 102 * @return {!WebInspector.TimelineProfileTree.Node} | 112 * @return {!WebInspector.TimelineProfileTree.Node} |
| 103 */ | 113 */ |
| 104 WebInspector.TimelineProfileTree.buildBottomUp = function(topDownTree, groupingC allback) { | 114 WebInspector.TimelineProfileTree.buildBottomUp = function(topDownTree) { |
| 105 var buRoot = new WebInspector.TimelineProfileTree.Node(); | 115 var buRoot = new WebInspector.TimelineProfileTree.Node(); |
| 116 var aggregator = new WebInspector.TimelineAggregator(); | |
| 106 buRoot.selfTime = 0; | 117 buRoot.selfTime = 0; |
| 107 buRoot.totalTime = 0; | 118 buRoot.totalTime = 0; |
| 108 /** @type {!Map<string, !WebInspector.TimelineProfileTree.Node>} */ | 119 /** @type {!Map<string, !WebInspector.TimelineProfileTree.Node>} */ |
| 109 buRoot.children = new Map(); | 120 buRoot.children = new Map(); |
| 110 var nodesOnStack = /** @type {!Set<string>} */ (new Set()); | 121 var nodesOnStack = /** @type {!Set<string>} */ (new Set()); |
| 111 if (topDownTree.children) | 122 if (topDownTree.children) |
| 112 topDownTree.children.forEach(processNode); | 123 topDownTree.children.forEach(processNode); |
| 113 buRoot.totalTime = topDownTree.totalTime; | 124 buRoot.totalTime = topDownTree.totalTime; |
| 114 | 125 |
| 115 /** | 126 /** |
| 116 * @param {!WebInspector.TimelineProfileTree.Node} tdNode | 127 * @param {!WebInspector.TimelineProfileTree.Node} tdNode |
| 117 */ | 128 */ |
| 118 function processNode(tdNode) { | 129 function processNode(tdNode) { |
| 119 var buParent = groupingCallback && groupingCallback(tdNode) || buRoot; | 130 var buParent = typeof tdNode._groupId === 'string' ? aggregator.nodeForId(td Node._groupId, tdNode.event) : buRoot; |
| 120 if (buParent !== buRoot) { | 131 if (buParent !== buRoot && !buParent.parent) { |
| 121 buRoot.children.set(buParent.id, buParent); | 132 buRoot.children.set(buParent.id, buParent); |
| 122 buParent.parent = buRoot; | 133 buParent.parent = buRoot; |
| 123 } | 134 } |
| 124 appendNode(tdNode, buParent); | 135 appendNode(tdNode, buParent); |
| 125 var hadNode = nodesOnStack.has(tdNode.id); | 136 var hadNode = nodesOnStack.has(tdNode.id); |
| 126 if (!hadNode) | 137 if (!hadNode) |
| 127 nodesOnStack.add(tdNode.id); | 138 nodesOnStack.add(tdNode.id); |
| 128 if (tdNode.children) | 139 if (tdNode.children) |
| 129 tdNode.children.forEach(processNode); | 140 tdNode.children.forEach(processNode); |
| 130 if (!hadNode) | 141 if (!hadNode) |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 199 if (event.name === WebInspector.TimelineModel.RecordType.JSFrame) | 210 if (event.name === WebInspector.TimelineModel.RecordType.JSFrame) |
| 200 return /** @type {?Protocol.Runtime.CallFrame} */ (event.args['data'] || nul l); | 211 return /** @type {?Protocol.Runtime.CallFrame} */ (event.args['data'] || nul l); |
| 201 var topFrame = event.stackTrace && event.stackTrace[0]; | 212 var topFrame = event.stackTrace && event.stackTrace[0]; |
| 202 if (topFrame) | 213 if (topFrame) |
| 203 return /** @type {!Protocol.Runtime.CallFrame} */ (topFrame); | 214 return /** @type {!Protocol.Runtime.CallFrame} */ (topFrame); |
| 204 var initiator = event.initiator; | 215 var initiator = event.initiator; |
| 205 return /** @type {?Protocol.Runtime.CallFrame} */ (initiator && initiator.stac kTrace && initiator.stackTrace[0] || null); | 216 return /** @type {?Protocol.Runtime.CallFrame} */ (initiator && initiator.stac kTrace && initiator.stackTrace[0] || null); |
| 206 }; | 217 }; |
| 207 | 218 |
| 208 /** | 219 /** |
| 220 * @param {!WebInspector.TracingModel.Event} event | |
| 221 * @return {string} | |
| 222 */ | |
| 223 WebInspector.TimelineProfileTree._eventId = function(event) { | |
| 224 if (event.name === WebInspector.TimelineModel.RecordType.JSFrame) { | |
| 225 var data = event.args['data']; | |
| 226 return 'f:' + data['functionName'] + '@' + (data['scriptId'] || data['url'] || ''); | |
|
alph
2016/11/07 19:10:02
nit: template string.
| |
| 227 } | |
| 228 return event.name; | |
| 229 }; | |
| 230 | |
| 231 | |
| 232 /** | |
| 209 * @unrestricted | 233 * @unrestricted |
| 210 */ | 234 */ |
| 211 WebInspector.TimelineAggregator = class { | 235 WebInspector.TimelineAggregator = class { |
| 212 /** | 236 constructor() { |
| 213 * @param {function(!WebInspector.TracingModel.Event):string} titleMapper | |
| 214 * @param {function(!WebInspector.TracingModel.Event):string} categoryMapper | |
| 215 */ | |
| 216 constructor(titleMapper, categoryMapper) { | |
| 217 this._titleMapper = titleMapper; | |
| 218 this._categoryMapper = categoryMapper; | |
| 219 /** @type {!Map<string, !WebInspector.TimelineProfileTree.Node>} */ | 237 /** @type {!Map<string, !WebInspector.TimelineProfileTree.Node>} */ |
| 220 this._groupNodes = new Map(); | 238 this._groupNodes = new Map(); |
| 221 } | 239 } |
| 222 | 240 |
| 223 /** | 241 /** |
| 224 * @param {!WebInspector.TracingModel.Event} event | |
| 225 * @return {string} | |
| 226 */ | |
| 227 static eventId(event) { | |
| 228 if (event.name === WebInspector.TimelineModel.RecordType.JSFrame) { | |
| 229 var data = event.args['data']; | |
| 230 return 'f:' + data['functionName'] + '@' + (data['scriptId'] || data['url' ] || ''); | |
| 231 } | |
| 232 return event.name + ':@' + WebInspector.TimelineProfileTree.eventURL(event); | |
| 233 } | |
| 234 | |
| 235 /** | |
| 236 * @param {string} url | |
| 237 * @return {boolean} | |
| 238 */ | |
| 239 static isExtensionInternalURL(url) { | |
| 240 return url.startsWith(WebInspector.TimelineAggregator._extensionInternalPref ix); | |
| 241 } | |
| 242 | |
| 243 /** | |
| 244 * @param {!WebInspector.TimelineAggregator.GroupBy} groupBy | |
| 245 * @return {?function(!WebInspector.TimelineProfileTree.Node):!WebInspector.Ti melineProfileTree.Node} | |
| 246 */ | |
| 247 groupFunction(groupBy) { | |
| 248 var idMapper = this._nodeToGroupIdFunction(groupBy); | |
| 249 return idMapper && this._nodeToGroupNode.bind(this, idMapper); | |
| 250 } | |
| 251 | |
| 252 /** | |
| 253 * @param {!WebInspector.TimelineProfileTree.Node} root | 242 * @param {!WebInspector.TimelineProfileTree.Node} root |
| 254 * @param {!WebInspector.TimelineAggregator.GroupBy} groupBy | |
| 255 * @return {!WebInspector.TimelineProfileTree.Node} | 243 * @return {!WebInspector.TimelineProfileTree.Node} |
| 256 */ | 244 */ |
| 257 performGrouping(root, groupBy) { | 245 performGrouping(root) { |
| 258 var nodeMapper = this.groupFunction(groupBy); | |
| 259 if (!nodeMapper) | |
| 260 return root; | |
| 261 for (var node of root.children.values()) { | 246 for (var node of root.children.values()) { |
| 262 var groupNode = nodeMapper(node); | 247 var groupNode = this.nodeForId(node._groupId, node.event); |
| 263 groupNode.parent = root; | 248 groupNode.parent = root; |
| 264 groupNode.selfTime += node.selfTime; | 249 groupNode.selfTime += node.selfTime; |
| 265 groupNode.totalTime += node.totalTime; | 250 groupNode.totalTime += node.totalTime; |
| 266 groupNode.children.set(node.id, node); | 251 groupNode.children.set(node.id, node); |
| 267 node.parent = root; | 252 node.parent = root; |
| 268 } | 253 } |
| 269 root.children = this._groupNodes; | 254 root.children = this._groupNodes; |
| 270 return root; | 255 return root; |
| 271 } | 256 } |
| 272 | 257 |
| 273 /** | 258 /** |
| 274 * @param {!WebInspector.TimelineAggregator.GroupBy} groupBy | 259 * @param {string} id |
|
dgozman
2016/11/07 17:43:16
groupId
| |
| 275 * @return {?function(!WebInspector.TimelineProfileTree.Node):string} | 260 * @param {!WebInspector.TracingModel.Event} event |
| 261 * @return {!WebInspector.TimelineProfileTree.Node} | |
| 276 */ | 262 */ |
| 277 _nodeToGroupIdFunction(groupBy) { | 263 nodeForId(id, event) { |
|
dgozman
2016/11/07 17:43:16
groupNodeForId?
| |
| 278 /** | 264 var node = this._groupNodes.get(id); |
| 279 * @param {!WebInspector.TimelineProfileTree.Node} node | 265 return node || this._buildGroupNode(id, event); |
| 280 * @return {string} | |
| 281 */ | |
| 282 function groupByURL(node) { | |
| 283 return WebInspector.TimelineProfileTree.eventURL(node.event) || ''; | |
| 284 } | |
| 285 | |
| 286 /** | |
| 287 * @param {boolean} groupSubdomains | |
| 288 * @param {!WebInspector.TimelineProfileTree.Node} node | |
| 289 * @return {string} | |
| 290 */ | |
| 291 function groupByDomain(groupSubdomains, node) { | |
| 292 var url = WebInspector.TimelineProfileTree.eventURL(node.event) || ''; | |
| 293 if (WebInspector.TimelineAggregator.isExtensionInternalURL(url)) | |
| 294 return WebInspector.TimelineAggregator._extensionInternalPrefix; | |
| 295 var parsedURL = url.asParsedURL(); | |
| 296 if (!parsedURL) | |
| 297 return ''; | |
| 298 if (parsedURL.scheme === 'chrome-extension') | |
| 299 return parsedURL.scheme + '://' + parsedURL.host; | |
| 300 if (!groupSubdomains) | |
| 301 return parsedURL.host; | |
| 302 if (/^[.0-9]+$/.test(parsedURL.host)) | |
| 303 return parsedURL.host; | |
| 304 var domainMatch = /([^.]*\.)?[^.]*$/.exec(parsedURL.host); | |
| 305 return domainMatch && domainMatch[0] || ''; | |
| 306 } | |
| 307 | |
| 308 switch (groupBy) { | |
| 309 case WebInspector.TimelineAggregator.GroupBy.None: | |
| 310 return null; | |
| 311 case WebInspector.TimelineAggregator.GroupBy.EventName: | |
| 312 return node => node.event ? this._titleMapper(node.event) : ''; | |
| 313 case WebInspector.TimelineAggregator.GroupBy.Category: | |
| 314 return node => node.event ? this._categoryMapper(node.event) : ''; | |
| 315 case WebInspector.TimelineAggregator.GroupBy.Subdomain: | |
| 316 return groupByDomain.bind(null, false); | |
| 317 case WebInspector.TimelineAggregator.GroupBy.Domain: | |
| 318 return groupByDomain.bind(null, true); | |
| 319 case WebInspector.TimelineAggregator.GroupBy.URL: | |
| 320 return groupByURL; | |
| 321 default: | |
| 322 return null; | |
| 323 } | |
| 324 } | 266 } |
| 325 | 267 |
| 326 /** | 268 /** |
| 327 * @param {string} id | 269 * @param {string} id |
| 328 * @param {!WebInspector.TracingModel.Event} event | 270 * @param {!WebInspector.TracingModel.Event} event |
| 329 * @return {!WebInspector.TimelineProfileTree.Node} | 271 * @return {!WebInspector.TimelineProfileTree.Node} |
| 330 */ | 272 */ |
| 331 _buildGroupNode(id, event) { | 273 _buildGroupNode(id, event) { |
| 332 var groupNode = new WebInspector.TimelineProfileTree.Node(); | 274 var groupNode = new WebInspector.TimelineProfileTree.Node(); |
| 333 groupNode.id = id; | 275 groupNode.id = id; |
| 334 groupNode.selfTime = 0; | 276 groupNode.selfTime = 0; |
| 335 groupNode.totalTime = 0; | 277 groupNode.totalTime = 0; |
| 336 groupNode.children = new Map(); | 278 groupNode.children = new Map(); |
| 337 groupNode.event = event; | 279 groupNode.event = event; |
| 338 groupNode._isGroupNode = true; | 280 groupNode._isGroupNode = true; |
| 339 this._groupNodes.set(id, groupNode); | 281 this._groupNodes.set(id, groupNode); |
| 340 return groupNode; | 282 return groupNode; |
| 341 } | 283 } |
| 342 | |
| 343 /** | |
| 344 * @param {function(!WebInspector.TimelineProfileTree.Node):string} nodeToGrou pId | |
| 345 * @param {!WebInspector.TimelineProfileTree.Node} node | |
| 346 * @return {!WebInspector.TimelineProfileTree.Node} | |
| 347 */ | |
| 348 _nodeToGroupNode(nodeToGroupId, node) { | |
| 349 var id = nodeToGroupId(node); | |
| 350 return this._groupNodes.get(id) || this._buildGroupNode(id, node.event); | |
| 351 } | |
| 352 }; | 284 }; |
| 353 | 285 |
| 354 /** | |
| 355 * @enum {string} | |
| 356 */ | |
| 357 WebInspector.TimelineAggregator.GroupBy = { | |
| 358 None: 'None', | |
| 359 EventName: 'EventName', | |
| 360 Category: 'Category', | |
| 361 Domain: 'Domain', | |
| 362 Subdomain: 'Subdomain', | |
| 363 URL: 'URL' | |
| 364 }; | |
| 365 | |
| 366 | |
| 367 WebInspector.TimelineAggregator._extensionInternalPrefix = 'extensions::'; | |
| 368 WebInspector.TimelineAggregator._groupNodeFlag = Symbol('groupNode'); | 286 WebInspector.TimelineAggregator._groupNodeFlag = Symbol('groupNode'); |
| OLD | NEW |