From: Florin Coras Date: Wed, 12 Jun 2019 15:12:58 +0000 (-0700) Subject: tcp: add cc stats plotting tools X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F92%2F20092%2F4;p=vpp.git 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 ", 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 --- diff --git a/src/scripts/host-stack/cc_plots.py b/src/scripts/host-stack/cc_plots.py new file mode 100755 index 00000000000..ea2d4c94353 --- /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)) diff --git a/src/scripts/host-stack/convert_evt b/src/scripts/host-stack/convert_evt new file mode 100755 index 00000000000..1aba67d0268 --- /dev/null +++ b/src/scripts/host-stack/convert_evt @@ -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