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