Verified Commit a1c0219e authored by Sebastian Endres's avatar Sebastian Endres
Browse files

Refactor tracer

parent 30315c25
......@@ -274,7 +274,7 @@ class PlotAllCli:
grouped[msg].append(measurement_result)
print()
print("Results:")
print("#### Results:")
for msg, measurement_results in grouped.items():
print(f" - {msg}: {len(measurement_results)}")
......@@ -282,10 +282,10 @@ class PlotAllCli:
print()
for msg, measurement_results in grouped.items():
print(msg)
print(f"- {msg}")
for measurement_result in measurement_results:
print(f" - {measurement_result.combination}")
print(f" - `{measurement_result.combination}`")
def run(self):
for result_file in self.result_files:
......
......@@ -16,13 +16,7 @@ import numpy as np
from matplotlib import pyplot as plt
from termcolor import colored, cprint
from tracer import (
ParsingError,
Trace,
get_quic_payload_size,
get_stream_offset,
iter_stream_frames,
)
from tracer import ParsingError, Trace
from utils import (
Subplot,
YaspinWrapper,
......@@ -40,6 +34,8 @@ if typing.TYPE_CHECKING:
class PlotMode(Enum):
"""The plot type."""
OFFSET_NUMBER = "offset-number"
PACKET_NUMBER = "packet-number"
FILE_SIZE = "file-size"
......@@ -125,7 +121,7 @@ def parse_args():
class PlotCli:
display_filter = "quic and quic.stream_data"
"""Cli for plotting."""
def __init__(
self,
......@@ -157,7 +153,6 @@ class PlotCli:
Trace(
file=pcap_file,
keylog_file=keylog_file,
display_filter=self.display_filter,
cache=cache,
debug=self.debug,
)
......@@ -209,32 +204,93 @@ class PlotCli:
alpha=0.5,
)
def _annotate_time_plot(self, ax: plt.Axes, y: Union[float, int]):
if self.annotate:
for label, value, label_side in (
(
"Req. Start = {value:.2f} s",
self.traces[0].facts["request_start"],
"left",
),
(
"TTFB = {value:.2f} s",
self.traces[0].facts["ttfb"],
"right",
),
(
"PLT = {value:.2f} s",
self.traces[0].facts["plt"],
"right",
),
):
self._vline_annotate(
ax=ax,
x=value,
y=y,
text=label.format(value=value),
label_side=label_side,
)
def _vdim_annotate(
self,
ax,
left: Union[int, float],
right: Union[int, float],
y: Union[int, float],
text: str,
):
"""Add a vertical dimension."""
ax.annotate(
"",
xy=(left, y),
xytext=(right, y),
textcoords=ax.transData,
arrowprops=dict(
arrowstyle="<->",
color="red",
alpha=0.5,
),
color="red",
alpha=0.5,
)
ax.annotate(
"",
xy=(left, y),
xytext=(right, y),
textcoords=ax.transData,
arrowprops=dict(
arrowstyle="|-|",
color="red",
alpha=0.5,
),
color="red",
alpha=0.5,
)
ax.text(
(right + left) / 2,
y,
text,
ha="center",
va="center",
color="red",
alpha=0.5,
bbox=dict(fc="white", ec="none"),
)
def _annotate_time_plot(self, ax: plt.Axes, height: Union[float, int]):
if not self.annotate:
return
ttfb = self.traces[0].facts["ttfb"]
req_start = self.traces[0].facts["request_start"]
pglt = self.traces[0].facts["plt"]
resp_delay = self.traces[0].facts["response_delay"]
for label, value, label_side in (
(
f"Req. Start = {req_start:.3f} s",
req_start,
"left",
),
(
f"TTFB = {ttfb:.3f} s",
ttfb,
"right",
),
(
f"PLT = {pglt:.3f} s",
pglt,
"right",
),
):
self._vline_annotate(
ax=ax,
x=value,
y=height / 2,
text=label,
label_side=label_side,
)
self._vdim_annotate(
ax=ax,
left=self.traces[0].facts["request_start"],
right=self.traces[0].facts["ttfb"],
y=height * 3 / 4,
text=f"{resp_delay * 1000:.0f} ms",
)
def plot_offset_number(self, output_file: Optional[Path]):
"""Plot the offset number diagram."""
......@@ -242,6 +298,7 @@ class PlotCli:
ax.grid(True)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Offset")
assert self.title
ax.set_title(self.title)
ax.yaxis.set_major_formatter(lambda val, _pos: format_file_size(val))
......@@ -265,25 +322,25 @@ class PlotCli:
response_retrans_timestamps.append(list[float]())
for layer in trace.request_stream_frames:
offset = get_stream_offset(layer)
offset = trace.get_stream_offset(layer)
if offset is not None:
request_offsets[-1].append(offset)
request_timestamps[-1].append(layer.sniff_timestamp)
request_timestamps[-1].append(layer.norm_time)
for layer in trace.response_stream_frames_first_tx:
offset = get_stream_offset(layer)
offset = trace.get_stream_offset(layer)
if offset is not None:
response_first_offsets[-1].append(offset)
response_first_timestamps[-1].append(layer.sniff_timestamp)
response_first_timestamps[-1].append(layer.norm_time)
for layer in trace.response_stream_frames_retrans:
offset = get_stream_offset(layer)
offset = trace.get_stream_offset(layer)
if offset is not None:
response_retrans_offsets[-1].append(offset)
response_retrans_timestamps[-1].append(layer.sniff_timestamp)
response_retrans_timestamps[-1].append(layer.norm_time)
all_offsets = (
*request_offsets,
......@@ -364,9 +421,8 @@ class PlotCli:
color="#b60000",
)
self._annotate_time_plot(ax, max_offset / 2)
spinner.ok("✔")
self._save(output_file)
self._annotate_time_plot(ax, height=max_offset)
self._save(output_file, spinner)
def plot_packet_number(self, output_file: Optional[Path]):
"""Plot the packet number diagram."""
......@@ -374,17 +430,18 @@ class PlotCli:
ax.grid(True)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Packet Number")
assert self.title
ax.set_title(self.title)
with YaspinWrapper(
debug=self.debug, text="processing...", color="cyan"
) as spinner:
request_timestamps = [
[layer.sniff_timestamp for layer in trace.request_stream_frames]
[layer.norm_time for layer in trace.request_stream_frames]
for trace in self.traces
]
response_timestamps = [
[layer.sniff_timestamp for layer in trace.response_stream_frames]
[layer.norm_time for layer in trace.response_stream_frames]
for trace in self.traces
]
request_packet_numbers = [
......@@ -451,9 +508,8 @@ class PlotCli:
color="#3465A4",
)
self._annotate_time_plot(ax, max_packet_number / 2)
spinner.ok("✔")
self._save(output_file)
self._annotate_time_plot(ax, height=max_packet_number)
self._save(output_file, spinner)
def plot_file_size(self, output_file: Optional[Path]):
"""Plot the file size diagram."""
......@@ -461,6 +517,7 @@ class PlotCli:
ax.grid(True)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Transmitted File Size")
assert self.title
ax.set_title(self.title)
ax.yaxis.set_major_formatter(lambda val, _pos: format_file_size(val))
......@@ -470,18 +527,15 @@ class PlotCli:
# only response
timestamps = [
np.array(
[
float(packet.sniff_timestamp)
for packet in trace.response_packets
]
[packet.norm_time for packet in trace.server_client_packets]
)
for trace in self.traces
]
file_sizes = [
np.array(
[
get_quic_payload_size(packet)
for packet in trace.response_packets
trace.get_quic_payload_size(packet)
for packet in trace.server_client_packets
]
)
for trace in self.traces
......@@ -526,20 +580,23 @@ class PlotCli:
color="#3465A4",
)
self._annotate_time_plot(ax, max_file_size / 2)
spinner.ok("✔")
self._save(output_file)
self._annotate_time_plot(ax, height=max_file_size)
self._save(output_file, spinner)
def _save(self, output_file: Optional[Path]):
def _save(self, output_file: Optional[Path], spinner: YaspinWrapper):
"""Save or show the plot."""
if output_file:
plt.savefig(output_file, dpi=300, transparent=True)
cprint(f"{create_relpath(output_file)} written.", color="green")
spinner.text = colored(
f"{create_relpath(output_file)} written.", color="green"
)
spinner.ok("✔")
else:
plt.show()
def run(self):
"""Run command line interface."""
mapping = {
PlotMode.OFFSET_NUMBER: {
"callback": self.plot_offset_number,
......
This diff is collapsed.
......@@ -60,7 +60,7 @@ def format_file_size(val: Union[int, float]) -> str:
class YaspinWrapper:
def __init__(self, debug: bool, text: str, color: str):
self.debug = debug
self.text = text
self._text = text
self.color = color
if not debug:
......@@ -68,7 +68,7 @@ class YaspinWrapper:
def __enter__(self):
if self.debug:
cprint(f"⚒ {self.text}", color=self.color, end="\r", flush=True)
self._update()
else:
self.yaspin.__enter__()
......@@ -80,6 +80,31 @@ class YaspinWrapper:
else:
self.yaspin.__exit__(*args, **kwargs)
@property
def text(self) -> str:
if self.debug:
return self._text
else:
return self.yaspin.text
@text.setter
def text(self, value: str):
if self.debug:
self._text = value
self._update()
else:
self.yaspin.text = value
def hidden(self):
if self.debug:
return self
else:
return self.yaspin.hidden()
def _update(self):
if self.debug:
cprint(f"⚒ {self.text}", color=self.color, end="\r", flush=True)
def ok(self, text: str):
if self.debug:
print(text)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment