v8_log_importer.html revision 8d2b206a675ec20ea07100c35df34e65ee1e45e8
1<!DOCTYPE html> 2<!-- 3Copyright (c) 2013 The Chromium Authors. All rights reserved. 4Use of this source code is governed by a BSD-style license that can be 5found in the LICENSE file. 6--> 7 8<link rel="import" href="/tracing/base/color_scheme.html"> 9<link rel="import" href="/tracing/extras/importer/v8/codemap.html"> 10<link rel="import" href="/tracing/extras/importer/v8/log_reader.html"> 11<link rel="import" href="/tracing/importer/importer.html"> 12<link rel="import" href="/tracing/model/model.html"> 13<link rel="import" href="/tracing/model/slice.html"> 14 15<script> 16 17'use strict'; 18 19/** 20 * @fileoverview V8LogImporter imports v8.log files into the provided model. 21 */ 22tr.exportTo('tr.e.importer.v8', function() { 23 var ColorScheme = tr.b.ColorScheme; 24 25 function V8LogImporter(model, eventData) { 26 this.importPriority = 3; 27 this.model_ = model; 28 29 this.logData_ = eventData; 30 31 this.code_map_ = new tr.e.importer.v8.CodeMap(); 32 this.v8_timer_thread_ = undefined; 33 this.v8_thread_ = undefined; 34 35 this.root_stack_frame_ = new tr.model.StackFrame( 36 undefined, 'v8-root-stack-frame', 'v8-root-stack-frame', 0); 37 38 // We reconstruct the stack timeline from ticks. 39 this.v8_stack_timeline_ = new Array(); 40 } 41 42 var kV8BinarySuffixes = ['/d8', '/libv8.so']; 43 44 45 var TimerEventDefaultArgs = { 46 'V8.Execute': { pause: false, no_execution: false}, 47 'V8.External': { pause: false, no_execution: true}, 48 'V8.CompileFullCode': { pause: true, no_execution: true}, 49 'V8.RecompileSynchronous': { pause: true, no_execution: true}, 50 'V8.RecompileParallel': { pause: false, no_execution: false}, 51 'V8.CompileEval': { pause: true, no_execution: true}, 52 'V8.Parse': { pause: true, no_execution: true}, 53 'V8.PreParse': { pause: true, no_execution: true}, 54 'V8.ParseLazy': { pause: true, no_execution: true}, 55 'V8.GCScavenger': { pause: true, no_execution: true}, 56 'V8.GCCompactor': { pause: true, no_execution: true}, 57 'V8.GCContext': { pause: true, no_execution: true} 58 }; 59 60 /** 61 * @return {boolean} Whether obj is a V8 log string. 62 */ 63 V8LogImporter.canImport = function(eventData) { 64 if (typeof(eventData) !== 'string' && !(eventData instanceof String)) 65 return false; 66 67 return eventData.substring(0, 11) == 'v8-version,' || 68 eventData.substring(0, 12) == 'timer-event,' || 69 eventData.substring(0, 5) == 'tick,' || 70 eventData.substring(0, 15) == 'shared-library,' || 71 eventData.substring(0, 9) == 'profiler,' || 72 eventData.substring(0, 14) == 'code-creation,'; 73 }; 74 75 V8LogImporter.prototype = { 76 77 __proto__: tr.importer.Importer.prototype, 78 79 processTimerEvent_: function(name, start, length) { 80 var args = TimerEventDefaultArgs[name]; 81 if (args === undefined) return; 82 start /= 1000; // Convert to milliseconds. 83 length /= 1000; 84 var colorId = ColorScheme.getColorIdForGeneralPurposeString(name); 85 var slice = new tr.model.Slice('v8', name, colorId, start, 86 args, length); 87 this.v8_timer_thread_.sliceGroup.pushSlice(slice); 88 }, 89 90 processTimerEventStart_: function(name, start) { 91 var args = TimerEventDefaultArgs[name]; 92 if (args === undefined) return; 93 start /= 1000; // Convert to milliseconds. 94 this.v8_timer_thread_.sliceGroup.beginSlice('v8', name, start, args); 95 }, 96 97 processTimerEventEnd_: function(name, end) { 98 end /= 1000; // Convert to milliseconds. 99 this.v8_timer_thread_.sliceGroup.endSlice(end); 100 }, 101 102 processCodeCreateEvent_: function(type, kind, address, size, name) { 103 var code_entry = new tr.e.importer.v8.CodeMap.CodeEntry(size, name); 104 code_entry.kind = kind; 105 this.code_map_.addCode(address, code_entry); 106 }, 107 108 processCodeMoveEvent_: function(from, to) { 109 this.code_map_.moveCode(from, to); 110 }, 111 112 processCodeDeleteEvent_: function(address) { 113 this.code_map_.deleteCode(address); 114 }, 115 116 processSharedLibrary_: function(name, start, end) { 117 var code_entry = new tr.e.importer.v8.CodeMap.CodeEntry( 118 end - start, name); 119 code_entry.kind = -3; // External code kind. 120 for (var i = 0; i < kV8BinarySuffixes.length; i++) { 121 var suffix = kV8BinarySuffixes[i]; 122 if (name.indexOf(suffix, name.length - suffix.length) >= 0) { 123 code_entry.kind = -1; // V8 runtime code kind. 124 break; 125 } 126 } 127 this.code_map_.addLibrary(start, code_entry); 128 }, 129 130 findCodeKind_: function(kind) { 131 for (name in CodeKinds) { 132 if (CodeKinds[name].kinds.indexOf(kind) >= 0) { 133 return CodeKinds[name]; 134 } 135 } 136 }, 137 138 processTickEvent_: function(pc, start, unused_x, unused_y, vmstate, stack) { 139 start /= 1000; 140 141 function findChildWithEntryID(stackFrame, entryID) { 142 for (var i = 0; i < stackFrame.children.length; i++) { 143 if (stackFrame.children[i].entryID == entryID) 144 return stackFrame.children[i]; 145 } 146 return undefined; 147 } 148 149 var lastStackFrame; 150 if (stack && stack.length) { 151 152 lastStackFrame = this.root_stack_frame_; 153 // v8 log stacks are inverted, leaf first and the root at the end. 154 stack = stack.reverse(); 155 for (var i = 0; i < stack.length; i++) { 156 if (!stack[i]) 157 break; 158 var entry = this.code_map_.findEntry(stack[i]); 159 160 var entryID = entry ? entry.id : 'Unknown'; 161 var childFrame = findChildWithEntryID(lastStackFrame, entryID); 162 if (childFrame === undefined) { 163 var entryName = entry ? entry.name : 'Unknown'; 164 lastStackFrame = new tr.model.StackFrame( 165 lastStackFrame, 'v8sf-' + tr.b.GUID.allocate(), entryName, 166 ColorScheme.getColorIdForGeneralPurposeString(entryName)); 167 lastStackFrame.entryID = entryID; 168 this.model_.addStackFrame(lastStackFrame); 169 } else { 170 lastStackFrame = childFrame; 171 } 172 } 173 } else { 174 var pcEntry = this.code_map_.findEntry(pc); 175 var pcEntryID = 'v8pc-' + (pcEntry ? pcEntry.id : 'Unknown'); 176 if (this.model_.stackFrames[pcEntryID] === undefined) { 177 var pcEntryName = pcEntry ? pcEntry.name : 'Unknown'; 178 lastStackFrame = new tr.model.StackFrame( 179 undefined, pcEntryID, pcEntryName, 180 ColorScheme.getColorIdForGeneralPurposeString(pcEntryName)); 181 this.model_.addStackFrame(lastStackFrame); 182 } 183 lastStackFrame = this.model_.stackFrames[pcEntryID]; 184 } 185 186 var sample = new tr.model.Sample( 187 undefined, this.v8_thread_, 'V8 PC', 188 start, lastStackFrame, 1); 189 this.model_.samples.push(sample); 190 }, 191 192 processDistortion_: function(distortion_in_picoseconds) { 193 distortion_per_entry = distortion_in_picoseconds / 1000000; 194 }, 195 196 processPlotRange_: function(start, end) { 197 xrange_start_override = start; 198 xrange_end_override = end; 199 }, 200 201 processV8Version_: function(major, minor, build, patch, candidate) { 202 // do nothing. 203 }, 204 205 /** 206 * Walks through the events_ list and outputs the structures discovered to 207 * model_. 208 */ 209 importEvents: function() { 210 var logreader = new tr.e.importer.v8.LogReader( 211 { 'timer-event' : { 212 parsers: [null, parseInt, parseInt], 213 processor: this.processTimerEvent_.bind(this) 214 }, 215 'shared-library': { 216 parsers: [null, parseInt, parseInt], 217 processor: this.processSharedLibrary_.bind(this) 218 }, 219 'timer-event-start' : { 220 parsers: [null, parseInt], 221 processor: this.processTimerEventStart_.bind(this) 222 }, 223 'timer-event-end' : { 224 parsers: [null, parseInt], 225 processor: this.processTimerEventEnd_.bind(this) 226 }, 227 'code-creation': { 228 parsers: [null, parseInt, parseInt, parseInt, null], 229 processor: this.processCodeCreateEvent_.bind(this) 230 }, 231 'code-move': { 232 parsers: [parseInt, parseInt], 233 processor: this.processCodeMoveEvent_.bind(this) 234 }, 235 'code-delete': { 236 parsers: [parseInt], 237 processor: this.processCodeDeleteEvent_.bind(this) 238 }, 239 'tick': { 240 parsers: [parseInt, parseInt, null, null, parseInt, 'var-args'], 241 processor: this.processTickEvent_.bind(this) 242 }, 243 'distortion': { 244 parsers: [parseInt], 245 processor: this.processDistortion_.bind(this) 246 }, 247 'plot-range': { 248 parsers: [parseInt, parseInt], 249 processor: this.processPlotRange_.bind(this) 250 }, 251 'v8-version': { 252 parsers: [parseInt, parseInt, parseInt, parseInt, parseInt], 253 processor: this.processV8Version_.bind(this) 254 } 255 }); 256 257 this.v8_timer_thread_ = 258 this.model_.getOrCreateProcess(-32).getOrCreateThread(1); 259 this.v8_timer_thread_.name = 'V8 Timers'; 260 this.v8_thread_ = 261 this.model_.getOrCreateProcess(-32).getOrCreateThread(2); 262 this.v8_thread_.name = 'V8'; 263 264 var lines = this.logData_.split('\n'); 265 for (var i = 0; i < lines.length; i++) { 266 logreader.processLogLine(lines[i]); 267 } 268 269 // The processing of samples adds all the root stack frame to 270 // this.root_stack_frame_. But, we don't want that stack frame in the real 271 // model. So get rid of it. 272 this.root_stack_frame_.removeAllChildren(); 273 274 function addSlices(slices, thread) { 275 for (var i = 0; i < slices.length; i++) { 276 var duration = slices[i].end - slices[i].start; 277 var slice = new tr.model.Slice('v8', slices[i].name, 278 ColorScheme.getColorIdForGeneralPurposeString(slices[i].name), 279 slices[i].start, {}, duration); 280 thread.sliceGroup.pushSlice(slice); 281 addSlices(slices[i].children, thread); 282 } 283 } 284 addSlices(this.v8_stack_timeline_, this.v8_thread_); 285 } 286 }; 287 288 tr.importer.Importer.register(V8LogImporter); 289 290 return { 291 V8LogImporter: V8LogImporter 292 }; 293}); 294</script> 295