tcp: add cc stats plotting tools
Type: feature
cc_plot.py can plot cc stats collected via elogs from tcp connections.
To enable the collection of cc stats, tcp must be compiled with
TCP_DEBUG_CC_STAT turned on.
Once the tests have been carried out, and elogs have been saved with "ev
save log <file>", the logs can be converted to txt format with the
convert_evt script. The resulting file can be used as input to the
cc_plots script.
Change-Id: I16df2993fa09cab9ad35f91825eab7df4da6428b
Signed-off-by: Florin Coras <fcoras@cisco.com>
diff --git a/src/scripts/host-stack/cc_plots.py b/src/scripts/host-stack/cc_plots.py
new file mode 100755
index 0000000..ea2d4c9
--- /dev/null
+++ b/src/scripts/host-stack/cc_plots.py
@@ -0,0 +1,219 @@
+#!/usr/bin/env python
+
+import sys
+import re
+import argparse
+import matplotlib.pyplot as plt
+from matplotlib.lines import Line2D
+
+class Point():
+ "CC event"
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+
+def listx(points):
+ return list(map(lambda pt: pt.x, points))
+
+def listy(points):
+ return list(map(lambda pt: pt.y, points))
+
+def plot_data(d):
+
+ plt.figure(1)
+
+ cwndx = listx(d["cwnd"])
+ cwndy = listy(d["cwnd"])
+ congx = listx(d["congestion"])
+ congy = listy(d["congestion"])
+ rcvrdx = listx(d["recovered"])
+ rcvrdy = listy(d["recovered"])
+ rxttx = listx(d["rxtTimeout"])
+ rxtty = listy(d["rxtTimeout"])
+
+ # cwnd/ssthresh/cc events
+ plt.subplot(311)
+ plt.title("cwnd/ssthresh")
+ pcwnd = plt.plot(cwndx, cwndy, 'r')
+ psst = plt.plot(cwndx, d["ssthresh"], 'y-')
+ pcong = plt.plot(congx, congy,'yo')
+ precov = plt.plot(rcvrdx, rcvrdy,'co')
+ prxtt = plt.plot(rxttx, rxtty,'mo')
+
+ marker1 = Line2D(range(1), range(1), color="r")
+ marker2 = Line2D(range(1), range(1), color="y")
+ marker3 = Line2D(range(1), range(1), color="w", marker="o", markerfacecolor="y")
+ marker4 = Line2D(range(1), range(1), color="w", marker="o", markerfacecolor="c")
+ marker5 = Line2D(range(1), range(1), color="w", marker="o", markerfacecolor="m")
+ plt.legend((marker1, marker2, marker3, marker4, marker5),
+ ('cwnd', 'ssthresh', 'congestion', 'recovered', 'rxt-timeout'),
+ loc=4)
+ axes = plt.gca()
+ axes.set_ylim([-20e4, max(cwndy) + 20e4])
+
+ # snd variables
+ plt.subplot(312)
+ plt.title("cc variables")
+ plt.plot(cwndx, d["space"], 'g-', markersize=1)
+ plt.plot(cwndx, d["flight"], 'b-', markersize=1)
+ plt.plot(cwndx, d["sacked"], 'm:', markersize=1)
+ plt.plot(cwndx, d["lost"], 'y:', markersize=1)
+ plt.plot(cwndx, d["cc-space"], 'k:', markersize=1)
+ plt.plot(cwndx, cwndy, 'ro', markersize=2)
+
+ plt.plot(congx, congy, 'y^', markersize=10, markerfacecolor="y")
+ plt.plot(rcvrdx, rcvrdy, 'c^', markersize=10, markerfacecolor="c")
+ plt.plot(rxttx, rxtty, 'm^', markersize=10, markerfacecolor="m")
+
+ #plt.plot(cwndx, d["snd_wnd"], 'ko', markersize=1)
+ plt.legend(("snd-space", "flight", "sacked", "lost", "cc-space", "cwnd",
+ "congestion", "recovered", "rxt-timeout"),
+ loc=1)
+
+ # rto/srrt/rttvar
+ plt.subplot(313)
+ plt.title("rtt")
+ plt.plot(cwndx, d["srtt"], 'g-')
+ plt.plot(cwndx, [x/1000 for x in d["mrtt-us"]], 'r-')
+ plt.plot(cwndx, d["rttvar"], 'b-')
+ plt.legend(["srtt", "mrtt-us", "rttvar"])
+ axes = plt.gca()
+ #plt.plot(cwndx, rto, 'r-')
+ #axes.set_ylim([0, int(max(rto[2:len(rto)])) + 50])
+
+ # show
+ plt.show()
+
+def find_pattern(file_path,session_idx):
+ is_active_open = 1
+ listener_pattern = "l\[\d\]"
+ if (is_active_open):
+ initial_pattern = "\[\d\](\.\d+:\d+\->\.\d+:\d+)\s+open:\s"
+ else:
+ initial_pattern = "\[\d\](\.\d+:\d+\->\.\d+:\d+)\s"
+ idx = 0
+ f = open(file_path, 'r')
+ for line in f:
+ # skip listener lines (server)
+ if (re.search(listener_pattern, line) != None):
+ continue
+ match = re.search(initial_pattern, line)
+ if (match == None):
+ continue
+ if (idx < session_idx):
+ idx += 1
+ continue
+ filter_pattern = str(match.group(1)) + "\s+(.+)"
+ print ("pattern is %s" % filter_pattern)
+ f.close()
+ return filter_pattern
+ raise Exception ("Could not find initial pattern")
+
+def compute_time(min, sec, msec):
+ return int(min)*60 + int(sec) + int(msec)/1000.0
+
+def run(file_path, session_idx):
+ filter_sessions = 1
+ filter_pattern = ""
+
+ patterns = {
+ "time" : "^\d+:(\d+):(\d+):(\d+):\d+",
+ "listener" : "l\[\d\]",
+ "cc" : "cwnd (\d+) flight (\d+) space (\d+) ssthresh (\d+) snd_wnd (\d+)",
+ "cc-snd" : "cc_space (\d+) sacked (\d+) lost (\d+)",
+ "rtt" : "rto (\d+) srtt (\d+) mrtt-us (\d+) rttvar (\d+)",
+ "rxtt" : "rxt-timeout",
+ "congestion": "congestion",
+ "recovered" : "recovered",
+ }
+ d = {
+ "cwnd" : [],
+ "space" : [],
+ "flight" : [],
+ "ssthresh" : [],
+ "snd_wnd" : [],
+ "cc-space" : [],
+ "lost" : [],
+ "sacked" : [],
+ "rto" : [],
+ "srtt" : [],
+ "mrtt-us" : [],
+ "rttvar" : [],
+ "rxtTimeout" : [],
+ "congestion" : [],
+ "recovered" : [],
+ }
+
+ if (filter_sessions):
+ filter_pattern = find_pattern(file_path, session_idx)
+ f = open(file_path, 'r')
+
+ stats_index = 0
+ start_time = 0
+
+ for line in f:
+ # skip listener lines (server)
+ if (re.search(patterns["listener"], line) != None):
+ continue
+ # filter sessions
+ if (filter_sessions):
+ match = re.search(filter_pattern, line)
+ if (match == None):
+ continue
+
+ original_line = line
+ line = match.group(1)
+ match = re.search (patterns["time"], original_line)
+ if (match == None):
+ print "something went wrong! no time!"
+ continue
+ time = compute_time (match.group(1), match.group(2), match.group(3))
+ if (start_time == 0):
+ start_time = time
+
+ time = time - start_time
+ match = re.search(patterns["cc"], line)
+ if (match != None):
+ d["cwnd"].append(Point(time, int(match.group(1))))
+ d["flight"].append(int(match.group(2)))
+ d["space"].append(int(match.group(3)))
+ d["ssthresh"].append(int(match.group(4)))
+ d["snd_wnd"].append(int(match.group(5)))
+ stats_index += 1
+ continue
+ match = re.search(patterns["cc-snd"], line)
+ if (match != None):
+ d["cc-space"].append(int(match.group(1)))
+ d["sacked"].append(int(match.group(2)))
+ d["lost"].append(int(match.group(3)))
+ match = re.search(patterns["rtt"], line)
+ if (match != None):
+ d["rto"].append(int(match.group(1)))
+ d["srtt"].append(int(match.group(2)))
+ d["mrtt-us"].append(int(match.group(3)))
+ d["rttvar"].append(int(match.group(4)))
+ if (stats_index == 0):
+ continue
+ match = re.search(patterns["rxtt"], line)
+ if (match != None):
+ d["rxtTimeout"].append(Point(time, d["cwnd"][stats_index - 1].y + 1e4))
+ continue
+ match = re.search(patterns["congestion"], line)
+ if (match != None):
+ d["congestion"].append(Point(time, d["cwnd"][stats_index - 1].y - 1e4))
+ continue
+ match = re.search(patterns["recovered"], line)
+ if (match != None):
+ d["recovered"].append(Point(time, d["cwnd"][stats_index - 1].y))
+ continue
+
+ plot_data(d)
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description="Plot tcp cc logs")
+ parser.add_argument('-f', action='store', dest='file', required=True,
+ help="elog file in txt format")
+ parser.add_argument('-s', action='store', dest='session_index', default=0,
+ help="session index for which to plot cc logs" )
+ results = parser.parse_args()
+ run(results.file, int(results.session_index))