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