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