3 # Copyright (C) 2024 Kemeng Shi <shikemeng@huaweicloud.com>
4 # Copyright (C) 2024 Huawei Inc
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 https://github.com/osandov/drgn.
10 writeback(kB) Amount of dirty pages are currently being written back to
13 reclaimable(kB) Amount of pages are currently reclaimable.
15 dirtied(kB) Amount of pages have been dirtied.
17 wrttien(kB) Amount of dirty pages have been written back to disk.
19 avg_wb(kBps) Smoothly estimated write bandwidth of writing dirty pages
29 from drgn
.helpers
.linux
.list import list_for_each_entry
32 parser
= argparse
.ArgumentParser(description
=desc
,
33 formatter_class
=argparse
.RawTextHelpFormatter
)
34 parser
.add_argument('bdi', metavar
='REGEX', nargs
='*',
35 help='Target backing device name patterns (all if empty)')
36 parser
.add_argument('-i', '--interval', metavar
='SECS', type=float, default
=1,
37 help='Monitoring interval (0 to print once and exit)')
38 parser
.add_argument('-j', '--json', action
='store_true',
39 help='Output in json')
40 parser
.add_argument('-c', '--cgroup', action
='store_true',
41 help='show writeback of bdi in cgroup')
42 args
= parser
.parse_args()
44 bdi_list
= prog
['bdi_list']
46 WB_RECLAIMABLE
= prog
['WB_RECLAIMABLE']
47 WB_WRITEBACK
= prog
['WB_WRITEBACK']
48 WB_DIRTIED
= prog
['WB_DIRTIED']
49 WB_WRITTEN
= prog
['WB_WRITTEN']
50 NR_WB_STAT_ITEMS
= prog
['NR_WB_STAT_ITEMS']
52 PAGE_SHIFT
= prog
['PAGE_SHIFT']
55 return x
<< (PAGE_SHIFT
- 10)
59 return { 'timestamp' : now
,
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
, }
67 def table_header_str():
68 return f
'{"":>16} {"writeback":>10} {"reclaimable":>12} ' \
69 f
'{"dirtied":>9} {"written":>9} {"avg_bw":>9}'
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} ' \
83 print(Stats
.table_header_str())
87 print(self
.table_row_str())
89 print(self
.dict(Stats
.now
))
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_():
99 ino
= str(wb
.memcg_css
.cgroup
.kn
.id.value_())
100 self
.name
= bdi_name
+ '_' + ino
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
))
109 self
.avg_bw
= int(K(wb
.avg_write_bandwidth
))
111 class BdiStats(Stats
):
112 def __init__(self
, bdi
):
113 self
.name
= bdi
.dev_name
.string_().decode()
114 self
.stats
= [0] * NR_WB_STAT_ITEMS
117 def collectStats(self
, wb_stats
):
118 for i
in range(NR_WB_STAT_ITEMS
):
119 self
.stats
[i
] += wb_stats
.stats
[i
]
121 self
.avg_bw
+= wb_stats
.avg_bw
125 def sigint_handler(signr
, frame
):
131 Stats
.table_fmt
= not args
.json
132 interval
= args
.interval
143 filter_re
= re
.compile(re_str
) if re_str
else None
146 signal
.signal(signal
.SIGINT
, sigint_handler
)
149 Stats
.now
= time
.time()
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
):
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
)
161 wb_stats
.show_stats()
163 bdi_stats
.show_stats()
164 if cgroup
and Stats
.table_fmt
:
171 if __name__
== "__main__":