blob: 5851ac333f11ba67d037cb073db542a1a5a9a3bf [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2012 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/linux_perf/android_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/binder_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/bus_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/clock_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/cpufreq_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/disk_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/drm_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/exynos_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/gesture_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/i915_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/irq_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/kfunc_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/mali_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/memreclaim_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/power_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/regulator_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/sched_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/sync_parser.html">
<link rel="import" href="/tracing/extras/importer/linux_perf/workqueue_parser.html">
<link rel="import" href="/tracing/importer/importer.html">
<link rel="import" href="/tracing/importer/simple_line_reader.html">
<link rel="import" href="/tracing/model/model.html">
<script>
/**
* @fileoverview Imports text files in the Linux event trace format into the
* Model. This format is output both by sched_trace and by Linux's perf tool.
*
* This importer assumes the events arrive as a string. The unit tests provide
* examples of the trace format.
*
* Linux scheduler traces use a definition for 'pid' that is different than
* tracing uses. Whereas tracing uses pid to identify a specific process, a pid
* in a linux trace refers to a specific thread within a process. Within this
* file, we the definition used in Linux traces, as it improves the importing
* code's readability.
*/
'use strict';
tr.exportTo('tr.e.importer.linux_perf', function() {
var ClockSyncRecord = tr.ClockSyncRecord;
/**
* Imports linux perf events into a specified model.
* @constructor
*/
function LinuxPerfImporter(model, events) {
this.importPriority = 2;
this.model_ = model;
this.events_ = events;
this.newlyAddedClockSyncRecords_ = [];
this.wakeups_ = [];
this.blocked_reasons_ = [];
this.kernelThreadStates_ = {};
this.buildMapFromLinuxPidsToThreads();
this.lines_ = [];
this.pseudoThreadCounter = 1;
this.parsers_ = [];
this.eventHandlers_ = {};
}
var TestExports = {};
// Matches the trace record in 3.2 and later with the print-tgid option:
// <idle>-0 0 [001] d... 1.23: sched_switch
//
// A TGID (Thread Group ID) is basically what the Linux kernel calls what
// userland refers to as a process ID (as opposed to a Linux pid, which is
// what userland calls a thread ID).
var lineREWithTGID = new RegExp(
'^\\s*(.+)-(\\d+)\\s+\\(\\s*(\\d+|-+)\\)\\s\\[(\\d+)\\]' +
'\\s+[dX.][Nnp.][Hhs.][0-9a-f.]' +
'\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$');
var lineParserWithTGID = function(line) {
var groups = lineREWithTGID.exec(line);
if (!groups) {
return groups;
}
var tgid = groups[3];
if (tgid[0] === '-')
tgid = undefined;
return {
threadName: groups[1],
pid: groups[2],
tgid: tgid,
cpuNumber: groups[4],
timestamp: groups[5],
eventName: groups[6],
details: groups[7]
};
};
TestExports.lineParserWithTGID = lineParserWithTGID;
// Matches the default trace record in 3.2 and later (includes irq-info):
// <idle>-0 [001] d... 1.23: sched_switch
var lineREWithIRQInfo = new RegExp(
'^\\s*(.+)-(\\d+)\\s+\\[(\\d+)\\]' +
'\\s+[dX.][Nnp.][Hhs.][0-9a-f.]' +
'\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$');
var lineParserWithIRQInfo = function(line) {
var groups = lineREWithIRQInfo.exec(line);
if (!groups) {
return groups;
}
return {
threadName: groups[1],
pid: groups[2],
cpuNumber: groups[3],
timestamp: groups[4],
eventName: groups[5],
details: groups[6]
};
};
TestExports.lineParserWithIRQInfo = lineParserWithIRQInfo;
// Matches the default trace record pre-3.2:
// <idle>-0 [001] 1.23: sched_switch
var lineREWithLegacyFmt =
/^\s*(.+)-(\d+)\s+\[(\d+)\]\s*(\d+\.\d+):\s+(\S+):\s(.*)$/;
var lineParserWithLegacyFmt = function(line) {
var groups = lineREWithLegacyFmt.exec(line);
if (!groups) {
return groups;
}
return {
threadName: groups[1],
pid: groups[2],
cpuNumber: groups[3],
timestamp: groups[4],
eventName: groups[5],
details: groups[6]
};
};
TestExports.lineParserWithLegacyFmt = lineParserWithLegacyFmt;
// Matches the trace_event_clock_sync record
// 0: trace_event_clock_sync: parent_ts=19581477508
var traceEventClockSyncRE = /trace_event_clock_sync: parent_ts=(\d+\.?\d*)/;
TestExports.traceEventClockSyncRE = traceEventClockSyncRE;
var realTimeClockSyncRE = /trace_event_clock_sync: realtime_ts=(\d+)/;
var genericClockSyncRE = /trace_event_clock_sync: name=(\w+)/;
// Some kernel trace events are manually classified in slices and
// hand-assigned a pseudo PID.
var pseudoKernelPID = 0;
/**
* Deduce the format of trace data. Linux kernels prior to 3.3 used one
* format (by default); 3.4 and later used another. Additionally, newer
* kernels can optionally trace the TGID.
*
* @return {function} the function for parsing data when the format is
* recognized; otherwise null.
*/
function autoDetectLineParser(line) {
if (line[0] == '{')
return false;
if (lineREWithTGID.test(line))
return lineParserWithTGID;
if (lineREWithIRQInfo.test(line))
return lineParserWithIRQInfo;
if (lineREWithLegacyFmt.test(line))
return lineParserWithLegacyFmt;
return null;
};
TestExports.autoDetectLineParser = autoDetectLineParser;
/**
* Guesses whether the provided events is a Linux perf string.
* Looks for the magic string "# tracer" at the start of the file,
* or the typical task-pid-cpu-timestamp-function sequence of a typical
* trace's body.
*
* @return {boolean} True when events is a linux perf array.
*/
LinuxPerfImporter.canImport = function(events) {
if (!(typeof(events) === 'string' || events instanceof String))
return false;
if (LinuxPerfImporter._extractEventsFromSystraceHTML(events, false).ok)
return true;
if (LinuxPerfImporter._extractEventsFromSystraceMultiHTML(events, false).ok)
return true;
if (/^# tracer:/.test(events))
return true;
var lineBreakIndex = events.indexOf('\n');
if (lineBreakIndex > -1)
events = events.substring(0, lineBreakIndex);
if (autoDetectLineParser(events))
return true;
return false;
};
LinuxPerfImporter._extractEventsFromSystraceHTML = function(
incoming_events, produce_result) {
var failure = {ok: false};
if (produce_result === undefined)
produce_result = true;
if (/^<!DOCTYPE html>/.test(incoming_events) == false)
return failure;
var r = new tr.importer.SimpleLineReader(incoming_events);
// Try to find the data...
if (!r.advanceToLineMatching(/^ <script>$/))
return failure;
if (!r.advanceToLineMatching(/^ var linuxPerfData = "\\$/))
return failure;
var events_begin_at_line = r.curLineNumber + 1;
r.beginSavingLines();
if (!r.advanceToLineMatching(/^ <\/script>$/))
return failure;
var raw_events = r.endSavingLinesAndGetResult();
// Drop off first and last event as it contains the tag.
raw_events = raw_events.slice(1, raw_events.length - 1);
if (!r.advanceToLineMatching(/^<\/body>$/))
return failure;
if (!r.advanceToLineMatching(/^<\/html>$/))
return failure;
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
function stripSuffix(str, suffix) {
if (!endsWith(str, suffix))
return str;
return str.substring(str, str.length - suffix.length);
}
// Strip off escaping in the file needed to preserve linebreaks.
var events = [];
if (produce_result) {
for (var i = 0; i < raw_events.length; i++) {
var event = raw_events[i];
event = stripSuffix(event, '\\n\\');
events.push(event);
}
} else {
events = [raw_events[raw_events.length - 1]];
}
// Last event ends differently. Strip that off too,
// treating absence of that trailing string as a failure.
var oldLastEvent = events[events.length - 1];
var newLastEvent = stripSuffix(oldLastEvent, '\\n";');
if (newLastEvent == oldLastEvent)
return failure;
events[events.length - 1] = newLastEvent;
return {ok: true,
lines: produce_result ? events : undefined,
events_begin_at_line: events_begin_at_line};
};
LinuxPerfImporter._extractEventsFromSystraceMultiHTML = function(
incoming_events, produce_result) {
var failure = {ok: false};
if (produce_result === undefined)
produce_result = true;
if (new RegExp('^<!DOCTYPE HTML>', 'i').test(incoming_events) == false)
return failure;
var r = new tr.importer.SimpleLineReader(incoming_events);
// Try to find the Linux perf trace in any of the trace-data tags
var events = [];
while (!/^# tracer:/.test(events)) {
if (!r.advanceToLineMatching(
/^ <script class="trace-data" type="application\/text">$/))
return failure;
var events_begin_at_line = r.curLineNumber + 1;
r.beginSavingLines();
if (!r.advanceToLineMatching(/^ <\/script>$/))
return failure;
events = r.endSavingLinesAndGetResult();
// Drop off first and last event as it contains the tag.
events = events.slice(1, events.length - 1);
}
if (!r.advanceToLineMatching(/^<\/body>$/))
return failure;
if (!r.advanceToLineMatching(/^<\/html>$/))
return failure;
return {ok: true,
lines: produce_result ? events : undefined,
events_begin_at_line: events_begin_at_line};
};
LinuxPerfImporter.prototype = {
__proto__: tr.importer.Importer.prototype,
get model() {
return this.model_;
},
/**
* Precomputes a lookup table from linux pids back to existing
* Threads. This is used during importing to add information to each
* thread about whether it was running, descheduled, sleeping, et
* cetera.
*/
buildMapFromLinuxPidsToThreads: function() {
this.threadsByLinuxPid = {};
this.model_.getAllThreads().forEach(
function(thread) {
this.threadsByLinuxPid[thread.tid] = thread;
}.bind(this));
},
/**
* @return {Cpu} A Cpu corresponding to the given cpuNumber.
*/
getOrCreateCpu: function(cpuNumber) {
return this.model_.kernel.getOrCreateCpu(cpuNumber);
},
/**
* @return {TimelineThread} A thread corresponding to the kernelThreadName.
*/
getOrCreateKernelThread: function(kernelThreadName, pid, tid) {
if (!this.kernelThreadStates_[kernelThreadName]) {
var thread = this.model_.getOrCreateProcess(pid).getOrCreateThread(tid);
thread.name = kernelThreadName;
this.kernelThreadStates_[kernelThreadName] = {
pid: pid,
thread: thread,
openSlice: undefined,
openSliceTS: undefined
};
this.threadsByLinuxPid[pid] = thread;
}
return this.kernelThreadStates_[kernelThreadName];
},
/**
* Processes can have multiple binder threads.
* Binder thread names are not unique across processes we therefore need to
* keep more information in order to return the correct threads.
*/
getOrCreateBinderKernelThread: function(kernelThreadName, pid, tid) {
var key = kernelThreadName + pid + tid;
if (!this.kernelThreadStates_[key]) {
var thread = this.model_.getOrCreateProcess(pid).getOrCreateThread(tid);
thread.name = kernelThreadName;
this.kernelThreadStates_[key] = {
pid: pid,
thread: thread,
openSlice: undefined,
openSliceTS: undefined
};
this.threadsByLinuxPid[pid] = thread;
}
return this.kernelThreadStates_[key];
},
/**
* @return {TimelineThread} A pseudo thread corresponding to the
* threadName. Pseudo threads are for events that we want to break
* out to a separate timeline but would not otherwise happen.
* These threads are assigned to pseudoKernelPID and given a
* unique (incrementing) TID.
*/
getOrCreatePseudoThread: function(threadName) {
var thread = this.kernelThreadStates_[threadName];
if (!thread) {
thread = this.getOrCreateKernelThread(threadName, pseudoKernelPID,
this.pseudoThreadCounter);
this.pseudoThreadCounter++;
}
return thread;
},
/**
* Imports the data in this.events_ into model_.
*/
importEvents: function(isSecondaryImport) {
this.parsers_ = this.createParsers_();
this.registerDefaultHandlers_();
this.parseLines();
this.importClockSyncRecords();
var timeShift = this.computeTimeTransform();
if (timeShift === undefined) {
this.model_.importWarning({
type: 'clock_sync',
message: 'Cannot import kernel trace without a clock sync.'
});
return;
}
this.shiftNewlyAddedClockSyncRecords(timeShift);
this.importCpuData(timeShift);
this.buildMapFromLinuxPidsToThreads();
this.buildPerThreadCpuSlicesFromCpuState();
this.computeCpuTimestampsForSlicesAsNeeded();
},
/**
* Builds the timeSlices array on each thread based on our knowledge of what
* each Cpu is doing. This is done only for Threads that are
* already in the model, on the assumption that not having any traced data
* on a thread means that it is not of interest to the user.
*/
buildPerThreadCpuSlicesFromCpuState: function() {
var SCHEDULING_STATE = tr.model.SCHEDULING_STATE;
// Push the cpu slices to the threads that they run on.
for (var cpuNumber in this.model_.kernel.cpus) {
var cpu = this.model_.kernel.cpus[cpuNumber];
for (var i = 0; i < cpu.slices.length; i++) {
var cpuSlice = cpu.slices[i];
var thread = this.threadsByLinuxPid[cpuSlice.args.tid];
if (!thread)
continue;
cpuSlice.threadThatWasRunning = thread;
if (!thread.tempCpuSlices)
thread.tempCpuSlices = [];
thread.tempCpuSlices.push(cpuSlice);
}
}
for (var i in this.wakeups_) {
var wakeup = this.wakeups_[i];
var thread = this.threadsByLinuxPid[wakeup.tid];
if (!thread)
continue;
thread.tempWakeups = thread.tempWakeups || [];
thread.tempWakeups.push(wakeup);
}
for (var i in this.blocked_reasons_) {
var reason = this.blocked_reasons_[i];
var thread = this.threadsByLinuxPid[reason.tid];
if (!thread)
continue;
thread.tempBlockedReasons = thread.tempBlockedReasons || [];
thread.tempBlockedReasons.push(reason);
}
// Create slices for when the thread is not running.
this.model_.getAllThreads().forEach(function(thread) {
if (thread.tempCpuSlices === undefined)
return;
var origSlices = thread.tempCpuSlices;
delete thread.tempCpuSlices;
origSlices.sort(function(x, y) {
return x.start - y.start;
});
var wakeups = thread.tempWakeups || [];
delete thread.tempWakeups;
wakeups.sort(function(x, y) {
return x.ts - y.ts;
});
var reasons = thread.tempBlockedReasons || [];
delete thread.tempBlockedReasons;
reasons.sort(function(x, y) {
return x.ts - y.ts;
});
// Walk the slice list and put slices between each original slice to
// show when the thread isn't running.
var slices = [];
if (origSlices.length) {
var slice = origSlices[0];
if (wakeups.length && wakeups[0].ts < slice.start) {
var wakeup = wakeups.shift();
var wakeupDuration = slice.start - wakeup.ts;
var args = {'wakeup from tid': wakeup.fromTid};
slices.push(new tr.model.ThreadTimeSlice(
thread, SCHEDULING_STATE.RUNNABLE, '',
wakeup.ts, args, wakeupDuration));
}
var runningSlice = new tr.model.ThreadTimeSlice(
thread, SCHEDULING_STATE.RUNNING, '',
slice.start, {}, slice.duration);
runningSlice.cpuOnWhichThreadWasRunning = slice.cpu;
slices.push(runningSlice);
}
var wakeup = undefined;
for (var i = 1; i < origSlices.length; i++) {
var prevSlice = origSlices[i - 1];
var nextSlice = origSlices[i];
var midDuration = nextSlice.start - prevSlice.end;
while (wakeups.length && wakeups[0].ts < nextSlice.start) {
var w = wakeups.shift();
if (wakeup === undefined && w.ts > prevSlice.end) {
wakeup = w;
}
}
var blocked_reason = undefined;
while (reasons.length && reasons[0].ts < prevSlice.end) {
var r = reasons.shift();
}
if (wakeup !== undefined &&
reasons.length &&
reasons[0].ts < wakeup.ts) {
blocked_reason = reasons.shift();
}
// Push a sleep slice onto the slices list, interrupting it with a
// wakeup if appropriate.
var pushSleep = function(state) {
if (wakeup !== undefined) {
midDuration = wakeup.ts - prevSlice.end;
}
if (blocked_reason !== undefined) {
var args = {
'kernel callsite when blocked:' : blocked_reason.caller
};
if (blocked_reason.iowait) {
switch (state) {
case SCHEDULING_STATE.UNINTR_SLEEP:
state = SCHEDULING_STATE.UNINTR_SLEEP_IO;
break;
case SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL:
state = SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL_IO;
break;
case SCHEDULING_STATE.UNINTR_SLEEP_WAKING:
state = SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL_IO;
break;
default:
}
}
slices.push(new tr.model.ThreadTimeSlice(
thread,
state, '', prevSlice.end, args, midDuration));
} else {
slices.push(new tr.model.ThreadTimeSlice(
thread,
state, '', prevSlice.end, {}, midDuration));
}
if (wakeup !== undefined) {
var wakeupDuration = nextSlice.start - wakeup.ts;
var args = {'wakeup from tid': wakeup.fromTid};
slices.push(new tr.model.ThreadTimeSlice(
thread, SCHEDULING_STATE.RUNNABLE, '',
wakeup.ts, args, wakeupDuration));
wakeup = undefined;
}
};
if (prevSlice.args.stateWhenDescheduled == 'S') {
pushSleep(SCHEDULING_STATE.SLEEPING);
} else if (prevSlice.args.stateWhenDescheduled == 'R' ||
prevSlice.args.stateWhenDescheduled == 'R+') {
slices.push(new tr.model.ThreadTimeSlice(
thread, SCHEDULING_STATE.RUNNABLE, '',
prevSlice.end, {}, midDuration));
} else if (prevSlice.args.stateWhenDescheduled == 'D') {
pushSleep(SCHEDULING_STATE.UNINTR_SLEEP);
} else if (prevSlice.args.stateWhenDescheduled == 'T') {
slices.push(new tr.model.ThreadTimeSlice(
thread, SCHEDULING_STATE.STOPPED, '',
prevSlice.end, {}, midDuration));
} else if (prevSlice.args.stateWhenDescheduled == 't') {
slices.push(new tr.model.ThreadTimeSlice(
thread, SCHEDULING_STATE.DEBUG, '',
prevSlice.end, {}, midDuration));
} else if (prevSlice.args.stateWhenDescheduled == 'Z') {
slices.push(new tr.model.ThreadTimeSlice(
thread, SCHEDULING_STATE.ZOMBIE, '', ioWaitId,
prevSlice.end, {}, midDuration));
} else if (prevSlice.args.stateWhenDescheduled == 'X') {
slices.push(new tr.model.ThreadTimeSlice(
thread, SCHEDULING_STATE.EXIT_DEAD, '',
prevSlice.end, {}, midDuration));
} else if (prevSlice.args.stateWhenDescheduled == 'x') {
slices.push(new tr.model.ThreadTimeSlice(
thread, SCHEDULING_STATE.TASK_DEAD, '',
prevSlice.end, {}, midDuration));
} else if (prevSlice.args.stateWhenDescheduled == 'K') {
slices.push(new tr.model.ThreadTimeSlice(
thread, SCHEDULING_STATE.WAKE_KILL, '',
prevSlice.end, {}, midDuration));
} else if (prevSlice.args.stateWhenDescheduled == 'W') {
slices.push(new tr.model.ThreadTimeSlice(
thread, SCHEDULING_STATE.WAKING, '',
prevSlice.end, {}, midDuration));
} else if (prevSlice.args.stateWhenDescheduled == 'D|K') {
pushSleep(SCHEDULING_STATE.UNINTR_SLEEP_WAKE_KILL);
} else if (prevSlice.args.stateWhenDescheduled == 'D|W') {
pushSleep(SCHEDULING_STATE.UNINTR_SLEEP_WAKING);
} else {
slices.push(new tr.model.ThreadTimeSlice(
thread, SCHEDULING_STATE.UNKNOWN, '',
prevSlice.end, {}, midDuration));
this.model_.importWarning({
type: 'parse_error',
message: 'Unrecognized sleep state: ' +
prevSlice.args.stateWhenDescheduled
});
}
var runningSlice = new tr.model.ThreadTimeSlice(
thread, SCHEDULING_STATE.RUNNING, '',
nextSlice.start, {}, nextSlice.duration);
runningSlice.cpuOnWhichThreadWasRunning = prevSlice.cpu;
slices.push(runningSlice);
}
thread.timeSlices = slices;
}, this);
},
computeCpuTimestampsForSlicesAsNeeded: function() {
/* iterate all slices and try to figure out cpuStart/endTimes */
},
/**
* Computes a time transform from perf time to parent time based on the
* imported clock sync records.
* @return {number} offset from perf time to parent time or undefined if
* the necessary sync records were not found.
*/
computeTimeTransform: function() {
var isSecondaryImport = this.model.getClockSyncRecordsNamed(
'ftrace_importer').length !== 0;
var mSyncs = this.model_.getClockSyncRecordsNamed('monotonic');
// If this is a secondary import, and no clock syncing records were
// found, then abort the import. Otherwise, just skip clock alignment.
if (mSyncs.length == 0)
return isSecondaryImport ? undefined : 0;
// Shift all the slice times based on the sync record.
// TODO(skyostil): Compute a scaling factor if we have multiple clock sync
// records.
var sync = mSyncs[0].args;
// NB: parentTS of zero denotes no times-shift; this is
// used when user and kernel event clocks are identical.
if (sync.parentTS == 0 || sync.parentTS == sync.perfTS)
return 0;
return sync.parentTS - sync.perfTS;
},
/**
* Creates an instance of each registered linux perf event parser.
* This allows the parsers to register handlers for the events they
* understand. We also register our own special handlers (for the
* timestamp synchronization markers).
*/
createParsers_: function() {
// Instantiate the parsers; this will register handlers for known events
var allTypeInfos = tr.e.importer.linux_perf.
Parser.getAllRegisteredTypeInfos();
var parsers = allTypeInfos.map(
function(typeInfo) {
return new typeInfo.constructor(this);
}, this);
return parsers;
},
registerDefaultHandlers_: function() {
this.registerEventHandler('tracing_mark_write',
LinuxPerfImporter.prototype.traceMarkingWriteEvent.bind(this));
// NB: old-style trace markers; deprecated
this.registerEventHandler('0',
LinuxPerfImporter.prototype.traceMarkingWriteEvent.bind(this));
// Register dummy clock sync handlers to avoid warnings in the log.
this.registerEventHandler('tracing_mark_write:trace_event_clock_sync',
function() { return true; });
this.registerEventHandler('0:trace_event_clock_sync',
function() { return true; });
},
/**
* Registers a linux perf event parser used by importCpuData.
*/
registerEventHandler: function(eventName, handler) {
// TODO(sleffler) how to handle conflicts?
this.eventHandlers_[eventName] = handler;
},
/**
* Records the fact that a pid has become runnable. This data will
* eventually get used to derive each thread's timeSlices array.
*/
markPidRunnable: function(ts, pid, comm, prio, fromPid) {
// The the pids that get passed in to this function are Linux kernel
// pids, which identify threads. The rest of trace-viewer refers to
// these as tids, so the change of nomenclature happens in the following
// construction of the wakeup object.
this.wakeups_.push({ts: ts, tid: pid, fromTid: fromPid});
},
/**
* Records the reason why a pid has gone into uninterruptible sleep.
*/
addPidBlockedReason: function(ts, pid, iowait, caller) {
// The the pids that get passed in to this function are Linux kernel
// pids, which identify threads. The rest of trace-viewer refers to
// these as tids, so the change of nomenclature happens in the following
// construction of the wakeup object.
this.blocked_reasons_.push({ts: ts, tid: pid, iowait: iowait,
caller: caller});
},
/**
* Processes a trace_event_clock_sync event.
*/
traceClockSyncEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
// Check for new-style clock sync records.
var event = /name=(\w+?)\s(.+)/.exec(eventBase.details);
if (event) {
var name = event[1];
var pieces = event[2].split(' ');
var args = {
perfTS: ts
};
for (var i = 0; i < pieces.length; i++) {
var parts = pieces[i].split('=');
if (parts.length != 2)
throw new Error('omgbbq');
args[parts[0]] = parts[1];
}
this.addClockSyncRecord(new ClockSyncRecord(name, ts, args));
return true;
}
// Old-style clock sync records from chromium
event = /parent_ts=(\d+\.?\d*)/.exec(eventBase.details);
if (!event)
return false;
this.addClockSyncRecord(new ClockSyncRecord('monotonic', ts, {
perfTS: ts,
parentTS: event[1] * 1000
}));
return true;
},
/**
* Processes a trace_marking_write event.
*/
traceMarkingWriteEvent: function(eventName, cpuNumber, pid, ts, eventBase,
threadName) {
// Some profiles end up with a \n\ on the end of each line. Strip it
// before we do the comparisons.
eventBase.details = eventBase.details.replace(/\\n.*$/, '');
var event = /^\s*(\w+):\s*(.*)$/.exec(eventBase.details);
if (!event) {
// Check if the event matches events traced by the Android framework
var tag = eventBase.details.substring(0, 2);
if (tag == 'B|' || tag == 'E' || tag == 'E|' || tag == 'X|' ||
tag == 'C|' || tag == 'S|' || tag == 'F|') {
eventBase.subEventName = 'android';
} else {
return false;
}
} else {
eventBase.subEventName = event[1];
eventBase.details = event[2];
}
var writeEventName = eventName + ':' + eventBase.subEventName;
var handler = this.eventHandlers_[writeEventName];
if (!handler) {
this.model_.importWarning({
type: 'parse_error',
message: 'Unknown trace_marking_write event ' + writeEventName
});
return true;
}
return handler(writeEventName, cpuNumber, pid, ts, eventBase, threadName);
},
/**
* Populates model clockSyncRecords with found clock sync markers.
*/
importClockSyncRecords: function() {
this.forEachLine(function(text, eventBase, cpuNumber, pid, ts) {
var eventName = eventBase.eventName;
if (eventName !== 'tracing_mark_write' && eventName !== '0')
return;
if (traceEventClockSyncRE.exec(eventBase.details))
this.traceClockSyncEvent(eventName, cpuNumber, pid, ts, eventBase);
if (realTimeClockSyncRE.exec(eventBase.details)) {
// This entry maps realtime to clock_monotonic; store in the model
// so that importers parsing files with realtime timestamps can
// map this back to monotonic.
var match = realTimeClockSyncRE.exec(eventBase.details);
this.model_.realtime_to_monotonic_offset_ms = ts - match[1];
}
if (genericClockSyncRE.exec(eventBase.details))
this.traceClockSyncEvent(eventName, cpuNumber, pid, ts, eventBase);
}.bind(this));
},
addClockSyncRecord: function(csr) {
this.newlyAddedClockSyncRecords_.push(csr);
this.model_.clockSyncRecords.push(csr);
},
shiftNewlyAddedClockSyncRecords: function(timeShift) {
this.newlyAddedClockSyncRecords_.forEach(function(csr) {
csr.ts += timeShift;
});
},
/**
* Walks the this.events_ structure and creates Cpu objects.
*/
importCpuData: function(timeShift) {
this.forEachLine(function(text, eventBase, cpuNumber, pid, ts) {
var eventName = eventBase.eventName;
var handler = this.eventHandlers_[eventName];
if (!handler) {
this.model_.importWarning({
type: 'parse_error',
message: 'Unknown event ' + eventName + ' (' + text + ')'
});
return;
}
ts += timeShift;
if (!handler(eventName, cpuNumber, pid, ts, eventBase)) {
this.model_.importWarning({
type: 'parse_error',
message: 'Malformed ' + eventName + ' event (' + text + ')'
});
}
}.bind(this));
},
/**
* Walks the this.events_ structure and populates this.lines_.
*/
parseLines: function() {
var lines = [];
var extractResult = LinuxPerfImporter._extractEventsFromSystraceHTML(
this.events_, true);
if (!extractResult.ok)
extractResult = LinuxPerfImporter._extractEventsFromSystraceMultiHTML(
this.events_, true);
var lines = extractResult.ok ?
extractResult.lines : this.events_.split('\n');
var lineParser = null;
for (var lineNumber = 0; lineNumber < lines.length; ++lineNumber) {
var line = lines[lineNumber].trim();
if (line.length == 0 || /^#/.test(line))
continue;
if (lineParser == null) {
lineParser = autoDetectLineParser(line);
if (lineParser == null) {
this.model_.importWarning({
type: 'parse_error',
message: 'Cannot parse line: ' + line
});
continue;
}
}
var eventBase = lineParser(line);
if (!eventBase) {
this.model_.importWarning({
type: 'parse_error',
message: 'Unrecognized line: ' + line
});
continue;
}
this.lines_.push([
line,
eventBase,
parseInt(eventBase.cpuNumber),
parseInt(eventBase.pid),
parseFloat(eventBase.timestamp) * 1000
]);
}
},
/**
* Calls |handler| for every parsed line.
*/
forEachLine: function(handler) {
for (var i = 0; i < this.lines_.length; ++i) {
var line = this.lines_[i];
handler.apply(this, line);
}
}
};
tr.importer.Importer.register(LinuxPerfImporter);
return {
LinuxPerfImporter: LinuxPerfImporter,
_LinuxPerfImporterTestExports: TestExports
};
});
</script>