Comparison to similar tools¶
A comparison of per-process (per-executable) network and bandwidth monitors on Linux, measuring completeness (is traffic seen and attributed to the right process) and accuracy (are the byte counts correct). It generates known amounts of traffic across many protocols and hard-to-attribute cases, runs each configuration in isolation, and checks the output against tool-independent reference measurements: whether it (a) detected the activity and attributed it to the right process, and (b) measured the right number of bytes.
Nine projects are covered in ten configurations.
24 scenarios × 5 trials on Ubuntu 26.04 (kernel 7.0) on a GCP e2-standard-4 (4 vCPUs, 16 GB), run 2026-08-05. Detection = seen and attributed to the right process; Bandwidth = bytes within ±10% (PASS) / ±25% (PARTIAL) of the reference for the tool's layer. Method, harness, versions, and how to reproduce: the bench/ directory.
Results summary¶
Each cell counts scenarios (of 24), unweighted; the scenarios are not equally important and the totals are not an overall ranking. N/A marks a missing capability or reference: OpenSnitch does no bandwidth accounting; the BCC utilities and the bpftrace script used here hook TCP only; Sniffnet reports one combined per-program total, so full-duplex scenarios it cannot split by direction are N/A; the loopback scenario has no wire measurement, so tools counting at a packet layer are N/A on its bandwidth.
| Tool | Detection: PASS / PART / FAIL / N/A | Bandwidth: PASS / PART / FAIL / N/A | Trial disagreement |
|---|---|---|---|
| Sysdig | 24 / 0 / 0 / 0 | 19 / 0 / 5 / 0 | 0 |
| picosnitch | 23 / 0 / 1 / 0 | 23 / 0 / 1 / 0 | 0 |
| Little Snitch | 19 / 3 / 2 / 0 | 16 / 0 / 8 / 0 | 2 |
| OpenSnitch | 19 / 0 / 5 / 0 | 0 / 0 / 0 / 24 | 3 |
| bandwhich | 15 / 6 / 3 / 0 | 16 / 1 / 6 / 1 | 5 |
| BCC tcplife/tcpconnect | 14 / 0 / 0 / 10 | 14 / 0 / 0 / 10 | 0 |
| bpftrace script | 14 / 0 / 0 / 10 | 14 / 0 / 0 / 10 | 0 |
| BCC tcptop | 14 / 0 / 0 / 10 | 13 / 0 / 1 / 10 | 0 |
| Sniffnet | 13 / 7 / 4 / 0 | 18 / 0 / 3 / 3 | 0 |
| NetHogs | 11 / 10 / 3 / 0 | 17 / 1 / 5 / 1 | 2 |
Versions¶
Read from each installed tool during the run. Pinned entries are built or downloaded at a fixed version by the harness; distro packages are whatever the Ubuntu archive shipped on the run date and are recorded, not pinned.
| Tool | Version observed | Source |
|---|---|---|
| Sysdig | 0.40.0 | distro package (recorded, not pinned) |
| picosnitch | 2.2.1 | pinned (PyPI via pipx) |
| Little Snitch | 1.1.0 | current release (recorded, not pinned) |
| OpenSnitch | 1.8.0-1 | pinned (release .deb) |
| bandwhich | 0.23.1 | pinned (release binary) |
| BCC tcplife/tcpconnect | 0.37.0 | pinned (built from source tag) |
| bpftrace script | 0.25.0 | distro package (recorded, not pinned) |
| BCC tcptop | 0.37.0 | pinned (built from source tag) |
| Sniffnet | 1.5.1-1 | pinned (release .deb) |
| NetHogs | 0.9.0 | pinned (built from source tag) |
Observed footprint¶
Each tool's whole process tree, sampled at 1 Hz across its session under its own capture scope; not a controlled performance comparison. CPU is % of one core (can exceed 100% across cores). PSS (proportional set size) charges each shared page once, split across its sharers.
| Tool | CPU mean % | CPU 95th pct % | PSS mean MB | PSS peak MB |
|---|---|---|---|---|
| Sysdig | 2.9 | 9.9 | 26.9 | 27.8 |
| picosnitch | 0.7 | 2.9 | 46.1 | 46.8 |
| Little Snitch | 0.3 | 1.0 | 42.7 | 42.7 |
| OpenSnitch | 11.4 | 106.7 | 68.4 | 70.8 |
| bandwhich | 2.9 | 10.0 | 2.6 | 2.9 |
| BCC tcplife/tcpconnect | 0.0 | 0.0 | 276.2 | 276.3 |
| bpftrace script | 0.1 | 1.0 | 170.8 | 170.8 |
| BCC tcptop | 0.1 | 1.0 | 166.7 | 166.8 |
| Sniffnet | 5.2 | 20.0 | 68.1 | 78.2 |
| NetHogs | 0.7 | 2.0 | 6.8 | 7.2 |
Detection¶
| # | Scenario | picosnitch | NetHogs | bandwhich | OpenSnitch | Sniffnet | Little Snitch | BCC tcplife/tcpconnect | BCC tcptop | bpftrace script | Sysdig |
|---|---|---|---|---|---|---|---|---|---|---|---|
| s01 | TCP bulk download (control) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| s02 | TCP bulk upload | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| s03 | TCP full-duplex up+down | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| s04 | UDP bulk up+down | ✅ | 🟡 | ✅ | ✅* | ✅ | ✅ | ⬜ | ⬜ | ⬜ | ✅ |
| s05 | ICMP echo flood w/ payload | ✅ | ❌ | ❌ | ❌ | ❌ | 🟡* | ⬜ | ⬜ | ⬜ | ✅ |
| s06 | UDP/443 bulk | ✅ | 🟡 | ✅ | ✅* | ✅ | ✅ | ⬜ | ⬜ | ⬜ | ✅ |
| s07 | IPv6 TCP transfer | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| s08 | SCTP transfer | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ⬜ | ⬜ | ⬜ | ✅ |
| s09 | Small-packet UDP/53 flood | ✅ | 🟡 | ✅ | ✅* | ✅ | ✅ | ⬜ | ⬜ | ⬜ | ✅ |
| s10 | Raw IP socket (proto 253) egress | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ⬜ | ⬜ | ⬜ | ✅ |
| s11 | Short-lived processes | ✅ | 🟡 | 🟡 | ✅ | 🟡 | ✅ | ✅ | ✅ | ✅ | ✅ |
| s12 | AF_PACKET raw-frame injection | ❌ | 🟡 | 🟡 | ❌ | 🟡 | 🟡 | ⬜ | ⬜ | ⬜ | ✅ |
| s13 | io_uring data path | ✅ | ✅ | 🟡 | ✅ | 🟡 | ✅ | ✅ | ✅ | ✅ | ✅ |
| s14 | sendfile() zero-copy upload | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| s15 | sendmmsg batched UDP | ✅ | 🟡 | ✅ | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ | ✅ |
| s16 | Loopback-only transfer | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
| s17 | In-container (docker) egress | ✅ | 🟡 | 🟡 | ❌ | 🟡 | 🟡 | ✅ | ✅ | ✅ | ✅ |
| s18 | Low-and-slow drip upload | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| s19 | Many small TCP connections | ✅ | ✅ | ✅ | ✅ | 🟡 | ✅ | ✅ | ✅ | ✅ | ✅ |
| s20 | High-rate parallel burst | ✅ | 🟡 | 🟡 | ✅ | 🟡 | ✅ | ✅ | ✅ | ✅ | ✅ |
| s21 | io_uring download (recv) | ✅ | ✅ | 🟡 | ✅ | 🟡 | ✅ | ✅ | ✅ | ✅ | ✅ |
| s22 | splice() zero-copy download | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| s23 | recvmmsg batched UDP (recv) | ✅ | 🟡 | ✅ | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ | ✅ |
| s24 | IPv6 UDP download | ✅ | 🟡 | ✅ | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ | ✅ |
Legend: ✅ PASS · 🟡 PARTIAL · ❌ FAIL · ⬜ N/A · ⚠️ not measured (setup failed or unresolved trial) · * trials disagreed, see the note
Bandwidth accuracy¶
Scored on bytes reported, independently of attribution: a tool that measured the traffic but bucketed it as unknown is scored here on those bytes, and the detection grid records that miss as PARTIAL.
| # | Scenario | picosnitch | NetHogs | bandwhich | OpenSnitch | Sniffnet | Little Snitch | BCC tcplife/tcpconnect | BCC tcptop | bpftrace script | Sysdig |
|---|---|---|---|---|---|---|---|---|---|---|---|
| s01 | TCP bulk download (control) | ✅ | ✅ | ✅ | ⬜ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| s02 | TCP bulk upload | ✅ | ✅ | ✅ | ⬜ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| s03 | TCP full-duplex up+down | ✅ | ✅ | ✅ | ⬜ | ⬜ | ✅ | ✅ | ✅ | ✅ | ✅ |
| s04 | UDP bulk up+down | ✅ | ✅ | ✅ | ⬜ | ⬜ | ✅ | ⬜ | ⬜ | ⬜ | ✅ |
| s05 | ICMP echo flood w/ payload | ✅ | ❌ | ❌ | ⬜ | ❌ | ❌* | ⬜ | ⬜ | ⬜ | ✅ |
| s06 | UDP/443 bulk | ✅ | ✅ | ✅ | ⬜ | ✅ | ✅ | ⬜ | ⬜ | ⬜ | ✅ |
| s07 | IPv6 TCP transfer | ✅ | ✅ | ✅ | ⬜ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| s08 | SCTP transfer | ✅ | ❌ | ❌ | ⬜ | ❌ | ✅ | ⬜ | ⬜ | ⬜ | ✅ |
| s09 | Small-packet UDP/53 flood | ✅ | ✅ | ✅ | ⬜ | ✅ | ❌ | ⬜ | ⬜ | ⬜ | ✅ |
| s10 | Raw IP socket (proto 253) egress | ✅ | ❌ | ❌ | ⬜ | ❌ | ❌ | ⬜ | ⬜ | ⬜ | ✅ |
| s11 | Short-lived processes | ✅ | ❌ | ✅* | ⬜ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| s12 | AF_PACKET raw-frame injection | ❌ | ✅ | 🟡* | ⬜ | ✅ | ❌ | ⬜ | ⬜ | ⬜ | ✅ |
| s13 | io_uring data path | ✅ | 🟡* | ❌* | ⬜ | ✅ | ❌* | ✅ | ✅ | ✅ | ❌ |
| s14 | sendfile() zero-copy upload | ✅ | ✅ | ✅* | ⬜ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| s15 | sendmmsg batched UDP | ✅ | ✅ | ✅ | ⬜ | ✅ | ✅ | ⬜ | ⬜ | ⬜ | ✅ |
| s16 | Loopback-only transfer | ✅ | ⬜ | ⬜ | ⬜ | ⬜ | ❌ | ✅ | ✅ | ✅ | ✅ |
| s17 | In-container (docker) egress | ✅ | ✅ | ✅ | ⬜ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
| s18 | Low-and-slow drip upload | ✅ | ✅ | ✅ | ⬜ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| s19 | Many small TCP connections | ✅ | ✅ | ✅ | ⬜ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| s20 | High-rate parallel burst | ✅ | ❌ | ❌ | ⬜ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| s21 | io_uring download (recv) | ✅ | ✅ | ❌* | ⬜ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ |
| s22 | splice() zero-copy download | ✅ | ✅ | ✅ | ⬜ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ |
| s23 | recvmmsg batched UDP (recv) | ✅ | ✅ | ✅ | ⬜ | ✅ | ✅ | ⬜ | ⬜ | ⬜ | ❌ |
| s24 | IPv6 UDP download | ✅ | ✅ | ✅ | ⬜ | ✅ | ✅ | ⬜ | ⬜ | ⬜ | ✅ |
Legend: ✅ PASS · 🟡 PARTIAL · ❌ FAIL · ⬜ N/A · ⚠️ not measured (setup failed or unresolved trial) · * trials disagreed, see the note
Full report¶
- Full findings: per-scenario notes and reference byte counts
- Detection scorecard · Bandwidth scorecard: the tables above, with a footnote per PARTIAL or FAIL cell and per cell whose trials disagreed
- Per-tool detail: one page per configuration, with the reference and reported bytes for every scenario
- Method, harness, and how to run