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