Blame tools/fiologparser.py

Packit Service 0d30d9
#! /usr/libexec/platform-python
Packit Service 0e769b
# Note: this script is python2 and python 3 compatible.
Packit Service 0e769b
#
Packit Service 0e769b
# fiologparser.py
Packit Service 0e769b
#
Packit Service 0e769b
# This tool lets you parse multiple fio log files and look at interaval
Packit Service 0e769b
# statistics even when samples are non-uniform.  For instance:
Packit Service 0e769b
#
Packit Service 0e769b
# fiologparser.py -s *bw*
Packit Service 0e769b
#
Packit Service 0e769b
# to see per-interval sums for all bandwidth logs or:
Packit Service 0e769b
#
Packit Service 0e769b
# fiologparser.py -a *clat*
Packit Service 0e769b
#
Packit Service 0e769b
# to see per-interval average completion latency.
Packit Service 0e769b
Packit Service 0e769b
from __future__ import absolute_import
Packit Service 0e769b
from __future__ import print_function
Packit Service 0e769b
import argparse
Packit Service 0e769b
import math
Packit Service 0e769b
Packit Service 0e769b
def parse_args():
Packit Service 0e769b
    parser = argparse.ArgumentParser()
Packit Service 0e769b
    parser.add_argument('-i', '--interval', required=False, type=int, default=1000, help='interval of time in seconds.')
Packit Service 0e769b
    parser.add_argument('-d', '--divisor', required=False, type=int, default=1, help='divide the results by this value.')
Packit Service 0e769b
    parser.add_argument('-f', '--full', dest='full', action='store_true', default=False, help='print full output.')
Packit Service 0e769b
    parser.add_argument('-A', '--all', dest='allstats', action='store_true', default=False, 
Packit Service 0e769b
                        help='print all stats for each interval.')
Packit Service 0e769b
    parser.add_argument('-a', '--average', dest='average', action='store_true', default=False, help='print the average for each interval.')
Packit Service 0e769b
    parser.add_argument('-s', '--sum', dest='sum', action='store_true', default=False, help='print the sum for each interval.')
Packit Service 0e769b
    parser.add_argument("FILE", help="collectl log output files to parse", nargs="+")
Packit Service 0e769b
    args = parser.parse_args()
Packit Service 0e769b
Packit Service 0e769b
    return args
Packit Service 0e769b
Packit Service 0e769b
def get_ftime(series):
Packit Service 0e769b
    ftime = 0
Packit Service 0e769b
    for ts in series:
Packit Service 0e769b
        if ftime == 0 or ts.last.end < ftime:
Packit Service 0e769b
            ftime = ts.last.end
Packit Service 0e769b
    return ftime
Packit Service 0e769b
Packit Service 0e769b
def print_full(ctx, series):
Packit Service 0e769b
    ftime = get_ftime(series)
Packit Service 0e769b
    start = 0 
Packit Service 0e769b
    end = ctx.interval
Packit Service 0e769b
Packit Service 0e769b
    while (start < ftime):
Packit Service 0e769b
        end = ftime if ftime < end else end
Packit Service 0e769b
        results = [ts.get_value(start, end) for ts in series]
Packit Service 0e769b
        print("%s, %s" % (end, ', '.join(["%0.3f" % i for i in results])))
Packit Service 0e769b
        start += ctx.interval
Packit Service 0e769b
        end += ctx.interval
Packit Service 0e769b
Packit Service 0e769b
def print_sums(ctx, series):
Packit Service 0e769b
    ftime = get_ftime(series)
Packit Service 0e769b
    start = 0
Packit Service 0e769b
    end = ctx.interval
Packit Service 0e769b
Packit Service 0e769b
    while (start < ftime):
Packit Service 0e769b
        end = ftime if ftime < end else end
Packit Service 0e769b
        results = [ts.get_value(start, end) for ts in series]
Packit Service 0e769b
        print("%s, %0.3f" % (end, sum(results)))
Packit Service 0e769b
        start += ctx.interval
Packit Service 0e769b
        end += ctx.interval
Packit Service 0e769b
Packit Service 0e769b
def print_averages(ctx, series):
Packit Service 0e769b
    ftime = get_ftime(series)
Packit Service 0e769b
    start = 0
Packit Service 0e769b
    end = ctx.interval
Packit Service 0e769b
Packit Service 0e769b
    while (start < ftime):
Packit Service 0e769b
        end = ftime if ftime < end else end
Packit Service 0e769b
        results = [ts.get_value(start, end) for ts in series]
Packit Service 0e769b
        print("%s, %0.3f" % (end, float(sum(results))/len(results)))
Packit Service 0e769b
        start += ctx.interval
Packit Service 0e769b
        end += ctx.interval
Packit Service 0e769b
Packit Service 0e769b
# FIXME: this routine is computationally inefficient
Packit Service 0e769b
# and has O(N^2) behavior
Packit Service 0e769b
# it would be better to make one pass through samples
Packit Service 0e769b
# to segment them into a series of time intervals, and
Packit Service 0e769b
# then compute stats on each time interval instead.
Packit Service 0e769b
# to debug this routine, use
Packit Service 0e769b
#   # sort -n -t ',' -k 2 small.log
Packit Service 0e769b
# on your input.
Packit Service 0e769b
Packit Service 0e769b
def my_extend( vlist, val ):
Packit Service 0e769b
    vlist.extend(val)
Packit Service 0e769b
    return vlist
Packit Service 0e769b
Packit Service 0e769b
array_collapser = lambda vlist, val:  my_extend(vlist, val) 
Packit Service 0e769b
Packit Service 0e769b
def print_all_stats(ctx, series):
Packit Service 0e769b
    ftime = get_ftime(series)
Packit Service 0e769b
    start = 0 
Packit Service 0e769b
    end = ctx.interval
Packit Service 0e769b
    print('start-time, samples, min, avg, median, 90%, 95%, 99%, max')
Packit Service 0e769b
    while (start < ftime):  # for each time interval
Packit Service 0e769b
        end = ftime if ftime < end else end
Packit Service 0e769b
        sample_arrays = [ s.get_samples(start, end) for s in series ]
Packit Service 0e769b
        samplevalue_arrays = []
Packit Service 0e769b
        for sample_array in sample_arrays:
Packit Service 0e769b
            samplevalue_arrays.append( 
Packit Service 0e769b
                [ sample.value for sample in sample_array ] )
Packit Service 0e769b
        # collapse list of lists of sample values into list of sample values
Packit Service 0e769b
        samplevalues = reduce( array_collapser, samplevalue_arrays, [] )
Packit Service 0e769b
        # compute all stats and print them
Packit Service 0e769b
        mymin = min(samplevalues)
Packit Service 0e769b
        myavg = sum(samplevalues) / float(len(samplevalues))
Packit Service 0e769b
        mymedian = median(samplevalues)
Packit Service 0e769b
        my90th = percentile(samplevalues, 0.90) 
Packit Service 0e769b
        my95th = percentile(samplevalues, 0.95)
Packit Service 0e769b
        my99th = percentile(samplevalues, 0.99)
Packit Service 0e769b
        mymax = max(samplevalues)
Packit Service 0e769b
        print( '%f, %d, %f, %f, %f, %f, %f, %f, %f' % (
Packit Service 0e769b
            start, len(samplevalues), 
Packit Service 0e769b
            mymin, myavg, mymedian, my90th, my95th, my99th, mymax))
Packit Service 0e769b
Packit Service 0e769b
        # advance to next interval
Packit Service 0e769b
        start += ctx.interval
Packit Service 0e769b
        end += ctx.interval
Packit Service 0e769b
Packit Service 0e769b
def median(values):
Packit Service 0e769b
    s=sorted(values)
Packit Service 0e769b
    return float(s[(len(s)-1)/2]+s[(len(s)/2)])/2
Packit Service 0e769b
Packit Service 0e769b
def percentile(values, p):
Packit Service 0e769b
    s = sorted(values)
Packit Service 0e769b
    k = (len(s)-1) * p
Packit Service 0e769b
    f = math.floor(k)
Packit Service 0e769b
    c = math.ceil(k)
Packit Service 0e769b
    if f == c:
Packit Service 0e769b
        return s[int(k)]
Packit Service 0e769b
    return (s[int(f)] * (c-k)) + (s[int(c)] * (k-f))
Packit Service 0e769b
Packit Service 0e769b
def print_default(ctx, series):
Packit Service 0e769b
    ftime = get_ftime(series)
Packit Service 0e769b
    start = 0
Packit Service 0e769b
    end = ctx.interval
Packit Service 0e769b
    averages = []
Packit Service 0e769b
    weights = []
Packit Service 0e769b
Packit Service 0e769b
    while (start < ftime):
Packit Service 0e769b
        end = ftime if ftime < end else end
Packit Service 0e769b
        results = [ts.get_value(start, end) for ts in series]
Packit Service 0e769b
        averages.append(sum(results)) 
Packit Service 0e769b
        weights.append(end-start)
Packit Service 0e769b
        start += ctx.interval
Packit Service 0e769b
        end += ctx.interval
Packit Service 0e769b
Packit Service 0e769b
    total = 0
Packit Service 0e769b
    for i in range(0, len(averages)):
Packit Service 0e769b
        total += averages[i]*weights[i]
Packit Service 0e769b
    print('%0.3f' % (total/sum(weights)))
Packit Service 0e769b
 
Packit Service 0e769b
class TimeSeries(object):
Packit Service 0e769b
    def __init__(self, ctx, fn):
Packit Service 0e769b
        self.ctx = ctx
Packit Service 0e769b
        self.last = None 
Packit Service 0e769b
        self.samples = []
Packit Service 0e769b
        self.read_data(fn)
Packit Service 0e769b
Packit Service 0e769b
    def read_data(self, fn):
Packit Service 0e769b
        f = open(fn, 'r')
Packit Service 0e769b
        p_time = 0
Packit Service 0e769b
        for line in f:
Packit Service 0e769b
            (time, value, foo, bar) = line.rstrip('\r\n').rsplit(', ')
Packit Service 0e769b
            self.add_sample(p_time, int(time), int(value))
Packit Service 0e769b
            p_time = int(time)
Packit Service 0e769b
 
Packit Service 0e769b
    def add_sample(self, start, end, value):
Packit Service 0e769b
        sample = Sample(ctx, start, end, value)
Packit Service 0e769b
        if not self.last or self.last.end < end:
Packit Service 0e769b
            self.last = sample
Packit Service 0e769b
        self.samples.append(sample)
Packit Service 0e769b
Packit Service 0e769b
    def get_samples(self, start, end):
Packit Service 0e769b
        sample_list = []
Packit Service 0e769b
        for s in self.samples:
Packit Service 0e769b
            if s.start >= start and s.end <= end:
Packit Service 0e769b
                sample_list.append(s)
Packit Service 0e769b
        return sample_list
Packit Service 0e769b
Packit Service 0e769b
    def get_value(self, start, end):
Packit Service 0e769b
        value = 0
Packit Service 0e769b
        for sample in self.samples:
Packit Service 0e769b
            value += sample.get_contribution(start, end)
Packit Service 0e769b
        return value
Packit Service 0e769b
Packit Service 0e769b
class Sample(object):
Packit Service 0e769b
    def __init__(self, ctx, start, end, value):
Packit Service 0e769b
       self.ctx = ctx
Packit Service 0e769b
       self.start = start
Packit Service 0e769b
       self.end = end
Packit Service 0e769b
       self.value = value
Packit Service 0e769b
Packit Service 0e769b
    def get_contribution(self, start, end):
Packit Service 0e769b
       # short circuit if not within the bound
Packit Service 0e769b
       if (end < self.start or start > self.end):
Packit Service 0e769b
           return 0 
Packit Service 0e769b
Packit Service 0e769b
       sbound = self.start if start < self.start else start
Packit Service 0e769b
       ebound = self.end if end > self.end else end
Packit Service 0e769b
       ratio = float(ebound-sbound) / (end-start) 
Packit Service 0e769b
       return self.value*ratio/ctx.divisor
Packit Service 0e769b
Packit Service 0e769b
Packit Service 0e769b
if __name__ == '__main__':
Packit Service 0e769b
    ctx = parse_args()
Packit Service 0e769b
    series = []
Packit Service 0e769b
    for fn in ctx.FILE:
Packit Service 0e769b
       series.append(TimeSeries(ctx, fn)) 
Packit Service 0e769b
    if ctx.sum:
Packit Service 0e769b
        print_sums(ctx, series)
Packit Service 0e769b
    elif ctx.average:
Packit Service 0e769b
        print_averages(ctx, series)
Packit Service 0e769b
    elif ctx.full:
Packit Service 0e769b
        print_full(ctx, series)
Packit Service 0e769b
    elif ctx.allstats:
Packit Service 0e769b
        print_all_stats(ctx, series)
Packit Service 0e769b
    else:
Packit Service 0e769b
        print_default(ctx, series)