From 3417d08700168d9a7c0fe561e40e73478d5e1229 Mon Sep 17 00:00:00 2001 From: Florin Coras Date: Wed, 12 Jun 2019 08:12:58 -0700 Subject: 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 --- src/scripts/host-stack/cc_plots.py | 219 +++++++++++++++++++++++++++++++++++++ src/scripts/host-stack/convert_evt | 12 ++ 2 files changed, 231 insertions(+) create mode 100755 src/scripts/host-stack/cc_plots.py create mode 100755 src/scripts/host-stack/convert_evt (limited to 'src/scripts') 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 -- cgit 1.2.3-korg