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 */
11base.require('tracing.importer.linux_perf.parser');
12base.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