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 = profileNode.functionName;
48 this._deoptReason = (!profileNode.deoptReason || profileNode.deoptReason === "no reason") ? "" : profileNode.deoptReason;
49 this.url = profileNode.url;
52 WebInspector.ProfileDataGridNode.prototype = {
55 function formatMilliseconds(time)
57 return WebInspector.UIString("%.1f\u2009ms", time);
62 if (this._deoptReason) {
63 var content = document.createDocumentFragment();
64 var marker = content.createChild("span", "profile-warn-marker");
65 marker.title = WebInspector.UIString("Not optimized: %s", this._deoptReason);
66 content.createTextChild(this.functionName);
67 data["function"] = content;
69 data["function"] = this.functionName;
71 if (this.tree.profileView.showSelfTimeAsPercent.get())
72 data["self"] = WebInspector.UIString("%.2f%", this.selfPercent);
74 data["self"] = formatMilliseconds(this.selfTime);
76 if (this.tree.profileView.showTotalTimeAsPercent.get())
77 data["total"] = WebInspector.UIString("%.2f%", this.totalPercent);
79 data["total"] = formatMilliseconds(this.totalTime);
86 * @param {string} columnIdentifier
89 createCell: function(columnIdentifier)
91 var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
93 if (columnIdentifier === "self" && this._searchMatchedSelfColumn)
94 cell.classList.add("highlight");
95 else if (columnIdentifier === "total" && this._searchMatchedTotalColumn)
96 cell.classList.add("highlight");
98 if (columnIdentifier !== "function")
101 if (this._deoptReason)
102 cell.classList.add("not-optimized");
104 if (this.profileNode._searchMatchedFunctionColumn)
105 cell.classList.add("highlight");
107 if (this.profileNode.scriptId !== "0") {
108 var lineNumber = this.profileNode.lineNumber ? this.profileNode.lineNumber - 1 : 0;
109 var columnNumber = this.profileNode.columnNumber ? this.profileNode.columnNumber - 1 : 0;
110 var location = new WebInspector.DebuggerModel.Location(this.profileNode.scriptId, lineNumber, columnNumber);
111 var urlElement = this.tree.profileView._linkifier.linkifyRawLocation(location, "profile-node-file");
113 urlElement = this.tree.profileView._linkifier.linkifyLocation(this.profileNode.url, lineNumber, columnNumber, "profile-node-file");
114 urlElement.style.maxWidth = "75%";
115 cell.insertBefore(urlElement, cell.firstChild);
121 select: function(supressSelectedEvent)
123 WebInspector.DataGridNode.prototype.select.call(this, supressSelectedEvent);
124 this.tree.profileView._dataGridNodeSelected(this);
127 deselect: function(supressDeselectedEvent)
129 WebInspector.DataGridNode.prototype.deselect.call(this, supressDeselectedEvent);
130 this.tree.profileView._dataGridNodeDeselected(this);
134 * @param {function(!T, !T)} comparator
135 * @param {boolean} force
138 sort: function(comparator, force)
140 var gridNodeGroups = [[this]];
142 for (var gridNodeGroupIndex = 0; gridNodeGroupIndex < gridNodeGroups.length; ++gridNodeGroupIndex) {
143 var gridNodes = gridNodeGroups[gridNodeGroupIndex];
144 var count = gridNodes.length;
146 for (var index = 0; index < count; ++index) {
147 var gridNode = gridNodes[index];
149 // If the grid node is collapsed, then don't sort children (save operation for later).
150 // If the grid node has the same sorting as previously, then there is no point in sorting it again.
151 if (!force && (!gridNode.expanded || gridNode.lastComparator === comparator)) {
152 if (gridNode.children.length)
153 gridNode.shouldRefreshChildren = true;
157 gridNode.lastComparator = comparator;
159 var children = gridNode.children;
160 var childCount = children.length;
163 children.sort(comparator);
165 for (var childIndex = 0; childIndex < childCount; ++childIndex)
166 children[childIndex]._recalculateSiblings(childIndex);
168 gridNodeGroups.push(children);
175 * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
176 * @param {number} index
178 insertChild: function(profileDataGridNode, index)
180 WebInspector.DataGridNode.prototype.insertChild.call(this, profileDataGridNode, index);
182 this.childrenByCallUID[profileDataGridNode.callUID] = profileDataGridNode;
186 * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode
188 removeChild: function(profileDataGridNode)
190 WebInspector.DataGridNode.prototype.removeChild.call(this, profileDataGridNode);
192 delete this.childrenByCallUID[profileDataGridNode.callUID];
195 removeChildren: function()
197 WebInspector.DataGridNode.prototype.removeChildren.call(this);
199 this.childrenByCallUID = {};
203 * @param {!WebInspector.ProfileDataGridNode} node
204 * @return {?WebInspector.ProfileDataGridNode}
206 findChild: function(node)
210 return this.childrenByCallUID[node.callUID];
215 return this.selfTime / this.tree.totalTime * 100.0;
220 return this.totalTime / this.tree.totalTime * 100.0;
225 return this.parent !== this.dataGrid ? this.parent : this.tree;
232 this._populated = true;
234 this._sharedPopulate();
236 var currentComparator = this.tree.lastComparator;
238 if (currentComparator)
239 this.sort(currentComparator, true);
242 // When focusing and collapsing we modify lots of nodes in the tree.
243 // This allows us to restore them all to their original state when we revert.
246 if (this._savedChildren)
249 this._savedSelfTime = this.selfTime;
250 this._savedTotalTime = this.totalTime;
252 this._savedChildren = this.children.slice();
255 // When focusing and collapsing we modify lots of nodes in the tree.
256 // This allows us to restore them all to their original state when we revert.
259 if (!this._savedChildren)
262 this.selfTime = this._savedSelfTime;
263 this.totalTime = this._savedTotalTime;
265 this.removeChildren();
267 var children = this._savedChildren;
268 var count = children.length;
270 for (var index = 0; index < count; ++index) {
271 children[index]._restore();
272 this.appendChild(children[index]);
276 _merge: function(child, shouldAbsorb)
278 this.selfTime += child.selfTime;
281 this.totalTime += child.totalTime;
283 var children = this.children.slice();
285 this.removeChildren();
287 var count = children.length;
289 for (var index = 0; index < count; ++index) {
290 if (!shouldAbsorb || children[index] !== child)
291 this.appendChild(children[index]);
294 children = child.children.slice();
295 count = children.length;
297 for (var index = 0; index < count; ++index) {
298 var orphanedChild = children[index],
299 existingChild = this.childrenByCallUID[orphanedChild.callUID];
302 existingChild._merge(orphanedChild, false);
304 this.appendChild(orphanedChild);
308 __proto__: WebInspector.DataGridNode.prototype
313 * @param {!WebInspector.CPUProfileView} profileView
314 * @param {!ProfilerAgent.CPUProfileNode} rootProfileNode
316 WebInspector.ProfileDataGridTree = function(profileView, rootProfileNode)
321 this.profileView = profileView;
323 this.totalTime = rootProfileNode.totalTime;
324 this.lastComparator = null;
326 this.childrenByCallUID = {};
329 WebInspector.ProfileDataGridTree.prototype = {
335 appendChild: function(child)
337 this.insertChild(child, this.children.length);
340 insertChild: function(child, index)
342 this.children.splice(index, 0, child);
343 this.childrenByCallUID[child.callUID] = child;
346 removeChildren: function()
349 this.childrenByCallUID = {};
352 findChild: WebInspector.ProfileDataGridNode.prototype.findChild,
353 sort: WebInspector.ProfileDataGridNode.prototype.sort,
357 if (this._savedChildren)
360 this._savedTotalTime = this.totalTime;
361 this._savedChildren = this.children.slice();
366 if (!this._savedChildren)
369 this.children = this._savedChildren;
370 this.totalTime = this._savedTotalTime;
372 var children = this.children;
373 var count = children.length;
375 for (var index = 0; index < count; ++index)
376 children[index]._restore();
378 this._savedChildren = null;
382 WebInspector.ProfileDataGridTree.propertyComparators = [{}, {}];
385 * @param {string} property
386 * @param {boolean} isAscending
387 * @return {function(!Object.<string, *>, !Object.<string, *>)}
389 WebInspector.ProfileDataGridTree.propertyComparator = function(property, isAscending)
391 var comparator = WebInspector.ProfileDataGridTree.propertyComparators[(isAscending ? 1 : 0)][property];
395 comparator = function(lhs, rhs)
397 if (lhs[property] < rhs[property])
400 if (lhs[property] > rhs[property])
406 comparator = function(lhs, rhs)
408 if (lhs[property] > rhs[property])
411 if (lhs[property] < rhs[property])
418 WebInspector.ProfileDataGridTree.propertyComparators[(isAscending ? 1 : 0)][property] = comparator;