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.
8 * @fileoverview V8LogImporter imports v8.log files into the provided model.
10 tvcm.require('tracing.importer.importer');
11 tvcm.require('tracing.trace_model');
12 tvcm.require('tracing.trace_model.slice');
13 tvcm.require('tracing.color_scheme');
14 tvcm.require('tracing.importer.v8.log_reader');
15 tvcm.require('tracing.importer.v8.codemap');
17 tvcm.exportTo('tracing.importer', function() {
19 var Importer = tracing.importer.Importer;
21 function V8LogImporter(model, eventData) {
23 this.importPriority = 3;
26 this.logData_ = eventData;
28 this.code_map_ = new tracing.importer.v8.CodeMap();
29 this.v8_timer_thread_ = undefined;
30 this.v8_thread_ = undefined;
32 this.root_stack_frame_ = new tracing.trace_model.StackFrame(
33 undefined, 'v8-root-stack-frame',
34 'v8-root-stack-frame', 'v8-root-stack-frame', 0);
36 // We reconstruct the stack timeline from ticks.
37 this.v8_stack_timeline_ = new Array();
40 var kV8BinarySuffixes = ['/d8', '/libv8.so'];
43 var TimerEventDefaultArgs = {
44 'V8.Execute': { pause: false, no_execution: false},
45 'V8.External': { pause: false, no_execution: true},
46 'V8.CompileFullCode': { pause: true, no_execution: true},
47 'V8.RecompileSynchronous': { pause: true, no_execution: true},
48 'V8.RecompileParallel': { pause: false, no_execution: false},
49 'V8.CompileEval': { pause: true, no_execution: true},
50 'V8.Parse': { pause: true, no_execution: true},
51 'V8.PreParse': { pause: true, no_execution: true},
52 'V8.ParseLazy': { pause: true, no_execution: true},
53 'V8.GCScavenger': { pause: true, no_execution: true},
54 'V8.GCCompactor': { pause: true, no_execution: true},
55 'V8.GCContext': { pause: true, no_execution: true}
59 * @return {boolean} Whether obj is a V8 log string.
61 V8LogImporter.canImport = function(eventData) {
62 if (typeof(eventData) !== 'string' && !(eventData instanceof String))
65 return eventData.substring(0, 12) == 'timer-event,' ||
66 eventData.substring(0, 5) == 'tick,' ||
67 eventData.substring(0, 15) == 'shared-library,' ||
68 eventData.substring(0, 9) == 'profiler,' ||
69 eventData.substring(0, 14) == 'code-creation,';
72 V8LogImporter.prototype = {
74 __proto__: Importer.prototype,
76 processTimerEvent_: function(name, start, length) {
77 var args = TimerEventDefaultArgs[name];
78 if (args === undefined) return;
79 start /= 1000; // Convert to milliseconds.
81 var colorId = tvcm.ui.getStringColorId(name);
82 var slice = new tracing.trace_model.Slice('v8', name, colorId, start,
84 this.v8_timer_thread_.sliceGroup.pushSlice(slice);
87 processTimerEventStart_: function(name, start) {
88 var args = TimerEventDefaultArgs[name];
89 if (args === undefined) return;
90 start /= 1000; // Convert to milliseconds.
91 this.v8_timer_thread_.sliceGroup.beginSlice('v8', name, start, args);
94 processTimerEventEnd_: function(name, end) {
95 end /= 1000; // Convert to milliseconds.
96 this.v8_timer_thread_.sliceGroup.endSlice(end);
99 processCodeCreateEvent_: function(type, kind, address, size, name) {
100 var code_entry = new tracing.importer.v8.CodeMap.CodeEntry(size, name);
101 code_entry.kind = kind;
102 this.code_map_.addCode(address, code_entry);
105 processCodeMoveEvent_: function(from, to) {
106 this.code_map_.moveCode(from, to);
109 processCodeDeleteEvent_: function(address) {
110 this.code_map_.deleteCode(address);
113 processSharedLibrary_: function(name, start, end) {
114 var code_entry = new tracing.importer.v8.CodeMap.CodeEntry(
116 code_entry.kind = -3; // External code kind.
117 for (var i = 0; i < kV8BinarySuffixes.length; i++) {
118 var suffix = kV8BinarySuffixes[i];
119 if (name.indexOf(suffix, name.length - suffix.length) >= 0) {
120 code_entry.kind = -1; // V8 runtime code kind.
124 this.code_map_.addLibrary(start, code_entry);
127 findCodeKind_: function(kind) {
128 for (name in CodeKinds) {
129 if (CodeKinds[name].kinds.indexOf(kind) >= 0) {
130 return CodeKinds[name];
135 processTickEvent_: function(pc, start, unused_x, unused_y, vmstate, stack) {
138 function findChildWithEntryID(stackFrame, entryID) {
139 for (var i = 0; i < stackFrame.children.length; i++) {
140 if (stackFrame.children[i].entryID == entryID)
141 return stackFrame.children[i];
147 if (stack && stack.length) {
149 lastStackFrame = this.root_stack_frame_;
150 // v8 log stacks are inverted, leaf first and the root at the end.
151 stack = stack.reverse();
152 for (var i = 0; i < stack.length; i++) {
155 var entry = this.code_map_.findEntry(stack[i]);
157 var entryID = entry ? entry.id : 'Unknown';
158 var childFrame = findChildWithEntryID(lastStackFrame, entryID);
159 if (childFrame === undefined) {
160 var entryName = entry ? entry.name : 'Unknown';
161 lastStackFrame = new tracing.trace_model.StackFrame(
162 lastStackFrame, 'v8sf-' + tvcm.GUID.allocate(),
164 tvcm.ui.getStringColorId(name));
165 lastStackFrame.entryID = entryID;
166 this.model_.addStackFrame(lastStackFrame);
168 lastStackFrame = childFrame;
172 var pcEntry = this.code_map_.findEntry(pc);
173 var pcEntryID = 'v8pc-' + (pcEntry ? pcEntry.id : 'Unknown');
174 if (this.model_.stackFrames[pcEntryID] === undefined) {
175 var pcEntryName = pcEntry ? pcEntry.name : 'Unknown';
176 lastStackFrame = new tracing.trace_model.StackFrame(
177 undefined, pcEntryID,
179 tvcm.ui.getStringColorId(name));
180 this.model_.addStackFrame(lastStackFrame);
182 lastStackFrame = this.model_.stackFrames[pcEntryID];
185 var sample = new tracing.trace_model.Sample(
186 undefined, this.v8_thread_, 'V8 PC',
187 start, lastStackFrame, 1);
188 this.model_.samples.push(sample);
191 processDistortion_: function(distortion_in_picoseconds) {
192 distortion_per_entry = distortion_in_picoseconds / 1000000;
195 processPlotRange_: function(start, end) {
196 xrange_start_override = start;
197 xrange_end_override = end;
201 * Walks through the events_ list and outputs the structures discovered to
204 importEvents: function() {
205 var logreader = new tracing.importer.v8.LogReader(
207 parsers: [null, parseInt, parseInt],
208 processor: this.processTimerEvent_.bind(this)
211 parsers: [null, parseInt, parseInt],
212 processor: this.processSharedLibrary_.bind(this)
214 'timer-event-start' : {
215 parsers: [null, parseInt],
216 processor: this.processTimerEventStart_.bind(this)
218 'timer-event-end' : {
219 parsers: [null, parseInt],
220 processor: this.processTimerEventEnd_.bind(this)
223 parsers: [null, parseInt, parseInt, parseInt, null],
224 processor: this.processCodeCreateEvent_.bind(this)
227 parsers: [parseInt, parseInt],
228 processor: this.processCodeMoveEvent_.bind(this)
232 processor: this.processCodeDeleteEvent_.bind(this)
235 parsers: [parseInt, parseInt, null, null, parseInt, 'var-args'],
236 processor: this.processTickEvent_.bind(this)
240 processor: this.processDistortion_.bind(this)
243 parsers: [parseInt, parseInt],
244 processor: this.processPlotRange_.bind(this)
248 this.v8_timer_thread_ =
249 this.model_.getOrCreateProcess(-32).getOrCreateThread(1);
250 this.v8_timer_thread_.name = 'V8 Timers';
252 this.model_.getOrCreateProcess(-32).getOrCreateThread(2);
253 this.v8_thread_.name = 'V8';
255 var lines = this.logData_.split('\n');
256 for (var i = 0; i < lines.length; i++) {
257 logreader.processLogLine(lines[i]);
260 // The processing of samples adds all the root stack frame to
261 // this.root_stack_frame_. But, we don't want that stack frame in the real
262 // model. So get rid of it.
263 this.root_stack_frame_.removeAllChildren();
265 function addSlices(slices, thread) {
266 for (var i = 0; i < slices.length; i++) {
267 var duration = slices[i].end - slices[i].start;
268 var slice = new tracing.trace_model.Slice('v8', slices[i].name,
269 tvcm.ui.getStringColorId(slices[i].name),
270 slices[i].start, {}, duration);
271 thread.sliceGroup.pushSlice(slice);
272 addSlices(slices[i].children, thread);
275 addSlices(this.v8_stack_timeline_, this.v8_thread_);
279 tracing.TraceModel.registerImporter(V8LogImporter);
282 V8LogImporter: V8LogImporter