agent-enviroments/builder/libs/seastar/scripts/io-trace-parse.py
2024-09-10 17:06:08 +03:00

174 lines
4.6 KiB
Python
Executable File

#!/bin/env python3
#
# Script to parse IO trace logs and show some stats
#
import sys
import statistics
# prints average, .99 quantile and maximum value for an array
def print_stat_line(what, st):
def q99(arr):
return statistics.quantiles(arr, n=100)[-1]
print("\t{:18}: avg:{:12.6f} .99:{:12.6f} max:{:12.6f}".format(what,
statistics.fmean(st), q99(st), max(st)))
# Inc/Dec counter that also collects its value history
class counter:
def __init__(self):
self._v = 0
self._stat = []
def inc(self):
self._v += 1
self._stat.append(self._v)
def dec(self):
self._v -= 1
self._stat.append(self._v)
def stat(self):
return self._stat
class req:
def __init__(self, rqlen):
self.len = rqlen
self.queue = None
self.submit = None
self.complete = None
# Timings for requests
class req_stat:
def __init__(self):
self.qtimes = [] # time in queue
self.xtimes = [] # time in disk
self.latencies = [] # sum of the above
self.delays = [] # time between submits
self.prev = None # helper for the above
self.in_queue = counter()
self.in_disk = counter()
def queue(self, rq):
self.in_queue.inc()
def submit(self, rq):
if self.prev:
self.delays.append(rq.submit - self.prev)
self.prev = rq.submit
self.qtimes.append(rq.submit - rq.queue)
self.in_queue.dec()
self.in_disk.inc()
def complete(self, rq):
self.xtimes.append(rq.complete - rq.submit)
self.latencies.append(rq.complete - rq.queue)
self.in_disk.dec()
def show(self, rqlen):
print("{}k requests".format(int(rqlen/1024)))
print("\ttotal: {}".format(len(self.latencies)))
print_stat_line('in queue usec', self.qtimes)
print_stat_line(' `- num ', self.in_queue.stat())
print_stat_line('in disk usec', self.xtimes)
print_stat_line(' `- num ', self.in_disk.stat())
print_stat_line('latency', self.latencies)
print_stat_line('period', self.delays)
# Stats for a device. Umbrella-object for the above stats
class device_stat:
def __init__(self):
self.reqs = {} # collection of req's
self.req_stats = {} # statistics by request size
self.in_queue = counter()
self.in_disk = counter()
def queue(self, rqid, ts, rqlen):
rq = req(rqlen)
self.reqs[rqid] = rq
rq.queue = ts
if rq.len not in self.req_stats:
self.req_stats[rq.len] = req_stat()
st = self.req_stats[rq.len]
st.queue(rq)
self.in_queue.inc()
def submit(self, rqid, ts):
rq = self.reqs[rqid]
rq.submit = ts
st = self.req_stats[rq.len]
st.submit(rq)
self.in_queue.dec()
self.in_disk.inc()
def complete(self, rqid, ts):
rq = self.reqs[rqid]
rq.complete = ts
st = self.req_stats[rq.len]
st.complete(rq)
del self.reqs[rqid]
self.in_disk.dec()
def _show_req_stats(self):
for rlen in self.req_stats:
st = self.req_stats[rlen]
st.show(rlen)
def _show_queue_stats(self):
print("queue")
print_stat_line('in queue num:', self.in_queue.stat())
print_stat_line('in disk num:', self.in_disk.stat())
def show(self, devid):
print("{}".format(devid).center(80, "-"))
self._show_req_stats()
self._show_queue_stats()
class parser:
def __init__(self, f):
self._file = f
self._dev_stats = {}
def _get_dev_stats(self, devid):
if devid not in self._dev_stats:
self._dev_stats[devid] = device_stat()
return self._dev_stats[devid]
def _parse_req_event(self, ln):
req_id = ln[10]
ts = float(ln[1])
st = self._get_dev_stats(int(ln[7]))
if ln[11] == 'queue':
st.queue(req_id, ts, int(ln[13]))
elif ln[11] == 'submit':
st.submit(req_id, ts)
elif ln[11] == 'complete':
st.complete(req_id, ts)
def _parse_line(self, ln):
if ln[4] == 'io':
if ln[9] == 'req':
self._parse_req_event(ln)
def parse(self):
for ln in self._file:
if ln.startswith('TRACE'):
self._parse_line(ln.strip().split())
return self._dev_stats
if __name__ == "__main__":
p = parser(sys.stdin)
stats = p.parse()
for devid in stats:
stats[devid].show(devid)