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