Created
May 25, 2020 11:45
-
-
Save jizhilong/bb07ef4eb621ef140aec39bd05776e47 to your computer and use it in GitHub Desktop.
count and sort jvm threadpool's context switch rate
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
Author: Ji.Zhilong <[email protected]> | |
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