Home | History | Annotate | Download | only in importer
      1 // Copyright (c) 2012 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.
      4 
      5 /**
      6  * @fileoverview Imports text files in the Linux event trace format into the
      7  * model. This format is output both by sched_trace and by Linux's perf
      8  * tool.
      9  *
     10  * This importer assumes the events arrive as a string. The unit tests provide
     11  * examples of the trace format.
     12  *
     13  * Linux scheduler traces use a definition for 'pid' that is different than
     14  * tracing uses. Whereas tracing uses pid to identify a specific process, a pid
     15  * in a linux trace refers to a specific thread within a process. Within this
     16  * file, we the definition used in Linux traces, as it improves the importing
     17  * code's readability.
     18  */
     19 'use strict';
     20 
     21 base.require('model');
     22 base.require('color_scheme');
     23 base.require('importer.linux_perf.bus_parser');
     24 base.require('importer.linux_perf.clock_parser');
     25 base.require('importer.linux_perf.cpufreq_parser');
     26 base.require('importer.linux_perf.disk_parser');
     27 base.require('importer.linux_perf.drm_parser');
     28 base.require('importer.linux_perf.exynos_parser');
     29 base.require('importer.linux_perf.gesture_parser');
     30 base.require('importer.linux_perf.i915_parser');
     31 base.require('importer.linux_perf.mali_parser');
     32 base.require('importer.linux_perf.power_parser');
     33 base.require('importer.linux_perf.sched_parser');
     34 base.require('importer.linux_perf.workqueue_parser');
     35 base.require('importer.linux_perf.android_parser');
     36 base.require('importer.linux_perf.kfunc_parser');
     37 
     38 base.exportTo('tracing.importer', function() {
     39   /**
     40    * Represents the scheduling state for a single thread.
     41    * @constructor
     42    */
     43   function CpuState(cpu) {
     44     this.cpu = cpu;
     45   }
     46 
     47   CpuState.prototype = {
     48     __proto__: Object.prototype,
     49 
     50     /**
     51      * Switches the active pid on this Cpu. If necessary, add a Slice
     52      * to the cpu representing the time spent on that Cpu since the last call to
     53      * switchRunningLinuxPid.
     54      */
     55     switchRunningLinuxPid: function(importer, prevState, ts, pid, comm, prio) {
     56       // Generate a slice if the last active pid was not the idle task
     57       if (this.lastActivePid !== undefined && this.lastActivePid != 0) {
     58         var duration = ts - this.lastActiveTs;
     59         var thread = importer.threadsByLinuxPid[this.lastActivePid];
     60         var name;
     61         if (thread)
     62           name = thread.userFriendlyName;
     63         else
     64           name = this.lastActiveComm;
     65 
     66         var slice = new tracing.model.Slice('', name,
     67                                             tracing.getStringColorId(name),
     68                                             this.lastActiveTs,
     69                                             {
     70                                               comm: this.lastActiveComm,
     71                                               tid: this.lastActivePid,
     72                                               prio: this.lastActivePrio,
     73                                               stateWhenDescheduled: prevState
     74                                             },
     75                                             duration);
     76         this.cpu.slices.push(slice);
     77       }
     78 
     79       this.lastActiveTs = ts;
     80       this.lastActivePid = pid;
     81       this.lastActiveComm = comm;
     82       this.lastActivePrio = prio;
     83     }
     84   };
     85 
     86   /**
     87    * Imports linux perf events into a specified model.
     88    * @constructor
     89    */
     90   function LinuxPerfImporter(model, events) {
     91     this.importPriority = 2;
     92     this.model_ = model;
     93     this.events_ = events;
     94     this.clockSyncRecords_ = [];
     95     this.cpuStates_ = {};
     96     this.wakeups_ = [];
     97     this.kernelThreadStates_ = {};
     98     this.buildMapFromLinuxPidsToThreads();
     99     this.lineNumberBase = 0;
    100     this.lineNumber = -1;
    101     this.pseudoThreadCounter = 1;
    102     this.parsers_ = [];
    103     this.eventHandlers_ = {};
    104   }
    105 
    106   TestExports = {};
    107 
    108   // Matches the trace record in 3.2 and later with the print-tgid option:
    109   //          <idle>-0    0 [001] d...  1.23: sched_switch
    110   //
    111   // A TGID (Thread Group ID) is basically what the Linux kernel calls what
    112   // userland refers to as a process ID (as opposed to a Linux pid, which is
    113   // what userland calls a thread ID).
    114   var lineREWithTGID = new RegExp(
    115       '^\\s*(.+)-(\\d+)\\s+\\(\\s*(\\d+|-+)\\)\\s\\[(\\d+)\\]' +
    116       '\\s+[dX.][N.][Hhs.][0-9a-f.]' +
    117       '\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$');
    118   var lineParserWithTGID = function(line) {
    119     var groups = lineREWithTGID.exec(line);
    120     if (!groups) {
    121       return groups;
    122     }
    123 
    124     var tgid = groups[3];
    125     if (tgid[0] === '-')
    126       tgid = undefined;
    127 
    128     return {
    129       threadName: groups[1],
    130       pid: groups[2],
    131       tgid: tgid,
    132       cpuNumber: groups[4],
    133       timestamp: groups[5],
    134       eventName: groups[6],
    135       details: groups[7]
    136     };
    137   }
    138   TestExports.lineParserWithTGID = lineParserWithTGID;
    139 
    140   // Matches the default trace record in 3.2 and later (includes irq-info):
    141   //          <idle>-0     [001] d...  1.23: sched_switch
    142   var lineREWithIRQInfo = new RegExp(
    143       '^\\s*(.+)-(\\d+)\\s+\\[(\\d+)\\]' +
    144       '\\s+[dX.][N.][Hhs.][0-9a-f.]' +
    145       '\\s+(\\d+\\.\\d+):\\s+(\\S+):\\s(.*)$');
    146   var lineParserWithIRQInfo = function(line) {
    147     var groups = lineREWithIRQInfo.exec(line);
    148     if (!groups) {
    149       return groups;
    150     }
    151     return {
    152       threadName: groups[1],
    153       pid: groups[2],
    154       cpuNumber: groups[3],
    155       timestamp: groups[4],
    156       eventName: groups[5],
    157       details: groups[6]
    158     };
    159   }
    160   TestExports.lineParserWithIRQInfo = lineParserWithIRQInfo;
    161 
    162   // Matches the default trace record pre-3.2:
    163   //          <idle>-0     [001]  1.23: sched_switch
    164   var lineREWithLegacyFmt =
    165     /^\s*(.+)-(\d+)\s+\[(\d+)\]\s*(\d+\.\d+):\s+(\S+):\s(.*)$/;
    166   var lineParserWithLegacyFmt = function(line) {
    167     var groups = lineREWithLegacyFmt.exec(line);
    168     if (!groups) {
    169       return groups;
    170     }
    171     return {
    172       threadName: groups[1],
    173       pid: groups[2],
    174       cpuNumber: groups[3],
    175       timestamp: groups[4],
    176       eventName: groups[5],
    177       details: groups[6]
    178     };
    179   }
    180   TestExports.lineParserWithLegacyFmt = lineParserWithLegacyFmt;
    181 
    182   // Matches the trace_event_clock_sync record
    183   //  0: trace_event_clock_sync: parent_ts=19581477508
    184   var traceEventClockSyncRE = /trace_event_clock_sync: parent_ts=(\d+\.?\d*)/;
    185   TestExports.traceEventClockSyncRE = traceEventClockSyncRE;
    186 
    187   // Some kernel trace events are manually classified in slices and
    188   // hand-assigned a pseudo PID.
    189   var pseudoKernelPID = 0;
    190 
    191   /**
    192    * Deduce the format of trace data. Linix kernels prior to 3.3 used one
    193    * format (by default); 3.4 and later used another.  Additionally, newer
    194    * kernels can optionally trace the TGID.
    195    *
    196    * @return {function} the function for parsing data when the format is
    197    * recognized; otherwise null.
    198    */
    199   function autoDetectLineParser(line) {
    200     if (lineREWithTGID.test(line))
    201       return lineParserWithTGID;
    202     if (lineREWithIRQInfo.test(line))
    203       return lineParserWithIRQInfo;
    204     if (lineREWithLegacyFmt.test(line))
    205       return lineParserWithLegacyFmt;
    206     return null;
    207   };
    208   TestExports.autoDetectLineParser = autoDetectLineParser;
    209 
    210   /**
    211    * Guesses whether the provided events is a Linux perf string.
    212    * Looks for the magic string "# tracer" at the start of the file,
    213    * or the typical task-pid-cpu-timestamp-function sequence of a typical
    214    * trace's body.
    215    *
    216    * @return {boolean} True when events is a linux perf array.
    217    */
    218   LinuxPerfImporter.canImport = function(events) {
    219     if (!(typeof(events) === 'string' || events instanceof String))
    220       return false;
    221 
    222     if (LinuxPerfImporter._extractEventsFromSystraceHTML(events, false).ok)
    223       return true;
    224 
    225     if (/^# tracer:/.test(events))
    226       return true;
    227 
    228     var m = /^(.+)\n/.exec(events);
    229     if (m)
    230       events = m[1];
    231     if (autoDetectLineParser(events))
    232       return true;
    233 
    234     return false;
    235   };
    236 
    237   LinuxPerfImporter._extractEventsFromSystraceHTML = function(
    238     incoming_events, produce_result) {
    239     var failure = {ok: false};
    240     if (produce_result === undefined)
    241       produce_result = true;
    242 
    243     if (/^<!DOCTYPE HTML>/.test(incoming_events) == false)
    244       return failure;
    245     var lines = incoming_events.split('\n');
    246     var cur_line = 1;
    247     function advanceToLineMatching(regex) {
    248       for (; cur_line < lines.length; cur_line++) {
    249         if (regex.test(lines[cur_line]))
    250           return true;
    251       }
    252       return false;
    253     }
    254 
    255     // Try to find the data...
    256     if (!advanceToLineMatching(/^  <script>$/))
    257       return failure;
    258     if (!advanceToLineMatching(/^  var linuxPerfData = "\\$/))
    259       return failure;
    260     var events_begin_at_line = cur_line + 1;
    261 
    262     if (!advanceToLineMatching(/^  <\/script>$/))
    263       return failure;
    264     var events_end_at_line = cur_line;
    265 
    266     if (!advanceToLineMatching(/^<\/body>$/))
    267       return failure;
    268     if (!advanceToLineMatching(/^<\/html>$/))
    269       return failure;
    270 
    271     var raw_events = lines.slice(events_begin_at_line,
    272                                  events_end_at_line);
    273     function endsWith(str, suffix) {
    274       return str.indexOf(suffix, str.length - suffix.length) !== -1;
    275     }
    276     function stripSuffix(str, suffix) {
    277       if (!endsWith(str, suffix))
    278         return str;
    279       return str.substring(str, str.length - suffix.length);
    280     }
    281 
    282     // Strip off escaping in the file needed to preserve linebreaks.
    283     var events = [];
    284     if (produce_result) {
    285       for (var i = 0; i < raw_events.length; i++) {
    286         var event = raw_events[i];
    287         event = stripSuffix(event, '\\n\\');
    288         events.push(event);
    289       }
    290     } else {
    291       events = [raw_events[raw_events.length - 1]];
    292     }
    293 
    294     // Last event ends differently. Strip that off too,
    295     // treating absence of that trailing stirng as a failure.
    296     var oldLastEvent = events[events.length - 1];
    297     var newLastEvent = stripSuffix(oldLastEvent, '\\n";');
    298     if (newLastEvent == oldLastEvent)
    299       return failure;
    300     events[events.length - 1] = newLastEvent;
    301 
    302     return {ok: true,
    303             lines: produce_result ? events : undefined,
    304             events_begin_at_line: events_begin_at_line};
    305   }
    306 
    307   LinuxPerfImporter.prototype = {
    308     __proto__: Object.prototype,
    309 
    310     get model() {
    311       return this.model_;
    312     },
    313 
    314     /**
    315      * Precomputes a lookup table from linux pids back to existing
    316      * Threads. This is used during importing to add information to each
    317      * thread about whether it was running, descheduled, sleeping, et
    318      * cetera.
    319      */
    320     buildMapFromLinuxPidsToThreads: function() {
    321       this.threadsByLinuxPid = {};
    322       this.model_.getAllThreads().forEach(
    323           function(thread) {
    324             this.threadsByLinuxPid[thread.tid] = thread;
    325           }.bind(this));
    326     },
    327 
    328     /**
    329      * @return {CpuState} A CpuState corresponding to the given cpuNumber.
    330      */
    331     getOrCreateCpuState: function(cpuNumber) {
    332       if (!this.cpuStates_[cpuNumber]) {
    333         var cpu = this.model_.getOrCreateCpu(cpuNumber);
    334         this.cpuStates_[cpuNumber] = new CpuState(cpu);
    335       }
    336       return this.cpuStates_[cpuNumber];
    337     },
    338 
    339     /**
    340      * @return {TimelinThread} A thread corresponding to the kernelThreadName.
    341      */
    342     getOrCreateKernelThread: function(kernelThreadName, pid, tid) {
    343       if (!this.kernelThreadStates_[kernelThreadName]) {
    344         var thread = this.model_.getOrCreateProcess(pid).getOrCreateThread(tid);
    345         thread.name = kernelThreadName;
    346         this.kernelThreadStates_[kernelThreadName] = {
    347           pid: pid,
    348           thread: thread,
    349           openSlice: undefined,
    350           openSliceTS: undefined
    351         };
    352         this.threadsByLinuxPid[pid] = thread;
    353       }
    354       return this.kernelThreadStates_[kernelThreadName];
    355     },
    356 
    357     /**
    358      * @return {TimelinThread} A pseudo thread corresponding to the
    359      * threadName.  Pseudo threads are for events that we want to break
    360      * out to a separate timeline but would not otherwise happen.
    361      * These threads are assigned to pseudoKernelPID and given a
    362      * unique (incrementing) TID.
    363      */
    364     getOrCreatePseudoThread: function(threadName) {
    365       var thread = this.kernelThreadStates_[threadName];
    366       if (!thread) {
    367         thread = this.getOrCreateKernelThread(threadName, pseudoKernelPID,
    368             this.pseudoThreadCounter);
    369         this.pseudoThreadCounter++;
    370       }
    371       return thread;
    372     },
    373 
    374     /**
    375      * Imports the data in this.events_ into model_.
    376      */
    377     importEvents: function(isSecondaryImport) {
    378       this.createParsers();
    379       this.importCpuData();
    380       if (!this.alignClocks(isSecondaryImport))
    381         return;
    382       this.buildMapFromLinuxPidsToThreads();
    383       this.buildPerThreadCpuSlicesFromCpuState();
    384     },
    385 
    386     /**
    387      * Called by the Model after all other importers have imported their
    388      * events.
    389      */
    390     finalizeImport: function() {
    391     },
    392 
    393     /**
    394      * Builds the cpuSlices array on each thread based on our knowledge of what
    395      * each Cpu is doing.  This is done only for Threads that are
    396      * already in the model, on the assumption that not having any traced data
    397      * on a thread means that it is not of interest to the user.
    398      */
    399     buildPerThreadCpuSlicesFromCpuState: function() {
    400       // Push the cpu slices to the threads that they run on.
    401       for (var cpuNumber in this.cpuStates_) {
    402         var cpuState = this.cpuStates_[cpuNumber];
    403         var cpu = cpuState.cpu;
    404 
    405         for (var i = 0; i < cpu.slices.length; i++) {
    406           var slice = cpu.slices[i];
    407 
    408           var thread = this.threadsByLinuxPid[slice.args.tid];
    409           if (!thread)
    410             continue;
    411           if (!thread.tempCpuSlices)
    412             thread.tempCpuSlices = [];
    413           thread.tempCpuSlices.push(slice);
    414         }
    415       }
    416 
    417       for (var i in this.wakeups_) {
    418         var wakeup = this.wakeups_[i];
    419         var thread = this.threadsByLinuxPid[wakeup.tid];
    420         if (!thread)
    421           continue;
    422         thread.tempWakeups = thread.tempWakeups || [];
    423         thread.tempWakeups.push(wakeup);
    424       }
    425 
    426       // Create slices for when the thread is not running.
    427       var runningId = tracing.getColorIdByName('running');
    428       var runnableId = tracing.getColorIdByName('runnable');
    429       var sleepingId = tracing.getColorIdByName('sleeping');
    430       var ioWaitId = tracing.getColorIdByName('iowait');
    431       this.model_.getAllThreads().forEach(function(thread) {
    432         if (thread.tempCpuSlices === undefined)
    433           return;
    434         var origSlices = thread.tempCpuSlices;
    435         delete thread.tempCpuSlices;
    436 
    437         origSlices.sort(function(x, y) {
    438           return x.start - y.start;
    439         });
    440 
    441         var wakeups = thread.tempWakeups || [];
    442         delete thread.tempWakeups;
    443         wakeups.sort(function(x, y) {
    444           return x.ts - y.ts;
    445         });
    446 
    447         // Walk the slice list and put slices between each original slice
    448         // to show when the thread isn't running
    449         var slices = [];
    450 
    451         if (origSlices.length) {
    452           var slice = origSlices[0];
    453 
    454           if (wakeups.length && wakeups[0].ts < slice.start) {
    455             var wakeup = wakeups.shift();
    456             var wakeupDuration = slice.start - wakeup.ts;
    457             var args = {'wakeup from tid': wakeup.fromTid};
    458             slices.push(new tracing.model.Slice('', 'Runnable', runnableId,
    459                 wakeup.ts, args, wakeupDuration));
    460           }
    461 
    462           slices.push(new tracing.model.Slice('', 'Running', runningId,
    463               slice.start, {}, slice.duration));
    464         }
    465 
    466         var wakeup = undefined;
    467         for (var i = 1; i < origSlices.length; i++) {
    468           var prevSlice = origSlices[i - 1];
    469           var nextSlice = origSlices[i];
    470           var midDuration = nextSlice.start - prevSlice.end;
    471           while (wakeups.length && wakeups[0].ts < nextSlice.start) {
    472             var w = wakeups.shift();
    473             if (wakeup === undefined && w.ts > prevSlice.end) {
    474               wakeup = w;
    475             }
    476           }
    477 
    478           // Push a sleep slice onto the slices list, interrupting it with a
    479           // wakeup if appropriate.
    480           var pushSleep = function(title, id) {
    481             if (wakeup !== undefined) {
    482               midDuration = wakeup.ts - prevSlice.end;
    483             }
    484             slices.push(new tracing.model.Slice('', title, id, prevSlice.end,
    485               {}, midDuration));
    486             if (wakeup !== undefined) {
    487               var wakeupDuration = nextSlice.start - wakeup.ts;
    488               var args = {'wakeup from tid': wakeup.fromTid};
    489               slices.push(new tracing.model.Slice('', 'Runnable', runnableId,
    490                   wakeup.ts, args, wakeupDuration));
    491               wakeup = undefined;
    492             }
    493           }
    494 
    495           if (prevSlice.args.stateWhenDescheduled == 'S') {
    496             pushSleep('Sleeping', sleepingId);
    497           } else if (prevSlice.args.stateWhenDescheduled == 'R' ||
    498                      prevSlice.args.stateWhenDescheduled == 'R+') {
    499             slices.push(new tracing.model.Slice('', 'Runnable', runnableId,
    500                 prevSlice.end, {}, midDuration));
    501           } else if (prevSlice.args.stateWhenDescheduled == 'D') {
    502             pushSleep('Uninterruptible Sleep', ioWaitId);
    503           } else if (prevSlice.args.stateWhenDescheduled == 'T') {
    504             slices.push(new tracing.model.Slice('', '__TASK_STOPPED',
    505                 ioWaitId, prevSlice.end, {}, midDuration));
    506           } else if (prevSlice.args.stateWhenDescheduled == 't') {
    507             slices.push(new tracing.model.Slice('', 'debug', ioWaitId,
    508                 prevSlice.end, {}, midDuration));
    509           } else if (prevSlice.args.stateWhenDescheduled == 'Z') {
    510             slices.push(new tracing.model.Slice('', 'Zombie', ioWaitId,
    511                 prevSlice.end, {}, midDuration));
    512           } else if (prevSlice.args.stateWhenDescheduled == 'X') {
    513             slices.push(new tracing.model.Slice('', 'Exit Dead', ioWaitId,
    514                 prevSlice.end, {}, midDuration));
    515           } else if (prevSlice.args.stateWhenDescheduled == 'x') {
    516             slices.push(new tracing.model.Slice('', 'Task Dead', ioWaitId,
    517                 prevSlice.end, {}, midDuration));
    518           } else if (prevSlice.args.stateWhenDescheduled == 'W') {
    519             slices.push(new tracing.model.Slice('', 'WakeKill', ioWaitId,
    520                 prevSlice.end, {}, midDuration));
    521           } else if (prevSlice.args.stateWhenDescheduled == 'D|W') {
    522             pushSleep('Uninterruptable Sleep | WakeKill', ioWaitId);
    523           } else {
    524             throw new Error('Unrecognized state: ') +
    525                 prevSlice.args.stateWhenDescheduled;
    526           }
    527 
    528           slices.push(new tracing.model.Slice('', 'Running', runningId,
    529               nextSlice.start, {}, nextSlice.duration));
    530         }
    531         thread.cpuSlices = slices;
    532       });
    533     },
    534 
    535     /**
    536      * Walks the slices stored on this.cpuStates_ and adjusts their timestamps
    537      * based on any alignment metadata we discovered.
    538      */
    539     alignClocks: function(isSecondaryImport) {
    540       if (this.clockSyncRecords_.length == 0) {
    541         // If this is a secondary import, and no clock syncing records were
    542         // found, then abort the import. Otherwise, just skip clock alignment.
    543         if (!isSecondaryImport)
    544           return true;
    545 
    546         // Remove the newly imported CPU slices from the model.
    547         this.abortImport();
    548         return false;
    549       }
    550 
    551       // Shift all the slice times based on the sync record.
    552       var sync = this.clockSyncRecords_[0];
    553       // NB: parentTS of zero denotes no times-shift; this is
    554       // used when user and kernel event clocks are identical.
    555       if (sync.parentTS == 0 || sync.parentTS == sync.perfTS)
    556         return true;
    557       var timeShift = sync.parentTS - sync.perfTS;
    558       for (var cpuNumber in this.cpuStates_) {
    559         var cpuState = this.cpuStates_[cpuNumber];
    560         var cpu = cpuState.cpu;
    561 
    562         for (var i = 0; i < cpu.slices.length; i++) {
    563           var slice = cpu.slices[i];
    564           slice.start = slice.start + timeShift;
    565           slice.duration = slice.duration;
    566         }
    567 
    568         for (var counterName in cpu.counters) {
    569           var counter = cpu.counters[counterName];
    570           for (var sI = 0; sI < counter.timestamps.length; sI++)
    571             counter.timestamps[sI] = (counter.timestamps[sI] + timeShift);
    572         }
    573       }
    574       for (var kernelThreadName in this.kernelThreadStates_) {
    575         var kthread = this.kernelThreadStates_[kernelThreadName];
    576         var thread = kthread.thread;
    577         thread.shiftTimestampsForward(timeShift);
    578       }
    579       return true;
    580     },
    581 
    582     /**
    583      * Removes any data that has been added to the model because of an error
    584      * detected during the import.
    585      */
    586     abortImport: function() {
    587       if (this.pushedEventsToThreads)
    588         throw new Error('Cannot abort, have alrady pushedCpuDataToThreads.');
    589 
    590       for (var cpuNumber in this.cpuStates_)
    591         delete this.model_.cpus[cpuNumber];
    592       for (var kernelThreadName in this.kernelThreadStates_) {
    593         var kthread = this.kernelThreadStates_[kernelThreadName];
    594         var thread = kthread.thread;
    595         var process = thread.parent;
    596         delete process.threads[thread.tid];
    597         delete this.model_.processes[process.pid];
    598       }
    599       this.model_.importErrors.push(
    600           'Cannot import kernel trace without a clock sync.');
    601     },
    602 
    603     /**
    604      * Creates an instance of each registered linux perf event parser.
    605      * This allows the parsers to register handlers for the events they
    606      * understand.  We also register our own special handlers (for the
    607      * timestamp synchronization markers).
    608      */
    609     createParsers: function() {
    610       // Instantiate the parsers; this will register handlers for known events
    611       var parserConstructors =
    612           tracing.importer.linux_perf.Parser.getSubtypeConstructors();
    613       for (var i = 0; i < parserConstructors.length; ++i) {
    614         var parserConstructor = parserConstructors[i];
    615         this.parsers_.push(new parserConstructor(this));
    616       }
    617 
    618       this.registerEventHandler('tracing_mark_write:trace_event_clock_sync',
    619           LinuxPerfImporter.prototype.traceClockSyncEvent.bind(this));
    620       this.registerEventHandler('tracing_mark_write',
    621           LinuxPerfImporter.prototype.traceMarkingWriteEvent.bind(this));
    622       // NB: old-style trace markers; deprecated
    623       this.registerEventHandler('0:trace_event_clock_sync',
    624           LinuxPerfImporter.prototype.traceClockSyncEvent.bind(this));
    625       this.registerEventHandler('0',
    626           LinuxPerfImporter.prototype.traceMarkingWriteEvent.bind(this));
    627     },
    628 
    629     /**
    630      * Registers a linux perf event parser used by importCpuData.
    631      */
    632     registerEventHandler: function(eventName, handler) {
    633       // TODO(sleffler) how to handle conflicts?
    634       this.eventHandlers_[eventName] = handler;
    635     },
    636 
    637     /**
    638      * Records the fact that a pid has become runnable. This data will
    639      * eventually get used to derive each thread's cpuSlices array.
    640      */
    641     markPidRunnable: function(ts, pid, comm, prio, fromPid) {
    642       // The the pids that get passed in to this function are Linux kernel
    643       // pids, which identify threads.  The rest of trace-viewer refers to
    644       // these as tids, so the change of nomenclature happens in the following
    645       // construction of the wakeup object.
    646       this.wakeups_.push({ts: ts, tid: pid, fromTid: fromPid});
    647     },
    648 
    649     importError: function(message) {
    650       this.model_.importErrors.push(
    651         'Line ' + (this.lineNumberBase + this.lineNumber + 1) +
    652           ': ' + message);
    653     },
    654 
    655     /**
    656      * Processes a trace_event_clock_sync event.
    657      */
    658     traceClockSyncEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
    659       var event = /parent_ts=(\d+\.?\d*)/.exec(eventBase.details);
    660       if (!event)
    661         return false;
    662 
    663       this.clockSyncRecords_.push({
    664         perfTS: ts,
    665         parentTS: event[1] * 1000
    666       });
    667       return true;
    668     },
    669 
    670     /**
    671      * Processes a trace_marking_write event.
    672      */
    673     traceMarkingWriteEvent: function(eventName, cpuNumber, pid, ts, eventBase,
    674                                      threadName) {
    675       var event = /^\s*(\w+):\s*(.*)$/.exec(eventBase.details);
    676       if (!event) {
    677         // Check if the event matches events traced by the Android framework
    678         var tag = eventBase.details.substring(0, 2);
    679         if (tag == 'B|' || tag == 'E' || tag == 'E|' || tag == 'C|') {
    680           eventBase.subEventName = 'android';
    681         } else {
    682           return false;
    683         }
    684       } else {
    685         eventBase.subEventName = event[1];
    686         eventBase.details = event[2];
    687       }
    688 
    689       var writeEventName = eventName + ':' + eventBase.subEventName;
    690       var handler = this.eventHandlers_[writeEventName];
    691       if (!handler) {
    692         this.importError('Unknown trace_marking_write event ' + writeEventName);
    693         return true;
    694       }
    695       return handler(writeEventName, cpuNumber, pid, ts, eventBase, threadName);
    696     },
    697 
    698     /**
    699      * Walks the this.events_ structure and creates Cpu objects.
    700      */
    701     importCpuData: function() {
    702       var extractResult = LinuxPerfImporter._extractEventsFromSystraceHTML(
    703         this.events_, true);
    704       if (extractResult.ok) {
    705         this.lineNumberBase = extractResult.events_begin_at_line;
    706         this.lines_ = extractResult.lines;
    707       } else {
    708         this.lineNumberBase = 0;
    709         this.lines_ = this.events_.split('\n');
    710       }
    711 
    712       var lineParser = null;
    713       for (this.lineNumber = 0;
    714            this.lineNumber < this.lines_.length;
    715           ++this.lineNumber) {
    716         var line = this.lines_[this.lineNumber];
    717         if (line.length == 0 || /^#/.test(line))
    718           continue;
    719         if (lineParser == null) {
    720           lineParser = autoDetectLineParser(line);
    721           if (lineParser == null) {
    722             this.importError('Cannot parse line: ' + line);
    723             continue;
    724           }
    725         }
    726         var eventBase = lineParser(line);
    727         if (!eventBase) {
    728           this.importError('Unrecognized line: ' + line);
    729           continue;
    730         }
    731 
    732         var pid = parseInt(eventBase.pid);
    733         var cpuNumber = parseInt(eventBase.cpuNumber);
    734         var ts = parseFloat(eventBase.timestamp) * 1000;
    735         var eventName = eventBase.eventName;
    736 
    737         var handler = this.eventHandlers_[eventName];
    738         if (!handler) {
    739           this.importError('Unknown event ' + eventName + ' (' + line + ')');
    740           continue;
    741         }
    742         if (!handler(eventName, cpuNumber, pid, ts, eventBase))
    743           this.importError('Malformed ' + eventName + ' event (' + line + ')');
    744       }
    745     }
    746   };
    747 
    748   tracing.Model.registerImporter(LinuxPerfImporter);
    749 
    750   return {
    751     LinuxPerfImporter: LinuxPerfImporter,
    752     _LinuxPerfImporterTestExports: TestExports
    753   };
    754 
    755 });
    756