// 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. /** * @fileoverview Parses filesystem and block device events in the Linux event * trace format. */ base.require('importer.linux_perf.parser'); base.exportTo('tracing.importer.linux_perf', function() { var Parser = tracing.importer.linux_perf.Parser; /** * Parses linux filesystem and block device trace events. * @constructor */ function DiskParser(importer) { Parser.call(this, importer); importer.registerEventHandler('ext4_sync_file_enter', DiskParser.prototype.ext4SyncFileEnterEvent.bind(this)); importer.registerEventHandler('ext4_sync_file_exit', DiskParser.prototype.ext4SyncFileExitEvent.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 slice = new tracing.model.AsyncSlice( category, name, tracing.getStringColorId(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 tracing.model.Slice(category, slice.title, slice.colorId, slice.start, slice.args, slice.duration)]; kthread.thread.asyncSlices.push(slice); delete kthread.openAsyncSlices[key]; } } }, /** * Parses events and sets up state in the importer. */ 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.registerSubtype(DiskParser); return { DiskParser: DiskParser }; });