Skip to content

feat: per-process network straggler detection (procfs + eBPF) - #121

Merged
shvbsle merged 5 commits into
mainfrom
net-probes
May 24, 2026
Merged

feat: per-process network straggler detection (procfs + eBPF)#121
shvbsle merged 5 commits into
mainfrom
net-probes

Conversation

@shvbsle

@shvbsle shvbsle commented May 23, 2026

Copy link
Copy Markdown
Owner

Adds per-process network telemetry to detect network-induced stragglers in distributed training jobs.

What it detects

Fault type Metric Source
Latency net.tcp_rtt_us eBPF kprobe on tcp_rtt_estimator
Packet loss net.tcp_retransmits /proc/<pid>/net/snmp
Bandwidth throttle net.tcp_out_segs + net.tcp_tx_queue_bytes /proc/<pid>/net/snmp + /proc/<pid>/net/tcp

How it works

Kitty watches processes that have KITTY_WATCH=1 in their environment. For each watched process:

  1. A background ProcessState task (5s refresh) resolves PIDs, parses /proc/<pid>/net/tcp for local ports and tx_queue. Optionally extracts RANK and JOB_NAME from environ as metric labels if available (not required — only KITTY_WATCH=1 is mandatory).
  2. The network collector reads /proc/<pid>/net/snmp every 500ms for monotonic TCP counters.
  3. The eBPF collector attaches a kprobe to tcp_rtt_estimator and captures raw (unsmoothed) RTT samples, attributed to the correct process via port matching.

Why raw RTT via eBPF

We tried and rejected several alternatives:

  • SRTT from /proc/net/tcp: doesn't decay after fault removal (exponential smoothing)
  • kprobe on tcp_sendmsg duration: sendmsg returns before qdisc delay is applied
  • kprobe on tcp_recvmsg duration: data already in socket buffer by the time recvmsg runs

tcp_rtt_estimator receives the raw RTT sample as its second argument (mrtt_us) before any smoothing is applied. This value jumps immediately when latency is introduced and drops immediately when removed.

Key design decisions

  • tcp_rtt_estimator runs in softirq context (not process context), so bpf_get_current_pid_tgid() returns an unrelated process. Instead, the BPF program reads the socket's local port from struct sock, and userspace maps port→pid by discovering NCCL connections from /proc/<pid>/net/tcp (refreshed every 5s in the snapshot). No hardcoded ports — they're whatever NCCL allocates at runtime.
  • ProcessState is a shared snapshot that both network and eBPF collectors consume — no duplicated procfs reads
  • /proc/<pid>/net/snmp is read per-tick (500ms) because counters must be fresh for rate computation; everything else is cached in the 5s snapshot

Validation

Tested with kfault inject-one on a 4-node GPT-2 DDP training job (g4dn.xlarge, 1x T4 per node):

Fault Signal Baseline → During fault → After removal
50ms netem delay tcp_rtt_us 2ms → 51ms → 3ms
5% netem loss tcp_retransmits flat → +510/min → flat
100Mbit tbf tcp_out_segs ~150k/2s → 0 → resumes

Usage

Add to training pod env:

env:
  - name: KITTY_WATCH
    value: "1"

Metrics appear at :9100/metrics labeled with {pid, rank, job}.

Parses /proc/net/snmp and /proc/net/netstat to emit 7 new metrics:
tcp_retransmits, tcp_in_segs, tcp_out_segs, tcp_estab_connections,
tcp_timeouts, tcp_fast_retrans, tcp_loss_probes. These enable detection
of network-induced stragglers that link-level counters alone cannot catch.
@shvbsle
shvbsle force-pushed the net-probes branch 19 times, most recently from bda9357 to cc0dbd9 Compare May 24, 2026 06:54
eBPF:
- kprobe on tcp_rtt_estimator captures raw unsmoothed RTT samples
- Reads socket's local port from sk->__sk_common.skc_num
- Emits (sport, dport, rtt_us) to ring buffer
- Validated: 50ms netem delay → immediate jump to 52ms, instant recovery

Architecture refactor:
- New ProcessState module replaces watch.rs
- Background task refreshes snapshot every 5s (configurable)
- Snapshot includes: labels, local_ports, tcp_tx_queue_bytes per PID
- Network + eBPF collectors consume snapshot (no direct procfs reads
  for discovery/ports/tx_queue)
- /proc/<pid>/net/snmp still read per-tick by network collector (counters
  must be fresh for rate computation)
- Per-PID RTT attribution via port→pid mapping from snapshot

Metrics:
- net.tcp_rtt_us: raw RTT per watched process (eBPF, 100ms)
- net.tcp_retransmits: per-process counter (procfs, 500ms)
- net.tcp_out_segs: per-process counter (procfs, 500ms)
- net.tcp_tx_queue_bytes: per-process gauge (snapshot, 5s)
Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml Outdated
@shvbsle shvbsle changed the title feat: add TCP-level metrics to network collector for straggler detection feat: per-process network straggler detection (procfs + eBPF) May 24, 2026
Comment thread src/crates/kitty/src/process_state.rs Outdated
- Remove net-probes from CI push/docker triggers (main only)
- Extract framework label parsing into separate function
  (extract_framework_labels) that supports multiple conventions:
  PyTorch (RANK, JOB_NAME), JAX (PROCESS_INDEX), MPI (OMPI_COMM_WORLD_RANK)
- Uses entry().or_insert_with() so first match wins (PyTorch takes priority)
@shvbsle
shvbsle marked this pull request as ready for review May 24, 2026 08:04
shvbsle added 2 commits May 24, 2026 08:07
- Replace stale per-interface link stats with actual per-process TCP metrics
- Document KITTY_WATCH=1 requirement and pod network namespace behavior
- Add detection matrix (latency/loss/bandwidth → which metric catches it)
- Document eBPF tcp_rtt_estimator approach and why alternatives were rejected
- Move implemented features out of "Planned" sections
- Add alpha notice
- Add straggler detection section with network (implemented),
  GPU/compute (planned), and CPU/data pipeline (planned) categories
- Show KITTY_WATCH=1 usage in installation section
- Update verify section to show what to grep for
- Update project structure to include kitty-ebpf
@shvbsle
shvbsle merged commit 7de683e into main May 24, 2026
3 checks passed
@shvbsle
shvbsle deleted the net-probes branch May 24, 2026 08:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant