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