1 // Copyright (c) 2013 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 'use strict'; 6 7 /** 8 * @fileoverview V8LogImporter imports v8.log files into the provided model. 9 */ 10 base.require('tracing.trace_model'); 11 base.require('tracing.trace_model.slice'); 12 base.require('tracing.color_scheme'); 13 base.require('tracing.importer.v8.log_reader'); 14 base.require('tracing.importer.v8.codemap'); 15 16 base.exportTo('tracing.importer', function() { 17 18 function V8LogImporter(model, eventData) { 19 20 this.importPriority = 3; 21 this.model_ = model; 22 23 this.logData_ = eventData; 24 25 this.code_map_ = new tracing.importer.v8.CodeMap(); 26 this.v8_timer_thread_ = undefined; 27 this.v8_stack_thread_ = undefined; 28 this.v8_samples_thread_ = undefined; 29 } 30 31 var kV8BinarySuffixes = ['/d8', '/libv8.so']; 32 var kStackFrames = 8; 33 34 var TimerEventDefaultArgs = { 35 'V8.Execute': { pause: false, no_execution: false}, 36 'V8.External': { pause: false, no_execution: true}, 37 'V8.CompileFullCode': { pause: true, no_execution: true}, 38 'V8.RecompileSynchronous': { pause: true, no_execution: true}, 39 'V8.RecompileParallel': { pause: false, no_execution: false}, 40 'V8.CompileEval': { pause: true, no_execution: true}, 41 'V8.Parse': { pause: true, no_execution: true}, 42 'V8.PreParse': { pause: true, no_execution: true}, 43 'V8.ParseLazy': { pause: true, no_execution: true}, 44 'V8.GCScavenger': { pause: true, no_execution: true}, 45 'V8.GCCompactor': { pause: true, no_execution: true}, 46 'V8.GCContext': { pause: true, no_execution: true} 47 }; 48 49 /** 50 * @return {boolean} Whether obj is a V8 log string. 51 */ 52 V8LogImporter.canImport = function(eventData) { 53 if (typeof(eventData) !== 'string' && !(eventData instanceof String)) 54 return false; 55 56 return eventData.substring(0, 12) == 'timer-event,' || 57 eventData.substring(0, 5) == 'tick,' || 58 eventData.substring(0, 15) == 'shared-library,' || 59 eventData.substring(0, 9) == 'profiler,'; 60 }; 61 62 V8LogImporter.prototype = { 63 64 __proto__: Object.prototype, 65 66 extractSubtrace: function() { 67 return undefined; 68 }, 69 70 processTimerEvent_: function(name, start, length) { 71 var args = TimerEventDefaultArgs[name]; 72 if (args === undefined) return; 73 start /= 1000; // Convert to milliseconds. 74 length /= 1000; 75 var colorId = tracing.getStringColorId(name); 76 var slice = new tracing.trace_model.Slice('v8', name, colorId, start, 77 args, length); 78 this.v8_timer_thread_.sliceGroup.pushSlice(slice); 79 }, 80 81 processTimerEventStart_: function(name, start) { 82 var args = TimerEventDefaultArgs[name]; 83 if (args === undefined) return; 84 start /= 1000; // Convert to milliseconds. 85 this.v8_timer_thread_.sliceGroup.beginSlice('v8', name, start, args); 86 }, 87 88 processTimerEventEnd_: function(name, end) { 89 end /= 1000; // Convert to milliseconds. 90 this.v8_timer_thread_.sliceGroup.endSlice(end); 91 }, 92 93 processCodeCreateEvent_: function(type, kind, address, size, name) { 94 var code_entry = new tracing.importer.v8.CodeMap.CodeEntry(size, name); 95 code_entry.kind = kind; 96 this.code_map_.addCode(address, code_entry); 97 }, 98 99 processCodeMoveEvent_: function(from, to) { 100 this.code_map_.moveCode(from, to); 101 }, 102 103 processCodeDeleteEvent_: function(address) { 104 this.code_map_.deleteCode(address); 105 }, 106 107 processSharedLibrary_: function(name, start, end) { 108 var code_entry = new tracing.importer.v8.CodeMap.CodeEntry( 109 end - start, name); 110 code_entry.kind = -3; // External code kind. 111 for (var i = 0; i < kV8BinarySuffixes.length; i++) { 112 var suffix = kV8BinarySuffixes[i]; 113 if (name.indexOf(suffix, name.length - suffix.length) >= 0) { 114 code_entry.kind = -1; // V8 runtime code kind. 115 break; 116 } 117 } 118 this.code_map_.addLibrary(start, code_entry); 119 }, 120 121 findCodeKind_: function(kind) { 122 for (name in CodeKinds) { 123 if (CodeKinds[name].kinds.indexOf(kind) >= 0) { 124 return CodeKinds[name]; 125 } 126 } 127 }, 128 129 nameForCodeEntry_: function(entry) { 130 if (entry) 131 return entry.name; 132 return 'UnknownCode'; 133 }, 134 135 processTickEvent_: function(pc, sp, start, unused_x, unused_y, vmstate, 136 stack) { 137 var entry = this.code_map_.findEntry(pc); 138 var name = this.nameForCodeEntry_(entry); 139 start /= 1000; 140 this.v8_samples_thread_.addSample('v8', name, start); 141 if (stack && stack.length) { 142 for (var i = 0; i < 8; i++) { 143 if (!stack[i]) break; 144 entry = this.code_map_.findEntry(stack[i]); 145 name = this.nameForCodeEntry_(entry); 146 var colorId = tracing.getStringColorId(name); 147 var slice = new tracing.trace_model.Slice( 148 'v8', name, colorId, start, {}, 0); 149 this.v8_stack_thread_.sliceGroup.pushSlice(slice); 150 } 151 } 152 }, 153 154 processDistortion_: function(distortion_in_picoseconds) { 155 distortion_per_entry = distortion_in_picoseconds / 1000000; 156 }, 157 158 processPlotRange_: function(start, end) { 159 xrange_start_override = start; 160 xrange_end_override = end; 161 }, 162 163 /** 164 * Walks through the events_ list and outputs the structures discovered to 165 * model_. 166 */ 167 importEvents: function() { 168 var logreader = new tracing.importer.v8.LogReader( 169 { 'timer-event' : { 170 parsers: [null, parseInt, parseInt], 171 processor: this.processTimerEvent_.bind(this) 172 }, 173 'shared-library': { 174 parsers: [null, parseInt, parseInt], 175 processor: this.processSharedLibrary_.bind(this) 176 }, 177 'timer-event-start' : { 178 parsers: [null, parseInt], 179 processor: this.processTimerEventStart_.bind(this) 180 }, 181 'timer-event-end' : { 182 parsers: [null, parseInt], 183 processor: this.processTimerEventEnd_.bind(this) 184 }, 185 'code-creation': { 186 parsers: [null, parseInt, parseInt, parseInt, null], 187 processor: this.processCodeCreateEvent_.bind(this) 188 }, 189 'code-move': { 190 parsers: [parseInt, parseInt], 191 processor: this.processCodeMoveEvent_.bind(this) 192 }, 193 'code-delete': { 194 parsers: [parseInt], 195 processor: this.processCodeDeleteEvent_.bind(this) 196 }, 197 'tick': { 198 parsers: [parseInt, parseInt, parseInt, null, null, parseInt, 199 'var-args'], 200 processor: this.processTickEvent_.bind(this) 201 }, 202 'distortion': { 203 parsers: [parseInt], 204 processor: this.processDistortion_.bind(this) 205 }, 206 'plot-range': { 207 parsers: [parseInt, parseInt], 208 processor: this.processPlotRange_.bind(this) 209 } 210 }); 211 212 this.v8_timer_thread_ = 213 this.model_.getOrCreateProcess(-32).getOrCreateThread(1); 214 this.v8_timer_thread_.name = 'V8 Timers'; 215 this.v8_stack_thread_ = 216 this.model_.getOrCreateProcess(-32).getOrCreateThread(2); 217 this.v8_stack_thread_.name = 'V8 JavaScript'; 218 this.v8_samples_thread_ = 219 this.model_.getOrCreateProcess(-32).getOrCreateThread(3); 220 this.v8_samples_thread_.name = 'V8 PC'; 221 222 var lines = this.logData_.split('\n'); 223 for (var i = 0; i < lines.length; i++) { 224 logreader.processLogLine(lines[i]); 225 } 226 }, 227 228 /** 229 * Called by the Model after all other importers have imported their 230 * events. 231 */ 232 finalizeImport: function() { 233 }, 234 235 /** 236 * Called by the model to join references between objects, after final model 237 * bounds have been computed. 238 */ 239 joinRefs: function() { 240 } 241 }; 242 243 tracing.TraceModel.registerImporter(V8LogImporter); 244 245 return { 246 V8LogImporter: V8LogImporter 247 }; 248 }); 249