2 * Copyright (C) 2009 280 North Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 * @extends {WebInspector.DataGridNode}
29 * @param {!ProfilerAgent.CPUProfileNode} profileNode
30 * @param {!WebInspector.TopDownProfileDataGridTree} owningTree
31 * @param {boolean} hasChildren
33 WebInspector.ProfileDataGridNode = function(profileNode, owningTree, hasChildren)
35 this.profileNode = profileNode;
37 WebInspector.DataGridNode.call(this, null, hasChildren);
39 this.tree = owningTree;
41 this.childrenByCallUID = {};
42 this.lastComparator = null;
44 this.callUID = profileNode.callUID;
45 this.selfTime = profileNode.selfTime;
46 this.totalTime = profileNode.totalTime;
47 this.functionName = WebInspector.CPUProfileDataModel.beautifyFunctionName(profileNode.functionName);
48 this._deoptReason = (!profileNode.deoptReason || profileNode.deoptReason === "no reason") ? "" : profileNode.deoptReason;
49 this.url = profileNode.url;
52 WebInspector.ProfileDataGridNode.prototype = {
55 * @param {string} columnIdentifier
58 createCell: function(columnIdentifier)
60 var cell = this._createValueCell(columnIdentifier) || WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
62 if (columnIdentifier === "self" && this._searchMatchedSelfColumn)
63 cell.classList.add("highlight");
64 else if (columnIdentifier === "total" && this._searchMatchedTotalColumn)
65 cell.classList.add("highlight");
67 if (columnIdentifier !== "function")
70 if (this._deoptReason)
71 cell.classList.add("not-optimized");
73 if (this._searchMatchedFunctionColumn)
74 cell.classList.add("highlight");
76 if (this.profileNode.scriptId !== "0") {
77 var lineNumber = this.profileNode.lineNumber ? this.profileNode.lineNumber - 1 : 0;
78 var columnNumber = this.profileNode.columnNumber ? this.profileNode.columnNumber - 1 : 0;
79 var target = this.tree.profileView.target();
80 var urlElement = this.tree.profileView._linkifier.linkifyScriptLocation(target, this.profileNode.scriptId, this.profileNode.url, lineNumber, columnNumber, "profile-node-file");
81 urlElement.style.maxWidth = "75%";
82 cell.insertBefore(urlElement, cell.firstChild);
89 * @param {string} columnIdentifier
92 _createValueCell: function(columnIdentifier)
94 if (columnIdentifier !== "self" && columnIdentifier !== "total")
97 var cell = document.createElement("td");
98 cell.className = "numeric-column";
99 var div = document.createElement("div");
100 var valueSpan = document.createElement("span");
101 valueSpan.textContent = this.data[columnIdentifier];
102 div.appendChild(valueSpan);
103 var percentColumn = columnIdentifier + "-percent";
104 if (percentColumn in this.data) {
105 var percentSpan = document.createElement("span");
106 percentSpan.className = "percent-column";
107 percentSpan.textContent = this.data[percentColumn];
108 div.appendChild(percentSpan);
109 div.classList.add("profile-multiple-values");
111 cell.appendChild(div);
115 buildData: function()
117 function formatMilliseconds(time)
119 return WebInspector.UIString("%.1f\u2009ms", time);
121 function formatPercent(value)
123 return WebInspector.UIString("%.2f\u2009%", value);
127 if (this._deoptReason) {
128 var content = document.createDocumentFragment();
129 var marker = content.createChild("span", "profile-warn-marker");
130 marker.title = WebInspector.UIString("Not optimized: %s", this._deoptReason);
131 content.createTextChild(this.functionName);
132 functionName = content;
134 functionName = this.functionName;
138 "function": functionName,
139 "self-percent": formatPercent(this.selfPercent),
140 "self": formatMilliseconds(this.selfTime),
141 "total-percent": formatPercent(this.totalPercent),
142 "total": formatMilliseconds(this.totalTime),
146 select: function(supressSelectedEvent)
148 WebInspector.DataGridNode.prototype.select.call(this, supressSelectedEvent);
149 this.tree.profileView._dataGridNodeSelected(this);
152 deselect: function(supressDeselectedEvent)
154 WebInspector.DataGridNode.prototype.deselect.call(this, supressDeselectedEvent);
155 this.tree.profileView._dataGridNodeDeselected(this);
159 * @param {function(!T, !T)} comparator
160 * @param {boolean} force
163 sort: function(comparator, force)
165 var gridNodeGroups = [[this]];
167 for (var gridNodeGroupIndex = 0; gridNodeGroupIndex < gridNodeGroups.length; ++gridNodeGroupIndex) {
168 var gridNodes = gridNodeGroups[gridNodeGroupIndex];
169 var count = gridNodes.length;
171 for (var index = 0; index < count; ++index) {
172 var gridNode = gridNodes[index];
174 // If the grid node is collapsed, then don't sort children (save operation for later).
175 // If the grid node has the same sorting as previously, then there is no point in sorting it again.
176 if (!force && (!gridNode.expanded || gridNode.lastComparator === comparator)) {
177 if (gridNode.children.length)
178 gridNode.shouldRefreshChildren = true;
182 gridNode.lastComparator = comparator;
184 var children = gridNode.children;
185 var childCount = children.length;
188 children.sort(comparator);
190 for (var childIndex = 0; childIndex < childCount; ++childIndex)
191 children[childIndex].recalculateSiblings(childIndex);
193 gridNodeGroups.push(children);
200 * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
201 * @param {number} index
203 insertChild: function(profileDataGridNode, index)
205 WebInspector.DataGridNode.prototype.insertChild.call(this, profileDataGridNode, index);
207 this.childrenByCallUID[profileDataGridNode.callUID] = profileDataGridNode;
211 * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
213 removeChild: function(profileDataGridNode)
215 WebInspector.DataGridNode.prototype.removeChild.call(this, profileDataGridNode);
217 delete this.childrenByCallUID[profileDataGridNode.callUID];
220 removeChildren: function()
222 WebInspector.DataGridNode.prototype.removeChildren.call(this);
224 this.childrenByCallUID = {};
228 * @param {!WebInspector.ProfileDataGridNode} node
229 * @return {?WebInspector.ProfileDataGridNode}
231 findChild: function(node)
235 return this.childrenByCallUID[node.callUID];
240 return this.selfTime / this.tree.totalTime * 100.0;
245 return this.totalTime / this.tree.totalTime * 100.0;
250 WebInspector.ProfileDataGridNode.populate(this);
256 populateChildren: function()
260 // When focusing and collapsing we modify lots of nodes in the tree.
261 // This allows us to restore them all to their original state when we revert.
265 if (this._savedChildren)
268 this._savedSelfTime = this.selfTime;
269 this._savedTotalTime = this.totalTime;
271 this._savedChildren = this.children.slice();
275 * When focusing and collapsing we modify lots of nodes in the tree.
276 * This allows us to restore them all to their original state when we revert.
281 if (!this._savedChildren)
284 this.selfTime = this._savedSelfTime;
285 this.totalTime = this._savedTotalTime;
287 this.removeChildren();
289 var children = this._savedChildren;
290 var count = children.length;
292 for (var index = 0; index < count; ++index) {
293 children[index].restore();
294 this.appendChild(children[index]);
299 * @param {!WebInspector.ProfileDataGridNode} child
300 * @param {boolean} shouldAbsorb
302 merge: function(child, shouldAbsorb)
304 WebInspector.ProfileDataGridNode.merge(this, child, shouldAbsorb);
307 __proto__: WebInspector.DataGridNode.prototype
311 * @param {!WebInspector.ProfileDataGridNode|!WebInspector.ProfileDataGridTree} container
312 * @param {!WebInspector.ProfileDataGridNode} child
313 * @param {boolean} shouldAbsorb
315 WebInspector.ProfileDataGridNode.merge = function(container, child, shouldAbsorb)
317 container.selfTime += child.selfTime;
320 container.totalTime += child.totalTime;
322 var children = container.children.slice();
324 container.removeChildren();
326 var count = children.length;
328 for (var index = 0; index < count; ++index) {
329 if (!shouldAbsorb || children[index] !== child)
330 container.appendChild(children[index]);
333 children = child.children.slice();
334 count = children.length;
336 for (var index = 0; index < count; ++index) {
337 var orphanedChild = children[index];
338 var existingChild = container.childrenByCallUID[orphanedChild.callUID];
341 existingChild.merge(orphanedChild, false);
343 container.appendChild(orphanedChild);
348 * @param {!WebInspector.ProfileDataGridNode|!WebInspector.ProfileDataGridTree} container
350 WebInspector.ProfileDataGridNode.populate = function(container)
352 if (container._populated)
354 container._populated = true;
356 container.populateChildren();
358 var currentComparator = container.tree.lastComparator;
360 if (currentComparator)
361 container.sort(currentComparator, true);
366 * @param {!WebInspector.CPUProfileView} profileView
367 * @param {!ProfilerAgent.CPUProfileNode} rootProfileNode
369 WebInspector.ProfileDataGridTree = function(profileView, rootProfileNode)
374 this.profileView = profileView;
376 this.totalTime = rootProfileNode.totalTime;
377 this.lastComparator = null;
379 this.childrenByCallUID = {};
382 WebInspector.ProfileDataGridTree.prototype = {
388 appendChild: function(child)
390 this.insertChild(child, this.children.length);
393 insertChild: function(child, index)
395 this.children.splice(index, 0, child);
396 this.childrenByCallUID[child.callUID] = child;
399 removeChildren: function()
402 this.childrenByCallUID = {};
405 populateChildren: function()
409 findChild: WebInspector.ProfileDataGridNode.prototype.findChild,
410 sort: WebInspector.ProfileDataGridNode.prototype.sort,
417 if (this._savedChildren)
420 this._savedTotalTime = this.totalTime;
421 this._savedChildren = this.children.slice();
426 if (!this._savedChildren)
429 this.children = this._savedChildren;
430 this.totalTime = this._savedTotalTime;
432 var children = this.children;
433 var count = children.length;
435 for (var index = 0; index < count; ++index)
436 children[index].restore();
438 this._savedChildren = null;
442 WebInspector.ProfileDataGridTree.propertyComparators = [{}, {}];
445 * @param {string} property
446 * @param {boolean} isAscending
447 * @return {function(!Object.<string, *>, !Object.<string, *>)}
449 WebInspector.ProfileDataGridTree.propertyComparator = function(property, isAscending)
451 var comparator = WebInspector.ProfileDataGridTree.propertyComparators[(isAscending ? 1 : 0)][property];
455 comparator = function(lhs, rhs)
457 if (lhs[property] < rhs[property])
460 if (lhs[property] > rhs[property])
466 comparator = function(lhs, rhs)
468 if (lhs[property] > rhs[property])
471 if (lhs[property] < rhs[property])
478 WebInspector.ProfileDataGridTree.propertyComparators[(isAscending ? 1 : 0)][property] = comparator;