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 get importerName() { 80 return 'V8LogImporter'; 81 }, 82 83 processTimerEvent_: function(name, start, length) { 84 var args = TimerEventDefaultArgs[name]; 85 if (args === undefined) return; 86 start /= 1000; // Convert to milliseconds. 87 length /= 1000; 88 var colorId = ColorScheme.getColorIdForGeneralPurposeString(name); 89 var slice = new tr.model.Slice('v8', name, colorId, start, 90 args, length); 91 this.v8_timer_thread_.sliceGroup.pushSlice(slice); 92 }, 93 94 processTimerEventStart_: function(name, start) { 95 var args = TimerEventDefaultArgs[name]; 96 if (args === undefined) return; 97 start /= 1000; // Convert to milliseconds. 98 this.v8_timer_thread_.sliceGroup.beginSlice('v8', name, start, args); 99 }, 100 101 processTimerEventEnd_: function(name, end) { 102 end /= 1000; // Convert to milliseconds. 103 this.v8_timer_thread_.sliceGroup.endSlice(end); 104 }, 105 106 processCodeCreateEvent_: function(type, kind, address, size, name) { 107 var code_entry = new tr.e.importer.v8.CodeMap.CodeEntry(size, name); 108 code_entry.kind = kind; 109 this.code_map_.addCode(address, code_entry); 110 }, 111 112 processCodeMoveEvent_: function(from, to) { 113 this.code_map_.moveCode(from, to); 114 }, 115 116 processCodeDeleteEvent_: function(address) { 117 this.code_map_.deleteCode(address); 118 }, 119 120 processSharedLibrary_: function(name, start, end) { 121 var code_entry = new tr.e.importer.v8.CodeMap.CodeEntry( 122 end - start, name); 123 code_entry.kind = -3; // External code kind. 124 for (var i = 0; i < kV8BinarySuffixes.length; i++) { 125 var suffix = kV8BinarySuffixes[i]; 126 if (name.indexOf(suffix, name.length - suffix.length) >= 0) { 127 code_entry.kind = -1; // V8 runtime code kind. 128 break; 129 } 130 } 131 this.code_map_.addLibrary(start, code_entry); 132 }, 133 134 findCodeKind_: function(kind) { 135 for (name in CodeKinds) { 136 if (CodeKinds[name].kinds.indexOf(kind) >= 0) { 137 return CodeKinds[name]; 138 } 139 } 140 }, 141 142 processTickEvent_: function(pc, start, unused_x, unused_y, vmstate, stack) { 143 start /= 1000; 144 145 function findChildWithEntryID(stackFrame, entryID) { 146 for (var i = 0; i < stackFrame.children.length; i++) { 147 if (stackFrame.children[i].entryID == entryID) 148 return stackFrame.children[i]; 149 } 150 return undefined; 151 } 152 153 var lastStackFrame; 154 if (stack && stack.length) { 155 156 lastStackFrame = this.root_stack_frame_; 157 // v8 log stacks are inverted, leaf first and the root at the end. 158 stack = stack.reverse(); 159 for (var i = 0; i < stack.length; i++) { 160 if (!stack[i]) 161 break; 162 var entry = this.code_map_.findEntry(stack[i]); 163 164 var entryID = entry ? entry.id : 'Unknown'; 165 var childFrame = findChildWithEntryID(lastStackFrame, entryID); 166 if (childFrame === undefined) { 167 var entryName = entry ? entry.name : 'Unknown'; 168 lastStackFrame = new tr.model.StackFrame( 169 lastStackFrame, 'v8sf-' + tr.b.GUID.allocate(), entryName, 170 ColorScheme.getColorIdForGeneralPurposeString(entryName)); 171 lastStackFrame.entryID = entryID; 172 this.model_.addStackFrame(lastStackFrame); 173 } else { 174 lastStackFrame = childFrame; 175 } 176 } 177 } else { 178 var pcEntry = this.code_map_.findEntry(pc); 179 var pcEntryID = 'v8pc-' + (pcEntry ? pcEntry.id : 'Unknown'); 180 if (this.model_.stackFrames[pcEntryID] === undefined) { 181 var pcEntryName = pcEntry ? pcEntry.name : 'Unknown'; 182 lastStackFrame = new tr.model.StackFrame( 183 undefined, pcEntryID, pcEntryName, 184 ColorScheme.getColorIdForGeneralPurposeString(pcEntryName)); 185 this.model_.addStackFrame(lastStackFrame); 186 } 187 lastStackFrame = this.model_.stackFrames[pcEntryID]; 188 } 189 190 var sample = new tr.model.Sample( 191 undefined, this.v8_thread_, 'V8 PC', 192 start, lastStackFrame, 1); 193 this.model_.samples.push(sample); 194 }, 195 196 processDistortion_: function(distortion_in_picoseconds) { 197 distortion_per_entry = distortion_in_picoseconds / 1000000; 198 }, 199 200 processPlotRange_: function(start, end) { 201 xrange_start_override = start; 202 xrange_end_override = end; 203 }, 204 205 processV8Version_: function(major, minor, build, patch, candidate) { 206 // do nothing. 207 }, 208 209 /** 210 * Walks through the events_ list and outputs the structures discovered to 211 * model_. 212 */ 213 importEvents: function() { 214 var logreader = new tr.e.importer.v8.LogReader( 215 { 'timer-event' : { 216 parsers: [null, parseInt, parseInt], 217 processor: this.processTimerEvent_.bind(this) 218 }, 219 'shared-library': { 220 parsers: [null, parseInt, parseInt], 221 processor: this.processSharedLibrary_.bind(this) 222 }, 223 'timer-event-start' : { 224 parsers: [null, parseInt], 225 processor: this.processTimerEventStart_.bind(this) 226 }, 227 'timer-event-end' : { 228 parsers: [null, parseInt], 229 processor: this.processTimerEventEnd_.bind(this) 230 }, 231 'code-creation': { 232 parsers: [null, parseInt, parseInt, parseInt, null], 233 processor: this.processCodeCreateEvent_.bind(this) 234 }, 235 'code-move': { 236 parsers: [parseInt, parseInt], 237 processor: this.processCodeMoveEvent_.bind(this) 238 }, 239 'code-delete': { 240 parsers: [parseInt], 241 processor: this.processCodeDeleteEvent_.bind(this) 242 }, 243 'tick': { 244 parsers: [parseInt, parseInt, null, null, parseInt, 'var-args'], 245 processor: this.processTickEvent_.bind(this) 246 }, 247 'distortion': { 248 parsers: [parseInt], 249 processor: this.processDistortion_.bind(this) 250 }, 251 'plot-range': { 252 parsers: [parseInt, parseInt], 253 processor: this.processPlotRange_.bind(this) 254 }, 255 'v8-version': { 256 parsers: [parseInt, parseInt, parseInt, parseInt, parseInt], 257 processor: this.processV8Version_.bind(this) 258 } 259 }); 260 261 this.v8_timer_thread_ = 262 this.model_.getOrCreateProcess(-32).getOrCreateThread(1); 263 this.v8_timer_thread_.name = 'V8 Timers'; 264 this.v8_thread_ = 265 this.model_.getOrCreateProcess(-32).getOrCreateThread(2); 266 this.v8_thread_.name = 'V8'; 267 268 var lines = this.logData_.split('\n'); 269 for (var i = 0; i < lines.length; i++) { 270 logreader.processLogLine(lines[i]); 271 } 272 273 // The processing of samples adds all the root stack frame to 274 // this.root_stack_frame_. But, we don't want that stack frame in the real 275 // model. So get rid of it. 276 this.root_stack_frame_.removeAllChildren(); 277 278 function addSlices(slices, thread) { 279 for (var i = 0; i < slices.length; i++) { 280 var duration = slices[i].end - slices[i].start; 281 var slice = new tr.model.Slice('v8', slices[i].name, 282 ColorScheme.getColorIdForGeneralPurposeString(slices[i].name), 283 slices[i].start, {}, duration); 284 thread.sliceGroup.pushSlice(slice); 285 addSlices(slices[i].children, thread); 286 } 287 } 288 addSlices(this.v8_stack_timeline_, this.v8_thread_); 289 } 290 }; 291 292 tr.importer.Importer.register(V8LogImporter); 293 294 return { 295 V8LogImporter: V8LogImporter 296 }; 297}); 298</script> 299