3 Copyright (c) 2012 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file.
8 <link rel="import" href="/tracing/trace_model.html">
9 <link rel="import" href="/tracing/color_scheme.html">
10 <link rel="import" href="/tracing/importer/linux_perf/bus_parser.html">
11 <link rel="import" href="/tracing/importer/linux_perf/clock_parser.html">
12 <link rel="import" href="/tracing/importer/linux_perf/cpufreq_parser.html">
13 <link rel="import" href="/tracing/importer/linux_perf/disk_parser.html">
14 <link rel="import" href="/tracing/importer/linux_perf/drm_parser.html">
15 <link rel="import" href="/tracing/importer/linux_perf/exynos_parser.html">
16 <link rel="import" href="/tracing/importer/linux_perf/gesture_parser.html">
17 <link rel="import" href="/tracing/importer/linux_perf/i915_parser.html">
18 <link rel="import" href="/tracing/importer/linux_perf/mali_parser.html">
19 <link rel="import" href="/tracing/importer/linux_perf/power_parser.html">
20 <link rel="import" href="/tracing/importer/linux_perf/sched_parser.html">
21 <link rel="import" href="/tracing/importer/linux_perf/sync_parser.html">
22 <link rel="import" href="/tracing/importer/linux_perf/workqueue_parser.html">
23 <link rel="import" href="/tracing/importer/linux_perf/android_parser.html">
24 <link rel="import" href="/tracing/importer/linux_perf/kfunc_parser.html">
25 <link rel="import" href="/tracing/importer/importer.html">
26 <link rel="import" href="/tracing/importer/simple_line_reader.html">
31 * @fileoverview Imports text files in the Linux event trace format into the
32 * Tracemodel. This format is output both by sched_trace and by Linux's perf
35 * This importer assumes the events arrive as a string. The unit tests provide
36 * examples of the trace format.
38 * Linux scheduler traces use a definition for 'pid' that is different than
39 * tracing uses. Whereas tracing uses pid to identify a specific process, a pid
40 * in a linux trace refers to a specific thread within a process. Within this
41 * file, we the definition used in Linux traces, as it improves the importing
46 tvcm.exportTo('tracing.importer', function() {
47 var Importer = tracing.importer.Importer;
50 * Imports linux perf events into a specified model.
53 function LinuxPerfImporter(model, events) {
54 this.importPriority = 2;
56 this.events_ = events;
57 this.clockSyncRecords_ = [];
59 this.kernelThreadStates_ = {};
60 this.buildMapFromLinuxPidsToThreads();
62 this.pseudoThreadCounter = 1;
64 this.eventHandlers_ = {};
69 // Matches the trace record in 3.2 and later with the print-tgid option:
70 // <idle>-0 0 [001] d... 1.23: sched_switch
72 // A TGID (Thread Group ID) is basically what the Linux kernel calls what
73 // userland refers to as a process ID (as opposed to a Linux pid, which is
74 // what userland calls a thread ID).
75 var lineREWithTGID = new RegExp(
76 '^\\s*(.+)-(\\d+)\\s+\\(\\s*(\\d+|-+)\\)\\s\\[(\\d+)\\]' +
77 '\\s+[dX.][N.][Hhs.][0-9a-f.]' +
78 '\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$');
79 var lineParserWithTGID = function(line) {
80 var groups = lineREWithTGID.exec(line);
90 threadName: groups[1],
99 TestExports.lineParserWithTGID = lineParserWithTGID;
101 // Matches the default trace record in 3.2 and later (includes irq-info):
102 // <idle>-0 [001] d... 1.23: sched_switch
103 var lineREWithIRQInfo = new RegExp(
104 '^\\s*(.+)-(\\d+)\\s+\\[(\\d+)\\]' +
105 '\\s+[dX.][N.][Hhs.][0-9a-f.]' +
106 '\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$');
107 var lineParserWithIRQInfo = function(line) {
108 var groups = lineREWithIRQInfo.exec(line);
113 threadName: groups[1],
115 cpuNumber: groups[3],
116 timestamp: groups[4],
117 eventName: groups[5],
121 TestExports.lineParserWithIRQInfo = lineParserWithIRQInfo;
123 // Matches the default trace record pre-3.2:
124 // <idle>-0 [001] 1.23: sched_switch
125 var lineREWithLegacyFmt =
126 /^\s*(.+)-(\d+)\s+\[(\d+)\]\s*(\d+\.\d+):\s+(\S+):\s(.*)$/;
127 var lineParserWithLegacyFmt = function(line) {
128 var groups = lineREWithLegacyFmt.exec(line);
133 threadName: groups[1],
135 cpuNumber: groups[3],
136 timestamp: groups[4],
137 eventName: groups[5],
141 TestExports.lineParserWithLegacyFmt = lineParserWithLegacyFmt;
143 // Matches the trace_event_clock_sync record
144 // 0: trace_event_clock_sync: parent_ts=19581477508
145 var traceEventClockSyncRE = /trace_event_clock_sync: parent_ts=(\d+\.?\d*)/;
146 TestExports.traceEventClockSyncRE = traceEventClockSyncRE;
148 // Some kernel trace events are manually classified in slices and
149 // hand-assigned a pseudo PID.
150 var pseudoKernelPID = 0;
153 * Deduce the format of trace data. Linux kernels prior to 3.3 used one
154 * format (by default); 3.4 and later used another. Additionally, newer
155 * kernels can optionally trace the TGID.
157 * @return {function} the function for parsing data when the format is
158 * recognized; otherwise null.
160 function autoDetectLineParser(line) {
163 if (lineREWithTGID.test(line))
164 return lineParserWithTGID;
165 if (lineREWithIRQInfo.test(line))
166 return lineParserWithIRQInfo;
167 if (lineREWithLegacyFmt.test(line))
168 return lineParserWithLegacyFmt;
171 TestExports.autoDetectLineParser = autoDetectLineParser;
174 * Guesses whether the provided events is a Linux perf string.
175 * Looks for the magic string "# tracer" at the start of the file,
176 * or the typical task-pid-cpu-timestamp-function sequence of a typical
179 * @return {boolean} True when events is a linux perf array.
181 LinuxPerfImporter.canImport = function(events) {
182 if (!(typeof(events) === 'string' || events instanceof String))
185 if (LinuxPerfImporter._extractEventsFromSystraceHTML(events, false).ok)
188 if (/^# tracer:/.test(events))
191 var m = /^(.+)\n/.exec(events);
194 if (autoDetectLineParser(events))
200 LinuxPerfImporter._extractEventsFromSystraceHTML = function(
201 incoming_events, produce_result) {
202 var failure = {ok: false};
203 if (produce_result === undefined)
204 produce_result = true;
206 if (/^<!DOCTYPE HTML>/.test(incoming_events) == false)
208 var r = new tracing.importer.SimpleLineReader(incoming_events);
210 // Try to find the data...
211 if (!r.advanceToLineMatching(/^ <script>$/))
213 if (!r.advanceToLineMatching(/^ var linuxPerfData = "\\$/))
216 var events_begin_at_line = r.curLineNumber + 1;
217 r.beginSavingLines();
218 if (!r.advanceToLineMatching(/^ <\/script>$/))
221 var raw_events = r.endSavingLinesAndGetResult();
223 // Drop off first and last event as it contains the tag.
224 raw_events = raw_events.slice(1, raw_events.length - 1);
226 if (!r.advanceToLineMatching(/^<\/body>$/))
228 if (!r.advanceToLineMatching(/^<\/html>$/))
231 function endsWith(str, suffix) {
232 return str.indexOf(suffix, str.length - suffix.length) !== -1;
234 function stripSuffix(str, suffix) {
235 if (!endsWith(str, suffix))
237 return str.substring(str, str.length - suffix.length);
240 // Strip off escaping in the file needed to preserve linebreaks.
242 if (produce_result) {
243 for (var i = 0; i < raw_events.length; i++) {
244 var event = raw_events[i];
245 event = stripSuffix(event, '\\n\\');
249 events = [raw_events[raw_events.length - 1]];
252 // Last event ends differently. Strip that off too,
253 // treating absence of that trailing string as a failure.
254 var oldLastEvent = events[events.length - 1];
255 var newLastEvent = stripSuffix(oldLastEvent, '\\n";');
256 if (newLastEvent == oldLastEvent)
258 events[events.length - 1] = newLastEvent;
261 lines: produce_result ? events : undefined,
262 events_begin_at_line: events_begin_at_line};
265 LinuxPerfImporter.prototype = {
266 __proto__: Importer.prototype,
273 * Precomputes a lookup table from linux pids back to existing
274 * Threads. This is used during importing to add information to each
275 * thread about whether it was running, descheduled, sleeping, et
278 buildMapFromLinuxPidsToThreads: function() {
279 this.threadsByLinuxPid = {};
280 this.model_.getAllThreads().forEach(
282 this.threadsByLinuxPid[thread.tid] = thread;
287 * @return {Cpu} A Cpu corresponding to the given cpuNumber.
289 getOrCreateCpu: function(cpuNumber) {
290 return this.model_.kernel.getOrCreateCpu(cpuNumber);
294 * @return {TimelineThread} A thread corresponding to the kernelThreadName.
296 getOrCreateKernelThread: function(kernelThreadName, pid, tid) {
297 if (!this.kernelThreadStates_[kernelThreadName]) {
298 var thread = this.model_.getOrCreateProcess(pid).getOrCreateThread(tid);
299 thread.name = kernelThreadName;
300 this.kernelThreadStates_[kernelThreadName] = {
303 openSlice: undefined,
304 openSliceTS: undefined
306 this.threadsByLinuxPid[pid] = thread;
308 return this.kernelThreadStates_[kernelThreadName];
312 * @return {TimelineThread} A pseudo thread corresponding to the
313 * threadName. Pseudo threads are for events that we want to break
314 * out to a separate timeline but would not otherwise happen.
315 * These threads are assigned to pseudoKernelPID and given a
316 * unique (incrementing) TID.
318 getOrCreatePseudoThread: function(threadName) {
319 var thread = this.kernelThreadStates_[threadName];
321 thread = this.getOrCreateKernelThread(threadName, pseudoKernelPID,
322 this.pseudoThreadCounter);
323 this.pseudoThreadCounter++;
329 * Imports the data in this.events_ into model_.
331 importEvents: function(isSecondaryImport) {
332 this.createParsers();
334 this.importClockSyncRecords();
335 var timeShift = this.computeTimeTransform(isSecondaryImport);
336 if (timeShift === undefined) {
337 this.model_.importWarning({
339 message: 'Cannot import kernel trace without a clock sync.'
343 this.importCpuData(timeShift);
344 this.buildMapFromLinuxPidsToThreads();
345 this.buildPerThreadCpuSlicesFromCpuState();
346 this.computeCpuTimestampsForSlicesAsNeeded();
350 * Builds the timeSlices array on each thread based on our knowledge of what
351 * each Cpu is doing. This is done only for Threads that are
352 * already in the model, on the assumption that not having any traced data
353 * on a thread means that it is not of interest to the user.
355 buildPerThreadCpuSlicesFromCpuState: function() {
356 // Push the cpu slices to the threads that they run on.
357 for (var cpuNumber in this.model_.kernel.cpus) {
358 var cpu = this.model_.kernel.cpus[cpuNumber];
360 for (var i = 0; i < cpu.slices.length; i++) {
361 var cpuSlice = cpu.slices[i];
363 var thread = this.threadsByLinuxPid[cpuSlice.args.tid];
367 cpuSlice.threadThatWasRunning = thread;
369 if (!thread.tempCpuSlices)
370 thread.tempCpuSlices = [];
371 thread.tempCpuSlices.push(cpuSlice);
375 for (var i in this.wakeups_) {
376 var wakeup = this.wakeups_[i];
377 var thread = this.threadsByLinuxPid[wakeup.tid];
380 thread.tempWakeups = thread.tempWakeups || [];
381 thread.tempWakeups.push(wakeup);
384 // Create slices for when the thread is not running.
385 var runningId = tvcm.ui.getColorIdByName('running');
386 var runnableId = tvcm.ui.getColorIdByName('runnable');
387 var sleepingId = tvcm.ui.getColorIdByName('sleeping');
388 var ioWaitId = tvcm.ui.getColorIdByName('iowait');
389 this.model_.getAllThreads().forEach(function(thread) {
390 if (thread.tempCpuSlices === undefined)
392 var origSlices = thread.tempCpuSlices;
393 delete thread.tempCpuSlices;
395 origSlices.sort(function(x, y) {
396 return x.start - y.start;
399 var wakeups = thread.tempWakeups || [];
400 delete thread.tempWakeups;
401 wakeups.sort(function(x, y) {
405 // Walk the slice list and put slices between each original slice to
406 // show when the thread isn't running.
409 if (origSlices.length) {
410 var slice = origSlices[0];
412 if (wakeups.length && wakeups[0].ts < slice.start) {
413 var wakeup = wakeups.shift();
414 var wakeupDuration = slice.start - wakeup.ts;
415 var args = {'wakeup from tid': wakeup.fromTid};
416 slices.push(new tracing.trace_model.ThreadTimeSlice(
417 thread, '', 'Runnable', runnableId,
418 wakeup.ts, args, wakeupDuration));
421 var runningSlice = new tracing.trace_model.ThreadTimeSlice(
422 thread, '', 'Running', runningId,
423 slice.start, {}, slice.duration);
424 runningSlice.cpuOnWhichThreadWasRunning = slice.cpu;
425 slices.push(runningSlice);
428 var wakeup = undefined;
429 for (var i = 1; i < origSlices.length; i++) {
430 var prevSlice = origSlices[i - 1];
431 var nextSlice = origSlices[i];
432 var midDuration = nextSlice.start - prevSlice.end;
433 while (wakeups.length && wakeups[0].ts < nextSlice.start) {
434 var w = wakeups.shift();
435 if (wakeup === undefined && w.ts > prevSlice.end) {
440 // Push a sleep slice onto the slices list, interrupting it with a
441 // wakeup if appropriate.
442 var pushSleep = function(title, id) {
443 if (wakeup !== undefined) {
444 midDuration = wakeup.ts - prevSlice.end;
446 slices.push(new tracing.trace_model.ThreadTimeSlice(
448 '', title, id, prevSlice.end, {}, midDuration));
449 if (wakeup !== undefined) {
450 var wakeupDuration = nextSlice.start - wakeup.ts;
451 var args = {'wakeup from tid': wakeup.fromTid};
452 slices.push(new tracing.trace_model.ThreadTimeSlice(
454 '', 'Runnable', runnableId, wakeup.ts, args, wakeupDuration));
459 if (prevSlice.args.stateWhenDescheduled == 'S') {
460 pushSleep('Sleeping', sleepingId);
461 } else if (prevSlice.args.stateWhenDescheduled == 'R' ||
462 prevSlice.args.stateWhenDescheduled == 'R+') {
463 slices.push(new tracing.trace_model.ThreadTimeSlice(
465 '', 'Runnable', runnableId, prevSlice.end, {}, midDuration));
466 } else if (prevSlice.args.stateWhenDescheduled == 'D') {
467 pushSleep('Uninterruptible Sleep', ioWaitId);
468 } else if (prevSlice.args.stateWhenDescheduled == 'T') {
469 slices.push(new tracing.trace_model.ThreadTimeSlice(
470 thread, '', '__TASK_STOPPED', ioWaitId,
471 prevSlice.end, {}, midDuration));
472 } else if (prevSlice.args.stateWhenDescheduled == 't') {
473 slices.push(new tracing.trace_model.ThreadTimeSlice(
474 thread, '', 'debug', ioWaitId,
475 prevSlice.end, {}, midDuration));
476 } else if (prevSlice.args.stateWhenDescheduled == 'Z') {
477 slices.push(new tracing.trace_model.ThreadTimeSlice(
478 thread, '', 'Zombie', ioWaitId,
479 prevSlice.end, {}, midDuration));
480 } else if (prevSlice.args.stateWhenDescheduled == 'X') {
481 slices.push(new tracing.trace_model.ThreadTimeSlice(
482 thread, '', 'Exit Dead', ioWaitId,
483 prevSlice.end, {}, midDuration));
484 } else if (prevSlice.args.stateWhenDescheduled == 'x') {
485 slices.push(new tracing.trace_model.ThreadTimeSlice(
486 thread, '', 'Task Dead', ioWaitId,
487 prevSlice.end, {}, midDuration));
488 } else if (prevSlice.args.stateWhenDescheduled == 'K') {
489 slices.push(new tracing.trace_model.ThreadTimeSlice(
490 thread, '', 'Wakekill', ioWaitId,
491 prevSlice.end, {}, midDuration));
492 } else if (prevSlice.args.stateWhenDescheduled == 'W') {
493 slices.push(new tracing.trace_model.ThreadTimeSlice(
494 thread, '', 'Waking', ioWaitId,
495 prevSlice.end, {}, midDuration));
496 } else if (prevSlice.args.stateWhenDescheduled == 'D|K') {
497 pushSleep('Uninterruptible Sleep | WakeKill', ioWaitId);
498 } else if (prevSlice.args.stateWhenDescheduled == 'D|W') {
499 pushSleep('Uninterruptible Sleep | Waking', ioWaitId);
501 slices.push(new tracing.trace_model.ThreadTimeSlice(
502 thread, '', 'UNKNOWN', ioWaitId,
503 prevSlice.end, {}, midDuration));
504 this.model_.importWarning({
506 message: 'Unrecognized sleep state: ' +
507 prevSlice.args.stateWhenDescheduled
511 var runningSlice = new tracing.trace_model.ThreadTimeSlice(
512 thread, '', 'Running', runningId,
513 nextSlice.start, {}, nextSlice.duration);
514 runningSlice.cpuOnWhichThreadWasRunning = prevSlice.cpu;
515 slices.push(runningSlice);
517 thread.timeSlices = slices;
521 computeCpuTimestampsForSlicesAsNeeded: function() {
522 /* iterate all slices and try to figure out cpuStart/endTimes */
527 * Computes a time transform from perf time to parent time based on the
528 * imported clock sync records.
529 * @return {number} offset from perf time to parent time or undefined if
530 * the necessary sync records were not found.
532 computeTimeTransform: function(isSecondaryImport) {
533 // If this is a secondary import, and no clock syncing records were
534 // found, then abort the import. Otherwise, just skip clock alignment.
535 if (this.clockSyncRecords_.length == 0)
536 return isSecondaryImport ? undefined : 0;
538 // Shift all the slice times based on the sync record.
539 // TODO(skyostil): Compute a scaling factor if we have multiple clock sync
541 var sync = this.clockSyncRecords_[0];
542 // NB: parentTS of zero denotes no times-shift; this is
543 // used when user and kernel event clocks are identical.
544 if (sync.parentTS == 0 || sync.parentTS == sync.perfTS)
546 return sync.parentTS - sync.perfTS;
550 * Creates an instance of each registered linux perf event parser.
551 * This allows the parsers to register handlers for the events they
552 * understand. We also register our own special handlers (for the
553 * timestamp synchronization markers).
555 createParsers: function() {
556 // Instantiate the parsers; this will register handlers for known events
557 var parserConstructors =
558 tracing.importer.linux_perf.Parser.getSubtypeConstructors();
559 for (var i = 0; i < parserConstructors.length; ++i) {
560 var parserConstructor = parserConstructors[i];
561 this.parsers_.push(new parserConstructor(this));
564 this.registerEventHandler('tracing_mark_write',
565 LinuxPerfImporter.prototype.traceMarkingWriteEvent.bind(this));
566 // NB: old-style trace markers; deprecated
567 this.registerEventHandler('0',
568 LinuxPerfImporter.prototype.traceMarkingWriteEvent.bind(this));
569 // Register dummy clock sync handlers to avoid warnings in the log.
570 this.registerEventHandler('tracing_mark_write:trace_event_clock_sync',
571 function() { return true; });
572 this.registerEventHandler('0:trace_event_clock_sync',
573 function() { return true; });
577 * Registers a linux perf event parser used by importCpuData.
579 registerEventHandler: function(eventName, handler) {
580 // TODO(sleffler) how to handle conflicts?
581 this.eventHandlers_[eventName] = handler;
585 * Records the fact that a pid has become runnable. This data will
586 * eventually get used to derive each thread's timeSlices array.
588 markPidRunnable: function(ts, pid, comm, prio, fromPid) {
589 // The the pids that get passed in to this function are Linux kernel
590 // pids, which identify threads. The rest of trace-viewer refers to
591 // these as tids, so the change of nomenclature happens in the following
592 // construction of the wakeup object.
593 this.wakeups_.push({ts: ts, tid: pid, fromTid: fromPid});
597 * Processes a trace_event_clock_sync event.
599 traceClockSyncEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
600 var event = /parent_ts=(\d+\.?\d*)/.exec(eventBase.details);
604 this.clockSyncRecords_.push({
606 parentTS: event[1] * 1000
612 * Processes a trace_marking_write event.
614 traceMarkingWriteEvent: function(eventName, cpuNumber, pid, ts, eventBase,
617 // Some profiles end up with a \n\ on the end of each line. Strip it
618 // before we do the comparisons.
619 eventBase.details = eventBase.details.replace(/\\n.*$/, '');
621 var event = /^\s*(\w+):\s*(.*)$/.exec(eventBase.details);
623 // Check if the event matches events traced by the Android framework
624 var tag = eventBase.details.substring(0, 2);
625 if (tag == 'B|' || tag == 'E' || tag == 'E|' || tag == 'X|' ||
626 tag == 'C|' || tag == 'S|' || tag == 'F|') {
627 eventBase.subEventName = 'android';
632 eventBase.subEventName = event[1];
633 eventBase.details = event[2];
636 var writeEventName = eventName + ':' + eventBase.subEventName;
637 var handler = this.eventHandlers_[writeEventName];
639 this.model_.importWarning({
641 message: 'Unknown trace_marking_write event ' + writeEventName
645 return handler(writeEventName, cpuNumber, pid, ts, eventBase, threadName);
649 * Populates this.clockSyncRecords_ with found clock sync markers.
651 importClockSyncRecords: function() {
652 this.forEachLine(function(text, eventBase, cpuNumber, pid, ts) {
653 var eventName = eventBase.eventName;
654 if ((eventName !== 'tracing_mark_write' && eventName !== '0') ||
655 !traceEventClockSyncRE.exec(eventBase.details))
657 this.traceClockSyncEvent(eventName, cpuNumber, pid, ts, eventBase);
662 * Walks the this.events_ structure and creates Cpu objects.
664 importCpuData: function(timeShift) {
665 this.forEachLine(function(text, eventBase, cpuNumber, pid, ts) {
666 var eventName = eventBase.eventName;
667 var handler = this.eventHandlers_[eventName];
669 this.model_.importWarning({
671 message: 'Unknown event ' + eventName + ' (' + text + ')'
676 if (!handler(eventName, cpuNumber, pid, ts, eventBase)) {
677 this.model_.importWarning({
679 message: 'Malformed ' + eventName + ' event (' + text + ')'
686 * Walks the this.events_ structure and populates this.lines_.
688 parseLines: function() {
689 var extractResult = LinuxPerfImporter._extractEventsFromSystraceHTML(
691 var lineNumberBase = 0;
693 if (extractResult.ok) {
694 lineNumberBase = extractResult.events_begin_at_line;
695 lines = extractResult.lines;
697 lines = this.events_.split('\n');
699 var lineParser = null;
700 for (var lineNumber = lineNumberBase;
701 lineNumber < lines.length;
703 var line = lines[lineNumber];
704 if (line.length == 0 || /^#/.test(line))
706 if (lineParser == null) {
707 lineParser = autoDetectLineParser(line);
708 if (lineParser == null) {
709 this.model_.importWarning({
711 message: 'Cannot parse line: ' + line
716 var eventBase = lineParser(line);
718 this.model_.importWarning({
720 message: 'Unrecognized line: ' + line
728 parseInt(eventBase.cpuNumber),
729 parseInt(eventBase.pid),
730 parseFloat(eventBase.timestamp) * 1000
736 * Calls |handler| for every parsed line.
738 forEachLine: function(handler) {
739 for (var i = 0; i < this.lines_.length; ++i) {
740 var line = this.lines_[i];
741 handler.apply(this, line);
746 tracing.TraceModel.registerImporter(LinuxPerfImporter);
749 LinuxPerfImporter: LinuxPerfImporter,
750 _LinuxPerfImporterTestExports: TestExports