1 // Copyright (c) 2013 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 'use strict'; 6 7 /** 8 * @fileoverview Parses filesystem and block device events in the Linux event 9 * trace format. 10 */ 11 base.require('tracing.importer.linux_perf.parser'); 12 base.exportTo('tracing.importer.linux_perf', function() { 13 14 var Parser = tracing.importer.linux_perf.Parser; 15 16 /** 17 * Parses linux filesystem and block device trace events. 18 * @constructor 19 */ 20 function DiskParser(importer) { 21 Parser.call(this, importer); 22 23 importer.registerEventHandler('f2fs_write_begin', 24 DiskParser.prototype.f2fsWriteBeginEvent.bind(this)); 25 importer.registerEventHandler('f2fs_write_end', 26 DiskParser.prototype.f2fsWriteEndEvent.bind(this)); 27 importer.registerEventHandler('f2fs_sync_file_enter', 28 DiskParser.prototype.f2fsSyncFileEnterEvent.bind(this)); 29 importer.registerEventHandler('f2fs_sync_file_exit', 30 DiskParser.prototype.f2fsSyncFileExitEvent.bind(this)); 31 importer.registerEventHandler('ext4_sync_file_enter', 32 DiskParser.prototype.ext4SyncFileEnterEvent.bind(this)); 33 importer.registerEventHandler('ext4_sync_file_exit', 34 DiskParser.prototype.ext4SyncFileExitEvent.bind(this)); 35 importer.registerEventHandler('ext4_da_write_begin', 36 DiskParser.prototype.ext4WriteBeginEvent.bind(this)); 37 importer.registerEventHandler('ext4_da_write_end', 38 DiskParser.prototype.ext4WriteEndEvent.bind(this)); 39 importer.registerEventHandler('block_rq_issue', 40 DiskParser.prototype.blockRqIssueEvent.bind(this)); 41 importer.registerEventHandler('block_rq_complete', 42 DiskParser.prototype.blockRqCompleteEvent.bind(this)); 43 } 44 45 DiskParser.prototype = { 46 __proto__: Parser.prototype, 47 48 openAsyncSlice: function(ts, category, threadName, pid, key, name) { 49 var kthread = this.importer.getOrCreateKernelThread( 50 category + ':' + threadName, pid); 51 var slice = new tracing.trace_model.AsyncSlice( 52 category, name, tracing.getStringColorId(name), ts); 53 slice.startThread = kthread.thread; 54 55 if (!kthread.openAsyncSlices) { 56 kthread.openAsyncSlices = { }; 57 } 58 kthread.openAsyncSlices[key] = slice; 59 }, 60 61 closeAsyncSlice: function(ts, category, threadName, pid, key, args) { 62 var kthread = this.importer.getOrCreateKernelThread( 63 category + ':' + threadName, pid); 64 if (kthread.openAsyncSlices) { 65 var slice = kthread.openAsyncSlices[key]; 66 if (slice) { 67 slice.duration = ts - slice.start; 68 slice.args = args; 69 slice.endThread = kthread.thread; 70 slice.subSlices = [ 71 new tracing.trace_model.Slice(category, slice.title, 72 slice.colorId, slice.start, slice.args, slice.duration) 73 ]; 74 kthread.thread.asyncSliceGroup.push(slice); 75 delete kthread.openAsyncSlices[key]; 76 } 77 } 78 }, 79 80 /** 81 * Parses events and sets up state in the importer. 82 */ 83 f2fsWriteBeginEvent: function(eventName, cpuNumber, pid, ts, eventBase) { 84 var event = /dev = \((\d+,\d+)\), ino = (\d+), pos = (\d+), len = (\d+), flags = (\d+)/. 85 exec(eventBase.details); 86 if (!event) 87 return false; 88 var device = event[1]; 89 var inode = parseInt(event[2]); 90 var pos = parseInt(event[3]); 91 var len = parseInt(event[4]); 92 var key = device + '-' + inode + '-' + pos + '-' + len; 93 this.openAsyncSlice(ts, "f2fs", eventBase.threadName, eventBase.pid, 94 key, "f2fs_write"); 95 return true; 96 }, 97 98 f2fsWriteEndEvent: function(eventName, cpuNumber, pid, ts, eventBase) { 99 var event = /dev = \((\d+,\d+)\), ino = (\d+), pos = (\d+), len = (\d+), copied = (\d+)/. 100 exec(eventBase.details); 101 if (!event) 102 return false; 103 104 var device = event[1]; 105 var inode = parseInt(event[2]); 106 var pos = parseInt(event[3]); 107 var len = parseInt(event[4]); 108 var error = parseInt(event[5]) !== len; 109 var key = device + '-' + inode + '-' + pos + '-' + len; 110 this.closeAsyncSlice(ts, 'f2fs', eventBase.threadName, eventBase.pid, 111 key, { 112 device: device, 113 inode: inode, 114 error: error 115 }); 116 return true; 117 }, 118 119 ext4WriteBeginEvent: function(eventName, cpuNumber, pid, ts, eventBase) { 120 var event = /dev (\d+,\d+) ino (\d+) pos (\d+) len (\d+) flags (\d+)/. 121 exec(eventBase.details); 122 if (!event) 123 return false; 124 var device = event[1]; 125 var inode = parseInt(event[2]); 126 var pos = parseInt(event[3]); 127 var len = parseInt(event[4]); 128 var key = device + '-' + inode + '-' + pos + '-' + len; 129 this.openAsyncSlice(ts, "ext4", eventBase.threadName, eventBase.pid, 130 key, "ext4_write"); 131 return true; 132 }, 133 134 ext4WriteEndEvent: function(eventName, cpuNumber, pid, ts, eventBase) { 135 var event = /dev (\d+,\d+) ino (\d+) pos (\d+) len (\d+) copied (\d+)/. 136 exec(eventBase.details); 137 if (!event) 138 return false; 139 140 var device = event[1]; 141 var inode = parseInt(event[2]); 142 var pos = parseInt(event[3]); 143 var len = parseInt(event[4]); 144 var error = parseInt(event[5]) !== len; 145 var key = device + '-' + inode + '-' + pos + '-' + len; 146 this.closeAsyncSlice(ts, 'ext4', eventBase.threadName, eventBase.pid, 147 key, { 148 device: device, 149 inode: inode, 150 error: error 151 }); 152 return true; 153 }, 154 155 f2fsSyncFileEnterEvent: function(eventName, cpuNumber, pid, ts, eventBase) { 156 var event = new RegExp( 157 'dev = \\((\\d+,\\d+)\\), ino = (\\d+), pino = (\\d+), i_mode = (\\S+), ' + 158 'i_size = (\\d+), i_nlink = (\\d+), i_blocks = (\\d+), i_advise = (\\d+)'). 159 exec(eventBase.details); 160 if (!event) 161 return false; 162 163 var device = event[1]; 164 var inode = parseInt(event[2]); 165 var key = device + '-' + inode; 166 this.openAsyncSlice(ts, 'f2fs', eventBase.threadName, eventBase.pid, 167 key, 'fsync'); 168 return true; 169 }, 170 171 f2fsSyncFileExitEvent: function(eventName, cpuNumber, pid, ts, eventBase) { 172 var event = new RegExp('dev = \\((\\d+,\\d+)\\), ino = (\\d+), checkpoint is (\\S+), ' + 173 'datasync = (\\d+), ret = (\\d+)'). 174 exec(eventBase.details.replace("not needed", "not_needed")); 175 if (!event) 176 return false; 177 178 var device = event[1]; 179 var inode = parseInt(event[2]); 180 var error = parseInt(event[5]); 181 var key = device + '-' + inode; 182 this.closeAsyncSlice(ts, 'f2fs', eventBase.threadName, eventBase.pid, 183 key, { 184 device: device, 185 inode: inode, 186 error: error 187 }); 188 return true; 189 }, 190 191 ext4SyncFileEnterEvent: function(eventName, cpuNumber, pid, ts, eventBase) { 192 var event = /dev (\d+,\d+) ino (\d+) parent (\d+) datasync (\d+)/. 193 exec(eventBase.details); 194 if (!event) 195 return false; 196 197 var device = event[1]; 198 var inode = parseInt(event[2]); 199 var datasync = event[4] == 1; 200 var key = device + '-' + inode; 201 var action = datasync ? 'fdatasync' : 'fsync'; 202 this.openAsyncSlice(ts, 'ext4', eventBase.threadName, eventBase.pid, 203 key, action); 204 return true; 205 }, 206 207 ext4SyncFileExitEvent: function(eventName, cpuNumber, pid, ts, eventBase) { 208 var event = /dev (\d+,\d+) ino (\d+) ret (\d+)/.exec(eventBase.details); 209 if (!event) 210 return false; 211 212 var device = event[1]; 213 var inode = parseInt(event[2]); 214 var error = parseInt(event[3]); 215 var key = device + '-' + inode; 216 this.closeAsyncSlice(ts, 'ext4', eventBase.threadName, eventBase.pid, 217 key, { 218 device: device, 219 inode: inode, 220 error: error 221 }); 222 return true; 223 }, 224 225 blockRqIssueEvent: function(eventName, cpuNumber, pid, ts, eventBase) { 226 var event = new RegExp('(\\d+,\\d+) (F)?([DWRN])(F)?(A)?(S)?(M)? ' + 227 '\\d+ \\(.*\\) (\\d+) \\+ (\\d+) \\[.*\\]').exec(eventBase.details); 228 if (!event) 229 return false; 230 231 var action; 232 switch (event[3]) { 233 case 'D': 234 action = 'discard'; 235 break; 236 case 'W': 237 action = 'write'; 238 break; 239 case 'R': 240 action = 'read'; 241 break; 242 case 'N': 243 action = 'none'; 244 break; 245 default: 246 action = 'unknown'; 247 break; 248 } 249 250 if (event[2]) { 251 action += ' flush'; 252 } 253 if (event[4] == 'F') { 254 action += ' fua'; 255 } 256 if (event[5] == 'A') { 257 action += ' ahead'; 258 } 259 if (event[6] == 'S') { 260 action += ' sync'; 261 } 262 if (event[7] == 'M') { 263 action += ' meta'; 264 } 265 var device = event[1]; 266 var sector = parseInt(event[8]); 267 var numSectors = parseInt(event[9]); 268 var key = device + '-' + sector + '-' + numSectors; 269 this.openAsyncSlice(ts, 'block', eventBase.threadName, eventBase.pid, 270 key, action); 271 return true; 272 }, 273 274 blockRqCompleteEvent: function(eventName, cpuNumber, pid, ts, eventBase) { 275 var event = new RegExp('(\\d+,\\d+) (F)?([DWRN])(F)?(A)?(S)?(M)? ' + 276 '\\(.*\\) (\\d+) \\+ (\\d+) \\[(.*)\\]').exec(eventBase.details); 277 if (!event) 278 return false; 279 280 var device = event[1]; 281 var sector = parseInt(event[8]); 282 var numSectors = parseInt(event[9]); 283 var error = parseInt(event[10]); 284 var key = device + '-' + sector + '-' + numSectors; 285 this.closeAsyncSlice(ts, 'block', eventBase.threadName, eventBase.pid, 286 key, { 287 device: device, 288 sector: sector, 289 numSectors: numSectors, 290 error: error 291 }); 292 return true; 293 } 294 }; 295 296 Parser.registerSubtype(DiskParser); 297 298 return { 299 DiskParser: DiskParser 300 }; 301 }); 302