Home | History | Annotate | Download | only in heap_snapshot_worker
      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