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