• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# Copyright (C) 2016 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17"""Analyze block trace"""
18
19import collections
20import os
21import re
22import string
23import sys
24
25RE_BLOCK = r'.+-([0-9]+).*\s+([0-9]+\.[0-9]+):\s+block_bio_queue:\s+([0-9]+)\,([0-9]+)\s+([RW]\S*)\s+([0-9]+)\s+\+\s+([0-9]+)\s+\[([^\]]+)'
26
27# dev_num = major * MULTIPLIER + minor
28DEV_MAJOR_MULTIPLIER = 1000
29
30# dm access is remapped to disk access. So account differently
31DM_MAJOR = 253
32
33MAX_PROCESS_DUMP = 10
34
35class RwEvent:
36  def __init__(self, block_num, start_time, size):
37    self.block_num = block_num
38    self.start_time = start_time
39    self.size = size
40
41def get_string_pos(strings, string_to_find):
42  for i, s in enumerate(strings):
43    if s == string_to_find:
44      return i
45  return -1
46
47class ProcessData:
48  def __init__(self, name):
49    self.name = name
50    self.reads = {} # k : dev_num, v : [] of reads
51    self.per_device_total_reads = {}
52    self.writes = {}
53    self.per_device_total_writes = {}
54    self.total_reads = 0
55    self.total_writes = 0
56    self.total_dm_reads = 0
57    self.total_dm_writes = 0
58
59  def add_read_event(self, major, minor, event):
60    devNum = major * DEV_MAJOR_MULTIPLIER + minor;
61    events = self.reads.get(devNum)
62    if not events:
63      events = []
64      self.reads[devNum] = events
65      self.per_device_total_reads[devNum] = 0
66    events.append(event)
67    self.total_reads += event.size
68    self.per_device_total_reads[devNum] += event.size
69
70  def add_write_event(self, major, minor, event):
71    devNum = major * DEV_MAJOR_MULTIPLIER + minor;
72    events = self.writes.get(devNum)
73    if not events:
74      events = []
75      self.writes[devNum] = events
76      self.per_device_total_writes[devNum] = 0
77    events.append(event)
78    self.total_writes += event.size
79    self.per_device_total_writes[devNum] += event.size
80
81  def add_dm_read(self, size):
82    self.total_dm_reads += size
83
84  def add_dm_write(self, size):
85    self.total_dm_writes += size
86
87  def dump(self):
88    print "Process,", self.name
89    print " total reads,", self.total_reads
90    print " total writes,", self.total_writes
91    print " total dm reads,", self.total_dm_reads
92    print " total dm writes,", self.total_dm_writes
93    print " R per device"
94    sorted_r = collections.OrderedDict(sorted(self.per_device_total_reads.items(), \
95      key = lambda item: item[1], reverse = True))
96    for i in range(len(sorted_r)):
97      dev = sorted_r.popitem(last=False)
98      print " ", dev[0],dev[1]
99
100    print " W per device"
101    sorted_w = collections.OrderedDict(sorted(self.per_device_total_writes.items(), \
102      key = lambda item: item[1], reverse = True))
103    for i in range(len(sorted_w)):
104      dev = sorted_w.popitem(last=False)
105      print " ", dev[0],dev[1]
106
107class Trace:
108
109  def __init__(self):
110    self.ios = {} #K: process name, v:ProcessData
111    self.total_reads = 0
112    self.total_writes = 0
113    self.total_reads_per_device = {} #K: block num, V: total blocks
114    self.total_writes_per_device = {}
115    self.total_dm_reads = {} #K: devnum, V: blocks
116    self.total_dm_writes = {}
117
118  def parse_bio_queue(self, l, match):
119    pid = match.group(1)
120    start_time = int(float(match.group(2))*1000000) #us
121    major = int(match.group(3))
122    minor =  int(match.group(4))
123    devNum = major * DEV_MAJOR_MULTIPLIER + minor;
124    operation =  match.group(5)
125    block_num = int(match.group(6))
126    size = int(match.group(7))
127    process = match.group(8) + "-" + pid
128    event = RwEvent(block_num, start_time, size)
129    io = self.ios.get(process)
130    if not io:
131      io = ProcessData(process)
132      self.ios[process] = io
133    if major == DM_MAJOR:
134      devNum = major * DEV_MAJOR_MULTIPLIER + minor;
135      if operation[0] == 'R':
136        if devNum not in self.total_dm_reads:
137          self.total_dm_reads[devNum] = 0
138        self.total_dm_reads[devNum] += size
139        io.add_dm_read(size)
140      elif operation[0] == 'W':
141        if devNum not in self.total_dm_writes:
142          self.total_dm_writes[devNum] = 0
143        self.total_dm_writes[devNum] += size
144        io.add_dm_write(size)
145      return
146    if operation[0] == 'R':
147      io.add_read_event(major, minor, event)
148      self.total_reads += size
149      per_device = self.total_reads_per_device.get(devNum)
150      if not per_device:
151        self.total_reads_per_device[devNum] = 0
152      self.total_reads_per_device[devNum] += size
153    elif operation[0] == 'W':
154      io.add_write_event(major, minor, event)
155      self.total_writes += size
156      per_device = self.total_writes_per_device.get(devNum)
157      if not per_device:
158        self.total_writes_per_device[devNum] = 0
159      self.total_writes_per_device[devNum] += size
160
161  def parse_block_trace(self, l, match):
162    try:
163      self.parse_bio_queue(l, match)
164    except ValueError:
165      print "cannot parse:", l
166      raise
167
168  def dump(self):
169    print "total read blocks,", self.total_reads
170    print "total write blocks,", self.total_writes
171    print "Total DM R"
172    for dev,size in self.total_dm_reads.items():
173      print dev, size
174    print "Total DM W"
175    for dev,size in self.total_dm_writes.items():
176      print dev, size
177    print "**Process total R/W"
178    sorted_by_total_rw = collections.OrderedDict(sorted(self.ios.items(), \
179      key = lambda item: item[1].total_reads + item[1].total_writes, reverse = True))
180    for i in range(MAX_PROCESS_DUMP):
181      process = sorted_by_total_rw.popitem(last=False)
182      if not process:
183        break
184      process[1].dump()
185
186    print "**Process total W"
187    sorted_by_total_w = collections.OrderedDict(sorted(self.ios.items(), \
188      key = lambda item: item[1].total_writes, reverse = True))
189    for i in range(5):
190      process = sorted_by_total_w.popitem(last=False)
191      if not process:
192        break
193      process[1].dump()
194
195    print "**Device total R"
196    sorted_by_total_r = collections.OrderedDict(sorted(self.total_reads_per_device.items(), \
197      key = lambda item: item[1], reverse = True))
198    for i in range(len(sorted_by_total_r)):
199      dev = sorted_by_total_r.popitem(last=False)
200      print dev[0],dev[1]
201
202    print "**Device total W"
203    sorted_by_total_w = collections.OrderedDict(sorted(self.total_writes_per_device.items(), \
204      key = lambda item: item[1], reverse = True))
205    for i in range(len(sorted_by_total_w)):
206      dev = sorted_by_total_w.popitem(last=False)
207      print dev[0],dev[1]
208
209def main(argv):
210  if (len(argv) < 2):
211    print "check_io_trace_all.py filename"
212    return
213  filename = argv[1]
214
215  trace = Trace()
216  prog = re.compile(RE_BLOCK)
217  with open(filename) as f:
218    for l in f:
219      result = prog.match(l)
220      if result:
221        trace.parse_block_trace(l, result)
222  trace.dump()
223
224if __name__ == '__main__':
225  main(sys.argv)
226