summaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 18:21:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 18:21:43 +0000
commitc8c3bd06ef1a7248c8195d050d8a4075d051256e (patch)
tree419655deec1b0af0c5d3ec488693f1494fb20959 /contrib
parentInitial commit. (diff)
downloadiperf3-c8c3bd06ef1a7248c8195d050d8a4075d051256e.tar.xz
iperf3-c8c3bd06ef1a7248c8195d050d8a4075d051256e.zip
Adding upstream version 3.16.upstream/3.16
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib')
-rw-r--r--contrib/Dockerfile15
-rw-r--r--contrib/README.txt12
-rw-r--r--contrib/iperf3.gp36
-rw-r--r--contrib/iperf3.service10
-rwxr-xr-xcontrib/iperf3_to_gnuplot.py124
5 files changed, 197 insertions, 0 deletions
diff --git a/contrib/Dockerfile b/contrib/Dockerfile
new file mode 100644
index 0000000..79f6c75
--- /dev/null
+++ b/contrib/Dockerfile
@@ -0,0 +1,15 @@
+# Instructions:
+# - Configure for a static binary: ./configure --enable-static "LDFLAGS=--static" --disable-shared --without-openssl
+# - Build: make
+# - Build Docker image: docker build -t iperf3 -f contrib/Dockerfile .
+#
+# Example invocations:
+# - Help: docker run iperf3 --help
+# - Server: docker run -p 5201:5201 -it iperf3 -s
+# - Client: docker run -it iperf3 -c 192.168.1.1 (note: since this is a minimal image and does not include DNS, name resolution will not work)
+FROM scratch
+COPY src/iperf3 /iperf3
+COPY tmp /tmp
+ENTRYPOINT ["/iperf3"]
+EXPOSE 5201
+CMD ["-s"]
diff --git a/contrib/README.txt b/contrib/README.txt
new file mode 100644
index 0000000..5a27790
--- /dev/null
+++ b/contrib/README.txt
@@ -0,0 +1,12 @@
+
+This directory contains files that might be useful to analyze iperf3 results
+
+iperf3_to_gnuplot.py: converts iperf3 JSON output to format easy to plot in gnuplot
+
+iperf3.gp: sample gnuplot commands to plot throughput and retransmits
+
+
+Other iperf3 related projects that might be of interest:
+
+https://github.com/kaihendry/iperf3chart
+
diff --git a/contrib/iperf3.gp b/contrib/iperf3.gp
new file mode 100644
index 0000000..898422c
--- /dev/null
+++ b/contrib/iperf3.gp
@@ -0,0 +1,36 @@
+#
+# sample Gnuplot command file for iperf3 results
+set term x11
+#set term png
+#set term postscript landscape color
+set key width -12
+
+# iperf3 data fields
+#start bytes bits_per_second retransmits snd_cwnd
+
+set output "iperf3.png"
+#set output "iperf3.eps"
+
+#set nokey
+
+set grid xtics
+set grid ytics
+set grid linewidth 1
+set title "TCP performance: 40G to 10G host"
+set xlabel "time (seconds)"
+set ylabel "Bandwidth (Gbits/second)"
+set xrange [0:60]
+set yrange [0:15]
+set ytics nomirror
+set y2tics
+set y2range [0:2500]
+# dont plot when retransmits = 0
+set datafile missing '0'
+set pointsize 1.6
+
+plot "40Gto10G.old.dat" using 1:3 title '3.10 kernel' with linespoints lw 3 pt 5, \
+ "40Gto10G.new.dat" using 1:3 title '4.2 kernel' with linespoints lw 3 pt 7, \
+ "40Gto10G.old.dat" using 1:4 title 'retransmits' with points pt 7 axes x1y2
+
+#plot "iperf3.old.dat" using 1:3 title '3.10 kernel' with linespoints lw 3 pt 5, \
+# "iperf3.new.dat" using 1:3 title '4.2 kernel' with linespoints lw 3 pt 7
diff --git a/contrib/iperf3.service b/contrib/iperf3.service
new file mode 100644
index 0000000..e9cf47a
--- /dev/null
+++ b/contrib/iperf3.service
@@ -0,0 +1,10 @@
+[Unit]
+Description=iperf3
+Requires=network.target
+
+[Service]
+ExecStart=/usr/bin/iperf3 -s
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/contrib/iperf3_to_gnuplot.py b/contrib/iperf3_to_gnuplot.py
new file mode 100755
index 0000000..6a1f7a0
--- /dev/null
+++ b/contrib/iperf3_to_gnuplot.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+
+"""
+Extract iperf data from json blob and format for gnuplot.
+"""
+
+import json
+import os
+import sys
+
+from optparse import OptionParser
+
+import pprint
+# for debugging, so output to stderr to keep verbose
+# output out of any redirected stdout.
+pp = pprint.PrettyPrinter(indent=4, stream=sys.stderr)
+
+
+def generate_output(iperf, options):
+ """Do the actual formatting."""
+ for i in iperf.get('intervals'):
+ for ii in i.get('streams'):
+ if options.verbose:
+ pp.pprint(ii)
+ row = '{0} {1} {2} {3} {4}\n'.format(
+ round(float(ii.get('start')), 4),
+ ii.get('bytes'),
+ # to Gbits/sec
+ round(float(ii.get('bits_per_second')) / (1000*1000*1000), 3),
+ ii.get('retransmits'),
+ round(float(ii.get('snd_cwnd')) / (1000*1000), 2)
+ )
+ yield row
+
+
+def summed_output(iperf, options):
+ """Format summed output."""
+
+ for i in iperf.get('intervals'):
+
+ row_header = None
+
+ byte = list()
+ bits_per_second = list()
+ retransmits = list()
+ snd_cwnd = list()
+
+ for ii in i.get('streams'):
+ if options.verbose:
+ pp.pprint(i)
+ # grab the first start value
+ if row_header is None:
+ row_header = round(float(ii.get('start')), 2)
+ # aggregate the rest of the values
+ byte.append(ii.get('bytes'))
+ bits_per_second.append(float(ii.get('bits_per_second')) / (1000*1000*1000))
+ retransmits.append(ii.get('retransmits'))
+ snd_cwnd.append(float(ii.get('snd_cwnd')) / (1000*1000))
+
+ row = '{h} {b} {bps} {r} {s}\n'.format(
+ h=row_header,
+ b=sum(byte),
+ bps=round(sum(bits_per_second), 3),
+ r=sum(retransmits),
+ s=round(sum(snd_cwnd) / len(snd_cwnd), 2)
+ )
+
+ yield row
+
+
+def main():
+ """Execute the read and formatting."""
+ usage = '%prog [ -f FILE | -o OUT | -v ]'
+ parser = OptionParser(usage=usage)
+ parser.add_option('-f', '--file', metavar='FILE',
+ type='string', dest='filename',
+ help='Input filename.')
+ parser.add_option('-o', '--output', metavar='OUT',
+ type='string', dest='output',
+ help='Optional file to append output to.')
+ parser.add_option('-s', '--sum',
+ dest='summed', action='store_true', default=False,
+ help='Summed version of the output.')
+ parser.add_option('-v', '--verbose',
+ dest='verbose', action='store_true', default=False,
+ help='Verbose debug output to stderr.')
+ options, _ = parser.parse_args()
+
+ if not options.filename:
+ parser.error('Filename is required.')
+
+ file_path = os.path.normpath(options.filename)
+
+ if not os.path.exists(file_path):
+ parser.error('{f} does not exist'.format(f=file_path))
+
+ with open(file_path, 'r') as fh:
+ data = fh.read()
+
+ try:
+ iperf = json.loads(data)
+ except Exception as ex: # pylint: disable=broad-except
+ parser.error('Could not parse JSON from file (ex): {0}'.format(str(ex)))
+
+ if options.output:
+ absp = os.path.abspath(options.output)
+ output_dir, _ = os.path.split(absp)
+ if not os.path.exists(output_dir):
+ parser.error('Output file directory path {0} does not exist'.format(output_dir))
+ fh = open(absp, 'a')
+ else:
+ fh = sys.stdout
+
+ if options.summed:
+ fmt = summed_output
+ else:
+ fmt = generate_output
+
+ for i in fmt(iperf, options):
+ fh.write(i)
+
+
+if __name__ == '__main__':
+ main()