| #!/usr/bin/env python3 |
| |
| 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)) |