| <!DOCTYPE html> |
| <!-- |
| Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| Use of this source code is governed by a BSD-style license that can be |
| found in the LICENSE file. |
| --> |
| |
| <link rel="import" href="/extras/importer/linux_perf/parser.html"> |
| |
| <script> |
| 'use strict'; |
| |
| /** |
| * @fileoverview Parses filesystem and block device events in the Linux event |
| * trace format. |
| */ |
| tv.exportTo('tv.e.importer.linux_perf', function() { |
| |
| var Parser = tv.e.importer.linux_perf.Parser; |
| |
| /** |
| * Parses linux filesystem and block device trace events. |
| * @constructor |
| */ |
| function DiskParser(importer) { |
| Parser.call(this, importer); |
| |
| importer.registerEventHandler('f2fs_write_begin', |
| DiskParser.prototype.f2fsWriteBeginEvent.bind(this)); |
| importer.registerEventHandler('f2fs_write_end', |
| DiskParser.prototype.f2fsWriteEndEvent.bind(this)); |
| importer.registerEventHandler('f2fs_sync_file_enter', |
| DiskParser.prototype.f2fsSyncFileEnterEvent.bind(this)); |
| importer.registerEventHandler('f2fs_sync_file_exit', |
| DiskParser.prototype.f2fsSyncFileExitEvent.bind(this)); |
| importer.registerEventHandler('ext4_sync_file_enter', |
| DiskParser.prototype.ext4SyncFileEnterEvent.bind(this)); |
| importer.registerEventHandler('ext4_sync_file_exit', |
| DiskParser.prototype.ext4SyncFileExitEvent.bind(this)); |
| importer.registerEventHandler('ext4_da_write_begin', |
| DiskParser.prototype.ext4WriteBeginEvent.bind(this)); |
| importer.registerEventHandler('ext4_da_write_end', |
| DiskParser.prototype.ext4WriteEndEvent.bind(this)); |
| importer.registerEventHandler('block_rq_issue', |
| DiskParser.prototype.blockRqIssueEvent.bind(this)); |
| importer.registerEventHandler('block_rq_complete', |
| DiskParser.prototype.blockRqCompleteEvent.bind(this)); |
| } |
| |
| DiskParser.prototype = { |
| __proto__: Parser.prototype, |
| |
| openAsyncSlice: function(ts, category, threadName, pid, key, name) { |
| var kthread = this.importer.getOrCreateKernelThread( |
| category + ':' + threadName, pid); |
| var asyncSliceConstructor = |
| tv.c.trace_model.AsyncSlice.getConstructor( |
| category, name); |
| var slice = new asyncSliceConstructor( |
| category, name, |
| tv.b.ui.getColorIdForGeneralPurposeString(name), |
| ts); |
| slice.startThread = kthread.thread; |
| |
| if (!kthread.openAsyncSlices) { |
| kthread.openAsyncSlices = { }; |
| } |
| kthread.openAsyncSlices[key] = slice; |
| }, |
| |
| closeAsyncSlice: function(ts, category, threadName, pid, key, args) { |
| var kthread = this.importer.getOrCreateKernelThread( |
| category + ':' + threadName, pid); |
| if (kthread.openAsyncSlices) { |
| var slice = kthread.openAsyncSlices[key]; |
| if (slice) { |
| slice.duration = ts - slice.start; |
| slice.args = args; |
| slice.endThread = kthread.thread; |
| slice.subSlices = [ |
| new tv.c.trace_model.AsyncSlice(category, slice.title, |
| slice.colorId, slice.start, slice.args, slice.duration) |
| ]; |
| kthread.thread.asyncSliceGroup.push(slice); |
| delete kthread.openAsyncSlices[key]; |
| } |
| } |
| }, |
| |
| /** |
| * Parses events and sets up state in the importer. |
| */ |
| f2fsWriteBeginEvent: function(eventName, cpuNumber, pid, ts, eventBase) { |
| var event = /dev = \((\d+,\d+)\), ino = (\d+), pos = (\d+), len = (\d+), flags = (\d+)/. // @suppress longLineCheck |
| exec(eventBase.details); |
| if (!event) |
| return false; |
| var device = event[1]; |
| var inode = parseInt(event[2]); |
| var pos = parseInt(event[3]); |
| var len = parseInt(event[4]); |
| var key = device + '-' + inode + '-' + pos + '-' + len; |
| this.openAsyncSlice(ts, 'f2fs', eventBase.threadName, eventBase.pid, |
| key, 'f2fs_write'); |
| return true; |
| }, |
| |
| f2fsWriteEndEvent: function(eventName, cpuNumber, pid, ts, eventBase) { |
| var event = /dev = \((\d+,\d+)\), ino = (\d+), pos = (\d+), len = (\d+), copied = (\d+)/. // @suppress longLineCheck |
| exec(eventBase.details); |
| if (!event) |
| return false; |
| |
| var device = event[1]; |
| var inode = parseInt(event[2]); |
| var pos = parseInt(event[3]); |
| var len = parseInt(event[4]); |
| var error = parseInt(event[5]) !== len; |
| var key = device + '-' + inode + '-' + pos + '-' + len; |
| this.closeAsyncSlice(ts, 'f2fs', eventBase.threadName, eventBase.pid, |
| key, { |
| device: device, |
| inode: inode, |
| error: error |
| }); |
| return true; |
| }, |
| |
| ext4WriteBeginEvent: function(eventName, cpuNumber, pid, ts, eventBase) { |
| var event = /dev (\d+,\d+) ino (\d+) pos (\d+) len (\d+) flags (\d+)/. |
| exec(eventBase.details); |
| if (!event) |
| return false; |
| var device = event[1]; |
| var inode = parseInt(event[2]); |
| var pos = parseInt(event[3]); |
| var len = parseInt(event[4]); |
| var key = device + '-' + inode + '-' + pos + '-' + len; |
| this.openAsyncSlice(ts, 'ext4', eventBase.threadName, eventBase.pid, |
| key, 'ext4_write'); |
| return true; |
| }, |
| |
| ext4WriteEndEvent: function(eventName, cpuNumber, pid, ts, eventBase) { |
| var event = /dev (\d+,\d+) ino (\d+) pos (\d+) len (\d+) copied (\d+)/. |
| exec(eventBase.details); |
| if (!event) |
| return false; |
| |
| var device = event[1]; |
| var inode = parseInt(event[2]); |
| var pos = parseInt(event[3]); |
| var len = parseInt(event[4]); |
| var error = parseInt(event[5]) !== len; |
| var key = device + '-' + inode + '-' + pos + '-' + len; |
| this.closeAsyncSlice(ts, 'ext4', eventBase.threadName, eventBase.pid, |
| key, { |
| device: device, |
| inode: inode, |
| error: error |
| }); |
| return true; |
| }, |
| |
| f2fsSyncFileEnterEvent: function(eventName, cpuNumber, pid, ts, eventBase) { |
| var event = new RegExp( |
| 'dev = \\((\\d+,\\d+)\\), ino = (\\d+), pino = (\\d+), i_mode = (\\S+), ' + // @suppress longLineCheck |
| 'i_size = (\\d+), i_nlink = (\\d+), i_blocks = (\\d+), i_advise = (\\d+)'). // @suppress longLineCheck |
| exec(eventBase.details); |
| if (!event) |
| return false; |
| |
| var device = event[1]; |
| var inode = parseInt(event[2]); |
| var key = device + '-' + inode; |
| this.openAsyncSlice(ts, 'f2fs', eventBase.threadName, eventBase.pid, |
| key, 'fsync'); |
| return true; |
| }, |
| |
| f2fsSyncFileExitEvent: function(eventName, cpuNumber, pid, ts, eventBase) { |
| var event = new RegExp('dev = \\((\\d+,\\d+)\\), ino = (\\d+), checkpoint is (\\S+), ' + // @suppress longLineCheck |
| 'datasync = (\\d+), ret = (\\d+)'). |
| exec(eventBase.details.replace('not needed', 'not_needed')); |
| if (!event) |
| return false; |
| |
| var device = event[1]; |
| var inode = parseInt(event[2]); |
| var error = parseInt(event[5]); |
| var key = device + '-' + inode; |
| this.closeAsyncSlice(ts, 'f2fs', eventBase.threadName, eventBase.pid, |
| key, { |
| device: device, |
| inode: inode, |
| error: error |
| }); |
| return true; |
| }, |
| |
| ext4SyncFileEnterEvent: function(eventName, cpuNumber, pid, ts, eventBase) { |
| var event = /dev (\d+,\d+) ino (\d+) parent (\d+) datasync (\d+)/. |
| exec(eventBase.details); |
| if (!event) |
| return false; |
| |
| var device = event[1]; |
| var inode = parseInt(event[2]); |
| var datasync = event[4] == 1; |
| var key = device + '-' + inode; |
| var action = datasync ? 'fdatasync' : 'fsync'; |
| this.openAsyncSlice(ts, 'ext4', eventBase.threadName, eventBase.pid, |
| key, action); |
| return true; |
| }, |
| |
| ext4SyncFileExitEvent: function(eventName, cpuNumber, pid, ts, eventBase) { |
| var event = /dev (\d+,\d+) ino (\d+) ret (\d+)/.exec(eventBase.details); |
| if (!event) |
| return false; |
| |
| var device = event[1]; |
| var inode = parseInt(event[2]); |
| var error = parseInt(event[3]); |
| var key = device + '-' + inode; |
| this.closeAsyncSlice(ts, 'ext4', eventBase.threadName, eventBase.pid, |
| key, { |
| device: device, |
| inode: inode, |
| error: error |
| }); |
| return true; |
| }, |
| |
| blockRqIssueEvent: function(eventName, cpuNumber, pid, ts, eventBase) { |
| var event = new RegExp('(\\d+,\\d+) (F)?([DWRN])(F)?(A)?(S)?(M)? ' + |
| '\\d+ \\(.*\\) (\\d+) \\+ (\\d+) \\[.*\\]').exec(eventBase.details); |
| if (!event) |
| return false; |
| |
| var action; |
| switch (event[3]) { |
| case 'D': |
| action = 'discard'; |
| break; |
| case 'W': |
| action = 'write'; |
| break; |
| case 'R': |
| action = 'read'; |
| break; |
| case 'N': |
| action = 'none'; |
| break; |
| default: |
| action = 'unknown'; |
| break; |
| } |
| |
| if (event[2]) { |
| action += ' flush'; |
| } |
| if (event[4] == 'F') { |
| action += ' fua'; |
| } |
| if (event[5] == 'A') { |
| action += ' ahead'; |
| } |
| if (event[6] == 'S') { |
| action += ' sync'; |
| } |
| if (event[7] == 'M') { |
| action += ' meta'; |
| } |
| var device = event[1]; |
| var sector = parseInt(event[8]); |
| var numSectors = parseInt(event[9]); |
| var key = device + '-' + sector + '-' + numSectors; |
| this.openAsyncSlice(ts, 'block', eventBase.threadName, eventBase.pid, |
| key, action); |
| return true; |
| }, |
| |
| blockRqCompleteEvent: function(eventName, cpuNumber, pid, ts, eventBase) { |
| var event = new RegExp('(\\d+,\\d+) (F)?([DWRN])(F)?(A)?(S)?(M)? ' + |
| '\\(.*\\) (\\d+) \\+ (\\d+) \\[(.*)\\]').exec(eventBase.details); |
| if (!event) |
| return false; |
| |
| var device = event[1]; |
| var sector = parseInt(event[8]); |
| var numSectors = parseInt(event[9]); |
| var error = parseInt(event[10]); |
| var key = device + '-' + sector + '-' + numSectors; |
| this.closeAsyncSlice(ts, 'block', eventBase.threadName, eventBase.pid, |
| key, { |
| device: device, |
| sector: sector, |
| numSectors: numSectors, |
| error: error |
| }); |
| return true; |
| } |
| }; |
| |
| Parser.register(DiskParser); |
| |
| return { |
| DiskParser: DiskParser |
| }; |
| }); |
| </script> |