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>
This commit is contained in:

committed by
Damjan Marion

parent
26ce6ca1fe
commit
3417d08700
219
src/scripts/host-stack/cc_plots.py
Executable file
219
src/scripts/host-stack/cc_plots.py
Executable file
@ -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))
|
12
src/scripts/host-stack/convert_evt
Executable file
12
src/scripts/host-stack/convert_evt
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# This depends on c2cpel and cpeldump. Enable their compilation by:
|
||||||
|
# ccmake build-root/build-vpp-native/vpp/
|
||||||
|
# and turning on VPP_BUILD_PERFTOOL
|
||||||
|
|
||||||
|
BIN_PATH=../../../build-root/install-vpp-native/vpp/bin
|
||||||
|
C2CPEL_BIN=$BIN_PATH/c2cpel
|
||||||
|
CPELDUMP_BIN=$BIN_PATH/cpeldump
|
||||||
|
|
||||||
|
$C2CPEL_BIN --in $1 --out /tmp/tmp_file.cpel
|
||||||
|
$CPELDUMP_BIN --in /tmp/tmp_file.cpel --out $2
|
Reference in New Issue
Block a user