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