| /* |
| * Copyright (C) 2013 Google 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: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * 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. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "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 THE COPYRIGHT |
| * OWNER 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 |
| */ |
| WebInspector.AllocationProfile = function(profile) |
| { |
| this._strings = profile.strings; |
| |
| this._nextNodeId = 1; |
| this._idToFunctionInfo = {}; |
| this._idToNode = {}; |
| this._collapsedTopNodeIdToFunctionInfo = {}; |
| |
| this._traceTops = null; |
| |
| this._buildAllocationFunctionInfos(profile); |
| this._traceTree = this._buildInvertedAllocationTree(profile); |
| } |
| |
| WebInspector.AllocationProfile.prototype = { |
| _buildAllocationFunctionInfos: function(profile) |
| { |
| var strings = this._strings; |
| |
| var functionInfoFields = profile.snapshot.meta.trace_function_info_fields; |
| var functionIdOffset = functionInfoFields.indexOf("function_id"); |
| var functionNameOffset = functionInfoFields.indexOf("name"); |
| var scriptNameOffset = functionInfoFields.indexOf("script_name"); |
| var scriptIdOffset = functionInfoFields.indexOf("script_id"); |
| var lineOffset = functionInfoFields.indexOf("line"); |
| var columnOffset = functionInfoFields.indexOf("column"); |
| var functionInfoFieldCount = functionInfoFields.length; |
| |
| var map = this._idToFunctionInfo; |
| |
| // Special case for the root node. |
| map[0] = new WebInspector.FunctionAllocationInfo("(root)", "<unknown>", 0, -1, -1); |
| |
| var rawInfos = profile.trace_function_infos; |
| var infoLength = rawInfos.length; |
| for (var i = 0; i < infoLength; i += functionInfoFieldCount) { |
| map[rawInfos[i + functionIdOffset]] = new WebInspector.FunctionAllocationInfo( |
| strings[rawInfos[i + functionNameOffset]], |
| strings[rawInfos[i + scriptNameOffset]], |
| rawInfos[i + scriptIdOffset], |
| rawInfos[i + lineOffset], |
| rawInfos[i + columnOffset]); |
| } |
| }, |
| |
| _buildInvertedAllocationTree: function(profile) |
| { |
| var traceTreeRaw = profile.trace_tree; |
| var idToFunctionInfo = this._idToFunctionInfo; |
| |
| var traceNodeFields = profile.snapshot.meta.trace_node_fields; |
| var nodeIdOffset = traceNodeFields.indexOf("id"); |
| var functionIdOffset = traceNodeFields.indexOf("function_id"); |
| var allocationCountOffset = traceNodeFields.indexOf("count"); |
| var allocationSizeOffset = traceNodeFields.indexOf("size"); |
| var childrenOffset = traceNodeFields.indexOf("children"); |
| var nodeFieldCount = traceNodeFields.length; |
| |
| function traverseNode(rawNodeArray, nodeOffset, parent) |
| { |
| var functionInfo = idToFunctionInfo[rawNodeArray[nodeOffset + functionIdOffset]]; |
| var result = new WebInspector.AllocationTraceNode( |
| rawNodeArray[nodeOffset + nodeIdOffset], |
| functionInfo, |
| rawNodeArray[nodeOffset + allocationCountOffset], |
| rawNodeArray[nodeOffset + allocationSizeOffset], |
| parent); |
| functionInfo.addTraceTopNode(result); |
| |
| var rawChildren = rawNodeArray[nodeOffset + childrenOffset]; |
| for (var i = 0; i < rawChildren.length; i += nodeFieldCount) { |
| result.children.push(traverseNode(rawChildren, i, result)); |
| } |
| return result; |
| } |
| |
| return traverseNode(traceTreeRaw, 0, null); |
| }, |
| |
| serializeTraceTops: function() |
| { |
| if (this._traceTops) |
| return this._traceTops; |
| var result = this._traceTops = []; |
| var idToFunctionInfo = this._idToFunctionInfo; |
| for (var id in idToFunctionInfo) { |
| var info = idToFunctionInfo[id]; |
| if (info.totalCount === 0) |
| continue; |
| var nodeId = this._nextNodeId++; |
| result.push(this._serializeNode( |
| nodeId, |
| info, |
| info.totalCount, |
| info.totalSize, |
| true)); |
| this._collapsedTopNodeIdToFunctionInfo[nodeId] = info; |
| } |
| result.sort(function(a, b) { |
| return b.size - a.size; |
| }); |
| return result; |
| }, |
| |
| serializeCallers: function(nodeId) |
| { |
| var node = this._idToNode[nodeId]; |
| if (!node) { |
| var functionInfo = this._collapsedTopNodeIdToFunctionInfo[nodeId]; |
| node = functionInfo.tracesWithThisTop(); |
| delete this._collapsedTopNodeIdToFunctionInfo[nodeId]; |
| this._idToNode[nodeId] = node; |
| } |
| |
| var nodesWithSingleCaller = []; |
| while (node.callers().length === 1) { |
| node = node.callers()[0]; |
| nodesWithSingleCaller.push(this._serializeCaller(node)); |
| } |
| |
| var branchingCallers = []; |
| var callers = node.callers(); |
| for (var i = 0; i < callers.length; i++) { |
| branchingCallers.push(this._serializeCaller(callers[i])); |
| } |
| return { |
| nodesWithSingleCaller: nodesWithSingleCaller, |
| branchingCallers: branchingCallers |
| }; |
| }, |
| |
| _serializeCaller: function(node) |
| { |
| var callerId = this._nextNodeId++; |
| this._idToNode[callerId] = node; |
| return this._serializeNode( |
| callerId, |
| node.functionInfo, |
| node.allocationCount, |
| node.allocationSize, |
| node.hasCallers()); |
| }, |
| |
| _serializeNode: function(nodeId, functionInfo, count, size, hasChildren) |
| { |
| return { |
| id: nodeId, |
| name: functionInfo.functionName, |
| scriptName: functionInfo.scriptName, |
| line: functionInfo.line, |
| column: functionInfo.column, |
| count: count, |
| size: size, |
| hasChildren: hasChildren |
| }; |
| } |
| } |
| |
| |
| /** |
| * @constructor |
| */ |
| WebInspector.AllocationTraceNode = function(id, functionInfo, count, size, parent) |
| { |
| this.id = id; |
| this.functionInfo = functionInfo; |
| this.allocationCount = count; |
| this.allocationSize = size; |
| this.parent = parent; |
| this.children = []; |
| } |
| |
| |
| /** |
| * @constructor |
| * @param {WebInspector.FunctionAllocationInfo} functionInfo |
| */ |
| WebInspector.AllocationBackTraceNode = function(functionInfo) |
| { |
| this.functionInfo = functionInfo; |
| this.allocationCount = 0; |
| this.allocationSize = 0; |
| this._callers = []; |
| } |
| |
| |
| WebInspector.AllocationBackTraceNode.prototype = { |
| /** |
| * @param {WebInspector.AllocationTraceNode} traceNode |
| * @return {WebInspector.AllocationTraceNode} |
| */ |
| addCaller: function(traceNode) |
| { |
| var functionInfo = traceNode.functionInfo; |
| var result; |
| for (var i = 0; i < this._callers.length; i++) { |
| var caller = this._callers[i]; |
| if (caller.functionInfo === functionInfo) { |
| result = caller; |
| break; |
| } |
| } |
| if (!result) { |
| result = new WebInspector.AllocationBackTraceNode(functionInfo); |
| this._callers.push(result); |
| } |
| return result; |
| }, |
| |
| callers: function() |
| { |
| return this._callers; |
| }, |
| |
| hasCallers: function() |
| { |
| return this._callers.length > 0; |
| } |
| } |
| |
| |
| /** |
| * @constructor |
| */ |
| WebInspector.FunctionAllocationInfo = function(functionName, scriptName, scriptId, line, column) |
| { |
| this.functionName = functionName; |
| this.scriptName = scriptName; |
| this.scriptId = scriptId; |
| this.line = line; |
| this.column = column; |
| this.totalCount = 0; |
| this.totalSize = 0; |
| this._traceTops = []; |
| } |
| |
| WebInspector.FunctionAllocationInfo.prototype = { |
| addTraceTopNode: function(node) |
| { |
| if (node.allocationCount === 0) |
| return; |
| this._traceTops.push(node); |
| this.totalCount += node.allocationCount; |
| this.totalSize += node.allocationSize; |
| }, |
| |
| tracesWithThisTop: function() |
| { |
| if (!this._traceTops.length) |
| return null; |
| if (!this._backTraceTree) |
| this._buildAllocationTraceTree(); |
| return this._backTraceTree; |
| }, |
| |
| _buildAllocationTraceTree: function() |
| { |
| this._backTraceTree = new WebInspector.AllocationBackTraceNode(this._traceTops[0].functionInfo); |
| |
| for (var i = 0; i < this._traceTops.length; i++) { |
| var node = this._traceTops[i]; |
| var backTraceNode = this._backTraceTree; |
| var count = node.allocationCount; |
| var size = node.allocationSize; |
| while (true) { |
| backTraceNode.allocationCount += count; |
| backTraceNode.allocationSize += size; |
| node = node.parent; |
| if (node === null) { |
| break; |
| } |
| backTraceNode = backTraceNode.addCaller(node); |
| } |
| } |
| } |
| } |