• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2012 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 * @param {!WebInspector.HeapSnapshotWorkerDispatcher} dispatcher
34 * @implements {WebInspector.OutputStream}
35 */
36WebInspector.HeapSnapshotLoader = function(dispatcher)
37{
38    this._reset();
39    this._progress = new WebInspector.HeapSnapshotProgress(dispatcher);
40}
41
42WebInspector.HeapSnapshotLoader.prototype = {
43    dispose: function()
44    {
45        this._reset();
46    },
47
48    _reset: function()
49    {
50        this._json = "";
51        this._state = "find-snapshot-info";
52        this._snapshot = {};
53    },
54
55    close: function()
56    {
57        if (this._json)
58            this._parseStringsArray();
59    },
60
61    buildSnapshot: function(constructorName)
62    {
63        this._progress.updateStatus("Processing snapshot\u2026");
64        var constructor = WebInspector[constructorName];
65        var result = new constructor(this._snapshot, this._progress);
66        this._reset();
67        return result;
68    },
69
70    _parseUintArray: function()
71    {
72        var index = 0;
73        var char0 = "0".charCodeAt(0), char9 = "9".charCodeAt(0), closingBracket = "]".charCodeAt(0);
74        var length = this._json.length;
75        while (true) {
76            while (index < length) {
77                var code = this._json.charCodeAt(index);
78                if (char0 <= code && code <= char9)
79                    break;
80                else if (code === closingBracket) {
81                    this._json = this._json.slice(index + 1);
82                    return false;
83                }
84                ++index;
85            }
86            if (index === length) {
87                this._json = "";
88                return true;
89            }
90            var nextNumber = 0;
91            var startIndex = index;
92            while (index < length) {
93                var code = this._json.charCodeAt(index);
94                if (char0 > code || code > char9)
95                    break;
96                nextNumber *= 10;
97                nextNumber += (code - char0);
98                ++index;
99            }
100            if (index === length) {
101                this._json = this._json.slice(startIndex);
102                return true;
103            }
104            this._array[this._arrayIndex++] = nextNumber;
105        }
106    },
107
108    _parseStringsArray: function()
109    {
110        this._progress.updateStatus("Parsing strings\u2026");
111        var closingBracketIndex = this._json.lastIndexOf("]");
112        if (closingBracketIndex === -1)
113            throw new Error("Incomplete JSON");
114        this._json = this._json.slice(0, closingBracketIndex + 1);
115        this._snapshot.strings = JSON.parse(this._json);
116    },
117
118    /**
119     * @param {string} chunk
120     */
121    write: function(chunk)
122    {
123        this._json += chunk;
124        while (true) {
125            switch (this._state) {
126            case "find-snapshot-info": {
127                var snapshotToken = "\"snapshot\"";
128                var snapshotTokenIndex = this._json.indexOf(snapshotToken);
129                if (snapshotTokenIndex === -1)
130                    throw new Error("Snapshot token not found");
131                this._json = this._json.slice(snapshotTokenIndex + snapshotToken.length + 1);
132                this._state = "parse-snapshot-info";
133                this._progress.updateStatus("Loading snapshot info\u2026");
134                break;
135            }
136            case "parse-snapshot-info": {
137                var closingBracketIndex = WebInspector.findBalancedCurlyBrackets(this._json);
138                if (closingBracketIndex === -1)
139                    return;
140                this._snapshot.snapshot = /** @type {!HeapSnapshotHeader} */ (JSON.parse(this._json.slice(0, closingBracketIndex)));
141                this._json = this._json.slice(closingBracketIndex);
142                this._state = "find-nodes";
143                break;
144            }
145            case "find-nodes": {
146                var nodesToken = "\"nodes\"";
147                var nodesTokenIndex = this._json.indexOf(nodesToken);
148                if (nodesTokenIndex === -1)
149                    return;
150                var bracketIndex = this._json.indexOf("[", nodesTokenIndex);
151                if (bracketIndex === -1)
152                    return;
153                this._json = this._json.slice(bracketIndex + 1);
154                var node_fields_count = this._snapshot.snapshot.meta.node_fields.length;
155                var nodes_length = this._snapshot.snapshot.node_count * node_fields_count;
156                this._array = new Uint32Array(nodes_length);
157                this._arrayIndex = 0;
158                this._state = "parse-nodes";
159                break;
160            }
161            case "parse-nodes": {
162                var hasMoreData = this._parseUintArray();
163                this._progress.updateProgress("Loading nodes\u2026 %d\%", this._arrayIndex, this._array.length);
164                if (hasMoreData)
165                    return;
166                this._snapshot.nodes = this._array;
167                this._state = "find-edges";
168                this._array = null;
169                break;
170            }
171            case "find-edges": {
172                var edgesToken = "\"edges\"";
173                var edgesTokenIndex = this._json.indexOf(edgesToken);
174                if (edgesTokenIndex === -1)
175                    return;
176                var bracketIndex = this._json.indexOf("[", edgesTokenIndex);
177                if (bracketIndex === -1)
178                    return;
179                this._json = this._json.slice(bracketIndex + 1);
180                var edge_fields_count = this._snapshot.snapshot.meta.edge_fields.length;
181                var edges_length = this._snapshot.snapshot.edge_count * edge_fields_count;
182                this._array = new Uint32Array(edges_length);
183                this._arrayIndex = 0;
184                this._state = "parse-edges";
185                break;
186            }
187            case "parse-edges": {
188                var hasMoreData = this._parseUintArray();
189                this._progress.updateProgress("Loading edges\u2026 %d\%", this._arrayIndex, this._array.length);
190                if (hasMoreData)
191                    return;
192                this._snapshot.edges = this._array;
193                this._array = null;
194                if (WebInspector.HeapSnapshot.enableAllocationProfiler)
195                    this._state = "find-trace-function-infos";
196                else
197                    this._state = "find-strings";
198                break;
199            }
200            case "find-trace-function-infos": {
201                var tracesToken = "\"trace_function_infos\"";
202                var tracesTokenIndex = this._json.indexOf(tracesToken);
203                if (tracesTokenIndex === -1)
204                    return;
205                var bracketIndex = this._json.indexOf("[", tracesTokenIndex);
206                if (bracketIndex === -1)
207                    return;
208                this._json = this._json.slice(bracketIndex + 1);
209
210                var trace_function_info_field_count = this._snapshot.snapshot.meta.trace_function_info_fields.length;
211                var trace_function_info_length = this._snapshot.snapshot.trace_function_count * trace_function_info_field_count;
212                this._array = new Uint32Array(trace_function_info_length);
213                this._arrayIndex = 0;
214                this._state = "parse-trace-function-infos";
215                break;
216            }
217            case "parse-trace-function-infos": {
218                if (this._parseUintArray())
219                    return;
220                this._snapshot.trace_function_infos = this._array;
221                this._array = null;
222                this._state = "find-trace-tree";
223                break;
224            }
225            case "find-trace-tree": {
226                var tracesToken = "\"trace_tree\"";
227                var tracesTokenIndex = this._json.indexOf(tracesToken);
228                if (tracesTokenIndex === -1)
229                    return;
230                var bracketIndex = this._json.indexOf("[", tracesTokenIndex);
231                if (bracketIndex === -1)
232                    return;
233                this._json = this._json.slice(bracketIndex);
234                this._state = "parse-trace-tree";
235                break;
236            }
237            case "parse-trace-tree": {
238                var stringsToken = "\"strings\"";
239                var stringsTokenIndex = this._json.indexOf(stringsToken);
240                if (stringsTokenIndex === -1)
241                    return;
242                var bracketIndex = this._json.lastIndexOf("]", stringsTokenIndex);
243                this._snapshot.trace_tree = JSON.parse(this._json.substring(0, bracketIndex + 1));
244                this._json = this._json.slice(bracketIndex);
245                this._state = "find-strings";
246                this._progress.updateStatus("Loading strings\u2026");
247                break;
248            }
249            case "find-strings": {
250                var stringsToken = "\"strings\"";
251                var stringsTokenIndex = this._json.indexOf(stringsToken);
252                if (stringsTokenIndex === -1)
253                    return;
254                var bracketIndex = this._json.indexOf("[", stringsTokenIndex);
255                if (bracketIndex === -1)
256                    return;
257                this._json = this._json.slice(bracketIndex);
258                this._state = "accumulate-strings";
259                break;
260            }
261            case "accumulate-strings":
262                return;
263            }
264        }
265    }
266};
267