#!/usr/bin/python """ Parses nmap -d4 debug log, looking for timing information and creates a gnuplot script. Usage: nmap -d4 somehost | ./parse-timing.py /dev/stdin | gnuplot > file.png """ from __future__ import print_function import sys import re WATCHED = ['cwnd', 'probes_active', 'ssthresh'] FIRST_LINE_RE = '^\\*\\*TIMING STATS\\*\\* \\((.*?)s\\).*$' NOTFIRST_LINE_RE = '^ ([^ ]+)( \\(([^/]+)/([^ ]+) incomplete\\))?:' + \ ' ' + '/'.join(['([-0-9*.]+)'] * 6) + \ ' ' + '/'.join(['([-0-9*.]+)'] * 3) + \ ' ' + '/'.join(['([-0-9*.]+)'] * 3) def parse_timing(timing): """ Parses Nmap's "TIMING STATS" lines and returns a dict, where keys are hostnames and values are values are dicts which pair labels with floating-point values or None. """ to_float = lambda f: float(f) if f != '*' else None lines = timing.split('\n') current_time = float(re.match(FIRST_LINE_RE, lines[0]).groups()[0]) d = {} for line in lines[1:]: if line.strip() == '': continue labels = ['group', 'ignored', 'num_complete', 'num_incomplete', 'probes_active', 'freshportsleft', 'retry_stack', 'outstanding', 'retranwait', 'onbench', 'cwnd', 'ssthresh', 'delay', 'timeout', 'srtt', 'rttvar'] groups = re.match(NOTFIRST_LINE_RE, line).groups() d[groups[0]] = {'current_time': current_time} for i in range(len(groups)): if i > 3: d[groups[0]][labels[i]] = to_float(groups[i]) else: d[groups[0]][labels[i]] = groups[i] return d def end_of_timing(data, hosts, timing_str): """ Used to signal that timing_str ended. Adds data from timing_str to the plot. """ d = parse_timing(timing_str) for k in d.keys(): hosts.add(k) for k, v in d.items(): for column in WATCHED: data_tuple = (v['current_time'], v[column]) filename = "%s-%s.txt" % (k, column) if filename not in data: data[filename] = open(filename, "w") data[filename].write("%s %s\n" % data_tuple) def read_data(input_file): """ Reads data from a given file, extracts timing-related lines and parses them. Returns the results in "data" dict (key being the column name, values are a lists of values), along with a set of hosts found in the timing information. """ hosts = set([]) data = {} now_timing = False timing_str = '' for line in input_file: if line.startswith('**TIMING STATS** '): if now_timing: end_of_timing(data, hosts, timing_str) timing_str = '' now_timing = True timing_str += line elif line.startswith(' ') and now_timing: timing_str += line else: if now_timing: now_timing = False end_of_timing(data, hosts, timing_str) timing_str = '' return data, hosts def print_graph(data, hosts): """ Generates a gnuplot script based on the data from function parameters for given hosts. """ print("set term png") print('set xlabel "Time (s)"') print('set ylabel "Timing variable"') keys = ['%s-%s' % (h, w) for w in WATCHED for h in hosts] for i in range(len(keys)): s = "" if i == 0: s += "plot " s += '"%s.txt" using 1:2 title "%s" with lines ' % (keys[i], keys[i]) if i != len(keys) - 1: s += ', \\' print(s) if __name__ == "__main__": with open(sys.argv[1]) as f: data, hosts = read_data(f) print("Done reading the file, printing the graph...", file=sys.stderr) print_graph(data, hosts) print("Done printing the graph.", file=sys.stderr)