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_stack_thread_ = undefined;
31 this.v8_samples_thread_ = undefined;
33 // we maintain the timestamp of the last tick to verify if a stack is
34 // a continuation from the last sample, or it is a new stack.
35 this.last_tick_timestamp_ = undefined;
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 = tracing.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 nameForCodeEntry_: function(entry) {
138 return 'UnknownCode';
141 processTickEvent_: function(pc, start, unused_x, unused_y, vmstate, stack) {
142 // Transform ticks into events by aggregating stack frames across ticks
145 // Tick Time: 0 1 2 3 4 5 6 7 8 9
146 // Stack: A A A A B B C C C
152 // Time: 0 1 3 4 5 7 9
153 // Slices: [ A ] [B] [ C ]
157 var entry = this.code_map_.findEntry(pc);
158 var name = this.nameForCodeEntry_(entry);
160 this.v8_samples_thread_.addSample('v8', name, start);
162 if (stack && stack.length) {
163 var stack_frame = this.v8_stack_timeline_;
164 for (var i = stack.length - 1; i >= 0; i--) {
165 if (!stack[i]) break;
166 entry = this.code_map_.findEntry(stack[i]);
167 name = this.nameForCodeEntry_(entry);
168 if (stack_frame.length > 0 &&
169 stack_frame[stack_frame.length - 1].name == name &&
170 stack_frame[stack_frame.length - 1].end ==
171 this.last_tick_timestamp_) {
172 // If the entry from the previous stack at the same level had the
173 // same name we extend it to include the current tick as well.
174 stack_frame[stack_frame.length - 1].end = start;
176 // otherwise we create a new entry.
181 children: new Array()
183 stack_frame.push(record);
185 stack_frame = stack_frame[stack_frame.length - 1].children;
187 // The last tick time stamp gets updated only when we have a stack
188 this.last_tick_timestamp_ = start;
192 processDistortion_: function(distortion_in_picoseconds) {
193 distortion_per_entry = distortion_in_picoseconds / 1000000;
196 processPlotRange_: function(start, end) {
197 xrange_start_override = start;
198 xrange_end_override = end;
202 * Walks through the events_ list and outputs the structures discovered to
205 importEvents: function() {
206 var logreader = new tracing.importer.v8.LogReader(
208 parsers: [null, parseInt, parseInt],
209 processor: this.processTimerEvent_.bind(this)
212 parsers: [null, parseInt, parseInt],
213 processor: this.processSharedLibrary_.bind(this)
215 'timer-event-start' : {
216 parsers: [null, parseInt],
217 processor: this.processTimerEventStart_.bind(this)
219 'timer-event-end' : {
220 parsers: [null, parseInt],
221 processor: this.processTimerEventEnd_.bind(this)
224 parsers: [null, parseInt, parseInt, parseInt, null],
225 processor: this.processCodeCreateEvent_.bind(this)
228 parsers: [parseInt, parseInt],
229 processor: this.processCodeMoveEvent_.bind(this)
233 processor: this.processCodeDeleteEvent_.bind(this)
236 parsers: [parseInt, parseInt, null, null, parseInt, 'var-args'],
237 processor: this.processTickEvent_.bind(this)
241 processor: this.processDistortion_.bind(this)
244 parsers: [parseInt, parseInt],
245 processor: this.processPlotRange_.bind(this)
249 this.v8_timer_thread_ =
250 this.model_.getOrCreateProcess(-32).getOrCreateThread(1);
251 this.v8_timer_thread_.name = 'V8 Timers';
252 this.v8_stack_thread_ =
253 this.model_.getOrCreateProcess(-32).getOrCreateThread(2);
254 this.v8_stack_thread_.name = 'V8 JavaScript';
255 this.v8_samples_thread_ =
256 this.model_.getOrCreateProcess(-32).getOrCreateThread(3);
257 this.v8_samples_thread_.name = 'V8 PC';
259 var lines = this.logData_.split('\n');
260 for (var i = 0; i < lines.length; i++) {
261 logreader.processLogLine(lines[i]);
264 function addSlices(slices, thread) {
265 for (var i = 0; i < slices.length; i++) {
266 var duration = slices[i].end - slices[i].start;
267 var slice = new tracing.trace_model.Slice('v8', slices[i].name,
268 tracing.getStringColorId(slices[i].name),
269 slices[i].start, {}, duration);
270 thread.sliceGroup.pushSlice(slice);
271 addSlices(slices[i].children, thread);
274 addSlices(this.v8_stack_timeline_, this.v8_stack_thread_);
275 this.v8_stack_thread_.createSubSlices();
276 this.v8_timer_thread_.createSubSlices();
280 tracing.TraceModel.registerImporter(V8LogImporter);
283 V8LogImporter: V8LogImporter