1*881f1bb5SKemeng Shi#!/usr/bin/env drgn 2*881f1bb5SKemeng Shi# 3*881f1bb5SKemeng Shi# Copyright (C) 2024 Kemeng Shi <[email protected]> 4*881f1bb5SKemeng Shi# Copyright (C) 2024 Huawei Inc 5*881f1bb5SKemeng Shi 6*881f1bb5SKemeng Shidesc = """ 7*881f1bb5SKemeng ShiThis is a drgn script based on wq_monitor.py to monitor writeback info on 8*881f1bb5SKemeng Shibacking dev. For more info on drgn, visit https://github.com/osandov/drgn. 9*881f1bb5SKemeng Shi 10*881f1bb5SKemeng Shi writeback(kB) Amount of dirty pages are currently being written back to 11*881f1bb5SKemeng Shi disk. 12*881f1bb5SKemeng Shi 13*881f1bb5SKemeng Shi reclaimable(kB) Amount of pages are currently reclaimable. 14*881f1bb5SKemeng Shi 15*881f1bb5SKemeng Shi dirtied(kB) Amount of pages have been dirtied. 16*881f1bb5SKemeng Shi 17*881f1bb5SKemeng Shi wrttien(kB) Amount of dirty pages have been written back to disk. 18*881f1bb5SKemeng Shi 19*881f1bb5SKemeng Shi avg_wb(kBps) Smoothly estimated write bandwidth of writing dirty pages 20*881f1bb5SKemeng Shi back to disk. 21*881f1bb5SKemeng Shi""" 22*881f1bb5SKemeng Shi 23*881f1bb5SKemeng Shiimport signal 24*881f1bb5SKemeng Shiimport re 25*881f1bb5SKemeng Shiimport time 26*881f1bb5SKemeng Shiimport json 27*881f1bb5SKemeng Shi 28*881f1bb5SKemeng Shiimport drgn 29*881f1bb5SKemeng Shifrom drgn.helpers.linux.list import list_for_each_entry 30*881f1bb5SKemeng Shi 31*881f1bb5SKemeng Shiimport argparse 32*881f1bb5SKemeng Shiparser = argparse.ArgumentParser(description=desc, 33*881f1bb5SKemeng Shi formatter_class=argparse.RawTextHelpFormatter) 34*881f1bb5SKemeng Shiparser.add_argument('bdi', metavar='REGEX', nargs='*', 35*881f1bb5SKemeng Shi help='Target backing device name patterns (all if empty)') 36*881f1bb5SKemeng Shiparser.add_argument('-i', '--interval', metavar='SECS', type=float, default=1, 37*881f1bb5SKemeng Shi help='Monitoring interval (0 to print once and exit)') 38*881f1bb5SKemeng Shiparser.add_argument('-j', '--json', action='store_true', 39*881f1bb5SKemeng Shi help='Output in json') 40*881f1bb5SKemeng Shiparser.add_argument('-c', '--cgroup', action='store_true', 41*881f1bb5SKemeng Shi help='show writeback of bdi in cgroup') 42*881f1bb5SKemeng Shiargs = parser.parse_args() 43*881f1bb5SKemeng Shi 44*881f1bb5SKemeng Shibdi_list = prog['bdi_list'] 45*881f1bb5SKemeng Shi 46*881f1bb5SKemeng ShiWB_RECLAIMABLE = prog['WB_RECLAIMABLE'] 47*881f1bb5SKemeng ShiWB_WRITEBACK = prog['WB_WRITEBACK'] 48*881f1bb5SKemeng ShiWB_DIRTIED = prog['WB_DIRTIED'] 49*881f1bb5SKemeng ShiWB_WRITTEN = prog['WB_WRITTEN'] 50*881f1bb5SKemeng ShiNR_WB_STAT_ITEMS = prog['NR_WB_STAT_ITEMS'] 51*881f1bb5SKemeng Shi 52*881f1bb5SKemeng ShiPAGE_SHIFT = prog['PAGE_SHIFT'] 53*881f1bb5SKemeng Shi 54*881f1bb5SKemeng Shidef K(x): 55*881f1bb5SKemeng Shi return x << (PAGE_SHIFT - 10) 56*881f1bb5SKemeng Shi 57*881f1bb5SKemeng Shiclass Stats: 58*881f1bb5SKemeng Shi def dict(self, now): 59*881f1bb5SKemeng Shi return { 'timestamp' : now, 60*881f1bb5SKemeng Shi 'name' : self.name, 61*881f1bb5SKemeng Shi 'writeback' : self.stats[WB_WRITEBACK], 62*881f1bb5SKemeng Shi 'reclaimable' : self.stats[WB_RECLAIMABLE], 63*881f1bb5SKemeng Shi 'dirtied' : self.stats[WB_DIRTIED], 64*881f1bb5SKemeng Shi 'written' : self.stats[WB_WRITTEN], 65*881f1bb5SKemeng Shi 'avg_wb' : self.avg_bw, } 66*881f1bb5SKemeng Shi 67*881f1bb5SKemeng Shi def table_header_str(): 68*881f1bb5SKemeng Shi return f'{"":>16} {"writeback":>10} {"reclaimable":>12} ' \ 69*881f1bb5SKemeng Shi f'{"dirtied":>9} {"written":>9} {"avg_bw":>9}' 70*881f1bb5SKemeng Shi 71*881f1bb5SKemeng Shi def table_row_str(self): 72*881f1bb5SKemeng Shi out = f'{self.name[-16:]:16} ' \ 73*881f1bb5SKemeng Shi f'{self.stats[WB_WRITEBACK]:10} ' \ 74*881f1bb5SKemeng Shi f'{self.stats[WB_RECLAIMABLE]:12} ' \ 75*881f1bb5SKemeng Shi f'{self.stats[WB_DIRTIED]:9} ' \ 76*881f1bb5SKemeng Shi f'{self.stats[WB_WRITTEN]:9} ' \ 77*881f1bb5SKemeng Shi f'{self.avg_bw:9} ' 78*881f1bb5SKemeng Shi return out 79*881f1bb5SKemeng Shi 80*881f1bb5SKemeng Shi def show_header(): 81*881f1bb5SKemeng Shi if Stats.table_fmt: 82*881f1bb5SKemeng Shi print() 83*881f1bb5SKemeng Shi print(Stats.table_header_str()) 84*881f1bb5SKemeng Shi 85*881f1bb5SKemeng Shi def show_stats(self): 86*881f1bb5SKemeng Shi if Stats.table_fmt: 87*881f1bb5SKemeng Shi print(self.table_row_str()) 88*881f1bb5SKemeng Shi else: 89*881f1bb5SKemeng Shi print(self.dict(Stats.now)) 90*881f1bb5SKemeng Shi 91*881f1bb5SKemeng Shiclass WbStats(Stats): 92*881f1bb5SKemeng Shi def __init__(self, wb): 93*881f1bb5SKemeng Shi bdi_name = wb.bdi.dev_name.string_().decode() 94*881f1bb5SKemeng Shi # avoid to use bdi.wb.memcg_css which is only defined when 95*881f1bb5SKemeng Shi # CONFIG_CGROUP_WRITEBACK is enabled 96*881f1bb5SKemeng Shi if wb == wb.bdi.wb.address_of_(): 97*881f1bb5SKemeng Shi ino = "1" 98*881f1bb5SKemeng Shi else: 99*881f1bb5SKemeng Shi ino = str(wb.memcg_css.cgroup.kn.id.value_()) 100*881f1bb5SKemeng Shi self.name = bdi_name + '_' + ino 101*881f1bb5SKemeng Shi 102*881f1bb5SKemeng Shi self.stats = [0] * NR_WB_STAT_ITEMS 103*881f1bb5SKemeng Shi for i in range(NR_WB_STAT_ITEMS): 104*881f1bb5SKemeng Shi if wb.stat[i].count >= 0: 105*881f1bb5SKemeng Shi self.stats[i] = int(K(wb.stat[i].count)) 106*881f1bb5SKemeng Shi else: 107*881f1bb5SKemeng Shi self.stats[i] = 0 108*881f1bb5SKemeng Shi 109*881f1bb5SKemeng Shi self.avg_bw = int(K(wb.avg_write_bandwidth)) 110*881f1bb5SKemeng Shi 111*881f1bb5SKemeng Shiclass BdiStats(Stats): 112*881f1bb5SKemeng Shi def __init__(self, bdi): 113*881f1bb5SKemeng Shi self.name = bdi.dev_name.string_().decode() 114*881f1bb5SKemeng Shi self.stats = [0] * NR_WB_STAT_ITEMS 115*881f1bb5SKemeng Shi self.avg_bw = 0 116*881f1bb5SKemeng Shi 117*881f1bb5SKemeng Shi def collectStats(self, wb_stats): 118*881f1bb5SKemeng Shi for i in range(NR_WB_STAT_ITEMS): 119*881f1bb5SKemeng Shi self.stats[i] += wb_stats.stats[i] 120*881f1bb5SKemeng Shi 121*881f1bb5SKemeng Shi self.avg_bw += wb_stats.avg_bw 122*881f1bb5SKemeng Shi 123*881f1bb5SKemeng Shiexit_req = False 124*881f1bb5SKemeng Shi 125*881f1bb5SKemeng Shidef sigint_handler(signr, frame): 126*881f1bb5SKemeng Shi global exit_req 127*881f1bb5SKemeng Shi exit_req = True 128*881f1bb5SKemeng Shi 129*881f1bb5SKemeng Shidef main(): 130*881f1bb5SKemeng Shi # handle args 131*881f1bb5SKemeng Shi Stats.table_fmt = not args.json 132*881f1bb5SKemeng Shi interval = args.interval 133*881f1bb5SKemeng Shi cgroup = args.cgroup 134*881f1bb5SKemeng Shi 135*881f1bb5SKemeng Shi re_str = None 136*881f1bb5SKemeng Shi if args.bdi: 137*881f1bb5SKemeng Shi for r in args.bdi: 138*881f1bb5SKemeng Shi if re_str is None: 139*881f1bb5SKemeng Shi re_str = r 140*881f1bb5SKemeng Shi else: 141*881f1bb5SKemeng Shi re_str += '|' + r 142*881f1bb5SKemeng Shi 143*881f1bb5SKemeng Shi filter_re = re.compile(re_str) if re_str else None 144*881f1bb5SKemeng Shi 145*881f1bb5SKemeng Shi # monitoring loop 146*881f1bb5SKemeng Shi signal.signal(signal.SIGINT, sigint_handler) 147*881f1bb5SKemeng Shi 148*881f1bb5SKemeng Shi while not exit_req: 149*881f1bb5SKemeng Shi Stats.now = time.time() 150*881f1bb5SKemeng Shi 151*881f1bb5SKemeng Shi Stats.show_header() 152*881f1bb5SKemeng Shi for bdi in list_for_each_entry('struct backing_dev_info', bdi_list.address_of_(), 'bdi_list'): 153*881f1bb5SKemeng Shi bdi_stats = BdiStats(bdi) 154*881f1bb5SKemeng Shi if filter_re and not filter_re.search(bdi_stats.name): 155*881f1bb5SKemeng Shi continue 156*881f1bb5SKemeng Shi 157*881f1bb5SKemeng Shi for wb in list_for_each_entry('struct bdi_writeback', bdi.wb_list.address_of_(), 'bdi_node'): 158*881f1bb5SKemeng Shi wb_stats = WbStats(wb) 159*881f1bb5SKemeng Shi bdi_stats.collectStats(wb_stats) 160*881f1bb5SKemeng Shi if cgroup: 161*881f1bb5SKemeng Shi wb_stats.show_stats() 162*881f1bb5SKemeng Shi 163*881f1bb5SKemeng Shi bdi_stats.show_stats() 164*881f1bb5SKemeng Shi if cgroup and Stats.table_fmt: 165*881f1bb5SKemeng Shi print() 166*881f1bb5SKemeng Shi 167*881f1bb5SKemeng Shi if interval == 0: 168*881f1bb5SKemeng Shi break 169*881f1bb5SKemeng Shi time.sleep(interval) 170*881f1bb5SKemeng Shi 171*881f1bb5SKemeng Shiif __name__ == "__main__": 172*881f1bb5SKemeng Shi main() 173