7.3 KiB
irtt-rs
irtt-rs is a Rust implementation of an IRTT-compatible client.
It is not the upstream IRTT project. For the original implementation, protocol background, and broader project documentation, see:
https://github.com/heistp/irtt
Status
This repository currently provides:
irtt-rs, the canonical multi-applet binaryirtt-cli, a stream/text IRTT-compatible client appletirtt-tui, an optional terminal UI applet when built with thetuifeatureirtt-client, a Rust library for running client sessions and consuming events- finite and continuous probe runs
- table, CSV, TSV, and JSON Lines event-row output formats with selectable columns
- optional local summary statistics
Server support is not implemented.
The installed binary is intentionally not named irtt to avoid conflicting or
confusing overlap with upstream IRTT.
Install
Requires Rust 1.85 or newer.
From a local checkout:
git clone https://github.com/Lochnair/irtt-rs.git
cd irtt-rs
cargo install --path crates/irtt-cli
The default install provides the normal client-oriented binaries, including
irtt-rs and irtt-cli. To install the TUI applet as a separate binary target:
cargo install --path crates/irtt-cli --features tui
Or run it directly without installing:
cargo run -p irtt-cli -- <server>
For space-sensitive packaging, such as OpenWrt packages, distributors can ship
only irtt-rs and symlink or hardlink applet names such as irtt-cli,
irtt-tui, and future server applet names to it. Applet dispatch is based on
the invoked binary name.
cargo-dist release archives currently bundle all enabled applets for a target. That is separate from space-sensitive package layouts, which may still ship one binary plus applet-name symlinks or hardlinks.
CLI usage
Basic test:
irtt-cli <server>
Set duration and interval:
irtt-cli <server> --duration 30s --interval 100ms
Run continuously:
irtt-cli <server> --duration 0
Use a specific output format or column selection:
irtt-cli <server> --format table
irtt-cli <server> --format jsonl
irtt-cli <server> --format csv --columns event,seq,remote,effective_rtt_us
irtt-cli <server> --format tsv --columns effective_rtt_us --header never
irtt-cli --list-columns
With the optional TUI feature, irtt-tui opens a live cumulative dashboard for
interactive probing. It defaults to continuous probing, equivalent to
--duration 0. Quit with q or Ctrl-C; the client will drain and close the
session gracefully.
irtt-tui <server>
irtt-tui <server> --duration 30s
cargo run -p irtt-cli --features tui --bin irtt-tui -- <server>
For available options:
irtt-cli --help
irtt-tui --help
Event row output
The stream client separates event row rendering from final summaries. Event rows
are controlled by --format, --columns, and --header; final summaries remain
separate table-style text and are printed for table output when the run policy
allows a summary.
Formats:
table: default, terminal-readable columns with a headercsv: comma-separated rows, intended for scriptstsv: tab-separated rows, useful for one-value streams and shell pipelinesjsonl: one JSON object per event row, suitable for streaming consumers
Headers are controlled with --header auto|always|never. In auto, table, CSV,
and TSV print one header row; JSON Lines never prints a header.
Columns are selected with -c, --columns <COLUMNS>, where COLUMNS is a
comma-separated list. Use --list-columns to print the available names. Useful
columns include:
event, seq, remote, token, rtt, rtt_us, raw_rtt_us, effective_rtt_us,
adjusted_rtt_us, rd, rd_us, sd, sd_us, ipdv, ipdv_us, proc,
server_processing_us, bytes, send_call_us, timer_error_us, highest_seen,
server_received, server_window, dscp, ecn, traffic_class, kernel_rx_ns,
warning_kind, message, event_wall_ns, client_send_wall_ns,
client_receive_wall_ns
Aliases are accepted for readability: receive_delay and receive_delay_us
for rd and rd_us, send_delay and send_delay_us for sd and sd_us,
server_processing for proc, server_received_count for
server_received, and server_received_window for server_window.
The default table output favors readability with compact columns:
event,seq,rtt,rd,sd,ipdv,proc,message. It omits echo_sent rows. Passing
--columns default is the same as omitting --columns, including the default
table row filtering. Custom table column selections include all event rows.
CSV, TSV, and JSONL default to all columns for structured export; use
--columns all explicitly to request every column in any format. Missing table
values render as -; missing CSV and TSV values render as empty fields;
missing JSONL values render as null.
Use JSON Lines for structured event streaming:
Example:
irtt-cli <server> --duration 0 --interval 250ms --format jsonl
Each line is one client event object, for example:
{"event":"echo_reply","seq":4,"remote":"203.0.113.10:2112","token":null,"rtt":"12.1ms","rtt_us":12100,"raw_rtt_us":12400,"effective_rtt_us":12100,"adjusted_rtt_us":12100,"rd":"6.0ms","rd_us":6000,"sd":"6.1ms","sd_us":6100,"ipdv":"300.0µs","ipdv_us":300,"proc":"300.0µs","server_processing_us":300,"bytes":64,"send_call_us":null,"timer_error_us":null,"highest_seen":null,"server_received":5,"server_window":"0x1f","dscp":0,"ecn":0,"traffic_class":0,"kernel_rx_ns":null,"warning_kind":null,"message":null,"event_wall_ns":1760000000012400000,"client_send_wall_ns":1760000000000000000,"client_receive_wall_ns":1760000000012400000}
Consumers should match on the event property and read the columns they need.
RTT, send-call, and timer-error *_us fields are reported in microseconds.
raw_rtt_us is the measured client send-to-receive RTT. effective_rtt_us and
adjusted_rtt_us are signed and may be negative when server processing exceeds
the raw RTT or timing correction produces a negative adjusted value. When
one-way delay fields are present, sd_us and rd_us are also signed
microseconds and may be negative due to wall-clock skew.
For consumers that only need effective RTT values in microseconds, use:
irtt-cli <server> --format tsv --columns effective_rtt_us --header never
Library usage
irtt-client can be used directly from Rust code.
Example Cargo.toml dependency from a local checkout:
[dependencies]
irtt-client = { path = "crates/irtt-client" }
Minimal managed-client example:
use std::time::Duration;
use irtt_client::{
ClientConfig, ClientEvent, ManagedClient, SubscriberConfig,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = ClientConfig {
server_addr: "netperf-eu.bufferbloat.net:2112".to_owned(),
duration: Some(Duration::from_secs(10)),
interval: Duration::from_secs(1),
..ClientConfig::default()
};
let (session, events) =
ManagedClient::start_with_subscription(config, SubscriberConfig::default())?;
while let Ok(event) = events.recv() {
match event {
ClientEvent::EchoReply { seq, rtt, .. } => {
println!("seq={seq} rtt_us={}", rtt.effective.as_micros());
}
ClientEvent::SessionClosed { .. } => break,
_ => {}
}
}
let outcome = session.join()?;
println!("session ended: {:?}", outcome.end_reason);
Ok(())
}