#!/usr/bin/env python3 """MP-QUIC Application & eBPF Results Visualizer. This script takes the CSV outputs and plots their latency distributions and timelines. Usage: python visualize.py --app ../server/app_metrics.csv python visualize.py --client ../results/client.csv --server ../results/server.csv """ import argparse import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import os def load_ebpf_data(csv_path, label): if not os.path.exists(csv_path): print(f"Warning: File {csv_path} not found.") return pd.DataFrame() df = pd.read_csv(csv_path) df['node'] = label df = df.sort_values('timestamp') if not df.empty: first_event_time = df['timestamp'].iloc[0] df['rel_time'] = df['timestamp'] - first_event_time else: df['rel_time'] = 0.0 df['value_us'] = df['value_ns'] / 1000.0 return df def plot_app_metrics(csv_path, output_dir): """Plot Application-Level Metrics (Drone -> Ground Station).""" if not os.path.exists(csv_path): print(f"App metrics file {csv_path} not found.") return df = pd.read_csv(csv_path) if df.empty: print("App metrics file is empty.") return df = df.sort_values('sequence_number') # Calculate Jitter and Rel Latency df['latency_ms'] = df['latency_ns'] / 1000000.0 # Relative time from first packet received df['rel_time'] = (df['ground_recv_time'] - df['ground_recv_time'].min()) / 1e9 plt.figure(figsize=(10, 5)) sns.scatterplot(x='rel_time', y='latency_ms', data=df, s=15, alpha=0.6) plt.title('App-Level End-to-End Latency Over Time (Glass-to-Glass)') plt.ylabel('Relative Latency (ms)') plt.xlabel('Time (s)') out_path = os.path.join(output_dir, 'app_latency_timeline.png') plt.savefig(out_path, dpi=300, bbox_inches='tight') plt.close() # Packet Loss Calculation max_seq = df['sequence_number'].max() min_seq = df['sequence_number'].min() expected_packets = max_seq - min_seq + 1 received_packets = len(df) lost_packets = expected_packets - received_packets reliability = (received_packets / expected_packets) * 100 if expected_packets > 0 else 0 print("\n" + "=" * 55) print(" 🏆 APP-LEVEL RELIABILITY (Drone -> Ground)") print("=" * 55) print(f" Packets Sent (Expected): {expected_packets}") print(f" Packets Received: {received_packets}") print(f" Packets Lost: {lost_packets}") print(f" Reliability: {reliability:.5f}%") print("=" * 55) print(f"Saved app-level plot to {out_path}") def plot_latency_distributions(df, output_dir): latency_df = df[df['event_type'].isin(['SEND_LATENCY', 'RECV_LATENCY'])] if latency_df.empty: return plt.figure(figsize=(10, 6)) sns.violinplot(x='event_type', y='value_us', hue='node', data=latency_df, split=True, inner="quartile") plt.yscale('log') plt.title('Kernel Network Stack Latency Distribution (Log Scale)') plt.ylabel('Latency (µs)') plt.xlabel('Event Type') out_path = os.path.join(output_dir, 'kernel_latency_distribution.png') plt.savefig(out_path, dpi=300, bbox_inches='tight') plt.close() print(f"Saved kernel distribution plot to {out_path}") def plot_latency_timeline(df, output_dir): latency_df = df[df['event_type'].isin(['SEND_LATENCY', 'RECV_LATENCY'])] if latency_df.empty: return g = sns.FacetGrid(latency_df, col="event_type", row="node", margin_titles=True, height=4, aspect=2) g.map(sns.scatterplot, "rel_time", "value_us", alpha=0.5, s=10) g.set_axis_labels("Time (s)", "Latency (µs)") g.set_titles(col_template="{col_name}", row_template="{row_name}") for ax in g.axes.flat: ax.set_yscale('log') g.fig.suptitle('Kernel Latency Over Time', y=1.02) out_path = os.path.join(output_dir, 'kernel_latency_timeline.png') plt.savefig(out_path, dpi=300, bbox_inches='tight') plt.close() print(f"Saved kernel timeline plot to {out_path}") def main(): parser = argparse.ArgumentParser(description="Visualize MP-QUIC data") parser.add_argument("--client", help="Path to client eBPF CSV file") parser.add_argument("--server", help="Path to server eBPF CSV file") parser.add_argument("--app", help="Path to App-Level metrics CSV file (e.g. app_metrics.csv)") parser.add_argument("--outdir", default="plots", help="Directory to save plots") args = parser.parse_args() os.makedirs(args.outdir, exist_ok=True) if args.app: plot_app_metrics(args.app, args.outdir) dfs = [] if args.client: dfs.append(load_ebpf_data(args.client, "Client")) if args.server: dfs.append(load_ebpf_data(args.server, "Server")) if dfs: df = pd.concat(dfs, ignore_index=True) sns.set_theme(style="whitegrid") plot_latency_distributions(df, args.outdir) plot_latency_timeline(df, args.outdir) print("\nVisualization complete! Check the '{}' directory.".format(args.outdir)) if __name__ == "__main__": main()