Home | History | Annotate | Download | only in io_analysis
      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 
     19 import collections
     20 import os
     21 import re
     22 import string
     23 import sys
     24 
     25 RE_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
     28 DEV_MAJOR_MULTIPLIER = 1000
     29 
     30 # dm access is remapped to disk access. So account differently
     31 DM_MAJOR = 253
     32 
     33 MAX_PROCESS_DUMP = 10
     34 
     35 class 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 
     41 def 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 
     47 class 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 
    107 class 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 
    209 def 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 
    224 if __name__ == '__main__':
    225   main(sys.argv)
    226