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