Verified Commit 1056f91a authored by Sebastian Endres's avatar Sebastian Endres
Browse files

Replace 'sequence number' with 'packet number'

parent 672713e2
......@@ -9,7 +9,7 @@ from typing import Literal, Optional, Union
from termcolor import colored, cprint
from plot_time_sequence_diagram import DEFAULT_TITLES, PlotMode, PlotTimeSequenceCli
from plot_diagram import DEFAULT_TITLES, PlotMode, PlotCli
from result_parser import ExtendedMeasurementResult, Result
from tracer import ParsingError
from utils import existing_file_path
......@@ -23,7 +23,7 @@ class Side(Enum):
class PlotModeAll(Enum):
SEQUENCE_NUMBER = "sequence-number"
PACKET_NUMBER = "packet-number"
FILE_SIZE = "file-size"
ALL = "all"
......@@ -31,9 +31,9 @@ class PlotModeAll(Enum):
"""Get PlotModes (resolve 'all')."""
return {
PlotModeAll.SEQUENCE_NUMBER: [PlotMode.SEQUENCE_NUMBER],
PlotModeAll.PACKET_NUMBER: [PlotMode.PACKET_NUMBER],
PlotModeAll.FILE_SIZE: [PlotMode.FILE_SIZE],
PlotModeAll.ALL: [PlotMode.SEQUENCE_NUMBER, PlotMode.FILE_SIZE],
PlotModeAll.ALL: [PlotMode.PACKET_NUMBER, PlotMode.FILE_SIZE],
}[self]
......@@ -96,8 +96,8 @@ def parse_args():
action="store",
choices=PlotModeAll,
type=PlotModeAll,
default=PlotModeAll.SEQUENCE_NUMBER,
help="The mode of plotting (time vs. sequence-number or time vs. file-size or both)",
default=PlotModeAll.PACKET_NUMBER,
help="The mode of plotting (time vs. packet-number or time vs. file-size or both)",
)
return parser.parse_args()
......@@ -115,7 +115,7 @@ class PlotAllCli:
force=False,
only_sat=False,
include_failed=False,
mode: PlotModeAll = PlotModeAll.SEQUENCE_NUMBER,
mode: PlotModeAll = PlotModeAll.PACKET_NUMBER,
):
# self.log_dirs = log_dirs
self.result_files = result_files
......@@ -197,7 +197,7 @@ class PlotAllCli:
),
attrs=["bold"],
)
cli = PlotTimeSequenceCli(
cli = PlotCli(
pcap_files=pcaps,
output_file=output_file,
title=self.title.format(
......
#!/usr/bin/env python3
"""Plot time sequence-number plots."""
"""Plot time packet-number plots and more."""
# TODO
# - lost packets?
......@@ -27,12 +27,12 @@ if typing.TYPE_CHECKING:
class PlotMode(Enum):
SEQUENCE_NUMBER = "sequence-number"
PACKET_NUMBER = "packet-number"
FILE_SIZE = "file-size"
DEFAULT_TITLES = {
PlotMode.SEQUENCE_NUMBER: "Time vs. Packet-Number",
PlotMode.PACKET_NUMBER: "Time vs. Packet-Number",
PlotMode.FILE_SIZE: "Time vs. Transmitted File Size",
}
......@@ -69,6 +69,20 @@ def format_file_size(val: Union[int, float]) -> str:
return f"{val} B"
class Subplot:
fig: plt.Figure
ax: plt.Axes
def __init__(self, *args, **kwargs):
self.fig, self.ax = plt.subplots(*args, **kwargs)
def __enter__(self):
return self.fig, self.ax
def __exit__(self, *args, **kwargs):
plt.close(fig=self.fig)
def parse_args():
"""Parse command line args."""
parser = argparse.ArgumentParser(__doc__)
......@@ -115,8 +129,8 @@ def parse_args():
action="store",
choices=[mode.value for mode in PlotMode],
type=PlotMode,
default=PlotMode.SEQUENCE_NUMBER,
help="The mode of plotting (time vs. sequence-number or time vs. file-size",
default=PlotMode.PACKET_NUMBER,
help="The mode of plotting (time vs. packet-number or time vs. file-size",
)
parser.add_argument(
"--cache",
......@@ -136,7 +150,7 @@ def parse_args():
return args
class PlotTimeSequenceCli:
class PlotCli:
display_filter = "quic and quic.stream_data"
def __init__(
......@@ -146,7 +160,7 @@ class PlotTimeSequenceCli:
keylog_files: list[Optional[Path]] = [],
output_file: Optional[Path] = None,
annotate=True,
mode: PlotMode = PlotMode.SEQUENCE_NUMBER,
mode: PlotMode = PlotMode.PACKET_NUMBER,
cache=False,
):
if not keylog_files:
......@@ -221,157 +235,157 @@ class PlotTimeSequenceCli:
label_side=label_side,
)
def plot_sequence_number(self):
"""Plot the sequence diagram."""
def plot_packet_number(self):
"""Plot the packet number diagram."""
print(
f"Plotting {len(self.traces)} traces into a time vs. sequence-number plot..."
f"Plotting {len(self.traces)} traces into a time vs. packet-number plot..."
)
fig: plt.Figure
ax: plt.Axes
fig, ax = plt.subplots(nrows=1, ncols=1)
ax.grid(True)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Packet Number")
ax.set_title(self.title or DEFAULT_TIME_SQN_TITLE)
# avoid lazy result parsing:
for trace in self.traces:
assert (
trace.packets
), "Trace {trace} contains no filtered packets! Are the secrets injected?"
if self.annotate:
# raise errors early
_ = trace.get_facts()
with yaspin(text="processing...", color="cyan") as spinner:
timestamps = [
np.array([float(packet.sniff_timestamp) for packet in trace.packets])
for trace in self.traces
]
packet_numbers = [
np.array([int(packet.quic.packet_number) for packet in trace.packets])
for trace in self.traces
]
min_packet_number: int = map2d(min, packet_numbers)
max_packet_number: int = map2d(max, packet_numbers)
min_timestamp: np.float64 = map2d(min, timestamps)
max_timestamp: np.float64 = map2d(max, timestamps)
ax.set_xlim(left=min(0, min_timestamp), right=max_timestamp)
ax.set_ylim(bottom=min(0, min_packet_number), top=max_packet_number)
spinner.ok("✅")
with yaspin(text="plotting...", color="cyan") as spinner:
# plot shadow traces
for trace_timestamps, trace_packet_numbers in zip(
timestamps[1:], packet_numbers[1:]
):
with Subplot(nrows=1, ncols=1) as (_fig, ax):
ax.grid(True)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Packet Number")
ax.set_title(self.title)
# avoid lazy result parsing:
for trace in self.traces:
assert (
trace.packets
), "Trace {trace} contains no filtered packets! Are the secrets injected?"
if self.annotate:
# raise errors early
_ = trace.get_facts()
with yaspin(text="processing...", color="cyan") as spinner:
timestamps = [
np.array(
[float(packet.sniff_timestamp) for packet in trace.packets]
)
for trace in self.traces
]
packet_numbers = [
np.array(
[int(packet.quic.packet_number) for packet in trace.packets]
)
for trace in self.traces
]
min_packet_number: int = map2d(min, packet_numbers)
max_packet_number: int = map2d(max, packet_numbers)
min_timestamp: np.float64 = map2d(min, timestamps)
max_timestamp: np.float64 = map2d(max, timestamps)
ax.set_xlim(left=min(0, min_timestamp), right=max_timestamp)
ax.set_ylim(bottom=min(0, min_packet_number), top=max_packet_number)
spinner.ok("✅")
with yaspin(text="plotting...", color="cyan") as spinner:
# plot shadow traces
for trace_timestamps, trace_packet_numbers in zip(
timestamps[1:], packet_numbers[1:]
):
ax.plot(
trace_timestamps,
trace_packet_numbers,
marker="o",
linestyle="",
color="#CCC",
)
# plot main trace
ax.plot(
trace_timestamps,
trace_packet_numbers,
timestamps[0],
packet_numbers[0],
marker="o",
linestyle="",
color="#CCC",
color="#3465A4",
)
# plot main trace
ax.plot(
timestamps[0],
packet_numbers[0],
marker="o",
linestyle="",
color="#3465A4",
)
self._annotate_time_plot(ax, max_packet_number / 2)
spinner.ok("✅")
self._save(fig)
self._annotate_time_plot(ax, max_packet_number / 2)
spinner.ok("✅")
self._save()
def plot_file_size(self):
"""Plot the file size diagram."""
fig: plt.Figure
ax: plt.Axes
fig, ax = plt.subplots(nrows=1, ncols=1)
ax.grid(True)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Transmitted File Size")
ax.set_title(self.title or DEFAULT_TIME_TX_FS_TITLE)
ax.yaxis.set_major_formatter(lambda val, _pos: format_file_size(val))
# avoid lazy result parsing:
for trace in self.traces:
assert (
trace.packets
), "Trace {trace} contains no filtered packets! Are the secrets injected?"
if self.annotate:
# raise errors early
_ = trace.get_facts()
with yaspin(text="processing...", color="cyan") as spinner:
# drop GET request
timestamps = [
np.array(
[float(packet.sniff_timestamp) for packet in trace.packets[1:]]
)
for trace in self.traces
]
file_sizes = [
np.array(
[get_quic_payload_size(packet) for packet in trace.packets[1:]]
)
for trace in self.traces
]
accumulated_transmitted_file_size = [
np.cumsum(trace_file_sizes) for trace_file_sizes in file_sizes
]
min_file_size: int = map2d(min, accumulated_transmitted_file_size)
max_file_size: int = map2d(max, accumulated_transmitted_file_size)
min_timestamp = map2d(min, timestamps)
max_timestamp = map2d(max, timestamps)
ax.set_xlim(left=min(0, min_timestamp), right=max_timestamp)
ax.set_ylim(bottom=min(0, min_file_size), top=max_file_size)
ax.set_yticks(np.arange(0, max_file_size * 1.1, 1024 * 1024))
spinner.ok("✅")
with yaspin(text="plotting...", color="cyan") as spinner:
# plot shadow traces
for trace_timestamps, trace_file_sizes in zip(
timestamps[1:], accumulated_transmitted_file_size[1:]
):
with Subplot() as (_fig, ax):
ax.grid(True)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Transmitted File Size")
ax.set_title(self.title)
ax.yaxis.set_major_formatter(lambda val, _pos: format_file_size(val))
# avoid lazy result parsing:
for trace in self.traces:
assert (
trace.packets
), "Trace {trace} contains no filtered packets! Are the secrets injected?"
if self.annotate:
# raise errors early
_ = trace.get_facts()
with yaspin(text="processing...", color="cyan") as spinner:
# drop GET request
timestamps = [
np.array(
[float(packet.sniff_timestamp) for packet in trace.packets[1:]]
)
for trace in self.traces
]
file_sizes = [
np.array(
[get_quic_payload_size(packet) for packet in trace.packets[1:]]
)
for trace in self.traces
]
accumulated_transmitted_file_size = [
np.cumsum(trace_file_sizes) for trace_file_sizes in file_sizes
]
min_file_size: int = map2d(min, accumulated_transmitted_file_size)
max_file_size: int = map2d(max, accumulated_transmitted_file_size)
min_timestamp = map2d(min, timestamps)
max_timestamp = map2d(max, timestamps)
ax.set_xlim(left=min(0, min_timestamp), right=max_timestamp)
ax.set_ylim(bottom=min(0, min_file_size), top=max_file_size)
ax.set_yticks(np.arange(0, max_file_size * 1.1, 1024 * 1024))
spinner.ok("✅")
with yaspin(text="plotting...", color="cyan") as spinner:
# plot shadow traces
for trace_timestamps, trace_file_sizes in zip(
timestamps[1:], accumulated_transmitted_file_size[1:]
):
ax.plot(
trace_timestamps,
trace_file_sizes,
marker="o",
linestyle="",
color="#CCC",
)
# plot main trace
ax.plot(
trace_timestamps,
trace_file_sizes,
timestamps[0],
accumulated_transmitted_file_size[0],
marker="o",
linestyle="",
color="#CCC",
color="#3465A4",
)
# plot main trace
self._annotate_time_plot(ax, max_file_size / 2)
spinner.ok("✅")
self._save()
ax.plot(
timestamps[0],
accumulated_transmitted_file_size[0],
marker="o",
linestyle="",
color="#3465A4",
)
self._annotate_time_plot(ax, max_file_size / 2)
spinner.ok("✅")
self._save(fig)
def _save(self, fig: plt.Figure):
def _save(self):
"""Save or show the plot."""
if self.output_file:
......@@ -380,12 +394,10 @@ class PlotTimeSequenceCli:
else:
plt.show()
plt.close(fig)
def run(self):
mapping = {
PlotMode.SEQUENCE_NUMBER: {
"callback": self.plot_sequence_number,
PlotMode.PACKET_NUMBER: {
"callback": self.plot_packet_number,
"desc": "time vs. packet number",
},
PlotMode.FILE_SIZE: {
......@@ -409,7 +421,7 @@ def main():
except argparse.ArgumentError as err:
sys.exit(err)
cli = PlotTimeSequenceCli(
cli = PlotCli(
pcap_files=args.pcap_files,
keylog_files=args.keylog_files,
output_file=args.output_file,
......
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