Commit | Line | Data |
---|---|---|
34831ee3 MN |
1 | #!/bin/python |
2 | # | |
3 | # fiologparser.py | |
4 | # | |
5 | # This tool lets you parse multiple fio log files and look at interaval | |
6 | # statistics even when samples are non-uniform. For instance: | |
7 | # | |
8 | # fiologparser.py -s *bw* | |
9 | # | |
10 | # to see per-interval sums for all bandwidth logs or: | |
11 | # | |
12 | # fiologparser.py -a *clat* | |
13 | # | |
14 | # to see per-interval average completion latency. | |
15 | ||
16 | import argparse | |
17 | ||
18 | def parse_args(): | |
19 | parser = argparse.ArgumentParser() | |
20 | parser.add_argument('-i', '--interval', required=False, type=int, default=1000, help='interval of time in seconds.') | |
21 | parser.add_argument('-d', '--divisor', required=False, type=int, default=1, help='divide the results by this value.') | |
22 | parser.add_argument('-f', '--full', dest='full', action='store_true', default=False, help='print full output.') | |
23 | parser.add_argument('-a', '--average', dest='average', action='store_true', default=False, help='print the average for each interval.') | |
24 | parser.add_argument('-s', '--sum', dest='sum', action='store_true', default=False, help='print the sum for each interval.') | |
25 | parser.add_argument("FILE", help="collectl log output files to parse", nargs="+") | |
26 | args = parser.parse_args() | |
27 | ||
28 | return args | |
29 | ||
30 | def get_ftime(series): | |
31 | ftime = 0 | |
32 | for ts in series: | |
33 | if ftime == 0 or ts.last.end < ftime: | |
34 | ftime = ts.last.end | |
35 | return ftime | |
36 | ||
37 | def print_full(ctx, series): | |
38 | ftime = get_ftime(series) | |
39 | start = 0 | |
40 | end = ctx.interval | |
41 | ||
42 | while (start < ftime): | |
43 | end = ftime if ftime < end else end | |
44 | results = [ts.get_value(start, end) for ts in series] | |
45 | print "%s, %s" % (end, ', '.join(["%0.3f" % i for i in results])) | |
46 | start += ctx.interval | |
47 | end += ctx.interval | |
48 | ||
49 | def print_sums(ctx, series): | |
50 | ftime = get_ftime(series) | |
51 | start = 0 | |
52 | end = ctx.interval | |
53 | ||
54 | while (start < ftime): | |
55 | end = ftime if ftime < end else end | |
56 | results = [ts.get_value(start, end) for ts in series] | |
57 | print "%s, %0.3f" % (end, sum(results)) | |
58 | start += ctx.interval | |
59 | end += ctx.interval | |
60 | ||
61 | def print_averages(ctx, series): | |
62 | ftime = get_ftime(series) | |
63 | start = 0 | |
64 | end = ctx.interval | |
65 | ||
66 | while (start < ftime): | |
67 | end = ftime if ftime < end else end | |
68 | results = [ts.get_value(start, end) for ts in series] | |
69 | print "%s, %0.3f" % (end, float(sum(results))/len(results)) | |
70 | start += ctx.interval | |
71 | end += ctx.interval | |
72 | ||
73 | ||
74 | def print_default(ctx, series): | |
75 | ftime = get_ftime(series) | |
76 | start = 0 | |
77 | end = ctx.interval | |
78 | averages = [] | |
79 | weights = [] | |
80 | ||
81 | while (start < ftime): | |
82 | end = ftime if ftime < end else end | |
83 | results = [ts.get_value(start, end) for ts in series] | |
84 | averages.append(sum(results)) | |
85 | weights.append(end-start) | |
86 | start += ctx.interval | |
87 | end += ctx.interval | |
88 | ||
89 | total = 0 | |
90 | for i in xrange(0, len(averages)): | |
91 | total += averages[i]*weights[i] | |
92 | print '%0.3f' % (total/sum(weights)) | |
93 | ||
94 | class TimeSeries(): | |
95 | def __init__(self, ctx, fn): | |
96 | self.ctx = ctx | |
97 | self.last = None | |
98 | self.samples = [] | |
99 | self.read_data(fn) | |
100 | ||
101 | def read_data(self, fn): | |
102 | f = open(fn, 'r') | |
103 | p_time = 0 | |
104 | for line in f: | |
105 | (time, value, foo, bar) = line.rstrip('\r\n').rsplit(', ') | |
106 | self.add_sample(p_time, int(time), int(value)) | |
107 | p_time = int(time) | |
108 | ||
109 | def add_sample(self, start, end, value): | |
110 | sample = Sample(ctx, start, end, value) | |
111 | if not self.last or self.last.end < end: | |
112 | self.last = sample | |
113 | self.samples.append(sample) | |
114 | ||
115 | def get_value(self, start, end): | |
116 | value = 0 | |
117 | for sample in self.samples: | |
118 | value += sample.get_contribution(start, end) | |
119 | return value | |
120 | ||
121 | class Sample(): | |
122 | def __init__(self, ctx, start, end, value): | |
123 | self.ctx = ctx | |
124 | self.start = start | |
125 | self.end = end | |
126 | self.value = value | |
127 | ||
128 | def get_contribution(self, start, end): | |
129 | # short circuit if not within the bound | |
130 | if (end < self.start or start > self.end): | |
131 | return 0 | |
132 | ||
133 | sbound = self.start if start < self.start else start | |
134 | ebound = self.end if end > self.end else end | |
135 | ratio = float(ebound-sbound) / (end-start) | |
136 | return self.value*ratio/ctx.divisor | |
137 | ||
138 | ||
139 | if __name__ == '__main__': | |
140 | ctx = parse_args() | |
141 | series = [] | |
142 | for fn in ctx.FILE: | |
143 | series.append(TimeSeries(ctx, fn)) | |
144 | if ctx.sum: | |
145 | print_sums(ctx, series) | |
146 | elif ctx.average: | |
147 | print_averages(ctx, series) | |
148 | elif ctx.full: | |
149 | print_full(ctx, series) | |
150 | else: | |
151 | print_default(ctx, series) | |
152 |