Skip to content

PingerPlot 1.3.0

Choose a tag to compare

@jamesccupps jamesccupps released this 21 Aug 16:59
· 17 commits to main since this release

A full audit pass plus four features.

Two fixes change results you may have relied on — see Behaviour changes before comparing old exports with new ones.

Fixed

  • A route longer than 16 hops took two or more reply timeouts per round. The probe pool was fixed at 16 workers while max_hops defaults to 30 and may reach 64, so a round silently split into waves — measured 2.0× at 30 hops, 4.0× at 64. With a 2 s timeout a 30-hop round overran the 2.5 s interval entirely, and the configured interval was quietly replaced by the round duration. Now 1.0× at every length.
  • An alert never cleared once a reroute changed the path length. Alerts are keyed by TTL and only the destination's TTL was evaluated, so the old key was orphaned: the banner, the active-alert count and the summary row stayed wrong for the rest of the run — reporting loss on a hop that no longer existed while the real destination was healthy.
  • A closed TCP port read as an unreachable host. Windows does not surface the RST until it has finished retransmitting the SYN (~2 s measured), and the default reply timeout was 1000 ms — so "the service is down but the box is fine" came back as "host unreachable". TCP mode now floors the reply timeout at 3000 ms and logs that it did.
  • settings.save() could raise on a value json cannot encode, from the window-close handler.
  • Writing to the probe log during shutdown raised ValueError, not OSError, so it escaped the handler and aborted the round as "Monitor error".
  • The Map tab never repainted when geo lookups returned — on a loaded session it stayed empty until the window was resized.
  • The comparison table crashed a cp1252 console (a delta sign in the header).
  • A router's identity could be discarded when its ICMP error and the probe socket's error arrived in the same select() wakeup.

Added

  • Baseline comparisonFile → Compare with saved session…, or --baseline in headless mode. A hop whose responding address changed is reported as rerouted with no latency delta, because subtracting one router's latency from another's is a meaningless number.
  • DSCP marking and source-interface binding — measure a QoS-marked path as itself rather than as best-effort, and pin the outgoing NIC on a multi-homed box. A source address the machine does not hold is rejected outright rather than quietly falling back.
  • MOS alerting — on the score that folds latency, jitter and loss together, so it catches the combination that ruins a call while each ingredient sits under its own threshold.
  • One-shot report mode--report N collects N rounds, prints an MTR-style table per target and exits. Exit status is 0 only if every target reached its destination, so a scheduled job can branch on it.
  • Linux and macOS ICMP backend — unprivileged SOCK_DGRAM + IP_RECVERR/MSG_ERRQUEUE, the mtr technique, with a separate path for macOS which has neither. Unit-tested on every platform in CI; not yet field-tested on real hardware — smoke-test with python -m pingerplot.selftest 8.8.8.8 before trusting a trace from it.
  • Automatic restart of a stopped target in headless mode, backing off 30 s → 5 min. A target whose name did not resolve at boot used to stay dead for the life of the service.

Behaviour changes

  • Latency alone can now colour a hop row red. The BAD_MS branch was dead code, so a 900 ms hop looked no more alarming than a 130 ms one.
  • TCP reply timeouts below 3000 ms are raised, with a note in the Events tab. An unreachable TCP destination now costs 3 s a round rather than 1 s — the trade for being able to tell "service down, host fine" from "host unreachable".

Notes

Tests 95 → 343. Engine coverage 62% → 81%; tcpudp.py, which had no coverage of either probe round loop, went 25% → 78%. The TCP/UDP rounds are now exercised against a mock router feeding a stand-in capture socket, so select(), recvfrom() and the timeouts are the real code paths.

That the DSCP byte reaches the wire has not been verified — every code point is accepted without error, which is a weaker claim. Confirm with a capture before relying on a QoS result.

Full detail in CHANGELOG.md.