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