2 * Copyright (C) 2013 Google 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 are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 WebInspector.AllocationProfile = function(profile, liveObjectStats)
36 this._strings = profile.strings;
37 this._liveObjectStats = liveObjectStats;
40 this._functionInfos = []
42 this._idToTopDownNode = {};
43 this._collapsedTopNodeIdToFunctionInfo = {};
45 this._traceTops = null;
47 this._buildFunctionAllocationInfos(profile);
48 this._traceTree = this._buildAllocationTree(profile, liveObjectStats);
51 WebInspector.AllocationProfile.prototype = {
52 _buildFunctionAllocationInfos: function(profile)
54 var strings = this._strings;
56 var functionInfoFields = profile.snapshot.meta.trace_function_info_fields;
57 var functionIdOffset = functionInfoFields.indexOf("function_id");
58 var functionNameOffset = functionInfoFields.indexOf("name");
59 var scriptNameOffset = functionInfoFields.indexOf("script_name");
60 var scriptIdOffset = functionInfoFields.indexOf("script_id");
61 var lineOffset = functionInfoFields.indexOf("line");
62 var columnOffset = functionInfoFields.indexOf("column");
63 var functionInfoFieldCount = functionInfoFields.length;
65 var rawInfos = profile.trace_function_infos;
66 var infoLength = rawInfos.length;
67 var functionInfos = this._functionInfos = new Array(infoLength / functionInfoFieldCount);
69 for (var i = 0; i < infoLength; i += functionInfoFieldCount) {
70 functionInfos[index++] = new WebInspector.FunctionAllocationInfo(
71 strings[rawInfos[i + functionNameOffset]],
72 strings[rawInfos[i + scriptNameOffset]],
73 rawInfos[i + scriptIdOffset],
74 rawInfos[i + lineOffset],
75 rawInfos[i + columnOffset]);
79 _buildAllocationTree: function(profile, liveObjectStats)
81 var traceTreeRaw = profile.trace_tree;
82 var functionInfos = this._functionInfos;
83 var idToTopDownNode = this._idToTopDownNode;
85 var traceNodeFields = profile.snapshot.meta.trace_node_fields;
86 var nodeIdOffset = traceNodeFields.indexOf("id");
87 var functionInfoIndexOffset = traceNodeFields.indexOf("function_info_index");
88 var allocationCountOffset = traceNodeFields.indexOf("count");
89 var allocationSizeOffset = traceNodeFields.indexOf("size");
90 var childrenOffset = traceNodeFields.indexOf("children");
91 var nodeFieldCount = traceNodeFields.length;
93 function traverseNode(rawNodeArray, nodeOffset, parent)
95 var functionInfo = functionInfos[rawNodeArray[nodeOffset + functionInfoIndexOffset]];
96 var id = rawNodeArray[nodeOffset + nodeIdOffset];
97 var stats = liveObjectStats[id];
98 var liveCount = stats ? stats.count : 0;
99 var liveSize = stats ? stats.size : 0;
100 var result = new WebInspector.TopDownAllocationNode(
103 rawNodeArray[nodeOffset + allocationCountOffset],
104 rawNodeArray[nodeOffset + allocationSizeOffset],
108 idToTopDownNode[id] = result;
109 functionInfo.addTraceTopNode(result);
111 var rawChildren = rawNodeArray[nodeOffset + childrenOffset];
112 for (var i = 0; i < rawChildren.length; i += nodeFieldCount) {
113 result.children.push(traverseNode(rawChildren, i, result));
118 return traverseNode(traceTreeRaw, 0, null);
122 * @return {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>}
124 serializeTraceTops: function()
127 return this._traceTops;
128 var result = this._traceTops = [];
129 var functionInfos = this._functionInfos;
130 for (var i = 0; i < functionInfos.length; i++) {
131 var info = functionInfos[i];
132 if (info.totalCount === 0)
134 var nodeId = this._nextNodeId++;
136 result.push(this._serializeNode(
144 this._collapsedTopNodeIdToFunctionInfo[nodeId] = info;
146 result.sort(function(a, b) {
147 return b.size - a.size;
153 * @param {number} nodeId
154 * @return {!WebInspector.HeapSnapshotCommon.AllocationNodeCallers}
156 serializeCallers: function(nodeId)
158 var node = this._ensureBottomUpNode(nodeId);
159 var nodesWithSingleCaller = [];
160 while (node.callers().length === 1) {
161 node = node.callers()[0];
162 nodesWithSingleCaller.push(this._serializeCaller(node));
165 var branchingCallers = [];
166 var callers = node.callers();
167 for (var i = 0; i < callers.length; i++) {
168 branchingCallers.push(this._serializeCaller(callers[i]));
170 return new WebInspector.HeapSnapshotCommon.AllocationNodeCallers(nodesWithSingleCaller, branchingCallers);
174 * @param {number} traceNodeId
175 * @return {!Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>}
177 serializeAllocationStack: function(traceNodeId)
179 var node = this._idToTopDownNode[traceNodeId];
182 var functionInfo = node.functionInfo;
183 result.push(new WebInspector.HeapSnapshotCommon.AllocationStackFrame(
184 functionInfo.functionName,
185 functionInfo.scriptName,
186 functionInfo.scriptId,
196 * @param {number} allocationNodeId
197 * @return {!Array.<number>}
199 traceIds: function(allocationNodeId)
201 return this._ensureBottomUpNode(allocationNodeId).traceTopIds;
205 * @param {number} nodeId
206 * @return {!WebInspector.BottomUpAllocationNode}
208 _ensureBottomUpNode: function(nodeId)
210 var node = this._idToNode[nodeId];
212 var functionInfo = this._collapsedTopNodeIdToFunctionInfo[nodeId];
213 node = functionInfo.bottomUpRoot();
214 delete this._collapsedTopNodeIdToFunctionInfo[nodeId];
215 this._idToNode[nodeId] = node;
221 * @param {!WebInspector.BottomUpAllocationNode} node
222 * @return {!WebInspector.HeapSnapshotCommon.SerializedAllocationNode}
224 _serializeCaller: function(node)
226 var callerId = this._nextNodeId++;
227 this._idToNode[callerId] = node;
228 return this._serializeNode(
231 node.allocationCount,
239 * @param {number} nodeId
240 * @param {!WebInspector.FunctionAllocationInfo} functionInfo
241 * @param {number} count
242 * @param {number} size
243 * @param {number} liveCount
244 * @param {number} liveSize
245 * @param {boolean} hasChildren
246 * @return {!WebInspector.HeapSnapshotCommon.SerializedAllocationNode}
248 _serializeNode: function(nodeId, functionInfo, count, size, liveCount, liveSize, hasChildren)
250 return new WebInspector.HeapSnapshotCommon.SerializedAllocationNode(
252 functionInfo.functionName,
253 functionInfo.scriptName,
254 functionInfo.scriptId,
270 * @param {!WebInspector.FunctionAllocationInfo} functionInfo
271 * @param {number} count
272 * @param {number} size
273 * @param {number} liveCount
274 * @param {number} liveSize
275 * @param {?WebInspector.TopDownAllocationNode} parent
277 WebInspector.TopDownAllocationNode = function(id, functionInfo, count, size, liveCount, liveSize, parent)
280 this.functionInfo = functionInfo;
281 this.allocationCount = count;
282 this.allocationSize = size;
283 this.liveCount = liveCount;
284 this.liveSize = liveSize;
285 this.parent = parent;
292 * @param {!WebInspector.FunctionAllocationInfo} functionInfo
294 WebInspector.BottomUpAllocationNode = function(functionInfo)
296 this.functionInfo = functionInfo;
297 this.allocationCount = 0;
298 this.allocationSize = 0;
301 this.traceTopIds = [];
306 WebInspector.BottomUpAllocationNode.prototype = {
308 * @param {!WebInspector.TopDownAllocationNode} traceNode
309 * @return {!WebInspector.BottomUpAllocationNode}
311 addCaller: function(traceNode)
313 var functionInfo = traceNode.functionInfo;
315 for (var i = 0; i < this._callers.length; i++) {
316 var caller = this._callers[i];
317 if (caller.functionInfo === functionInfo) {
323 result = new WebInspector.BottomUpAllocationNode(functionInfo);
324 this._callers.push(result);
330 * @return {!Array.<!WebInspector.BottomUpAllocationNode>}
334 return this._callers;
340 hasCallers: function()
342 return this._callers.length > 0;
349 * @param {string} functionName
350 * @param {string} scriptName
351 * @param {number} scriptId
352 * @param {number} line
353 * @param {number} column
355 WebInspector.FunctionAllocationInfo = function(functionName, scriptName, scriptId, line, column)
357 this.functionName = functionName;
358 this.scriptName = scriptName;
359 this.scriptId = scriptId;
361 this.column = column;
364 this.totalLiveCount = 0;
365 this.totalLiveSize = 0;
366 this._traceTops = [];
369 WebInspector.FunctionAllocationInfo.prototype = {
371 * @param {!WebInspector.TopDownAllocationNode} node
373 addTraceTopNode: function(node)
375 if (node.allocationCount === 0)
377 this._traceTops.push(node);
378 this.totalCount += node.allocationCount;
379 this.totalSize += node.allocationSize;
380 this.totalLiveCount += node.liveCount;
381 this.totalLiveSize += node.liveSize;
385 * @return {?WebInspector.BottomUpAllocationNode}
387 bottomUpRoot: function()
389 if (!this._traceTops.length)
391 if (!this._bottomUpTree)
392 this._buildAllocationTraceTree();
393 return this._bottomUpTree;
396 _buildAllocationTraceTree: function()
398 this._bottomUpTree = new WebInspector.BottomUpAllocationNode(this);
400 for (var i = 0; i < this._traceTops.length; i++) {
401 var node = this._traceTops[i];
402 var bottomUpNode = this._bottomUpTree;
403 var count = node.allocationCount;
404 var size = node.allocationSize;
405 var liveCount = node.liveCount;
406 var liveSize = node.liveSize;
407 var traceId = node.id;
409 bottomUpNode.allocationCount += count;
410 bottomUpNode.allocationSize += size;
411 bottomUpNode.liveCount += liveCount;
412 bottomUpNode.liveSize += liveSize;
413 bottomUpNode.traceTopIds.push(traceId);
418 bottomUpNode = bottomUpNode.addCaller(node);