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