xref: /aosp_15_r20/external/iperf3/contrib/iperf3_to_gnuplot.py (revision 7ab6e6ace082586527a400463bc693a412a40341)
1*7ab6e6acSAndroid Build Coastguard Worker#!/usr/bin/env python
2*7ab6e6acSAndroid Build Coastguard Worker
3*7ab6e6acSAndroid Build Coastguard Worker"""
4*7ab6e6acSAndroid Build Coastguard WorkerExtract iperf data from json blob and format for gnuplot.
5*7ab6e6acSAndroid Build Coastguard Worker"""
6*7ab6e6acSAndroid Build Coastguard Worker
7*7ab6e6acSAndroid Build Coastguard Workerimport json
8*7ab6e6acSAndroid Build Coastguard Workerimport os
9*7ab6e6acSAndroid Build Coastguard Workerimport sys
10*7ab6e6acSAndroid Build Coastguard Worker
11*7ab6e6acSAndroid Build Coastguard Workerfrom optparse import OptionParser
12*7ab6e6acSAndroid Build Coastguard Worker
13*7ab6e6acSAndroid Build Coastguard Workerimport pprint
14*7ab6e6acSAndroid Build Coastguard Worker# for debugging, so output to stderr to keep verbose
15*7ab6e6acSAndroid Build Coastguard Worker# output out of any redirected stdout.
16*7ab6e6acSAndroid Build Coastguard Workerpp = pprint.PrettyPrinter(indent=4, stream=sys.stderr)
17*7ab6e6acSAndroid Build Coastguard Worker
18*7ab6e6acSAndroid Build Coastguard Worker
19*7ab6e6acSAndroid Build Coastguard Workerdef generate_output(iperf, options):
20*7ab6e6acSAndroid Build Coastguard Worker    """Do the actual formatting."""
21*7ab6e6acSAndroid Build Coastguard Worker    for i in iperf.get('intervals'):
22*7ab6e6acSAndroid Build Coastguard Worker        for ii in i.get('streams'):
23*7ab6e6acSAndroid Build Coastguard Worker            if options.verbose:
24*7ab6e6acSAndroid Build Coastguard Worker                pp.pprint(ii)
25*7ab6e6acSAndroid Build Coastguard Worker            row = '{0} {1} {2} {3} {4}\n'.format(
26*7ab6e6acSAndroid Build Coastguard Worker                round(float(ii.get('start')), 4),
27*7ab6e6acSAndroid Build Coastguard Worker                ii.get('bytes'),
28*7ab6e6acSAndroid Build Coastguard Worker                # to Gbits/sec
29*7ab6e6acSAndroid Build Coastguard Worker                round(float(ii.get('bits_per_second')) / (1000*1000*1000), 3),
30*7ab6e6acSAndroid Build Coastguard Worker                ii.get('retransmits'),
31*7ab6e6acSAndroid Build Coastguard Worker                round(float(ii.get('snd_cwnd')) / (1000*1000), 2)
32*7ab6e6acSAndroid Build Coastguard Worker            )
33*7ab6e6acSAndroid Build Coastguard Worker            yield row
34*7ab6e6acSAndroid Build Coastguard Worker
35*7ab6e6acSAndroid Build Coastguard Worker
36*7ab6e6acSAndroid Build Coastguard Workerdef summed_output(iperf, options):
37*7ab6e6acSAndroid Build Coastguard Worker    """Format summed output."""
38*7ab6e6acSAndroid Build Coastguard Worker
39*7ab6e6acSAndroid Build Coastguard Worker    for i in iperf.get('intervals'):
40*7ab6e6acSAndroid Build Coastguard Worker
41*7ab6e6acSAndroid Build Coastguard Worker        row_header = None
42*7ab6e6acSAndroid Build Coastguard Worker
43*7ab6e6acSAndroid Build Coastguard Worker        byte = list()
44*7ab6e6acSAndroid Build Coastguard Worker        bits_per_second = list()
45*7ab6e6acSAndroid Build Coastguard Worker        retransmits = list()
46*7ab6e6acSAndroid Build Coastguard Worker        snd_cwnd = list()
47*7ab6e6acSAndroid Build Coastguard Worker
48*7ab6e6acSAndroid Build Coastguard Worker        for ii in i.get('streams'):
49*7ab6e6acSAndroid Build Coastguard Worker            if options.verbose:
50*7ab6e6acSAndroid Build Coastguard Worker                pp.pprint(i)
51*7ab6e6acSAndroid Build Coastguard Worker            # grab the first start value
52*7ab6e6acSAndroid Build Coastguard Worker            if row_header is None:
53*7ab6e6acSAndroid Build Coastguard Worker                row_header = round(float(ii.get('start')), 2)
54*7ab6e6acSAndroid Build Coastguard Worker            # aggregate the rest of the values
55*7ab6e6acSAndroid Build Coastguard Worker            byte.append(ii.get('bytes'))
56*7ab6e6acSAndroid Build Coastguard Worker            bits_per_second.append(float(ii.get('bits_per_second')) / (1000*1000*1000))
57*7ab6e6acSAndroid Build Coastguard Worker            retransmits.append(ii.get('retransmits'))
58*7ab6e6acSAndroid Build Coastguard Worker            snd_cwnd.append(float(ii.get('snd_cwnd')) / (1000*1000))
59*7ab6e6acSAndroid Build Coastguard Worker
60*7ab6e6acSAndroid Build Coastguard Worker        row = '{h} {b} {bps} {r} {s}\n'.format(
61*7ab6e6acSAndroid Build Coastguard Worker            h=row_header,
62*7ab6e6acSAndroid Build Coastguard Worker            b=sum(byte),
63*7ab6e6acSAndroid Build Coastguard Worker            bps=round(sum(bits_per_second), 3),
64*7ab6e6acSAndroid Build Coastguard Worker            r=sum(retransmits),
65*7ab6e6acSAndroid Build Coastguard Worker            s=round(sum(snd_cwnd) / len(snd_cwnd), 2)
66*7ab6e6acSAndroid Build Coastguard Worker        )
67*7ab6e6acSAndroid Build Coastguard Worker
68*7ab6e6acSAndroid Build Coastguard Worker        yield row
69*7ab6e6acSAndroid Build Coastguard Worker
70*7ab6e6acSAndroid Build Coastguard Worker
71*7ab6e6acSAndroid Build Coastguard Workerdef main():
72*7ab6e6acSAndroid Build Coastguard Worker    """Execute the read and formatting."""
73*7ab6e6acSAndroid Build Coastguard Worker    usage = '%prog [ -f FILE | -o OUT | -v ]'
74*7ab6e6acSAndroid Build Coastguard Worker    parser = OptionParser(usage=usage)
75*7ab6e6acSAndroid Build Coastguard Worker    parser.add_option('-f', '--file', metavar='FILE',
76*7ab6e6acSAndroid Build Coastguard Worker                      type='string', dest='filename',
77*7ab6e6acSAndroid Build Coastguard Worker                      help='Input filename.')
78*7ab6e6acSAndroid Build Coastguard Worker    parser.add_option('-o', '--output', metavar='OUT',
79*7ab6e6acSAndroid Build Coastguard Worker                      type='string', dest='output',
80*7ab6e6acSAndroid Build Coastguard Worker                      help='Optional file to append output to.')
81*7ab6e6acSAndroid Build Coastguard Worker    parser.add_option('-s', '--sum',
82*7ab6e6acSAndroid Build Coastguard Worker                      dest='summed', action='store_true', default=False,
83*7ab6e6acSAndroid Build Coastguard Worker                      help='Summed version of the output.')
84*7ab6e6acSAndroid Build Coastguard Worker    parser.add_option('-v', '--verbose',
85*7ab6e6acSAndroid Build Coastguard Worker                      dest='verbose', action='store_true', default=False,
86*7ab6e6acSAndroid Build Coastguard Worker                      help='Verbose debug output to stderr.')
87*7ab6e6acSAndroid Build Coastguard Worker    options, _ = parser.parse_args()
88*7ab6e6acSAndroid Build Coastguard Worker
89*7ab6e6acSAndroid Build Coastguard Worker    if not options.filename:
90*7ab6e6acSAndroid Build Coastguard Worker        parser.error('Filename is required.')
91*7ab6e6acSAndroid Build Coastguard Worker
92*7ab6e6acSAndroid Build Coastguard Worker    file_path = os.path.normpath(options.filename)
93*7ab6e6acSAndroid Build Coastguard Worker
94*7ab6e6acSAndroid Build Coastguard Worker    if not os.path.exists(file_path):
95*7ab6e6acSAndroid Build Coastguard Worker        parser.error('{f} does not exist'.format(f=file_path))
96*7ab6e6acSAndroid Build Coastguard Worker
97*7ab6e6acSAndroid Build Coastguard Worker    with open(file_path, 'r') as fh:
98*7ab6e6acSAndroid Build Coastguard Worker        data = fh.read()
99*7ab6e6acSAndroid Build Coastguard Worker
100*7ab6e6acSAndroid Build Coastguard Worker    try:
101*7ab6e6acSAndroid Build Coastguard Worker        iperf = json.loads(data)
102*7ab6e6acSAndroid Build Coastguard Worker    except Exception as ex:  # pylint: disable=broad-except
103*7ab6e6acSAndroid Build Coastguard Worker        parser.error('Could not parse JSON from file (ex): {0}'.format(str(ex)))
104*7ab6e6acSAndroid Build Coastguard Worker
105*7ab6e6acSAndroid Build Coastguard Worker    if options.output:
106*7ab6e6acSAndroid Build Coastguard Worker        absp = os.path.abspath(options.output)
107*7ab6e6acSAndroid Build Coastguard Worker        output_dir, _ = os.path.split(absp)
108*7ab6e6acSAndroid Build Coastguard Worker        if not os.path.exists(output_dir):
109*7ab6e6acSAndroid Build Coastguard Worker            parser.error('Output file directory path {0} does not exist'.format(output_dir))
110*7ab6e6acSAndroid Build Coastguard Worker        fh = open(absp, 'a')
111*7ab6e6acSAndroid Build Coastguard Worker    else:
112*7ab6e6acSAndroid Build Coastguard Worker        fh = sys.stdout
113*7ab6e6acSAndroid Build Coastguard Worker
114*7ab6e6acSAndroid Build Coastguard Worker    if options.summed:
115*7ab6e6acSAndroid Build Coastguard Worker        fmt = summed_output
116*7ab6e6acSAndroid Build Coastguard Worker    else:
117*7ab6e6acSAndroid Build Coastguard Worker        fmt = generate_output
118*7ab6e6acSAndroid Build Coastguard Worker
119*7ab6e6acSAndroid Build Coastguard Worker    for i in fmt(iperf, options):
120*7ab6e6acSAndroid Build Coastguard Worker        fh.write(i)
121*7ab6e6acSAndroid Build Coastguard Worker
122*7ab6e6acSAndroid Build Coastguard Worker
123*7ab6e6acSAndroid Build Coastguard Workerif __name__ == '__main__':
124*7ab6e6acSAndroid Build Coastguard Worker    main()
125