Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/writeback/wb_monitor.py
26285 views
1
#!/usr/bin/env drgn
2
#
3
# Copyright (C) 2024 Kemeng Shi <[email protected]>
4
# Copyright (C) 2024 Huawei Inc
5
6
desc = """
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.
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
23
import signal
24
import re
25
import time
26
import json
27
28
import drgn
29
from drgn.helpers.linux.list import list_for_each_entry
30
31
import argparse
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()
43
44
bdi_list = prog['bdi_list']
45
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']
51
52
PAGE_SHIFT = prog['PAGE_SHIFT']
53
54
def K(x):
55
return x << (PAGE_SHIFT - 10)
56
57
class 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
91
class 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
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
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
123
exit_req = False
124
125
def sigint_handler(signr, frame):
126
global exit_req
127
exit_req = True
128
129
def 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
171
if __name__ == "__main__":
172
main()
173
174