• 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)
35{
36    this._strings = profile.strings;
37
38    this._nextNodeId = 1;
39    this._idToFunctionInfo = {};
40    this._idToNode = {};
41    this._collapsedTopNodeIdToFunctionInfo = {};
42
43    this._traceTops = null;
44
45    this._buildAllocationFunctionInfos(profile);
46    this._traceTree = this._buildInvertedAllocationTree(profile);
47}
48
49WebInspector.AllocationProfile.prototype = {
50    _buildAllocationFunctionInfos: function(profile)
51    {
52        var strings = this._strings;
53
54        var functionInfoFields = profile.snapshot.meta.trace_function_info_fields;
55        var functionIdOffset = functionInfoFields.indexOf("function_id");
56        var functionNameOffset = functionInfoFields.indexOf("name");
57        var scriptNameOffset = functionInfoFields.indexOf("script_name");
58        var scriptIdOffset = functionInfoFields.indexOf("script_id");
59        var lineOffset = functionInfoFields.indexOf("line");
60        var columnOffset = functionInfoFields.indexOf("column");
61        var functionInfoFieldCount = functionInfoFields.length;
62
63        var map = this._idToFunctionInfo;
64
65        // Special case for the root node.
66        map[0] = new WebInspector.FunctionAllocationInfo("(root)", "<unknown>", 0, -1, -1);
67
68        var rawInfos = profile.trace_function_infos;
69        var infoLength = rawInfos.length;
70        for (var i = 0; i < infoLength; i += functionInfoFieldCount) {
71            map[rawInfos[i + functionIdOffset]] = new WebInspector.FunctionAllocationInfo(
72                strings[rawInfos[i + functionNameOffset]],
73                strings[rawInfos[i + scriptNameOffset]],
74                rawInfos[i + scriptIdOffset],
75                rawInfos[i + lineOffset],
76                rawInfos[i + columnOffset]);
77        }
78    },
79
80    _buildInvertedAllocationTree: function(profile)
81    {
82        var traceTreeRaw = profile.trace_tree;
83        var idToFunctionInfo = this._idToFunctionInfo;
84
85        var traceNodeFields = profile.snapshot.meta.trace_node_fields;
86        var nodeIdOffset = traceNodeFields.indexOf("id");
87        var functionIdOffset = traceNodeFields.indexOf("function_id");
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 = idToFunctionInfo[rawNodeArray[nodeOffset + functionIdOffset]];
96            var result = new WebInspector.AllocationTraceNode(
97                rawNodeArray[nodeOffset + nodeIdOffset],
98                functionInfo,
99                rawNodeArray[nodeOffset + allocationCountOffset],
100                rawNodeArray[nodeOffset + allocationSizeOffset],
101                parent);
102            functionInfo.addTraceTopNode(result);
103
104            var rawChildren = rawNodeArray[nodeOffset + childrenOffset];
105            for (var i = 0; i < rawChildren.length; i += nodeFieldCount) {
106                result.children.push(traverseNode(rawChildren, i, result));
107            }
108            return result;
109        }
110
111        return traverseNode(traceTreeRaw, 0, null);
112    },
113
114    serializeTraceTops: function()
115    {
116        if (this._traceTops)
117            return this._traceTops;
118        var result = this._traceTops = [];
119        var idToFunctionInfo = this._idToFunctionInfo;
120        for (var id in idToFunctionInfo) {
121            var info = idToFunctionInfo[id];
122            if (info.totalCount === 0)
123                continue;
124            var nodeId = this._nextNodeId++;
125            result.push(this._serializeNode(
126                nodeId,
127                info,
128                info.totalCount,
129                info.totalSize,
130                true));
131            this._collapsedTopNodeIdToFunctionInfo[nodeId] = info;
132        }
133        result.sort(function(a, b) {
134            return b.size - a.size;
135        });
136        return result;
137    },
138
139    serializeCallers: function(nodeId)
140    {
141        var node = this._idToNode[nodeId];
142        if (!node) {
143            var functionInfo = this._collapsedTopNodeIdToFunctionInfo[nodeId];
144            node = functionInfo.tracesWithThisTop();
145            delete this._collapsedTopNodeIdToFunctionInfo[nodeId];
146            this._idToNode[nodeId] = node;
147        }
148
149        var nodesWithSingleCaller = [];
150        while (node.callers().length === 1) {
151            node = node.callers()[0];
152            nodesWithSingleCaller.push(this._serializeCaller(node));
153        }
154
155        var branchingCallers = [];
156        var callers = node.callers();
157        for (var i = 0; i < callers.length; i++) {
158            branchingCallers.push(this._serializeCaller(callers[i]));
159        }
160        return {
161            nodesWithSingleCaller: nodesWithSingleCaller,
162            branchingCallers: branchingCallers
163        };
164    },
165
166    _serializeCaller: function(node)
167    {
168        var callerId = this._nextNodeId++;
169        this._idToNode[callerId] = node;
170        return this._serializeNode(
171            callerId,
172            node.functionInfo,
173            node.allocationCount,
174            node.allocationSize,
175            node.hasCallers());
176    },
177
178    _serializeNode: function(nodeId, functionInfo, count, size, hasChildren)
179    {
180        return {
181            id: nodeId,
182            name: functionInfo.functionName,
183            scriptName: functionInfo.scriptName,
184            line: functionInfo.line,
185            column: functionInfo.column,
186            count: count,
187            size: size,
188            hasChildren: hasChildren
189        };
190    }
191}
192
193
194/**
195 * @constructor
196 */
197WebInspector.AllocationTraceNode = function(id, functionInfo, count, size, parent)
198{
199    this.id = id;
200    this.functionInfo = functionInfo;
201    this.allocationCount = count;
202    this.allocationSize = size;
203    this.parent = parent;
204    this.children = [];
205}
206
207
208/**
209 * @constructor
210 * @param {!WebInspector.FunctionAllocationInfo} functionInfo
211 */
212WebInspector.AllocationBackTraceNode = function(functionInfo)
213{
214    this.functionInfo = functionInfo;
215    this.allocationCount = 0;
216    this.allocationSize = 0;
217    this._callers = [];
218}
219
220
221WebInspector.AllocationBackTraceNode.prototype = {
222    /**
223     * @param {!WebInspector.AllocationTraceNode} traceNode
224     * @return {!WebInspector.AllocationTraceNode}
225     */
226    addCaller: function(traceNode)
227    {
228        var functionInfo = traceNode.functionInfo;
229        var result;
230        for (var i = 0; i < this._callers.length; i++) {
231            var caller = this._callers[i];
232            if (caller.functionInfo === functionInfo) {
233                result = caller;
234                break;
235            }
236        }
237        if (!result) {
238            result = new WebInspector.AllocationBackTraceNode(functionInfo);
239            this._callers.push(result);
240        }
241        return result;
242    },
243
244    callers: function()
245    {
246        return this._callers;
247    },
248
249    hasCallers: function()
250    {
251        return this._callers.length > 0;
252    }
253}
254
255
256/**
257 * @constructor
258 */
259WebInspector.FunctionAllocationInfo = function(functionName, scriptName, scriptId, line, column)
260{
261    this.functionName = functionName;
262    this.scriptName = scriptName;
263    this.scriptId = scriptId;
264    this.line = line;
265    this.column = column;
266    this.totalCount = 0;
267    this.totalSize = 0;
268    this._traceTops = [];
269}
270
271WebInspector.FunctionAllocationInfo.prototype = {
272    addTraceTopNode: function(node)
273    {
274        if (node.allocationCount === 0)
275            return;
276        this._traceTops.push(node);
277        this.totalCount += node.allocationCount;
278        this.totalSize += node.allocationSize;
279    },
280
281    tracesWithThisTop: function()
282    {
283        if (!this._traceTops.length)
284            return null;
285        if (!this._backTraceTree)
286            this._buildAllocationTraceTree();
287        return this._backTraceTree;
288    },
289
290    _buildAllocationTraceTree: function()
291    {
292        this._backTraceTree = new WebInspector.AllocationBackTraceNode(this._traceTops[0].functionInfo);
293
294        for (var i = 0; i < this._traceTops.length; i++) {
295            var node = this._traceTops[i];
296            var backTraceNode = this._backTraceTree;
297            var count = node.allocationCount;
298            var size = node.allocationSize;
299            while (true) {
300                backTraceNode.allocationCount += count;
301                backTraceNode.allocationSize += size;
302                node = node.parent;
303                if (node === null) {
304                    break;
305                }
306                backTraceNode = backTraceNode.addCaller(node);
307            }
308        }
309    }
310}
311