blob: 116eb5742661fd0b4e70d7bd7f0acaa294b67f31 [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2013 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<link rel="import" href="/tracing/base/color_scheme.html">
<link rel="import" href="/tracing/extras/importer/v8/codemap.html">
<link rel="import" href="/tracing/extras/importer/v8/log_reader.html">
<link rel="import" href="/tracing/importer/importer.html">
<link rel="import" href="/tracing/model/model.html">
<link rel="import" href="/tracing/model/slice.html">
<script>
'use strict';
/**
* @fileoverview V8LogImporter imports v8.log files into the provided model.
*/
tr.exportTo('tr.e.importer.v8', function() {
var ColorScheme = tr.b.ColorScheme;
function V8LogImporter(model, eventData) {
this.importPriority = 3;
this.model_ = model;
this.logData_ = eventData;
this.code_map_ = new tr.e.importer.v8.CodeMap();
this.v8_timer_thread_ = undefined;
this.v8_thread_ = undefined;
this.root_stack_frame_ = new tr.model.StackFrame(
undefined, 'v8-root-stack-frame', 'v8-root-stack-frame', 0);
// We reconstruct the stack timeline from ticks.
this.v8_stack_timeline_ = new Array();
}
var kV8BinarySuffixes = ['/d8', '/libv8.so'];
var TimerEventDefaultArgs = {
'V8.Execute': { pause: false, no_execution: false},
'V8.External': { pause: false, no_execution: true},
'V8.CompileFullCode': { pause: true, no_execution: true},
'V8.RecompileSynchronous': { pause: true, no_execution: true},
'V8.RecompileParallel': { pause: false, no_execution: false},
'V8.CompileEval': { pause: true, no_execution: true},
'V8.Parse': { pause: true, no_execution: true},
'V8.PreParse': { pause: true, no_execution: true},
'V8.ParseLazy': { pause: true, no_execution: true},
'V8.GCScavenger': { pause: true, no_execution: true},
'V8.GCCompactor': { pause: true, no_execution: true},
'V8.GCContext': { pause: true, no_execution: true}
};
/**
* @return {boolean} Whether obj is a V8 log string.
*/
V8LogImporter.canImport = function(eventData) {
if (typeof(eventData) !== 'string' && !(eventData instanceof String))
return false;
return eventData.substring(0, 11) == 'v8-version,' ||
eventData.substring(0, 12) == 'timer-event,' ||
eventData.substring(0, 5) == 'tick,' ||
eventData.substring(0, 15) == 'shared-library,' ||
eventData.substring(0, 9) == 'profiler,' ||
eventData.substring(0, 14) == 'code-creation,';
};
V8LogImporter.prototype = {
__proto__: tr.importer.Importer.prototype,
get importerName() {
return 'V8LogImporter';
},
processTimerEvent_: function(name, start, length) {
var args = TimerEventDefaultArgs[name];
if (args === undefined) return;
start /= 1000; // Convert to milliseconds.
length /= 1000;
var colorId = ColorScheme.getColorIdForGeneralPurposeString(name);
var slice = new tr.model.Slice('v8', name, colorId, start,
args, length);
this.v8_timer_thread_.sliceGroup.pushSlice(slice);
},
processTimerEventStart_: function(name, start) {
var args = TimerEventDefaultArgs[name];
if (args === undefined) return;
start /= 1000; // Convert to milliseconds.
this.v8_timer_thread_.sliceGroup.beginSlice('v8', name, start, args);
},
processTimerEventEnd_: function(name, end) {
end /= 1000; // Convert to milliseconds.
this.v8_timer_thread_.sliceGroup.endSlice(end);
},
processCodeCreateEvent_: function(type, kind, address, size, name) {
var code_entry = new tr.e.importer.v8.CodeMap.CodeEntry(size, name);
code_entry.kind = kind;
this.code_map_.addCode(address, code_entry);
},
processCodeMoveEvent_: function(from, to) {
this.code_map_.moveCode(from, to);
},
processCodeDeleteEvent_: function(address) {
this.code_map_.deleteCode(address);
},
processSharedLibrary_: function(name, start, end) {
var code_entry = new tr.e.importer.v8.CodeMap.CodeEntry(
end - start, name);
code_entry.kind = -3; // External code kind.
for (var i = 0; i < kV8BinarySuffixes.length; i++) {
var suffix = kV8BinarySuffixes[i];
if (name.indexOf(suffix, name.length - suffix.length) >= 0) {
code_entry.kind = -1; // V8 runtime code kind.
break;
}
}
this.code_map_.addLibrary(start, code_entry);
},
findCodeKind_: function(kind) {
for (name in CodeKinds) {
if (CodeKinds[name].kinds.indexOf(kind) >= 0) {
return CodeKinds[name];
}
}
},
processTickEvent_: function(pc, start, unused_x, unused_y, vmstate, stack) {
start /= 1000;
function findChildWithEntryID(stackFrame, entryID) {
for (var i = 0; i < stackFrame.children.length; i++) {
if (stackFrame.children[i].entryID == entryID)
return stackFrame.children[i];
}
return undefined;
}
var lastStackFrame;
if (stack && stack.length) {
lastStackFrame = this.root_stack_frame_;
// v8 log stacks are inverted, leaf first and the root at the end.
stack = stack.reverse();
for (var i = 0; i < stack.length; i++) {
if (!stack[i])
break;
var entry = this.code_map_.findEntry(stack[i]);
var entryID = entry ? entry.id : 'Unknown';
var childFrame = findChildWithEntryID(lastStackFrame, entryID);
if (childFrame === undefined) {
var entryName = entry ? entry.name : 'Unknown';
lastStackFrame = new tr.model.StackFrame(
lastStackFrame, 'v8sf-' + tr.b.GUID.allocate(), entryName,
ColorScheme.getColorIdForGeneralPurposeString(entryName));
lastStackFrame.entryID = entryID;
this.model_.addStackFrame(lastStackFrame);
} else {
lastStackFrame = childFrame;
}
}
} else {
var pcEntry = this.code_map_.findEntry(pc);
var pcEntryID = 'v8pc-' + (pcEntry ? pcEntry.id : 'Unknown');
if (this.model_.stackFrames[pcEntryID] === undefined) {
var pcEntryName = pcEntry ? pcEntry.name : 'Unknown';
lastStackFrame = new tr.model.StackFrame(
undefined, pcEntryID, pcEntryName,
ColorScheme.getColorIdForGeneralPurposeString(pcEntryName));
this.model_.addStackFrame(lastStackFrame);
}
lastStackFrame = this.model_.stackFrames[pcEntryID];
}
var sample = new tr.model.Sample(
undefined, this.v8_thread_, 'V8 PC',
start, lastStackFrame, 1);
this.model_.samples.push(sample);
},
processDistortion_: function(distortion_in_picoseconds) {
distortion_per_entry = distortion_in_picoseconds / 1000000;
},
processPlotRange_: function(start, end) {
xrange_start_override = start;
xrange_end_override = end;
},
processV8Version_: function(major, minor, build, patch, candidate) {
// do nothing.
},
/**
* Walks through the events_ list and outputs the structures discovered to
* model_.
*/
importEvents: function() {
var logreader = new tr.e.importer.v8.LogReader(
{ 'timer-event' : {
parsers: [null, parseInt, parseInt],
processor: this.processTimerEvent_.bind(this)
},
'shared-library': {
parsers: [null, parseInt, parseInt],
processor: this.processSharedLibrary_.bind(this)
},
'timer-event-start' : {
parsers: [null, parseInt],
processor: this.processTimerEventStart_.bind(this)
},
'timer-event-end' : {
parsers: [null, parseInt],
processor: this.processTimerEventEnd_.bind(this)
},
'code-creation': {
parsers: [null, parseInt, parseInt, parseInt, null],
processor: this.processCodeCreateEvent_.bind(this)
},
'code-move': {
parsers: [parseInt, parseInt],
processor: this.processCodeMoveEvent_.bind(this)
},
'code-delete': {
parsers: [parseInt],
processor: this.processCodeDeleteEvent_.bind(this)
},
'tick': {
parsers: [parseInt, parseInt, null, null, parseInt, 'var-args'],
processor: this.processTickEvent_.bind(this)
},
'distortion': {
parsers: [parseInt],
processor: this.processDistortion_.bind(this)
},
'plot-range': {
parsers: [parseInt, parseInt],
processor: this.processPlotRange_.bind(this)
},
'v8-version': {
parsers: [parseInt, parseInt, parseInt, parseInt, parseInt],
processor: this.processV8Version_.bind(this)
}
});
this.v8_timer_thread_ =
this.model_.getOrCreateProcess(-32).getOrCreateThread(1);
this.v8_timer_thread_.name = 'V8 Timers';
this.v8_thread_ =
this.model_.getOrCreateProcess(-32).getOrCreateThread(2);
this.v8_thread_.name = 'V8';
var lines = this.logData_.split('\n');
for (var i = 0; i < lines.length; i++) {
logreader.processLogLine(lines[i]);
}
// The processing of samples adds all the root stack frame to
// this.root_stack_frame_. But, we don't want that stack frame in the real
// model. So get rid of it.
this.root_stack_frame_.removeAllChildren();
function addSlices(slices, thread) {
for (var i = 0; i < slices.length; i++) {
var duration = slices[i].end - slices[i].start;
var slice = new tr.model.Slice('v8', slices[i].name,
ColorScheme.getColorIdForGeneralPurposeString(slices[i].name),
slices[i].start, {}, duration);
thread.sliceGroup.pushSlice(slice);
addSlices(slices[i].children, thread);
}
}
addSlices(this.v8_stack_timeline_, this.v8_thread_);
}
};
tr.importer.Importer.register(V8LogImporter);
return {
V8LogImporter: V8LogImporter
};
});
</script>