Home | History | Annotate | Download | only in v8
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 /**
      6  * @fileoverview Log Reader is used to process log file produced by V8.
      7  */
      8 base.exportTo('tracing.importer.v8', function() {
      9   /**
     10    * Creates a CSV lines parser.
     11    */
     12   function CsvParser() {
     13   };
     14 
     15 
     16   /**
     17    * A regex for matching a CSV field.
     18    * @private
     19    */
     20   CsvParser.CSV_FIELD_RE_ = /^"((?:[^"]|"")*)"|([^,]*)/;
     21 
     22 
     23   /**
     24    * A regex for matching a double quote.
     25    * @private
     26    */
     27   CsvParser.DOUBLE_QUOTE_RE_ = /""/g;
     28 
     29 
     30   /**
     31    * Parses a line of CSV-encoded values. Returns an array of fields.
     32    *
     33    * @param {string} line Input line.
     34    */
     35   CsvParser.prototype.parseLine = function(line) {
     36     var fieldRe = CsvParser.CSV_FIELD_RE_;
     37     var doubleQuoteRe = CsvParser.DOUBLE_QUOTE_RE_;
     38     var pos = 0;
     39     var endPos = line.length;
     40     var fields = [];
     41     if (endPos > 0) {
     42       do {
     43         var fieldMatch = fieldRe.exec(line.substr(pos));
     44         if (typeof fieldMatch[1] === 'string') {
     45           var field = fieldMatch[1];
     46           pos += field.length + 3;  // Skip comma and quotes.
     47           fields.push(field.replace(doubleQuoteRe, '"'));
     48         } else {
     49           // The second field pattern will match anything, thus
     50           // in the worst case the match will be an empty string.
     51           var field = fieldMatch[2];
     52           pos += field.length + 1;  // Skip comma.
     53           fields.push(field);
     54         }
     55       } while (pos <= endPos);
     56     }
     57     return fields;
     58   };
     59 
     60   /**
     61    * Base class for processing log files.
     62    *
     63    * @param {Array.<Object>} dispatchTable A table used for parsing and
     64    * processing log records.
     65    *
     66    * @constructor
     67    */
     68   function LogReader(dispatchTable) {
     69     /**
     70      * @type {Array.<Object>}
     71      */
     72     this.dispatchTable_ = dispatchTable;
     73 
     74     /**
     75      * Current line.
     76      * @type {number}
     77      */
     78     this.lineNum_ = 0;
     79 
     80     /**
     81      * CSV lines parser.
     82      * @type {CsvParser}
     83      */
     84     this.csvParser_ = new CsvParser();
     85   };
     86 
     87 
     88   /**
     89    * Used for printing error messages.
     90    *
     91    * @param {string} str Error message.
     92    */
     93   LogReader.prototype.printError = function(str) {
     94     // Do nothing.
     95   };
     96 
     97 
     98   /**
     99    * Processes a portion of V8 profiler event log.
    100    *
    101    * @param {string} chunk A portion of log.
    102    */
    103   LogReader.prototype.processLogChunk = function(chunk) {
    104     this.processLog_(chunk.split('\n'));
    105   };
    106 
    107 
    108   /**
    109    * Processes a line of V8 profiler event log.
    110    *
    111    * @param {string} line A line of log.
    112    */
    113   LogReader.prototype.processLogLine = function(line) {
    114     this.processLog_([line]);
    115   };
    116 
    117 
    118   /**
    119    * Processes stack record.
    120    *
    121    * @param {number} pc Program counter.
    122    * @param {number} func JS Function.
    123    * @param {Array.<string>} stack String representation of a stack.
    124    * @return {Array.<number>} Processed stack.
    125    */
    126   LogReader.prototype.processStack = function(pc, func, stack) {
    127     var fullStack = func ? [pc, func] : [pc];
    128     var prevFrame = pc;
    129     for (var i = 0, n = stack.length; i < n; ++i) {
    130       var frame = stack[i];
    131       var firstChar = frame.charAt(0);
    132       if (firstChar == '+' || firstChar == '-') {
    133         // An offset from the previous frame.
    134         prevFrame += parseInt(frame, 16);
    135         fullStack.push(prevFrame);
    136       // Filter out possible 'overflow' string.
    137       } else if (firstChar != 'o') {
    138         fullStack.push(parseInt(frame, 16));
    139       }
    140     }
    141     return fullStack;
    142   };
    143 
    144 
    145   /**
    146    * Returns whether a particular dispatch must be skipped.
    147    *
    148    * @param {!Object} dispatch Dispatch record.
    149    * @return {boolean} True if dispatch must be skipped.
    150    */
    151   LogReader.prototype.skipDispatch = function(dispatch) {
    152     return false;
    153   };
    154 
    155 
    156   /**
    157    * Does a dispatch of a log record.
    158    *
    159    * @param {Array.<string>} fields Log record.
    160    * @private
    161    */
    162   LogReader.prototype.dispatchLogRow_ = function(fields) {
    163     // Obtain the dispatch.
    164     var command = fields[0];
    165     if (!(command in this.dispatchTable_)) return;
    166 
    167     var dispatch = this.dispatchTable_[command];
    168 
    169     if (dispatch === null || this.skipDispatch(dispatch)) {
    170       return;
    171     }
    172 
    173     // Parse fields.
    174     var parsedFields = [];
    175     for (var i = 0; i < dispatch.parsers.length; ++i) {
    176       var parser = dispatch.parsers[i];
    177       if (parser === null) {
    178         parsedFields.push(fields[1 + i]);
    179       } else if (typeof parser == 'function') {
    180         parsedFields.push(parser(fields[1 + i]));
    181       } else {
    182         // var-args
    183         parsedFields.push(fields.slice(1 + i));
    184         break;
    185       }
    186     }
    187 
    188     // Run the processor.
    189     dispatch.processor.apply(this, parsedFields);
    190   };
    191 
    192 
    193   /**
    194    * Processes log lines.
    195    *
    196    * @param {Array.<string>} lines Log lines.
    197    * @private
    198    */
    199   LogReader.prototype.processLog_ = function(lines) {
    200     for (var i = 0, n = lines.length; i < n; ++i, ++this.lineNum_) {
    201       var line = lines[i];
    202       if (!line) {
    203         continue;
    204       }
    205       try {
    206         var fields = this.csvParser_.parseLine(line);
    207         this.dispatchLogRow_(fields);
    208       } catch (e) {
    209         this.printError('line ' + (this.lineNum_ + 1) + ': ' +
    210                         (e.message || e));
    211       }
    212     }
    213   };
    214   return {
    215     LogReader: LogReader
    216   };
    217 });
    218