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