#!/usr/bin/env python # Copyright (c) 2016, Arvid Norberg # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the distribution. # * Neither the name of the author nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # this script can parse and generate reports from the alert log from a # libtorrent session import os, sys, time, os, math from multiprocessing.pool import ThreadPool thread_pool = ThreadPool(8) output_dir = 'session_stats_report' stat = open(sys.argv[1]) line = stat.readline() while not 'session stats header:' in line: line = stat.readline() keys = line.split('session stats header:')[1].strip().split(', ') try: os.mkdir(output_dir) except: pass data_out = open(os.path.join(output_dir, 'counters.dat'), 'w+') idx = 0 for l in stat: if not 'session stats (' in l: continue data_out.write(("%d\t" % idx) + l.split(' values): ')[1].strip().replace(', ', '\t') + '\n') idx += 1 data_out.close() line_graph = 0 histogram = 1 stacked = 2 diff = 3 graph_colors = [] pattern = [[0,0,1], [0,1,0], [1,0,0], [1,0,1], [0,1,1], [1,1,0]] def process_color(c, op): for i in range(3): if op == 0: c[i] = min(255, c[i] + 0xb0) if op == 2: c[i] = max(0, c[i] - 0x50) return c for i in range(0,len(pattern) * 3): op = i / len(pattern) c = list(pattern[i % len(pattern)]) for i in range(3): c[i] *= 0xff c = process_color(c, op) c = '#%02x%02x%02x' % (c[0], c[1], c[2]) graph_colors.append(c) line_colors = list(graph_colors) line_colors.reverse() gradient16_colors = [] for i in range(0, 16): f = i / 16. pi = 3.1415927 r = max(int(255 * (math.sin(f*pi)+0.2)), 0) g = max(int(255 * (math.sin((f-0.5)*pi)+0.2)), 0) b = max(int(255 * (math.sin((f+0.5)*pi)+0.2)), 0) c = '#%02x%02x%02x' % (min(r, 255), min(g, 255), min(b, 255)) gradient16_colors.append(c) gradient18_colors = [] for i in range(0, 18): f = i / 18. pi = 3.1415927 r = max(int(255 * (math.sin(f*pi)+0.2)), 0) g = max(int(255 * (math.sin((f-0.5)*pi)+0.2)), 0) b = max(int(255 * (math.sin((f+0.5)*pi)+0.2)), 0) c = '#%02x%02x%02x' % (min(r, 255), min(g, 255), min(b, 255)) gradient18_colors.append(c) gradient6_colors = [] for i in range(0, 6): f = i / 6. c = '#%02x%02x%02x' % (min(int(255 * (-2 * f + 2)), 255), min(int(255 * (2 * f)), 255), 100) gradient6_colors.append(c) def plot_fun(script): ret = os.system('gnuplot "%s" 2>/dev/null' % script) if ret != 0 and ret != 256: print 'system: %d\n' % ret raise Exception("abort") sys.stdout.write('.') sys.stdout.flush() def to_title(key): return key.replace('_', ' ').replace('.', ' - ') def gen_report(name, unit, lines, short_unit, generation, log_file, options): filename = os.path.join(output_dir, '%s_%04d.png' % (name, generation)) thumb = os.path.join(output_dir, '%s_%04d_thumb.png' % (name, generation)) # don't re-render a graph unless the logfile has changed try: dst1 = os.stat(filename) dst2 = os.stat(thumb) src = os.stat(log_file) if dst1.st_mtime > src.st_mtime and dst2.st_mtime > src.st_mtime: sys.stdout.write('.') return None except: pass script = os.path.join(output_dir, '%s_%04d.gnuplot' % (name, generation)) out = open(script, 'wb') print >>out, "set term png size 1200,700" print >>out, 'set output "%s"' % filename if not 'allow-negative' in options: print >>out, 'set yrange [0:*]' print >>out, "set tics nomirror" print >>out, "set key box" print >>out, "set key left top" colors = graph_colors if options['type'] == line_graph: colors = line_colors try: if options['colors'] == 'gradient16': colors = gradient16_colors elif options['colors'] == 'gradient6': colors = gradient6_colors if options['colors'] == 'gradient18': colors = gradient18_colors except: pass if options['type'] == histogram: binwidth = options['binwidth'] numbins = int(options['numbins']) print >>out, 'binwidth=%f' % binwidth print >>out, 'set boxwidth binwidth' print >>out, 'bin(x,width)=width*floor(x/width) + binwidth/2' print >>out, 'set xrange [0:%f]' % (binwidth * numbins) print >>out, 'set xlabel "%s"' % unit print >>out, 'set ylabel "number"' k = lines[0] try: column = keys.index(k) + 2 except: print '"%s" not found' % k return print >>out, 'plot "%s" using (bin($%d,binwidth)):(1.0) smooth freq with boxes' % (log_file, column) print >>out, '' print >>out, '' print >>out, '' elif options['type'] == stacked: print >>out, 'set xrange [0:*]' print >>out, 'set ylabel "%s"' % unit print >>out, 'set xlabel "time (s)"' print >>out, 'set format y "%%.1s%%c%s";' % short_unit print >>out, 'set style fill solid 1.0 noborder' print >>out, 'plot', column = 2 first = True graph = '' plot_expression = '' color = 0 for k in lines: try: column = keys.index(k) + 2 except: print '"%s" not found' % k continue; if not first: plot_expression = ', ' + plot_expression graph += '+' axis = 'x1y1' graph += '$%d' % column plot_expression = ' "%s" using 1:(%s) title "%s" axes %s with filledcurves x1 lc rgb "%s"' % (log_file, graph, to_title(k), axis, colors[color % len(colors)]) + plot_expression first = False color += 1 print >>out, plot_expression elif options['type'] == diff: print >>out, 'set xrange [0:*]' print >>out, 'set ylabel "%s"' % unit print >>out, 'set xlabel "time (s)"' print >>out, 'set format y "%%.1s%%c%s";' % short_unit column = 2 first = True graph = '' title = '' for k in lines: try: column = keys.index(k) + 2 except: print '"%s" not found' % k continue; if not first: graph += '-' title += ' - ' graph += '$%d' % column title += to_title(k) first = False print >>out, 'plot "%s" using 1:(%s) title "%s" with step' % (log_file, graph, title) else: print >>out, 'set xrange [0:*]' print >>out, 'set ylabel "%s"' % unit print >>out, 'set xlabel "time (s)"' print >>out, 'set format y "%%.1s%%c%s";' % short_unit print >>out, 'plot', column = 2 first = True color = 0 for k in lines: try: column = keys.index(k) + 2 except: print '"%s" not found' % k continue; if not first: print >>out, ', ', axis = 'x1y1' print >>out, ' "%s" using 1:%d title "%s" axes %s with steps lc rgb "%s"' % (log_file, column, to_title(k), axis, colors[color % len(colors)]), first = False color += 1 print >>out, '' print >>out, 'set term png size 150,100' print >>out, 'set output "%s"' % thumb print >>out, 'set key off' print >>out, 'unset tics' print >>out, 'set format x ""' print >>out, 'set format y ""' print >>out, 'set xlabel ""' print >>out, 'set ylabel ""' print >>out, 'set y2label ""' print >>out, 'set rmargin 0' print >>out, 'set lmargin 0' print >>out, 'set tmargin 0' print >>out, 'set bmargin 0' print >>out, "replot" out.close() return script def gen_html(reports, generations): file = open(os.path.join(output_dir, 'index.html'), 'w+') css = '''img { margin: 0} #head { display: block } #graphs { white-space:nowrap; } h1 { line-height: 1; display: inline } h2 { line-height: 1; display: inline; font-size: 1em; font-weight: normal};''' print >>file, '
' % css for i in reports: print >>file, '