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