Add CPU usage monitoring, remove btest-opensource submodule
All checks were successful
CI / test (push) Successful in 2m16s

CPU usage feature:
- New cpu.rs module: background sampler thread, cross-platform (macOS + Linux)
- Status message byte 1 now carries CPU load (0-100%), matching MikroTik format
- Status format corrected: [type][cpu][00][00][seq:4 LE][bytes:4 LE]
- Client and server exchange CPU in every status message
- Display format: "cpu: 40%/12%" (local/remote), "!" warning if > 70%
- Both client and server show local + remote CPU per interval
- Syslog TEST_END could include CPU averages (future enhancement)

Removed btest-opensource submodule — we've fully reimplemented the protocol
with EC-SRP5 auth, multi-connection, IPv6, syslog, CSV, and CPU monitoring.
The original project is still credited in LICENSE and README.

58 tests, all passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-04-01 10:53:00 +04:00
parent 10dd0c3835
commit 24f634170d
9 changed files with 200 additions and 24 deletions

View File

@@ -382,11 +382,13 @@ async fn client_status_loop(cmd: &Command, state: &BandwidthState) {
let rx = if cmd.client_rx() { state.rx_bytes.swap(0, Ordering::Relaxed) } else { 0 };
state.record_interval(tx, rx, 0);
let local_cpu = crate::cpu::get();
let remote_cpu = state.remote_cpu.load(Ordering::Relaxed);
if cmd.client_tx() {
bandwidth::print_status(seq, "TX", tx, Duration::from_secs(1), None);
bandwidth::print_status_with_cpu(seq, "TX", tx, Duration::from_secs(1), None, Some(local_cpu), Some(remote_cpu));
}
if cmd.client_rx() {
bandwidth::print_status(seq, "RX", rx, Duration::from_secs(1), None);
bandwidth::print_status_with_cpu(seq, "RX", rx, Duration::from_secs(1), None, Some(local_cpu), Some(remote_cpu));
}
}
}
@@ -420,6 +422,7 @@ async fn udp_client_status_loop(
match tokio::time::timeout(wait_time, reader.read_exact(&mut status_buf)).await {
Ok(Ok(_)) => {
let server_status = StatusMessage::deserialize(&status_buf);
state.remote_cpu.store(server_status.cpu_load, Ordering::Relaxed);
if server_status.bytes_received > 0 && cmd.client_tx() {
let new_speed =
@@ -453,7 +456,7 @@ async fn udp_client_status_loop(
let lost = state.rx_lost_packets.swap(0, Ordering::Relaxed);
state.record_interval(tx_bytes, rx_bytes, lost);
let status = StatusMessage {
let status = StatusMessage { cpu_load: crate::cpu::get(),
seq,
bytes_received: rx_bytes as u32,
};
@@ -463,11 +466,13 @@ async fn udp_client_status_loop(
}
let _ = writer.flush().await;
let local_cpu = crate::cpu::get();
let remote_cpu = state.remote_cpu.load(Ordering::Relaxed);
if cmd.client_tx() {
bandwidth::print_status(seq, "TX", tx_bytes, Duration::from_secs(1), None);
bandwidth::print_status_with_cpu(seq, "TX", tx_bytes, Duration::from_secs(1), None, Some(local_cpu), Some(remote_cpu));
}
if cmd.client_rx() {
bandwidth::print_status(seq, "RX", rx_bytes, Duration::from_secs(1), Some(lost));
bandwidth::print_status_with_cpu(seq, "RX", rx_bytes, Duration::from_secs(1), Some(lost), Some(local_cpu), Some(remote_cpu));
}
}
}