OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2009 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 // Initlialize namespaces | |
33 var devtools = devtools || {}; | |
34 devtools.profiler = devtools.profiler || {}; | |
35 | |
36 | |
37 /** | |
38 * Base class for processing log files. | |
39 * | |
40 * @param {Array.<Object>} dispatchTable A table used for parsing and processing | |
41 * log records. | |
42 * @constructor | |
43 */ | |
44 devtools.profiler.LogReader = function(dispatchTable) { | |
45 /** | |
46 * @type {Array.<Object>} | |
47 */ | |
48 this.dispatchTable_ = dispatchTable; | |
49 this.dispatchTable_['alias'] = | |
50 { parsers: [null, null], processor: this.processAlias_ }; | |
51 this.dispatchTable_['repeat'] = | |
52 { parsers: [parseInt, 'var-args'], processor: this.processRepeat_, | |
53 backrefs: true }; | |
54 | |
55 /** | |
56 * A key-value map for aliases. Translates short name -> full name. | |
57 * @type {Object} | |
58 */ | |
59 this.aliases_ = {}; | |
60 | |
61 /** | |
62 * A key-value map for previous address values. | |
63 * @type {Object} | |
64 */ | |
65 this.prevAddresses_ = {}; | |
66 | |
67 /** | |
68 * A key-value map for events than can be backreference-compressed. | |
69 * @type {Object} | |
70 */ | |
71 this.backRefsCommands_ = {}; | |
72 this.initBackRefsCommands_(); | |
73 | |
74 /** | |
75 * Back references for decompression. | |
76 * @type {Array.<string>} | |
77 */ | |
78 this.backRefs_ = []; | |
79 }; | |
80 | |
81 | |
82 /** | |
83 * Creates a parser for an address entry. | |
84 * | |
85 * @param {string} addressTag Address tag to perform offset decoding. | |
86 * @return {function(string):number} Address parser. | |
87 */ | |
88 devtools.profiler.LogReader.prototype.createAddressParser = function( | |
89 addressTag) { | |
90 var self = this; | |
91 return function addressParser (str) { | |
Søren Thygesen Gjesse
2009/06/18 06:57:44
Maybe add parentheses arround the function returne
Mikhail Naganov
2009/06/18 07:57:07
Parentheses added. I thought that giving a name ad
| |
92 var value = parseInt(str, 16); | |
93 var firstChar = str.charAt(0); | |
94 if (firstChar == '+' || firstChar == '-') { | |
95 var addr = self.prevAddresses_[addressTag]; | |
96 addr += value; | |
97 self.prevAddresses_[addressTag] = addr; | |
98 return addr; | |
99 } else if (firstChar != '0' || str.charAt(1) != 'x') { | |
100 self.prevAddresses_[addressTag] = value; | |
101 } | |
102 return value; | |
103 }; | |
104 }; | |
105 | |
106 | |
107 /** | |
108 * Expands an alias symbol, if applicable. | |
109 * | |
110 * @param {string} symbol Symbol to expand. | |
111 * @return {string} Expanded symbol, or the input symbol itself. | |
112 */ | |
113 devtools.profiler.LogReader.prototype.expandAlias = function(symbol) { | |
114 return symbol in this.aliases_ ? this.aliases_[symbol] : symbol; | |
115 }; | |
116 | |
117 | |
118 /** | |
119 * Used for printing error messages. | |
120 * | |
121 * @param {string} str Error message. | |
122 */ | |
123 devtools.profiler.LogReader.prototype.printError = function(str) { | |
124 // Do nothing. | |
125 }; | |
126 | |
127 | |
128 /** | |
129 * Processes a portion of V8 profiler event log. | |
130 * | |
131 * @param {string} chunk A portion of log. | |
132 */ | |
133 devtools.profiler.LogReader.prototype.processLogChunk = function(chunk) { | |
134 this.processLog_(chunk.split('\n')); | |
135 }; | |
136 | |
137 | |
138 /** | |
139 * Processes stack record. | |
140 * | |
141 * @param {number} pc Program counter. | |
142 * @param {Array.<string>} stack String representation of a stack. | |
143 * @return {Array.<number>} Processed stack. | |
144 */ | |
145 devtools.profiler.LogReader.prototype.processStack = function(pc, stack) { | |
146 var fullStack = [pc]; | |
147 var prevFrame = pc; | |
148 for (var i = 0, n = stack.length; i < n; ++i) { | |
149 var frame = stack[i]; | |
150 var firstChar = frame.charAt(0); | |
151 if (firstChar == '+' || firstChar == '-') { | |
152 // An offset from the previous frame. | |
153 prevFrame += parseInt(frame, 16); | |
154 fullStack.push(prevFrame); | |
155 // Filter out possible 'overflow' string. | |
156 } else if (firstChar != 'o') { | |
157 fullStack.push(parseInt(frame, 16)); | |
158 } | |
159 } | |
160 return fullStack; | |
161 }; | |
162 | |
163 | |
164 /** | |
165 * Returns whether a particular dispatch must be skipped. | |
166 * | |
167 * @param {!Object} dispatch Dispatch record. | |
168 * @return {boolean} True if dispatch must be skipped. | |
169 */ | |
170 devtools.profiler.LogReader.prototype.skipDispatch = function(dispatch) { | |
171 return false; | |
172 }; | |
173 | |
174 | |
175 /** | |
176 * Does a dispatch of a log record. | |
177 * | |
178 * @param {Array.<string>} fields Log record. | |
179 * @private | |
180 */ | |
181 devtools.profiler.LogReader.prototype.dispatchLogRow_ = function(fields) { | |
182 // Obtain the dispatch. | |
183 var command = fields[0]; | |
184 if (!(command in this.dispatchTable_)) { | |
185 throw new Error('unknown command: ' + command); | |
186 } | |
187 var dispatch = this.dispatchTable_[command]; | |
188 | |
189 if (dispatch === null || this.skipDispatch(dispatch)) { | |
190 return; | |
191 } | |
192 | |
193 // Parse fields. | |
194 var parsedFields = []; | |
195 for (var i = 0; i < dispatch.parsers.length; ++i) { | |
196 var parser = dispatch.parsers[i]; | |
197 if (parser === null) { | |
198 parsedFields.push(fields[1 + i]); | |
199 } else if (typeof parser == 'function') { | |
200 parsedFields.push(parser(fields[1 + i])); | |
201 } else { | |
202 // var-args | |
203 parsedFields.push(fields.slice(1 + i)); | |
204 break; | |
205 } | |
206 } | |
207 | |
208 // Run the processor. | |
209 dispatch.processor.apply(this, parsedFields); | |
210 }; | |
211 | |
212 | |
213 /** | |
214 * Decompresses a line if it was backreference-compressed. | |
215 * | |
216 * @param {string} line Possibly compressed line. | |
217 * @return {string} Decompressed line. | |
218 * @private | |
219 */ | |
220 devtools.profiler.LogReader.prototype.expandBackRef_ = function(line) { | |
221 var backRefPos; | |
222 // Filter out case when a regexp is created containing '#'. | |
223 if (line.charAt(line.length - 1) != '"' | |
224 && (backRefPos = line.lastIndexOf('#')) != -1) { | |
225 var backRef = line.substr(backRefPos + 1); | |
226 var backRefIdx = parseInt(backRef, 10) - 1; | |
227 var colonPos = backRef.indexOf(':'); | |
228 var backRefStart = | |
229 colonPos != -1 ? parseInt(backRef.substr(colonPos + 1), 10) : 0; | |
230 line = line.substr(0, backRefPos) + | |
231 this.backRefs_[backRefIdx].substr(backRefStart); | |
232 } | |
233 this.backRefs_.unshift(line); | |
234 if (this.backRefs_.length > 10) { | |
235 this.backRefs_.length = 10; | |
236 } | |
237 return line; | |
238 }; | |
239 | |
240 | |
241 /** | |
242 * Initializes the map of backward reference compressible commands. | |
243 * @private | |
244 */ | |
245 devtools.profiler.LogReader.prototype.initBackRefsCommands_ = function() { | |
246 for (var event in this.dispatchTable_) { | |
247 var dispatch = this.dispatchTable_[event]; | |
248 if (dispatch && dispatch.backrefs) { | |
249 this.backRefsCommands_[event] = true; | |
250 } | |
251 } | |
252 }; | |
253 | |
254 | |
255 /** | |
256 * Processes alias log record. Adds an alias to a corresponding map. | |
257 * | |
258 * @param {string} symbol Short name. | |
259 * @param {string} expansion Long name. | |
260 * @private | |
261 */ | |
262 devtools.profiler.LogReader.prototype.processAlias_ = function( | |
263 symbol, expansion) { | |
264 if (expansion in this.dispatchTable_) { | |
265 this.dispatchTable_[symbol] = this.dispatchTable_[expansion]; | |
266 if (expansion in this.backRefsCommands_) { | |
267 this.backRefsCommands_[symbol] = true; | |
268 } | |
269 } else { | |
270 this.aliases_[symbol] = expansion; | |
271 } | |
272 }; | |
273 | |
274 | |
275 /** | |
276 * Processes log lines. | |
277 * | |
278 * @param {Array.<string>} lines Log lines. | |
279 * @private | |
280 */ | |
281 devtools.profiler.LogReader.prototype.processLog_ = function(lines) { | |
282 var csvParser = new devtools.profiler.CsvParser(); | |
283 try { | |
284 for (var i = 0, n = lines.length; i < n; ++i) { | |
285 var line = lines[i]; | |
286 if (!line) { | |
287 continue; | |
288 } | |
289 if (line.charAt(0) == '#' || | |
290 line.substr(0, line.indexOf(',')) in this.backRefsCommands_) { | |
291 line = this.expandBackRef_(line); | |
292 } | |
293 var fields = csvParser.parseLine(line); | |
294 this.dispatchLogRow_(fields); | |
295 } | |
296 } catch (e) { | |
297 this.printError('line ' + (i + 1) + ': ' + (e.message || e)); | |
298 throw e; | |
299 } | |
300 }; | |
301 | |
302 | |
303 /** | |
304 * Processes repeat log record. Expands it according to calls count and | |
305 * invokes processing. | |
306 * | |
307 * @param {number} count Count. | |
308 * @param {Array.<string>} cmd Parsed command. | |
309 * @private | |
310 */ | |
311 devtools.profiler.LogReader.prototype.processRepeat_ = function(count, cmd) { | |
312 // Replace the repeat-prefixed command from backrefs list with a non-prefixed. | |
313 this.backRefs_[0] = cmd.join(','); | |
314 for (var i = 0; i < count; ++i) { | |
315 this.dispatchLogRow_(cmd); | |
316 } | |
317 }; | |
OLD | NEW |