1// Copyright 2011 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28/** 29 * @fileoverview Log Reader is used to process log file produced by V8. 30 */ 31 32 33/** 34 * Base class for processing log files. 35 * 36 * @param {Array.<Object>} dispatchTable A table used for parsing and processing 37 * log records. 38 * @param {boolean} timedRange Ignore ticks outside timed range. 39 * @param {boolean} pairwiseTimedRange Ignore ticks outside pairs of timer 40 * markers. 41 * @constructor 42 */ 43function LogReader(dispatchTable, timedRange, pairwiseTimedRange) { 44 /** 45 * @type {Array.<Object>} 46 */ 47 this.dispatchTable_ = dispatchTable; 48 49 /** 50 * @type {boolean} 51 */ 52 this.timedRange_ = timedRange; 53 54 /** 55 * @type {boolean} 56 */ 57 this.pairwiseTimedRange_ = pairwiseTimedRange; 58 if (pairwiseTimedRange) { 59 this.timedRange_ = true; 60 } 61 62 /** 63 * Current line. 64 * @type {number} 65 */ 66 this.lineNum_ = 0; 67 68 /** 69 * CSV lines parser. 70 * @type {CsvParser} 71 */ 72 this.csvParser_ = new CsvParser(); 73 74 /** 75 * Keeps track of whether we've seen a "current-time" tick yet. 76 * @type {boolean} 77 */ 78 this.hasSeenTimerMarker_ = false; 79 80 /** 81 * List of log lines seen since last "current-time" tick. 82 * @type {Array.<String>} 83 */ 84 this.logLinesSinceLastTimerMarker_ = []; 85}; 86 87 88/** 89 * Used for printing error messages. 90 * 91 * @param {string} str Error message. 92 */ 93LogReader.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 */ 103LogReader.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 */ 113LogReader.prototype.processLogLine = function(line) { 114 if (!this.timedRange_) { 115 this.processLog_([line]); 116 return; 117 } 118 if (line.startsWith("current-time")) { 119 if (this.hasSeenTimerMarker_) { 120 this.processLog_(this.logLinesSinceLastTimerMarker_); 121 this.logLinesSinceLastTimerMarker_ = []; 122 // In pairwise mode, a "current-time" line ends the timed range. 123 if (this.pairwiseTimedRange_) { 124 this.hasSeenTimerMarker_ = false; 125 } 126 } else { 127 this.hasSeenTimerMarker_ = true; 128 } 129 } else { 130 if (this.hasSeenTimerMarker_) { 131 this.logLinesSinceLastTimerMarker_.push(line); 132 } else if (!line.startsWith("tick")) { 133 this.processLog_([line]); 134 } 135 } 136}; 137 138 139/** 140 * Processes stack record. 141 * 142 * @param {number} pc Program counter. 143 * @param {number} func JS Function. 144 * @param {Array.<string>} stack String representation of a stack. 145 * @return {Array.<number>} Processed stack. 146 */ 147LogReader.prototype.processStack = function(pc, func, stack) { 148 var fullStack = func ? [pc, func] : [pc]; 149 var prevFrame = pc; 150 for (var i = 0, n = stack.length; i < n; ++i) { 151 var frame = stack[i]; 152 var firstChar = frame.charAt(0); 153 if (firstChar == '+' || firstChar == '-') { 154 // An offset from the previous frame. 155 prevFrame += parseInt(frame, 16); 156 fullStack.push(prevFrame); 157 // Filter out possible 'overflow' string. 158 } else if (firstChar != 'o') { 159 fullStack.push(parseInt(frame, 16)); 160 } else { 161 print("dropping: " + frame); 162 } 163 } 164 return fullStack; 165}; 166 167 168/** 169 * Returns whether a particular dispatch must be skipped. 170 * 171 * @param {!Object} dispatch Dispatch record. 172 * @return {boolean} True if dispatch must be skipped. 173 */ 174LogReader.prototype.skipDispatch = function(dispatch) { 175 return false; 176}; 177 178 179/** 180 * Does a dispatch of a log record. 181 * 182 * @param {Array.<string>} fields Log record. 183 * @private 184 */ 185LogReader.prototype.dispatchLogRow_ = function(fields) { 186 // Obtain the dispatch. 187 var command = fields[0]; 188 if (!(command in this.dispatchTable_)) return; 189 190 var dispatch = this.dispatchTable_[command]; 191 192 if (dispatch === null || this.skipDispatch(dispatch)) { 193 return; 194 } 195 196 // Parse fields. 197 var parsedFields = []; 198 for (var i = 0; i < dispatch.parsers.length; ++i) { 199 var parser = dispatch.parsers[i]; 200 if (parser === null) { 201 parsedFields.push(fields[1 + i]); 202 } else if (typeof parser == 'function') { 203 parsedFields.push(parser(fields[1 + i])); 204 } else { 205 // var-args 206 parsedFields.push(fields.slice(1 + i)); 207 break; 208 } 209 } 210 211 // Run the processor. 212 dispatch.processor.apply(this, parsedFields); 213}; 214 215 216/** 217 * Processes log lines. 218 * 219 * @param {Array.<string>} lines Log lines. 220 * @private 221 */ 222LogReader.prototype.processLog_ = function(lines) { 223 for (var i = 0, n = lines.length; i < n; ++i, ++this.lineNum_) { 224 var line = lines[i]; 225 if (!line) { 226 continue; 227 } 228 try { 229 var fields = this.csvParser_.parseLine(line); 230 this.dispatchLogRow_(fields); 231 } catch (e) { 232 this.printError('line ' + (this.lineNum_ + 1) + ': ' + (e.message || e)); 233 } 234 } 235}; 236