• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
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
13 * distribution.
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.
17 *
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.
29 */
30
31/**
32 * @constructor
33 */
34WebInspector.AllocationProfile = function(profile, liveObjectStats)
35{
36    this._strings = profile.strings;
37    this._liveObjectStats = liveObjectStats;
38
39    this._nextNodeId = 1;
40    this._functionInfos = []
41    this._idToNode = {};
42    this._idToTopDownNode = {};
43    this._collapsedTopNodeIdToFunctionInfo = {};
44
45    this._traceTops = null;
46
47    this._buildFunctionAllocationInfos(profile);
48    this._traceTree = this._buildAllocationTree(profile, liveObjectStats);
49}
50
51WebInspector.AllocationProfile.prototype = {
52    _buildFunctionAllocationInfos: function(profile)
53    {
54        var strings = this._strings;
55
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;
64
65        var rawInfos = profile.trace_function_infos;
66        var infoLength = rawInfos.length;
67        var functionInfos = this._functionInfos = new Array(infoLength / functionInfoFieldCount);
68        var index = 0;
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]);
76        }
77    },
78
79    _buildAllocationTree: function(profile, liveObjectStats)
80    {
81        var traceTreeRaw = profile.trace_tree;
82        var functionInfos = this._functionInfos;
83        var idToTopDownNode = this._idToTopDownNode;
84
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;
92
93        function traverseNode(rawNodeArray, nodeOffset, parent)
94        {
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(
101                id,
102                functionInfo,
103                rawNodeArray[nodeOffset + allocationCountOffset],
104                rawNodeArray[nodeOffset + allocationSizeOffset],
105                liveCount,
106                liveSize,
107                parent);
108            idToTopDownNode[id] = result;
109            functionInfo.addTraceTopNode(result);
110
111            var rawChildren = rawNodeArray[nodeOffset + childrenOffset];
112            for (var i = 0; i < rawChildren.length; i += nodeFieldCount) {
113                result.children.push(traverseNode(rawChildren, i, result));
114            }
115            return result;
116        }
117
118        return traverseNode(traceTreeRaw, 0, null);
119    },
120
121    /**
122     * @return {!Array.<!WebInspector.HeapSnapshotCommon.SerializedAllocationNode>}
123     */
124    serializeTraceTops: function()
125    {
126        if (this._traceTops)
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)
133                continue;
134            var nodeId = this._nextNodeId++;
135            var isRoot = i == 0;
136            result.push(this._serializeNode(
137                nodeId,
138                info,
139                info.totalCount,
140                info.totalSize,
141                info.totalLiveCount,
142                info.totalLiveSize,
143                !isRoot));
144            this._collapsedTopNodeIdToFunctionInfo[nodeId] = info;
145        }
146        result.sort(function(a, b) {
147            return b.size - a.size;
148        });
149        return result;
150    },
151
152    /**
153     * @param {number} nodeId
154     * @return {!WebInspector.HeapSnapshotCommon.AllocationNodeCallers}
155     */
156    serializeCallers: function(nodeId)
157    {
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));
163        }
164
165        var branchingCallers = [];
166        var callers = node.callers();
167        for (var i = 0; i < callers.length; i++) {
168            branchingCallers.push(this._serializeCaller(callers[i]));
169        }
170        return new WebInspector.HeapSnapshotCommon.AllocationNodeCallers(nodesWithSingleCaller, branchingCallers);
171    },
172
173    /**
174     * @param {number} traceNodeId
175     * @return {!Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>}
176     */
177    serializeAllocationStack: function(traceNodeId)
178    {
179        var node = this._idToTopDownNode[traceNodeId];
180        var result = [];
181        while (node) {
182            var functionInfo = node.functionInfo;
183            result.push(new WebInspector.HeapSnapshotCommon.AllocationStackFrame(
184                functionInfo.functionName,
185                functionInfo.scriptName,
186                functionInfo.scriptId,
187                functionInfo.line,
188                functionInfo.column
189            ));
190            node = node.parent;
191        }
192        return result;
193    },
194
195    /**
196     * @param {number} allocationNodeId
197     * @return {!Array.<number>}
198     */
199    traceIds: function(allocationNodeId)
200    {
201        return this._ensureBottomUpNode(allocationNodeId).traceTopIds;
202    },
203
204    /**
205     * @param {number} nodeId
206     * @return {!WebInspector.BottomUpAllocationNode}
207     */
208    _ensureBottomUpNode: function(nodeId)
209    {
210        var node = this._idToNode[nodeId];
211        if (!node) {
212            var functionInfo = this._collapsedTopNodeIdToFunctionInfo[nodeId];
213            node = functionInfo.bottomUpRoot();
214            delete this._collapsedTopNodeIdToFunctionInfo[nodeId];
215            this._idToNode[nodeId] = node;
216        }
217        return node;
218    },
219
220    /**
221     * @param {!WebInspector.BottomUpAllocationNode} node
222     * @return {!WebInspector.HeapSnapshotCommon.SerializedAllocationNode}
223     */
224    _serializeCaller: function(node)
225    {
226        var callerId = this._nextNodeId++;
227        this._idToNode[callerId] = node;
228        return this._serializeNode(
229            callerId,
230            node.functionInfo,
231            node.allocationCount,
232            node.allocationSize,
233            node.liveCount,
234            node.liveSize,
235            node.hasCallers());
236    },
237
238    /**
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}
247     */
248    _serializeNode: function(nodeId, functionInfo, count, size, liveCount, liveSize, hasChildren)
249    {
250        return new WebInspector.HeapSnapshotCommon.SerializedAllocationNode(
251            nodeId,
252            functionInfo.functionName,
253            functionInfo.scriptName,
254            functionInfo.scriptId,
255            functionInfo.line,
256            functionInfo.column,
257            count,
258            size,
259            liveCount,
260            liveSize,
261            hasChildren
262        );
263    }
264}
265
266
267/**
268 * @constructor
269 * @param {number} id
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
276 */
277WebInspector.TopDownAllocationNode = function(id, functionInfo, count, size, liveCount, liveSize, parent)
278{
279    this.id = id;
280    this.functionInfo = functionInfo;
281    this.allocationCount = count;
282    this.allocationSize = size;
283    this.liveCount = liveCount;
284    this.liveSize = liveSize;
285    this.parent = parent;
286    this.children = [];
287}
288
289
290/**
291 * @constructor
292 * @param {!WebInspector.FunctionAllocationInfo} functionInfo
293 */
294WebInspector.BottomUpAllocationNode = function(functionInfo)
295{
296    this.functionInfo = functionInfo;
297    this.allocationCount = 0;
298    this.allocationSize = 0;
299    this.liveCount = 0;
300    this.liveSize = 0;
301    this.traceTopIds = [];
302    this._callers = [];
303}
304
305
306WebInspector.BottomUpAllocationNode.prototype = {
307    /**
308     * @param {!WebInspector.TopDownAllocationNode} traceNode
309     * @return {!WebInspector.BottomUpAllocationNode}
310     */
311    addCaller: function(traceNode)
312    {
313        var functionInfo = traceNode.functionInfo;
314        var result;
315        for (var i = 0; i < this._callers.length; i++) {
316            var caller = this._callers[i];
317            if (caller.functionInfo === functionInfo) {
318                result = caller;
319                break;
320            }
321        }
322        if (!result) {
323            result = new WebInspector.BottomUpAllocationNode(functionInfo);
324            this._callers.push(result);
325        }
326        return result;
327    },
328
329    /**
330     * @return {!Array.<!WebInspector.BottomUpAllocationNode>}
331     */
332    callers: function()
333    {
334        return this._callers;
335    },
336
337    /**
338     * @return {boolean}
339     */
340    hasCallers: function()
341    {
342        return this._callers.length > 0;
343    }
344}
345
346
347/**
348 * @constructor
349 * @param {string} functionName
350 * @param {string} scriptName
351 * @param {number} scriptId
352 * @param {number} line
353 * @param {number} column
354 */
355WebInspector.FunctionAllocationInfo = function(functionName, scriptName, scriptId, line, column)
356{
357    this.functionName = functionName;
358    this.scriptName = scriptName;
359    this.scriptId = scriptId;
360    this.line = line;
361    this.column = column;
362    this.totalCount = 0;
363    this.totalSize = 0;
364    this.totalLiveCount = 0;
365    this.totalLiveSize = 0;
366    this._traceTops = [];
367}
368
369WebInspector.FunctionAllocationInfo.prototype = {
370    /**
371     * @param {!WebInspector.TopDownAllocationNode} node
372     */
373    addTraceTopNode: function(node)
374    {
375        if (node.allocationCount === 0)
376            return;
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;
382    },
383
384    /**
385     * @return {?WebInspector.BottomUpAllocationNode}
386     */
387    bottomUpRoot: function()
388    {
389        if (!this._traceTops.length)
390            return null;
391        if (!this._bottomUpTree)
392            this._buildAllocationTraceTree();
393        return this._bottomUpTree;
394    },
395
396    _buildAllocationTraceTree: function()
397    {
398        this._bottomUpTree = new WebInspector.BottomUpAllocationNode(this);
399
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;
408            while (true) {
409                bottomUpNode.allocationCount += count;
410                bottomUpNode.allocationSize += size;
411                bottomUpNode.liveCount += liveCount;
412                bottomUpNode.liveSize += liveSize;
413                bottomUpNode.traceTopIds.push(traceId);
414                node = node.parent;
415                if (node === null) {
416                    break;
417                }
418                bottomUpNode = bottomUpNode.addCaller(node);
419            }
420        }
421    }
422}
423