#!/usr/bin/env python3 """Tabulate link-test runs from the Orin's results file: python3 linktest_summary.py [results.jsonl] [label...]""" import json import os import sys path = os.path.expanduser(sys.argv[1] if len(sys.argv) > 1 else "~/linktest/results.jsonl") labels = sys.argv[2:] runs = {} for line in open(path): r = json.loads(line) if "id" in r: runs.setdefault(r["id"], []).append(r) for run_id, recs in runs.items(): if labels and not any(run_id.startswith(l + "-") for l in labels): continue print(f"\n== {run_id}") for r in recs: k = r["kind"] if k in ("udp", "soak"): print(f" {k:5} {r['size']:>4} B {r['rate']:>3}/s {r['secs']:>2}s loss {r['loss_pct']:>5}% " f"rtt p50 {r['rtt_p50_ms']:>6} p99 {r['rtt_p99_ms']:>7} max {r['rtt_max_ms']:>7} ms " f"gap {r['max_gap_ms']:>7} ms sendfail {r['send_fails']}") elif k in ("tcp_down", "tcp_up"): print(f" {k:8} {r['mb_s']:>6} MB/s over {r['secs']} s") elif k in ("tcp_down_orin", "tcp_up_orin"): mode = r.get("mode", "") print(f" {k:13} {mode:6} retrans {r.get('retrans')} rtt {r.get('rtt_ms')} ms rto {r.get('rto_ms')} ms {r.get('error', '')}") elif k == "dl_bulk": print(f" bulk #{r['n']} {r['mb_s']:>5} MB/s over {r['secs']} s 250 ms windows: min {r['win_min_mb_s']} " f"p10 {r['win_p10_mb_s']} p50 {r['win_p50_mb_s']} max {r['win_max_mb_s']} zero {r['zero_windows']} " f"longest stall {r['max_gap_ms']} ms") elif k == "dl_bursts": print(f" bursts after {r.get('idle_ms', 1000)} ms idle, keepalive {r.get('keepalive_pps', 0)}/s:") for e in r["sizes"]: print(f" burst {e['bytes'] // 1024:>5} KB p50 {e['p50_ms']:>7} ms max {e['max_ms']:>7} ms " f"({e['mb_s_p50']} MB/s at p50) all {e['ms']}") elif k == "dl_paced": print(f" paced {r['rate'] / 1e6:>3} MB/s got {r['mb_s']:>5} MB/s frames {r['frames']}/{r['expected']} " f"queueing p50 {r['q_p50_ms']} p99 {r['q_p99_ms']} max {r['q_max_ms']} ms " f">100 ms {r['late_100']} >500 ms {r['late_500']}") elif k == "error": print(f" ERROR {r['error']}") elif k == "end": print(" (complete)")