Skip to content

Instantly share code, notes, and snippets.

@jizhilong
Created May 25, 2020 11:45
Show Gist options
  • Save jizhilong/bb07ef4eb621ef140aec39bd05776e47 to your computer and use it in GitHub Desktop.
Save jizhilong/bb07ef4eb621ef140aec39bd05776e47 to your computer and use it in GitHub Desktop.
count and sort jvm threadpool's context switch rate
#!/usr/bin/env python
"""
Author: Ji.Zhilong <zhilongji@gmail.com>
Description: count and sort jvm threadpool's context switch rate
Usage: python cswsnoop_jvm.py
"""
import sys
import time
import os
import subprocess
import math
milli_second = 1000000
second = 1000 * milli_second
minute = 60 * second
interval = 5 * second
seconds_per_minute = 60.0
one_minute = 1
five_minutes = 5
fifteen_minutes = 15
interval_ = 1.0 * interval
m1_alpha = 1 - math.exp(-5.0 / (seconds_per_minute * one_minute))
m5_alpha = 1 - math.exp(-5.0 / (seconds_per_minute * five_minutes))
m15_alpha = 1 - math.exp(-5.0 / (seconds_per_minute * fifteen_minutes))
class EWMA:
def __init__(self, alpha):
self.alpha = alpha
self.uncounted = 0
self.initialized = False
self.rate = 0.0
def update(self, n):
self.uncounted += n
def tick(self):
count = self.uncounted
self.uncounted = 0
instant_rate = (count*1.0) / interval
if self.initialized:
self.rate += (self.alpha * (instant_rate - self.rate))
else:
self.rate = instant_rate
self.initialized = True
def get_rate(self, unit):
return self.rate * unit
class Meter:
def __init__(self):
self.m1 = EWMA(m1_alpha)
self.m5 = EWMA(m5_alpha)
self.m15 = EWMA(m15_alpha)
self.count = 0
self.start = now()
self.last_tick = self.start
def tick_if_necessary(self):
current = now()
age = current - self.last_tick
if age >= interval:
self.last_tick = current - (age % interval)
for i in range(0, age / interval):
self.m1.tick()
self.m5.tick()
self.m15.tick()
def mark(self, n=1):
self.tick_if_necessary()
self.count += n
self.m1.update(n)
self.m5.update(n)
self.m15.update(n)
def get_mean_rate(self, unit=second):
if self.count == 0:
return 0.0
else:
return ((self.count * 1.0) / (now() - self.start)) * unit
def get_m1_rate(self):
self.tick_if_necessary()
return self.m1.get_rate(second)
def get_m5_rate(self):
self.tick_if_necessary()
return self.m5.get_rate(second)
def get_m15_rate(self):
self.tick_if_necessary()
return self.m15.get_rate(second)
def now():
return int(time.time() * second)
ppid = 1
def getswitch(pid):
result = {}
total = 0
try:
with open("/proc/%s/status" % pid) as f:
for line in f:
if line.startswith('voluntary_ctxt_switches'):
switch = int(line.split()[-1])
total += switch
result['voluntrary'] = switch
elif line.startswith('nonvoluntary_ctxt_switches'):
switch = int(line.split()[-1])
total += switch
result['nonvoluntrary'] = switch
except IOError:
pass
result['total'] = total
return result
def getswitches():
d = {}
for pid in os.listdir('/proc/%s/task' % ppid):
d[int(pid)] = getswitch(pid)
return d
def get_threads(ppid):
d = {}
s = subprocess.check_output('jstack %s' % ppid, shell=True)
for line in s.splitlines():
start = line.find('nid=0x')
if start == -1:
continue
start += 6
pool_name = line[1:line.find('"', 1)]
tid = line[start: line.find(' ', start)]
d[int(tid, 16)] = pool_name
return d
def dosnoop(kind):
threads = get_threads(ppid)
meters = {}
last_switches = getswitches()
last_reported = time.time()
while True:
current_switches = getswitches()
for pid, stat in current_switches.items():
if pid not in last_switches:
continue
count = stat[kind] - last_switches[pid][kind]
pool = threads.get(pid, 'unknown')[:10]
meter = meters.get(pool)
if meter is None:
meter = Meter()
meters[pool] = meter
meter.mark(count)
last_switches = current_switches
now = time.time()
if now - last_reported > 1:
last_reported = now
rates = [(name, m.get_mean_rate()) for name, m in meters.iteritems()]
rates.sort(key=lambda t: t[1], reverse=True)
print '#' * 100
print 'total: %s' % sum(t[1] for t in rates)
accumulate = 0
for name, rate in rates[:10]:
accumulate += rate
print('%s\t%s\t%s' % (name, rate, accumulate))
time.sleep(0.2)
if __name__ == '__main__':
kind = 'total'
for arg in sys.argv:
if arg.startswith('non'):
kind = 'nonvoluntrary'
elif arg.startswith('vol'):
kind = 'voluntrary'
elif str.isdigit(arg):
ppid = arg
dosnoop(kind)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment