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

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineProfileTree.js

Issue 2473283003: DevTools: add per-frame grouping into aggregated timeline details (Closed)
Patch Set: Created 4 years, 1 month 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 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
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');
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698