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