UNPKG

strong-arc

Version:

A visual suite for the StrongLoop API Platform

483 lines (393 loc) 14.7 kB
/* * Copyright (C) 2009 280 North Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @constructor * @extends {WebInspector.DataGridNode} * @param {!ProfilerAgent.CPUProfileNode} profileNode * @param {!WebInspector.TopDownProfileDataGridTree} owningTree * @param {boolean} hasChildren */ WebInspector.ProfileDataGridNode = function(profileNode, owningTree, hasChildren) { this.profileNode = profileNode; WebInspector.DataGridNode.call(this, null, hasChildren); this.tree = owningTree; this.childrenByCallUID = {}; this.lastComparator = null; this.callUID = profileNode.callUID; this.selfTime = profileNode.selfTime; this.totalTime = profileNode.totalTime; this.functionName = WebInspector.CPUProfileDataModel.beautifyFunctionName(profileNode.functionName); this._deoptReason = (!profileNode.deoptReason || profileNode.deoptReason === "no reason") ? "" : profileNode.deoptReason; this.url = profileNode.url; } WebInspector.ProfileDataGridNode.prototype = { /** * @override * @param {string} columnIdentifier * @return {!Element} */ createCell: function(columnIdentifier) { var cell = this._createValueCell(columnIdentifier) || WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier); if (columnIdentifier === "self" && this._searchMatchedSelfColumn) cell.classList.add("highlight"); else if (columnIdentifier === "total" && this._searchMatchedTotalColumn) cell.classList.add("highlight"); if (columnIdentifier !== "function") return cell; if (this._deoptReason) cell.classList.add("not-optimized"); if (this._searchMatchedFunctionColumn) cell.classList.add("highlight"); if (this.profileNode.scriptId !== "0") { var lineNumber = this.profileNode.lineNumber ? this.profileNode.lineNumber - 1 : 0; var columnNumber = this.profileNode.columnNumber ? this.profileNode.columnNumber - 1 : 0; var target = this.tree.profileView.target(); var urlElement = this.tree.profileView._linkifier.linkifyScriptLocation(target, this.profileNode.scriptId, this.profileNode.url, lineNumber, columnNumber, "profile-node-file"); urlElement.style.maxWidth = "75%"; cell.insertBefore(urlElement, cell.firstChild); } return cell; }, /** * @param {string} columnIdentifier * @return {?Element} */ _createValueCell: function(columnIdentifier) { if (columnIdentifier !== "self" && columnIdentifier !== "total") return null; var cell = document.createElement("td"); cell.className = "numeric-column"; var div = document.createElement("div"); var valueSpan = document.createElement("span"); valueSpan.textContent = this.data[columnIdentifier]; div.appendChild(valueSpan); var percentColumn = columnIdentifier + "-percent"; if (percentColumn in this.data) { var percentSpan = document.createElement("span"); percentSpan.className = "percent-column"; percentSpan.textContent = this.data[percentColumn]; div.appendChild(percentSpan); div.classList.add("profile-multiple-values"); } cell.appendChild(div); return cell; }, buildData: function() { function formatMilliseconds(time) { return WebInspector.UIString("%.1f\u2009ms", time); } function formatPercent(value) { return WebInspector.UIString("%.2f\u2009%", value); } var functionName; if (this._deoptReason) { var content = document.createDocumentFragment(); var marker = content.createChild("span", "profile-warn-marker"); marker.title = WebInspector.UIString("Not optimized: %s", this._deoptReason); content.createTextChild(this.functionName); functionName = content; } else { functionName = this.functionName; } this.data = { "function": functionName, "self-percent": formatPercent(this.selfPercent), "self": formatMilliseconds(this.selfTime), "total-percent": formatPercent(this.totalPercent), "total": formatMilliseconds(this.totalTime), }; }, select: function(supressSelectedEvent) { WebInspector.DataGridNode.prototype.select.call(this, supressSelectedEvent); this.tree.profileView._dataGridNodeSelected(this); }, deselect: function(supressDeselectedEvent) { WebInspector.DataGridNode.prototype.deselect.call(this, supressDeselectedEvent); this.tree.profileView._dataGridNodeDeselected(this); }, /** * @param {function(!T, !T)} comparator * @param {boolean} force * @template T */ sort: function(comparator, force) { var gridNodeGroups = [[this]]; for (var gridNodeGroupIndex = 0; gridNodeGroupIndex < gridNodeGroups.length; ++gridNodeGroupIndex) { var gridNodes = gridNodeGroups[gridNodeGroupIndex]; var count = gridNodes.length; for (var index = 0; index < count; ++index) { var gridNode = gridNodes[index]; // If the grid node is collapsed, then don't sort children (save operation for later). // If the grid node has the same sorting as previously, then there is no point in sorting it again. if (!force && (!gridNode.expanded || gridNode.lastComparator === comparator)) { if (gridNode.children.length) gridNode.shouldRefreshChildren = true; continue; } gridNode.lastComparator = comparator; var children = gridNode.children; var childCount = children.length; if (childCount) { children.sort(comparator); for (var childIndex = 0; childIndex < childCount; ++childIndex) children[childIndex].recalculateSiblings(childIndex); gridNodeGroups.push(children); } } } }, /** * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode * @param {number} index */ insertChild: function(profileDataGridNode, index) { WebInspector.DataGridNode.prototype.insertChild.call(this, profileDataGridNode, index); this.childrenByCallUID[profileDataGridNode.callUID] = profileDataGridNode; }, /** * @param {!WebInspector.ProfileDataGridNode} profileDataGridNode */ removeChild: function(profileDataGridNode) { WebInspector.DataGridNode.prototype.removeChild.call(this, profileDataGridNode); delete this.childrenByCallUID[profileDataGridNode.callUID]; }, removeChildren: function() { WebInspector.DataGridNode.prototype.removeChildren.call(this); this.childrenByCallUID = {}; }, /** * @param {!WebInspector.ProfileDataGridNode} node * @return {?WebInspector.ProfileDataGridNode} */ findChild: function(node) { if (!node) return null; return this.childrenByCallUID[node.callUID]; }, get selfPercent() { return this.selfTime / this.tree.totalTime * 100.0; }, get totalPercent() { return this.totalTime / this.tree.totalTime * 100.0; }, populate: function() { WebInspector.ProfileDataGridNode.populate(this); }, /** * @protected */ populateChildren: function() { }, // When focusing and collapsing we modify lots of nodes in the tree. // This allows us to restore them all to their original state when we revert. save: function() { if (this._savedChildren) return; this._savedSelfTime = this.selfTime; this._savedTotalTime = this.totalTime; this._savedChildren = this.children.slice(); }, /** * When focusing and collapsing we modify lots of nodes in the tree. * This allows us to restore them all to their original state when we revert. * @protected */ restore: function() { if (!this._savedChildren) return; this.selfTime = this._savedSelfTime; this.totalTime = this._savedTotalTime; this.removeChildren(); var children = this._savedChildren; var count = children.length; for (var index = 0; index < count; ++index) { children[index].restore(); this.appendChild(children[index]); } }, /** * @param {!WebInspector.ProfileDataGridNode} child * @param {boolean} shouldAbsorb */ merge: function(child, shouldAbsorb) { WebInspector.ProfileDataGridNode.merge(this, child, shouldAbsorb); }, __proto__: WebInspector.DataGridNode.prototype } /** * @param {!WebInspector.ProfileDataGridNode|!WebInspector.ProfileDataGridTree} container * @param {!WebInspector.ProfileDataGridNode} child * @param {boolean} shouldAbsorb */ WebInspector.ProfileDataGridNode.merge = function(container, child, shouldAbsorb) { container.selfTime += child.selfTime; if (!shouldAbsorb) container.totalTime += child.totalTime; var children = container.children.slice(); container.removeChildren(); var count = children.length; for (var index = 0; index < count; ++index) { if (!shouldAbsorb || children[index] !== child) container.appendChild(children[index]); } children = child.children.slice(); count = children.length; for (var index = 0; index < count; ++index) { var orphanedChild = children[index]; var existingChild = container.childrenByCallUID[orphanedChild.callUID]; if (existingChild) existingChild.merge(orphanedChild, false); else container.appendChild(orphanedChild); } } /** * @param {!WebInspector.ProfileDataGridNode|!WebInspector.ProfileDataGridTree} container */ WebInspector.ProfileDataGridNode.populate = function(container) { if (container._populated) return; container._populated = true; container.populateChildren(); var currentComparator = container.tree.lastComparator; if (currentComparator) container.sort(currentComparator, true); } /** * @constructor * @param {!WebInspector.CPUProfileView} profileView * @param {!ProfilerAgent.CPUProfileNode} rootProfileNode */ WebInspector.ProfileDataGridTree = function(profileView, rootProfileNode) { this.tree = this; this.children = []; this.profileView = profileView; this.totalTime = rootProfileNode.totalTime; this.lastComparator = null; this.childrenByCallUID = {}; } WebInspector.ProfileDataGridTree.prototype = { get expanded() { return true; }, appendChild: function(child) { this.insertChild(child, this.children.length); }, insertChild: function(child, index) { this.children.splice(index, 0, child); this.childrenByCallUID[child.callUID] = child; }, removeChildren: function() { this.children = []; this.childrenByCallUID = {}; }, populateChildren: function() { }, findChild: WebInspector.ProfileDataGridNode.prototype.findChild, sort: WebInspector.ProfileDataGridNode.prototype.sort, /** * @protected */ save: function() { if (this._savedChildren) return; this._savedTotalTime = this.totalTime; this._savedChildren = this.children.slice(); }, restore: function() { if (!this._savedChildren) return; this.children = this._savedChildren; this.totalTime = this._savedTotalTime; var children = this.children; var count = children.length; for (var index = 0; index < count; ++index) children[index].restore(); this._savedChildren = null; } } WebInspector.ProfileDataGridTree.propertyComparators = [{}, {}]; /** * @param {string} property * @param {boolean} isAscending * @return {function(!Object.<string, *>, !Object.<string, *>)} */ WebInspector.ProfileDataGridTree.propertyComparator = function(property, isAscending) { var comparator = WebInspector.ProfileDataGridTree.propertyComparators[(isAscending ? 1 : 0)][property]; if (!comparator) { if (isAscending) { comparator = function(lhs, rhs) { if (lhs[property] < rhs[property]) return -1; if (lhs[property] > rhs[property]) return 1; return 0; } } else { comparator = function(lhs, rhs) { if (lhs[property] > rhs[property]) return -1; if (lhs[property] < rhs[property]) return 1; return 0; } } WebInspector.ProfileDataGridTree.propertyComparators[(isAscending ? 1 : 0)][property] = comparator; } return comparator; }