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
21base.require('tracing.trace_model');
22base.require('tracing.color_scheme');
23base.require('tracing.importer.linux_perf.bus_parser');
24base.require('tracing.importer.linux_perf.clock_parser');
25base.require('tracing.importer.linux_perf.cpufreq_parser');
26base.require('tracing.importer.linux_perf.disk_parser');
27base.require('tracing.importer.linux_perf.drm_parser');
28base.require('tracing.importer.linux_perf.exynos_parser');
29base.require('tracing.importer.linux_perf.gesture_parser');
30base.require('tracing.importer.linux_perf.i915_parser');
31base.require('tracing.importer.linux_perf.mali_parser');
32base.require('tracing.importer.linux_perf.power_parser');
33base.require('tracing.importer.linux_perf.sched_parser');
34base.require('tracing.importer.linux_perf.sync_parser');
35base.require('tracing.importer.linux_perf.workqueue_parser');
36base.require('tracing.importer.linux_perf.android_parser');
37base.require('tracing.importer.linux_perf.kfunc_parser');
38base.require('tracing.importer.linux_perf.irq_parser');
39base.require('tracing.importer.linux_perf.memreclaim_parser');
40
41base.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