tests: replace pycodestyle with black
[vpp.git] / src / scripts / host-stack / cc_plots.py
1 #!/usr/bin/env python3
2
3 import sys
4 import re
5 import argparse
6 import matplotlib.pyplot as plt
7 from matplotlib.lines import Line2D
8
9
10 class Point:
11     "CC event"
12
13     def __init__(self, x, y):
14         self.x = x
15         self.y = y
16
17
18 def listx(points):
19     return list(map(lambda pt: pt.x, points))
20
21
22 def listy(points):
23     return list(map(lambda pt: pt.y, points))
24
25
26 def plot_data(d):
27
28     plt.figure(1)
29
30     cwndx = listx(d["cwnd"])
31     cwndy = listy(d["cwnd"])
32     congx = listx(d["congestion"])
33     congy = listy(d["congestion"])
34     rcvrdx = listx(d["recovered"])
35     rcvrdy = listy(d["recovered"])
36     rxttx = listx(d["rxtTimeout"])
37     rxtty = listy(d["rxtTimeout"])
38
39     # cwnd/ssthresh/cc events
40     plt.subplot(311)
41     plt.title("cwnd/ssthresh")
42     pcwnd = plt.plot(cwndx, cwndy, "r")
43     psst = plt.plot(cwndx, d["ssthresh"], "y-")
44     pcong = plt.plot(congx, congy, "yo")
45     precov = plt.plot(rcvrdx, rcvrdy, "co")
46     prxtt = plt.plot(rxttx, rxtty, "mo")
47
48     marker1 = Line2D(range(1), range(1), color="r")
49     marker2 = Line2D(range(1), range(1), color="y")
50     marker3 = Line2D(range(1), range(1), color="w", marker="o", markerfacecolor="y")
51     marker4 = Line2D(range(1), range(1), color="w", marker="o", markerfacecolor="c")
52     marker5 = Line2D(range(1), range(1), color="w", marker="o", markerfacecolor="m")
53     plt.legend(
54         (marker1, marker2, marker3, marker4, marker5),
55         ("cwnd", "ssthresh", "congestion", "recovered", "rxt-timeout"),
56         loc=4,
57     )
58     axes = plt.gca()
59     axes.set_ylim([-20e4, max(cwndy) + 20e4])
60
61     # snd variables
62     plt.subplot(312)
63     plt.title("cc variables")
64     plt.plot(cwndx, d["space"], "g-", markersize=1)
65     plt.plot(cwndx, d["flight"], "b-", markersize=1)
66     plt.plot(cwndx, d["sacked"], "m:", markersize=1)
67     plt.plot(cwndx, d["lost"], "y:", markersize=1)
68     plt.plot(cwndx, d["cc-space"], "k:", markersize=1)
69     plt.plot(cwndx, cwndy, "ro", markersize=2)
70
71     plt.plot(congx, congy, "y^", markersize=10, markerfacecolor="y")
72     plt.plot(rcvrdx, rcvrdy, "c^", markersize=10, markerfacecolor="c")
73     plt.plot(rxttx, rxtty, "m^", markersize=10, markerfacecolor="m")
74
75     # plt.plot(cwndx, d["snd_wnd"], 'ko', markersize=1)
76     plt.legend(
77         (
78             "snd-space",
79             "flight",
80             "sacked",
81             "lost",
82             "cc-space",
83             "cwnd",
84             "congestion",
85             "recovered",
86             "rxt-timeout",
87         ),
88         loc=1,
89     )
90
91     # rto/srrt/rttvar
92     plt.subplot(313)
93     plt.title("rtt")
94     plt.plot(cwndx, d["srtt"], "g-")
95     plt.plot(cwndx, [x / 1000 for x in d["mrtt-us"]], "r-")
96     plt.plot(cwndx, d["rttvar"], "b-")
97     plt.legend(["srtt", "mrtt-us", "rttvar"])
98     axes = plt.gca()
99     # plt.plot(cwndx, rto, 'r-')
100     # axes.set_ylim([0, int(max(rto[2:len(rto)])) + 50])
101
102     # show
103     plt.show()
104
105
106 def find_pattern(file_path, session_idx):
107     is_active_open = 1
108     listener_pattern = "l\[\d\]"
109     if is_active_open:
110         initial_pattern = "\[\d\](\.\d+:\d+\->\.\d+:\d+)\s+open:\s"
111     else:
112         initial_pattern = "\[\d\](\.\d+:\d+\->\.\d+:\d+)\s"
113     idx = 0
114     f = open(file_path, "r")
115     for line in f:
116         # skip listener lines (server)
117         if re.search(listener_pattern, line) != None:
118             continue
119         match = re.search(initial_pattern, line)
120         if match == None:
121             continue
122         if idx < session_idx:
123             idx += 1
124             continue
125         filter_pattern = str(match.group(1)) + "\s+(.+)"
126         print("pattern is %s" % filter_pattern)
127         f.close()
128         return filter_pattern
129     raise Exception("Could not find initial pattern")
130
131
132 def compute_time(min, sec, msec):
133     return int(min) * 60 + int(sec) + int(msec) / 1000.0
134
135
136 def run(file_path, session_idx):
137     filter_sessions = 1
138     filter_pattern = ""
139
140     patterns = {
141         "time": "^\d+:(\d+):(\d+):(\d+):\d+",
142         "listener": "l\[\d\]",
143         "cc": "cwnd (\d+) flight (\d+) space (\d+) ssthresh (\d+) snd_wnd (\d+)",
144         "cc-snd": "cc_space (\d+) sacked (\d+) lost (\d+)",
145         "rtt": "rto (\d+) srtt (\d+) mrtt-us (\d+) rttvar (\d+)",
146         "rxtt": "rxt-timeout",
147         "congestion": "congestion",
148         "recovered": "recovered",
149     }
150     d = {
151         "cwnd": [],
152         "space": [],
153         "flight": [],
154         "ssthresh": [],
155         "snd_wnd": [],
156         "cc-space": [],
157         "lost": [],
158         "sacked": [],
159         "rto": [],
160         "srtt": [],
161         "mrtt-us": [],
162         "rttvar": [],
163         "rxtTimeout": [],
164         "congestion": [],
165         "recovered": [],
166     }
167
168     if filter_sessions:
169         filter_pattern = find_pattern(file_path, session_idx)
170     f = open(file_path, "r")
171
172     stats_index = 0
173     start_time = 0
174
175     for line in f:
176         # skip listener lines (server)
177         if re.search(patterns["listener"], line) != None:
178             continue
179         # filter sessions
180         if filter_sessions:
181             match = re.search(filter_pattern, line)
182             if match == None:
183                 continue
184
185         original_line = line
186         line = match.group(1)
187         match = re.search(patterns["time"], original_line)
188         if match == None:
189             print("something went wrong! no time!")
190             continue
191         time = compute_time(match.group(1), match.group(2), match.group(3))
192         if start_time == 0:
193             start_time = time
194
195         time = time - start_time
196         match = re.search(patterns["cc"], line)
197         if match != None:
198             d["cwnd"].append(Point(time, int(match.group(1))))
199             d["flight"].append(int(match.group(2)))
200             d["space"].append(int(match.group(3)))
201             d["ssthresh"].append(int(match.group(4)))
202             d["snd_wnd"].append(int(match.group(5)))
203             stats_index += 1
204             continue
205         match = re.search(patterns["cc-snd"], line)
206         if match != None:
207             d["cc-space"].append(int(match.group(1)))
208             d["sacked"].append(int(match.group(2)))
209             d["lost"].append(int(match.group(3)))
210         match = re.search(patterns["rtt"], line)
211         if match != None:
212             d["rto"].append(int(match.group(1)))
213             d["srtt"].append(int(match.group(2)))
214             d["mrtt-us"].append(int(match.group(3)))
215             d["rttvar"].append(int(match.group(4)))
216         if stats_index == 0:
217             continue
218         match = re.search(patterns["rxtt"], line)
219         if match != None:
220             d["rxtTimeout"].append(Point(time, d["cwnd"][stats_index - 1].y + 1e4))
221             continue
222         match = re.search(patterns["congestion"], line)
223         if match != None:
224             d["congestion"].append(Point(time, d["cwnd"][stats_index - 1].y - 1e4))
225             continue
226         match = re.search(patterns["recovered"], line)
227         if match != None:
228             d["recovered"].append(Point(time, d["cwnd"][stats_index - 1].y))
229             continue
230
231     plot_data(d)
232
233
234 if __name__ == "__main__":
235     parser = argparse.ArgumentParser(description="Plot tcp cc logs")
236     parser.add_argument(
237         "-f", action="store", dest="file", required=True, help="elog file in txt format"
238     )
239     parser.add_argument(
240         "-s",
241         action="store",
242         dest="session_index",
243         default=0,
244         help="session index for which to plot cc logs",
245     )
246     results = parser.parse_args()
247     run(results.file, int(results.session_index))