Skip to content

Commit 8d5620b

Browse files
author
Don Brady
authored
DLPX-75711 [Backport of DLPX-75405 to 6.0.9.0] nfs_threads script should flush output and include timestamps (#63)
1 parent b095412 commit 8d5620b

File tree

1 file changed

+75
-47
lines changed

1 file changed

+75
-47
lines changed

cmd/nfs_threads.py

Lines changed: 75 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python3
22
#
3-
# Copyright (c) 2020 by Delphix. All rights reserved.
3+
# Copyright (c) 2020-2021 by Delphix. All rights reserved.
44
#
55
# SPDX-License-Identifier: GPL-2.0-or-later
66
#
@@ -9,22 +9,24 @@
99
Display NFS thread usage info along with NFS I/O context.
1010
1111
Output Sample:
12-
13-
packets sockets threads threads metadata read read write write
14-
arrived enqueued woken used calls iops thruput iops thruput
15-
4589 0 4589 25 16 273 3.6MB 212 2.6MB
16-
4735 0 4735 8 1 287 3.8MB 212 2.7MB
17-
4693 0 4693 10 0 280 3.7MB 216 2.7MB
18-
4625 0 4625 15 0 278 3.7MB 212 2.6MB
19-
4687 0 4687 7 1 285 3.8MB 210 2.6MB
20-
4701 0 4701 12 0 285 3.8MB 215 2.7MB
12+
packets sockets threads threads metadata read read write write
13+
arrived enqueued woken used calls iops thruput iops thruput
14+
78683 538 78145 57 209 3390 142.5MB 9014 107.0MB
15+
106114 4527 101587 63 50 4211 166.8MB 13294 133.0MB
16+
110220 1511 108709 61 10 4347 10.7MB 13767 137.5MB
17+
80630 4741 75889 62 50 4218 179.4MB 8743 107.9MB
18+
115463 11400 104063 62 21 4231 179.4MB 15404 150.5MB
2119
'''
2220

21+
import os
2322
import psutil
2423
from signal import signal, SIGINT
2524
import sys
2625
from time import sleep
26+
import datetime
27+
import argparse
2728

29+
PROCFS_NFSD = "/proc/fs/nfsd"
2830
POOL_STATS = "/proc/fs/nfsd/pool_stats"
2931
NFSD_STATS = "/proc/net/rpc/nfsd"
3032

@@ -33,18 +35,26 @@
3335
H2 = ['arrived', 'enqueued', 'woken', 'used', 'calls', 'iops', 'thruput',
3436
'iops', 'thruput']
3537

36-
INTERVAL = 5
38+
39+
def parse_cmdline():
40+
parser = argparse.ArgumentParser(
41+
description='Display nfsd thread usage info along with NFS I/O '
42+
'context')
43+
parser.add_argument(
44+
'--interval', type=int, choices=range(1, 31),
45+
default=5, help='sampling interval in seconds (defaults to 5)')
46+
return parser.parse_args()
3747

3848

3949
def server_stopped(message=''):
40-
print("NFS Server Stopped {}".format(message))
41-
sys.exit()
50+
print("*NFS Server Stopped {}".format(message))
4251

4352

4453
def print_header(header):
54+
print(' '*19, end='')
4555
for col in header:
4656
print('{0:>10}'.format(col), end='')
47-
print()
57+
print(flush=True)
4858

4959

5060
def pool_stats():
@@ -56,10 +66,10 @@ def pool_stats():
5666
packets = int(fields[1])
5767
enqueued = int(fields[2])
5868
woken = int(fields[3])
59-
timedout = int(fields[4])
60-
return packets, enqueued, woken, timedout
61-
except OSError:
69+
return packets, enqueued, woken, None
70+
except OSError as e:
6271
server_stopped()
72+
return 0, 0, 0, e
6373

6474

6575
def nfs_stats():
@@ -93,21 +103,22 @@ def nfs_stats():
93103
metadata += int(fields[11])
94104
metadata += int(fields[17])
95105
metadata += int(fields[36])
96-
return readbytes, writebytes, readops, writeops, metadata
97-
except OSError:
106+
return readbytes, writebytes, readops, writeops, metadata, None
107+
except OSError as e:
98108
server_stopped()
109+
return 0, 0, 0, 0, 0, e
99110

100111

101-
def context_switches(pids):
102-
"Return a list of context switches per process in pids"
112+
def cpu_time(pids):
113+
"Return a list of time spent on cpu per process in pids"
103114
ls = []
104115
for pid in pids:
105116
try:
106-
pctxsw = psutil.Process(pid).num_ctx_switches()
107-
ls.append(pctxsw.voluntary + pctxsw.involuntary)
108-
except psutil.NoSuchProcess:
117+
ls.append(psutil.Process(pid).cpu_times().system)
118+
except psutil.NoSuchProcess as e:
109119
server_stopped()
110-
return ls
120+
return None, e
121+
return ls, None
111122

112123

113124
def nfsd_processes():
@@ -132,35 +143,44 @@ def print_thruput(value):
132143
print('{0:>8}KB'.format(int(value / 1024)), end='')
133144

134145

135-
def print_line():
146+
def print_line(interval):
147+
lines = 0
136148
pids = nfsd_processes()
137149

138-
prevSwitches = context_switches(pids)
139-
prevPackets, prevEnqueued, prevWoken, prevTimedout = pool_stats()
140-
prevRB, prevWB, prevRO, prevWO, prevMeta = nfs_stats()
150+
prevCpuTime, e1 = cpu_time(pids)
151+
prevPackets, prevEnqueued, prevWoken, e2 = pool_stats()
152+
prevRB, prevWB, prevRO, prevWO, prevMeta, e3 = nfs_stats()
153+
if e1 or e2 or e3:
154+
return
141155

142-
while(not sleep(INTERVAL)):
143-
nextSwitches = context_switches(pids)
144-
nextPackets, nextEnqueued, nextWoken, nextTimedout = pool_stats()
145-
nextRB, nextWB, nextRO, nextWO, nextMeta = nfs_stats()
156+
while(not sleep(interval)):
157+
nextCpuTime, e1 = cpu_time(pids)
158+
nextPackets, nextEnqueued, nextWoken, e2 = pool_stats()
159+
nextRB, nextWB, nextRO, nextWO, nextMeta, e3 = nfs_stats()
160+
if e1 or e2 or e3:
161+
return
146162

163+
#
164+
# Count threads that used cpu time in this interval
165+
#
147166
threads = 0
148-
for i in range(0, len(prevSwitches)):
149-
if not prevSwitches[i] == nextSwitches[i]:
167+
for i in range(0, len(prevCpuTime)):
168+
if not prevCpuTime[i] == nextCpuTime[i]:
150169
threads += 1
151-
threads -= nextTimedout - prevTimedout
152-
prevSwitches = nextSwitches.copy()
170+
prevCpuTime = nextCpuTime.copy()
153171

154172
#
155173
# The published 'sockets-enqueued' value needs adjustment
156174
#
157175
enqueued = (nextEnqueued - prevEnqueued) - (nextWoken - prevWoken)
176+
if enqueued < 0:
177+
enqueued = 0
158178

159179
#
160180
# For IOPS values less than 10 display with decimal
161181
#
162-
readOps = (nextRO - prevRO) / INTERVAL
163-
writeOps = (nextWO - prevWO) / INTERVAL
182+
readOps = (nextRO - prevRO) / interval
183+
writeOps = (nextWO - prevWO) / interval
164184
readOps = int(readOps) if readOps > 9 else round(readOps, 1)
165185
writeOps = int(writeOps) if writeOps > 9 else round(writeOps, 1)
166186

@@ -173,35 +193,43 @@ def print_line():
173193
if nextWB < prevWB:
174194
prevWB = 0
175195

196+
if lines % 48 == 0:
197+
print_header(H1)
198+
print_header(H2)
199+
200+
print('{:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now()), end='')
176201
print_value(nextPackets - prevPackets)
177202
print_value(enqueued)
178203
print_value(nextWoken - prevWoken)
179204
print_value(threads)
180205
print_value(nextMeta - prevMeta)
181206
print_value(readOps)
182-
print_thruput((nextRB - prevRB) / INTERVAL)
207+
print_thruput((nextRB - prevRB) / interval)
183208
print_value(writeOps)
184-
print_thruput((nextWB - prevWB) / INTERVAL)
185-
print()
209+
print_thruput((nextWB - prevWB) / interval)
210+
print(flush=True)
186211

187212
prevPackets = nextPackets
188213
prevEnqueued = nextEnqueued
189214
prevWoken = nextWoken
190-
prevTimedout = nextTimedout
191215
prevMeta = nextMeta
192216
prevRB = nextRB
193217
prevWB = nextWB
194218
prevRO = nextRO
195219
prevWO = nextWO
220+
lines += 1
196221

197222

198223
def handler(signal_received, frame):
199-
print()
224+
print(flush=True)
200225
sys.exit(0)
201226

202227

203228
signal(SIGINT, handler)
204229

205-
print_header(H1)
206-
print_header(H2)
207-
print_line()
230+
arguments = parse_cmdline()
231+
232+
while True:
233+
if os.path.exists(PROCFS_NFSD):
234+
print_line(arguments.interval)
235+
sleep(2)

0 commit comments

Comments
 (0)